Траектория «Нейросети» · конспект 5 из 12

Переобучение и регуляризация

О чём эта тема
Точность на обучающих и на тестовых данных (конспект 3) — разные числа, и иногда очень по-разному ведут себя при обучении. Разбирается, почему так происходит, и какими средствами (ранняя остановка, Dropout, batch normalization, аугментация данных) с этим справляются.
Аннотация
Конспект начинается с определений недообучения и переобучения и разбора того, как в этих ситуациях ведут себя ошибки на обучающей и проверочной выборках. Затем рассматривается механизм callbacks в Keras — EarlyStopping, ModelCheckpoint и LearningRateScheduler — позволяющий вмешиваться в обучение по ходу процесса. Далее вводятся два специальных слоя для борьбы с переобучением: Dropout, случайно отключающий нейроны при обучении, и batch normalization, нормализующий вход слоя; отдельно обсуждается, почему их сочетание требует осторожности. Завершается конспект разговором о данных: откуда берётся ручная разметка и почему она дорога, и аугментация — искусственное увеличение обучающей выборки за счёт преобразований исходных изображений.
Пререквизиты
Конспект 3 (compile/fit, epochs, accuracy на обучающих/тестовых данных), конспект 4 (метрики качества).
Мотивация
В конспекте 3 точность на обучающих данных и на тестовых оценивались отдельно — с явной оговоркой, что высокая точность на обучающих данных «почти ничего не говорит о качестве модели на новых, невиданных примерах». Эта тема объясняет, почему так происходит, и что можно с этим сделать ещё на этапе обучения, а не только после него.

1. Недообучение и переобучение

Переобучение (англ. overfitting) — явление, при котором ошибка модели на объектах, не участвовавших в обучении, оказывается существенно выше, чем ошибка на объектах, участвовавших в обучении. Возникает при слишком сложной модели (много весов) относительно объёма и разнообразия обучающих данных: модель запоминает конкретные примеры вместо того, чтобы находить закономерность, которая обобщается на новые данные.

Недообучение (англ. underfitting) — обратная ситуация: ошибка модели велика уже на самих обучающих данных. Возникает при слишком простой модели, неспособной уловить даже ту зависимость, что есть в обучающей выборке.

Тренажёр: недообучение, оптимум, переобучение
обучающие точки проверочные точки

По 11 обучающим точкам подбирается полином степени \(d\) методом наименьших квадратов (конспект 1, формула (1.4), обобщённая на полином вместо прямой). Двигайте степень: при малых \(d\) велика ошибка и на обучающих, и на проверочных точках (недообучение); при больших \(d\) ошибка на обучающих становится почти нулевой, а на проверочных — резко растёт (переобучение). Точки подобраны иллюстративно (не измерены).

Типичная ошибка Считают, что чем ниже ошибка на обучающих данных, тем лучше модель. В тренажёре выше при степени полинома 9 ошибка на обучающих точках почти нулевая, но на проверочных — одна из самых высоких на всём графике. Судить о качестве модели нужно по ошибке на данных, не участвовавших в обучении.

Типичные причины переобучения: избыточное число весов относительно объёма обучающей выборки (модель обладает достаточной «ёмкостью», чтобы запомнить конкретные примеры), слишком долгое обучение (модель начинает подстраиваться под шум в данных) и непредставительная обучающая выборка.

2. Остановка и управление обучением: callbacks

При переобучении ошибка на обучающих данных обычно продолжает падать с ростом числа эпох, а ошибка на проверочной выборке в какой-то момент начинает расти:

График ошибки от числа итераций: кривая проверочной выборки после точки «окончание обучения» начинает расти, кривая обучающей выборки продолжает убывать
После точки «окончание обучения» ошибка на проверочной выборке растёт, хотя на обучающей — продолжает убывать

EarlyStopping останавливает обучение, когда отслеживаемая метрика (обычно val_loss) не улучшается на протяжении заданного числа эпох:

from tensorflow.keras.callbacks import EarlyStopping

early_stop = EarlyStopping(
    monitor='val_loss',
    min_delta=0,
    patience=3,
    restore_best_weights=True
)

patience=3 — сколько эпох подряд без улучшения нужно потерпеть перед остановкой; restore_best_weights=True возвращает веса той эпохи, где val_loss был минимален, а не веса последней (уже переобученной) эпохи.

ModelCheckpoint сохраняет модель на диск при улучшении отслеживаемой метрики — полезно само по себе, а не только вместе с EarlyStopping:

from tensorflow.keras.callbacks import ModelCheckpoint

checkpoint = ModelCheckpoint(
    filepath,
    monitor='val_loss',
    mode='min',
    save_best_only=True
)

LearningRateScheduler изменяет скорость обучения \(\alpha\) (конспект 2, формула (2.5)) по ходу обучения согласно заданной функции от номера эпохи — например, уменьшает её со временем, что часто помогает точнее сойтись к минимуму на поздних эпохах:

from tensorflow.keras.callbacks import LearningRateScheduler

callback = LearningRateScheduler(scheduler)

Все callbacks передаются в fit списком:

history = model.fit(X_train, y_train,
                    validation_data=(X_test, y_test),
                    epochs=50, batch_size=32,
                    callbacks=[early_stop, checkpoint, callback])
Типичная ошибка Ставят patience равным 0 или 1, ожидая немедленной реакции на любое ухудшение. Проверочная ошибка колеблется от эпохи к эпохе даже у нормально обучающейся сети; слишком маленькое patience останавливает обучение из-за случайного колебания, а не из-за настоящего начала переобучения.

3. Dropout

Dropout — способ регуляризации, при котором на каждом шаге обучения каждый нейрон слоя с вероятностью \(p\) («rate») временно исключается из сети: его выход обнуляется, вместе со всеми исходящими из него связями на этом шаге. Оставшиеся выходы масштабируются на \(1/(1-p)\), чтобы сохранить средний масштаб сигнала:

\[ \tilde h_i = \frac{m_i}{1-p}\,h_i, \qquad m_i \sim \mathrm{Bernoulli}(1-p) \tag{5.1}\]

где \(m_i\) — случайная величина, равная 1 с вероятностью \(1-p\) (нейрон активен) и 0 с вероятностью \(p\) (нейрон исключён на этом шаге). На каждом шаге обучения исключается свой случайный набор нейронов — сеть не может полагаться на конкретную комбинацию нейронов и вынуждена находить более устойчивые признаки.

Слева полносвязная сеть без Dropout; справа та же сеть с тремя случайно отключёнными нейронами, отмеченными крестиком
Исключённые на данном шаге нейроны (отмечены крестиком) и их связи временно не участвуют в прямом и обратном проходе
from tensorflow.keras.layers import Dropout

model.add(Dropout(0.2))

Dropout(0.2) ставится сразу после слоя, выход которого нужно проредить (как в Conv2D из конспекта 4); значение 0.2 означает, что каждый нейрон исключается с вероятностью 20% на каждом шаге обучения.

Типичная ошибка Путают, что означает rate: это вероятность исключить нейрон, а не вероятность его оставить. Dropout(0.8) — это агрессивное прореживание (исключается в среднем 80% нейронов), а не мягкое. При использовании сети (не при обучении) Dropout отключается автоматически — все нейроны активны, а слой ничего не масштабирует заново.

4. Batch normalization

Batch normalization нормализует вход слоя внутри каждого батча так, чтобы он имел нулевое среднее и единичную дисперсию, а затем позволяет сети восстановить нужный масштаб через два обучаемых параметра:

\[ \hat x = \frac{x - \mu_B}{\sqrt{\sigma_B^2 + \varepsilon}}, \qquad y = \gamma\hat x + \beta \tag{5.2}\]

где \(\mu_B\), \(\sigma_B^2\) — среднее и дисперсия по текущему батчу, \(\varepsilon\) — малая константа для устойчивости при делении, а \(\gamma\), \(\beta\) — обучаемые параметры (масштаб и сдвиг), которые находятся тем же градиентным спуском (конспект 2, формула (2.5)), что и остальные веса сети. Смысл параметров \(\gamma,\beta\): без них слой был бы обязан всегда иметь нормализованный выход, что могло бы обеднить представление; с ними сеть может обучиться и обратно расширить масштаб, если это окажется полезным.

Нормализация входа стабилизирует обучение: без неё по мере прохождения сигнала через слои его масштаб и среднее могут сильно исказиться, что затрудняет выбор скорости обучения \(\alpha\), общей для всех весов сети.

from tensorflow.keras.layers import BatchNormalization

model.add(BatchNormalization())
Типичная ошибка Считают batch normalization разновидностью Dropout или его заменой. Dropout случайно исключает нейроны для борьбы с переобучением; batch normalization нормализует масштаб сигнала для стабильности обучения — это разные механизмы.

4.1. Когда не стоит ставить Dropout перед BatchNormalization

Dropout и batch normalization не всегда стоит комбинировать в одном блоке без разбора порядка. Ли с соавторами показали, что если слой Dropout стоит непосредственно перед BatchNormalization, возникает так называемый сдвиг дисперсии (variance shift): во время обучения Dropout меняет дисперсию сигнала случайным образом от шага к шагу, а во время использования сети (когда Dropout не действует) статистика, накопленная BatchNormalization за всё обучение, этому изменившемуся масштабу уже не соответствует — это ухудшает точность на новых данных сильнее, чем можно было бы ожидать от каждого из этих слоёв по отдельности. Рекомендация из этой работы: если оба слоя нужны в одной сети, ставить весь Dropout после всех слоёв BatchNormalization, а не перед ними или между ними.

Типичная ошибка Ставят Dropout сразу перед BatchNormalization в одном и том же блоке (например, Conv2D → Dropout → BatchNormalization). Из-за сдвига дисперсии такой порядок может дать худший результат, чем использование только одного из этих двух слоёв.

5. Аугментация данных

5.1. Откуда берутся размеченные данные

До сих пор обучающие данные были готовыми: CIFAR-10 поставляется уже с метками. В реальной задаче метки приходится создавать — размечать данные вручную. Для изображений это делается в специализированных программах: разметчик обводит объекты и присваивает им классы. Одна из распространённых свободных программ — LabelMe:

Окно программы LabelMe: изображение с обведёнными полигонами фигурами и список меток классов справа
Разметка изображения полигонами в LabelMe. Из практикума курса

Разметку масштабируют несколькими путями: краудсорсинг (платформы вроде Amazon Mechanical Turk — быстро и доступно, но качество не гарантировано), аутсорсинг фрилансерам и полуавтоматическая разметка, когда предварительные метки расставляет уже обученная модель, а человек только исправляет её ошибки. Во всех случаях ручная разметка — самая дорогая часть подготовки данных: счёт идёт на человеко-часы за каждую тысячу примеров.

5.2. Искусственное расширение выборки

Именно из-за дороговизны разметки размеченных примеров обычно мало, и их число стараются увеличить искусственно: применить к исходным примерам преобразования, которые не меняют класс объекта, но меняют его представление в пикселях. Для изображений — это, например, отражение, поворот, добавление отступов, обрезка и добавление шума.

Тренажёр: аугментация изображения

Класс объекта (цифра «3») не меняется ни при одном из преобразований — это и есть условие, при котором аугментацию можно применять: результат остаётся правдоподобным примером того же класса.

В Keras такие преобразования можно добавить прямо в модель отдельными слоями, применяемыми только во время обучения:

from tensorflow.keras.layers import RandomFlip, RandomRotation

model = Sequential()
model.add(RandomFlip('horizontal'))
model.add(RandomRotation(0.05))
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu'))
# ... остальная часть сети, как в конспекте 4
Типичная ошибка Аугментируют проверочные и тестовые данные наравне с обучающими. Аугментация — способ расширить обучающую выборку; проверочная и тестовая выборки должны отражать реальные данные, на которых модель будет использоваться, без искусственных преобразований.

Контрольные вопросы