reconocimiento de imagen con pytorch

En esta publicación, revisamos un ejemplo en pytorch que utiliza un modelo pre-entrenado para la clasificación de imágenes

reconocimiento de imagen con pytorch

idea

La idea es entrenar un modelo para reconocer a un perro en dos posiciones: sentado y parado; pero este mismo concepto puede escalarse a más categorías (más posiciones)

información para el entrenamiento

Para esto, he recabado algunas imágenes de mi perrita Renata <3, todas las imágenes cuentan con al misma resolución de 4032 x 3024 pixeles
La estructura que vamos a necesitar en nuestro directorio es:

images
    test
    	dog_sitting
        dog_standing
    train
    	dog_sitting
        dog_standing

Como puedes observar, dentro del directorio, tenemos dos categorías principales que corresponden a la información que vamos a utilizar para el entrenamiento (train) y validación (test) de nuestro modelo. Después, un nivel más adentro, contamos con carpetas que describen a la categoría a la cuál pertenecen las imágenes que contienen, esto es:

  • dog_sitting - contiene imágenes de perros sentados
  • dog_standing - contiene imágenes de perros parados

pytorch

Disclaimer: No soy un experto en pytorch, así que mucha información que veras en este post tendrán referencias externas a las publicaciones originales donde puedes seguir el pensamiento detrás de la selección de los componentes

modelo

Utilizaremos un modelo que se encuentra dentro de pytorch llamado resnet50, este modelo ya se encuentra pre-entrenado y será nuestra base para enseñarle a reconocer nuestras categorías (dog_sitting, dog_standing)

# si tienes un hardware con cuda cores,
# puedes cambiar este parametros por "cuda"
device = "cpu"

# cargar el modelo pre-entrenado
# y configurarlo para utilizar el hardware seleccionado previamente
model = models.resnet50(pretrained=True).to(device)

for param in model.parameters():
  param.requires_grad = False

# capa personalizada para adaptarse a nuestro caso particular
model.fc = nn.Sequential(
  nn.Linear(2048, 128),
  nn.ReLU(inplace=True),
  nn.Linear(128, 2)
).to(device)

carga de datos de entrenamiento

Para la carga de los datos de train y test, primero debemos definir un transformador, el cual nos ayudara a estandarizar el tamaño de las imágenes; y como paso adicional para el entrenamiento, las imágenes se giran de forma aleatoria para asegurarnos que el training no tenga un sesgo; por ejemplo, podríamos tener un sesgo por el ángulo de la cámara

normalizadores

normalizer = transforms.Normalize(
    [0.5, 0.5, 0.5],
    [0.5, 0.5, 0.5]
  )

  transformers = {
    'train': transforms.Compose([
      transforms.Resize((224, 224)),
      transforms.RandomHorizontalFlip(),
      transforms.ToTensor(),
      normalizer
    ]),
    'test': transforms.Compose([
      transforms.Resize((224, 224)),
      transforms.ToTensor(),
      normalizer
    ])
  }

Nota: los diccionarios que estaremos utilizando van a contar siempre con las fases train y test, ya que cada fase cuenta con un transformador distinto

data set

Ahora debemos indicar dónde se encuentran los directorios para cada fase y proveer el transformador que les corresponden

data_set = {
  'train': datasets.ImageFolder('./images/train', transformers['train']),
  'test': datasets.ImageFolder('./images/test', transformers['test'])
}

También, hay que configurar los componentes que realizarán la lectura de los datos

data_loaders = {
  'train': torch.utils.data.DataLoader(
    data_set['train'],
    batch_size=32,
    shuffle=True,
    num_workers=2
  ),
  'test': torch.utils.data.DataLoader(
    data_set['test'],
    batch_size=32,
    shuffle=False,
    num_workers=2
  )
}

recapitulando

Hasta ahora hemos realizado lo siguiente:

  • cargar y ajustar el modelo que vamos a utilizar para el entrenamiento
  • configurar cómo se tratará a la información
  • indicar donde se encuentran nuestros datos
  • configurar los componentes de lectura

entrenamiento

El entrenamiento será definido de la siguiente manera:

  • definir un criterio para el cálculo del error (loss) y optimización para el aprendizaje
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.fc.parameters())
  • ejecutar las fases de train y test distintas veces
epochs = 10

for epoch in range(epochs):
    for phase in ['train', 'test']:
      model_train(phase)
  • Tratar de reducir el error y aumentar el puntaje acertado de nuestro modelo
def model_train(phase):
  running_loss = 0.0
  running_corrects = 0.0

  for x, y in data_loaders[phase]:
    # ingresar X y Y en el hardware
    inputs = x.to(device)
    labels = y.to(device)

    # realizar la prediccion
    outputs = model(inputs)
    # medir el error
    loss = criterion(outputs, labels)

    # para fase de train, es necesario
    # efectuar la parte de aprendizaje
    if phase == 'train':
      optimizer.zero_grad()
      loss.backward()
      optimizer.step()
    
    _, prediction = torch.max(outputs, 1)
    # medir como va mejorando/empeorando el modelo en esta fase
    running_loss += loss.item() * inputs.size(0)
    running_corrects += torch.sum(prediction == labels.data)
  
  # medir como va mejorando/empeorando el model en la iteracion (epoch)
  epoch_loss = running_loss / len(data_set[phase])
  eopch_corrects = running_corrects.double() / len(data_set[phase])
  • guardar el modelo
torch.save(trained_model.state_dict(), './models/dog-trainer.h5')

probando el modelo

Ya que contamos con nuestro modelo entrenado, haremos una prueba con un par de fotos que no se encuentra en el conjunto de datos iniciales:

sitting.jpg
standing.jpg

Resultado:

Definitivamente podemos mejorar las predicciones para el caso del perro parado (standing), pero es algo que podría arreglarse fácilmente agregando más datos de prueba. Por ahora, para este proyecto personal, es suficiente

conclusiones

Pytorch es una librería que nos ayuda a realizar un entrenamiento de modelos de manera simple y rápida; ésta también provee un catálogo de modelos pre-entrenados que pueden ser adaptados a nuevos casos de uso, quitando así, la necesidad de obtener una cantidad enorme de datos para tener una precisión adecuada, y por ende, se reduce el costo de cómputo necesario para el entrenamiento

Es agradable ver lo mucho que encapsulan este tipo de librerías, tanto así que no es necesario contar con un conocimiento profundo de los temas para realizar proyectos personales o comenzar a familiarizarnos con los conceptos y componentes que viven dentro del mundo de ML

referencias

Transfer learning with ResNet-50 in PyTorch | Kaggle

ResNet | PyTorch

Repositorio: https://github.com/carlosJoseloMtz/pytorch-clasificador-de-imagenes

Carlos Jose Martinez Arenas

Carlos Jose Martinez Arenas

Enamorado de la tecnología, con interés en machine learning, código limpio, buenas prácticas y arquitectura de sistemas; música y cine; naturaleza, espacios abiertos y los perritos