Переобучение и регуляризация
- О чём эта тема
- Точность на обучающих и на тестовых данных (конспект 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\) ошибка на обучающих становится почти нулевой, а на проверочных — резко растёт (переобучение). Точки подобраны иллюстративно (не измерены).
Типичные причины переобучения: избыточное число весов относительно объёма обучающей выборки (модель обладает достаточной «ёмкостью», чтобы запомнить конкретные примеры), слишком долгое обучение (модель начинает подстраиваться под шум в данных) и непредставительная обучающая выборка.
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)\), чтобы сохранить средний масштаб сигнала:
где \(m_i\) — случайная величина, равная 1 с вероятностью \(1-p\) (нейрон активен) и 0 с вероятностью \(p\) (нейрон исключён на этом шаге). На каждом шаге обучения исключается свой случайный набор нейронов — сеть не может полагаться на конкретную комбинацию нейронов и вынуждена находить более устойчивые признаки.
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 нормализует вход слоя внутри каждого батча так, чтобы он имел нулевое среднее и единичную дисперсию, а затем позволяет сети восстановить нужный масштаб через два обучаемых параметра:
где \(\mu_B\), \(\sigma_B^2\) — среднее и дисперсия по текущему батчу, \(\varepsilon\) — малая константа для устойчивости при делении, а \(\gamma\), \(\beta\) — обучаемые параметры (масштаб и сдвиг), которые находятся тем же градиентным спуском (конспект 2, формула (2.5)), что и остальные веса сети. Смысл параметров \(\gamma,\beta\): без них слой был бы обязан всегда иметь нормализованный выход, что могло бы обеднить представление; с ними сеть может обучиться и обратно расширить масштаб, если это окажется полезным.
Нормализация входа стабилизирует обучение: без неё по мере прохождения сигнала через слои его масштаб и среднее могут сильно исказиться, что затрудняет выбор скорости обучения \(\alpha\), общей для всех весов сети.
from tensorflow.keras.layers import BatchNormalization model.add(BatchNormalization())
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:
Разметку масштабируют несколькими путями: краудсорсинг (платформы вроде 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
Контрольные вопросы
-
При недообучении ошибка велика уже на обучающих данных (модель слишком простая). При переобучении ошибка на обучающих данных мала, а на данных, не участвовавших в обучении, — существенно выше (модель слишком сложная и запомнила обучающие примеры).
-
Ошибка на обучающей выборке продолжает убывать, а ошибка на проверочной выборке в какой-то момент начинает расти — это и есть точка, после которой обучение стоит остановить.
-
EarlyStopping останавливает обучение, если отслеживаемая метрика (обычно val_loss) не улучшается. patience задаёт число эпох подряд без улучшения, которое нужно выдержать перед остановкой.
-
Вероятность исключить нейрон на данном шаге обучения, а не вероятность его оставить. Dropout(0.2) исключает в среднем 20% нейронов слоя на каждом шаге.
-
Batch normalization нормализует масштаб (среднее и дисперсию) входа слоя внутри батча для стабильности обучения — это не связано со случайным исключением нейронов, которым занимается Dropout; механизмы решают разные задачи и часто используются вместе.
-
Аугментация — способ расширить обучающую выборку. Проверочная и тестовая выборки должны отражать реальные данные, на которых будет использоваться модель, а не искусственно изменённые примеры.