Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Overfit y underfit

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar cuaderno

Como siempre, el código de este ejemplo utilizará la API tf.keras , sobre la que puede obtener más información en la guía de TensorFlow Keras .

En los dos ejemplos anteriores, la clasificación del texto y la predicción de la eficiencia del combustible , vimos que la precisión de nuestro modelo en los datos de validación alcanzaría su punto máximo después del entrenamiento durante varias épocas y luego se estancaría o comenzaría a disminuir.

En otras palabras, nuestro modelo se adaptaría demasiado a los datos de entrenamiento. Aprender a lidiar con el sobreajuste es importante. Aunque a menudo es posible lograr una alta precisión en el conjunto de entrenamiento , lo que realmente queremos es desarrollar modelos que se generalicen bien en un conjunto de pruebas (o datos que no hayan visto antes).

Lo opuesto al sobreajuste es el desajuste . El desajuste se produce cuando todavía hay margen de mejora en los datos de prueba. Esto puede suceder por varias razones: si el modelo no es lo suficientemente potente, está sobre regularizado o simplemente no se ha entrenado lo suficiente. Esto significa que la red no ha aprendido los patrones relevantes en los datos de entrenamiento.

Sin embargo, si entrena durante demasiado tiempo, el modelo comenzará a sobreajustarse y aprenderá patrones de los datos de entrenamiento que no se generalizan a los datos de prueba. Necesitamos encontrar un equilibrio. Comprender cómo entrenar para un número apropiado de épocas, como exploraremos a continuación, es una habilidad útil.

Para evitar el sobreajuste, la mejor solución es utilizar datos de entrenamiento más completos. El conjunto de datos debe cubrir la gama completa de entradas que se espera que maneje el modelo. Los datos adicionales solo pueden ser útiles si cubren casos nuevos e interesantes.

Un modelo entrenado con datos más completos, naturalmente, generalizará mejor. Cuando eso ya no sea posible, la siguiente mejor solución es utilizar técnicas como la regularización. Estos imponen restricciones sobre la cantidad y el tipo de información que puede almacenar su modelo. Si una red solo puede permitirse el lujo de memorizar una pequeña cantidad de patrones, el proceso de optimización la obligará a concentrarse en los patrones más destacados, que tienen más posibilidades de generalizarse bien.

En este cuaderno, exploraremos varias técnicas de regularización comunes y las usaremos para mejorar un modelo de clasificación.

Preparar

Antes de comenzar, importe los paquetes necesarios:

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)

El conjunto de datos de Higgs

El objetivo de este tutorial no es hacer física de partículas, así que no te detengas en los detalles del conjunto de datos. Contiene 11 000 000 de ejemplos, cada uno con 28 funciones y una etiqueta de clase binaria.

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 clase tf.data.experimental.CsvDataset se puede utilizar para leer registros csv directamente desde un archivo gzip sin un paso de descompresión intermedio.

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

Esa clase de lector de csv devuelve una lista de escalares para cada registro. La siguiente función vuelve a empaquetar esa lista de escalares en un par (feature_vector, label).

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

TensorFlow es más eficiente cuando opera con grandes lotes de datos.

Entonces, en lugar de volver a empaquetar cada fila individualmente, pack_row un nuevo Dataset que tome lotes de 10000 ejemplos, aplique la función pack_row a cada lote y luego divida los lotes en registros individuales:

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

Eche un vistazo a algunos de los registros de este nuevo packed_ds .

Las funciones no están perfectamente normalizadas, pero esto es suficiente para este tutorial.

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

Para mantener este tutorial relativamente corto, use solo las primeras 1000 muestras para la validación y las siguientes 10000 para el entrenamiento:

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

Los métodos Dataset.skip y Dataset.take facilitan esta tarea.

Al mismo tiempo, use el método Dataset.cache para asegurarse de que el cargador no necesite volver a leer los datos del archivo en cada época:

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

Estos conjuntos de datos devuelven ejemplos individuales. Utilice el método .batch para crear lotes de un tamaño adecuado para el entrenamiento. Antes de realizar el procesamiento por lotes, recuerde también .shuffle y .repeat el conjunto de entrenamiento.

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

Demostrar sobreajuste

La forma más sencilla de evitar el sobreajuste es comenzar con un modelo pequeño: un modelo con una pequeña cantidad de parámetros que se pueden aprender (que está determinada por la cantidad de capas y la cantidad de unidades por capa). En el aprendizaje profundo, la cantidad de parámetros que se pueden aprender en un modelo a menudo se denomina "capacidad" del modelo.

Intuitivamente, un modelo con más parámetros tendrá más "capacidad de memorización" y, por lo tanto, podrá aprender fácilmente un mapeo perfecto tipo diccionario entre las muestras de entrenamiento y sus objetivos, un mapeo sin ningún poder de generalización, pero esto sería inútil al hacer predicciones. en datos nunca antes vistos.

Siempre tenga esto en cuenta: los modelos de aprendizaje profundo tienden a adaptarse bien a los datos de entrenamiento, pero el verdadero desafío es la generalización, no la adaptación.

Por otro lado, si la red tiene recursos de memorización limitados, no podrá aprender el mapeo tan fácilmente. Para minimizar su pérdida, tendrá que aprender representaciones comprimidas que tengan más poder predictivo. Al mismo tiempo, si hace que su modelo sea demasiado pequeño, tendrá dificultades para adaptarse a los datos de entrenamiento. Existe un equilibrio entre "demasiada capacidad" y "poca capacidad".

Desafortunadamente, no existe una fórmula mágica para determinar el tamaño o la arquitectura correctos de su modelo (en términos de la cantidad de capas o el tamaño correcto para cada capa). Tendrás que experimentar utilizando una serie de arquitecturas diferentes.

Para encontrar un tamaño de modelo apropiado, es mejor comenzar con relativamente pocas capas y parámetros, luego comenzar a aumentar el tamaño de las capas o agregar nuevas capas hasta que vea rendimientos decrecientes en la pérdida de validación.

Comience con un modelo simple usando solo layers.Dense como línea de base, luego cree versiones más grandes y compárelas.

Procedimiento de entrenamiento

Muchos modelos entrenan mejor si reduce gradualmente la tasa de aprendizaje durante el entrenamiento. Use optimizers.schedules para reducir la tasa de aprendizaje con el tiempo:

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)

El código anterior establece un schedules.InverseTimeDecay para disminuir hiperbólicamente la tasa de aprendizaje a la mitad de la tasa base en 1000 épocas, 1/3 en 2000 épocas y así sucesivamente.

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

Cada modelo de este tutorial utilizará la misma configuración de entrenamiento. Así que configúrelos de manera reutilizable, comenzando con la lista de devoluciones de llamada.

El entrenamiento para este tutorial se ejecuta durante muchas épocas cortas. Para reducir el ruido de registro, use tfdocs.EpochDots que simplemente imprime un . para cada época y un conjunto completo de métricas cada 100 épocas.

A continuación, incluya callbacks.EarlyStopping de callbacks.EarlyStopping para evitar tiempos de entrenamiento largos e innecesarios. Tenga en cuenta que esta devolución de llamada está configurada para monitorear val_binary_crossentropy , no val_loss . Esta diferencia será importante más adelante.

Usa callbacks.TensorBoard para generar registros de TensorBoard para el entrenamiento.

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 manera similar, cada modelo utilizará la misma configuración de Model.compile y 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

Pequeño modelo

Empiece por entrenar un modelo:

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

Ahora mira cómo le fue al modelo:

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

Modelo pequeño

Para ver si puede superar el rendimiento del modelo pequeño, entrene progresivamente algunos modelos más grandes.

Prueba dos capas ocultas con 16 unidades cada una:

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

Modelo mediano

Ahora prueba 3 capas ocultas con 64 unidades cada una:

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

Y entrena el modelo usando los mismos datos:

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

Modelo grande

Como ejercicio, puede crear un modelo aún más grande y ver qué tan rápido comienza a sobreajustarse. A continuación, agreguemos a este punto de referencia una red que tiene mucha más capacidad, mucho más de lo que el problema justificaría:

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

Y, nuevamente, entrene el modelo usando los mismos datos:

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

Trazar las pérdidas de formación y validación

Las líneas continuas muestran la pérdida de entrenamiento y las líneas discontinuas muestran la pérdida de validación (recuerde: una pérdida de validación menor indica un modelo mejor).

Si bien la construcción de un modelo más grande le da más poder, si este poder no está restringido de alguna manera, puede adaptarse fácilmente al conjunto de entrenamiento.

En este ejemplo, normalmente, solo el modelo "Tiny" logra evitar el sobreajuste por completo, y cada uno de los modelos más grandes sobreajusta los datos más rápidamente. Esto se vuelve tan severo para el modelo "large" que necesita cambiar el gráfico a una escala logarítmica para ver realmente lo que está sucediendo.

Esto es evidente si traza y compara las métricas de validación con las métricas de entrenamiento.

  • Es normal que haya una pequeña diferencia.
  • Si ambas métricas se mueven en la misma dirección, todo está bien.
  • Si la métrica de validación comienza a estancarse mientras la métrica de entrenamiento continúa mejorando, probablemente esté cerca de sobreajustar.
  • Si la métrica de validación va en la dirección incorrecta, el modelo está claramente sobreajustado.
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

Ver en TensorBoard

Todos estos modelos escribieron registros de TensorBoard durante el entrenamiento.

Abra un visor de TensorBoard integrado dentro de una computadora portátil:


# Load the TensorBoard notebook extension
%load_ext tensorboard

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

Puede ver los resultados de una ejecución anterior de este cuaderno en TensorBoard.dev .

TensorBoard.dev es una experiencia administrada para alojar, rastrear y compartir experimentos de AA con todos.

También se incluye en un <iframe> para mayor comodidad:

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

Si desea compartir los resultados de TensorBoard, puede cargar los registros en TensorBoard.dev copiando lo siguiente en una celda de código.

tensorboard dev upload --logdir  {logdir}/sizes

Estrategias para prevenir el sobreajuste

Antes de entrar en el contenido de esta sección, copie los registros de entrenamiento del modelo "Tiny" anterior, para usarlos como referencia para la comparación.

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

Agregar regularización de peso

Es posible que esté familiarizado con el principio de la navaja de Occam: dadas dos explicaciones para algo, la explicación que probablemente sea correcta es la "más simple", la que hace la menor cantidad de suposiciones. Esto también se aplica a los modelos aprendidos por las redes neuronales: dados algunos datos de entrenamiento y una arquitectura de red, existen múltiples conjuntos de valores de ponderación (múltiples modelos) que podrían explicar los datos, y los modelos más simples tienen menos probabilidades de sobreajustarse que los complejos.

Un "modelo simple" en este contexto es un modelo donde la distribución de los valores de los parámetros tiene menos entropía (o un modelo con menos parámetros en total, como vimos en la sección anterior). Por lo tanto, una forma común de mitigar el sobreajuste es imponer restricciones a la complejidad de una red obligando a sus pesos a tomar valores pequeños, lo que hace que la distribución de los valores de peso sea más "regular". Esto se llama "regularización de peso" y se realiza agregando a la función de pérdida de la red un costo asociado con tener grandes pesos. Este costo viene en dos sabores:

  • Regularización L1 , donde el costo agregado es proporcional al valor absoluto de los coeficientes de ponderaciones (es decir, a lo que se denomina la "norma L1" de las ponderaciones).

  • Regularización L2 , donde el costo agregado es proporcional al cuadrado del valor de los coeficientes de los pesos (es decir, a lo que se llama la "norma L2" al cuadrado de los pesos). La regularización de L2 también se denomina disminución de peso en el contexto de las redes neuronales. No permita que el nombre diferente lo confunda: la disminución del peso es matemáticamente exactamente igual que la regularización L2.

La regularización L1 empuja los pesos hacia exactamente cero, lo que fomenta un modelo disperso. La regularización L2 penalizará los parámetros de pesos sin hacerlos dispersos, ya que la penalización llega a cero para pesos pequeños. una de las razones por las que L2 es más común.

En tf.keras , la regularización de peso se agrega pasando instancias de regularizador de peso a capas como argumentos de palabras clave. Agreguemos ahora la regularización de peso 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) significa que cada coeficiente en la matriz de ponderaciones de la capa agregará 0.001 * weight_coefficient_value**2 a la pérdida total de la red.

Es por eso que estamos monitoreando la binary_crossentropy directamente. Porque no tiene este componente de regularización mezclado.

Entonces, ese mismo modelo "Large" con una penalización de regularización L2 funciona mucho mejor:

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

png

Como puede ver, el modelo regularizado "L2" es ahora mucho más competitivo que el modelo "Tiny" . Este modelo "L2" también es mucho más resistente al sobreajuste que el modelo "Large" que se basó a pesar de tener el mismo número de parámetros.

Más información

Hay dos cosas importantes a tener en cuenta sobre este tipo de regularización.

Primero: si está escribiendo su propio ciclo de entrenamiento, entonces debe asegurarse de preguntarle al modelo sus pérdidas de regularización.

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

Segundo: esta implementación funciona agregando las penalizaciones de peso a la pérdida del modelo y luego aplicando un procedimiento de optimización estándar después de eso.

Hay un segundo enfoque que, en cambio, solo ejecuta el optimizador en la pérdida bruta, y luego, mientras se aplica el paso calculado, el optimizador también aplica cierta disminución de peso. Esta "disminución de peso desacoplada" se ve en optimizadores como optimizers.FTRL y optimizers.AdamW .

Agregar abandono

La deserción es una de las técnicas de regularización más efectivas y más utilizadas para las redes neuronales, desarrollada por Hinton y sus estudiantes en la Universidad de Toronto.

La explicación intuitiva de la deserción es que debido a que los nodos individuales de la red no pueden depender de la salida de los demás, cada nodo debe generar características que sean útiles por sí mismas.

La eliminación, aplicada a una capa, consiste en "eliminar" aleatoriamente (es decir, establecer en cero) una serie de características de salida de la capa durante el entrenamiento. Digamos que una capa dada normalmente habría devuelto un vector [0.2, 0.5, 1.3, 0.8, 1.1] para una muestra de entrada dada durante el entrenamiento; después de aplicar el abandono, este vector tendrá algunas entradas cero distribuidas al azar, por ejemplo, [0, 0.5, 1.3, 0, 1.1].

La "tasa de abandono" es la fracción de las funciones que se están reduciendo a cero; normalmente se establece entre 0,2 y 0,5. En el momento de la prueba, no se elimina ninguna unidad y, en cambio, los valores de salida de la capa se reducen en un factor igual a la tasa de abandono, para equilibrar el hecho de que hay más unidades activas que en el momento del entrenamiento.

En tf.keras puede introducir la deserción en una red a través de la capa Dropout, que se aplica a la salida de la capa justo antes.

Agreguemos dos capas de deserción en nuestra red para ver qué tan bien lo hacen para reducir el sobreajuste:

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

De esta gráfica se desprende claramente que ambos enfoques de regularización mejoran el comportamiento del modelo "Large" . Pero esto todavía no supera ni siquiera la línea de base "Tiny" .

A continuación, pruébelos juntos y vea si funciona mejor.

Abandono combinado 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

Este modelo con la regularización "Combined" es obviamente el mejor hasta ahora.

Ver en TensorBoard

Estos modelos también registraron registros de TensorBoard.

Para abrir un visor de tensorboard incrustado dentro de un cuaderno, copie lo siguiente en una celda de código:

%tensorboard --logdir {logdir}/regularizers

Puede ver los resultados de una ejecución anterior de este cuaderno en TensorDoard.dev .

También se incluye en un <iframe> para mayor comodidad:

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

Esto fue subido con:

tensorboard dev upload --logdir  {logdir}/regularizers

Conclusiones

En resumen: estas son las formas más comunes de prevenir el sobreajuste en las redes neuronales:

  • Obtén más datos de entrenamiento.
  • Reducir la capacidad de la red.
  • Agrega regularización de peso.
  • Agregar deserción.

Dos enfoques importantes no cubiertos en esta guía son:

  • aumento de datos
  • normalización de lotes

Recuerde que cada método puede ayudar por sí solo, pero a menudo combinarlos puede ser aún más efectivo.


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