На этом шаге мы рассмотрим особенности использования пакетной нормализации.
Нормализация - это широкая категория методов, стремящихся сделать сходство разных образцов более заметным для модели машинного обучения, что помогает модели выделять и обобщать новые данные. Вы уже несколько раз видели наиболее распространенную форму нормализации: центрирование данных по нулю вычитанием среднего значения и придание единичного стандартного отклонения делением на их стандартное отклонение. Фактически такая нормализация предполагает, что данные соответствуют нормальному закону распределения (или закону Гаусса), центрируя и приводя это распределение к единичной дисперсии:
normalized_data = (data - np.mean(data, axis=...)) / np.std(data, axis=...)
В предыдущих примерах нормализация выполнялась перед передачей данных в модели. Однако нормализация должна проводиться после каждого преобразования, выполняемого сетью: даже если данные на входе в сеть Dense или Conv2D имеют среднее значение 0 и единичную дисперсию, нет оснований полагать, что то же самое можно будет сказать в отношении данных на выходе.
Пакетная нормализация - это тип слоя (BatchNormalization в Keras), введенный в 2015 году Сергеем Йоффе и Кристианом Сегеди;
В оригинальной статье авторы утверждают, что пакетная нормализация работает за счет "уменьшения внутреннего ковариантного сдвига", но в действительности никто точно не знает, почему она способствует улучшению эффективности обучения. Есть разные гипотезы, но нет уверенности. Далее вы не раз убедитесь, что подобное положение дел характерно для многих вопросов глубокого обучения. Глубокое обучение - это не точная наука, а набор постоянно меняющихся инженерных практик, полученных опытным путем и сплетенных в единое целое ненадежными стереотипами. Иногда может казаться, что излагаемый материал говорит вам, как делать то или это, но не дает конкретного объяснения, почему это работает. Причина проста: мы сами этого не знаем. При наличии надежного объяснения мы обязательно его упоминаем. Пакетная нормализация не относится к таким случаям.
Эффект пакетной нормализации, по всей видимости, способствует распространению градиента - подобно остаточным связям - и, соответственно, делает возможным создание более глубоких сетей. Некоторые глубокие сети могут обучаться, только если включают в себя несколько слоев BatchNormalization. Например, слои пакетной нормализации широко используются во многих продвинутых архитектурах сверточных нейронных сетей, входящих в состав Keras (таких как ResNet50, EfficientNet и Xception).
Слой BatchNormalization можно использовать после любого слоя - Dense, Conv2D и т. д.:
x = ... # Поскольку выход слоя Conv2D нормализуется, слой не нуждается в собственном векторе смещения x = layers.Conv2D(32, 3, use_bias=False)(x) x = layers.BatchNormalization()(x)
Обычно мы рекомендуем размещать активацию предыдущего слоя после слоя пакетной нормализации (хотя это и спорно). Поэтому вместо приема, показанного в примере 9.4, желательно использовать подход из примера 9.5.
x = layers.Conv2D(32, 3, activation="relu")(x)
x = layers.BatchNormalization()(x)
# Обратите внимание на отсутствие функции активации здесь x = layers.Conv2D(32, 3, use bias=False)(x) x = layers.BatchNormalization()(x) # Активация применяется после слоя BatchNormalization x = layers.Activation("relu")(x)
Интуитивная подоплека такого выбора заключается в том, что при пакетной нормализации входные данные будут центрированы по нулю, при этом активация relu использует ноль как точку, где принимается решение о сохранении или отбрасывании активированных каналов, - и применение нормализации перед активацией максимизирует эффект relu. Однако такой способ упорядочения не особенно критичен, то есть, если выполнить свертку, затем активацию, а потом пакетную нормализацию, модель все равно будет обучаться и не обязательно покажет худшие результаты.
Применение пакетной нормализации имеет множество особенностей. Одна из них связана с дообучением: при дообучении модели, включающей слои BatchNormalization, рекомендуется замораживать эти слои (передавать им в атрибуте trainable значение False). Иначе они продолжат обновлять свое внутреннее среднее значение и дисперсию, что может помешать очень небольшим корректировкам, применяемым к окружающим слоям Conv2D.
Перейдем к следующему архитектурному шаблону: раздельной свертке по глубине.
На следующем шаге мы рассмотрим раздельную свертку по глубине.