Aplicación en un conjunto de datos de imágenes en color#
import torchvision
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
Presentación del conjunto de datos CIFAR-10#
En esta aplicación, utilizaremos el conjunto de datos CIFAR-10. Contiene 60 000 imágenes en color de tamaño \(3 \times 32 \times 32\), distribuidas en 10 clases: avión, automóvil, pájaro, gato, ciervo, perro, rana, caballo, barco y camión. Cargaremos el conjunto de datos usando torchvision:
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# Transformation des données, normalisation et transformation en tensor pytorch
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# Téléchargement et chargement du dataset
dataset = torchvision.datasets.CIFAR10(root='./../data', train=True,download=True, transform=transform)
testdataset = torchvision.datasets.CIFAR10(root='./../data', train=False,download=True, transform=transform)
print("taille d'une image : ",dataset[0][0].shape)
#Création des dataloaders pour le train, validation et test
train_dataset, val_dataset=torch.utils.data.random_split(dataset, [0.8,0.2])
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16,shuffle=True, num_workers=2)
val_loader= torch.utils.data.DataLoader(val_dataset, batch_size=16,shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(testdataset, batch_size=16,shuffle=False, num_workers=2)
Files already downloaded and verified
Files already downloaded and verified
taille d'une image : torch.Size([3, 32, 32])
Podemos visualizar las imágenes y sus clases:
def imshow_with_labels(img, labels):
img = img / 2 + 0.5 # Dénormaliser
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.xticks([]) # Supprimer les graduations sur l'axe des x
plt.yticks([]) # Supprimer les graduations sur l'axe des y
for i in range(len(labels)):
plt.text(i * 35, -2, classes[labels[i]], color='black', fontsize=8, ha='left')
# Récupération d'un batch d'images
images, labels = next(iter(train_loader))
# Affichage des images avec leurs classes
imshow_with_labels(torchvision.utils.make_grid(images[:8]), labels[:8])
plt.show()
Diseño de una red convolucional para este problema#
En el curso sobre convoluciones, vimos el parámetro stride, que corresponde al paso de la convolución. Con un stride de 2, la resolución de la imagen se reduce a la mitad al finalizar la operación (siempre que el padding compense el tamaño del filtro). Por lo tanto, la operación de pooling puede reemplazarse aumentando el stride.
La salida de una capa convolucional con stride 1 seguida de una capa de pooling tiene la misma dimensión que la de una capa convolucional con stride 2 sin pooling.
En esta aplicación, reemplazaremos las capas de pooling aumentando el stride de las convoluciones. Para más información, consulta este artículo del blog o este artículo académico.
class cnn(nn.Module):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.conv1=nn.Conv2d(3,8,kernel_size=3,stride=2,padding=1)
self.conv2=nn.Conv2d(8,16,kernel_size=3,stride=2,padding=1)
self.conv3=nn.Conv2d(16,32,kernel_size=3,stride=2,padding=1)
self.fc=nn.Linear(4*4*32,10)
def forward(self,x):
x=F.relu(self.conv1(x))
x=F.relu(self.conv2(x))
x=F.relu(self.conv3(x))
x=x.view(-1,4*4*32)
output=self.fc(x)
return output
model = cnn() # Couches d'entrée de taille 2, deux couches cachées de 16 neurones et un neurone de sortie
print(model)
print("Nombre de paramètres", sum(p.numel() for p in model.parameters()))
cnn(
(conv1): Conv2d(3, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(conv3): Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(fc): Linear(in_features=512, out_features=10, bias=True)
)
Nombre de paramètres 11162
Entrenamiento del modelo#
criterion = nn.CrossEntropyLoss()
epochs=5
learning_rate=0.001
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)
for i in range(epochs):
loss_train=0
for images, labels in train_loader:
preds=model(images)
loss=criterion(preds,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_train+=loss
if i % 1 == 0:
print(f"step {i} train loss {loss_train/len(train_loader)}")
loss_val=0
for images, labels in val_loader:
with torch.no_grad(): # permet de ne pas calculer les gradients
preds=model(images)
loss=criterion(preds,labels)
loss_val+=loss
if i % 1 == 0:
print(f"step {i} val loss {loss_val/len(val_loader)}")
step 0 train loss 1.5988761186599731
step 0 val loss 1.4532517194747925
step 1 train loss 1.3778905868530273
step 1 val loss 1.3579093217849731
step 2 train loss 1.2898519039154053
step 2 val loss 1.2919617891311646
step 3 train loss 1.2295998334884644
step 3 val loss 1.256637692451477
step 4 train loss 1.186734914779663
step 4 val loss 1.240902304649353
Ahora podemos verificar la precisión en los datos de prueba:
correct = 0
total = 0
for images,labels in test_loader:
with torch.no_grad():
preds=model(images)
_, predicted = torch.max(preds.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_acc = 100 * correct / total
print("Précision du modèle en phase de test : ",test_acc)
Précision du modèle en phase de test : 55.92
Uso de una GPU#
Si tienes una GPU en tu computadora o acceso a un servicio en la nube con GPU, puedes acelerar el entrenamiento y la inferencia de tu modelo. Aquí te mostramos cómo hacerlo con PyTorch:
model = cnn().to('cuda') # Ajouter le .to('cuda') pour charger le modèle sur GPU
criterion = nn.CrossEntropyLoss()
epochs=5
learning_rate=0.001
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)
for i in range(epochs):
loss_train=0
for images, labels in train_loader:
images=images.to('cuda') # Ajouter le .to('cuda') pour charger les images sur GPU
labels=labels.to('cuda') # Ajouter le .to('cuda') pour charger les labels sur GPU
preds=model(images)
loss=criterion(preds,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_train+=loss
if i % 1 == 0:
print(f"step {i} train loss {loss_train/len(train_loader)}")
loss_val=0
for images, labels in val_loader:
with torch.no_grad():
images=images.to('cuda')
labels=labels.to('cuda')
preds=model(images)
loss=criterion(preds,labels)
loss_val+=loss
if i % 1 == 0:
print(f"step {i} val loss {loss_val/len(val_loader)}")
/home/aquilae/anaconda3/envs/dev/lib/python3.11/site-packages/torch/autograd/graph.py:744: UserWarning: Plan failed with a cudnnException: CUDNN_BACKEND_EXECUTION_PLAN_DESCRIPTOR: cudnnFinalize Descriptor Failed cudnn_status: CUDNN_STATUS_NOT_SUPPORTED (Triggered internally at ../aten/src/ATen/native/cudnn/Conv_v8.cpp:919.)
return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
step 0 train loss 1.6380540132522583
step 0 val loss 1.4465171098709106
step 1 train loss 1.369913101196289
step 1 val loss 1.3735681772232056
step 2 train loss 1.2817057371139526
step 2 val loss 1.2942956686019897
step 3 train loss 1.2238909006118774
step 3 val loss 1.2601954936981201
step 4 train loss 1.186323881149292
step 4 val loss 1.2583365440368652
correct = 0
total = 0
for images,labels in test_loader:
images=images.to('cuda')
labels=labels.to('cuda')
with torch.no_grad():
preds=model(images)
_, predicted = torch.max(preds.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
test_acc = 100 * correct / total
print("Précision du modèle en phase de test : ",test_acc)
Précision du modèle en phase de test : 55.76
Según tu GPU, probablemente hayas notado un aumento en la velocidad de entrenamiento e inferencia del modelo. Esta diferencia es aún más notable con GPUs potentes y modelos profundos y paralelizables.
Ejercicio práctico#
Para practicar, intenta mejorar el rendimiento del modelo en el conjunto de datos CIFAR-10. Algunas opciones son:
Aumentar el número de capas.
Modificar el número de filtros por capa.
Agregar dropout.
Utilizar batch normalization.
Incrementar el número de epochs.
Ajustar la tasa de aprendizaje (learning rate).
Intenta alcanzar al menos un 70% de precisión.