Нейронные сети
Классические алгоритмы хорошо работают с признаками, сформулированными человеком, будь то размер пучка, ток корректора или энергия события. Однако если признаки необходимо извлекать из сырого сигнала, из снимка, полученного камерой, из осциллограммы или спектра, их приходится конструировать вручную, и качество ограничивается изобретательностью физика. Нейронные сети выучивают признаки самостоятельно.
В настоящей главе рассматриваются математическая модель нейрона и функции активации, построение из нейронов многослойной сети и её обучение методом обратного распространения ошибки (backpropagation), а далее — оптимизаторы (SGD, Adam, RMSProp), нормализация данных и весов и регуляризация, позволяющая бороться с переобучением. Материал, изложенный здесь в одной главе, подробно рассмотрен у Гудфеллоу [28], а доведён до работающего кода — у Жерона [29].
1. Нейрон и функции активации
Математический нейрон
Математический нейрон подобен биологическому: он принимает входные сигналы, взвешивает их и пропускает полученную сумму через выбранную функцию активации.
Рассмотрим пример, в котором по трём признакам необходимо предсказать, сдаст ли студент экзамен.
- \(X_1\) показывает, задавал ли студент вопросы на лекциях (1/0).
- \(X_2\) показывает, попал ли он к лектору на экзамене (1/0).
- \(X_3\) показывает, сдал ли он лабораторные работы (1/0).
Логика задачи следующая.
- Если студент попал к лектору, то он сдаст экзамен только при условии, что задавал вопросы и сдал лабораторные работы.
- Если студент не попал к лектору, то для сдачи достаточно либо задавать вопросы, либо сдать лабораторные работы.
Модель нейрона вычисляет взвешенную сумму входов. $$S = W_1X_1 + W_2X_2 + W_3X_3$$
Выход нейрона \(Y\) определяется пороговой функцией активации. $$f(x) = \begin{cases} 1, & x \ge 0.5 \ 0, & x < 0.5 \end{cases}$$
Для такой простой логики подходят веса \(W_1 = 0.5, W_2 = -0.5, W_3 = 0.5\). Тогда для входа \((1, 1, 1)\) получаем \(0.5 \cdot 1 - 0.5 \cdot 1 + 0.5 \cdot 1 = 0.5 \ge 0.5 \rightarrow\) сдал (1).
Проблема одного слоя
Один слой проводит в пространстве признаков единственную разделяющую плоскость. Поэтому задачу, в которой ответ зависит от комбинации признаков, а не от каждого по отдельности (например, экзамен сдают те, кто либо задавал вопросы, либо сдал лабораторные работы, но не то и другое одновременно — это XOR, и никакая прямая \(W_1X_1 + W_3X_3 = c\) такие классы не разделит), персептрон не решает.
Скрытые слои
Скрытые слои располагаются между входом и выходом. Слой \(Z\) строит новое признаковое пространство, в котором прежде неразделимая задача решается одной плоскостью, проведённой уже в нём. $$Z_1 = f(W_{11,1}X_1 + W_{12,1}X_2 + \dots)$$ $$Y_1 = f(W_{21,1}Z_1 + W_{22,1}Z_2 + \dots)$$
Функции активации
Для обучения сети методом градиентного спуска функции активации должны быть дифференцируемы.
- Пороговая функция. Она ближе всего к биологической модели, но для обучения не подходит: в точке порога она не дифференцируема, а всюду вне её производная равна нулю, и градиент отсутствует.
- Сигмоида: Дифференцируемая функция. $$\sigma(x) = \frac{1}{1 + e^{-x}}$$ Производная сигмоиды имеет следующий вид. $$\frac{d\sigma(x)}{dx} = \sigma(x)(1 - \sigma(x))$$
2. Обучение нейронной сети
Обучение одного нейрона
Обучение одного нейрона с сигмоидальной функцией активации ничем не отличается от обучения логистической регрессии, а обучаемыми параметрами здесь являются веса \(W\).
Метод обратного распространения ошибки (Backpropagation)
Для пересчёта весов в многослойной сети необходимо знать ошибку, приписанную каждому нейрону; для выходного слоя \(Y_1\) она вычисляется непосредственно, а для скрытых слоёв \(Z\) требуется метод обратного распространения.
Цепное правило (Chain Rule): $$\frac{dz}{dx} = \frac{dz}{dy} \cdot \frac{dy}{dx}$$
Пример вычисления градиента: Пусть задана функция \(f(x, y, z) = (x + y)z\).
- \(q = x + y\), откуда \(\frac{dq}{dx} = 1, \frac{dq}{dy} = 1\).
- \(f = zq\), откуда \(\frac{df}{dq} = z, \frac{df}{dz} = q\).
- Итоговые градиенты дают \(\frac{df}{dx} = \frac{df}{dq} \cdot \frac{dq}{dx} = z \cdot 1\).
Обновление весов: Ошибкой называется разность между полученным выходом (actual) и ожидаемым (expected). $$err = actual - expected$$ Для сигмоиды градиент веса включает производную функции активации. $$weights_delta = err \cdot \sigma(x) \cdot (1 - \sigma(x))$$ Обновление веса с learning rate (\(lr\)) записывается следующим образом. $$weight_{new} = weight_{old} - output \cdot weights_delta \cdot lr$$
Ошибка для скрытых слоёв распределяется пропорционально весам, связывающим их с последующим слоем. $$err_{hidden} = weight \cdot weights_delta$$
Проблемы сигмоиды
Сигмоида дифференцируема и удобна, однако у неё имеются три недостатка, из-за которых в глубоких сетях её практически вытеснил ReLU.
- На хвостах производная близка к нулю, и в глубокой сети градиент затухает, не доходя до нижних слоёв.
- Выход всегда положителен, поэтому градиенты весов одного нейрона, смещённые в одну сторону, приводят к зигзагообразному ходу обучения.
- Вычисление экспоненты является дорогостоящим.
3. Оптимизаторы
Для минимизации функции потерь, выбранной под задачу, применяются различные варианты градиентного спуска.
SGD (Stochastic Gradient Descent)
Базовый метод записывается следующим образом. $$x_{t+1} = x_t - \alpha f'(x_t)$$ где \(\alpha\) — learning rate.
SGD with Momentum
Накапливает инерцию движения, сглаживая колебания. $$v_{t+1} = \rho v_t + \alpha f'(x_t)$$ $$x_{t+1} = x_t - v_{t+1}$$
Nesterov Momentum
Версия momentum, вычисляющая градиент в точке, в которую инерция сдвинет параметры, и потому реже проскакивающая минимум. $$v_{t+1} = \rho v_t - \alpha f'(x_t + \rho v_t)$$ $$x_{t+1} = x_t + v_{t+1}$$
Adagrad (Adaptive Gradient)
Адаптирует learning rate для каждого параметра индивидуально, вследствие чего веса, сдвинутые сильно, изменяются меньше. $$cache_{t+1} = cache_t + f'(x_t)^2$$ $$x_{t+1} = x_t - \frac{\alpha f'(x_t)}{\sqrt{cache_{t+1}} + \epsilon}$$ Проблема: \(cache \to \infty\), что может остановить обучение.
RMSProp
Сглаживает изменения кеша, забывая старые значения (экспоненциальное скользящее среднее). $$cache_{t+1} = \beta cache_t + (1 - \beta) f'(x_t)^2, \quad \beta \in [0, 1]$$ $$x_{t+1} = x_t - \frac{\alpha f'(x_t)}{\sqrt{cache_{t+1}} + \epsilon}$$
Adam
Комбинация RMSProp и Momentum записывается следующим образом. $$v_{t+1} = \gamma v_t + (1 - \gamma) f'(x_t)$$ $$cache_{t+1} = \beta cache_t + (1 - \beta) f'(x_t)^2$$ Оба накопителя стартуют с нуля, поэтому на первых шагах они занижены, и перед их использованием вводится поправка на смещение. $$\hat{v}{t+1} = \frac{v{t+1}}{1 - \gamma^{t+1}}, \qquad \widehat{cache}{t+1} = \frac{cache{t+1}}{1 - \beta^{t+1}}$$ Обновление выполняется уже по исправленным величинам; без этой поправки Adam сводится к RMSProp, дополненному инерцией. $$x_{t+1} = x_t - \frac{\alpha \hat{v}{t+1}}{\sqrt{\widehat{cache}{t+1}} + \epsilon}$$ В качестве отправной точки обычно выбирают Adam или Nesterov momentum: с ними сеть, собранная из типовых слоёв, в большинстве случаев обучается без ручного подбора.
4. Нормализация и инициализация
Нормализация данных
Признаки перед подачей в сеть приводятся к сопоставимым масштабам, и причин тому три.
- Технические ограничения точности дробных чисел.
- Без нормализации страдают L1 и L2 регуляризация.
- Ускоряется обучение. В нейронных сетях это критично, так как каждый слой отображает данные в новое пространство признаков, и при изменении весов смещается распределение признаков, получаемых на выходе слоя.
Инициализация весов
Два очевидных способа заполнить веса перед обучением не работают.
- Нули: сеть не обучается, все нейроны слоя одинаковы и получают один и тот же градиент.
- Константа: то же самое, слой вырождается в один нейрон.
- Случайные значения: работают, однако масштаб необходимо подобрать так, чтобы слой не изменял дисперсию проходящего через него сигнала.
Для сохранения дисперсии выведем соотношение, связывающее веса линейного слоя.
$$D(s) = D\left(\sum_{i}^{N} w_i x_i\right) = \sum_{i}^{N} D(w_i x_i) = n D(w) D(x)$$
Чтобы \(D(s) = D(x)\), необходимо выполнение условия
$$D(w) = \frac{1}{n}$$
Если взять случайные значения и умножить их на \(\frac{1}{\sqrt{n}}\) (или использовать распределение с дисперсией \(1/n\)), получается инициализация ЛеКуна, сохраняющая дисперсию только в прямом проходе, поскольку она учитывает лишь число входов. Инициализация Ксавье (Glorot и Bengio) выравнивает прямой и обратный проходы одновременно и использует \(D(w) = 2/(n_{in} + n_{out})\). Для ReLU половина выходов обнуляется, поэтому дисперсия весов берётся вдвое больше; такой вариант называется инициализацией Хе (He), и именно он применяется для слоёв с ReLU. По умолчанию фреймворки используют другое: Keras у Dense и Conv2D — glorot_uniform (Ксавье), PyTorch у nn.Linear и nn.Conv2d — kaiming_uniform_(a=√5). Следовательно, инициализацию под ReLU обычно приходится задавать явно.
Batch Normalization
Нормирует активации, вычисленные внутри одного батча; в исходной работе слой располагали перед функцией активации, чтобы распределение входов, приходящих в каждый слой, не смещалось по мере обучения (internal covariate shift). Объяснение, предложенное авторами, впоследствии было оспорено, однако слой работает.
Формула нормализации имеет следующий вид. $$\hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \varepsilon}}$$ где \(\mu\) и \(\sigma^2\) — матожидание и дисперсия в батче.
Далее применяется масштабирующее преобразование с обучаемыми параметрами \(\gamma, \beta\). $$y_i = \gamma \hat{x}_i + \beta$$
На инференсе, когда сеть только вычисляет предсказание, батча может не быть, поэтому вместо статистик батча используются скользящие средние, накопленные при обучении. $$\mu_{t+1} = \alpha \times \mu_{batch} + (1 - \alpha) \mu_t$$ $$\sigma^2_{t+1} = \alpha \times \sigma^2_{batch} + (1 - \alpha) \sigma^2_t$$
Batch normalization ускоряет сходимость сети и позволяет использовать больший learning rate.
5. Регуляризация
L1 и L2 регуляризация
Штраф, добавленный к функции потерь за большие веса, позволяет бороться с переобучением.
- L2: \(|W|_2^2 = \sum w^2\)
- L1: \(|W|_1 = \sum |w|\)
- Elastic Net: Комбинация L1 и L2.
Dropout
Случайным образом «выключает» часть нейронов в процессе обучения, причём на каждой итерации выключаются разные нейроны.
- Интерпретация: Уменьшение количества признаков.
- На инференсе. Все нейроны включены, и масштаб сигнала должен совпадать с обучением. В исходной работе для этого выходы домножались на долю оставленных нейронов уже при выводе; в современных реализациях поступают наоборот — при обучении делят на эту долю (inverted dropout), поэтому
model.eval()в PyTorch иtraining=Falseв Keras просто отключают слой без дополнительных пересчётов.
Residual Connection (Остаточные связи)
В очень глубоких сетях градиент может не доходить до нижних слоёв (становиться очень малым), поэтому его «проталкивают» туда сложением признаков, взятых в обход нескольких слоёв (skip connection).
Аугментация
Искусственное расширение обучающей выборки даёт те же примеры, повёрнутые, сдвинутые и зашумлённые, вследствие чего сеть перестаёт опираться на случайные особенности предъявленных ей изображений, и качество растёт как на тестовой выборке, так и на реальных данных.
6. Заключение
Таким образом, модель нейрона, прохождение градиента через сеть, различия оптимизаторов, нормализация и регуляризация представляют собой минимум, без которого работа с готовыми фреймворками превращается в перебор настроек наугад.
Расширенная версия раздела, включая задачи компьютерного зрения (Computer Vision), — книга «Базовые методы ИИ в физических исследованиях». В следующей главе эти модели обучаются средствами scikit-learn: интерфейс у них общий, а готовые рецепты для сетей прямого распространения собраны в отдельном сборнике.