В предыдущих постах мы рассматривали классификацию отзывов к фильмам и прогнозирование цен на дома, в обоих случаях мы видели, что аккуратность нашей модели на валидационных данных достигает пика после тренировки ряда эпох и затем начинает уменьшаться.
Другими словами, наша модель переобучается (overfit) тренировочным данным. Умение разбираться с переобучением - это важный момент. Хотя довольно часто возможно достичь высокой аккуратности на тренировочном наборе, главной задачей является разработать модель, которая будет так же хорошо генерализоваться на тестовом наборе (показывать такой же уровень аккуратности на ранее не встречавшихся данных).
Противоположность переобучению - недообучение (underfitting). Недообучение происходит, когда все еще присутствует место для улучшения на тестовых данных. Это может случиться по ряду причин: если модель недостаточно сильная, либо слишком сильно регуляризована, либо просто недостаточно долго тренирована. Это означает что сеть необучилась соотвествующим паттернам в тренировочных данных.
Если тренировка, однако, проводится слишком долго, тогда модель начинает переобучаться и обучается паттернам из тренировочных данных, которые не генерализуются на тестовых данных. Необходимо добиться баланса.
Чтобы предупредить переобучение лучшим решением будет использовать больше тренировочных данных. Модель, тренированная на большем объеме данных, естественно будет генерализоваться лучше. Когда это недоступно (нет большего количества тренировочных данных), следующим лучшим решением будет использовать техники, такие как регуляризация. Они выставляют ограничения на количество и тип информации, которую может сохранять модель. Если сеть может позволить себе запоминать только малое количество паттернов, тогда оптимизационный процесс усилит это, чтобы сфокусировать модель на наиболее выдающихся паттернах, которые имеют больше шансов генерализоваться хорошо.
В этом посте мы рассмотрим две основные техники регуляризации - весовая регуляризация и дропаут (dropout)(отсев) - и используем их, чтобы улучшить модель классификации отзывов к фильмам.
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
print(tf.version)
1.9.0
Загрузка IMDB набора данных
Вместо использования встраиваний (embedding), как в предыдущих постах, здесь мы будем использовать многоразовое кодирование (multi-hot encode) предложений. Эта модель быстро переобучится (overfit) на тренировочном наборе. Она будет использоваться, чтобы демонстрировать случаи, когда происходит переобучение, и как бороться с этим.
Многоразовое кодирование наших листов означает преобразование их в вектора нулей и единиц. Конкретно, это означает, что экземпляр преобразует последовательность [3, 5] в вектор с 10 000 измерений, которые все будут нулями, исключая индексы для 3 и 5, которые будут единицами.
NUM_WORDS = 10000
(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)
def multi_hot_sequences(sequences, dimension):
# Создаем матрицу со нулями формы (len(sequences), dimension)
results = np.zeros((len(sequences), dimension))
for i, word_indices in enumerate(sequences):
# установим специфичные индексы results[i] равными 1
results[i, word_indices] = 1.0
return results
train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)
test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)
Downloading data from https://s3.amazonaws.com/text-datasets/imdb.npz
17465344/17464789 [==============================] - 2s 0us/step
Взглянем на один из результирующих многоразовых векторов. Индексы слов сортированы по частоте встречаемости, таким образом ожидаемо, что присутствуют больше 1-значений около нулевого индекса, как мы увидим в следующем графике:
plt.plot(train_data[0])
[]
Демонстрация переобучения
Простеший путь для предупреждения переобучения - это уменьшить размер модели, то есть количество изучаемых параметров в модели (которые определены количеством слоев и количеством элементов в слое). В глубоком обучении количество изучаемых параметров в модели часто обозначают как "вместимость" ("capacity") модели. Интуитивно модель с большим количеством параметров будет иметь большую "запоминающую вместимость" и поэтому будет способна легко обучаться превосходному картированию между тренировочными примерами и их целями, картированию без какой-либо способности к генерализации - это будет бесполезно для выполнения прогнозов на ранее не известных данных.
Всегда необходимо помнить, что модели глубокого обучения имеют тенденцию хорошо обучаться тренировочным данным, но главная трудность - это генерализация.
С другой стороны, если сеть имеет ограниченные ресурсы для запоминания, она будет не способна обучиться картированию легко. Чтобы уменьшить ее потерю, модели придется обучаться сжатым представлениям, которые имеют большую способность к прогнозированию. В то же время, если сделать модель слишком маленькой, ей будет трудно обучаться тренировочным данным. Существует баланс между "слишком большой емкостью" и "недостаточной емкостью".
К сожалению, не существует магической формулы, чтобы определить правильный размер или архитектуру модели (количество слоев или правильный размер каждого слоя). Необходимо экспериментировать, используя серии различных архитектур.
Чтобы найти подходящий размер модели лучше всего начать с относительно небольшого количества слоев и параметров, затем начать увеличивать размер слоев или добавлять новые слои до тех пор пока не появятся уменьшающиеся результаты в валидационной потере. Попробуем применить это к модели классификации отзывов к фильмам.
Создадим простую модель, используя только Dense слой как основание, затем создадим меньшие и большие версии и сравним их.
Создание модели-основания
baseline_model = keras.Sequential([
# input_shape требуется только здесь, чтобы .summary работал.
keras.layers.Dense(16, activation=tf.nn.relu,
input_shape=(NUM_WORDS,)),
keras.layers.Dense(16, activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy', 'binary_crossentropy'])
baseline_model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 16) 160016
_________________________________________________________________
dense_1 (Dense) (None, 16) 272
_________________________________________________________________
dense_2 (Dense) (None, 1) 17
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________
baseline_history = baseline_model.fit(train_data,
train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
- 4s - loss: 0.4830 - acc: 0.8082 - binary_crossentropy: 0.4830 - val_loss: 0.3383 - val_acc: 0.8758 - val_binary_crossentropy: 0.3383
Epoch 2/20
- 4s - loss: 0.2494 - acc: 0.9114 - binary_crossentropy: 0.2494 - val_loss: 0.2851 - val_acc: 0.8862 - val_binary_crossentropy: 0.2851
Epoch 3/20
- 4s - loss: 0.1816 - acc: 0.9372 - binary_crossentropy: 0.1816 - val_loss: 0.2894 - val_acc: 0.8850 - val_binary_crossentropy: 0.2894
Epoch 4/20
- 4s - loss: 0.1444 - acc: 0.9516 - binary_crossentropy: 0.1444 - val_loss: 0.3147 - val_acc: 0.8790 - val_binary_crossentropy: 0.3147
Epoch 5/20
- 4s - loss: 0.1184 - acc: 0.9613 - binary_crossentropy: 0.1184 - val_loss: 0.3409 - val_acc: 0.8747 - val_binary_crossentropy: 0.3409
Epoch 6/20
- 4s - loss: 0.0960 - acc: 0.9704 - binary_crossentropy: 0.0960 - val_loss: 0.3744 - val_acc: 0.8702 - val_binary_crossentropy: 0.3744
Epoch 7/20
- 4s - loss: 0.0766 - acc: 0.9788 - binary_crossentropy: 0.0766 - val_loss: 0.4213 - val_acc: 0.8624 - val_binary_crossentropy: 0.4213
Epoch 8/20
- 4s - loss: 0.0610 - acc: 0.9843 - binary_crossentropy: 0.0610 - val_loss: 0.4507 - val_acc: 0.8634 - val_binary_crossentropy: 0.4507
Epoch 9/20
- 4s - loss: 0.0472 - acc: 0.9901 - binary_crossentropy: 0.0472 - val_loss: 0.4909 - val_acc: 0.8617 - val_binary_crossentropy: 0.4909
Epoch 10/20
- 4s - loss: 0.0355 - acc: 0.9942 - binary_crossentropy: 0.0355 - val_loss: 0.5343 - val_acc: 0.8570 - val_binary_crossentropy: 0.5343
Epoch 11/20
- 4s - loss: 0.0270 - acc: 0.9963 - binary_crossentropy: 0.0270 - val_loss: 0.5752 - val_acc: 0.8584 - val_binary_crossentropy: 0.5752
Epoch 12/20
- 4s - loss: 0.0202 - acc: 0.9979 - binary_crossentropy: 0.0202 - val_loss: 0.6161 - val_acc: 0.8565 - val_binary_crossentropy: 0.6161
Epoch 13/20
- 4s - loss: 0.0148 - acc: 0.9989 - binary_crossentropy: 0.0148 - val_loss: 0.6524 - val_acc: 0.8562 - val_binary_crossentropy: 0.6524
Epoch 14/20
- 4s - loss: 0.0111 - acc: 0.9995 - binary_crossentropy: 0.0111 - val_loss: 0.6874 - val_acc: 0.8538 - val_binary_crossentropy: 0.6874
Epoch 15/20
- 4s - loss: 0.0084 - acc: 0.9998 - binary_crossentropy: 0.0084 - val_loss: 0.7186 - val_acc: 0.8536 - val_binary_crossentropy: 0.7186
Epoch 16/20
- 4s - loss: 0.0067 - acc: 0.9999 - binary_crossentropy: 0.0067 - val_loss: 0.7471 - val_acc: 0.8535 - val_binary_crossentropy: 0.7471
Epoch 17/20
- 4s - loss: 0.0053 - acc: 1.0000 - binary_crossentropy: 0.0053 - val_loss: 0.7722 - val_acc: 0.8536 - val_binary_crossentropy: 0.7722
Epoch 18/20
- 4s - loss: 0.0044 - acc: 1.0000 - binary_crossentropy: 0.0044 - val_loss: 0.7973 - val_acc: 0.8522 - val_binary_crossentropy: 0.7973
Epoch 19/20
- 4s - loss: 0.0037 - acc: 1.0000 - binary_crossentropy: 0.0037 - val_loss: 0.8185 - val_acc: 0.8536 - val_binary_crossentropy: 0.8185
Epoch 20/20
- 4s - loss: 0.0031 - acc: 1.0000 - binary_crossentropy: 0.0031 - val_loss: 0.8396 - val_acc: 0.8522 - val_binary_crossentropy: 0.8396
Создание меньшей модели
Создадим модель с меньшим количеством скрытых элементов, чтобы сравнить ее с моделью-основанием, которую мы создали ранее:
smaller_model = keras.Sequential([
keras.layers.Dense(4, activation=tf.nn.relu,
input_shape=(NUM_WORDS,)),
keras.layers.Dense(4, activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
smaller_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy', 'binary_crossentropy'])
smaller_model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_3 (Dense) (None, 4) 40004
_________________________________________________________________
dense_4 (Dense) (None, 4) 20
_________________________________________________________________
dense_5 (Dense) (None, 1) 5
=================================================================
Total params: 40,029
Trainable params: 40,029
Non-trainable params: 0
_________________________________________________________________
Тренируем модель, используя те же данные:
smaller_history = smaller_model.fit(train_data,
train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
- 4s - loss: 0.5875 - acc: 0.7590 - binary_crossentropy: 0.5875 - val_loss: 0.4841 - val_acc: 0.8534 - val_binary_crossentropy: 0.4841
Epoch 2/20
- 4s - loss: 0.3949 - acc: 0.8851 - binary_crossentropy: 0.3949 - val_loss: 0.3738 - val_acc: 0.8733 - val_binary_crossentropy: 0.3738
Epoch 3/20
- 4s - loss: 0.3001 - acc: 0.9069 - binary_crossentropy: 0.3001 - val_loss: 0.3222 - val_acc: 0.8838 - val_binary_crossentropy: 0.3222
Epoch 4/20
- 4s - loss: 0.2469 - acc: 0.9221 - binary_crossentropy: 0.2469 - val_loss: 0.2992 - val_acc: 0.8860 - val_binary_crossentropy: 0.2992
Epoch 5/20
- 4s - loss: 0.2129 - acc: 0.9320 - binary_crossentropy: 0.2129 - val_loss: 0.2901 - val_acc: 0.8862 - val_binary_crossentropy: 0.2901
Epoch 6/20
- 4s - loss: 0.1881 - acc: 0.9386 - binary_crossentropy: 0.1881 - val_loss: 0.2850 - val_acc: 0.8878 - val_binary_crossentropy: 0.2850
Epoch 7/20
- 4s - loss: 0.1689 - acc: 0.9461 - binary_crossentropy: 0.1689 - val_loss: 0.2862 - val_acc: 0.8854 - val_binary_crossentropy: 0.2862
Epoch 8/20
- 4s - loss: 0.1534 - acc: 0.9506 - binary_crossentropy: 0.1534 - val_loss: 0.2903 - val_acc: 0.8847 - val_binary_crossentropy: 0.2903
Epoch 9/20
- 4s - loss: 0.1412 - acc: 0.9550 - binary_crossentropy: 0.1412 - val_loss: 0.2964 - val_acc: 0.8828 - val_binary_crossentropy: 0.2964
Epoch 10/20
- 4s - loss: 0.1291 - acc: 0.9600 - binary_crossentropy: 0.1291 - val_loss: 0.3066 - val_acc: 0.8791 - val_binary_crossentropy: 0.3066
Epoch 11/20
- 4s - loss: 0.1193 - acc: 0.9646 - binary_crossentropy: 0.1193 - val_loss: 0.3139 - val_acc: 0.8790 - val_binary_crossentropy: 0.3139
Epoch 12/20
- 4s - loss: 0.1103 - acc: 0.9680 - binary_crossentropy: 0.1103 - val_loss: 0.3267 - val_acc: 0.8759 - val_binary_crossentropy: 0.3267
Epoch 13/20
- 4s - loss: 0.1022 - acc: 0.9712 - binary_crossentropy: 0.1022 - val_loss: 0.3347 - val_acc: 0.8755 - val_binary_crossentropy: 0.3347
Epoch 14/20
- 4s - loss: 0.0953 - acc: 0.9735 - binary_crossentropy: 0.0953 - val_loss: 0.3465 - val_acc: 0.8731 - val_binary_crossentropy: 0.3465
Epoch 15/20
- 4s - loss: 0.0883 - acc: 0.9762 - binary_crossentropy: 0.0883 - val_loss: 0.3589 - val_acc: 0.8712 - val_binary_crossentropy: 0.3589
Epoch 16/20
- 4s - loss: 0.0822 - acc: 0.9783 - binary_crossentropy: 0.0822 - val_loss: 0.3717 - val_acc: 0.8704 - val_binary_crossentropy: 0.3717
Epoch 17/20
- 4s - loss: 0.0767 - acc: 0.9811 - binary_crossentropy: 0.0767 - val_loss: 0.3864 - val_acc: 0.8688 - val_binary_crossentropy: 0.3864
Epoch 18/20
- 4s - loss: 0.0712 - acc: 0.9833 - binary_crossentropy: 0.0712 - val_loss: 0.4017 - val_acc: 0.8668 - val_binary_crossentropy: 0.4017
Epoch 19/20
- 4s - loss: 0.0659 - acc: 0.9857 - binary_crossentropy: 0.0659 - val_loss: 0.4142 - val_acc: 0.8666 - val_binary_crossentropy: 0.4142
Epoch 20/20
- 4s - loss: 0.0606 - acc: 0.9877 - binary_crossentropy: 0.0606 - val_loss: 0.4292 - val_acc: 0.8656 - val_binary_crossentropy: 0.4292
Создание большей модели
Создадим модель побольше и посмотрим как быстро она начнет переобучаться. Затем добавим к этому эталону сеть, которая имеет намного большую емкость, намного большую, чем задача может оправдать:
bigger_model = keras.models.Sequential([
keras.layers.Dense(512, activation=tf.nn.relu,
input_shape=(NUM_WORDS,)),
keras.layers.Dense(512, activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
bigger_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
bigger_model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_6 (Dense) (None, 512) 5120512
_________________________________________________________________
dense_7 (Dense) (None, 512) 262656
_________________________________________________________________
dense_8 (Dense) (None, 1) 513
=================================================================
Total params: 5,383,681
Trainable params: 5,383,681
Non-trainable params: 0
_________________________________________________________________
Тренируем модель, используя те же данные:
bigger_history = bigger_model.fit(train_data, train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
- 8s - loss: 0.3478 - acc: 0.8486 - binary_crossentropy: 0.3478 - val_loss: 0.2999 - val_acc: 0.8788 - val_binary_crossentropy: 0.2999
Epoch 2/20
- 8s - loss: 0.1424 - acc: 0.9483 - binary_crossentropy: 0.1424 - val_loss: 0.3631 - val_acc: 0.8639 - val_binary_crossentropy: 0.3631
Epoch 3/20
- 8s - loss: 0.0515 - acc: 0.9854 - binary_crossentropy: 0.0515 - val_loss: 0.4228 - val_acc: 0.8700 - val_binary_crossentropy: 0.4228
Epoch 4/20
- 8s - loss: 0.0081 - acc: 0.9989 - binary_crossentropy: 0.0081 - val_loss: 0.5690 - val_acc: 0.8706 - val_binary_crossentropy: 0.5690
Epoch 5/20
- 8s - loss: 0.0013 - acc: 0.9999 - binary_crossentropy: 0.0013 - val_loss: 0.6585 - val_acc: 0.8719 - val_binary_crossentropy: 0.6585
Epoch 6/20
- 8s - loss: 8.3323e-04 - acc: 1.0000 - binary_crossentropy: 8.3323e-04 - val_loss: 0.6917 - val_acc: 0.8710 - val_binary_crossentropy: 0.6917
Epoch 7/20
- 8s - loss: 1.8393e-04 - acc: 1.0000 - binary_crossentropy: 1.8393e-04 - val_loss: 0.7174 - val_acc: 0.8717 - val_binary_crossentropy: 0.7174
Epoch 8/20
- 7s - loss: 1.2353e-04 - acc: 1.0000 - binary_crossentropy: 1.2353e-04 - val_loss: 0.7397 - val_acc: 0.8720 - val_binary_crossentropy: 0.7397
Epoch 9/20
- 8s - loss: 9.1524e-05 - acc: 1.0000 - binary_crossentropy: 9.1524e-05 - val_loss: 0.7577 - val_acc: 0.8720 - val_binary_crossentropy: 0.7577
Epoch 10/20
- 8s - loss: 7.1093e-05 - acc: 1.0000 - binary_crossentropy: 7.1093e-05 - val_loss: 0.7716 - val_acc: 0.8721 - val_binary_crossentropy: 0.7716
Epoch 11/20
- 8s - loss: 5.7032e-05 - acc: 1.0000 - binary_crossentropy: 5.7032e-05 - val_loss: 0.7852 - val_acc: 0.8719 - val_binary_crossentropy: 0.7852
Epoch 12/20
- 8s - loss: 4.6592e-05 - acc: 1.0000 - binary_crossentropy: 4.6592e-05 - val_loss: 0.7972 - val_acc: 0.8719 - val_binary_crossentropy: 0.7972
Epoch 13/20
- 8s - loss: 3.8803e-05 - acc: 1.0000 - binary_crossentropy: 3.8803e-05 - val_loss: 0.8071 - val_acc: 0.8722 - val_binary_crossentropy: 0.8071
Epoch 14/20
- 8s - loss: 3.2781e-05 - acc: 1.0000 - binary_crossentropy: 3.2781e-05 - val_loss: 0.8176 - val_acc: 0.8722 - val_binary_crossentropy: 0.8176
Epoch 15/20
- 8s - loss: 2.7967e-05 - acc: 1.0000 - binary_crossentropy: 2.7967e-05 - val_loss: 0.8265 - val_acc: 0.8723 - val_binary_crossentropy: 0.8265
Epoch 16/20
- 7s - loss: 2.4134e-05 - acc: 1.0000 - binary_crossentropy: 2.4134e-05 - val_loss: 0.8344 - val_acc: 0.8722 - val_binary_crossentropy: 0.8344
Epoch 17/20
- 7s - loss: 2.0978e-05 - acc: 1.0000 - binary_crossentropy: 2.0978e-05 - val_loss: 0.8435 - val_acc: 0.8723 - val_binary_crossentropy: 0.8435
Epoch 18/20
- 8s - loss: 1.8398e-05 - acc: 1.0000 - binary_crossentropy: 1.8398e-05 - val_loss: 0.8501 - val_acc: 0.8722 - val_binary_crossentropy: 0.8501
Epoch 19/20
- 8s - loss: 1.6204e-05 - acc: 1.0000 - binary_crossentropy: 1.6204e-05 - val_loss: 0.8572 - val_acc: 0.8722 - val_binary_crossentropy: 0.8572
Epoch 20/20
- 8s - loss: 1.4384e-05 - acc: 1.0000 - binary_crossentropy: 1.4384e-05 - val_loss: 0.8645 - val_acc: 0.8723 - val_binary_crossentropy: 0.8645
Создадим график тренировочной и валидационной потери
Непрерывные линии показывают тренировочную потерю, а пунктирные линии показывают валидационную потерю (следует помнить: более низкое значение валидационной потери означает лучшее качество модели). Здесь меньшая сеть начинает переобучаться позже, чем модель-основание (после 6 эпох (меньшая модель), чем через 4 эпохи (модель-основание)) и ее производительность уменьшается намного медленней после начала переобучения.
def plot_history(histories, key='binary_crossentropy'):
plt.figure(figsize=(16,10))
for name, history in histories:
val = plt.plot(history.epoch, history.history['val_'+key],
'--', label=name.title()+' Val')
plt.plot(history.epoch, history.history[key],
color=val[0].get_color(),
label=name.title()+' Train')
plt.xlabel('Epochs')
plt.ylabel(key.replace('_',' ').title())
plt.legend()
plt.xlim([0,max(history.epoch)])
plot_history([('baseline', baseline_history),
('smaller', smaller_history),
('bigger', bigger_history)])
Следует отметить, что большая сеть начинает переобучаться почти сразу после одной эпохи, и переобучается намного быстрее. Чем большую емкость имеет сеть, тем быстрее она будет способна моделировать тренировочные данные (приводя в результате к низкой тренировочной потери), но более восприимчива к переобучению (приводя в результате к большой разнице между тренировочной и валидационной потерей).
Стратегии
Добавление весовой регуляризации
Скорее всего вы слышали о принципе бритва Оккама: даны два объяснения чего-либо, "простейшее" объяснение скорее всего будет правильным, то которое делает меньше предположений. Этот принцип применим и к моделям обучаемым нейронными сетями: даны тренировочные данные и архитектура сети, присутствует наборов значений весов (несколько моделей), которые могут объяснить данные, и более простые модели с меньшей вероятностью будут подвергаться переобучению, чем более сложные.
"Простая модель" в этом контексте - это модель, где распределение значений параметров имеет меньшую энтропию (или модель с меньшим количеством параметров в целом). Таким образом, общий путь смягчения переобучения - наложить ограничения на сложность сети, принуждая веса принимать только небольшие значения, которые делают распределение весов более "регулярным". Это называется "весовая регуляризация", и она выполняется добавлением к функции потери сети цену, связанную с обладанием больших весов. Эта цена имеет два подвида:
- L1 регуляризация, где цена добавляется пропорционально абсолютному значению весовых коэффициентов (то что называется "L1 норма"("L1 norm") весов).
- L2 регуляризация, где цена добавляется пропорционально квадрату значения весовых коэффициентов (то что называется "L2 норма"("L2 norm") весов).
L2 регуляризацию также называют весовой распад в контексте нейронных сетей.
В tf.keras высовая регуляризация добавляется передачей экземпляров весового регуляризатора слоям как аргументы ключевого слова. Теперь добавим L2 весовую регуляризацию.
l2_model = keras.models.Sequential([
keras.layers.Dense(16,
kernel_regularizer=keras.regularizers.l2(0.001),
activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
keras.layers.Dense(16,
kernel_regularizer=keras.regularizers.l2(0.001),
activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
l2_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy', 'binary_crossentropy'])
l2_model_history = l2_model.fit(train_data, train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
- 4s - loss: 0.5401 - acc: 0.7926 - binary_crossentropy: 0.5019 - val_loss: 0.3785 - val_acc: 0.8760 - val_binary_crossentropy: 0.3386
Epoch 2/20
- 4s - loss: 0.2991 - acc: 0.9103 - binary_crossentropy: 0.2543 - val_loss: 0.3339 - val_acc: 0.8870 - val_binary_crossentropy: 0.2856
Epoch 3/20
- 4s - loss: 0.2479 - acc: 0.9311 - binary_crossentropy: 0.1970 - val_loss: 0.3392 - val_acc: 0.8854 - val_binary_crossentropy: 0.2863
Epoch 4/20
- 4s - loss: 0.2230 - acc: 0.9424 - binary_crossentropy: 0.1684 - val_loss: 0.3551 - val_acc: 0.8804 - val_binary_crossentropy: 0.2994
Epoch 5/20
- 4s - loss: 0.2109 - acc: 0.9484 - binary_crossentropy: 0.1535 - val_loss: 0.3678 - val_acc: 0.8781 - val_binary_crossentropy: 0.3093
Epoch 6/20
- 4s - loss: 0.1980 - acc: 0.9528 - binary_crossentropy: 0.1389 - val_loss: 0.3845 - val_acc: 0.8745 - val_binary_crossentropy: 0.3249
Epoch 7/20
- 4s - loss: 0.1915 - acc: 0.9560 - binary_crossentropy: 0.1307 - val_loss: 0.3987 - val_acc: 0.8718 - val_binary_crossentropy: 0.3371
Epoch 8/20
- 4s - loss: 0.1841 - acc: 0.9600 - binary_crossentropy: 0.1217 - val_loss: 0.4122 - val_acc: 0.8698 - val_binary_crossentropy: 0.3495
Epoch 9/20
- 4s - loss: 0.1791 - acc: 0.9607 - binary_crossentropy: 0.1158 - val_loss: 0.4294 - val_acc: 0.8684 - val_binary_crossentropy: 0.3656
Epoch 10/20
- 4s - loss: 0.1775 - acc: 0.9620 - binary_crossentropy: 0.1130 - val_loss: 0.4455 - val_acc: 0.8642 - val_binary_crossentropy: 0.3804
Epoch 11/20
- 4s - loss: 0.1726 - acc: 0.9639 - binary_crossentropy: 0.1071 - val_loss: 0.4527 - val_acc: 0.8648 - val_binary_crossentropy: 0.3867
Epoch 12/20
- 4s - loss: 0.1690 - acc: 0.9653 - binary_crossentropy: 0.1029 - val_loss: 0.4718 - val_acc: 0.8604 - val_binary_crossentropy: 0.4054
Epoch 13/20
- 4s - loss: 0.1682 - acc: 0.9660 - binary_crossentropy: 0.1011 - val_loss: 0.4770 - val_acc: 0.8628 - val_binary_crossentropy: 0.4095
Epoch 14/20
- 4s - loss: 0.1597 - acc: 0.9693 - binary_crossentropy: 0.0922 - val_loss: 0.4842 - val_acc: 0.8592 - val_binary_crossentropy: 0.4170
Epoch 15/20
- 4s - loss: 0.1542 - acc: 0.9729 - binary_crossentropy: 0.0871 - val_loss: 0.4987 - val_acc: 0.8596 - val_binary_crossentropy: 0.4315
Epoch 16/20
- 4s - loss: 0.1525 - acc: 0.9733 - binary_crossentropy: 0.0851 - val_loss: 0.5099 - val_acc: 0.8566 - val_binary_crossentropy: 0.4423
Epoch 17/20
- 4s - loss: 0.1500 - acc: 0.9742 - binary_crossentropy: 0.0824 - val_loss: 0.5175 - val_acc: 0.8567 - val_binary_crossentropy: 0.4495
Epoch 18/20
- 4s - loss: 0.1487 - acc: 0.9741 - binary_crossentropy: 0.0805 - val_loss: 0.5354 - val_acc: 0.8552 - val_binary_crossentropy: 0.4668
Epoch 19/20
- 4s - loss: 0.1466 - acc: 0.9748 - binary_crossentropy: 0.0778 - val_loss: 0.5389 - val_acc: 0.8589 - val_binary_crossentropy: 0.4700
Epoch 20/20
- 4s - loss: 0.1451 - acc: 0.9755 - binary_crossentropy: 0.0757 - val_loss: 0.5499 - val_acc: 0.8557 - val_binary_crossentropy: 0.4800
l2(0.001) означает, что каждый коэффициент в марице весов слоя добавит 0.001 * weight_coefficient_value**2 к общей потери сети. Стоит отметить, что ввиду того, что это ограничение добавлено только в тренировочное время, потеря для сети будет намного больше при тренировке, чем при тестировании.
Вот влияние нашей L2 регуляризации:
plot_history([('baseline', baseline_history),
('l2', l2_model_history)])
Как можно видеть, L2 регуляризованная модель становится устойчивей к переобучению, чем модель-основание, даже при том что обе модели имеют одинаковое количество параметров.
Добавление дропаута (dropout)
Дропаут (отсеивание) - это одна из наиболее эффективных и наиболее общеиспользуемых техник регуляризации для нейронных сетей. Дропаут, применяемый к слою, представляет собой "отбрасывание" ("dropping out") (установка в ноль) случайным образом ряда выводных свойств слоя в ходе тренировки. Скажем дан слой, который в обычном случае возвращает вектор [0.2, 0.5, 1.3, 0.8, 1.1] для данного вводного примера в ходе тренировки; после применения дропаута этот вектор будет иметь несколько нулевых значений, распределенных случайным образом, например [0, 0.5, 1.3, 0, 1.1]. "Коэффициент дропаута" - это порция свойств, которые будут приведены к нулю; обычно устанавливается между 0.2 и 0.5. Во время тестирования элементы не подвергаются дропауту, и вместо этого выводные значения слоя сокращаются на коэффициент, равный коэффициенту дропаута, чтобы таким образом сбалансировать факт наличия большего количества активных элементов, чем во время тренировки.
В tf.keras можно вводить дропаут в сеть через Dropout слой, который применяется выводу вышележащего слоя.
Добавим два Dropout слоя в нашу IMDB сеть, чтобы увидеть насколько хороши они в снижении переобучения:
dpt_model = keras.models.Sequential([
keras.layers.Dense(16, activation=tf.nn.relu,
input_shape=(NUM_WORDS,)),
keras.layers.Dropout(0.5),
keras.layers.Dense(16, activation=tf.nn.relu),
keras.layers.Dropout(0.5),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
dpt_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
dpt_model_history = dpt_model.fit(train_data, train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
- 4s - loss: 0.6363 - acc: 0.6294 - binary_crossentropy: 0.6363 - val_loss: 0.5156 - val_acc: 0.8526 - val_binary_crossentropy: 0.5156
Epoch 2/20
- 4s - loss: 0.4776 - acc: 0.7993 - binary_crossentropy: 0.4776 - val_loss: 0.3629 - val_acc: 0.8782 - val_binary_crossentropy: 0.3629
Epoch 3/20
- 4s - loss: 0.3731 - acc: 0.8549 - binary_crossentropy: 0.3731 - val_loss: 0.2976 - val_acc: 0.8864 - val_binary_crossentropy: 0.2976
Epoch 4/20
- 4s - loss: 0.3065 - acc: 0.8850 - binary_crossentropy: 0.3065 - val_loss: 0.2748 - val_acc: 0.8896 - val_binary_crossentropy: 0.2748
Epoch 5/20
- 4s - loss: 0.2610 - acc: 0.9066 - binary_crossentropy: 0.2610 - val_loss: 0.2751 - val_acc: 0.8882 - val_binary_crossentropy: 0.2751
Epoch 6/20
- 4s - loss: 0.2245 - acc: 0.9192 - binary_crossentropy: 0.2245 - val_loss: 0.2844 - val_acc: 0.8870 - val_binary_crossentropy: 0.2844
Epoch 7/20
- 4s - loss: 0.1968 - acc: 0.9266 - binary_crossentropy: 0.1968 - val_loss: 0.2999 - val_acc: 0.8873 - val_binary_crossentropy: 0.2999
Epoch 8/20
- 4s - loss: 0.1759 - acc: 0.9362 - binary_crossentropy: 0.1759 - val_loss: 0.3136 - val_acc: 0.8859 - val_binary_crossentropy: 0.3136
Epoch 9/20
- 4s - loss: 0.1596 - acc: 0.9438 - binary_crossentropy: 0.1596 - val_loss: 0.3242 - val_acc: 0.8826 - val_binary_crossentropy: 0.3242
Epoch 10/20
- 4s - loss: 0.1412 - acc: 0.9498 - binary_crossentropy: 0.1412 - val_loss: 0.3551 - val_acc: 0.8823 - val_binary_crossentropy: 0.3551
Epoch 11/20
- 4s - loss: 0.1292 - acc: 0.9539 - binary_crossentropy: 0.1292 - val_loss: 0.3745 - val_acc: 0.8792 - val_binary_crossentropy: 0.3745
Epoch 12/20
- 4s - loss: 0.1176 - acc: 0.9568 - binary_crossentropy: 0.1176 - val_loss: 0.3983 - val_acc: 0.8800 - val_binary_crossentropy: 0.3983
Epoch 13/20
- 4s - loss: 0.1115 - acc: 0.9582 - binary_crossentropy: 0.1115 - val_loss: 0.4218 - val_acc: 0.8791 - val_binary_crossentropy: 0.4218
Epoch 14/20
- 4s - loss: 0.1035 - acc: 0.9612 - binary_crossentropy: 0.1035 - val_loss: 0.4368 - val_acc: 0.8780 - val_binary_crossentropy: 0.4368
Epoch 15/20
- 4s - loss: 0.0949 - acc: 0.9626 - binary_crossentropy: 0.0949 - val_loss: 0.4608 - val_acc: 0.8773 - val_binary_crossentropy: 0.4608
Epoch 16/20
- 4s - loss: 0.0909 - acc: 0.9626 - binary_crossentropy: 0.0909 - val_loss: 0.4573 - val_acc: 0.8776 - val_binary_crossentropy: 0.4573
Epoch 17/20
- 4s - loss: 0.0860 - acc: 0.9642 - binary_crossentropy: 0.0860 - val_loss: 0.4963 - val_acc: 0.8770 - val_binary_crossentropy: 0.4963
Epoch 18/20
- 4s - loss: 0.0832 - acc: 0.9655 - binary_crossentropy: 0.0832 - val_loss: 0.4879 - val_acc: 0.8762 - val_binary_crossentropy: 0.4879
Epoch 19/20
- 4s - loss: 0.0752 - acc: 0.9698 - binary_crossentropy: 0.0752 - val_loss: 0.5262 - val_acc: 0.8760 - val_binary_crossentropy: 0.5262
Epoch 20/20
- 4s - loss: 0.0768 - acc: 0.9666 - binary_crossentropy: 0.0768 - val_loss: 0.5360 - val_acc: 0.8754 - val_binary_crossentropy: 0.5360
plot_history([('baseline', baseline_history),
('dropout', dpt_model_history)])
Добавления дропаута - это чистое улучшения по сравнению с моделью-основанием.
Вот наиболее распространенные пути для предупреждения переобучения в нейронных сетях:
- Взять больше тренировочных данных.
- Уменьшить емкость сети.
- Добавить весовую регуляризацию.
- Добавить дропаут.
И два важных подхода, не описанных в этом руководстве - это приращивание данных (data-augmentation) и пакетная нормализация (batch normalization).