Clasificación de imágenes con Deep Learning

En este artículo, utilizaremos PyTorch para probar diferentes modelos para lograr la clasificación de imágenes con Deep Learning del conjunto de datos COIL-100 y comparar su rendimiento.
COIL-100 Dataset
La Biblioteca de Imágenes de la Universidad de Columbia (COIL-100) es un conjunto de datos de imágenes en color de 100 objetos. Los objetos se colocaron en una plataforma giratoria motorizada sobre un fondo negro. La plataforma giratoria se hizo girar 360 grados para variar la postura del objeto con respecto a una cámara en color fija. Se tomaron imágenes de los objetos a intervalos de 5 grados. Esto corresponde a 72 poses por objeto (7.200 imágenes en total). Las imágenes se normalizaron por tamaño.
Preparación de los datos para la clasificación de imágenes con Deep Learning
En primer lugar, importamos las bibliotecas que vamos a utilizar para este proyecto.
In [1]:
import torch from torch.utils.data import Dataset, DataLoader, random_split from PIL import Image import pandas as pd from torchvision.transforms import ToTensor import matplotlib.pyplot as plt import torch.nn as nn import torch.nn.functional as F from torchvision.utils import make_grid
Aquí creamos dos variables, una para la ruta de la carpeta de imágenes y otra para la ruta de un fichero .csv que he creado con información sobre el nombre de los ficheros y su clasificación entre las 100 clases existentes.
In [2]:
path = '../input/coil100/coil-100/coil-100/' csv_path = '../input/namescsv/names.csv'
In [3]:
!head "{csv_path}"
Ahora, creamos un conjunto de datos personalizado extendiendo la clase Dataset de PyTorch. Esto nos permitirá asociar la etiqueta correcta a cada imagen utilizando los datos de nuestro archivo .csv.
In [4]:
class CoilDataset(Dataset): def __init__(self, csv_file, root_dir, transform=None): self.df = pd.read_csv(csv_file) self.transform = transform self.root_dir = root_dir def __len__(self): return len(self.df) def __getitem__(self, idx): row = self.df.loc[idx] img_id, img_label = row['image'], row['label'] img_fname = self.root_dir + "/" + str(img_id) img = Image.open(img_fname) if self.transform: img = self.transform(img) return img, img_label
In [5]:
dataset = CoilDataset(csv_path, path, transform=ToTensor())
Podemos ver que convertimos cada imagen en un tensor de 3 dimensiones (3, 128, 128). La primera dimensión corresponde al número de canales. Como estamos utilizando imágenes en color, habrá 3 canales (rojo, verde, azul). La segunda y tercera dimensión son para el tamaño de la imagen, en este caso, 128px por 128px.
In [6]:
img, label = dataset[0] print(img.shape, label) img
torch.Size([3, 128, 128]) 20
Out[6]:
tensor([[[0.2078, 0.2078, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], ..., [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980]], [[0.2000, 0.2000, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], ..., [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980]], [[0.1255, 0.0392, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], ..., [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980], [0.0980, 0.0980, 0.0980, ..., 0.0980, 0.0980, 0.0980]]])
Ahora definiremos los hiperparámetros de nuestros modelos.
In [7]:
# Hiperparámetros
batch_size = 128
learning_rate = 0.001
# Otras constantes
in_channels = 3
input_size = in_channels * 128 * 128
num_classes = 101
Conjuntos de datos de entrenamiento y validación
Vamos a dividir el conjunto de datos en 3 partes:
- Conjunto de entrenamiento: se utiliza para entrenar el modelo (calcular la pérdida y ajustar los pesos del modelo mediante el descenso de gradiente).
- Conjunto de validación: se utiliza para evaluar el modelo durante el entrenamiento, ajustar los hiperparámetros (velocidad de aprendizaje, etc.) y elegir la mejor versión del modelo.
- Conjunto de prueba: se utiliza para comparar diferentes modelos o diferentes tipos de enfoques de modelado e informar de la precisión final del modelo.
In [8]:
random_seed = 11 torch.manual_seed(random_seed);
In [9]:
val_size = 720 test_size = 720 train_size = len(dataset) - val_size - test_size train_ds, val_ds, test_ds = random_split(dataset, [train_size, val_size, test_size]) len(train_ds), len(val_ds), len(test_ds)
Out[9]:
(5760, 720, 720)
Ahora podemos crear cargadores de datos para el entrenamiento y la validación, para cargar los datos por lotes.
In [10]:
train_dl = DataLoader(train_ds, batch_size, shuffle=True, num_workers=4, pin_memory=True) val_dl = DataLoader(val_ds, batch_size*2, num_workers=4, pin_memory=True) test_dl = DataLoader(test_ds, batch_size*2, num_workers=4, pin_memory=True)
Podemos ver lotes de imágenes del conjunto de datos utilizando el método make_grid de torchvision. Cada vez que se ejecuta el siguiente código, obtenemos un bach diferente, ya que el muestreador baraja los índices antes de crear los lotes.
In [11]:
def show_batch(dl): for images, labels in dl: fig, ax = plt.subplots(figsize=(12, 6)) ax.set_xticks([]); ax.set_yticks([]) ax.imshow(make_grid(images, nrow=16).permute(1, 2, 0)) break
In [12]:
show_batch(train_dl)

Definición de los modelos
Vamos a crear tres modelos diferentes para este proyecto:
- Regresión Logística.
- Red neuronal profunda.
- Red neuronal convolucional.
1. Regresión Logística
La regresión logística es un algoritmo de clasificación de aprendizaje supervisado que se utiliza para predecir la probabilidad de una variable objetivo. La naturaleza de la variable objetivo o dependiente es dicotómica, lo que significa que sólo hay dos clases posibles. La variable dependiente es de naturaleza binaria, con datos codificados como 1 (significa éxito/sí) o 0 (significa fracaso/no).
Nuestras imágenes tienen la forma 3x128x128, pero necesitamos que sean vectores de tamaño 49.152. Utilizaremos el método .reshape de un tensor, que nos permitirá «ver» eficientemente cada imagen como un vector plano, sin modificar realmente los datos subyacentes.
Para incluir esta funcionalidad adicional en nuestro modelo, necesitamos definir un modelo personalizado, extendiendo la clase nn.Module de PyTorch.
In [13]:
class CoilLRModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(input_size, num_classes)
def forward(self, xb):
xb = xb.reshape(-1, in_channels*128*128)
out = self.linear(xb)
return out
def training_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdida
return loss
def validation_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdida
acc = accuracy(out, labels) # Calcular exactitud
return {'val_loss': loss.detach(), 'val_acc': acc.detach()}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs] epoch_loss = torch.stack(batch_losses).mean() # Combinar pérdidas
batch_accs = [x['val_acc'] for x in outputs] epoch_acc = torch.stack(batch_accs).mean() # Combinar exactitud
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
def epoch_end(self, epoch, result):
print("Epoch [{}], val_loss: {:.4f}, val_acc: {:.4f}".format(epoch, result['val_loss'], result['val_acc']))
model = CoilLRModel()
Métrica de evaluación y función de pérdida para la clasificación de imágenes con Deep Learning
Necesitamos una forma de evaluar el rendimiento de nuestro modelo. Una forma natural de hacerlo sería encontrar el porcentaje de etiquetas que se predijeron correctamente (precisión de las predicciones). A continuación, definiremos una función de evaluación, que llevará a cabo la fase de validación, y una función de ajuste que realizará todo el proceso de formación.
In [14]:
def accuracy(outputs, labels): _, preds = torch.max(outputs, dim=1) return torch.tensor(torch.sum(preds == labels).item() / len(preds)) def evaluate(model, val_loader): outputs = [model.validation_step(batch) for batch in val_loader] return model.validation_epoch_end(outputs)
Entrenamiento del modelo para la clasificación de imágenes con Deep Learning
In [15]:
def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD): history = [] optimizer = opt_func(model.parameters(), lr) for epoch in range(epochs): # Training Phase for batch in train_loader: loss = model.training_step(batch) loss.backward() optimizer.step() optimizer.zero_grad() # Validation phase result = evaluate(model, val_loader) model.epoch_end(epoch, result) history.append(result) return history
La función de ajuste registra la pérdida de validación y la métrica de cada época y devuelve un historial del proceso de entrenamiento. Esto es útil para depurar y visualizar el proceso de entrenamiento. Antes de entrenar el modelo, veamos cómo se comporta en el conjunto de validación con el conjunto inicial de pesos y sesgos inicializados aleatoriamente.
Las configuraciones como el tamaño del lote, la tasa de aprendizaje, etc. deben elegirse de antemano al entrenar modelos de aprendizaje automático, y se denominan hiperparámetros. Elegir los hiperparámetros adecuados es fundamental para entrenar un modelo preciso en un tiempo razonable, y es un área activa de investigación y experimentación.
In [16]:
result0 = evaluate(model, val_dl) result0
Out[16]:
{'val_loss': 4.655781269073486, 'val_acc': 0.018830128014087677}
La precisión inicial ronda el 1,8%, que es lo que cabría esperar de un modelo inicializado aleatoriamente (ya que tiene una probabilidad de 1 entre 100 de acertar una etiqueta adivinando al azar).
In [17]:
history = fit(10, learning_rate, model, train_dl, val_dl)
Epoch [0], val_loss: 4.2210, val_acc: 0.1641 Epoch [1], val_loss: 3.8676, val_acc: 0.2875 Epoch [2], val_loss: 3.5594, val_acc: 0.4057 Epoch [3], val_loss: 3.2867, val_acc: 0.5219 Epoch [4], val_loss: 3.0482, val_acc: 0.5643 Epoch [5], val_loss: 2.8349, val_acc: 0.6155 Epoch [6], val_loss: 2.6460, val_acc: 0.6747 Epoch [7], val_loss: 2.4802, val_acc: 0.7192 Epoch [8], val_loss: 2.3304, val_acc: 0.7447 Epoch [9], val_loss: 2.1958, val_acc: 0.7738
Con 10 iteraciones, pasamos de un 1,8% de precisión a un 79%, lo que supone una mejora muy buena.
In [18]:
accuracies = [r['val_acc'] for r in history] plt.plot(accuracies, '-x') plt.xlabel('epoch') plt.ylabel('accuracy') plt.title('Accuracy vs. No. of epochs');

In [19]:
# Evaluar en el conjunto de datos de prueba
result = evaluate(model, test_dl)
result
Out[19]:
{'val_loss': 2.218763589859009, 'val_acc': 0.7651241421699524}
Predicción
In [20]:
def predict_image(img, model): xb = img.unsqueeze(0) yb = model(xb) _, preds = torch.max(yb, dim=1) return preds[0].item()
In [21]:
img, label = test_ds[158] plt.imshow(img.permute(1, 2, 0)) print('Label:', label, ', Predicted:', predict_image(img, model))
Label: 64 , Predicted: 64

Guardar el modelo
In [22]:
torch.save(model.state_dict(), '1-coil-logistic.pth')
2. Red neuronal profunda para la clasificación de imágenes con Deep Learning
Una red neuronal profunda (DNN) es una red neuronal artificial (ANN) con múltiples capas entre las capas de entrada y salida. La DNN encuentra la manipulación matemática correcta para convertir la entrada en la salida, ya sea una relación lineal o no lineal. La red se desplaza por las capas calculando la probabilidad de cada salida.
In [23]:
def accuracy(outputs, labels): _, preds = torch.max(outputs, dim=1) return torch.tensor(torch.sum(preds == labels).item() / len(preds))
In [24]:
class CoilDNNModel(nn.Module):
"""Feedfoward neural network with 1 hidden layer"""
def __init__(self, in_size, out_size):
super().__init__()
# hidden layer
self.linear1 = nn.Linear(in_size, 2048)
# hidden layer 2
self.linear2 = nn.Linear(2048, 1024)
# output layer
self.linear3 = nn.Linear(1024, out_size)
def forward(self, xb):
# Flatten the image tensors
out = xb.view(xb.size(0), -1)
# Get intermediate outputs using hidden layer 1
out = self.linear1(out)
# Apply activation function
out = F.relu(out)
# Get intermediate outputs using hidden layer 2
out = self.linear2(out)
# Apply activation function
out = F.relu(out)
# Get predictions using output layer
out = self.linear3(out)
return out
def training_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdidas
return loss
def validation_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdidas
acc = accuracy(out, labels) # Calcular exactitud
return {'val_loss': loss, 'val_acc': acc}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs] epoch_loss = torch.stack(batch_losses).mean() # Combinar pérdidas
batch_accs = [x['val_acc'] for x in outputs] epoch_acc = torch.stack(batch_accs).mean() # Combinar precisiones
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
def epoch_end(self, epoch, result):
print("Epoch [{}], val_loss: {:.4f}, val_acc: {:.4f}".format(epoch, result['val_loss'], result['val_acc']))
Uso de GPU para la clasificación de imágenes con Deep Learning
In [25]:
torch.cuda.is_available()
Out[25]:
True
In [26]:
def get_default_device(): """Pick GPU if available, else CPU""" if torch.cuda.is_available(): return torch.device('cuda') else: return torch.device('cpu')
In [27]:
device = get_default_device() device
Out[27]:
device(type='cuda')
In [28]:
def to_device(data, device): """Move tensor(s) to chosen device""" if isinstance(data, (list,tuple)): return [to_device(x, device) for x in data] return data.to(device, non_blocking=True)
In [29]:
class DeviceDataLoader(): """Wrap a dataloader to move data to a device""" def __init__(self, dl, device): self.dl = dl self.device = device def __iter__(self): """Yield a batch of data after moving it to device""" for b in self.dl: yield to_device(b, self.device) def __len__(self): """Number of batches""" return len(self.dl)
In [30]:
train_dl = DeviceDataLoader(train_dl, device) val_dl = DeviceDataLoader(val_dl, device) test_dl = DeviceDataLoader(test_dl, device)
Entrenamiento del modelo para la clasificación de imágenes con Deep Learning
In [31]:
def evaluate(model, val_loader): outputs = [model.validation_step(batch) for batch in val_loader] return model.validation_epoch_end(outputs) def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD): history = [] optimizer = opt_func(model.parameters(), lr) for epoch in range(epochs): # Training Phase for batch in train_loader: loss = model.training_step(batch) loss.backward() optimizer.step() optimizer.zero_grad() # Validation phase result = evaluate(model, val_loader) model.epoch_end(epoch, result) history.append(result) return history
In [32]:
model = CoilDNNModel(input_size, out_size=num_classes) to_device(model, device)
Out[32]:
CoilDNNModel( (linear1): Linear(in_features=49152, out_features=2048, bias=True) (linear2): Linear(in_features=2048, out_features=1024, bias=True) (linear3): Linear(in_features=1024, out_features=101, bias=True) )
In [33]:
history = fit(10, learning_rate*10, model, train_dl, val_dl)
Epoch [0], val_loss: 4.4509, val_acc: 0.2058 Epoch [1], val_loss: 4.2162, val_acc: 0.2385 Epoch [2], val_loss: 3.8548, val_acc: 0.2724 Epoch [3], val_loss: 3.3516, val_acc: 0.3716 Epoch [4], val_loss: 2.8126, val_acc: 0.4919 Epoch [5], val_loss: 2.3059, val_acc: 0.5774 Epoch [6], val_loss: 1.8835, val_acc: 0.6671 Epoch [7], val_loss: 1.5763, val_acc: 0.6943 Epoch [8], val_loss: 1.3278, val_acc: 0.7539 Epoch [9], val_loss: 1.1209, val_acc: 0.8125
In [34]:
losses = [x['val_loss'] for x in history] plt.plot(losses, '-x') plt.xlabel('epoch') plt.ylabel('loss') plt.title('Loss vs. No. of epochs');

In [35]:
accuracies = [x['val_acc'] for x in history] plt.plot(accuracies, '-x') plt.xlabel('epoch') plt.ylabel('accuracy') plt.title('Accuracy vs. No. of epochs');

In [36]:
# Evaluate on test dataset result = evaluate(model, test_dl) result
Out[36]:
{'val_loss': 1.1144559383392334, 'val_acc': 0.8215144276618958}
Predicciones
In [37]:
def predict_image(img, model): xb = to_device(img.unsqueeze(0), device) yb = model(xb) _, preds = torch.max(yb, dim=1) return preds[0].item()
In [38]:
img, label = test_ds[200] plt.imshow(img.permute(1, 2, 0)) print('Label:', label, ', Predicted:', predict_image(img, model))
Label: 2 , Predicted: 2

Guardar el modelo
In [39]:
torch.save(model.state_dict(), '2-coil-dnn.pth')
3. Red neuronal convolucional para la clasificación de imágenes con Deep Learning
Las redes neuronales convolucionales (CNN o ConvNet) son un tipo de redes neuronales profundas que suelen aplicarse al análisis de imágenes visuales. También se conocen como redes neuronales artificiales invariantes de desplazamiento o espacio (SIANN), debido a su arquitectura de pesos compartidos y a sus características de invariancia de traslación. Tienen aplicaciones en el reconocimiento de imágenes y vídeos, sistemas de recomendación, clasificación de imágenes, análisis de imágenes médicas, procesamiento del lenguaje natural y series temporales financieras.
In [40]:
def accuracy(outputs, labels):
_, preds = torch.max(outputs, dim=1)
return torch.tensor(torch.sum(preds == labels).item() / len(preds))
class ImageClassificationBase(nn.Module):
def training_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdidas
return loss
def validation_step(self, batch):
images, labels = batch
out = self(images) # Generar predicciones
loss = F.cross_entropy(out, labels) # Calcular pérdidas
acc = accuracy(out, labels) # Calcular exactitud
return {'val_loss': loss.detach(), 'val_acc': acc}
def validation_epoch_end(self, outputs):
batch_losses = [x['val_loss'] for x in outputs] epoch_loss = torch.stack(batch_losses).mean() # Combinar pérdidas
batch_accs = [x['val_acc'] for x in outputs] epoch_acc = torch.stack(batch_accs).mean() # Combinar precisiones
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
def epoch_end(self, epoch, result):
print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
epoch, result['train_loss'], result['val_loss'], result['val_acc']))
In [41]:
class CoilCNNModel(ImageClassificationBase): def __init__(self, in_channels, num_classes): super().__init__() self.network = nn.Sequential( nn.Conv2d(in_channels, 128, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool2d(2, 2), nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool2d(2, 2), nn.Flatten(), nn.Linear(256*1024, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, num_classes)) def forward(self, xb): return self.network(xb)
In [42]:
model = CoilCNNModel(in_channels, num_classes) model
Out[42]:
CoilCNNModel( (network): Sequential( (0): Conv2d(3, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): ReLU() (2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): ReLU() (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): ReLU() (7): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): ReLU() (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Flatten() (11): Linear(in_features=262144, out_features=1024, bias=True) (12): ReLU() (13): Linear(in_features=1024, out_features=512, bias=True) (14): ReLU() (15): Linear(in_features=512, out_features=101, bias=True) ) )
Uso de GPU
In [43]:
def get_default_device(): """Pick GPU if available, else CPU""" if torch.cuda.is_available(): return torch.device('cuda') else: return torch.device('cpu') def to_device(data, device): """Move tensor(s) to chosen device""" if isinstance(data, (list,tuple)): return [to_device(x, device) for x in data] return data.to(device, non_blocking=True) class DeviceDataLoader(): """Wrap a dataloader to move data to a device""" def __init__(self, dl, device): self.dl = dl self.device = device def __iter__(self): """Yield a batch of data after moving it to device""" for b in self.dl: yield to_device(b, self.device) def __len__(self): """Number of batches""" return len(self.dl)
In [44]:
device = get_default_device() device
Out[44]:
device(type='cuda')
In [45]:
train_dl = DeviceDataLoader(train_dl, device) val_dl = DeviceDataLoader(val_dl, device) test_dl = DeviceDataLoader(test_dl, device) to_device(model, device);
Entrenamiento del modelo para la clasificación de imágenes con Deep Learning
In [46]:
@torch.no_grad() def evaluate(model, val_loader): model.eval() outputs = [model.validation_step(batch) for batch in val_loader] return model.validation_epoch_end(outputs) def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD): history = [] optimizer = opt_func(model.parameters(), lr) for epoch in range(epochs): # Training Phase model.train() train_losses = [] for batch in train_loader: loss = model.training_step(batch) train_losses.append(loss) loss.backward() optimizer.step() optimizer.zero_grad() # Validation phase result = evaluate(model, val_loader) result['train_loss'] = torch.stack(train_losses).mean().item() model.epoch_end(epoch, result) history.append(result) return history
In [47]:
model = to_device(CoilCNNModel(in_channels, num_classes), device)
In [48]:
evaluate(model, val_dl)
Out[48]:
{'val_loss': 4.615262031555176, 'val_acc': 0.011017628014087677}
In [49]:
num_epochs = 4 opt_func = torch.optim.Adam
In [50]:
history = fit(num_epochs, learning_rate, model, train_dl, val_dl, opt_func)
Epoch [0], train_loss: 2.8882, val_loss: 0.8734, val_acc: 0.7458 Epoch [1], train_loss: 0.4132, val_loss: 0.1532, val_acc: 0.9605 Epoch [2], train_loss: 0.1390, val_loss: 0.0667, val_acc: 0.9764 Epoch [3], train_loss: 0.0844, val_loss: 0.1092, val_acc: 0.9676
In [51]:
def plot_accuracies(history): accuracies = [x['val_acc'] for x in history] plt.plot(accuracies, '-x') plt.xlabel('epoch') plt.ylabel('accuracy') plt.title('Accuracy vs. No. of epochs');
In [52]:
plot_accuracies(history)

In [53]:
def plot_losses(history): train_losses = [x.get('train_loss') for x in history] val_losses = [x['val_loss'] for x in history] plt.plot(train_losses, '-bx') plt.plot(val_losses, '-rx') plt.xlabel('epoch') plt.ylabel('loss') plt.legend(['Training', 'Validation']) plt.title('Loss vs. No. of epochs');
In [54]:
plot_losses(history)

Pruebas con imágenes individuales
In [55]:
def predict_image(img, model): # Convert to a batch of 1 xb = to_device(img.unsqueeze(0), device) # Get predictions from model yb = model(xb) # Pick index with highest probability _, preds = torch.max(yb, dim=1) # Retrieve the class label return preds[0].item()
In [56]:
img, label = test_ds[0] plt.imshow(img.permute(1, 2, 0)) print('Label:', label, ', Predicted:', predict_image(img, model))
Label: 94 , Predicted: 94

In [57]:
img, label = test_ds[100] plt.imshow(img.permute(1, 2, 0)) print('Label:', label, ', Predicted:', predict_image(img, model))
Label: 35 , Predicted: 35

In [58]:
# Evaluate on test dataset result = evaluate(model, test_dl) result
Out[58]:
{'val_loss': 0.10439281165599823, 'val_acc': 0.9718549847602844}
Guardar el modelo
In [59]:
torch.save(model.state_dict(), '3-coil-cnn.pth')