Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
Jak zawsze, kod w tym przykładzie będzie korzystał z API tf.keras
, o którym możesz dowiedzieć się więcej w przewodniku TensorFlow Keras .
W obu poprzednich przykładach — klasyfikowaniu tekstu i przewidywaniu zużycia paliwa — widzieliśmy, że dokładność naszego modelu na danych walidacyjnych osiąga szczyt po uczeniu przez kilka epok, a następnie stagnuje lub zaczyna spadać.
Innymi słowy, nasz model nadawałby się do danych uczących. Ważna jest nauka radzenia sobie z nadmiernym dopasowaniem. Chociaż często można uzyskać wysoką dokładność zestawu uczącego , tak naprawdę chcemy opracować modele, które dobrze uogólniają zestaw testowy (lub dane, których wcześniej nie widzieli).
Przeciwieństwem overfittingu jest underfitting . 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, co omówimy poniżej, jest użyteczną 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 wyszkolony 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ć tylko na zapamiętanie 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.8.0-rc1
!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 fizyka cząstek elementarnych, więc nie zajmuj się szczegółami 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 [==============================] - 123s 0us/step 2816417792/2816407858 [==============================] - 123s 0us/step
FEATURES = 28
Klasa tf.data.experimental.CsvDataset
może służyć do odczytywania rekordów csv bezpośrednio z pliku gzip bez pośredniego kroku 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.
Dlatego zamiast przepakowywania każdego wiersza osobno, utwórz nowy Dataset
, który pobiera partie 10000 przykładów, stosuje funkcję pack_row
do każdej partii, a następnie dzieli partie z powrotem na pojedyncze rekordy:
packed_ds = ds.batch(10000).map(pack_row).unbatch()
Spójrz na niektóre rekordy z tego nowego 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)
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
Ułatwiają to metody Dataset.skip
i Dataset.take
.
Jednocześnie użyj metody Dataset.cache
, aby upewnić się, że program ładujący nie musi ponownie odczytywać danych z pliku w każdej epoce:
validate_ds = packed_ds.take(N_VALIDATION).cache()
train_ds = packed_ds.skip(N_VALIDATION).take(N_TRAIN).cache()
train_ds
<CacheDataset element_spec=(TensorSpec(shape=(28,), dtype=tf.float32, name=None), TensorSpec(shape=(), dtype=tf.float32, name=None))>
Te zbiory danych zwracają poszczególne przykłady. Użyj metody .batch
, aby utworzyć partie o odpowiednim rozmiarze do szkolenia. Przed dozowaniem pamiętaj również o .shuffle
i .repeat
zestawu treningowego.
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: Model z niewielką liczbą parametrów do nauczenia (co jest określane przez liczbę warstw i liczbę 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 warstw. layers.Dense
jako punkt odniesienia, a następnie utwórz większe wersje i porównaj je.
Procedura szkolenia
Wiele modeli trenuje lepiej, jeśli stopniowo zmniejszasz tempo uczenia się podczas treningu. Użyj optimizers.schedules
, aby zmniejszyć tempo 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)
Powyższy kod ustawia schedules.InverseTimeDecay
aby hiperbolicznie zmniejszyć szybkość uczenia się do 1/2 stawki podstawowej w 1000 epok, 1/3 w 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')
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 zredukować hałas związany z logowaniem, użyj tfdocs.EpochDots
, które po prostu wyświetlają .
dla każdej epoki oraz pełny zestaw metryk co 100 epok.
Następnie callbacks.EarlyStopping
, aby uniknąć długich i niepotrzebnych czasów szkolenia. Zauważ, że to wywołanie zwrotne jest ustawione na monitorowanie val_binary_crossentropy
, a nie val_loss
. Ta różnica będzie ważna później.
Użyj funkcji callbacks.TensorBoard
, aby wygenerować logi TensorBoard na potrzeby szkolenia.
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 używał tych samych ustawień Model.compile
i 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
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 _________________________________________________________________ Epoch: 0, accuracy:0.4961, binary_crossentropy:0.7294, loss:0.7294, val_accuracy:0.4840, val_binary_crossentropy:0.7200, val_loss:0.7200, .................................................................................................... Epoch: 100, accuracy:0.5931, binary_crossentropy:0.6279, loss:0.6279, val_accuracy:0.5860, val_binary_crossentropy:0.6288, val_loss:0.6288, .................................................................................................... Epoch: 200, accuracy:0.6157, binary_crossentropy:0.6178, loss:0.6178, val_accuracy:0.6200, val_binary_crossentropy:0.6134, val_loss:0.6134, .................................................................................................... Epoch: 300, accuracy:0.6370, binary_crossentropy:0.6086, loss:0.6086, val_accuracy:0.6220, val_binary_crossentropy:0.6055, val_loss:0.6055, .................................................................................................... Epoch: 400, accuracy:0.6522, binary_crossentropy:0.6008, loss:0.6008, val_accuracy:0.6260, val_binary_crossentropy:0.5997, val_loss:0.5997, .................................................................................................... Epoch: 500, accuracy:0.6513, binary_crossentropy:0.5946, loss:0.5946, val_accuracy:0.6480, val_binary_crossentropy:0.5911, val_loss:0.5911, .................................................................................................... Epoch: 600, accuracy:0.6636, binary_crossentropy:0.5894, loss:0.5894, val_accuracy:0.6390, val_binary_crossentropy:0.5898, val_loss:0.5898, .................................................................................................... Epoch: 700, accuracy:0.6696, binary_crossentropy:0.5852, loss:0.5852, val_accuracy:0.6530, val_binary_crossentropy:0.5870, val_loss:0.5870, .................................................................................................... Epoch: 800, accuracy:0.6706, binary_crossentropy:0.5824, loss:0.5824, val_accuracy:0.6590, val_binary_crossentropy:0.5850, val_loss:0.5850, .................................................................................................... Epoch: 900, accuracy:0.6709, binary_crossentropy:0.5796, loss:0.5796, val_accuracy:0.6680, val_binary_crossentropy:0.5831, val_loss:0.5831, .................................................................................................... Epoch: 1000, accuracy:0.6780, binary_crossentropy:0.5769, loss:0.5769, val_accuracy:0.6530, val_binary_crossentropy:0.5851, val_loss:0.5851, .................................................................................................... Epoch: 1100, accuracy:0.6735, binary_crossentropy:0.5752, loss:0.5752, val_accuracy:0.6620, val_binary_crossentropy:0.5807, val_loss:0.5807, .................................................................................................... Epoch: 1200, accuracy:0.6759, binary_crossentropy:0.5729, loss:0.5729, val_accuracy:0.6620, val_binary_crossentropy:0.5792, val_loss:0.5792, .................................................................................................... Epoch: 1300, accuracy:0.6849, binary_crossentropy:0.5716, loss:0.5716, val_accuracy:0.6450, val_binary_crossentropy:0.5859, val_loss:0.5859, .................................................................................................... Epoch: 1400, accuracy:0.6790, binary_crossentropy:0.5695, loss:0.5695, val_accuracy:0.6700, val_binary_crossentropy:0.5776, val_loss:0.5776, .................................................................................................... Epoch: 1500, accuracy:0.6824, binary_crossentropy:0.5681, loss:0.5681, val_accuracy:0.6730, val_binary_crossentropy:0.5761, val_loss:0.5761, .................................................................................................... Epoch: 1600, accuracy:0.6828, binary_crossentropy:0.5669, loss:0.5669, val_accuracy:0.6690, val_binary_crossentropy:0.5766, val_loss:0.5766, .................................................................................................... Epoch: 1700, accuracy:0.6874, binary_crossentropy:0.5657, loss:0.5657, val_accuracy:0.6600, val_binary_crossentropy:0.5774, val_loss:0.5774, .................................................................................................... Epoch: 1800, accuracy:0.6845, binary_crossentropy:0.5655, loss:0.5655, val_accuracy:0.6780, val_binary_crossentropy:0.5752, val_loss:0.5752, .................................................................................................... Epoch: 1900, accuracy:0.6837, binary_crossentropy:0.5644, loss:0.5644, val_accuracy:0.6790, val_binary_crossentropy:0.5753, val_loss:0.5753, .................................................................................................... Epoch: 2000, accuracy:0.6853, binary_crossentropy:0.5632, loss:0.5632, val_accuracy:0.6780, val_binary_crossentropy:0.5753, val_loss:0.5753, .................................................................................................... Epoch: 2100, accuracy:0.6871, binary_crossentropy:0.5625, loss:0.5625, val_accuracy:0.6670, val_binary_crossentropy:0.5769, val_loss:0.5769, ...................................
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)
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 _________________________________________________________________ Epoch: 0, accuracy:0.4864, binary_crossentropy:0.7769, loss:0.7769, val_accuracy:0.4930, val_binary_crossentropy:0.7211, val_loss:0.7211, .................................................................................................... Epoch: 100, accuracy:0.6386, binary_crossentropy:0.6052, loss:0.6052, val_accuracy:0.6020, val_binary_crossentropy:0.6177, val_loss:0.6177, .................................................................................................... Epoch: 200, accuracy:0.6697, binary_crossentropy:0.5829, loss:0.5829, val_accuracy:0.6310, val_binary_crossentropy:0.6018, val_loss:0.6018, .................................................................................................... Epoch: 300, accuracy:0.6838, binary_crossentropy:0.5721, loss:0.5721, val_accuracy:0.6490, val_binary_crossentropy:0.5940, val_loss:0.5940, .................................................................................................... Epoch: 400, accuracy:0.6911, binary_crossentropy:0.5656, loss:0.5656, val_accuracy:0.6430, val_binary_crossentropy:0.5985, val_loss:0.5985, .................................................................................................... Epoch: 500, accuracy:0.6930, binary_crossentropy:0.5607, loss:0.5607, val_accuracy:0.6430, val_binary_crossentropy:0.6028, val_loss:0.6028, .........................
Model średni
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 _________________________________________________________________ Epoch: 0, accuracy:0.5017, binary_crossentropy:0.6840, loss:0.6840, val_accuracy:0.4790, val_binary_crossentropy:0.6723, val_loss:0.6723, .................................................................................................... Epoch: 100, accuracy:0.7173, binary_crossentropy:0.5221, loss:0.5221, val_accuracy:0.6470, val_binary_crossentropy:0.6111, val_loss:0.6111, .................................................................................................... Epoch: 200, accuracy:0.7884, binary_crossentropy:0.4270, loss:0.4270, val_accuracy:0.6390, val_binary_crossentropy:0.7045, val_loss:0.7045, ..............................................................
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 _________________________________________________________________ Epoch: 0, accuracy:0.5145, binary_crossentropy:0.7740, loss:0.7740, val_accuracy:0.4980, val_binary_crossentropy:0.6793, val_loss:0.6793, .................................................................................................... Epoch: 100, accuracy:1.0000, binary_crossentropy:0.0020, loss:0.0020, val_accuracy:0.6600, val_binary_crossentropy:1.8540, val_loss:1.8540, .................................................................................................... Epoch: 200, accuracy:1.0000, binary_crossentropy:0.0001, loss:0.0001, val_accuracy:0.6560, val_binary_crossentropy:2.5293, val_loss:2.5293, ..........................
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).
Podczas gdy budowanie większego modelu daje mu większą moc, jeśli ta moc nie jest jakoś ograniczona, może łatwo przełożyć się na zestaw treningowy.
W tym przykładzie zazwyczaj tylko model "Tiny"
pozwala całkowicie uniknąć nadmiernego dopasowania, a każdy z większych modeli szybciej dopasowuje się do danych. Staje się to tak poważne dla "large"
modelu, że musisz przełączyć wykres na skalę logarytmiczną, 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]')
Zobacz w TensorBoard
Wszystkie te modele zapisały logi TensorBoard podczas treningu.
Otwórz wbudowaną przeglądarkę TensorBoard w notatniku:
#docs_infra: no_execute
# Load the TensorBoard notebook extension
%load_ext tensorboard
# Open an embedded TensorBoard viewer
%tensorboard --logdir {logdir}/sizes
Możesz zobaczyć wyniki poprzedniego uruchomienia tego notatnika na TensorBoard.dev .
TensorBoard.dev to zarządzane środowisko do hostingu, śledzenia i udostępniania eksperymentów ML ze wszystkimi.
Jest również zawarty w <iframe>
dla wygody:
display.IFrame(
src="https://tensorboard.dev/experiment/vW7jmmF9TmKmy3rbheMQpw/#scalars&_smoothingWeight=0.97",
width="100%", height="800px")
Jeśli chcesz udostępnić wyniki TensorBoard, możesz przesłać logi do TensorBoard.dev , kopiując następujące elementy do komórki kodu.
tensorboard dev upload --logdir {logdir}/sizes
Strategie zapobiegania nadmiernemu dopasowaniu
Zanim przejdziesz do treści tej sekcji, skopiuj dzienniki treningowe z powyższego modelu "Tiny"
, aby wykorzystać je jako punkt odniesienia dla porównania.
shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')
PosixPath('/tmp/tmpn1rdh98q/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, 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ą wyjaśniać 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 o całkowitej mniejszej liczbie parametrów, jak widzieliśmy w sekcji powyżej). Dlatego 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:
Regularyzacja L1 , gdzie dodany koszt jest proporcjonalny do bezwzględnej wartości współczynników wag (tj. do tzw. „norma L1” wag).
Regularyzacja L2 , gdzie dodany koszt jest proporcjonalny do kwadratu wartości współczynników wag (tj. do kwadratu „norma L2” wag). 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
, regularyzacja wagi jest dodawana przez przekazywanie instancji regulatora wagi do warstw jako argumentów słów kluczowych. 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 _________________________________________________________________ Epoch: 0, accuracy:0.5126, binary_crossentropy:0.7481, loss:2.2415, val_accuracy:0.4950, val_binary_crossentropy:0.6707, val_loss:2.0653, .................................................................................................... Epoch: 100, accuracy:0.6625, binary_crossentropy:0.5945, loss:0.6173, val_accuracy:0.6400, val_binary_crossentropy:0.5871, val_loss:0.6100, .................................................................................................... Epoch: 200, accuracy:0.6690, binary_crossentropy:0.5864, loss:0.6079, val_accuracy:0.6650, val_binary_crossentropy:0.5856, val_loss:0.6076, .................................................................................................... Epoch: 300, accuracy:0.6790, binary_crossentropy:0.5762, loss:0.5976, val_accuracy:0.6550, val_binary_crossentropy:0.5881, val_loss:0.6095, .................................................................................................... Epoch: 400, accuracy:0.6843, binary_crossentropy:0.5697, loss:0.5920, val_accuracy:0.6650, val_binary_crossentropy:0.5878, val_loss:0.6101, .................................................................................................... Epoch: 500, accuracy:0.6897, binary_crossentropy:0.5651, loss:0.5907, val_accuracy:0.6890, val_binary_crossentropy:0.5798, val_loss:0.6055, .................................................................................................... Epoch: 600, accuracy:0.6945, binary_crossentropy:0.5610, loss:0.5864, val_accuracy:0.6820, val_binary_crossentropy:0.5772, val_loss:0.6026, ..........................................................
l2(0.001)
oznacza, że każdy współczynnik w macierzy wag warstwy doda 0.001 * weight_coefficient_value**2
do całkowitej straty sieci.
Dlatego bezpośrednio monitorujemy binary_crossentropy
. Ponieważ nie ma wmieszanego składnika regularyzacji.
Tak więc ten sam "Large"
model z karą za regularyzację L2
działa znacznie lepiej:
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)
Jak widać, regularyzowany model "L2"
jest teraz znacznie bardziej konkurencyjny w stosunku do modelu "Tiny"
. Ten model "L2"
jest również znacznie bardziej odporny na przefazowanie niż model "Large"
, na którym był oparty, mimo że ma taką samą liczbę 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ę treningową, musisz koniecznie zapytać model o jego straty w regularyzacji.
result = l2_model(features)
regularization_loss=tf.add_n(l2_model.losses)
Po drugie: ta implementacja działa poprzez dodanie kar wagi do utraty modelu, a następnie zastosowanie standardowej procedury optymalizacji.
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 Weight Deas” jest widoczny w optimizers.FTRL
, takich jakOptimizers.FTRL 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 danych wyjściowych innych, każdy węzeł musi samodzielnie udostępniać 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żesz wprowadzić dropout w sieci za pośrednictwem warstwy Dropout, która jest stosowana na wyjściu 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 _________________________________________________________________ Epoch: 0, accuracy:0.4961, binary_crossentropy:0.8110, loss:0.8110, val_accuracy:0.5330, val_binary_crossentropy:0.6900, val_loss:0.6900, .................................................................................................... Epoch: 100, accuracy:0.6557, binary_crossentropy:0.5961, loss:0.5961, val_accuracy:0.6710, val_binary_crossentropy:0.5788, val_loss:0.5788, .................................................................................................... Epoch: 200, accuracy:0.6871, binary_crossentropy:0.5622, loss:0.5622, val_accuracy:0.6860, val_binary_crossentropy:0.5856, val_loss:0.5856, .................................................................................................... Epoch: 300, accuracy:0.7246, binary_crossentropy:0.5121, loss:0.5121, val_accuracy:0.6820, val_binary_crossentropy:0.5927, val_loss:0.5927, ............
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)
Z tego wykresu jasno wynika, że oba te podejścia do regularyzacji poprawiają zachowanie modelu "Large"
. Ale to nadal nie przebije nawet linii podstawowej "Tiny"
.
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 _________________________________________________________________ Epoch: 0, accuracy:0.5090, binary_crossentropy:0.8064, loss:0.9648, val_accuracy:0.4660, val_binary_crossentropy:0.6877, val_loss:0.8454, .................................................................................................... Epoch: 100, accuracy:0.6445, binary_crossentropy:0.6050, loss:0.6350, val_accuracy:0.6630, val_binary_crossentropy:0.5871, val_loss:0.6169, .................................................................................................... Epoch: 200, accuracy:0.6660, binary_crossentropy:0.5932, loss:0.6186, val_accuracy:0.6880, val_binary_crossentropy:0.5722, val_loss:0.5975, .................................................................................................... Epoch: 300, accuracy:0.6697, binary_crossentropy:0.5818, loss:0.6100, val_accuracy:0.6900, val_binary_crossentropy:0.5614, val_loss:0.5895, .................................................................................................... Epoch: 400, accuracy:0.6749, binary_crossentropy:0.5742, loss:0.6046, val_accuracy:0.6870, val_binary_crossentropy:0.5576, val_loss:0.5881, .................................................................................................... Epoch: 500, accuracy:0.6854, binary_crossentropy:0.5703, loss:0.6029, val_accuracy:0.6970, val_binary_crossentropy:0.5458, val_loss:0.5784, .................................................................................................... Epoch: 600, accuracy:0.6806, binary_crossentropy:0.5673, loss:0.6015, val_accuracy:0.6980, val_binary_crossentropy:0.5453, val_loss:0.5795, .................................................................................................... Epoch: 700, accuracy:0.6937, binary_crossentropy:0.5583, loss:0.5938, val_accuracy:0.6870, val_binary_crossentropy:0.5477, val_loss:0.5832, .................................................................................................... Epoch: 800, accuracy:0.6911, binary_crossentropy:0.5576, loss:0.5947, val_accuracy:0.7000, val_binary_crossentropy:0.5446, val_loss:0.5817, .......................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)
Ten model z regularyzacją "Combined"
jest oczywiście najlepszym do tej pory.
Zobacz w TensorBoard
Modele te rejestrowały również logi TensorBoard.
Aby otworzyć wbudowaną przeglądarkę tensorboard w notatniku, skopiuj następujące elementy do komórki kodu:
%tensorboard --logdir {logdir}/regularizers
Możesz zobaczyć wyniki poprzedniego uruchomienia tego notatnika na TensorDoard.dev .
Jest również zawarty 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 z siebie, 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.