Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/guide/keras/train_and_evaluate.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Entrenamiento y evaluación con los métodos integrados

Preparación

import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers

Introducción

Esta guía cubre los modelos de entrenamiento, evaluación y predicción (inferencia) cuando se utilizan las API incorporadas para el entrenamiento y la validación (como Model.fit(), Model.evaluate() y Model.predict()).

Si está interesado en aprovechar fit() mientras especifica su propia función de pasos de entrenamiento, consulte la guía Personalización de lo que ocurre en fit().

Si está interesado en escribir sus propios bucles de entrenamiento y evaluación desde cero, consulte la guía "escribir un bucle de entrenamiento desde cero".

En general, tanto si utiliza bucles incorporados como si escribe los suyos propios, el entrenamiento y la evaluación de modelos funcionan estrictamente de la misma manera en todos los tipos de modelos Keras: modelos secuenciales, modelos construidos con la API funcional y modelos escritos desde cero mediante subclases de modelos.

Esta guía no cubre el entrenamiento distribuido, que está cubierto en nuestra guía de multi-GPU & entrenamiento distribuido.

Visión general de la API: un primer ejemplo de extremo a extremo

Cuando pase datos a los bucles de entrenamiento incorporados de un modelo, debería utilizar matrices NumPy (si sus datos son pequeños y caben en la memoria) u objetos tf.data Dataset. En los próximos párrafos, utilizaremos el conjunto de datos MNIST como matrices NumPy, con el fin de demostrar cómo utilizar optimizadores, pérdidas y métricas.

Consideremos el siguiente modelo (aquí, construimos con la API Funcional, pero podría ser un modelo Secuencial o un modelo subclaseado también):

inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, activation="softmax", name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs)

Este es el flujo de trabajo típico de principio a fin:

  • Entrenamiento

  • Validación en un conjunto de retención generado a partir de los datos de entrenamiento originales

  • Evaluación de los datos de las pruebas

Para este ejemplo utilizaremos datos MNIST.

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() # Preprocess the data (these are NumPy arrays) x_train = x_train.reshape(60000, 784).astype("float32") / 255 x_test = x_test.reshape(10000, 784).astype("float32") / 255 y_train = y_train.astype("float32") y_test = y_test.astype("float32") # Reserve 10,000 samples for validation x_val = x_train[-10000:] y_val = y_train[-10000:] x_train = x_train[:-10000] y_train = y_train[:-10000]

Especificamos la configuración de entrenamiento (optimizador, pérdida, métrica):

model.compile( optimizer=keras.optimizers.RMSprop(), # Optimizer # Loss function to minimize loss=keras.losses.SparseCategoricalCrossentropy(), # List of metrics to monitor metrics=[keras.metrics.SparseCategoricalAccuracy()], )

Llamamos a fit(), que entrenará el modelo cortando los datos en "lotes" de tamaño batch_size, e iterando repetidamente sobre todo el conjunto de datos para obtener un número determinado de epochs.

print("Fit model on training data") history = model.fit( x_train, y_train, batch_size=64, epochs=2, # We pass some validation for # monitoring validation loss and metrics # at the end of each epoch validation_data=(x_val, y_val), )

El objeto history devuelto contiene un registro de los valores de pérdida y los valores métricos durante el entrenamiento:

history.history

Evaluamos el modelo en los datos de prueba mediante evaluate():

# Evaluate the model on the test data using `evaluate` print("Evaluate on test data") results = model.evaluate(x_test, y_test, batch_size=128) print("test loss, test acc:", results) # Generate predictions (probabilities -- the output of the last layer) # on new data using `predict` print("Generate predictions for 3 samples") predictions = model.predict(x_test[:3]) print("predictions shape:", predictions.shape)

Ahora, revisemos cada pieza de este flujo de trabajo en detalle.

El método compile(): especifica una pérdida, métricas y un optimizador

Para entrenar un modelo con fit(), necesita especificar una función de pérdida, un optimizador y, de forma opcional, algunas métricas que monitorear.

Se pasan al modelo como argumentos del método compile():

model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[keras.metrics.SparseCategoricalAccuracy()], )

El argumento metrics debe ser una lista -- su modelo puede tener cualquier número de métricas.

Si su modelo tiene múltiples salidas, puede especificar diferentes pérdidas y métricas para cada salida, y puede modular la contribución de cada salida a la pérdida total del modelo. Encontrará más detalles al respecto en la sección Paso de datos a modelos con múltiples entradas y salidas.

Tenga en cuenta que si está satisfecho con la configuración predeterminada, en muchos casos el optimizador, la pérdida y las métricas se pueden especificar mediante identificadores de cadena como un acceso directo:

model.compile( optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["sparse_categorical_accuracy"], )

Para su posterior re-uso, pongamos nuestra definición del modelo y el paso de compilación en funciones; las llamaremos varias veces por medio de diferentes ejemplos en esta guía.

def get_uncompiled_model(): inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, activation="softmax", name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) return model def get_compiled_model(): model = get_uncompiled_model() model.compile( optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["sparse_categorical_accuracy"], ) return model

Hay disponibles muchos optimizadores, pérdidas y métricas integrados.

En general, no tendrá que crear sus propias pérdidas, métricas u optimizadores desde cero, porque es probable que lo que necesite ya forme parte de la API de Keras:

Optimizadores:

  • SGD() (con o sin momentum)

  • RMSprop()

  • Adam()

  • etc.

Pérdidas:

  • MeanSquaredError()

  • KLDivergence()

  • CosineSimilarity()

  • etc.

Métricas:

  • AUC()

  • Precision()

  • Recall()

  • etc.

Pérdidas personalizadas

Si necesita crear una pérdida personalizada, Keras proporciona dos formas de hacerlo.

El primer método consiste en crear una función que acepte las entradas y_true y y_pred. El siguiente ejemplo muestra una función de pérdida que calcula el error cuadrático promedio entre los datos reales y las predicciones:

def custom_mean_squared_error(y_true, y_pred): return tf.math.reduce_mean(tf.square(y_true - y_pred)) model = get_uncompiled_model() model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error) # We need to one-hot encode the labels to use MSE y_train_one_hot = tf.one_hot(y_train, depth=10) model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)

Si necesita una función de pérdida que tome parámetros además de y_true y y_pred, puede subclasificar la clase tf.keras.losses.Loss e implementar los dos siguientes métodos:

  • __init__(self): aceptar parámetros para pasar durante la llamada de su función de pérdida

  • call(self, y_true, y_pred): utilizar los objetivos (y_true) y las predicciones del modelo (y_pred) para calcular la pérdida del modelo

Digamos que desea utilizar el error cuadrático promedio, pero con un término adicional que desincentivará los valores de predicción alejados de 0.5 (suponemos que los objetivos categóricos se codifican con un solo disparo y toman valores entre 0 y 1). Esto crea un incentivo para que el modelo no se confíe demasiado, lo que puede ayudar a reducir el sobreajuste (¡no sabremos si funciona hasta que lo probemos!).

Así es como debe hacerlo:

class CustomMSE(keras.losses.Loss): def __init__(self, regularization_factor=0.1, name="custom_mse"): super().__init__(name=name) self.regularization_factor = regularization_factor def call(self, y_true, y_pred): mse = tf.math.reduce_mean(tf.square(y_true - y_pred)) reg = tf.math.reduce_mean(tf.square(0.5 - y_pred)) return mse + reg * self.regularization_factor model = get_uncompiled_model() model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE()) y_train_one_hot = tf.one_hot(y_train, depth=10) model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)

Métricas personalizadas

Si necesita una métrica que no forma parte de la API, puede crear fácilmente métricas personalizadas subclasificando la clase tf.keras.metrics.Metric. Necesitará implementar 4 métodos:

  • __init__(self), en el que creará variables de estado para su métrica.

  • update_state(self, y_true, y_pred, sample_weight=None), que utiliza los objetivos y_true y las predicciones del modelo y_pred para actualizar las variables de estado.

  • result(self), que utiliza las variables de estado para calcular los resultados finales.

  • reset_state(self), que reinicializa el estado de la métrica.

La actualización del estado y el cálculo de resultados se mantienen separados (en update_state() y result(), respectivamente) porque en algunos casos, el cálculo de resultados puede ser muy costoso y solo se realizaría periódicamente.

A continuación se muestra un ejemplo sencillo de cómo implementar una métrica CategoricalTruePositives que cuenta cuántas muestras se clasificaron correctamente como si pertenecieran a una clase determinada:

class CategoricalTruePositives(keras.metrics.Metric): def __init__(self, name="categorical_true_positives", **kwargs): super(CategoricalTruePositives, self).__init__(name=name, **kwargs) self.true_positives = self.add_weight(name="ctp", initializer="zeros") def update_state(self, y_true, y_pred, sample_weight=None): y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1)) values = tf.cast(y_true, "int32") == tf.cast(y_pred, "int32") values = tf.cast(values, "float32") if sample_weight is not None: sample_weight = tf.cast(sample_weight, "float32") values = tf.multiply(values, sample_weight) self.true_positives.assign_add(tf.reduce_sum(values)) def result(self): return self.true_positives def reset_state(self): # The state of the metric will be reset at the start of each epoch. self.true_positives.assign(0.0) model = get_uncompiled_model() model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[CategoricalTruePositives()], ) model.fit(x_train, y_train, batch_size=64, epochs=3)

Manejo de pérdidas y métricas que no se ajustan a la firma estándar

La inmensa mayoría de las pérdidas y métricas pueden calcularse a partir de y_true y y_pred, donde y_pred es una salida del modelo, pero no todas. Por ejemplo, una pérdida de regularización puede requerir solo la activación de una capa (no hay objetivos en este caso), y esta activación puede no ser una salida del modelo.

En tales casos, puede llamar a self.add_loss(loss_value) desde dentro del método de llamada de una capa personalizada. Las pérdidas agregadas de esta manera se suman a la pérdida "principal" durante el entrenamiento (la que se pasa a compile()). Aquí hay un ejemplo simple que agrega regularización de la actividad (tenga en cuenta que la regularización de la actividad está incorporada en todas las capas de Keras - esta capa es solo para proporcionar un ejemplo concreto):

class ActivityRegularizationLayer(layers.Layer): def call(self, inputs): self.add_loss(tf.reduce_sum(inputs) * 0.1) return inputs # Pass-through layer. inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) # Insert activity regularization as a layer x = ActivityRegularizationLayer()(x) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) # The displayed loss will be much higher than before # due to the regularization component. model.fit(x_train, y_train, batch_size=64, epochs=1)

Puede hacer lo mismo para registrar valores métricos, utilizando add_metric():

class MetricLoggingLayer(layers.Layer): def call(self, inputs): # The `aggregation` argument defines # how to aggregate the per-batch values # over each epoch: # in this case we simply average them. self.add_metric( keras.backend.std(inputs), name="std_of_activation", aggregation="mean" ) return inputs # Pass-through layer. inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) # Insert std logging as a layer. x = MetricLoggingLayer()(x) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)

En la API funcional, también puede llamar a model.add_loss(loss_tensor), o a model.add_metric(metric_tensor, name, aggregation).

Este es un ejemplo sencillo:

inputs = keras.Input(shape=(784,), name="digits") x1 = layers.Dense(64, activation="relu", name="dense_1")(inputs) x2 = layers.Dense(64, activation="relu", name="dense_2")(x1) outputs = layers.Dense(10, name="predictions")(x2) model = keras.Model(inputs=inputs, outputs=outputs) model.add_loss(tf.reduce_sum(x1) * 0.1) model.add_metric(keras.backend.std(x1), name="std_of_activation", aggregation="mean") model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)

Tenga en cuenta que cuando se pasan las pérdidas mediante add_loss(), se hace posible llamar a compile() sin una función de pérdida, ya que el modelo ya tiene una pérdida que minimizar.

Considere la siguiente capa LogisticEndpoint: toma como entradas objetivos y logits, y rastrea una pérdida de entropía cruzada mediante add_loss(). También realiza un seguimiento de la precisión de la clasificación mediante add_metric().

class LogisticEndpoint(keras.layers.Layer): def __init__(self, name=None): super(LogisticEndpoint, self).__init__(name=name) self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True) self.accuracy_fn = keras.metrics.BinaryAccuracy() def call(self, targets, logits, sample_weights=None): # Compute the training-time loss value and add it # to the layer using `self.add_loss()`. loss = self.loss_fn(targets, logits, sample_weights) self.add_loss(loss) # Log accuracy as a metric and add it # to the layer using `self.add_metric()`. acc = self.accuracy_fn(targets, logits, sample_weights) self.add_metric(acc, name="accuracy") # Return the inference-time prediction tensor (for `.predict()`). return tf.nn.softmax(logits)

Puede utilizarlo en un modelo con dos entradas (datos de entrada y objetivos), compilado sin un argumento loss, así:

import numpy as np inputs = keras.Input(shape=(3,), name="inputs") targets = keras.Input(shape=(10,), name="targets") logits = keras.layers.Dense(10)(inputs) predictions = LogisticEndpoint(name="predictions")(logits, targets) model = keras.Model(inputs=[inputs, targets], outputs=predictions) model.compile(optimizer="adam") # No loss argument! data = { "inputs": np.random.random((3, 3)), "targets": np.random.random((3, 10)), } model.fit(data)

Para obtener más información sobre el entrenamiento de modelos multientrada, consulte la sección Paso de datos a modelos multientrada y multisalida.

Apartar automáticamente un conjunto de retención de validación

En el primer ejemplo de extremo a extremo que vio, usamos el argumento validation_data para pasar una tupla de matrices NumPy (x_val, y_val) al modelo para evaluar una pérdida de validación y métricas de validación al final de cada época.

Aquí hay otra opción: el argumento validation_split permite reservar automáticamente parte de los datos de entrenamiento para realizar la validación. El valor del argumento representa la fracción de los datos que se reservará para la validación, por lo que debe establecerse en un número superior a 0 e inferior a 1. Por ejemplo, validation_split=0.2 significa "utilizar el 20% de los datos para la validación", y validation_split=0.6 significa "utilizar el 60% de los datos para la validación".

La forma en que se calcula la validación es tomar las últimas muestras x% de las matrices recibidas por la llamada fit(), antes de mezclar.

Tenga en cuenta que solo puede utilizar validation_split cuando entrene con datos NumPy.

model = get_compiled_model() model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=1)

Entrenamiento y evaluación a partir de conjuntos de datos tf.data

En los últimos párrafos, habrá visto cómo manejar pérdidas, métricas y optimizadores, y habrá visto cómo usar los argumentos validation_data y validation_split en fit(), cuando sus datos se pasan como matrices NumPy.

Veamos ahora el caso en el que los datos se presentan en forma de objeto tf.data.Dataset.

La API tf.data es un conjunto de utilidades de TensorFlow 2.0 para cargar y preprocesar datos de forma rápida y escalable.

Para obtener una guía completa sobre la creación de Datasets, consulte la documentación tf.data.

Puede pasar una instancia Dataset directamente a los métodos fit(), evaluate() y predict():

model = get_compiled_model() # First, let's create a training Dataset instance. # For the sake of our example, we'll use the same MNIST data as before. train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Now we get a test dataset. test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)) test_dataset = test_dataset.batch(64) # Since the dataset already takes care of batching, # we don't pass a `batch_size` argument. model.fit(train_dataset, epochs=3) # You can also evaluate or predict on a dataset. print("Evaluate") result = model.evaluate(test_dataset) dict(zip(model.metrics_names, result))

Tenga en cuenta que el conjunto de datos se restablece al final de cada época, por lo que puede volver a utilizarse en la siguiente.

Si desea ejecutar el entrenamiento solo en un número específico de lotes de este conjunto de datos, puede pasar el argumento steps_per_epoch, que especifica cuántos pasos de entrenamiento debe ejecutar el modelo utilizando este conjunto de datos antes de pasar a la siguiente época.

Si se hace esto, el conjunto de datos no se reinicia al final de cada época, sino que se siguen dibujando los siguientes lotes. El conjunto de datos eventualmente se quedará sin datos (a menos que sea un conjunto de datos de bucle infinito).

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Only use the 100 batches per epoch (that's 64 * 100 samples) model.fit(train_dataset, epochs=3, steps_per_epoch=100)

Utilizar un conjunto de datos de validación

Puede pasar una instancia de Dataset como argumento de validation_data en fit():

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) model.fit(train_dataset, epochs=1, validation_data=val_dataset)

Al final de cada época, el modelo iterará sobre el conjunto de datos de validación y calculará la pérdida de validación y las métricas de validación.

Si desea ejecutar la validación solo en un número específico de lotes de este conjunto de datos, puede pasar el argumento validation_steps, que especifica cuántos pasos de validación debe ejecutar el modelo con el conjunto de datos de validación antes de interrumpir la validación y pasar a la siguiente época:

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) model.fit( train_dataset, epochs=1, # Only run validation using the first 10 batches of the dataset # using the `validation_steps` argument validation_data=val_dataset, validation_steps=10, )

Tenga en cuenta que el conjunto de datos de validación se restablecerá después de cada uso (de modo que siempre evaluará las mismas muestras de una época a otra).

El argumento validation_split (generar un conjunto de retención a partir de los datos de entrenamiento) no es compatible cuando se entrena a partir de objetos Dataset, ya que esta función requiere la capacidad de indexar las muestras de los conjuntos de datos, lo que no es posible en general con la API Dataset.

Otros formatos de entrada compatibles

Además de las matrices NumPy, los tensores eager y los Datasets de TensorFlow, es posible entrenar un modelo Keras usando dataframes Pandas, o desde generadores Python que producen lotes de datos y etiquetas.

En particular, la clase keras.utils.Sequence ofrece una interfaz sencilla para construir generadores de datos Python compatibles con el multiprocesamiento y que se pueden mezclar.

En general, le recomendamos que utilice:

  • Datos de entrada NumPy si sus datos son pequeños y caben en la memoria

  • Dataset objetos si tiene grandes conjuntos de datos y necesita realizar un entrenamiento distribuido.

  • Sequence si tiene grandes conjuntos de datos y necesita hacer mucho procesamiento personalizado en Python que no se puede hacer en TensorFlow (por ejemplo, si depende de bibliotecas externas para cargar o preprocesar datos).

Utilizando un objeto keras.utils.Sequence como entrada

keras.utils.Sequence es una utilidad que podrá subclasificar para obtener un generador de Python con dos propiedades importantes:

  • Funciona bien con el multiprocesamiento.

  • Se puede mezclar (por ejemplo, al pasar shuffle=True en fit()).

Un Sequence debe implementar dos métodos:

  • __getitem__

  • __len__

El método __getitem__ debe devolver un lote completo. Si desea modificar su conjunto de datos entre épocas, puede implementar on_epoch_end.

Este es un ejemplo rápido:

from skimage.io import imread from skimage.transform import resize import numpy as np # Here, `filenames` is list of path to the images # and `labels` are the associated labels. class CIFAR10Sequence(Sequence): def __init__(self, filenames, labels, batch_size): self.filenames, self.labels = filenames, labels self.batch_size = batch_size def __len__(self): return int(np.ceil(len(self.filenames) / float(self.batch_size))) def __getitem__(self, idx): batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size] batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size] return np.array([ resize(imread(filename), (200, 200)) for filename in batch_x]), np.array(batch_y) sequence = CIFAR10Sequence(filenames, labels, batch_size) model.fit(sequence, epochs=10)

Cómo utilizar la ponderación de muestras y la ponderación de clases

Con la configuración predeterminada, el peso de una muestra se decide por su frecuencia en el conjunto de datos. Existen dos métodos para ponderar los datos, independientes de la frecuencia de las muestras:

  • Pesos de la clase

  • Pesos de la muestra

Pesos de la clase

Se establece pasando un diccionario al argumento class_weight de Model.fit(). Este diccionario asigna índices de clase al peso que debe utilizarse para las muestras que pertenecen a esta clase.

Esto se puede utilizar para equilibrar las clases sin remuestreo, o para entrenar un modelo que dé más importancia a una clase en particular.

Por ejemplo, si la clase "0" tiene la mitad de representación que la clase "1" en sus datos, podría utilizar Model.fit(..., class_weight={0: 1., 1: 0.5}).

Este es un ejemplo de NumPy en el que utilizamos pesos de la clase o pesos de la muestra para dar más importancia a la clasificación correcta de la clase #5 (que es el dígito "5" en el conjunto de datos MNIST).

import numpy as np class_weight = { 0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, # Set weight "2" for class "5", # making this class 2x more important 5: 2.0, 6: 1.0, 7: 1.0, 8: 1.0, 9: 1.0, } print("Fit with class weight") model = get_compiled_model() model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)

Pesos de la muestra

Para un control más preciso, o si no está construyendo un clasificador, puede utilizar "pesos de la muestra".

  • Cuando se entrena a partir de datos NumPy: Pase el argumento sample_weight a Model.fit().

  • Cuando se entrena a partir de tf.data o cualquier otro tipo de iterador: Produce tuplas (input_batch, label_batch, sample_weight_batch).

Un arreglo de "pesos de la muestra" es un arreglo de números que especifica cuánto peso debe tener cada muestra de un lote en el cálculo de la pérdida total. Se suele utilizar en problemas de clasificación desequilibrada (la idea es dar más peso a las clases poco frecuentes).

Cuando los pesos utilizados son unos y ceros, el arreglo puede utilizarse como una máscara para la función de pérdida (se descarta por completo la contribución de ciertas muestras en la pérdida total).

sample_weight = np.ones(shape=(len(y_train),)) sample_weight[y_train == 5] = 2.0 print("Fit with sample weight") model = get_compiled_model() model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=1)

A continuación se muestra un ejemplo de Dataset:

sample_weight = np.ones(shape=(len(y_train),)) sample_weight[y_train == 5] = 2.0 # Create a Dataset that includes sample weights # (3rd element in the return tuple). train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model = get_compiled_model() model.fit(train_dataset, epochs=1)

Pasar datos a modelos multientrada y multisalida

En los ejemplos anteriores, considerábamos un modelo con una única entrada (un tensor de forma (764,)) y una única salida (un tensor de predicción de forma (10,)). Pero, ¿qué ocurre con los modelos que tienen varias entradas o salidas?

Consideremos el siguiente modelo, que tiene una entrada de imagen de forma (32, 32, 3) (es decir, (height, width, channels)) y una entrada de serie temporal de forma (None, 10) (es decir, (timesteps, features)). Nuestro modelo tendrá dos salidas calculadas a partir de la combinación de estas entradas: una "puntuación" (de forma (1,)) y una distribución de probabilidad sobre cinco clases (de forma (5,)).

image_input = keras.Input(shape=(32, 32, 3), name="img_input") timeseries_input = keras.Input(shape=(None, 10), name="ts_input") x1 = layers.Conv2D(3, 3)(image_input) x1 = layers.GlobalMaxPooling2D()(x1) x2 = layers.Conv1D(3, 3)(timeseries_input) x2 = layers.GlobalMaxPooling1D()(x2) x = layers.concatenate([x1, x2]) score_output = layers.Dense(1, name="score_output")(x) class_output = layers.Dense(5, name="class_output")(x) model = keras.Model( inputs=[image_input, timeseries_input], outputs=[score_output, class_output] )

Vamos a trazar este modelo, para que pueda ver claramente lo que estamos haciendo aquí (tenga en cuenta que las formas que se muestran en el gráfico son formas de lote, en vez de formas por muestra).

keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)

En el momento de la compilación, podemos especificar diferentes pérdidas para diferentes salidas, pasando las funciones de pérdida como una lista:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], )

Si solo pasamos una única función de pérdida al modelo, se aplicará la misma función de pérdida a cada salida (lo que no es apropiado en este caso).

Lo mismo sucede con las métricas:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], metrics=[ [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], [keras.metrics.CategoricalAccuracy()], ], )

Como dimos nombres a nuestras capas de salida, también podríamos especificar pérdidas y métricas por salida mediante un dict:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ "score_output": keras.losses.MeanSquaredError(), "class_output": keras.losses.CategoricalCrossentropy(), }, metrics={ "score_output": [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], "class_output": [keras.metrics.CategoricalAccuracy()], }, )

Recomendamos el uso de nombres explícitos y dicts si tiene más de 2 salidas.

Es posible dar diferentes pesos a diferentes pérdidas específicas de la salida (por ejemplo, uno podría desear privilegiar la pérdida de "puntuación" en nuestro ejemplo, dando a 2x la importancia de la pérdida de la clase), utilizando el argumento loss_weights:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ "score_output": keras.losses.MeanSquaredError(), "class_output": keras.losses.CategoricalCrossentropy(), }, metrics={ "score_output": [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], "class_output": [keras.metrics.CategoricalAccuracy()], }, loss_weights={"score_output": 2.0, "class_output": 1.0}, )

También puede elegir no calcular una pérdida para ciertas salidas, si estas salidas están destinadas a la predicción pero no al entrenamiento:

# List loss version model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[None, keras.losses.CategoricalCrossentropy()], ) # Or dict loss version model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={"class_output": keras.losses.CategoricalCrossentropy()}, )

Pasar datos a un modelo multientrada o multisalida en fit() funciona de forma similar a especificar una función de pérdida en compilación: puede pasar listas de matrices NumPy (con mapeo 1:1 a las salidas que recibieron una función de pérdida) o dictos que mapean nombres de salida a matrices NumPy.

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], ) # Generate dummy NumPy data img_data = np.random.random_sample(size=(100, 32, 32, 3)) ts_data = np.random.random_sample(size=(100, 20, 10)) score_targets = np.random.random_sample(size=(100, 1)) class_targets = np.random.random_sample(size=(100, 5)) # Fit on lists model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1) # Alternatively, fit on dicts model.fit( {"img_input": img_data, "ts_input": ts_data}, {"score_output": score_targets, "class_output": class_targets}, batch_size=32, epochs=1, )

Este es el caso de uso de Dataset: de forma similar a lo que hicimos para las matrices de NumPy, Dataset debería devolver una tupla de dicts.

train_dataset = tf.data.Dataset.from_tensor_slices( ( {"img_input": img_data, "ts_input": ts_data}, {"score_output": score_targets, "class_output": class_targets}, ) ) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model.fit(train_dataset, epochs=1)

Cómo utilizar las retrollamadas

Las retrollamadas en Keras son objetos que se llaman en diferentes puntos durante el entrenamiento (al inicio de una época, al final de un lote, al final de una época, etc.). Pueden ser utilizados para implementar ciertos comportamientos, tales como:

  • Realizar la validación en diferentes puntos durante el entrenamiento (más allá de la validación incorporada por época).

  • Verificación del modelo a intervalos regulares o cuando supera un determinado umbral de precisión.

  • Modificación del ritmo de aprendizaje del modelo cuando el entrenamiento parece estancarse

  • Realizar un ajuste fino de las capas superiores cuando el entrenamiento parece estancarse.

  • Envío de notificaciones por correo electrónico o mensaje instantáneo cuando finalice el entrenamiento o se supere un determinado umbral de rendimiento.

  • Etc.

Las retrollamadas pueden pasarse como una lista a su llamada a fit():

model = get_compiled_model() callbacks = [ keras.callbacks.EarlyStopping( # Stop training when `val_loss` is no longer improving monitor="val_loss", # "no longer improving" being defined as "no better than 1e-2 less" min_delta=1e-2, # "no longer improving" being further defined as "for at least 2 epochs" patience=2, verbose=1, ) ] model.fit( x_train, y_train, epochs=20, batch_size=64, callbacks=callbacks, validation_split=0.2, )

Dispone de muchas retrollamadas integradas

Hay muchas retrollamadas incorporadas ya disponibles en Keras, tales como:

  • ModelCheckpoint: Guarda periódicamente el modelo.

  • Detención temprana: Detiene el entrenamiento cuando éste ya no mejora las métricas de validación.

  • TensorBoard: escribe periódicamente registros del modelo que pueden visualizarse en TensorBoard (más detalles en la sección "Visualización").

  • CSVLogger: transmite datos de pérdidas y métricas a un archivo CSV.

  • etc.

Consulte la documentación sobre retrollamadas para ver la lista completa.

Cómo escribir su propia retrollamada

Puede crear una retrollamada personalizada ampliando la clase base keras.callbacks.Callback. Una retrollamada tiene acceso a su modelo asociado mediante la propiedad de la clase self.model.

Asegúrese de leer la guía completa para escribir retrollamadas personalizadas.

Este es un ejemplo sencillo en el que se guarda una lista de valores de pérdida por lote durante el entrenamiento:

class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs): self.per_batch_losses = [] def on_batch_end(self, batch, logs): self.per_batch_losses.append(logs.get("loss"))

Modelos de punto de verificación

Cuando se entrena un modelo en conjuntos de datos relativamente grandes, es crucial guardar puntos de verificación del modelo a intervalos frecuentes.

La forma más sencilla de conseguirlo es con la retrollamada ModelCheckpoint:

model = get_compiled_model() callbacks = [ keras.callbacks.ModelCheckpoint( # Path where to save the model # The two parameters below mean that we will overwrite # the current checkpoint if and only if # the `val_loss` score has improved. # The saved model name will include the current epoch. filepath="mymodel_{epoch}", save_best_only=True, # Only save a model if `val_loss` has improved. monitor="val_loss", verbose=1, ) ] model.fit( x_train, y_train, epochs=2, batch_size=64, callbacks=callbacks, validation_split=0.2 )

La retrollamada ModelCheckpoint puede utilizarse para implementar la tolerancia ante errores: la capacidad de reiniciar el entrenamiento desde el último estado guardado del modelo en caso de que el entrenamiento se interrumpa aleatoriamente. Este es un ejemplo básico:

import os # Prepare a directory to store all the checkpoints. checkpoint_dir = "./ckpt" if not os.path.exists(checkpoint_dir): os.makedirs(checkpoint_dir) def make_or_restore_model(): # Either restore the latest model, or create a fresh one # if there is no checkpoint available. checkpoints = [checkpoint_dir + "/" + name for name in os.listdir(checkpoint_dir)] if checkpoints: latest_checkpoint = max(checkpoints, key=os.path.getctime) print("Restoring from", latest_checkpoint) return keras.models.load_model(latest_checkpoint) print("Creating a new model") return get_compiled_model() model = make_or_restore_model() callbacks = [ # This callback saves a SavedModel every 100 batches. # We include the training loss in the saved model name. keras.callbacks.ModelCheckpoint( filepath=checkpoint_dir + "/ckpt-loss={loss:.2f}", save_freq=100 ) ] model.fit(x_train, y_train, epochs=1, callbacks=callbacks)

También puede escribir su propia retrollamada para guardar y restaurar modelos.

Para obtener una guía completa sobre serialización y guardado, consulte la guía para guardar y serializar Modelos.

Cómo utilizar los programas de aprendizaje

Un patrón común cuando se entrenan modelos de aprendizaje profundo es reducir gradualmente el aprendizaje conforme avanza el entrenamiento. Esto se conoce generalmente como "decaimiento de la tasa de aprendizaje".

El programa de decaimiento del aprendizaje puede ser estático (fijado por adelantado, en función de la época actual o del índice del lote actual), o dinámico (que responde al comportamiento actual del modelo, en particular a la pérdida de validación).

Cómo pasar un programa a un optimizador

Puede utilizar fácilmente un programa estático de decaimiento de la tasa de aprendizaje pasando un objeto del programa como argumento learning_rate en su optimizador:

initial_learning_rate = 0.1 lr_schedule = keras.optimizers.schedules.ExponentialDecay( initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True ) optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)

Se dispone de varias programaciones incorporadas: ExponentialDecay, PiecewiseConstantDecay, PolynomialDecay, y InverseTimeDecay.

Uso de retrollamadas para aplicar una programación dinámica de la tasa de aprendizaje

Una programación dinámica de la tasa de aprendizaje (por ejemplo, la disminución de la tasa de aprendizaje cuando la pérdida de validación ya no está mejorando) no se puede lograr con estos objetos de programación, ya que el optimizador no tiene acceso a las métricas de validación.

Sin embargo, las retrollamadas tienen acceso a todas las métricas, ¡incluidas las de validación! Por lo tanto, puede lograr este patrón mediante el uso de una retrollamada que modifica la tasa de aprendizaje actual en el optimizador. De hecho, esto incluso está incorporado como la retrollamada ReduceLROnPlateau.

Visualización de pérdidas y métricas durante el entrenamiento

La mejor manera de mantener un ojo en su modelo durante el entrenamiento es utilizar TensorBoard -- una aplicación basada en el navegador que puede ejecutar localmente y le proporciona:

  • Gráficos en vivo de la pérdida y métricas para el entrenamiento y la evaluación

  • (opcionalmente) Visualizaciones de los histogramas de las activaciones de sus capas

  • (opcionalmente) visualizaciones 3D de los espacios de incorporación aprendidos por sus capas Embedding

Si instaló TensorFlow con la herramienta pip, debería poder iniciar TensorBoard desde la línea de comandos:

tensorboard --logdir=/full_path_to_your_logs

Cómo utilizar la retrollamada de TensorBoard

La forma más sencilla de utilizar TensorBoard con un modelo Keras y el método fit() es la retrollamada TensorBoard.

En el caso más sencillo, basta con especificar dónde desea que la retrollamada escriba los registros, y listo:

keras.callbacks.TensorBoard( log_dir="/full_path_to_your_logs", histogram_freq=0, # How often to log histogram visualizations embeddings_freq=0, # How often to log embedding visualizations update_freq="epoch", ) # How often to write logs (default: once per epoch)

Para obtener más información, consulte la documentación para la retrollamada TensorBoard.