Google I / O возвращается 18-20 мая! Зарезервируйте место и составьте свое расписание Зарегистрируйтесь сейчас

Переобучение и недообучение

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Как всегда, код в этом примере будет использовать API tf.keras , о котором вы можете узнать больше в руководстве tf.keras .

В обоих предыдущих примерах - классификации текста и прогнозирования эффективности использования топлива - мы увидели, что точность нашей модели на данных проверки достигает максимума после обучения в течение нескольких эпох, а затем стагнирует или начинает снижаться.

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

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

Однако, если вы тренируетесь слишком долго, модель начнет переобучаться и изучать закономерности на основе данных обучения, которые не распространяются на данные теста. Нам нужно найти баланс. Понимание того, как тренироваться в течение определенного количества эпох, как мы рассмотрим ниже, является полезным навыком.

Лучшее решение для предотвращения переобучения - использовать более полные данные обучения. Набор данных должен охватывать весь диапазон входных данных, которые, как ожидается, будет обрабатывать модель. Дополнительные данные могут быть полезны только в том случае, если они касаются новых и интересных случаев.

Модель, обученная на более полных данных, естественно, будет лучше обобщать. Когда это уже невозможно, следующим лучшим решением будет использование таких методов, как регуляризация. Это накладывает ограничения на количество и тип информации, которую может хранить ваша модель. Если сеть может позволить себе запоминать только небольшое количество паттернов, процесс оптимизации заставит ее сосредоточиться на наиболее выдающихся паттернах, которые имеют больше шансов на хорошее обобщение.

В этой записной книжке мы рассмотрим несколько распространенных методов регуляризации и используем их для улучшения модели классификации.

Настраивать

Перед тем как начать, импортируйте необходимые пакеты:

import tensorflow as tf

from tensorflow.keras import layers
from tensorflow.keras import regularizers

print(tf.__version__)
2.4.0
!pip install -q git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots
from  IPython import display
from matplotlib import pyplot as plt

import numpy as np

import pathlib
import shutil
import tempfile
logdir = pathlib.Path(tempfile.mkdtemp())/"tensorboard_logs"
shutil.rmtree(logdir, ignore_errors=True)

Набор данных Хиггса

Цель этого руководства - не заниматься физикой элементарных частиц, поэтому не останавливайтесь на деталях набора данных. Он содержит 11 000 000 примеров, каждый с 28 функциями и меткой двоичного класса.

gz = tf.keras.utils.get_file('HIGGS.csv.gz', 'http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz')
Downloading data from http://mlphysics.ics.uci.edu/data/higgs/HIGGS.csv.gz
2816409600/2816407858 [==============================] - 120s 0us/step
FEATURES = 28

Класс tf.data.experimental.CsvDataset можно использовать для чтения записей csv непосредственно из файла gzip без промежуточного шага распаковки.

ds = tf.data.experimental.CsvDataset(gz,[float(),]*(FEATURES+1), compression_type="GZIP")

Этот класс чтения csv возвращает список скаляров для каждой записи. Следующая функция переупаковывает этот список скаляров в пару (feature_vector, label).

def pack_row(*row):
  label = row[0]
  features = tf.stack(row[1:],1)
  return features, label

TensorFlow наиболее эффективен при работе с большими пакетами данных.

Поэтому вместо того, чтобы переупаковывать каждую строку по отдельности, создайте новый Dataset который принимает пакеты из 10000 примеров, применяет функцию pack_row к каждому pack_row , а затем разбивает пакеты на отдельные записи:

packed_ds = ds.batch(10000).map(pack_row).unbatch()

Взгляните на некоторые записи из этого нового packed_ds .

Функции не полностью нормализованы, но для этого урока этого достаточно.

for features,label in packed_ds.batch(1000).take(1):
  print(features[0])
  plt.hist(features.numpy().flatten(), bins = 101)
tf.Tensor(
[ 0.8692932  -0.6350818   0.22569026  0.32747006 -0.6899932   0.75420225
 -0.24857314 -1.0920639   0.          1.3749921  -0.6536742   0.9303491
  1.1074361   1.1389043  -1.5781983  -1.0469854   0.          0.65792954
 -0.01045457 -0.04576717  3.1019614   1.35376     0.9795631   0.97807616
  0.92000484  0.72165745  0.98875093  0.87667835], shape=(28,), dtype=float32)

PNG

Чтобы этот учебник был относительно коротким, используйте только первые 1000 образцов для проверки и следующие 10 000 для обучения:

N_VALIDATION = int(1e3)
N_TRAIN = int(1e4)
BUFFER_SIZE = int(1e4)
BATCH_SIZE = 500
STEPS_PER_EPOCH = N_TRAIN//BATCH_SIZE

Это Dataset.skip методы Dataset.skip и Dataset.take .

В то же время используйте метод Dataset.cache чтобы гарантировать, что загрузчику не нужно повторно считывать данные из файла в каждую эпоху:

validate_ds = packed_ds.take(N_VALIDATION).cache()
train_ds = packed_ds.skip(N_VALIDATION).take(N_TRAIN).cache()
train_ds
<CacheDataset shapes: ((28,), ()), types: (tf.float32, tf.float32)>

Эти наборы данных возвращают отдельные примеры. Используйте метод .batch для создания пакетов подходящего размера для обучения. Перед пакетированием также не забудьте .shuffle и .repeat обучающий набор.

validate_ds = validate_ds.batch(BATCH_SIZE)
train_ds = train_ds.shuffle(BUFFER_SIZE).repeat().batch(BATCH_SIZE)

Продемонстрируйте переоснащение

Самый простой способ предотвратить переобучение - начать с небольшой модели: модели с небольшим количеством обучаемых параметров (которое определяется количеством слоев и количеством единиц на уровне). В глубоком обучении количество обучаемых параметров в модели часто называют «емкостью» модели.

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

Всегда помните об этом: модели глубокого обучения, как правило, хорошо подходят для обучающих данных, но настоящая проблема - это обобщение, а не подгонка.

С другой стороны, если сеть имеет ограниченные ресурсы запоминания, она не сможет так легко изучить отображение. Чтобы свести к минимуму его потерю, ему придется изучить сжатые представления, которые обладают большей предсказательной силой. В то же время, если вы сделаете свою модель слишком маленькой, у нее будут проблемы с подгонкой к обучающим данным. Существует баланс между «слишком большой емкостью» и «недостаточно емкостью».

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

Чтобы найти подходящий размер модели, лучше всего начать с относительно небольшого количества слоев и параметров, а затем начать увеличивать размер слоев или добавлять новые слои, пока не увидите убывающую отдачу от потерь при проверке.

Начните с простой модели, используя только layers.Dense как основу, затем создайте более крупные версии и сравните их.

Порядок обучения

Многие модели обучаются лучше, если постепенно снижать скорость обучения во время обучения. Используйте optimizers.schedules чтобы со временем снизить скорость обучения:

lr_schedule = tf.keras.optimizers.schedules.InverseTimeDecay(
  0.001,
  decay_steps=STEPS_PER_EPOCH*1000,
  decay_rate=1,
  staircase=False)

def get_optimizer():
  return tf.keras.optimizers.Adam(lr_schedule)

Приведенный выше код устанавливаетschedules.InverseTimeDecay для гиперболического уменьшения скорости обучения до 1/2 базовой скорости в 1000 эпох, 1/3 в 2000 эпох и так далее.

step = np.linspace(0,100000)
lr = lr_schedule(step)
plt.figure(figsize = (8,6))
plt.plot(step/STEPS_PER_EPOCH, lr)
plt.ylim([0,max(plt.ylim())])
plt.xlabel('Epoch')
_ = plt.ylabel('Learning Rate')

PNG

Каждая модель в этом руководстве будет использовать одну и ту же конфигурацию обучения. Поэтому настройте их повторно, начиная со списка обратных вызовов.

Обучение для этого учебника рассчитано на многие короткие эпохи. Чтобы уменьшить шум журналирования, используйте tfdocs.EpochDots который просто печатает tfdocs.EpochDots . для каждой эпохи и полный набор показателей каждые 100 эпох.

Затем включите callbacks.EarlyStopping чтобы избежать длительного и ненужного времени на обучение. Обратите внимание, что этот обратный вызов настроен на мониторинг val_binary_crossentropy , а не val_loss . Эта разница будет важна позже.

Используйте callbacks.TensorBoard для создания журналов TensorBoard для обучения.

def get_callbacks(name):
  return [
    tfdocs.modeling.EpochDots(),
    tf.keras.callbacks.EarlyStopping(monitor='val_binary_crossentropy', patience=200),
    tf.keras.callbacks.TensorBoard(logdir/name),
  ]

Точно так же каждая модель будет использовать одни и Model.fit настройки Model.compile и Model.fit :

def compile_and_fit(model, name, optimizer=None, max_epochs=10000):
  if optimizer is None:
    optimizer = get_optimizer()
  model.compile(optimizer=optimizer,
                loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                metrics=[
                  tf.keras.losses.BinaryCrossentropy(
                      from_logits=True, name='binary_crossentropy'),
                  'accuracy'])

  model.summary()

  history = model.fit(
    train_ds,
    steps_per_epoch = STEPS_PER_EPOCH,
    epochs=max_epochs,
    validation_data=validate_ds,
    callbacks=get_callbacks(name),
    verbose=0)
  return history

Крошечная модель

Начните с обучения модели:

tiny_model = tf.keras.Sequential([
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(1)
])
size_histories = {}
size_histories['Tiny'] = compile_and_fit(tiny_model, 'sizes/Tiny')
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 16)                464       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 481
Trainable params: 481
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0027s vs `on_train_batch_end` time: 0.0064s). Check your callbacks.

Epoch: 0, accuracy:0.5142,  binary_crossentropy:0.7823,  loss:0.7823,  val_accuracy:0.5230,  val_binary_crossentropy:0.7322,  val_loss:0.7322,  
....................................................................................................
Epoch: 100, accuracy:0.5933,  binary_crossentropy:0.6284,  loss:0.6284,  val_accuracy:0.5730,  val_binary_crossentropy:0.6271,  val_loss:0.6271,  
....................................................................................................
Epoch: 200, accuracy:0.6176,  binary_crossentropy:0.6135,  loss:0.6135,  val_accuracy:0.5950,  val_binary_crossentropy:0.6167,  val_loss:0.6167,  
....................................................................................................
Epoch: 300, accuracy:0.6411,  binary_crossentropy:0.6011,  loss:0.6011,  val_accuracy:0.6230,  val_binary_crossentropy:0.6052,  val_loss:0.6052,  
....................................................................................................
Epoch: 400, accuracy:0.6542,  binary_crossentropy:0.5943,  loss:0.5943,  val_accuracy:0.6300,  val_binary_crossentropy:0.6003,  val_loss:0.6003,  
....................................................................................................
Epoch: 500, accuracy:0.6569,  binary_crossentropy:0.5903,  loss:0.5903,  val_accuracy:0.6470,  val_binary_crossentropy:0.5949,  val_loss:0.5949,  
....................................................................................................
Epoch: 600, accuracy:0.6631,  binary_crossentropy:0.5872,  loss:0.5872,  val_accuracy:0.6550,  val_binary_crossentropy:0.5927,  val_loss:0.5927,  
....................................................................................................
Epoch: 700, accuracy:0.6673,  binary_crossentropy:0.5846,  loss:0.5846,  val_accuracy:0.6690,  val_binary_crossentropy:0.5905,  val_loss:0.5905,  
....................................................................................................
Epoch: 800, accuracy:0.6743,  binary_crossentropy:0.5824,  loss:0.5824,  val_accuracy:0.6710,  val_binary_crossentropy:0.5890,  val_loss:0.5890,  
....................................................................................................
Epoch: 900, accuracy:0.6739,  binary_crossentropy:0.5815,  loss:0.5815,  val_accuracy:0.6630,  val_binary_crossentropy:0.5888,  val_loss:0.5888,  
....................................................................................................
Epoch: 1000, accuracy:0.6748,  binary_crossentropy:0.5791,  loss:0.5791,  val_accuracy:0.6610,  val_binary_crossentropy:0.5881,  val_loss:0.5881,  
....................................................................................................
Epoch: 1100, accuracy:0.6763,  binary_crossentropy:0.5774,  loss:0.5774,  val_accuracy:0.6710,  val_binary_crossentropy:0.5875,  val_loss:0.5875,  
....................................................................................................
Epoch: 1200, accuracy:0.6767,  binary_crossentropy:0.5763,  loss:0.5763,  val_accuracy:0.6630,  val_binary_crossentropy:0.5869,  val_loss:0.5869,  
....................................................................................................
Epoch: 1300, accuracy:0.6833,  binary_crossentropy:0.5750,  loss:0.5750,  val_accuracy:0.6550,  val_binary_crossentropy:0.5878,  val_loss:0.5878,  
....................................................................................................
Epoch: 1400, accuracy:0.6766,  binary_crossentropy:0.5737,  loss:0.5737,  val_accuracy:0.6710,  val_binary_crossentropy:0.5865,  val_loss:0.5865,  
....................................................................................................
Epoch: 1500, accuracy:0.6806,  binary_crossentropy:0.5727,  loss:0.5727,  val_accuracy:0.6630,  val_binary_crossentropy:0.5864,  val_loss:0.5864,  
....................................................................................................
Epoch: 1600, accuracy:0.6807,  binary_crossentropy:0.5717,  loss:0.5717,  val_accuracy:0.6720,  val_binary_crossentropy:0.5859,  val_loss:0.5859,  
....................................................................................................
Epoch: 1700, accuracy:0.6823,  binary_crossentropy:0.5707,  loss:0.5707,  val_accuracy:0.6570,  val_binary_crossentropy:0.5872,  val_loss:0.5872,  
..........

Теперь проверим, как работала модель:

plotter = tfdocs.plots.HistoryPlotter(metric = 'binary_crossentropy', smoothing_std=10)
plotter.plot(size_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

PNG

Маленькая модель

Чтобы увидеть, сможете ли вы превзойти небольшую модель по производительности, постепенно обучайте несколько более крупных моделей.

Попробуйте два скрытых слоя по 16 единиц в каждом:

small_model = tf.keras.Sequential([
    # `input_shape` is only required here so that `.summary` works.
    layers.Dense(16, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(16, activation='elu'),
    layers.Dense(1)
])
size_histories['Small'] = compile_and_fit(small_model, 'sizes/Small')
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_2 (Dense)              (None, 16)                464       
_________________________________________________________________
dense_3 (Dense)              (None, 16)                272       
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 17        
=================================================================
Total params: 753
Trainable params: 753
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0028s vs `on_train_batch_end` time: 0.0112s). Check your callbacks.

Epoch: 0, accuracy:0.5037,  binary_crossentropy:0.9699,  loss:0.9699,  val_accuracy:0.4950,  val_binary_crossentropy:0.7648,  val_loss:0.7648,  
....................................................................................................
Epoch: 100, accuracy:0.6141,  binary_crossentropy:0.6173,  loss:0.6173,  val_accuracy:0.6000,  val_binary_crossentropy:0.6165,  val_loss:0.6165,  
....................................................................................................
Epoch: 200, accuracy:0.6532,  binary_crossentropy:0.5935,  loss:0.5935,  val_accuracy:0.6430,  val_binary_crossentropy:0.6023,  val_loss:0.6023,  
....................................................................................................
Epoch: 300, accuracy:0.6762,  binary_crossentropy:0.5797,  loss:0.5797,  val_accuracy:0.6540,  val_binary_crossentropy:0.5987,  val_loss:0.5987,  
....................................................................................................
Epoch: 400, accuracy:0.6843,  binary_crossentropy:0.5725,  loss:0.5725,  val_accuracy:0.6570,  val_binary_crossentropy:0.6005,  val_loss:0.6005,  
.............................................................................

Средняя модель

Теперь попробуйте 3 скрытых слоя по 64 блока в каждом:

medium_model = tf.keras.Sequential([
    layers.Dense(64, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(64, activation='elu'),
    layers.Dense(64, activation='elu'),
    layers.Dense(1)
])

И обучите модель, используя те же данные:

size_histories['Medium']  = compile_and_fit(medium_model, "sizes/Medium")
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_5 (Dense)              (None, 64)                1856      
_________________________________________________________________
dense_6 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_7 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 65        
=================================================================
Total params: 10,241
Trainable params: 10,241
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0026s vs `on_train_batch_end` time: 0.0107s). Check your callbacks.

Epoch: 0, accuracy:0.4897,  binary_crossentropy:0.7502,  loss:0.7502,  val_accuracy:0.4590,  val_binary_crossentropy:0.7012,  val_loss:0.7012,  
....................................................................................................
Epoch: 100, accuracy:0.7040,  binary_crossentropy:0.5396,  loss:0.5396,  val_accuracy:0.6760,  val_binary_crossentropy:0.5956,  val_loss:0.5956,  
....................................................................................................
Epoch: 200, accuracy:0.7784,  binary_crossentropy:0.4380,  loss:0.4380,  val_accuracy:0.6560,  val_binary_crossentropy:0.6714,  val_loss:0.6714,  
.......................................................................

Большая модель

В качестве упражнения вы можете создать модель еще большего размера и посмотреть, как быстро она начнет переобучаться. Затем давайте добавим к этому тесту сеть с гораздо большей пропускной способностью, гораздо большей, чем того требует проблема:

large_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(512, activation='elu'),
    layers.Dense(1)
])

И снова обучаем модель на тех же данных:

size_histories['large'] = compile_and_fit(large_model, "sizes/large")
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_9 (Dense)              (None, 512)               14848     
_________________________________________________________________
dense_10 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_11 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_12 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_13 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0035s vs `on_train_batch_end` time: 0.0129s). Check your callbacks.

Epoch: 0, accuracy:0.4901,  binary_crossentropy:0.8508,  loss:0.8508,  val_accuracy:0.5540,  val_binary_crossentropy:0.6800,  val_loss:0.6800,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0025,  loss:0.0025,  val_accuracy:0.6480,  val_binary_crossentropy:1.7541,  val_loss:1.7541,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0002,  loss:0.0002,  val_accuracy:0.6530,  val_binary_crossentropy:2.4090,  val_loss:2.4090,  
....................

Постройте график потерь при обучении и проверке

Сплошные линии показывают потери при обучении, а пунктирные линии показывают потери при проверке (помните: более низкие потери при проверке указывают на лучшую модель).

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

В этом примере, как правило, только "Tiny" модели удается полностью избежать переобучения, а каждая из более крупных моделей быстрее переоборудует данные. Это становится настолько серьезным для "large" модели, что вам нужно переключить график в логарифмический масштаб, чтобы действительно увидеть, что происходит.

Это станет очевидным, если вы построите и сравните метрики валидации с метриками обучения.

  • Небольшая разница - это нормально.
  • Если обе метрики движутся в одном направлении, все в порядке.
  • Если метрика проверки начинает стагнировать, а метрика обучения продолжает улучшаться, вы, вероятно, близки к переобучению.
  • Если метрика валидации идет в неправильном направлении, модель явно переоснащается.
plotter.plot(size_histories)
a = plt.xscale('log')
plt.xlim([5, max(plt.xlim())])
plt.ylim([0.5, 0.7])
plt.xlabel("Epochs [Log Scale]")
Text(0.5, 0, 'Epochs [Log Scale]')

PNG

Посмотреть в TensorBoard

Все эти модели вели журналы TensorBoard во время обучения.

Откройте встроенную программу просмотра TensorBoard внутри записной книжки:


# Load the TensorBoard notebook extension
%load_ext tensorboard

# Open an embedded TensorBoard viewer
%tensorboard --logdir {logdir}/sizes

Вы можете просмотреть результаты предыдущего запуска этой записной книжки на TensorBoard.dev .

TensorBoard.dev - это управляемая среда для размещения, отслеживания и обмена экспериментами машинного обучения со всеми.

Он также включен в <iframe> для удобства:

display.IFrame(
    src="https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&_smoothingWeight=0.97",
    width="100%", height="800px")

Если вы хотите поделиться результатами TensorBoard, вы можете загрузить журналы в TensorBoard.dev , скопировав следующее в ячейку кода.

tensorboard dev upload --logdir  {logdir}/sizes

Стратегии предотвращения переобучения

Перед тем, как перейти к содержанию этого раздела, скопируйте журналы обучения из модели "Tiny" выше, чтобы использовать их в качестве основы для сравнения.

shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')
PosixPath('/tmp/tmpr1cbfjvl/tensorboard_logs/regularizers/Tiny')
regularizer_histories = {}
regularizer_histories['Tiny'] = size_histories['Tiny']

Добавьте регуляризацию веса

Возможно, вы знакомы с принципом Бритвы Оккама: учитывая два объяснения чего-либо, наиболее вероятным будет правильное объяснение - «простейшее», то есть такое, которое делает наименьшее количество предположений. Это также относится к моделям, изучаемым нейронными сетями: с учетом некоторых обучающих данных и сетевой архитектуры существует несколько наборов значений весов (несколько моделей), которые могут объяснить данные, и более простые модели с меньшей вероятностью переоборудуются, чем сложные.

«Простая модель» в этом контексте - это модель, в которой распределение значений параметров имеет меньшую энтропию (или модель с меньшим количеством параметров в целом, как мы видели в разделе выше). Таким образом, распространенный способ смягчить переоснащение - это наложить ограничения на сложность сети, заставляя ее веса принимать только небольшие значения, что делает распределение значений весов более «регулярным». Это называется «регуляризацией веса», и это достигается добавлением к функции потерь сети затрат, связанных с наличием больших весов. Эта стоимость бывает двух видов:

  • Регуляризация L1 , где добавленные затраты пропорциональны абсолютному значению весовых коэффициентов (то есть тому, что называется «нормой L1» весов).

  • Регуляризация L2 , где добавленные затраты пропорциональны квадрату значения весовых коэффициентов (т. Е. Тому, что называется квадратом «L2 нормы» весов). Регуляризация L2 также называется уменьшением веса в контексте нейронных сетей. Не позволяйте другому названию сбивать вас с толку: математически спад веса - это то же самое, что регуляризация L2.

Регуляризация L1 подталкивает веса к точному нулю, поощряя разреженную модель. Регуляризация L2 будет штрафовать параметры весов, не делая их разреженными, поскольку штраф стремится к нулю для малых весов - одна из причин, почему L2 более распространен.

В tf.keras регуляризация веса добавляется путем передачи экземпляров регуляризатора веса слоям в качестве аргументов ключевого слова. Давайте теперь добавим регуляризацию веса L2.

l2_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001),
                 input_shape=(FEATURES,)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(512, activation='elu',
                 kernel_regularizer=regularizers.l2(0.001)),
    layers.Dense(1)
])

regularizer_histories['l2'] = compile_and_fit(l2_model, "regularizers/l2")
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_14 (Dense)             (None, 512)               14848     
_________________________________________________________________
dense_15 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_16 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_17 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_18 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0034s vs `on_train_batch_end` time: 0.0134s). Check your callbacks.

Epoch: 0, accuracy:0.5162,  binary_crossentropy:0.7512,  loss:2.2423,  val_accuracy:0.4830,  val_binary_crossentropy:0.6906,  val_loss:2.0808,  
....................................................................................................
Epoch: 100, accuracy:0.6586,  binary_crossentropy:0.5961,  loss:0.6196,  val_accuracy:0.6590,  val_binary_crossentropy:0.5953,  val_loss:0.6188,  
....................................................................................................
Epoch: 200, accuracy:0.6736,  binary_crossentropy:0.5817,  loss:0.6037,  val_accuracy:0.6610,  val_binary_crossentropy:0.5796,  val_loss:0.6015,  
....................................................................................................
Epoch: 300, accuracy:0.6840,  binary_crossentropy:0.5771,  loss:0.6019,  val_accuracy:0.6770,  val_binary_crossentropy:0.5868,  val_loss:0.6124,  
....................................................................................................
Epoch: 400, accuracy:0.6926,  binary_crossentropy:0.5668,  loss:0.5924,  val_accuracy:0.6670,  val_binary_crossentropy:0.5748,  val_loss:0.6004,  
....................................................................................................
Epoch: 500, accuracy:0.6991,  binary_crossentropy:0.5550,  loss:0.5824,  val_accuracy:0.6890,  val_binary_crossentropy:0.5743,  val_loss:0.6018,  
....................................................................................................
Epoch: 600, accuracy:0.7073,  binary_crossentropy:0.5475,  loss:0.5758,  val_accuracy:0.6800,  val_binary_crossentropy:0.5734,  val_loss:0.6016,  
....................................................................................................
Epoch: 700, accuracy:0.7139,  binary_crossentropy:0.5381,  loss:0.5666,  val_accuracy:0.6770,  val_binary_crossentropy:0.5848,  val_loss:0.6132,  
....................................................................................................
Epoch: 800, accuracy:0.7169,  binary_crossentropy:0.5354,  loss:0.5645,  val_accuracy:0.6920,  val_binary_crossentropy:0.5835,  val_loss:0.6126,  
...........

l2(0.001) означает, что каждый коэффициент в матрице весов слоя добавляет 0.001 * weight_coefficient_value**2 к общим потерям в сети.

Вот почему мы напрямую отслеживаем binary_crossentropy . Потому что в нем нет этого компонента регуляризации.

Итак, та же самая "Large" модель со штрафом за регуляризацию L2 работает намного лучше:

plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

PNG

Как видите, регуляризованная модель "L2" теперь намного более конкурентоспособна по "Tiny" моделью "Tiny" . Эта модель "L2" также намного более устойчива к переобучению, чем модель "Large" она была основана, несмотря на то же количество параметров.

Больше информации

В отношении такого рода регуляризации следует отметить два важных момента.

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

result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)

Во-вторых: эта реализация работает, добавляя штрафы за вес к потерям модели, а затем применяя стандартную процедуру оптимизации после этого.

Существует второй подход, который вместо этого запускает оптимизатор только для необработанных потерь, а затем, применяя вычисленный шаг, оптимизатор также применяет некоторое снижение веса. Это «развязанное снижение веса» наблюдается в оптимизаторах, таких как optimizers.FTRL и optimizers.AdamW .

Добавить отсев

Отсев - один из наиболее эффективных и наиболее часто используемых методов регуляризации нейронных сетей, разработанный Хинтоном и его студентами из Университета Торонто.

Интуитивно понятное объяснение отсева состоит в том, что, поскольку отдельные узлы в сети не могут полагаться на вывод других, каждый узел должен выводить функции, которые полезны сами по себе.

Выпадение, применяемое к слою, состоит из случайного «отбрасывания» (т. Е. Обнуления) ряда выходных характеристик слоя во время обучения. Допустим, данный слой обычно возвращал бы вектор [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, чтобы посмотреть, насколько хорошо они справляются с сокращением переобучения:

dropout_model = tf.keras.Sequential([
    layers.Dense(512, activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['dropout'] = compile_and_fit(dropout_model, "regularizers/dropout")
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_19 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_20 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_21 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_22 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_3 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_23 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0036s vs `on_train_batch_end` time: 0.0141s). Check your callbacks.

Epoch: 0, accuracy:0.4944,  binary_crossentropy:0.8070,  loss:0.8070,  val_accuracy:0.5630,  val_binary_crossentropy:0.6919,  val_loss:0.6919,  
....................................................................................................
Epoch: 100, accuracy:0.6579,  binary_crossentropy:0.5956,  loss:0.5956,  val_accuracy:0.6780,  val_binary_crossentropy:0.5794,  val_loss:0.5794,  
....................................................................................................
Epoch: 200, accuracy:0.6878,  binary_crossentropy:0.5569,  loss:0.5569,  val_accuracy:0.6760,  val_binary_crossentropy:0.5896,  val_loss:0.5896,  
....................................................................................................
Epoch: 300, accuracy:0.7189,  binary_crossentropy:0.5084,  loss:0.5084,  val_accuracy:0.6960,  val_binary_crossentropy:0.6009,  val_loss:0.6009,  
....................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

PNG

Из этого графика ясно, что оба этих подхода к регуляризации улучшают поведение "Large" модели. Но это все равно не превосходит даже базовый уровень "Tiny" .

Затем попробуйте их обоих вместе и посмотрите, получится ли это лучше.

Комбинированный L2 + отсев

combined_model = tf.keras.Sequential([
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu', input_shape=(FEATURES,)),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(512, kernel_regularizer=regularizers.l2(0.0001),
                 activation='elu'),
    layers.Dropout(0.5),
    layers.Dense(1)
])

regularizer_histories['combined'] = compile_and_fit(combined_model, "regularizers/combined")
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_24 (Dense)             (None, 512)               14848     
_________________________________________________________________
dropout_4 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_25 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_5 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_26 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_27 (Dense)             (None, 512)               262656    
_________________________________________________________________
dropout_7 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_28 (Dense)             (None, 1)                 513       
=================================================================
Total params: 803,329
Trainable params: 803,329
Non-trainable params: 0
_________________________________________________________________
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0040s vs `on_train_batch_end` time: 0.0151s). Check your callbacks.

Epoch: 0, accuracy:0.5052,  binary_crossentropy:0.8050,  loss:0.9634,  val_accuracy:0.4700,  val_binary_crossentropy:0.6965,  val_loss:0.8544,  
....................................................................................................
Epoch: 100, accuracy:0.6432,  binary_crossentropy:0.6061,  loss:0.6349,  val_accuracy:0.6440,  val_binary_crossentropy:0.5868,  val_loss:0.6154,  
....................................................................................................
Epoch: 200, accuracy:0.6633,  binary_crossentropy:0.5916,  loss:0.6159,  val_accuracy:0.6420,  val_binary_crossentropy:0.5788,  val_loss:0.6031,  
....................................................................................................
Epoch: 300, accuracy:0.6685,  binary_crossentropy:0.5828,  loss:0.6107,  val_accuracy:0.6900,  val_binary_crossentropy:0.5632,  val_loss:0.5911,  
....................................................................................................
Epoch: 400, accuracy:0.6719,  binary_crossentropy:0.5808,  loss:0.6102,  val_accuracy:0.6880,  val_binary_crossentropy:0.5620,  val_loss:0.5914,  
....................................................................................................
Epoch: 500, accuracy:0.6776,  binary_crossentropy:0.5739,  loss:0.6050,  val_accuracy:0.6880,  val_binary_crossentropy:0.5539,  val_loss:0.5851,  
....................................................................................................
Epoch: 600, accuracy:0.6856,  binary_crossentropy:0.5669,  loss:0.6006,  val_accuracy:0.6960,  val_binary_crossentropy:0.5493,  val_loss:0.5830,  
....................................................................................................
Epoch: 700, accuracy:0.6833,  binary_crossentropy:0.5648,  loss:0.5996,  val_accuracy:0.6980,  val_binary_crossentropy:0.5430,  val_loss:0.5778,  
....................................................................................................
Epoch: 800, accuracy:0.6943,  binary_crossentropy:0.5594,  loss:0.5955,  val_accuracy:0.6930,  val_binary_crossentropy:0.5412,  val_loss:0.5772,  
....................................................................................................
Epoch: 900, accuracy:0.6966,  binary_crossentropy:0.5558,  loss:0.5932,  val_accuracy:0.7060,  val_binary_crossentropy:0.5419,  val_loss:0.5793,  
.................................................................................................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

PNG

Эта модель с "Combined" регуляризацией, очевидно, пока лучшая.

Посмотреть в TensorBoard

Эти модели также вели журналы TensorBoard.

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

%tensorboard --logdir {logdir}/regularizers

Вы можете просмотреть результаты предыдущего запуска этого блокнота на TensorDoard.dev .

Он также включен в <iframe> для удобства:

display.IFrame(
    src="https://tensorboard.dev/experiment/fGInKDo8TXes1z7HQku9mw/#scalars&_smoothingWeight=0.97",
    width = "100%",
    height="800px")

Это было загружено с помощью:

tensorboard dev upload --logdir  {logdir}/regularizers

Выводы

Напомним: вот наиболее распространенные способы предотвращения переобучения в нейронных сетях:

  • Получите больше данных для обучения.
  • Уменьшите емкость сети.
  • Добавьте регуляризацию веса.
  • Добавить отсев.

В этом руководстве не рассматриваются два важных подхода:

  • расширение данных
  • пакетная нормализация

Помните, что каждый метод может помочь сам по себе, но часто их сочетание может быть еще более эффективным.

# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.