26 Batch Normalization
Контекст
Глубокие сети чувствительны к инициализации и learning rate; распределение входов каждого слоя «плывёт» по мере обучения предыдущих, замедляя сходимость. Иоффе и Сегеди дают на это лекарство.
Идея и механизм
Для каждого мини-батча нормируем вход слоя по каждому признаку, затем масштабируем и сдвигаем обучаемыми γ, β (чтобы сеть при желании вернула любое распределение). На инференсе вместо статистик батча используем бегущие средние, накопленные при обучении.
оптимизация Преобразование BN и спор о том, почему оно работает
Для мини-батча B по каждому признаку считаем среднее и дисперсию, нормируем и применяем обучаемые параметры:
Обучаемые γ, β важны: без них слой не смог бы при необходимости восстановить полезное распределение — нормировка не должна обеднять представление.
Спор о причине. Авторы объясняли эффект уменьшением «internal covariate shift» (дрейфа распределений между слоями). Позже MIT-работа (2018) это оспорила: измерения показали, что BN не столько убирает дрейф, сколько сглаживает ландшафт потерь (делает градиенты предсказуемее), отчего и можно брать больший шаг. Так что цитируйте BN как технику-веху, но осторожно с её исходным объяснением.
NumPy Forward Batch Normalization
import numpy as np
def batchnorm(x, gamma, beta, eps=1e-5): # x: (batch, features)
mu = x.mean(0)
var = x.var(0)
xhat = (x - mu) / np.sqrt(var + eps) # нормировка по батчу
return gamma * xhat + beta # обучаемые масштаб/сдвиг
# на инференсе mu, var берут как бегущие средние с обучения
Почему это важно
Стало почти обязательным в CNN; ускорило обучение глубоких сетей в разы (та же точность за ~14× меньше шагов) и снизило зависимость от инициализации. В трансформерах чаще LayerNorm (нормировка по признакам одного примера, не по батчу) — но идея «нормируй активации» оттуда же.
Связи
ResNet ставит BatchNorm в каждый остаточный блок. Вместе skip-связи и BN — две опоры, на которых стоят очень глубокие сети: одна спасает градиент, другая стабилизирует активации.
Оба регуляризуют/стабилизируют, но по-разному, и плохо уживаются: BN полагается на статистику батча, а dropout её зашумляет. С приходом BN и больших данных dropout в свёрточных сетях заметно потеснили.
Трансформеры используют не BatchNorm, а LayerNorm — нормировку по признакам одного примера. Причина: в NLP батчи разной длины и зависимость от статистики батча вредна; нормировка «внутри примера» устойчивее.
Вопросы пытливого ума
Если объяснение через «covariate shift» неверно, почему BN всё равно так помогает?
Потому что польза реальна, а вот её механизм оказался другим. По данным MIT-работы, BN делает ландшафт потерь глаже — градиенты становятся стабильнее и предсказуемее, что и позволяет больший learning rate и быструю сходимость. Эффект тот же, причина — не та, что заявляли авторы. Классический случай «работает, но не по той теории».
Почему BN плохо работает при маленьком размере батча?
Статистики μ и σ оцениваются по батчу; на батче из 2–4 примеров они шумные и нестабильные, и нормировка начинает вредить. Для таких случаев придумали альтернативы, не зависящие от батча: LayerNorm, GroupNorm, InstanceNorm. Зависимость от размера батча — главная практическая слабость BatchNorm.
BN ведёт себя по-разному на обучении и инференсе — это не источник багов?
Ещё какой. На обучении используются статистики текущего батча, на инференсе — накопленные бегущие средние. Забыть переключить модель в eval-режим — классическая ошибка, дающая «плавающие» предсказания. И если распределение на инференсе отличается от обучающего, бегущие средние могут не подойти. Двойной режим BN — известный источник тонких багов.
Что читать в оригинале
Конспекта + ключевых мест достаточно: само преобразование и режимы train/inference. Про «почему работает» полезнее прочитать критику (MIT 2018), чем исходное объяснение.