Любое вычисление в нейронной сети можно представить как направленный ациклический граф. Проход вперед по нему дает значение функции $f(x)$. Проходом назад называют вычисление градиентов от функции потерь по весам
Пусть нейросеть представляет функцию $f(x) = (x + w_1) \cdot w_2$. Проходом вперед называют непосредственное вычисление:
Пусть функция потерь равна $\mathcal{L} = \frac{1}{2} (y - \hat y)^2$, где $y$ - известное значение для $x$, а $\hat y = f(x)$
Обратным проходом, то есть обратным распространение ошибки (back propagation), называют вычисление градиентов для параметров с помощью цепного правила дифференцирования сложных функций:
Возьмем другой пример - $\hat y = z^{(2)}$, $z^{(2)} = W^{(2)} h^{(1)} + b^{(2)}$, $h^{(1)} = \mathrm{ReLU}(z^{(1)})$, $z^{(1)} = W^{(1)} x + b^{(1)}$, функция потерь равна $\mathcal{L} = \frac{1}{2} (y - \hat y)^2$
Тогда градиенты равны:
Как и с другими методами машинного обучения, нейросети подвержены переобучению - ситуации, в которой функция потерь для тренировочных данных низка, а для тестовых данных высока, то есть модель не запомнила общие закономерности, а просто подстраивается под тренировочные данные и при этом сильно реагирует на шумы
Для борьбы с переобучением существуют такие методы регуляризации:
Остановка обучения
Сохраняем ту модель, у которой минимальная функция потерь (или максимальная аккуратность) для тестовых данных. Если функция потерь для тестовых данных растет заданное число эпох, то скорее всего происходит переобучение, и процесс обучение можно останавливать
L2-регуляризация (Weight Decay)
Вводим штраф за большие веса для функции потерь: $\mathcal{L}^\prime = \mathcal{L} + \frac{\lambda}{2} \sum_l | W^{(l)} |^2$
Градиент тогда будет равным $\frac{\partial \mathcal{L}^\prime}{\partial W^{(l)}} = \frac{\partial \mathcal{L}}{\partial W^{(l)}} + \lambda W^{(l)}$
Исключение или дропаут (Dropout)
При таком подходе некоторая часть (например, 30%) нейронов не участвует в обучении при вычислении на разных эпохах - в результате более обученные нейроны получают в сети больший вес
Дропаут значительно увеличивает скорость и качество обучения. При предсказании данных с помощью нейросети дропаут не используется, тем самым используются все нейроны
Дополнение данных (Data augmentation)
Дополнение данных применяется для расширения изначального датасета производными данными. Для изображений, например, используются повороты, обрезка, сдвиги, изменение яркости, контраста и другие фильтры. Для текста это могут быть переформулировка фраз, исключение слов и так далее
Так модель видит больше разнообразных данных и труднее переобучается на шум
Обрезка градиента (Gradient Clipping)
Обрезка градиента ограничивает градиент до заданного числа $\lambda$, что позволяет решить проблему взрывающегося градиента (особенно в рекуррентных нейросетях)
Обрезка работает так: \(w_{n + 1} = \begin{cases}w_n - \eta \frac{\partial \mathcal{L}}{\partial w_{n}}, & \|\frac{\partial \mathcal{L}}{\partial w_{n}}\| < \lambda \\ w_n - \eta \lambda \frac{\frac{\partial \mathcal{L}}{\partial w_{n}}}{\left\| \frac{\partial \mathcal{L}}{\partial w_{n}} \right\|}, & \|\frac{\partial \mathcal{L}}{\partial w_{n}}\| \geq \lambda\end{cases}\)
Также можно использовать адаптивную обрезку: \(w_{n + 1} = \begin{cases}w_n - \eta \frac{\partial \mathcal{L}}{\partial w_{n}}, & \frac{\|\frac{\partial \mathcal{L}}{\partial w_{n}}\|}{\|w\|} < \lambda \\ w_n - \eta \lambda \|w\| \frac{\frac{\partial \mathcal{L}}{\partial w_{n}}}{\left\| \frac{\partial \mathcal{L}}{\partial w_{n}} \right\|}, & \frac{\|\frac{\partial \mathcal{L}}{\partial w_{n}}\|}{\|w\|} \geq \lambda\end{cases}\)
Источник: https://deepmachinelearning.ru/docs/Neural-networks/Training-simplification/Gradient-clipping
Также применяют нормализацию - приведение к одному масштабу значение нейрона. Без нормализации распределение значений нейронов по слоям могут сильно меняться, обучение может проходить нестабильно и медленно, а градиенты могут взрываться (становится слишком большими) или затухать (становится слишком маленькими). Выделяют:
Пакетную нормализацию (Batch Normalization) - для пакета (батча) объектов из выборки вычисляют значения конкретного нейрона, далее от этих значений считается среднее и отклонение, чтобы затем масштабировать значения этого нейрона, то есть $\hat z_i = \frac{z_i - \mu}{\sqrt{\sigma^2 + \varepsilon}}$, где $\varepsilon$ - близкое к 0 число (например, $10^{-5}$)
Обучение небольшими пакетами позволяют параллельно вычислять значения нейронов
После пакетной нормализации обычно применяют канальное масштабирование $y_i = \gamma z_i + \beta$, где $\gamma$ и $\beta$ - обучаемые параметры, а под каналом подразумевается один или несколько нейронов
Нормализация слоев (Layer Normalization) работает аналогично пакетной, только значения нейронов масштабируются относительно среднего и отклонения значений всех нейронов слоя
Огромную роль имеет первоначальный выбор значения весов нейронов, чаще всего используют такие подходы:
Метод инициализации Xavier, предложенный Ксавье Глоро и Йошуа Бенджио в 2010, предполагает инициализировать веса как $w_i \in U\left(-\frac{\sqrt{6}}{\sqrt{f_{\text{in}} + f_{\text{out}}}}, \frac{\sqrt{6}}{\sqrt{f_{\text{in}} + f_{\text{out}}}}\right)$ или как $w_i \in N\left(0, \frac{2}{f_{\text{in}} + f_{\text{out}}}\right)$, где $f_{\text{in}}$ - размерность предыдущего слоя, а $f_{\text{out}}$ - размерность этого
В результате этого подхода дисперсия значений на входе и на выходе слоя совпадает
Такой метод отлично подходит для симметричных функций активации (например, гиперболический тангенс), но плохо для таких, как ReLU
Метод инициализации He, предложенный Каймингом Хе в 2015, предполагает инициализировать веса как $w_i \in N\left(0, \frac{2}{f_{\text{in}}}\right)$
Такой метод хорошо работает с ReLU, LeakyReLU, GELU и другими несимметричными функциями
Для упрощения обучения используют практики непрерывной интеграции и непрерывного развертывания (CI/CD):
Версионирование
Версионируются все, что можно: код модели, препроцессинга, обучения (обычно с помощью git), сами данные (обычно с помощью DVC - Data Version Control), обученные модели, конфигурации
Непрерывная интеграция
Непрерывная интеграция состоит из сборки модели, тестировании кода модели и данных, а также публикации артефакта - обученной модели
Непрерывное развертывание
Непрерывное развертывание автоматически запускает обученную модель в продакшн
Для этого используют разные утилиты:
TenserBoard - визуализационная панель для фреймворка TenserFlow и других. Позволяет визуализировать процесс обучения, графики функции потерь, метрик качества, осуществлять мониторинг обучения без остановки процесса
MLflow - платформа с открытым кодом для управления жизненным циклом обучения, а именно логирование, централизованное хранение, универсальная упаковка кода и параметров для воспроизводимости, развертывание моделей
Weights & Biases (W&B) - платформа, похожая на MLflow, но имеющая интерактивный интерфейс и прост для коллаборации