AI-For-Beginners/translations/ru/lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md

108 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Трюки для обучения глубоких нейронных сетей
По мере увеличения глубины нейронных сетей процесс их обучения становится все более сложным. Одной из основных проблем являются так называемые [исчезающие градиенты](https://en.wikipedia.org/wiki/Vanishing_gradient_problem) или [взрывающиеся градиенты](https://deepai.org/machine-learning-glossary-and-terms/exploding-gradient-problem#:~:text=Exploding%20gradients%20are%20a%20problem,updates%20are%20small%20and%20controlled.). [Эта статья](https://towardsdatascience.com/the-vanishing-exploding-gradient-problem-in-deep-neural-networks-191358470c11) дает хорошее введение в эти проблемы.
Чтобы сделать обучение глубоких сетей более эффективным, можно использовать несколько техник.
## Поддержание значений в разумных пределах
Для обеспечения стабильности численных вычислений важно, чтобы все значения внутри нейронной сети находились в разумных пределах, обычно [-1..1] или [0..1]. Это не строгое требование, но природа вычислений с плавающей точкой такова, что значения разных порядков величины не могут быть точно обработаны вместе. Например, если мы сложим 10<sup>-10</sup> и 10<sup>10</sup>, скорее всего, получим 10<sup>10</sup>, так как меньшее значение будет "преобразовано" к тому же порядку, что и большее, и мантисса будет потеряна.
Большинство функций активации имеют нелинейности в пределах [-1..1], поэтому имеет смысл масштабировать все входные данные в интервал [-1..1] или [0..1].
## Инициализация начальных весов
Идеально, чтобы значения оставались в том же диапазоне после прохождения через слои сети. Поэтому важно инициализировать веса таким образом, чтобы сохранить распределение значений.
Нормальное распределение **N(0,1)** не является хорошей идеей, так как если у нас есть *n* входов, стандартное отклонение выхода будет *n*, и значения, скорее всего, выйдут за пределы интервала [0..1].
Часто используются следующие методы инициализации:
- Равномерное распределение — `uniform`
- **N(0,1/n)** — `gaussian`
- **N(0,1/√n_in)** гарантирует, что для входов с нулевым средним и стандартным отклонением 1 сохранятся те же среднее и стандартное отклонение
- **N(0,√2/(n_in+n_out))** — так называемая **инициализация Ксавье** (`glorot`), помогает сохранять сигналы в пределах диапазона как при прямом, так и при обратном распространении
## Нормализация батча
Даже при правильной инициализации весов они могут становиться произвольно большими или маленькими в процессе обучения, выводя сигналы за пределы допустимого диапазона. Мы можем вернуть сигналы обратно, используя одну из техник **нормализации**. Хотя существует несколько методов (нормализация весов, нормализация слоя), наиболее часто используется нормализация батча.
Идея **нормализации батча** заключается в учете всех значений в минибатче и выполнении нормализации (т.е. вычитание среднего и деление на стандартное отклонение) на основе этих значений. Это реализуется как слой сети, который выполняет нормализацию после применения весов, но перед функцией активации. В результате мы, скорее всего, получим более высокую итоговую точность и более быстрое обучение.
Вот [оригинальная статья](https://arxiv.org/pdf/1502.03167.pdf) о нормализации батча, [объяснение на Википедии](https://en.wikipedia.org/wiki/Batch_normalization) и [хорошая вводная статья](https://towardsdatascience.com/batch-normalization-in-3-levels-of-understanding-14c2da90a338) (и одна [на русском](https://habrahabr.ru/post/309302/)).
## Dropout
**Dropout** — это интересная техника, которая удаляет определенный процент случайных нейронов во время обучения. Она также реализуется как слой с одним параметром (процент удаляемых нейронов, обычно 10%-50%), и во время обучения обнуляет случайные элементы входного вектора перед передачей его следующему слою.
Хотя это может показаться странной идеей, вы можете увидеть эффект dropout на обучении классификатора цифр MNIST в ноутбуке [`Dropout.ipynb`](../../../../../lessons/4-ComputerVision/08-TransferLearning/Dropout.ipynb). Это ускоряет обучение и позволяет достичь более высокой точности за меньшее количество эпох.
Этот эффект можно объяснить несколькими способами:
- Это можно рассматривать как случайный шоковый фактор для модели, который выводит оптимизацию из локального минимума
- Это можно рассматривать как *неявное усреднение модели*, так как можно сказать, что во время dropout мы обучаем немного другую модель
> *Некоторые люди говорят, что когда пьяный человек пытается что-то выучить, он запомнит это лучше на следующее утро, чем трезвый, потому что мозг с некоторыми неработающими нейронами лучше адаптируется к пониманию смысла. Мы сами никогда не проверяли, правда это или нет.*
## Предотвращение переобучения
Одним из очень важных аспектов глубокого обучения является способность предотвращать [переобучение](../../3-NeuralNetworks/05-Frameworks/Overfitting.md). Хотя может быть заманчиво использовать очень мощную модель нейронной сети, мы всегда должны балансировать количество параметров модели с количеством обучающих выборок.
> Убедитесь, что вы понимаете концепцию [переобучения](../../3-NeuralNetworks/05-Frameworks/Overfitting.md), которую мы объясняли ранее!
Существует несколько способов предотвращения переобучения:
- Раннее прекращение — непрерывный мониторинг ошибки на валидационном наборе и остановка обучения, когда ошибка на валидации начинает увеличиваться.
- Явное затухание весов / регуляризация — добавление дополнительного штрафа к функции потерь за высокие абсолютные значения весов, что предотвращает получение моделью очень нестабильных результатов.
- Усреднение моделей — обучение нескольких моделей и последующее усреднение результата. Это помогает минимизировать дисперсию.
- Dropout (неявное усреднение модели)
## Оптимизаторы / Алгоритмы обучения
Еще один важный аспект обучения — выбор хорошего алгоритма обучения. Хотя классический **градиентный спуск** является разумным выбором, он иногда может быть слишком медленным или приводить к другим проблемам.
В глубоком обучении мы используем **стохастический градиентный спуск** (SGD), который представляет собой градиентный спуск, применяемый к минибатчам, случайно выбранным из обучающего набора. Веса корректируются по следующей формуле:
w<sup>t+1</sup> = w<sup>t</sup> - η∇ℒ
### Momentum
В **Momentum SGD** мы сохраняем часть градиента из предыдущих шагов. Это похоже на движение с инерцией: если мы получаем удар в другом направлении, наша траектория не меняется мгновенно, а сохраняет часть первоначального движения. Здесь вводится дополнительный вектор v для представления *скорости*:
- v<sup>t+1</sup> = γ v<sup>t</sup> - η∇ℒ
- w<sup>t+1</sup> = w<sup>t</sup> + v<sup>t+1</sup>
Параметр γ указывает, насколько мы учитываем инерцию: γ=0 соответствует классическому SGD; γ=1 — это чистое уравнение движения.
### Adam, Adagrad и другие
Поскольку в каждом слое мы умножаем сигналы на некоторую матрицу W<sub>i</sub>, в зависимости от ||W<sub>i</sub>|| градиент может либо уменьшаться до почти 0, либо возрастать бесконечно. Это и есть суть проблемы исчезающих/взрывающихся градиентов.
Одним из решений этой проблемы является использование только направления градиента в уравнении, игнорируя абсолютное значение, т.е.
w<sup>t+1</sup> = w<sup>t</sup> - η(∇ℒ/||∇ℒ||), где ||∇ℒ|| = √∑(∇ℒ)<sup>2</sup>
Этот алгоритм называется **Adagrad**. Другие алгоритмы, использующие ту же идею: **RMSProp**, **Adam**.
> **Adam** считается очень эффективным алгоритмом для многих приложений, поэтому если вы не уверены, какой выбрать — используйте Adam.
### Gradient clipping
Gradient clipping — это расширение идеи выше. Когда ||∇ℒ|| ≤ θ, мы используем оригинальный градиент в оптимизации весов, а когда ||∇ℒ|| > θ — делим градиент на его норму. Здесь θ — параметр, в большинстве случаев можно взять θ=1 или θ=10.
### Learning rate decay
Успех обучения часто зависит от параметра скорости обучения η. Логично предположить, что большие значения η приводят к более быстрому обучению, что обычно желательно в начале обучения, а затем меньшие значения η позволяют тонко настроить сеть. Таким образом, в большинстве случаев мы хотим уменьшать η в процессе обучения.
Это можно сделать, умножая η на некоторое число (например, 0.98) после каждой эпохи обучения или используя более сложный **график изменения скорости обучения**.
## Различные архитектуры сетей
Выбор правильной архитектуры сети для вашей задачи может быть сложным. Обычно мы берем архитектуру, которая доказала свою эффективность для нашей конкретной задачи (или похожей). Вот [хороший обзор](https://www.topbots.com/a-brief-history-of-neural-network-architectures/) архитектур нейронных сетей для компьютерного зрения.
> Важно выбрать архитектуру, которая будет достаточно мощной для количества обучающих выборок, которые у нас есть. Выбор слишком мощной модели может привести к [переобучению](../../3-NeuralNetworks/05-Frameworks/Overfitting.md).
Другим хорошим вариантом будет использование архитектуры, которая автоматически адаптируется к требуемой сложности. В некоторой степени архитектуры **ResNet** и **Inception** являются самонастраивающимися. [Подробнее об архитектурах для компьютерного зрения](../07-ConvNets/CNN_Architectures.md).
**Отказ от ответственности**:
Этот документ был переведен с использованием сервиса автоматического перевода [Co-op Translator](https://github.com/Azure/co-op-translator). Несмотря на наши усилия обеспечить точность, автоматические переводы могут содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для получения критически важной информации рекомендуется профессиональный перевод человеком. Мы не несем ответственности за любые недоразумения или неправильные интерпретации, возникающие в результате использования данного перевода.