Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/tutorials/images/transfer_learning.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.
#@title MIT License # # Copyright (c) 2017 François Chollet # IGNORE_COPYRIGHT: cleared by OSS licensing # # 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.

Aprendizaje por transferencia y la puesta a punto

En este tutorial aprenderá a clasificar imágenes de perros y gatos usando el aprendizaje por transferencia a partir de una red preentrenada.

Un modelo preentrenado es una red guardada que se ha entrenado previamente en un gran conjunto de datos, normalmente en una tarea de clasificación de imágenes a gran escala. Puede usar el modelo preentrenado tal cual o utilizar el aprendizaje por transferencia para adaptar este modelo a una tarea determinada.

La intuición que hay detrás del aprendizaje por transferencia para la clasificación de imágenes es que si se entrena un modelo en un conjunto de datos lo suficientemente grande y general, este modelo servirá efectivamente como modelo genérico del mundo visual. Entonces, se pueden aprovechar estos mapas de características aprendidas sin tener que empezar de cero a entrenar un modelo amplio en un gran conjunto de datos.

En este bloc de notas, probará dos formas de personalizar un modelo preentrenado:

  1. Extracción de características: Use las representaciones aprendidas por una red anterior para extraer características significativas de las nuevas muestras. Basta con añadir un nuevo clasificador, que se entrenará desde cero, sobre el modelo preentrenado para poder reutilizar los mapas de características aprendidas previamente para el conjunto de datos.

No es necesario (re)entrenar todo el modelo. La red convolucional base ya contiene características que son genéricamente útiles para clasificar imágenes. Sin embargo, la parte final de clasificación del modelo preentrenado es específica de la tarea de clasificación original y, posteriormente, específica del conjunto de clases sobre las que se estableció el modelo.

  1. Ajuste fino: Desbloquee algunas de las capas superiores de un modelo base inmovilizado y entrene conjuntamente tanto las capas clasificadoras recién añadidas como las últimas capas del modelo base. Esto nos permite "afinar" las representaciones de características de más alto orden en el modelo base para hacerlas más relevantes en la tarea específica.

Seguirá el flujo de trabajo general del aprendizaje automático.

  1. Examinar y comprender los datos

  2. Construir una canalización de entrada, en este caso usando el ImageDataGenerator de Keras.

  3. Armar el modelo

    • Cargar el modelo base preentrenado (y las ponderaciones preentrenadas)

    • Acumular las capas de clasificación en la parte superior

  4. Entrenar el modelo

  5. Evaluar el modelo

import matplotlib.pyplot as plt import numpy as np import os import tensorflow as tf

Preprocesamiento de los datos

Descarga de datos

En este tutorial, usará un conjunto de datos compuesto por varios miles de imágenes de gatos y perros. Descargue y extraiga un archivo zip que contiene las imágenes y, a continuación, cree un tf.data.Dataset para el entrenamiento y la validación mediante la utilidad tf.keras.utils.image_dataset_from_directory. Si desea obtener más información sobre la carga de imágenes, consulte este tutorial.

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip' path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True) PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered') train_dir = os.path.join(PATH, 'train') validation_dir = os.path.join(PATH, 'validation') BATCH_SIZE = 32 IMG_SIZE = (160, 160) train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)
validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir, shuffle=True, batch_size=BATCH_SIZE, image_size=IMG_SIZE)

Muestre las nueve primeras imágenes y etiquetas del conjunto de entrenamiento:

class_names = train_dataset.class_names plt.figure(figsize=(10, 10)) for images, labels in train_dataset.take(1): for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(images[i].numpy().astype("uint8")) plt.title(class_names[labels[i]]) plt.axis("off")

Como el conjunto de datos original no incluye un conjunto de prueba, deberá crear uno. Para ello, determine cuántos lotes de datos están disponibles en el conjunto de validación utilizando tf.data.experimental.cardinality y, a continuación, traslade el 20 % de ellos a un conjunto de prueba.

val_batches = tf.data.experimental.cardinality(validation_dataset) test_dataset = validation_dataset.take(val_batches // 5) validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset)) print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))

Configurar el conjunto de datos para rendimiento

Use la preextracción en búfer para cargar imágenes desde el disco sin que la E/S se bloquee. Si desea más información sobre este método, consulte la guía rendimiento de datos.

AUTOTUNE = tf.data.AUTOTUNE train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE) validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE) test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)

Usar aumentación de datos

Cuando no se dispone de un gran conjunto de datos de imágenes, es una buena práctica introducir artificialmente la diversidad del muestreo aplicando transformaciones aleatorias, aunque realistas, a las imágenes de entrenamiento, como la rotación y el volteado horizontal. Esto ayuda a exponer el modelo a diferentes aspectos de los datos de entrenamiento y a reducir el sobreajuste. Si desea obtener más información sobre la aumentación de datos, consulte este tutorial.

data_augmentation = tf.keras.Sequential([ tf.keras.layers.RandomFlip('horizontal'), tf.keras.layers.RandomRotation(0.2), ])

Nota: Estas capas sólo están activas durante el entrenamiento, cuando se llama a Model.fit. Están inactivas cuando se usa el modelo en modo de inferencia en Model.evaluate, Model.predict, o Model.call.

Apliquemos repetidamente estas capas a la misma imagen y veamos el resultado.

for image, _ in train_dataset.take(1): plt.figure(figsize=(10, 10)) first_image = image[0] for i in range(9): ax = plt.subplot(3, 3, i + 1) augmented_image = data_augmentation(tf.expand_dims(first_image, 0)) plt.imshow(augmented_image[0] / 255) plt.axis('off')

Reescalar valores de pixel

En un momento, descargará tf.keras.applications.MobileNetV2 para usarlo como modelo base. Este modelo espera valores de píxel en [-1, 1], pero en este momento, los valores de píxel de sus imágenes están en [0, 255]. Para reescalarlos, use el método de preprocesamiento incluido con el modelo.

preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input

Nota: Como alternativa, podría reescalar los valores de los píxeles de [0, 255] a [-1, 1] usando tf.keras.layers.Rescaling.

rescale = tf.keras.layers.Rescaling(1./127.5, offset=-1)

Nota: Si utiliza otras tf.keras.applications, asegúrese de consultar la documentación de la API para determinar si esperan pixeles en [-1, 1] o [0, 1], o use la función incluida preprocess_input.

Crear el modelo base a partir de las redes convolucionales preentrenadas

Creará el modelo base a partir del modelo MobileNet V2 desarrollado en Google. Éste está preentrenado en el conjunto de datos ImageNet, un gran conjunto de datos compuesto por 1.4M de imágenes y 1000 clases. ImageNet es un conjunto de datos de entrenamiento para la investigación con una gran variedad de categorías como jackfruit y syringe. Esta base de conocimientos nos ayudará a clasificar perros y gatos a partir de nuestro conjunto de datos específico.

En primer lugar, tiene que elegir qué capa de MobileNet V2 va a usar para la extracción de características. La última capa de clasificación (en la "parte superior", ya que la mayoría de los diagramas de modelos de aprendizaje automático van de abajo a arriba) no es muy útil. En su lugar, seguirá la práctica común de depender de la última capa antes de la operación de aplanamiento. Esta capa se denomina "capa cuello de botella". Las características de la capa cuello de botella conservan más generalidad comparadas con las de la capa final/superior.

En primer lugar, instancie un modelo MobileNet V2 precargado con ponderaciones entrenadas en ImageNet. Al especificar el argumento include_top=False, se carga una red que no incluye las capas de clasificación en la parte superior, lo que resulta ideal para la extracción de características.

# Create the base model from the pre-trained model MobileNet V2 IMG_SHAPE = IMG_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet')

Este extractor de características convierte cada imagen 160x160x3 en un bloque 5x5x1280 de características. Veamos lo que hace con un lote de imágenes de ejemplo:

image_batch, label_batch = next(iter(train_dataset)) feature_batch = base_model(image_batch) print(feature_batch.shape)

Extracción de características

En este paso, congelará la base convolucional creada en el paso anterior y la usará como extractor de características. Además, añadirá un clasificador sobre ella y entrenará al clasificador de nivel superior.

Congelar la base convolucional

Es importante congelar la base convolucional antes de compilar y entrenar el modelo. La congelación (al configurar layer.trainable = False) impide que las ponderaciones de una capa determinada se actualicen durante el entrenamiento. MobileNet V2 tiene muchas capas, por lo que si configura el indicador trainable de todo el modelo en False, se congelarán todas ellas.

base_model.trainable = False

Nota importante sobre las capas BatchNormalization

Muchos modelos contienen capas tf.keras.layers.BatchNormalization. Esta capa es un caso especial y deben tomarse precauciones en el contexto del ajuste fino, como se muestra más adelante en este tutorial.

Si configura layer.trainable = False, la capa BatchNormalization se ejecutará en modo de inferencia y no actualizará sus estadísticas de media y varianza.

Cuando descongele un modelo que contenga capas BatchNormalization para realizar un ajuste fino, deberá mantener las capas BatchNormalization en modo de inferencia pasando training = False al llamar al modelo base. De lo contrario, las actualizaciones aplicadas a las ponderaciones no entrenables destruirán lo que el modelo ha aprendido.

Para más detalles, consulte la Guía de aprendizaje por transferencia.

# Let's take a look at the base model architecture base_model.summary()

Añadir una cabecera de clasificación

Para generar predicciones a partir del bloque de características, realice un promedio sobre las ubicaciones espaciales 5x5, usando una capa tf.keras.layers.GlobalAveragePooling2D para convertir las características en un único vector de 1280 elementos por imagen.

global_average_layer = tf.keras.layers.GlobalAveragePooling2D() feature_batch_average = global_average_layer(feature_batch) print(feature_batch_average.shape)

Aplique una capa tf.keras.layers.Dense para convertir estas características en una única predicción por imagen. No necesita una función de activación aquí porque esta predicción se tratará como un logit, o un valor de predicción en bruto. Los números positivos predicen la clase 1, los números negativos predicen la clase 0.

prediction_layer = tf.keras.layers.Dense(1) prediction_batch = prediction_layer(feature_batch_average) print(prediction_batch.shape)

Construya un modelo encadenando las capas de aumento de datos, reescalado, base_model y extractor de características usando la API Functional de Keras. Como se mencionó anteriormente, use training=False ya que nuestro modelo contiene una capa BatchNormalization.

inputs = tf.keras.Input(shape=(160, 160, 3)) x = data_augmentation(inputs) x = preprocess_input(x) x = base_model(x, training=False) x = global_average_layer(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = prediction_layer(x) model = tf.keras.Model(inputs, outputs)
model.summary()

Los más de 8 millones de parámetros de MobileNet están congelados, pero hay 1.2 mil parámetros entrenables en la capa Dense. Éstos se dividen entre dos objetos tf.Variable, las ponderaciones y los sesgos.

len(model.trainable_variables)
tf.keras.utils.plot_model(model, show_shapes=True)

Compilar el modelo

Compile el modelo antes de entrenarlo. Como hay dos clases, use la pérdida tf.keras.losses.BinaryCrossentropy con from_logits=True ya que el modelo proporciona una salida lineal.

base_learning_rate = 0.0001 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate), loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), metrics=[tf.keras.metrics.BinaryAccuracy(threshold=0, name='accuracy')])

Entrenar el modelo

Tras un entrenamiento de 10 épocas, debería obtener una precisión del 96 % en el conjunto de validación.

initial_epochs = 10 loss0, accuracy0 = model.evaluate(validation_dataset)
print("initial loss: {:.2f}".format(loss0)) print("initial accuracy: {:.2f}".format(accuracy0))
history = model.fit(train_dataset, epochs=initial_epochs, validation_data=validation_dataset)

Curvas de aprendizaje

Analicemos las curvas de aprendizaje de la precisión/pérdida en el entrenamiento y la validación al usar el modelo base MobileNetV2 como extractor de características fijas.

acc = history.history['accuracy'] val_acc = history.history['val_accuracy'] loss = history.history['loss'] val_loss = history.history['val_loss'] plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.legend(loc='lower right') plt.ylabel('Accuracy') plt.ylim([min(plt.ylim()),1]) plt.title('Training and Validation Accuracy') plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.legend(loc='upper right') plt.ylabel('Cross Entropy') plt.ylim([0,1.0]) plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()

Nota: Si se pregunta por qué las métricas de validación son claramente mejores que las de entrenamiento, el factor principal se debe a que capas como tf.keras.layers.BatchNormalization y tf.keras.layers.Dropout afectan a la precisión durante el entrenamiento. Se desactivan cuando se calcula la pérdida de validación.

En menor medida, también se debe a que las métricas de entrenamiento informan de la media de una época, mientras que las métricas de validación se evalúan después de la época, por lo que las métricas de validación ven un modelo que ha entrenado ligeramente más tiempo.

Afinado

En el experimento de extracción de características, sólo se entrenaron unas pocas capas sobre un modelo base MobileNetV2. Las ponderaciones de la red preentrenada no se actualizaron durante el entrenamiento.

Una forma de aumentar aún más el rendimiento es entrenar (o "afinar") las ponderaciones de las capas superiores del modelo preentrenado junto con el entrenamiento del clasificador que ha añadido. El proceso de entrenamiento forzará el afinado de las ponderaciones a partir de mapas de características genéricas a características asociadas específicamente con el conjunto de datos.

Nota: Esto sólo debe intentarse después de haber entrenado el clasificador de nivel superior con el modelo preentrenado configurado como no entrenable. Si añade un clasificador inicializado aleatoriamente sobre un modelo preentrenado e intenta entrenar todas las capas conjuntamente, la magnitud de las actualizaciones del gradiente será demasiado grande (debido a las ponderaciones aleatorias del clasificador) y su modelo preentrenado olvidará lo que ha aprendido.

Además, debería intentar afinar un pequeño número de capas superiores en lugar de todo el modelo MobileNet. En la mayoría de las redes convolucionales, cuanto más alta es una capa, más especializada está. Las primeras capas aprenden características muy simples y genéricas que se generalizan a casi todos los tipos de imágenes. A medida que se asciende, las características son cada vez más específicas del conjunto de datos sobre el que se ha entrenado el modelo. La meta de la afinación es adaptar estas características especializadas para que funcionen con el nuevo conjunto de datos, en lugar de sobrescribir el aprendizaje genérico.

Descongelar las capas superiores del modelo

Todo lo que tiene que hacer es descongelar el base_model y configurar que las capas inferiores no se puedan entrenar. Luego, deberá recompilar el modelo (necesario para que estos cambios surtan efecto), y reanudar el entrenamiento.

base_model.trainable = True
# Let's take a look to see how many layers are in the base model print("Number of layers in the base model: ", len(base_model.layers)) # Fine-tune from this layer onwards fine_tune_at = 100 # Freeze all the layers before the `fine_tune_at` layer for layer in base_model.layers[:fine_tune_at]: layer.trainable = False

Compilar el modelo

Como está entrenando un modelo mucho más grande y quiere readaptar las ponderaciones preentrenadas, es importante usar una tasa de aprendizaje más baja en esta fase. De lo contrario, su modelo podría sobreajustarse muy rápidamente.

model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer = tf.keras.optimizers.RMSprop(learning_rate=base_learning_rate/10), metrics=[tf.keras.metrics.BinaryAccuracy(threshold=0, name='accuracy')])
model.summary()
len(model.trainable_variables)

Continuar entrenando el modelo

Si se ha entrenado antes hasta la convergencia, este paso mejorará su precisión en algunos puntos porcentuales.

fine_tune_epochs = 10 total_epochs = initial_epochs + fine_tune_epochs history_fine = model.fit(train_dataset, epochs=total_epochs, initial_epoch=history.epoch[-1], validation_data=validation_dataset)

Echemos un vistazo a las curvas de aprendizaje de la precisión/pérdida de entrenamiento y validación al ajustar las últimas capas del modelo base MobileNetV2 y entrenar el clasificador sobre él. La pérdida de validación es mucho mayor que la pérdida de entrenamiento, por lo que es posible que se produzca un sobreajuste.

También es posible que se produzca cierto sobreajuste, ya que el nuevo conjunto de entrenamiento es relativamente pequeño y similar a los conjuntos de datos originales de MobileNetV2.

Tras el afinamiento, el modelo alcanza casi un 98 % de precisión en el conjunto de validación.

acc += history_fine.history['accuracy'] val_acc += history_fine.history['val_accuracy'] loss += history_fine.history['loss'] val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8)) plt.subplot(2, 1, 1) plt.plot(acc, label='Training Accuracy') plt.plot(val_acc, label='Validation Accuracy') plt.ylim([0.8, 1]) plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy') plt.subplot(2, 1, 2) plt.plot(loss, label='Training Loss') plt.plot(val_loss, label='Validation Loss') plt.ylim([0, 1.0]) plt.plot([initial_epochs-1,initial_epochs-1], plt.ylim(), label='Start Fine Tuning') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.xlabel('epoch') plt.show()

Evaluación y predicción

Por último, puede verificar el rendimiento del modelo en los nuevos datos usando el conjunto de pruebas.

loss, accuracy = model.evaluate(test_dataset) print('Test accuracy :', accuracy)

Y ahora ya está todo configurado para usar este modelo para predecir si su mascota es un gato o un perro.

# Retrieve a batch of images from the test set image_batch, label_batch = test_dataset.as_numpy_iterator().next() predictions = model.predict_on_batch(image_batch).flatten() # Apply a sigmoid since our model returns logits predictions = tf.nn.sigmoid(predictions) predictions = tf.where(predictions < 0.5, 0, 1) print('Predictions:\n', predictions.numpy()) print('Labels:\n', label_batch) plt.figure(figsize=(10, 10)) for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(image_batch[i].astype("uint8")) plt.title(class_names[predictions[i]]) plt.axis("off")

Resumen

  • Usar un modelo preentrenado para la extracción de características: Cuando se trabaja con un conjunto de datos pequeño, es una práctica común aprovechar las características aprendidas por un modelo entrenado en un conjunto de datos más grande en el mismo dominio. Esto se hace instanciando el modelo preentrenado y añadiendo encima un clasificador totalmente conectado. El modelo preentrenado se "congela" y sólo se actualizan las ponderaciones del clasificador durante el entrenamiento. En este caso, la base convolucional extrajo todas las características asociadas a cada imagen y usted acaba de entrenar un clasificador que determina la clase de imagen dado ese conjunto de características extraídas.

  • {nbsp}Afinando un modelo preentrenado: Para mejorar aún más el rendimiento, quizá quiera adaptar las capas superiores de los modelos preentrenados al nuevo conjunto de datos afinando el ajuste. En este caso, ajustó las ponderaciones de forma que su modelo aprendiera características de alto nivel específicas del conjunto de datos. Esta técnica suele recomendarse cuando el conjunto de datos de entrenamiento es grande y muy similar al conjunto de datos original en el que se entrenó el modelo preentrenado.

Para saber más, visite la Guía de aprendizaje por transferencia.