TICS-579-Deep Learning

Clase 8: Transfer Learning y Data Augmentation

Alfonso Tobar-Arancibia

Arquitecturas Famosas

LeNet-5 (LeCun et al., 1998)

Probablemente la primera arquitectura famosa en poder realizar tareas importantes de reconocimiento de imagen. Diseñada especialmente para reconocimiento de dígitos, introduce los bloques de convolución más pooling para luego conectarse con FFN.

Adaptive Pooling

La mayoría de arquitecturas más modernas utiliza una capa llamada Adaptive Pooling antes del proceso de Flatten. El Adaptive Pooling es una especie de Pooling inverso, donde uno define el tamaño del output, y automáticamente se calcula el Kernel, Stride, Padding, etc. necesario para obtener ese tamaño.

Eso garantiza que cualquier tamaño de imagen puede pasar por la red sin romper las dimensiones necesarias para la transición al MLP.

AlexNext (Krizhevsky, Sutskever y Hinton, 2012)

Ganó el concurso Imagenet (ILSVRC) en 2012 por un largo margen (algo impensado para ese tiempo). Introdujo los conceptos de ReLU, Dropout y Aceleración por GPU. Esta arquitectura está disponible en torchvision.

import torchvision
torchvision.models.alexnet(weights = "IMAGENET1K_V1")

La arquitectura de Torchvision está inspirada en una versión alternativa de Alexnet. Esto probablemente no será corregido ya que no es una arquitectura que se utilice comunmente en la actualidad.

VGGNet (Simonyan, Zisserman, 2014)

Presentaron las primeras redes relativamente profundas con Kernels pequeños de \(3 \times 3\). Su propuesta incluye Redes de hasta 19 capas.

import torchvision
torchvision.models.vgg16(weights = "IMAGENET1K_V1")
## Versión con Batchnorm
torchvision.models.vgg16_bn(weights = "IMAGENET1K_V1")

torchvision incluye las arquitecturas de 11, 13, 16 y 19 capas, además de variantes que incluyen Batchnorm (que en eltiempo del paper no existían aún).

GoogleNet/Inception (Szegedy et al., 2014)

Introduce las “Pointwise Convolutions” (Convoluciones de 1x1) que permiten reducir la complejidad de canales (mediante una combinación lineal) manteniendo las dimensiones de la imagen. Además introduce los Inception Modules, que combinan resultados de Kernels de distinto tamaño. Fue la Arquitectura ganadora de ILSVRC 2014.

import torchvision
torchvision.models.googlenet(weights = "IMAGENET1K_V1")

1x1 Convolutions

Resnet (He et al., 2015)

Introduce las conexiones residuales, lo cual permite evitar el problema del vanishing gradient para redes muy profundas. Es la Arquitectura ganadora de ILSVRC 2015.

Esta arquitectura se puede encontrar tanto en torchvision como timm. Recomiendo timm, ya que hay muchas más variantes, mejor mantención y procesos de entrenamiento actualizados.

import timm
model = timm.create_model("resnet50", pretrained = True)

## Listar todas las versiones de Resnet disponibles
timm.list_models("resnet*")


Conexiones Residuales

EfficientNet (Tan, Le, 2019)

Introducen el concepto de Compound Scaling que permite cambiar la escala de profundidad (número de capas en la red), ancho (número de canales en cada capa) y resolución (dimensiones de la imagen) para poder mejorar la performance. Permite crear resultados al nivel del estado del arte con muchísimos menos parámetros.

import timm
model = timm.create_model("efficientnet_b0", pretrained = True)

## Listar todas las versiones de Resnet disponibles
timm.list_models("efficientnet*")

Pre-training

Imagenet
Corresponde a un dataset de cerca de 14M de imágenes con que fueron anotados a mano. Este dataset se utilizó para la competencia de ImageNet Large Scale Visual Recognition Challenge (ILSVRC) desde el año 2010 al 2017, el cuál generó innumerables avances en el estado del arte. Normalmente las imágenes tienen rangos entre \(4288 \times 2848\) hasta \(75 \times 56\). Las imágenes se encuentran normalizadas restando medias por canal de \([0.485,0.456,0.406]\) y divididas por SD de \([0.229,0.224,0.225]\).
  • Las dos variantes más conocidas son el ImageNet-1K que tiene 1.281.167, 50.000 y 100.000 imágenes para train, validation y test set con 1000 categorías y el ImageNet-21K que tiene 14.197.122 imágenes con 21.841 clases.

Debido a la importancia y complejidad de este dataset es que la mayoría de los backbones han sido pre-entrenados con este él. Por lo que las distintas arquitecturas “pueden ver” gracias a este dataset.

Debido a que muchas arquitecturas pueden/saben ver en un dataset tan complejo como Imagenet. ¿Sería posible utilizar ese conocimiento en otro dataset?

Entering Transfer Learning

Transfer Learning

Dataset Público/alta complejidad

Normalmente se utilizan datos públicos y de alta complejidad y se utiliza para pre-entrenar una arquitectura.

Pre-entrenamiento

Se entrena una arquitectura para una tarea en específico con los detalles del dataset a utilizar.

Fine-Tuning

Se carga la arquitectura pre-entrenada, con los pesos obtenidos en el pre-entrenamiento y se ajusta el prediction head para la nueva tarea y se vuelve a entrenar el modelo.

Freezing Layers

Se refiere a congelar los parámetros del backbone pre-entrenado, es decir, estos no se actualizan. Este paso es opcional, y en ocasiones puede funcionar de mejor manera que un Full-Fine-Tuning

Image Preprocessing y Data Augmentation

En general el proceso de Preprocesamiento de Imágenes es bastante más engorroso que el de datos tabulares. Afortunadamente Pytorch tiene algunos utilities que permiten hacer el proceso más sencillo:

ImageFolder
Permite cargar imágenes de un Path en específico. Dentro de esa carpeta ImageFolder considerará cada carpeta como una clase y los elementos (imágenes) dentro de dicha clase como instancia de la clase en cuestión.
from torchvision.dataset import ImageFolder

train_data = ImageFolder("path/to/train/images", transform = None)
validation_data = ImageFolder("path/to/validation/images", transform = None)
test_data = ImageFolder("path/to/test/images", transform = None)

Además ImageFolder posee un parámetro llamado transform en el cuál se pueden ingresar transformaciones a los datos para realizar procesos de Data Augmentation.

Ojo

Image Folder entrega los datos como una Imagen PIL. Por lo tanto, es necesario aplicar procesamientos que permitan su transformación en Tensor.

Data Augmentation

Corresponde a un proceso de generación de datos sintéticos. Este proceso se puede utilizar para:

  • Permite la generación de datos adicionales debido a escasez por costo o disponibilidad de ellos. Ejemplo: Datos médicos.
  • Genera variedad de datos, que entrega al modelo un mayor poder de generalización en datos no vistos.
  • Al introducir mayor variabilidad en los datos entrega una mayor robustez ante el overfitting (Regularización).
  • Simular condiciones adversas para el modelo en la cuál se quiera generar robustez.
    • Ej: Se tiene un modelo de reconocimiento de vehículos, pero que tiene que funcionar en condiciones de niebla.

Albumentations

Existen diversas librerías que permiten generar Aumento de Datos. La librerías más famosas son Albumentations y Kornia. Albumentations, permite transformaciones extremadamente eficientes en CPU, mientras que Kornia hace lo mismo pero en GPU. Debido a las limitaciones de GPU que contamos, utilizaremos Albumentations, de manera tal de balancear procesamiento tanto en CPU como en GPU.

Normalmente este tipo de transformaciones entrega mejores resultados cuando se generan de manera aleatoria y on-the-fly. Es decir, se genera el aumento de datos en la carga de datos durante el entrenamiento.

Transformaciones Básicas

import albumentations as A
from albumentations.pytorch.transforms import ToTensorV2

Albumentations espera que la imagen venga como Numpy Array. Además es una librería bastante quisquillosa, por lo que toma un rato acostumbrarse. Pero su eficiencia y utilidad hace que valga la pena.

A.Compose()
Permite generar Pipelines de Transformación. Es decir, irá aplicando transformaciones una a una.
A.ToFloat()
Transforma los datos en tipo Float. Esto a veces es necesario cuando hay incompatibilidad de data types en ciertos módulos.
ToTensorV2()
Transforma a Tensor de Pytorch. Existe una versión ToTensor() pero está deprecada y no debería usarse.
A.Normalize()
Permite normalizar imágenes según su proceso de pre-entrenamiento. Normalmente estos provienen de pre-entrenamiento en Imagenet por lo que se debe normalizar con \(mean=[0.485,0.456,0.406]\) y \(SD=[0.229,0.224,0.225]\).
A.Resize()
Se utiliza para estandarizar el tamaño de las imágenes. Imágenes más grandes permiten mejores resultados pero son computacionalmente más costosas.

Transformaciones Probabilísticas

Como su nombre lo indica, la transformación se aplicará con una cierta probabilidad, lo que permitirá que cada epoch haya mayor variabilidad.

A.CenterCrop/A.RandomCrop
Genera un Crop de la imagen o al centro o Random. Esto logrará que los elementos de la imagen cambien de posición.
A.VerticalFlip
Genera Flip Vertical.
A.HorizontalFlip
Genera Flip Horizontal.
A.Rotate
Genera rotaciones aleatorias entre un ángulo mínimo y máximo.

Existen un sinnúmero de transformaciones que se pueden aplicar. La lista completa se puede encontrar acá. Y existen transformaciones que incluso permiten simular niebla, lluvia, nieve, sepia, Zoom, y variados otros efectos.

Aplicar estas transformaciones es de extremo cuidado ya que para tareas más complejas como Semantic Segmentation, Object Detection, Keypoint Detection, se debe aplicar dichas transformaciones también a las etiquetas.

Class Dismissed