Path: blob/master/site/es-419/guide/keras/transfer_learning.ipynb
25118 views
Copyright 2020 The TensorFlow Authors.
Aprendizaje por transferencia y ajuste
Preparación
Introducción
El aprendizaje por transferencia consiste en tomar características aprendidas en un problema y aprovecharlas en un nuevo problema similar. Por ejemplo, las características de un modelo que aprendió a identificar mapaches pueden ser útiles para poner en marcha un modelo destinado a identificar tanukis.
El aprendizaje por transferencia suele realizarse para tareas en las que el conjunto de datos es demasiado escaso para entrenar un modelo completo desde cero.
La forma más común de aplicar el aprendizaje por transferencia en el contexto del deep learning es el siguiente flujo de trabajo:
Tomar capas de un modelo previamente entrenado.
Congélelas para no destruir la información que contienen en futuras rondas de entrenamiento.
Agregue algunas capas nuevas, entrenables, sobre las capas congeladas. Aprenderán a convertir las características antiguas en predicciones sobre un nuevo conjunto de datos.
Entrene las nuevas capas en su conjunto de datos.
Un último paso opcional es el ajuste fino, que consiste en descongelar todo el modelo obtenido anteriormente (o parte de él) y volver a entrenarlo con los nuevos datos a un ritmo de aprendizaje muy bajo. De este modo se pueden conseguir mejoras significativas, adaptando de forma gradual las características preentrenadas a los nuevos datos.
En primer lugar, repasaremos en detalle la API trainable
de Keras, que subyace en la mayoría de los flujos de trabajo de aprendizaje por transferencia y ajuste fino.
A continuación, demostraremos el flujo de trabajo típico tomando un modelo preentrenado en el conjunto de datos ImageNet y reentrenándolo en el conjunto de datos de clasificación "gatos contra perros" de Kaggle.
Esto es una adaptación de Deep Learning con Python y la entrada de blog del 2016 "building powerful image classification models using very little data".
Congelar capas: cómo entender el atributo trainable
Las capas y los modelos tienen tres atributos de peso:
weights
es la lista de todas las variables de pesos de la capa.trainable_weights
es la lista de los que están destinados a ser actualizados (mediante descenso del gradiente) para minimizar la pérdida durante el entrenamiento.non_trainable_weights
es la lista de los que no están destinados a ser entrenados. Normalmente son actualizados por el modelo durante el siguiente paso.
Ejemplo: la capa Dense
tiene 2 pesos entrenables (kernel & bias)
En general, todos los pesos son entrenables. La única capa incorporada que tiene pesos no entrenables es la capa BatchNormalization
. Utiliza pesos no entrenables para realizar un seguimiento de la media y la varianza de sus entradas durante el entrenamiento. Para aprender a utilizar pesos no entrenables en sus propias capas personalizadas, consulte la guía para escribir nuevas capas desde cero.
Ejemplo: la capa BatchNormalization
tiene 2 pesos entrenables y 2 pesos no entrenables.
Las capas y modelos también cuentan con un atributo booleano trainable
. Su valor puede cambiarse. Establecer layer.trainable
a False
mueve todos los pesos de la capa de entrenable a no entrenable. Esto se denomina "congelar" la capa: el estado de una capa congelada no se actualizará durante el entrenamiento (ya sea cuando se entrene con fit()
o cuando se entrene con cualquier bucle personalizado que dependa de trainable_weights
para aplicar actualizaciones del gradiente).
Ejemplo: establecer trainable
en False
Cuando un peso entrenable se convierte en uno no entrenable, su valor deja de actualizarse durante el entrenamiento.
No confunda el atributo layer.trainable
con el argumento training
en layer.__call__()
(que controla si la capa debe ejecutar su siguiente paso en modo de inferencia o en modo de entrenamiento). Para obtener más información, consulte Keras FAQ.
Establecer de forma recursiva el atributo trainable
Si establece trainable = False
en un modelo o en cualquier capa que tenga subcapas, todas las capas hijas se convertirán también en no entrenables.
Ejemplo:
El flujo de trabajo típico del aprendizaje por transferencia
Esto nos lleva a cómo se puede implementar un flujo de trabajo típico de aprendizaje por transferencia en Keras:
Crear instancias de un modelo base y cargar en él los pesos preentrenados.
Congele todas las capas del modelo base estableciendo
trainable = False
.Cree un nuevo modelo sobre la salida de una (o varias) capas del modelo base.
Entrene su nuevo modelo en su nuevo conjunto de datos.
Tenga en cuenta que un flujo de trabajo alternativo, más ligero, también podría ser:
Crear instancias de un modelo base y cargar en él los pesos preentrenados.
Ejecute su nuevo conjunto de datos mediante él y registre la salida de una (o varias) capas del modelo base. Esto se denomina extracción de características.
Utilice ese resultado como datos de entrada para un nuevo modelo más pequeño.
Una ventaja clave de este segundo flujo de trabajo es que solo se ejecuta el modelo base una vez en los datos, en vez de una vez por cada época de entrenamiento. Así que es mucho más rápido y barato.
Sin embargo, el problema de este segundo flujo de trabajo es que no permite modificar dinámicamente los datos de entrada del nuevo modelo durante el entrenamiento, lo que es necesario, por ejemplo, al aumentar los datos. El aprendizaje por transferencia se utiliza normalmente para realizar tareas en las que el nuevo conjunto de datos tiene muy pocos datos para entrenar un modelo completo desde cero, y en estos casos el aumento de datos es muy importante. Por lo tanto, a continuación nos centraremos en el primer flujo de trabajo.
Este es el aspecto del primer flujo de trabajo en Keras:
En primer lugar, cree instancias de un modelo base con pesos preentrenados.
Después, congele el modelo base.
Cree un nuevo modelo encima.
Entrene el modelo con nuevos datos.
Ajuste fino
Una vez que su modelo haya convergido con los nuevos datos, puede intentar descongelar todo o parte del modelo base y volver a entrenar todo el modelo de principio a fin con una tasa de aprendizaje muy baja.
Se trata de un último paso opcional que puede proporcionarle posibles mejoras incrementales. También puede dar lugar a un sobreajuste rápido, así que téngalo en cuenta.
Es fundamental realizar este paso después de que el modelo con capas congeladas haya sido entrenado hasta la convergencia. Si mezcla capas entrenables inicializadas aleatoriamente con capas entrenables que contienen características pre-entrenadas, las capas inicializadas aleatoriamente causarán actualizaciones de gradiente muy grandes durante el entrenamiento, lo que destruirá sus características pre-entrenadas.
También es fundamental utilizar una tasa de aprendizaje muy baja en esta fase, porque se está entrenando un modelo mucho mayor que en la primera ronda de entrenamiento, en un conjunto de datos que suele ser muy pequeño. Como resultado, corre el riesgo de sobreajustarse muy rápidamente si aplica grandes actualizaciones de pesos. En este caso, lo único que se desea es readaptar los pesos preentrenados de forma gradual.
Así se aplica el ajuste fino de todo el modelo base:
Nota importante sobre compile()
y trainable
Llamar a compile()
en un modelo es "congelar" el comportamiento de ese modelo. Esto implica que los valores de los atributos trainable
en el momento en que se compila el modelo deben conservarse durante toda la vida del modelo, hasta que se vuelva a llamar a compile
. Por lo tanto, si cambia algún valor de trainable
, asegúrese de volver a llamar a compile()
en el modelo para que se tengan en cuenta los cambios.
Notas importantes sobre la capa BatchNormalization
Muchos modelos de imágenes contienen capas BatchNormalization
. Esa capa es un caso especial en todos los aspectos imaginables. Aquí hay algunas cosas que debe tener en cuenta.
BatchNormalization
contiene 2 pesos no entrenables que se actualizan durante el entrenamiento. Estas son las variables de seguimiento de la media y la varianza de las entradas.Cuando se establece
bn_layer.trainable = False
, la capaBatchNormalization
se ejecutará en modo de inferencia, y no se actualizarán sus estadísticas de media y varianza. Este no es el caso de otras capas en general, ya que la entrenabilidad del peso y los modos de inferencia/entrenamiento son dos conceptos ortogonales. Pero ambos están relacionados en el caso de la capaBatchNormalization
.Cuando se descongela un modelo que contiene capas
BatchNormalization
para realizar un ajuste fino, se deben mantener las capasBatchNormalization
en el modo de inferencia pasandotraining=False
al llamar al modelo base. De lo contrario, las actualizaciones aplicadas a los pesos no entrenables destruirán repentinamente lo que el modelo ha aprendido.
Verá este patrón en acción en el ejemplo de extremo a extremo al final de esta guía.
Aprendizaje por transferencia y ajuste con un bucle de entrenamiento personalizado
Si en lugar de fit()
, utiliza su propio bucle de entrenamiento de bajo nivel, el flujo de trabajo seguirá siendo esencialmente el mismo. Debe tener cuidado de sólo tener en cuenta la lista model.trainable_weights
al aplicar actualizaciones del gradiente:
Lo mismo ocurre con el ajuste fino.
Un ejemplo de extremo a extremo: puesta a punto de un modelo de clasificación de imágenes en un conjunto de datos de gatos frente a perros
Para consolidar estos conceptos, veamos un ejemplo concreto de aprendizaje por transferencia y ajuste de extremo a extremo. Cargaremos el modelo Xception, preentrenado en ImageNet, y lo utilizaremos en el conjunto de datos de clasificación "gatos frente a perros" de Kaggle.
Obtener los datos
En primer lugar, vamos a obtener el conjunto de datos de gatos frente a perros utilizando TFDS. Si tiene su propio conjunto de datos, probablemente querrá utilizar la utilidad tf.keras.preprocessing.image_dataset_from_directory
para generar objetos a partir de un conjunto de imágenes archivadas en carpetas específicas para las clases.
El aprendizaje por transferencia es más útil cuando se trabaja con conjuntos de datos muy pequeños. Para que nuestro conjunto de datos sea pequeño, utilizaremos el 40% de los datos de entrenamiento originales (25,000 imágenes) para el entrenamiento, el 10% para la validación y el 10% para las pruebas.
Estas son las 9 primeras imágenes del conjunto de datos de entrenamiento: como puede ver, todas tienen tamaños diferentes.
También podemos ver que la etiqueta 1 es "perro" y la etiqueta 0 es "gato".
Normalización de los datos
Nuestras imágenes sin procesar tienen diversos tamaños. Además, cada pixel consiste en 3 valores enteros entre 0 y 255 (valores de nivel RGB). Esto no es muy adecuado para alimentar una red neuronal. Tenemos que hacer 2 cosas:
Estandarizar a un tamaño de imagen fijo. Elegimos 150x150.
Normalizar los valores de los pixeles entre -1 y 1. Haremos esto usando una capa
Normalization
como parte del propio modelo.
En general, es una práctica recomendada desarrollar modelos que tomen datos sin procesar como entrada, en vez de modelos que tomen datos ya preprocesados. La razón es que, si su modelo espera datos preprocesados, cada vez que exporte su modelo para utilizarlo en otro lugar (en un navegador web, en una aplicación móvil), tendrá que volver a implementar exactamente el mismo proceso de preprocesamiento. Esto se complica rápidamente. Así que debemos hacer la menor cantidad posible de preprocesamiento antes de golpear el modelo.
En este caso, cambiaremos el tamaño de la imagen en la canalización de datos (porque una red neuronal profunda solo puede procesar lotes contiguos de datos), y haremos el escalado del valor de entrada como parte del modelo, cuando lo creemos.
Redimensionemos las imágenes a 150x150:
Además, procesemos los datos por lotes y utilicemos el almacenamiento en caché y la preextracción para optimizar la velocidad de carga.
Utilizar el aumento aleatorio de datos
Cuando no se dispone de un gran conjunto de datos de imágenes, es una práctica recomendada introducir artificialmente una diversidad de muestras aplicando transformaciones aleatorias pero realistas a las imágenes de entrenamiento, como volteos horizontales aleatorios o pequeñas rotaciones aleatorias. Esto ayuda a exponer el modelo a diferentes aspectos de los datos de entrenamiento y ralentiza el sobreajuste.
Visualicemos el aspecto de la primera imagen del primer lote después de varias transformaciones aleatorias:
Cómo construir un modelo
Ahora vamos a construir un modelo que siga el esquema que hemos explicado anteriormente.
Tenga en cuenta que:
Agregamos una capa
Rescaling
para escalar los valores de entrada (inicialmente en el rango[0, 255]
) al rango[-1, 1]
.Agregamos una capa
Dropout
antes de la capa de clasificación, para realizar la regularización.Nos aseguramos de pasar
training=False
al llamar al modelo base, para que se ejecute en modo de inferencia, de modo que las estadísticas batchnorm no se actualicen incluso después de descongelar el modelo base para el ajuste fino.
Entrene la capa superior
Realice una ronda de ajuste de todo el modelo
Por último, descongelemos el modelo base y entrenemos todo el modelo de principio a fin con una tasa de aprendizaje baja.
Es importante destacar que, aunque el modelo base se convierte en entrenable, todavía se está ejecutando en modo de inferencia ya que pasamos training=False
al llamarlo cuando construimos el modelo. Esto significa que las capas de normalización de lotes no actualizarán sus estadísticas de lotes. Si lo hicieran, causarían estragos en las representaciones aprendidas por el modelo como hasta ahora.
Después de 10 épocas, el ajuste fino nos proporcionará una buena mejora.