Cette page a été traduite par l'API Cloud Translation.
Switch to English

Overfit et underfit

Voir sur TensorFlow.org Exécuter dans Google Colab Afficher la source sur GitHub Télécharger le carnet

Comme toujours, le code de cet exemple utilisera l'API tf.keras , sur laquelle vous pouvez en savoir plus dans le guide TensorFlow Keras .

Dans les deux exemples précédents - classification du texte et prédiction du rendement énergétique - nous avons vu que la précision de notre modèle sur les données de validation atteindrait son maximum après l'entraînement pendant un certain nombre d'époques, puis stagnerait ou commencerait à diminuer.

En d'autres termes, notre modèle se suradapterait aux données d'entraînement. Apprendre à gérer le surajustement est important. Bien qu'il soit souvent possible d'obtenir une précision élevée sur l' ensemble d'entraînement , ce que nous voulons vraiment, c'est développer des modèles qui se généralisent bien à un ensemble de tests (ou à des données qu'ils n'ont pas vues auparavant).

Le contraire du surajustement est le sous- ajustement . Un sous-ajustement se produit lorsqu'il y a encore place à l'amélioration des données de test. Cela peut se produire pour un certain nombre de raisons: si le modèle n'est pas assez puissant, est sur-régularisé ou n'a tout simplement pas été formé assez longtemps. Cela signifie que le réseau n'a pas appris les modèles pertinents dans les données d'apprentissage.

Cependant, si vous vous entraînez trop longtemps, le modèle commencera à se surajuster et à apprendre des modèles à partir des données d'entraînement qui ne se généralisent pas aux données de test. Nous devons trouver un équilibre. Comprendre comment s'entraîner pour un nombre approprié d'époques comme nous l'explorerons ci-dessous est une compétence utile.

Pour éviter le surajustement, la meilleure solution consiste à utiliser des données d'entraînement plus complètes. L'ensemble de données doit couvrir la gamme complète des entrées que le modèle est censé gérer. Des données supplémentaires ne peuvent être utiles que si elles couvrent des cas nouveaux et intéressants.

Un modèle formé sur des données plus complètes généralisera naturellement mieux. Lorsque cela n'est plus possible, la meilleure solution suivante consiste à utiliser des techniques comme la régularisation. Celles-ci imposent des contraintes sur la quantité et le type d'informations que votre modèle peut stocker. Si un réseau ne peut se permettre de mémoriser qu'un petit nombre de modèles, le processus d'optimisation l'obligera à se concentrer sur les modèles les plus importants, qui ont de meilleures chances de bien se généraliser.

Dans ce cahier, nous allons explorer plusieurs techniques de régularisation courantes et les utiliser pour améliorer un modèle de classification.

Installer

Avant de commencer, importez les packages nécessaires:

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)

L'ensemble de données Higgs

Le but de ce didacticiel n'est pas de faire de la physique des particules, alors ne vous attardez pas sur les détails du jeu de données. Il contient 11 000 000 d'exemples, chacun avec 28 caractéristiques et une étiquette de classe binaire.

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

La classe tf.data.experimental.CsvDataset peut être utilisée pour lire les enregistrements csv directement à partir d'un fichier gzip sans étape de décompression intermédiaire.

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

Cette classe de lecteur csv renvoie une liste de scalaires pour chaque enregistrement. La fonction suivante reconditionne cette liste de scalaires en une paire (feature_vector, label).

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

TensorFlow est plus efficace lorsqu'il fonctionne sur de grands lots de données.

Ainsi, au lieu de reconditionner chaque ligne individuellement, créez un nouvel ensemble de Dataset qui prend des lots de 10000 exemples, applique la fonction pack_row à chaque lot, puis divise les lots en enregistrements individuels:

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

Jetez un œil à certains des enregistrements de ce nouveau packed_ds .

Les fonctionnalités ne sont pas parfaitement normalisées, mais cela suffit pour ce tutoriel.

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

Pour garder ce tutoriel relativement court, utilisez uniquement les 1000 premiers échantillons pour la validation et les 10 000 suivants pour la formation:

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

Les méthodes Dataset.skip et Dataset.take facilitent cette tâche.

En même temps, utilisez la méthode Dataset.cache pour vous assurer que le chargeur n'a pas besoin de relire les données du fichier à chaque époque:

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

Ces ensembles de données renvoient des exemples individuels. Utilisez la méthode .batch pour créer des lots de taille appropriée pour l'entraînement. Avant de .shuffle au traitement par lots, n'oubliez pas de .shuffle et de .repeat l'ensemble d'entraînement.

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

Démontrer un surajustement

Le moyen le plus simple d'éviter le surajustement est de commencer avec un petit modèle: Un modèle avec un petit nombre de paramètres apprenables (qui est déterminé par le nombre de couches et le nombre d'unités par couche). Dans l'apprentissage en profondeur, le nombre de paramètres apprenables dans un modèle est souvent appelé «capacité» du modèle.

Intuitivement, un modèle avec plus de paramètres aura plus de "capacité de mémorisation" et pourra donc facilement apprendre un mappage parfait de type dictionnaire entre les échantillons d'apprentissage et leurs cibles, un mappage sans aucun pouvoir de généralisation, mais cela ne servirait à rien lors des prédictions sur des données inédites.

Gardez toujours cela à l'esprit: les modèles d'apprentissage en profondeur ont tendance à bien s'adapter aux données de formation, mais le véritable défi est la généralisation, pas l'ajustement.

En revanche, si le réseau dispose de ressources de mémorisation limitées, il ne pourra pas apprendre la cartographie aussi facilement. Pour minimiser sa perte, il devra apprendre des représentations compressées qui ont plus de puissance prédictive. Dans le même temps, si vous rendez votre modèle trop petit, il aura du mal à s'adapter aux données d'entraînement. Il existe un équilibre entre «trop de capacité» et «pas assez de capacité».

Malheureusement, il n'y a pas de formule magique pour déterminer la bonne taille ou l'architecture de votre modèle (en termes de nombre de couches, ou de la bonne taille pour chaque couche). Vous devrez expérimenter en utilisant une série d'architectures différentes.

Pour trouver une taille de modèle appropriée, il est préférable de commencer avec relativement peu de couches et de paramètres, puis de commencer à augmenter la taille des couches ou à ajouter de nouvelles couches jusqu'à ce que vous voyiez des rendements décroissants sur la perte de validation.

Commencez par un modèle simple utilisant uniquement des layers.Dense comme référence, puis créez des versions plus grandes et comparez-les.

Procédure de formation

De nombreux modèles s'entraînent mieux si vous réduisez progressivement le taux d'apprentissage pendant l'entraînement. Utilisez optimizers.schedules pour réduire le taux d'apprentissage au fil du temps:

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)

Le code ci-dessus définit un schedules.InverseTimeDecay pour diminuer de manière hyperbolique le taux d'apprentissage à 1/2 du taux de base à 1000 époques, 1/3 à 2000 époques et ainsi de suite.

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

Chaque modèle de ce didacticiel utilisera la même configuration d'entraînement. Donc, configurez-les de manière réutilisable, en commençant par la liste des rappels.

La formation pour ce didacticiel dure de nombreuses époques courtes. Pour réduire le bruit de journalisation, utilisez les tfdocs.EpochDots qui imprime simplement un tfdocs.EpochDots . pour chaque époque, et un ensemble complet de métriques toutes les 100 époques.

Ensuite, incluez les callbacks.EarlyStopping pour éviter des temps d'entraînement longs et inutiles. Notez que ce rappel est défini pour surveiller la val_binary_crossentropy , pas la val_loss . Cette différence sera importante plus tard.

Utilisez callbacks.TensorBoard pour générer des journaux TensorBoard pour la formation.

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

De même, chaque modèle utilisera les mêmes paramètres Model.compile et 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

Modèle minuscule

Commencez par entraîner un modèle:

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

Maintenant, vérifiez comment le modèle a fonctionné:

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

Petit modèle

Pour voir si vous pouvez battre les performances du petit modèle, entraînez progressivement certains modèles plus grands.

Essayez deux couches cachées de 16 unités chacune:

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

Modèle moyen

Maintenant, essayez 3 couches cachées de 64 unités chacune:

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

Et entraînez le modèle en utilisant les mêmes données:

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

Grand modèle

En tant qu'exercice, vous pouvez créer un modèle encore plus grand et voir à quelle vitesse il commence le surajustement. Ensuite, ajoutons à ce benchmark un réseau qui a beaucoup plus de capacité, bien plus que le problème ne le justifierait:

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

Et, encore une fois, entraînez le modèle en utilisant les mêmes données:

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

Tracer les pertes de formation et de validation

Les lignes pleines montrent la perte de formation et les lignes pointillées indiquent la perte de validation (rappelez-vous: une perte de validation inférieure indique un meilleur modèle).

Bien que la construction d'un modèle plus grand lui donne plus de puissance, si cette puissance n'est pas limitée d'une manière ou d'une autre, elle peut facilement s'adapter à l'ensemble d'entraînement.

Dans cet exemple, en général, seul le modèle "Tiny" parvient à éviter le surajustement complet, et chacun des modèles plus grands suradapte les données plus rapidement. Cela devient si grave pour le modèle "large" que vous devez basculer le tracé à une échelle logarithmique pour vraiment voir ce qui se passe.

Cela est évident si vous tracez et comparez les métriques de validation aux métriques d'entraînement.

  • Il est normal qu'il y ait une petite différence.
  • Si les deux métriques évoluent dans la même direction, tout va bien.
  • Si la métrique de validation commence à stagner alors que la métrique d'entraînement continue de s'améliorer, vous êtes probablement proche du surajustement.
  • Si la métrique de validation va dans la mauvaise direction, le modèle est clairement surajusté.
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

Afficher dans TensorBoard

Ces modèles ont tous écrit des journaux TensorBoard pendant la formation.

Ouvrez une visionneuse TensorBoard intégrée dans un bloc-notes:


# Load the TensorBoard notebook extension
%load_ext tensorboard

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

Vous pouvez afficher les résultats d'une précédente exécution de ce notebook sur TensorBoard.dev .

TensorBoard.dev est une expérience gérée pour l'hébergement, le suivi et le partage d'expériences ML avec tout le monde.

Il est également inclus dans un <iframe> pour plus de commodité:

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

Si vous souhaitez partager les résultats de TensorBoard, vous pouvez télécharger les journaux sur TensorBoard.dev en copiant ce qui suit dans une cellule de code.

tensorboard dev upload --logdir  {logdir}/sizes

Stratégies pour éviter le surajustement

Avant d'entrer dans le contenu de cette section, copiez les journaux d'entraînement du modèle "Tiny" ci-dessus, à utiliser comme base de comparaison.

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

Ajouter la régularisation du poids

Vous connaissez peut-être le principe du rasoir d'Occam: étant donné deux explications pour quelque chose, l'explication la plus vraisemblablement correcte est la plus «simple», celle qui fait le moins d'hypothèses. Cela s'applique également aux modèles appris par les réseaux de neurones: compte tenu de certaines données d'apprentissage et d'une architecture de réseau, il existe plusieurs ensembles de valeurs de poids (plusieurs modèles) qui pourraient expliquer les données, et les modèles plus simples sont moins susceptibles de se sur-adapter que les complexes.

Un «modèle simple» dans ce contexte est un modèle où la distribution des valeurs de paramètres a moins d'entropie (ou un modèle avec moins de paramètres au total, comme nous l'avons vu dans la section ci-dessus). Ainsi, une manière courante d'atténuer le surajustement est de mettre des contraintes sur la complexité d'un réseau en forçant ses poids à ne prendre que de petites valeurs, ce qui rend la distribution des valeurs de poids plus "régulière". C'est ce qu'on appelle la «régularisation des poids», et cela se fait en ajoutant à la fonction de perte du réseau un coût associé à des poids importants. Ce coût se décline en deux saveurs:

  • Régularisation L1 , où le coût ajouté est proportionnel à la valeur absolue des coefficients de poids (c'est-à-dire à ce qu'on appelle la «norme L1» des poids).

  • Régularisation L2 , où le coût ajouté est proportionnel au carré de la valeur des coefficients de poids (c'est-à-dire à ce qu'on appelle le carré de la «norme L2» des poids). La régularisation L2 est également appelée décroissance de poids dans le contexte des réseaux de neurones. Ne laissez pas le nom différent vous confondre: la décroissance du poids est mathématiquement identique à la régularisation L2.

La régularisation L1 pousse les poids vers exactement zéro, ce qui encourage un modèle clairsemé. La régularisation L2 pénalisera les paramètres de poids sans les rendre clairsemés puisque la pénalité passe à zéro pour les petits poids. une des raisons pour lesquelles L2 est plus courante.

Dans tf.keras , la régularisation de poids est ajoutée en passant des instances de régularisation de poids aux couches en tant qu'arguments de mots clés. Ajoutons maintenant la régularisation du poids 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) signifie que chaque coefficient de la matrice de poids de la couche ajoutera 0.001 * weight_coefficient_value**2 à la perte totale du réseau.

C'est pourquoi nous surveillons directement le binary_crossentropy . Parce que ce composant de régularisation n'est pas mélangé.

Donc, ce même modèle "Large" avec une pénalité de régularisation L2 fonctionne bien mieux:

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

png

Comme vous pouvez le voir, le modèle régularisé "L2" est désormais beaucoup plus compétitif que le modèle "Tiny" . Ce modèle "L2" est également beaucoup plus résistant au surajustement que le modèle "Large" lequel il était basé malgré le même nombre de paramètres.

Plus d'informations

Il y a deux choses importantes à noter à propos de ce type de régularisation.

Premièrement: si vous écrivez votre propre boucle d'entraînement, vous devez vous assurer de demander au modèle ses pertes de régularisation.

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

Deuxièmement: Cette implémentation fonctionne en ajoutant les pénalités de poids à la perte du modèle, puis en appliquant une procédure d'optimisation standard par la suite.

Il existe une deuxième approche qui n'exécute à la place que l'optimiseur sur la perte brute, puis tout en appliquant l'étape calculée, l'optimiseur applique également une certaine décroissance du poids. Cette "décroissance de poids découplée" est vue dans les optimiseurs comme optimizers.FTRL et optimizers.AdamW .

Ajouter un abandon

Le décrochage est l'une des techniques de régularisation les plus efficaces et les plus couramment utilisées pour les réseaux de neurones, développée par Hinton et ses étudiants de l'Université de Toronto.

L'explication intuitive de l'abandon est que, étant donné que les nœuds individuels du réseau ne peuvent pas compter sur la sortie des autres, chaque nœud doit générer des fonctionnalités qui sont utiles en soi.

L'abandon, appliqué à un calque, consiste à «abandonner» de manière aléatoire (c'est-à-dire mis à zéro) un certain nombre de caractéristiques de sortie de la couche pendant l'apprentissage. Disons qu'une couche donnée aurait normalement renvoyé un vecteur [0,2, 0,5, 1,3, 0,8, 1,1] pour un échantillon d'entrée donné pendant l'apprentissage; après application de la suppression, ce vecteur aura quelques entrées nulles distribuées au hasard, par exemple [0, 0,5, 1,3, 0, 1,1].

Le «taux d'abandon» est la fraction des fonctionnalités qui sont supprimées; il est généralement réglé entre 0,2 et 0,5. Au moment du test, aucune unité n'est abandonnée, et à la place, les valeurs de sortie de la couche sont réduites d'un facteur égal au taux d'abandon, afin d'équilibrer le fait que plus d'unités sont actives qu'au moment de l'apprentissage.

Dans tf.keras vous pouvez introduire un abandon dans un réseau via la couche Dropout, qui est appliquée à la sortie de la couche juste avant.

Ajoutons deux couches Dropout dans notre réseau pour voir dans quelle mesure elles réduisent le surajustement:

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

Il ressort clairement de ce graphique que ces deux approches de régularisation améliorent le comportement du modèle "Large" . Mais cela ne bat toujours pas même la ligne de base "Tiny" .

Ensuite, essayez les deux, ensemble, et voyez si cela fait mieux.

Abandon combiné 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

Ce modèle avec la régularisation "Combined" est évidemment le meilleur à ce jour.

Afficher dans TensorBoard

Ces modèles ont également enregistré les journaux TensorBoard.

Pour ouvrir une visionneuse de tableau de mesure intégrée dans un bloc-notes, copiez ce qui suit dans une cellule de code:

%tensorboard --logdir {logdir}/regularizers

Vous pouvez voir les résultats d'une précédente exécution de ce notebook sur TensorDoard.dev .

Il est également inclus dans un <iframe> pour plus de commodité:

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

Cela a été téléchargé avec:

tensorboard dev upload --logdir  {logdir}/regularizers

Conclusions

Pour récapituler: voici les moyens les plus courants pour éviter le surajustement dans les réseaux de neurones:

  • Obtenez plus de données d'entraînement.
  • Réduisez la capacité du réseau.
  • Ajoutez la régularisation du poids.
  • Ajouter un abandon.

Deux approches importantes non couvertes dans ce guide sont:

  • augmentation de données
  • normalisation par lots

N'oubliez pas que chaque méthode peut aider seule, mais souvent, les combiner peut être encore plus efficace.


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