junio 4, 2021 10:00 am

Jesús

El aprendizaje por transferencia, así como TensorFlow Hub, son dos temas que hemos tratado con anterioridad aquí en DataSmarts, por lo que tiene sentido que combinemos ambas ideas en un mismo post, ¿no crees?

Pues, sí. TensorFlow Hub, ese lugar mágico donde habitan un sinnúmero de redes entrenadas, a nuestra disposición para aplicarlas a todo tipo de tareas, también nos facilita la creación de clasificadores de imágenes con muy poco esfuerzo, todo gracias a transfer learning.

En este post descubrirás:

  • Cómo descargar una modelo de TensorFlow Hub.
  • Cómo instanciar en Keras un modelo de TensorFlow Hub.
  • Cómo implementar y entrenar un nuevo clasificador de imágenes mediante transfer learning, usando TensorFlow Hub.

¿Estás listo? 

Comencemos.

¡ATENTO!

Si quieres entrar un poco más en contexto acerca de TensorFlow Hub, te recomiendo que leas este artículo en el que explico con más detalle de qué se trata.

Creación del Entorno de Desarrollo con Anaconda

¿Te parece si vemos la estructura del proyecto? Como puedes observar, el archivo más importante es datasmarts/transfer_learning.py, ya que contiene la totalidad de nuestro programa.

C:\USERS\JESUS\DATASMARTS\TFHUB-TRANSFER-LEARNING\DATASMARTS
    transfer_learning.py

Teniendo clara la estructura del código, lo siguiente que debemos hacer es crear el entorno de programación. Para ello, asegúrate de tener Anaconda instalado.

Abajo se encuentra la configuración del ambiente que replicaremos en breve:

Para crear el ambiente, solo ejecuta estas instrucciones:

conda env create -f env.yml

Y actívalo así:

conda activate tfhub-transfer-learning

Genial. Pasemos a la siguiente sección.

Transfer Learning con TFHub

Abre el archivo datasmarts/transfer_learning.py e inserta las siguientes líneas, las cuales importan las dependencias del script:

Definimos dos constantes: la primera contiene las dimensiones de que deben tener las imágenes para ser aceptadas por el modelo que descargaremos de TFHub más adelante, y la segunda simplemente define la ubicación en disco donde almacenaremos el conjunto de datos:

El conjunto de datos que usaremos es el mismo que ya exploramos en este artículo. La URL del mismo es:

Descargamos (si no lo hemos hecho antes) los datos en la ubicación definida por CACHE_URL:

Ahora, instanciamos un tf.data.Dataset a partir de los datos guardados en disco. Fíjate que train_dataset está configurado para darnos lotes de 64 imágenes a la vez, redimensionadas a 224x224 (es decir, IMAGE_SHAPE):

Determinamos las clases del conjunto de datos:

Normalizamos los datos para que los píxeles de cada imagen se ubiquen en el rango [0, 1]. Ten en cuenta que esto lo hacemos mediante la capa Rescaling, que bien podríamos incluir como parte de la arquitectura de la red neuronal, aunque esta vez optamos por aplicarla directamente a todo el dataset:

La siguiente instrucción se usa para indicarle a TensorFlow que queremos mantener un caché de las imágenes ya procesadas del conjunto de datos en memoria:

¡Llegamos a la parte buena! Descargamos una red pre-entrenada en ImageNet de TensorFlow Hub. Si te fijas en FEATURE_EXTRACTOR_URL, verás que se trata de una MobileNetV2. Sin embargo, como queremos reentrenar dicho modelo para clasificar flores, debemos traernos solo las capas convolucionales, no las densas, que son las que llevan a cabo la clasificación per se.

En otras palabras, usaremos MobileNetV2 como un extractor de features:

La descarga e instanciación del modelo en Keras corre por cuenta de tfhub.KerasLayer(). Si te fijas, pasamos el parámetro trainable=False, lo que significa que los pesos o parámetros de esta instancia de MobileNetV2 quedarán congelados.

Nuestra red basada en transfer learning no es más que una capa densa, con 5 neuronas (correspondientes a los 5 tipos de flores a clasificar) conectada al feature extractor que nos bajamos de TFHub, mediante un modelo secuencia (tf.keras.Sequential()):

Compilamos el modelo, que optimizaremos con Adam. Debido a que la última capa de la red no se activa, tenemos que pasar from_logits=True a la función de pérdida para que desempeñe la función de una activación Softmax:

Para recopilar estadísticas al final de cada lote procesado (en vez de al final de cada iteración o epoch), crearemos un callback muy simple:

Este callback lo que hace es almacenar la pérdida (loss) y el accuracy posteriormente al procesamiento de un lote de imágenes. 

Entrenamos la red por tan solo 5 iteraciones, pasándole una instancia del callback CollectBatchStats():

Dibujamos y mostramos las curvas de pérdida y accuracy, recopiladas al final de cada lote:

Finalmente, una vez la red se ha entrenado, realizaremos predicciones sobre 30 imágenes del conjunto de entrenamiento, y crearemos una grilla con estas imágenes y su correspondiente predicción:

Para ejecutar el programa, corre esto en la raíz del proyecto:

python datasmarts/transfer_learning.py

Puede que se demore un poco la primera vez, ya que tiene que descargar tanto los datos, como el modelo de TFHub. Las ejecuciones posteriores debieran ser más veloces.

En la terminal veremos la arquitectura de la red:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
keras_layer (KerasLayer)     (None, 1280)              2257984
_________________________________________________________________
dense (Dense)                (None, 5)                 6405
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________
None

Lo interesante del caso es que el modelo que nos trajimos de TFHub actúa como una caja negra, ya que no tenemos detalle de su arquitectura en lo absoluto. En cambio, sólo lo vemos reflejado como keras_layer, de tipo KerasLayer, compuesto de 2.257.984 de parámetros. Curioso.

Después, veremos la siguiente curva, correspondiente a la función de pérdida a lo largo de más de 200 lotes de imágenes procesados por la red:

Se ve claramente la tendencia descendente de la curva, una señal saludable de que la red está aprendiendo. 

El siguiente gráfico en aparecer es el de la curva del accuracy:

Si ignoramos los picos, vemos que la curva presenta una tendencia ascendente, que se estabiliza alrededor del 90%, lo cual no está nada mal si tenemos en cuenta que la mayor parte de nuestra red fue entrenada en ImageNet, no en este conjunto de datos en particular.

Por último, tenemos las predicciones:

Sinceramente, se ve bastante bien, en especial si tomamos en consideración la gran variabilidad en condiciones de iluminación, fondo, perspectiva, oclusión y elementos foráneos aparecen en estas fotos.

Resumen

Hoy aprendimos a crear un modelo mediante transfer learning, basado en una red ya entrenada descargada de TensorFlow Hub.

El proceso es muy simple:

  1. 1
    Buscamos la URL del modelo que queremos extender.
  2. 2
    Lo descargamos e instanciamos usando tfhub.KerasLayer().
  3. 3
    Usamos el API tf.keras.Sequential() para conectar el modelo descargado con las nuevas capas que deseemos añadir.

Voilà.

También creamos un callback personalizado para loggear las métricas de entrenamiento al final de cada lote de imágenes, lo cual nos provee de mayor detalle sobre el aprendizaje de la red durante el entrenamiento, como pudimos ver en las curvas de la función de pérdida y del accuracy.

Por último, inspeccionamos visualmente las predicciones hechas por la red sobre una muestra del conjunto de entrenamiento. Los resultados son bastante satisfactorios, en especial porque se trata de un conjunto de datos retador, ya que las imágenes presentan mucha variabilidad en sus condiciones (oclusión, iluminación, perspectiva, entre otras).

Hay muchos otros modelos en TensorFlow Hub que podrías explorar. ¿Por qué no lo intentas, a ver si mejoras mis resultados? Descarga el código acá para que lo uses de base:

¡Hasta luego!

Sobre el Autor

Jesús Martínez es el creador de DataSmarts, un lugar para los apasionados por computer vision y machine learning. Cuando no se encuentra bloggeando, jugando con algún algoritmo o trabajando en un proyecto (muy) cool, disfruta escuchar a The Beatles, leer o viajar por carretera.