Información de la Tarea
Estudiante: Andrés Cruz Chipol
Curso: Aprendizaje Profundo
Fecha de entrega: 19 de Mayo, 2026
Descripción de la Tarea
Realizar la red del tutorial de Pytorch para clasificar los imágenes de dígitos de MNIST
Clasificación de dígitos manuscritos
Introducción
El dataset MNIST es fundamental en el Aprendizaje Profundo. Contiene imágenes de dígitos manuscritos del 0 al 9 en formato de escala de grises de 28x28 píxeles. El objetivo es entrenar una red neuronal multicapa (MLP) para predecir correctamente la clase a partir de los píxeles de entrada.
Importaciones e Hiperparámetros
Comenzamos configurando el entorno en PyTorch, estableciendo la semilla y definiendo los hiperparámetros de entrenamiento.
import torchfrom torch import nnfrom torch.utils.data import DataLoaderfrom torchvision import datasetsfrom torchvision.transforms import v2import matplotlib.pyplot as plt
torch.manual_seed(2)device = "mps"print(f"Using {device} device")
learning_rate = 0.001batch_size = 64epochs = 20Preparación de Datos
Utilizamos las transformaciones v2 de torchvision para convertir las imágenes a tensores de tipo float32 y preparamos los iteradores DataLoader.
#Preparación de datostransform = v2.Compose([v2.ToImage(), v2.ToDtype(torch.float32, scale=True)])
training_data = datasets.MNIST(root="data", train=True, download=True, transform=transform)test_data = datasets.MNIST(root="data", train=False, download=True, transform=transform)
train_dataloader = DataLoader(training_data, batch_size=batch_size)test_dataloader = DataLoader(test_data, batch_size=batch_size)Visualización
Para explorar el conjunto de datos, seleccionamos y mostramos 9 dígitos aleatorios utilizando Matplotlib.
# Visualizaciónlabels_map = {i: str(i) for i in range(10)}figure = plt.figure(figsize=(8, 8))
for i in range(1, 10): sample_idx = torch.randint(len(training_data), size=(1,)).item() img, label = training_data[sample_idx] figure.add_subplot(3, 3, i) plt.title(labels_map[label]) plt.axis("off") plt.imshow(img.squeeze(), cmap="gray")
figure.suptitle("Imágenes de MNIST", fontsize=16)plt.show()
Definición del Modelo
Nuestra arquitectura MLP aplana la imagen a un vector de 784 elementos y utiliza dos capas ocultas de 512 neuronas con activación ReLU, finalizando en 10 clases de salida.
# Modelo - Red Neuronalclass NeuralNetwork(nn.Module): def __init__(self): super().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28*28, 512), nn.ReLU(), nn.Linear(512, 512), nn.ReLU(), nn.Linear(512, 10), )
def forward(self, x): x = self.flatten(x) return self.linear_relu_stack(x)
model = NeuralNetwork().to(device)loss_fn = nn.CrossEntropyLoss()optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, nesterov=True, momentum=0.99)Funciones de Entrenamiento y Prueba
Definimos los bucles para propagar los datos, calcular el error y actualizar los pesos del modelo, así como para evaluar el rendimiento sin calcular gradientes.
# Funciones de train & testdef train_loop(dataloader, model, loss_fn, optimizer): size = len(dataloader.dataset) model.train()
for batch, (X, y) in enumerate(dataloader): X, y = X.to(device), y.to(device)
pred = model(X) loss = loss_fn(pred, y)
loss.backward() optimizer.step() optimizer.zero_grad()
if batch % 100 == 0: loss_val, current = loss.item(), batch * batch_size + len(X) print(f"loss: {loss_val:>7f} [{current:>5d}/{size:>5d}]")
def test_loop(dataloader, model, loss_fn): model.eval() size = len(dataloader.dataset) num_batches = len(dataloader) test_loss, correct = 0, 0
with torch.no_grad(): for X, y in dataloader: X, y = X.to(device), y.to(device) pred = model(X) test_loss += loss_fn(pred, y).item() correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches correct /= size print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")Ejecución del Entrenamiento
Ejecutamos el entrenamiento por 20 épocas y guardamos los pesos finales.
# Entrenamientofor t in range(epochs): print(f"Epoch {t+1}\n-------------------------------") train_loop(train_dataloader, model, loss_fn, optimizer) test_loop(test_dataloader, model, loss_fn)
torch.save(model, 'model.pth')Resultados de la Consola
Al iniciar la ejecución, se reporta el dispositivo utilizado y la arquitectura de la red:
Using mps deviceNeuralNetwork( (flatten): Flatten(start_dim=1, end_dim=-1) (linear_relu_stack): Sequential( (0): Linear(in_features=784, out_features=512, bias=True) (1): ReLU() (2): Linear(in_features=512, out_features=512, bias=True) (3): ReLU() (4): Linear(in_features=512, out_features=10, bias=True) ))Tras procesar las 20 épocas de entrenamiento, se alcanzan los siguientes resultados finales:
Epoch 20-------------------------------loss: 0.003598 [ 64/60000]loss: 0.002625 [ 6464/60000]loss: 0.003522 [12864/60000]loss: 0.002404 [19264/60000]loss: 0.012981 [25664/60000]loss: 0.005628 [32064/60000]loss: 0.002907 [38464/60000]loss: 0.008000 [44864/60000]loss: 0.007265 [51264/60000]loss: 0.012696 [57664/60000]Test Error: Accuracy: 98.2%, Avg loss: 0.063219Conclusión
La implementación de la red neuronal feedforward en PyTorch permitió clasificar los dígitos manuscritos del dataset MNIST con una precisión final del 98.2% tras 20 épocas, demostrando la eficacia del optimizador SGD con momento Nesterov y el procesamiento acelerado para el entrenamiento de modelos multicapa. Este es el mejor resultado al que se llegó utilizando SGD, momentum y Nesterov; se hicieron varios cambios de semilla, learning rate y épocas, siendo este el mejor modelo obtenido después de varias iteraciones.