Overfit i underfit

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Jak zawsze, kod w tym przykładzie użyje tf.keras API, które można dowiedzieć się więcej na temat w TensorFlow przewodnika Keras .

W obu poprzednich examples- tekstu klasyfikacyjnego i przewidywania zużycia paliwa - widzieliśmy, że dokładność naszego modelu na danych walidacyjnych byłby szczyt po treningu z wielu epok, i następnie stagnacji lub zacząć spadać.

Innymi słowy, nasz model będzie nadmierne dopasowanie do danych szkolenia. Ważna jest nauka radzenia sobie z nadmiernym dopasowaniem. Chociaż jest to często możliwe jest uzyskanie wysokiej dokładności na zbiorze treningowym, co naprawdę chcemy jest opracowanie modeli uogólnić również do zestawu testowego (lub danych, których nie widziałem wcześniej).

Przeciwieństwem jest niedouczenia nadmiernego dopasowania. Niedopasowanie występuje, gdy nadal istnieje możliwość poprawy danych dotyczących pociągu. Może się tak zdarzyć z wielu powodów: Jeśli model nie jest wystarczająco wydajny, jest nadmiernie uregulowany lub po prostu nie był trenowany wystarczająco długo. Oznacza to, że sieć nie nauczyła się odpowiednich wzorców w danych treningowych.

Jeśli jednak trenujesz zbyt długo, model zacznie się przesadzać i uczyć się wzorców z danych szkoleniowych, które nie są uogólniane na dane testowe. Musimy znaleźć równowagę. Zrozumienie, jak trenować przez odpowiednią liczbę epok, które omówimy poniżej, jest przydatną umiejętnością.

Aby zapobiec nadmiernemu dopasowaniu, najlepszym rozwiązaniem jest korzystanie z pełniejszych danych treningowych. Zbiór danych powinien obejmować pełny zakres danych wejściowych, które model ma obsłużyć. Dodatkowe dane mogą być przydatne tylko wtedy, gdy obejmują nowe i interesujące przypadki.

Model wytrenowany na pełniejszych danych naturalnie będzie lepiej uogólniał. Kiedy nie jest to już możliwe, następnym najlepszym rozwiązaniem jest użycie technik takich jak regularyzacja. Nakładają one ograniczenia na ilość i typ informacji, które może przechowywać Twój model. Jeśli sieć może sobie pozwolić na zapamiętanie tylko niewielkiej liczby wzorców, proces optymalizacji zmusi ją do skupienia się na najbardziej widocznych wzorcach, które mają większą szansę na dobre uogólnienie.

W tym notatniku omówimy kilka typowych technik regularyzacji i wykorzystamy je do ulepszenia modelu klasyfikacji.

Ustawiać

Przed rozpoczęciem zaimportuj niezbędne pakiety:

import tensorflow as tf

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

print(tf.__version__)
2.5.0
!pip install 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)

Zbiór danych Higgsa

Celem tego samouczka nie jest wykonywanie fizyki cząstek, więc nie rozpamiętuj szczegółów zestawu danych. Zawiera 11 000 000 przykładów, każdy z 28 cechami i binarną etykietą klasy.

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 [==============================] - 148s 0us/step
FEATURES = 28

tf.data.experimental.CsvDataset klasa może być używany do odczytu zapisów csv bezpośrednio z pliku gzip bez etapu pośredniego dekompresji.

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

Ta klasa czytnika csv zwraca listę skalarów dla każdego rekordu. Poniższa funkcja przepakowuje listę skalarów do pary (wektor_funkcji, etykieta).

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

TensorFlow jest najbardziej wydajny podczas pracy na dużych partiach danych.

Więc zamiast zapakowania każdy wiersz indywidualnie utworzyć nowy Dataset , które ma partii 10000-przykładach stosuje pack_row funkcję do każdej partii, a następnie dzieli partie kopii zapasowej do poszczególnych zapisów:

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

Wystarczy popatrzeć na niektóre z zapisów z tej nowej packed_ds .

Funkcje nie są idealnie znormalizowane, ale to wystarczy w tym samouczku.

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

Aby ten samouczek był stosunkowo krótki, użyj tylko pierwszych 1000 próbek do walidacji i następnych 10 000 do szkolenia:

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

W Dataset.skip i Dataset.take metody uczynić to łatwe.

Jednocześnie, należy użyć Dataset.cache sposób, aby zapewnić, że ładowarka nie wymaga ponownego odczytania danych z pliku z każdej epoki:

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)>

Te zbiory danych zwracają poszczególne przykłady. Za pomocą .batch metody tworzenia partii o odpowiedniej wielkości do szkolenia. Przed dozowania również pamiętać, aby .shuffle i .repeat zestaw treningowy.

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

Zademonstruj nadmierne dopasowanie

Najprostszym sposobem uniknięcia nadmiernego dopasowania jest rozpoczęcie od małego modelu: modelu z niewielką liczbą parametrów, które można nauczyć się (co zależy od liczby warstw i liczby jednostek na warstwę). W uczeniu głębokim liczba parametrów, których można się nauczyć w modelu, jest często określana jako „pojemność” modelu.

Intuicyjnie, model z większą liczbą parametrów będzie miał większą „zdolność zapamiętywania”, a zatem będzie w stanie łatwo nauczyć się doskonałego odwzorowania słownikowego między próbkami uczącymi a ich celami, mapowania bez żadnej mocy uogólniania, ale byłoby to bezużyteczne przy przewidywaniu na wcześniej niewidocznych danych.

Zawsze miej to na uwadze: modele uczenia głębokiego zwykle dobrze dopasowują się do danych treningowych, ale prawdziwym wyzwaniem jest uogólnianie, a nie dopasowanie.

Z drugiej strony, jeśli sieć ma ograniczone zasoby zapamiętywania, nie będzie w stanie tak łatwo nauczyć się mapowania. Aby zminimalizować jego utratę, będzie musiał nauczyć się skompresowanych reprezentacji, które mają większą moc predykcyjną. Jednocześnie, jeśli zmniejszysz swój model, będzie miał trudności z dopasowaniem do danych treningowych. Istnieje równowaga między „zbyt dużą pojemnością” a „niewystarczającą pojemnością”.

Niestety, nie ma magicznej formuły, która pozwoliłaby określić odpowiedni rozmiar lub architekturę Twojego modelu (pod względem liczby warstw lub odpowiedniego rozmiaru dla każdej warstwy). Będziesz musiał poeksperymentować, używając szeregu różnych architektur.

Aby znaleźć odpowiedni rozmiar modelu, najlepiej zacząć od stosunkowo niewielkiej liczby warstw i parametrów, a następnie zacząć zwiększać rozmiar warstw lub dodawać nowe warstwy, aż zobaczysz malejące zwroty z utraty walidacji.

Zacznij od prostego modelu używając tylko layers.Dense jako punkt odniesienia, a następnie utworzyć większe wersje, i porównać je.

Procedura szkolenia

Wiele modeli trenuje lepiej, jeśli stopniowo zmniejszasz tempo uczenia się podczas treningu. Użyj optimizers.schedules zmniejsza szybkość uczenia się w czasie:

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)

Kod powyżej powoduje ustawienie schedules.InverseTimeDecay do hiperbolicznie zmniejszyć szybkość uczenia się na 1/2 stopy bazowej w 1000 epok 1/3 przy 2000 epok i tak dalej.

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

Każdy model w tym samouczku będzie używał tej samej konfiguracji szkolenia. Więc skonfiguruj je w sposób wielokrotnego użytku, zaczynając od listy wywołań zwrotnych.

Szkolenie do tego samouczka trwa przez wiele krótkich epok. Aby zmniejszyć hałas rejestrowanie korzystania z tfdocs.EpochDots która po prostu drukuje . dla każdej epoki oraz pełny zestaw metryk co 100 epok.

Obok m.in. callbacks.EarlyStopping aby uniknąć długich i niepotrzebnych razy szkoleniowych. Zauważ, że ta funkcja zwrotna jest ustawiony na monitorowanie val_binary_crossentropy , a nie val_loss . Ta różnica będzie ważna później.

Zastosowanie callbacks.TensorBoard wygenerować logi TensorBoard w szkoleniu.

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

Podobnie każdy model będzie korzystać z tych samych Model.compile i Model.fit ustawienia:

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

Mały model

Zacznij od wytrenowania modelu:

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_begin` is slow compared to the batch time (batch time: 0.0031s vs `on_train_batch_begin` time: 0.0346s). Check your callbacks.
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0031s vs `on_train_batch_end` time: 0.0125s). Check your callbacks.

Epoch: 0, accuracy:0.5093,  binary_crossentropy:0.7681,  loss:0.7681,  val_accuracy:0.5000,  val_binary_crossentropy:0.7207,  val_loss:0.7207,  
....................................................................................................
Epoch: 100, accuracy:0.6058,  binary_crossentropy:0.6244,  loss:0.6244,  val_accuracy:0.5700,  val_binary_crossentropy:0.6322,  val_loss:0.6322,  
....................................................................................................
Epoch: 200, accuracy:0.6220,  binary_crossentropy:0.6124,  loss:0.6124,  val_accuracy:0.5990,  val_binary_crossentropy:0.6202,  val_loss:0.6202,  
....................................................................................................
Epoch: 300, accuracy:0.6388,  binary_crossentropy:0.6045,  loss:0.6045,  val_accuracy:0.6150,  val_binary_crossentropy:0.6114,  val_loss:0.6114,  
....................................................................................................
Epoch: 400, accuracy:0.6475,  binary_crossentropy:0.5976,  loss:0.5976,  val_accuracy:0.6270,  val_binary_crossentropy:0.6012,  val_loss:0.6012,  
....................................................................................................
Epoch: 500, accuracy:0.6579,  binary_crossentropy:0.5917,  loss:0.5917,  val_accuracy:0.6390,  val_binary_crossentropy:0.5929,  val_loss:0.5929,  
....................................................................................................
Epoch: 600, accuracy:0.6662,  binary_crossentropy:0.5878,  loss:0.5878,  val_accuracy:0.6410,  val_binary_crossentropy:0.5890,  val_loss:0.5890,  
....................................................................................................
Epoch: 700, accuracy:0.6664,  binary_crossentropy:0.5847,  loss:0.5847,  val_accuracy:0.6670,  val_binary_crossentropy:0.5865,  val_loss:0.5865,  
....................................................................................................
Epoch: 800, accuracy:0.6709,  binary_crossentropy:0.5822,  loss:0.5822,  val_accuracy:0.6460,  val_binary_crossentropy:0.5896,  val_loss:0.5896,  
....................................................................................................
Epoch: 900, accuracy:0.6772,  binary_crossentropy:0.5793,  loss:0.5793,  val_accuracy:0.6540,  val_binary_crossentropy:0.5880,  val_loss:0.5880,  
...................

Teraz sprawdź, jak poradził sobie model:

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

Mały model

Aby sprawdzić, czy możesz pobić wydajność małego modelu, stopniowo trenuj kilka większych modeli.

Wypróbuj dwie ukryte warstwy po 16 jednostek każda:

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_begin` is slow compared to the batch time (batch time: 0.0030s vs `on_train_batch_begin` time: 0.0258s). Check your callbacks.
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0030s vs `on_train_batch_end` time: 0.0176s). Check your callbacks.

Epoch: 0, accuracy:0.4757,  binary_crossentropy:0.7130,  loss:0.7130,  val_accuracy:0.4630,  val_binary_crossentropy:0.7012,  val_loss:0.7012,  
....................................................................................................
Epoch: 100, accuracy:0.6295,  binary_crossentropy:0.6092,  loss:0.6092,  val_accuracy:0.6120,  val_binary_crossentropy:0.6145,  val_loss:0.6145,  
....................................................................................................
Epoch: 200, accuracy:0.6575,  binary_crossentropy:0.5879,  loss:0.5879,  val_accuracy:0.6520,  val_binary_crossentropy:0.5976,  val_loss:0.5976,  
....................................................................................................
Epoch: 300, accuracy:0.6758,  binary_crossentropy:0.5774,  loss:0.5774,  val_accuracy:0.6610,  val_binary_crossentropy:0.5958,  val_loss:0.5958,  
....................................................................................................
Epoch: 400, accuracy:0.6830,  binary_crossentropy:0.5698,  loss:0.5698,  val_accuracy:0.6690,  val_binary_crossentropy:0.5949,  val_loss:0.5949,  
....................................................................................................
Epoch: 500, accuracy:0.6873,  binary_crossentropy:0.5650,  loss:0.5650,  val_accuracy:0.6720,  val_binary_crossentropy:0.5930,  val_loss:0.5930,  
....................................................................................................
Epoch: 600, accuracy:0.6923,  binary_crossentropy:0.5600,  loss:0.5600,  val_accuracy:0.6570,  val_binary_crossentropy:0.5946,  val_loss:0.5946,  
......................................................

Średni model

Teraz wypróbuj 3 ukryte warstwy po 64 jednostki każda:

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)
])

I trenuj model, używając tych samych danych:

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_begin` is slow compared to the batch time (batch time: 0.0033s vs `on_train_batch_begin` time: 0.0251s). Check your callbacks.
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0033s vs `on_train_batch_end` time: 0.0189s). Check your callbacks.

Epoch: 0, accuracy:0.5026,  binary_crossentropy:0.6944,  loss:0.6944,  val_accuracy:0.4740,  val_binary_crossentropy:0.6830,  val_loss:0.6830,  
....................................................................................................
Epoch: 100, accuracy:0.7164,  binary_crossentropy:0.5242,  loss:0.5242,  val_accuracy:0.6490,  val_binary_crossentropy:0.6316,  val_loss:0.6316,  
....................................................................................................
Epoch: 200, accuracy:0.7919,  binary_crossentropy:0.4224,  loss:0.4224,  val_accuracy:0.6480,  val_binary_crossentropy:0.7022,  val_loss:0.7022,  
.......................................

Duży model

W ramach ćwiczenia możesz stworzyć jeszcze większy model i zobaczyć, jak szybko zaczyna się on przesadzać. Następnie dodajmy do tego testu sieć, która ma znacznie większą pojemność, znacznie większą niż wynikałoby to z problemu:

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)
])

I znowu wytrenuj model, używając tych samych danych:

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_begin` is slow compared to the batch time (batch time: 0.0033s vs `on_train_batch_begin` time: 0.0237s). Check your callbacks.
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0033s vs `on_train_batch_end` time: 0.0182s). Check your callbacks.

Epoch: 0, accuracy:0.5116,  binary_crossentropy:0.7680,  loss:0.7680,  val_accuracy:0.5440,  val_binary_crossentropy:0.6753,  val_loss:0.6753,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0021,  loss:0.0021,  val_accuracy:0.6610,  val_binary_crossentropy:1.8058,  val_loss:1.8058,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0001,  loss:0.0001,  val_accuracy:0.6500,  val_binary_crossentropy:2.4712,  val_loss:2.4712,  
.........................

Wykreśl straty związane ze szkoleniem i walidacją

Linie ciągłe pokazują utratę treningu, a linie przerywane utratę walidacji (pamiętaj: mniejsza strata walidacji oznacza lepszy model).

O ile budowanie większego modelu daje mu więcej mocy, to jeśli ta moc nie jest w jakiś sposób ograniczona, może łatwo dopasować się do zestawu treningowego.

W tym przykładzie, zazwyczaj tylko "Tiny" modelu udaje się uniknąć przeuczenia całkowicie, a każda z większych modeli szybciej nadmierne dopasowanie do danych. To staje się tak dotkliwe dla "large" modelu, że trzeba przełączyć działkę do dziennika skalę, aby naprawdę zobaczyć, co się dzieje.

Jest to widoczne, jeśli wykreślisz i porównasz metryki walidacji z metrykami uczącymi.

  • To normalne, że istnieje niewielka różnica.
  • Jeśli obie metryki idą w tym samym kierunku, wszystko jest w porządku.
  • Jeśli metryka walidacji zaczyna się zatrzymywać, podczas gdy metryka treningu nadal się poprawia, prawdopodobnie jesteś bliski nadmiernego dopasowania.
  • Jeśli metryka walidacji idzie w złym kierunku, model wyraźnie przesadza.
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

Zobacz w TensorBoard

Wszystkie te modele zapisały logi TensorBoard podczas treningu.

Otwórz wbudowaną przeglądarkę TensorBoard w notatniku:


# Load the TensorBoard notebook extension
%load_ext tensorboard

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

Można zobaczyć wyniki poprzedniego uruchomienia tego notebooka na TensorBoard.dev .

TensorBoard.dev to zarządzane środowisko do hostingu, śledzenia i udostępniania eksperymentów ML ze wszystkimi.

Jest to także zawarte w <iframe> dla wygody:

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

Jeśli chcesz podzielić się TensorBoard wyniki można przesłać dzienniki do TensorBoard.dev kopiując następujące wartości w kodzie komórek.

tensorboard dev upload --logdir  {logdir}/sizes

Strategie zapobiegania nadmiernemu dopasowaniu

Zanim zagłębimy się w treści tej sekcji skopiować dzienniki treningowe z "Tiny" modelu powyżej, aby użyć jako punkt odniesienia dla porównania.

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

Dodaj regularyzację wagi

Być może znasz zasadę Brzytwy Ockhama: biorąc pod uwagę dwa wyjaśnienia czegoś, wyjaśnieniem, które najprawdopodobniej będzie poprawne, jest to „najprostsze”, takie, które zawiera najmniej założeń. Dotyczy to również modeli wyuczonych przez sieci neuronowe: biorąc pod uwagę niektóre dane szkoleniowe i architekturę sieci, istnieje wiele zestawów wartości wag (wiele modeli), które mogłyby wyjaśnić dane, a prostsze modele są mniej podatne na przeuczenie niż te złożone.

„Prosty model” w tym kontekście to model, w którym rozkład wartości parametrów ma mniejszą entropię (lub model z mniejszą liczbą parametrów, jak widzieliśmy w powyższej sekcji). Tak więc powszechnym sposobem łagodzenia nadmiernego dopasowania jest nałożenie ograniczeń na złożoność sieci poprzez wymuszenie na jej wagach przyjmowania małych wartości, co sprawia, że ​​rozkład wartości wag jest bardziej „regularny”. Nazywa się to „regulacją wagi” i odbywa się poprzez dodanie do funkcji strat sieci kosztów związanych z posiadaniem dużych wag. Koszt ten występuje w dwóch wariantach:

  • L1 uregulowanie , gdzie koszt dodany jest proporcjonalna do wartości bezwzględnej współczynników wag (czyli do tego, co nazywa się „normą L1” z ciężarkami).

  • Uregulowanie L2 , gdzie koszt dodany jest proporcjonalna do kwadratu wartości współczynników wag (czyli do tego, co nazywa się squared „norma L2” z ciężarkami). Regularyzacja L2 jest również nazywana zanikiem wagi w kontekście sieci neuronowych. Nie pozwól, aby ta inna nazwa Cię zmyliła: spadek wagi jest matematycznie dokładnie taki sam jak regularyzacja L2.

Regularyzacja L1 przesuwa wagi w kierunku dokładnie zera, zachęcając do rzadkiego modelu. Regularyzacja L2 będzie karać parametry wag, nie czyniąc ich rzadkimi, ponieważ kara spada do zera za małe wagi – jeden z powodów, dla których L2 jest bardziej powszechny.

W tf.keras , waga regularyzacji dodaje przepuszczając przykłady masy regularizer warstwom jako parametry kluczowe. Dodajmy teraz regularyzację wagi 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_begin` is slow compared to the batch time (batch time: 0.0038s vs `on_train_batch_begin` time: 0.0242s). Check your callbacks.
WARNING:tensorflow:Callback method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0038s vs `on_train_batch_end` time: 0.0199s). Check your callbacks.

Epoch: 0, accuracy:0.5059,  binary_crossentropy:0.7720,  loss:2.2831,  val_accuracy:0.4620,  val_binary_crossentropy:0.7035,  val_loss:2.1321,  
....................................................................................................
Epoch: 100, accuracy:0.6490,  binary_crossentropy:0.5996,  loss:0.6228,  val_accuracy:0.6270,  val_binary_crossentropy:0.5898,  val_loss:0.6131,  
....................................................................................................
Epoch: 200, accuracy:0.6737,  binary_crossentropy:0.5826,  loss:0.6061,  val_accuracy:0.6680,  val_binary_crossentropy:0.5857,  val_loss:0.6096,  
....................................................................................................
Epoch: 300, accuracy:0.6842,  binary_crossentropy:0.5748,  loss:0.5993,  val_accuracy:0.6840,  val_binary_crossentropy:0.5754,  val_loss:0.5998,  
....................................................................................................
Epoch: 400, accuracy:0.6934,  binary_crossentropy:0.5620,  loss:0.5862,  val_accuracy:0.6690,  val_binary_crossentropy:0.5825,  val_loss:0.6066,  
.....................................................................................

l2(0.001) oznacza, że każdy współczynnik w osnowie masy warstwy doda 0.001 * weight_coefficient_value**2 do całkowitej utraty sieci.

Dlatego jesteśmy monitorowania binary_crossentropy bezpośrednio. Ponieważ nie ma wmieszanego składnika regularyzacji.

Tak, że samo "Large" Model z L2 karnych uregulowanie sprawuje się dużo lepiej:

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

png

Jak można widać, "L2" uregulowana model jest teraz znacznie bardziej konkurencyjny w stosunku do "Tiny" modelu. Ten "L2" model jest również znacznie bardziej odporna na przeuczenia niż "Large" modelu został on oparty pomimo takiej samej liczby parametrów.

Więcej informacji

Należy zwrócić uwagę na dwie ważne kwestie dotyczące tego rodzaju regularyzacji.

Po pierwsze: jeśli piszesz własną pętlę treningowy, a następnie trzeba będzie zwrócić się do modelu strat legalizacji.

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

Po drugie: Ta implementacja działa poprzez dodanie do utraty wagi kar modelu, a następnie stosując standardową procedurę optymalizacji potem.

Istnieje drugie podejście, które zamiast tego uruchamia optymalizator tylko na surowej stracie, a następnie, stosując obliczoną wartość kroku, optymalizator stosuje również pewien zanik wagi. Ten „Decoupled Waga Decay” jest postrzegana w optymalizujące jak optimizers.FTRL i optimizers.AdamW .

Dodaj rezygnację

Dropout to jedna z najskuteczniejszych i najczęściej stosowanych technik regularyzacji sieci neuronowych, opracowana przez Hintona i jego studentów z Uniwersytetu w Toronto.

Intuicyjne wyjaśnienie porzucania jest takie, że ponieważ poszczególne węzły w sieci nie mogą polegać na wynikach innych, każdy węzeł musi samodzielnie generować funkcje, które są przydatne.

Dropout, nałożony na warstwę, polega na losowym „wyrzuceniu” (tj. ustawionym na zero) szeregu cech wyjściowych warstwy podczas treningu. Powiedzmy, że dana warstwa normalnie zwróciłaby wektor [0,2, 0,5, 1,3, 0,8, 1,1] dla danej próbki wejściowej podczas uczenia; po zastosowaniu dropout wektor ten będzie miał kilka wpisów zerowych rozmieszczonych losowo, np. [0, 0.5, 1.3, 0, 1.1].

„Wskaźnik rezygnacji” to ułamek funkcji, które są wyzerowane; zwykle wynosi od 0,2 do 0,5. W czasie testu żadne jednostki nie są pomijane, a zamiast tego wartości wyjściowe warstwy są zmniejszane o współczynnik równy wskaźnikowi rezygnacji, aby zrównoważyć fakt, że więcej jednostek jest aktywnych niż w czasie treningu.

W tf.keras można wprowadzać przerywania w sieci poprzez warstwy Wypadanie, który zostanie zastosowany do produkcji warstwy tuż przed.

Dodajmy dwie warstwy Dropout w naszej sieci, aby zobaczyć, jak dobrze radzą sobie z redukcją nadmiernego dopasowania:

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_begin` is slow compared to the batch time (batch time: 0.0040s vs `on_train_batch_begin` time: 0.0241s). Check your callbacks.
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.0208s). Check your callbacks.

Epoch: 0, accuracy:0.5060,  binary_crossentropy:0.7949,  loss:0.7949,  val_accuracy:0.5140,  val_binary_crossentropy:0.6710,  val_loss:0.6710,  
....................................................................................................
Epoch: 100, accuracy:0.6623,  binary_crossentropy:0.5950,  loss:0.5950,  val_accuracy:0.6840,  val_binary_crossentropy:0.5723,  val_loss:0.5723,  
....................................................................................................
Epoch: 200, accuracy:0.6897,  binary_crossentropy:0.5559,  loss:0.5559,  val_accuracy:0.6800,  val_binary_crossentropy:0.5971,  val_loss:0.5971,  
....................................................................................................
Epoch: 300, accuracy:0.7202,  binary_crossentropy:0.5114,  loss:0.5114,  val_accuracy:0.6800,  val_binary_crossentropy:0.5984,  val_loss:0.5984,  
...............................................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

To wynika z tego wykresu, że oba te uregulowania zbliża się poprawić zachowanie "Large" modelu. Ale to jeszcze nie pokonał nawet "Tiny" bazową.

Następnie wypróbuj je oba razem i zobacz, czy to lepiej.

Połączone L2 + odpadanie

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_begin` is slow compared to the batch time (batch time: 0.0040s vs `on_train_batch_begin` time: 0.0245s). Check your callbacks.
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.0214s). Check your callbacks.

Epoch: 0, accuracy:0.5056,  binary_crossentropy:0.8201,  loss:0.9784,  val_accuracy:0.5130,  val_binary_crossentropy:0.6691,  val_loss:0.8269,  
....................................................................................................
Epoch: 100, accuracy:0.6409,  binary_crossentropy:0.6052,  loss:0.6362,  val_accuracy:0.6670,  val_binary_crossentropy:0.5831,  val_loss:0.6139,  
....................................................................................................
Epoch: 200, accuracy:0.6673,  binary_crossentropy:0.5893,  loss:0.6147,  val_accuracy:0.6880,  val_binary_crossentropy:0.5666,  val_loss:0.5920,  
....................................................................................................
Epoch: 300, accuracy:0.6724,  binary_crossentropy:0.5814,  loss:0.6092,  val_accuracy:0.6850,  val_binary_crossentropy:0.5638,  val_loss:0.5916,  
....................................................................................................
Epoch: 400, accuracy:0.6791,  binary_crossentropy:0.5764,  loss:0.6061,  val_accuracy:0.6960,  val_binary_crossentropy:0.5536,  val_loss:0.5832,  
....................................................................................................
Epoch: 500, accuracy:0.6750,  binary_crossentropy:0.5722,  loss:0.6037,  val_accuracy:0.6760,  val_binary_crossentropy:0.5583,  val_loss:0.5899,  
....................................................................................................
Epoch: 600, accuracy:0.6818,  binary_crossentropy:0.5651,  loss:0.5989,  val_accuracy:0.6940,  val_binary_crossentropy:0.5422,  val_loss:0.5761,  
....................................................................................................
Epoch: 700, accuracy:0.6882,  binary_crossentropy:0.5594,  loss:0.5943,  val_accuracy:0.6880,  val_binary_crossentropy:0.5436,  val_loss:0.5786,  
....................................................................................................
Epoch: 800, accuracy:0.6886,  binary_crossentropy:0.5567,  loss:0.5927,  val_accuracy:0.6960,  val_binary_crossentropy:0.5446,  val_loss:0.5807,  
....................................................................................................
Epoch: 900, accuracy:0.6994,  binary_crossentropy:0.5535,  loss:0.5907,  val_accuracy:0.6900,  val_binary_crossentropy:0.5463,  val_loss:0.5835,  
................................................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

Model ten z "Combined" uregulowania jest oczywiście jednym z najlepszych do tej pory.

Zobacz w TensorBoard

Modele te rejestrowały również logi TensorBoard.

Aby otworzyć osadzoną przeglądarkę tensorboard w notatniku, skopiuj następujące elementy do komórki kodu:

%tensorboard --logdir {logdir}/regularizers

Można zobaczyć wyniki poprzedniego uruchomienia tego notebooka na TensorDoard.dev .

Jest to także zawarte w <iframe> dla wygody:

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

To zostało przesłane z:

tensorboard dev upload --logdir  {logdir}/regularizers

Wnioski

Podsumowując: oto najczęstsze sposoby zapobiegania nadmiernemu dopasowaniu w sieciach neuronowych:

  • Uzyskaj więcej danych treningowych.
  • Zmniejsz pojemność sieci.
  • Dodaj regularyzację wagi.
  • Dodaj rezygnację.

Dwa ważne podejścia nieomówione w tym przewodniku to:

  • powiększanie danych
  • normalizacja wsadowa

Pamiętaj, że każda metoda może pomóc sama, ale często ich łączenie może być jeszcze skuteczniejsze.

# 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.