Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

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 będzie korzystał z interfejsu API tf.keras , o którym można dowiedzieć się więcej w przewodniku TensorFlow Keras .

W obu poprzednich przykładach - klasyfikowaniu tekstu i prognozowaniu zużycia paliwa - widzieliśmy, że dokładność naszego modelu na danych walidacyjnych osiągnęłaby szczyt po treningu przez kilka epok, a następnie zatrzymałaby się lub zaczęła spadać.

Innymi słowy, nasz model byłby nadmiernie dopasowany do danych uczących . Ważne jest nauczenie się, jak radzić sobie z nadmiernym dopasowaniem. Chociaż często można osiągnąć wysoką dokładność na zbiorze uczącym , tak naprawdę chcemy opracować modele, które dobrze uogólniają zestaw testowy (lub dane, których wcześniej nie widzieli).

Przeciwieństwem overfittingu jest niedopasowanie . Niedopasowanie występuje, gdy wciąż jest miejsce na ulepszenie danych testowych. Może się tak zdarzyć z wielu powodów: jeśli model nie jest wystarczająco silny, jest nadmiernie uregulowany lub po prostu nie był wystarczająco długo szkolony. Oznacza to, że sieć nie nauczyła się odpowiednich wzorców w danych szkoleniowych.

Jeśli jednak będziesz trenować zbyt długo, model zacznie się nadmiernie dopasowywać i uczyć się wzorców na podstawie 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 przydatną umiejętnością.

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

Model wytrenowany na pełniejszych danych będzie naturalnie lepiej uogólniał. Kiedy nie jest to już możliwe, najlepszym rozwiązaniem jest użycie technik takich jak regularyzacja. To nakłada 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ększe szanse na dobre uogólnienie.

W tym notatniku przyjrzymy się kilku typowym technikom regularyzacji i użyjemy ich do ulepszenia modelu klasyfikacji.

Ustawiać

Zanim zaczniesz, zaimportuj niezbędne pakiety:

import tensorflow as tf

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

print(tf.__version__)
2.3.0

!pip install -q git+https://github.com/tensorflow/docs

import tensorflow_docs as tfdocs
import tensorflow_docs.modeling
import tensorflow_docs.plots
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

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 zajmowanie się fizyką cząstek elementarnych, więc nie rozwodź się nad szczegółami zbioru danych. Zawiera 11 000 000 przykładów, każdy z 28 funkcjami 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 [==============================] - 195s 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 etapu 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. Następująca funkcja przepakowuje tę listę skalarów w parę (wektor_funkcji, etykieta).

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

TensorFlow jest najbardziej wydajny, gdy działa na dużych partiach danych.

Zamiast więc przepakowywać każdy wiersz indywidualnie, 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 osobne rekordy:

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

Spójrz na niektóre rekordy z tego nowego packed_ds .

Funkcje nie są doskonale 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, a następnych 10000 do szkolenia:

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.take 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 shapes: ((28,), ()), types: (tf.float32, tf.float32)>

Te zbiory danych zwracają indywidualne przykłady. Użyj metody .batch , aby utworzyć partie o odpowiednim rozmiarze do szkolenia. Przed porcjowaniem pamiętaj również o .shuffle i .repeat zestawu .repeat .

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ą możliwych do nauczenia parametrów (która jest określana 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 nazywana „pojemnością” modelu.

Intuicyjnie, model z większą liczbą parametrów będzie miał większą „zdolność zapamiętywania” i dlatego będzie w stanie łatwo nauczyć się idealnego mapowania słownikowego między próbkami uczącymi a ich celami, mapowania bez żadnej mocy uogólniającej, ale byłoby to bezużyteczne podczas prognozowania 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ólnienie, a nie dopasowanie.

Z drugiej strony, jeśli sieć ma ograniczone zasoby pamięci, nie będzie w stanie nauczyć się mapowania tak łatwo. Aby zminimalizować straty, będzie musiał nauczyć się skompresowanych reprezentacji, które mają większą moc predykcyjną. Jednocześnie, jeśli sprawisz, że model będzie zbyt mały, 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 na określenie odpowiedniego rozmiaru lub architektury Twojego modelu (pod względem liczby warstw lub odpowiedniego rozmiaru dla każdej warstwy). Będziesz musiał poeksperymentować z szeregiem różnych architektur.

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

Zacznij od prostego modelu, używając tylko layers.Dense jako podstawę, a następnie utwórz większe wersje i porównaj je.

Procedura szkoleniowa

Wiele modeli trenuje lepiej, jeśli stopniowo zmniejszasz tempo uczenia się podczas treningu. Użyj optimizers.schedules aby zmniejszyć współczynnik 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 przy 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 szkoleniowej. 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ć szum rejestrowania, użyj tfdocs.EpochDots który po prostu drukuje plik . dla każdej epoki i pełny zestaw metryk co 100 epok.

Następnie uwzględnij callbacks.EarlyStopping aby uniknąć długich i niepotrzebnych czasów szkolenia. Zwróć uwagę, że to wywołanie zwrotne jest ustawione tak, aby monitorować wartość val_binary_crossentropy , a nie val_loss . Ta różnica będzie ważna później.

Użyj callbacks.TensorBoard aby wygenerować dzienniki 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
_________________________________________________________________
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/summary_ops_v2.py:1277: stop (from tensorflow.python.eager.profiler) is deprecated and will be removed after 2020-07-01.
Instructions for updating:
use `tf.profiler.experimental.stop` instead.
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0030s vs `on_train_batch_end` time: 0.0237s). Check your callbacks.

Epoch: 0, accuracy:0.4851,  binary_crossentropy:0.7694,  loss:0.7694,  val_accuracy:0.5020,  val_binary_crossentropy:0.7430,  val_loss:0.7430,  
....................................................................................................
Epoch: 100, accuracy:0.5922,  binary_crossentropy:0.6288,  loss:0.6288,  val_accuracy:0.5730,  val_binary_crossentropy:0.6331,  val_loss:0.6331,  
....................................................................................................
Epoch: 200, accuracy:0.6069,  binary_crossentropy:0.6215,  loss:0.6215,  val_accuracy:0.5820,  val_binary_crossentropy:0.6277,  val_loss:0.6277,  
....................................................................................................
Epoch: 300, accuracy:0.6131,  binary_crossentropy:0.6165,  loss:0.6165,  val_accuracy:0.6040,  val_binary_crossentropy:0.6208,  val_loss:0.6208,  
....................................................................................................
Epoch: 400, accuracy:0.6213,  binary_crossentropy:0.6111,  loss:0.6111,  val_accuracy:0.5980,  val_binary_crossentropy:0.6179,  val_loss:0.6179,  
....................................................................................................
Epoch: 500, accuracy:0.6344,  binary_crossentropy:0.6045,  loss:0.6045,  val_accuracy:0.6250,  val_binary_crossentropy:0.6092,  val_loss:0.6092,  
....................................................................................................
Epoch: 600, accuracy:0.6408,  binary_crossentropy:0.5985,  loss:0.5985,  val_accuracy:0.6170,  val_binary_crossentropy:0.6068,  val_loss:0.6068,  
....................................................................................................
Epoch: 700, accuracy:0.6454,  binary_crossentropy:0.5941,  loss:0.5941,  val_accuracy:0.6380,  val_binary_crossentropy:0.6033,  val_loss:0.6033,  
....................................................................................................
Epoch: 800, accuracy:0.6571,  binary_crossentropy:0.5907,  loss:0.5907,  val_accuracy:0.6350,  val_binary_crossentropy:0.6023,  val_loss:0.6023,  
....................................................................................................
Epoch: 900, accuracy:0.6568,  binary_crossentropy:0.5879,  loss:0.5879,  val_accuracy:0.6390,  val_binary_crossentropy:0.6022,  val_loss:0.6022,  
....................................................................................................
Epoch: 1000, accuracy:0.6592,  binary_crossentropy:0.5860,  loss:0.5860,  val_accuracy:0.6410,  val_binary_crossentropy:0.6006,  val_loss:0.6006,  
....................................................................................................
Epoch: 1100, accuracy:0.6674,  binary_crossentropy:0.5833,  loss:0.5833,  val_accuracy:0.6310,  val_binary_crossentropy:0.6020,  val_loss:0.6020,  
....................................................................................................
Epoch: 1200, accuracy:0.6681,  binary_crossentropy:0.5814,  loss:0.5814,  val_accuracy:0.6300,  val_binary_crossentropy:0.6013,  val_loss:0.6013,  
....................................................................................................
Epoch: 1300, accuracy:0.6711,  binary_crossentropy:0.5798,  loss:0.5798,  val_accuracy:0.6430,  val_binary_crossentropy:0.5985,  val_loss:0.5985,  
....................................................................................................
Epoch: 1400, accuracy:0.6723,  binary_crossentropy:0.5781,  loss:0.5781,  val_accuracy:0.6440,  val_binary_crossentropy:0.5984,  val_loss:0.5984,  
....................................................................................................
Epoch: 1500, accuracy:0.6723,  binary_crossentropy:0.5773,  loss:0.5773,  val_accuracy:0.6490,  val_binary_crossentropy:0.5969,  val_loss:0.5969,  
....................................................................................................
Epoch: 1600, accuracy:0.6710,  binary_crossentropy:0.5762,  loss:0.5762,  val_accuracy:0.6620,  val_binary_crossentropy:0.5953,  val_loss:0.5953,  
....................................................................................................
Epoch: 1700, accuracy:0.6757,  binary_crossentropy:0.5744,  loss:0.5744,  val_accuracy:0.6510,  val_binary_crossentropy:0.5956,  val_loss:0.5956,  
....................................................................................................
Epoch: 1800, accuracy:0.6771,  binary_crossentropy:0.5734,  loss:0.5734,  val_accuracy:0.6560,  val_binary_crossentropy:0.5947,  val_loss:0.5947,  
....................................................................................................
Epoch: 1900, accuracy:0.6780,  binary_crossentropy:0.5723,  loss:0.5723,  val_accuracy:0.6550,  val_binary_crossentropy:0.5942,  val_loss:0.5942,  
....................................................................................................
Epoch: 2000, accuracy:0.6794,  binary_crossentropy:0.5716,  loss:0.5716,  val_accuracy:0.6590,  val_binary_crossentropy:0.5930,  val_loss:0.5930,  
....................................................................................................
Epoch: 2100, accuracy:0.6777,  binary_crossentropy:0.5707,  loss:0.5707,  val_accuracy:0.6560,  val_binary_crossentropy:0.5938,  val_loss:0.5938,  
....................................................................................................
Epoch: 2200, accuracy:0.6817,  binary_crossentropy:0.5699,  loss:0.5699,  val_accuracy:0.6480,  val_binary_crossentropy:0.5942,  val_loss:0.5942,  
....................................................................................................
Epoch: 2300, accuracy:0.6796,  binary_crossentropy:0.5696,  loss:0.5696,  val_accuracy:0.6540,  val_binary_crossentropy:0.5922,  val_loss:0.5922,  
....................................................................................................
Epoch: 2400, accuracy:0.6823,  binary_crossentropy:0.5695,  loss:0.5695,  val_accuracy:0.6530,  val_binary_crossentropy:0.5919,  val_loss:0.5919,  
....................................................................................................
Epoch: 2500, accuracy:0.6848,  binary_crossentropy:0.5688,  loss:0.5688,  val_accuracy:0.6530,  val_binary_crossentropy:0.5943,  val_loss:0.5943,  
....................................................................................................
Epoch: 2600, accuracy:0.6837,  binary_crossentropy:0.5683,  loss:0.5683,  val_accuracy:0.6580,  val_binary_crossentropy:0.5920,  val_loss:0.5920,  
....................................................................................................
Epoch: 2700, accuracy:0.6867,  binary_crossentropy:0.5687,  loss:0.5687,  val_accuracy:0.6560,  val_binary_crossentropy:0.5938,  val_loss:0.5938,  
....................................................................................................
Epoch: 2800, accuracy:0.6874,  binary_crossentropy:0.5671,  loss:0.5671,  val_accuracy:0.6550,  val_binary_crossentropy:0.5922,  val_loss:0.5922,  
....................................................................................................
Epoch: 2900, accuracy:0.6814,  binary_crossentropy:0.5666,  loss:0.5666,  val_accuracy:0.6590,  val_binary_crossentropy:0.5907,  val_loss:0.5907,  
.......................................................

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 uda Ci się pokonać 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:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0034s vs `on_train_batch_end` time: 0.0481s). Check your callbacks.

Epoch: 0, accuracy:0.4760,  binary_crossentropy:0.7078,  loss:0.7078,  val_accuracy:0.4700,  val_binary_crossentropy:0.6948,  val_loss:0.6948,  
....................................................................................................
Epoch: 100, accuracy:0.6241,  binary_crossentropy:0.6134,  loss:0.6134,  val_accuracy:0.5950,  val_binary_crossentropy:0.6196,  val_loss:0.6196,  
....................................................................................................
Epoch: 200, accuracy:0.6435,  binary_crossentropy:0.5975,  loss:0.5975,  val_accuracy:0.6290,  val_binary_crossentropy:0.6116,  val_loss:0.6116,  
....................................................................................................
Epoch: 300, accuracy:0.6636,  binary_crossentropy:0.5827,  loss:0.5827,  val_accuracy:0.6310,  val_binary_crossentropy:0.6020,  val_loss:0.6020,  
....................................................................................................
Epoch: 400, accuracy:0.6742,  binary_crossentropy:0.5730,  loss:0.5730,  val_accuracy:0.6500,  val_binary_crossentropy:0.5945,  val_loss:0.5945,  
....................................................................................................
Epoch: 500, accuracy:0.6822,  binary_crossentropy:0.5670,  loss:0.5670,  val_accuracy:0.6470,  val_binary_crossentropy:0.5919,  val_loss:0.5919,  
....................................................................................................
Epoch: 600, accuracy:0.6864,  binary_crossentropy:0.5631,  loss:0.5631,  val_accuracy:0.6510,  val_binary_crossentropy:0.5909,  val_loss:0.5909,  
....................................................................................................
Epoch: 700, accuracy:0.6928,  binary_crossentropy:0.5596,  loss:0.5596,  val_accuracy:0.6600,  val_binary_crossentropy:0.5910,  val_loss:0.5910,  
....................................................................................................
Epoch: 800, accuracy:0.6965,  binary_crossentropy:0.5564,  loss:0.5564,  val_accuracy:0.6620,  val_binary_crossentropy:0.5898,  val_loss:0.5898,  
....................................................................................................
Epoch: 900, accuracy:0.7008,  binary_crossentropy:0.5544,  loss:0.5544,  val_accuracy:0.6480,  val_binary_crossentropy:0.5921,  val_loss:0.5921,  
...............................................

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

Wytrenuj model, korzystając z 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:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0037s vs `on_train_batch_end` time: 0.0511s). Check your callbacks.

Epoch: 0, accuracy:0.4852,  binary_crossentropy:0.6982,  loss:0.6982,  val_accuracy:0.4830,  val_binary_crossentropy:0.6815,  val_loss:0.6815,  
....................................................................................................
Epoch: 100, accuracy:0.7123,  binary_crossentropy:0.5315,  loss:0.5315,  val_accuracy:0.6540,  val_binary_crossentropy:0.5983,  val_loss:0.5983,  
....................................................................................................
Epoch: 200, accuracy:0.7796,  binary_crossentropy:0.4328,  loss:0.4328,  val_accuracy:0.6590,  val_binary_crossentropy:0.6763,  val_loss:0.6763,  
...................................................

Duży model

W ramach ćwiczenia możesz utworzyć jeszcze większy model i zobaczyć, jak szybko zacznie się on nadmiernie dopasowywać. Następnie dodajmy do tego wzorca sieć, która ma znacznie większą przepustowość, o wiele 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 ponownie wytrenuj model przy użyciu 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:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0038s vs `on_train_batch_end` time: 0.0571s). Check your callbacks.

Epoch: 0, accuracy:0.5119,  binary_crossentropy:0.7993,  loss:0.7993,  val_accuracy:0.4630,  val_binary_crossentropy:0.7125,  val_loss:0.7125,  
....................................................................................................
Epoch: 100, accuracy:1.0000,  binary_crossentropy:0.0021,  loss:0.0021,  val_accuracy:0.6640,  val_binary_crossentropy:1.8146,  val_loss:1.8146,  
....................................................................................................
Epoch: 200, accuracy:1.0000,  binary_crossentropy:0.0001,  loss:0.0001,  val_accuracy:0.6630,  val_binary_crossentropy:2.4702,  val_loss:2.4702,  
............................

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 podczas walidacji oznacza lepszy model).

Zbudowanie większego modelu daje mu większą moc, jeśli ta moc nie jest w jakiś sposób ograniczona, można go łatwo dopasować do zestawu treningowego.

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 w przypadku "large" modelu, że trzeba 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 szkolenia.

  • To normalne, że istnieje niewielka różnica.
  • Jeśli obie metryki zmierzają w tym samym kierunku, wszystko jest w porządku.
  • Jeśli miernik walidacji zacznie się stagnować, podczas gdy miernik szkolenia nadal się poprawia, prawdopodobnie jesteś bliski nadmiernego dopasowania.
  • Jeśli miernik walidacji zmierza w złym kierunku, model jest wyraźnie nadmiernie dopasowany.
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

Wyświetl w TensorBoard

Wszystkie te modele zapisywały dzienniki TensorBoard podczas treningu.

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


# Load the TensorBoard notebook extension
%load_ext tensorboard

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

Możesz wyświetlić wyniki poprzedniego uruchomienia tego notatnika na TensorBoard.dev .

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

Dla wygody jest również zawarty w <iframe> :

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

Jeśli chcesz udostępniać wyniki TensorBoard, możesz przesłać dzienniki do TensorBoard.dev , kopiując poniższe pliki do komórki kodu.

tensorboard dev upload --logdir  {logdir}/sizes

Strategie zapobiegania nadmiernemu dopasowaniu

Przed przejściem do treści tej sekcji skopiuj dzienniki szkolenia z powyższego modelu "Tiny" , aby użyć ich jako podstawy do porównania.

shutil.rmtree(logdir/'regularizers/Tiny', ignore_errors=True)
shutil.copytree(logdir/'sizes/Tiny', logdir/'regularizers/Tiny')
PosixPath('/tmp/tmpnnkr5005/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, najprawdopodobniej jest to „najprostsze” wyjaśnienie, które przyjmuje najmniej założeń. Dotyczy to również modeli wyuczonych przez sieci neuronowe: biorąc pod uwagę pewne 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 nadmierne dopasowanie niż modele 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). W związku z tym powszechnym sposobem łagodzenia nadmiernego dopasowania jest nakładanie ograniczeń na złożoność sieci przez zmuszanie jej wag tylko do przyjmowania małych wartości, co sprawia, że ​​rozkład wartości wag jest bardziej „regularny”. Nazywa się to „regularyzacją wagi” i dokonuje się tego poprzez dodanie do funkcji straty sieci kosztów związanych z posiadaniem dużych wag. Koszt ten ma dwa warianty:

  • Regularyzacja L1 , gdzie dodany koszt jest proporcjonalny do bezwzględnej wartości współczynników wag (tj. Do tak zwanej „normy L1” wag).

  • Regularyzacja L2 , gdzie dodany koszt jest proporcjonalny do kwadratu wartości współczynników wag (tj. Do kwadratu „normy L2” wag). W kontekście sieci neuronowych regularyzacja L2 jest również nazywana zanikiem wagi. Nie pozwól, aby inna nazwa Cię zmyliła: rozpad masy jest matematycznie taki sam, jak regularyzacja L2.

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

W tf.keras regulacja wagi jest dodawana poprzez przekazywanie instancji regulatora wagi do warstw jako argumenty 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
_________________________________________________________________
WARNING:tensorflow:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0039s vs `on_train_batch_end` time: 0.0601s). Check your callbacks.

Epoch: 0, accuracy:0.5034,  binary_crossentropy:0.7595,  loss:2.2567,  val_accuracy:0.5360,  val_binary_crossentropy:0.6737,  val_loss:2.0767,  
....................................................................................................
Epoch: 100, accuracy:0.6562,  binary_crossentropy:0.5978,  loss:0.6212,  val_accuracy:0.6240,  val_binary_crossentropy:0.5912,  val_loss:0.6144,  
....................................................................................................
Epoch: 200, accuracy:0.6610,  binary_crossentropy:0.5914,  loss:0.6147,  val_accuracy:0.6480,  val_binary_crossentropy:0.5813,  val_loss:0.6055,  
....................................................................................................
Epoch: 300, accuracy:0.6794,  binary_crossentropy:0.5768,  loss:0.5998,  val_accuracy:0.6730,  val_binary_crossentropy:0.5780,  val_loss:0.6009,  
....................................................................................................
Epoch: 400, accuracy:0.6843,  binary_crossentropy:0.5685,  loss:0.5914,  val_accuracy:0.6760,  val_binary_crossentropy:0.5798,  val_loss:0.6027,  
....................................................................................................
Epoch: 500, accuracy:0.6971,  binary_crossentropy:0.5602,  loss:0.5856,  val_accuracy:0.6600,  val_binary_crossentropy:0.5855,  val_loss:0.6107,  
................................................................................................

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

Dlatego bezpośrednio monitorujemy binary_crossentropy . Ponieważ nie ma wmieszanego składnika regularyzacji.

Tak więc ten sam "Large" model z karą regularyzacji L2 działa znacznie lepiej:

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

png

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 overfitting niż model "Large" którym był oparty, mimo posiadania tej samej liczby parametrów.

Więcej informacji

Istnieją dwie ważne rzeczy, na które należy zwrócić uwagę w przypadku tego rodzaju uregulowania.

Po pierwsze: jeśli piszesz własną pętlę treningową, to musisz zapytać model o jego straty regularyzacyjne.

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

Po drugie: ta implementacja polega na dodaniu kar wagi do utraty modelu, a następnie zastosowaniu standardowej procedury optymalizacji.

Istnieje drugie podejście, które zamiast tego uruchamia optymalizator tylko na surowej stracie, a następnie, stosując obliczony krok, optymalizator również stosuje pewien spadek wagi. Ten „Decoupled Weight Decay” jest widoczny w optymalizatorach, takich jak optimizers.FTRL i optimizers.AdamW .

Dodaj rezygnację

Dropout jest jedną z najskuteczniejszych i najczęściej stosowanych technik regularyzacji sieci neuronowych, opracowaną przez Hintona i jego studentów na Uniwersytecie w Toronto.

Intuicyjnym wyjaśnieniem zaniku jest to, że ponieważ poszczególne węzły w sieci nie mogą polegać na wynikach innych, każdy węzeł musi generować funkcje, które są użyteczne samodzielnie.

Eliminacja zastosowana do warstwy polega na losowym „odrzucaniu” (tj. Ustawieniu na zero) szeregu wyjściowych cech warstwy podczas uczenia. Powiedzmy, że dana warstwa zwróciłaby normalnie 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 zerowych wpisów rozłożonych losowo, np. [0, 0,5, 1,3, 0, 1,1].

„Wskaźnik rezygnacji” to ułamek funkcji, które są zerowane; 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ą skalowane w dół o współczynnik równy współczynnikowi 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 zostanie zastosowana na wyjściu warstwy tuż przed.

Dodajmy dwie warstwy Dropout w naszej sieci, aby zobaczyć, jak dobrze radzą sobie w zmniejszaniu 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:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0045s vs `on_train_batch_end` time: 0.0648s). Check your callbacks.

Epoch: 0, accuracy:0.4955,  binary_crossentropy:0.8108,  loss:0.8108,  val_accuracy:0.4970,  val_binary_crossentropy:0.6725,  val_loss:0.6725,  
....................................................................................................
Epoch: 100, accuracy:0.6590,  binary_crossentropy:0.5943,  loss:0.5943,  val_accuracy:0.6730,  val_binary_crossentropy:0.5780,  val_loss:0.5780,  
....................................................................................................
Epoch: 200, accuracy:0.6894,  binary_crossentropy:0.5594,  loss:0.5594,  val_accuracy:0.6820,  val_binary_crossentropy:0.5753,  val_loss:0.5753,  
....................................................................................................
Epoch: 300, accuracy:0.7231,  binary_crossentropy:0.5111,  loss:0.5111,  val_accuracy:0.6830,  val_binary_crossentropy:0.6013,  val_loss:0.6013,  
.....................
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

Z tego wykresu jasno wynika, że ​​oba te podejścia regularyzacyjne poprawiają zachowanie modelu "Large" . Ale to nadal nie przebija nawet linii bazowej "Tiny" .

Następnie wypróbuj oba razem i zobacz, czy to działa lepiej.

Połączone zanikanie 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:Callbacks method `on_train_batch_end` is slow compared to the batch time (batch time: 0.0041s vs `on_train_batch_end` time: 0.0627s). Check your callbacks.

Epoch: 0, accuracy:0.5101,  binary_crossentropy:0.7867,  loss:0.9452,  val_accuracy:0.5440,  val_binary_crossentropy:0.6681,  val_loss:0.8258,  
....................................................................................................
Epoch: 100, accuracy:0.6491,  binary_crossentropy:0.6032,  loss:0.6321,  val_accuracy:0.6660,  val_binary_crossentropy:0.5830,  val_loss:0.6116,  
....................................................................................................
Epoch: 200, accuracy:0.6667,  binary_crossentropy:0.5912,  loss:0.6171,  val_accuracy:0.6850,  val_binary_crossentropy:0.5687,  val_loss:0.5946,  
....................................................................................................
Epoch: 300, accuracy:0.6718,  binary_crossentropy:0.5828,  loss:0.6106,  val_accuracy:0.6840,  val_binary_crossentropy:0.5667,  val_loss:0.5945,  
....................................................................................................
Epoch: 400, accuracy:0.6750,  binary_crossentropy:0.5770,  loss:0.6067,  val_accuracy:0.6870,  val_binary_crossentropy:0.5534,  val_loss:0.5832,  
....................................................................................................
Epoch: 500, accuracy:0.6733,  binary_crossentropy:0.5752,  loss:0.6071,  val_accuracy:0.6910,  val_binary_crossentropy:0.5526,  val_loss:0.5846,  
....................................................................................................
Epoch: 600, accuracy:0.6895,  binary_crossentropy:0.5634,  loss:0.5976,  val_accuracy:0.7060,  val_binary_crossentropy:0.5466,  val_loss:0.5809,  
....................................................................................................
Epoch: 700, accuracy:0.6876,  binary_crossentropy:0.5590,  loss:0.5940,  val_accuracy:0.6860,  val_binary_crossentropy:0.5502,  val_loss:0.5852,  
....................................................................................................
Epoch: 800, accuracy:0.6921,  binary_crossentropy:0.5594,  loss:0.5956,  val_accuracy:0.6990,  val_binary_crossentropy:0.5496,  val_loss:0.5858,  
....................................................................................................
Epoch: 900, accuracy:0.6900,  binary_crossentropy:0.5603,  loss:0.5975,  val_accuracy:0.7000,  val_binary_crossentropy:0.5393,  val_loss:0.5765,  
....................................................................................................
Epoch: 1000, accuracy:0.6946,  binary_crossentropy:0.5592,  loss:0.5975,  val_accuracy:0.6750,  val_binary_crossentropy:0.5564,  val_loss:0.5947,  
....................................................................................................
Epoch: 1100, accuracy:0.7000,  binary_crossentropy:0.5476,  loss:0.5872,  val_accuracy:0.7030,  val_binary_crossentropy:0.5460,  val_loss:0.5856,  
....................................................................................................
Epoch: 1200, accuracy:0.7045,  binary_crossentropy:0.5474,  loss:0.5879,  val_accuracy:0.6860,  val_binary_crossentropy:0.5480,  val_loss:0.5886,  
...........
plotter.plot(regularizer_histories)
plt.ylim([0.5, 0.7])
(0.5, 0.7)

png

Ten model z regularyzacją "Combined" jest jak dotąd najlepszy.

Wyświetl w TensorBoard

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

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

%tensorboard --logdir {logdir}/regularizers

Możesz wyświetlić wyniki poprzedniego uruchomienia tego notatnika na TensorDoard.dev .

Dla wygody jest również zawarty w <iframe> :

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

To zostało przesłane za pomocą:

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 przepustowość sieci.
  • Dodaj regularyzację wagi.
  • Dodaj rezygnację.

Dwa ważne podejścia nieujęte w tym przewodniku to:

  • rozszerzanie danych
  • normalizacja partii

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


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