LECCIÓN 12 · AVANZADO

Redes convolucionales (CNNs)

La arquitectura que revolucionó la visión por computadora. En vez de conectar todo con todo, las CNNs deslizan pequeños filtros sobre la imagen para detectar patrones — bordes, texturas, formas.

Recuerda de la lección anterior: aprendiste a combatir el overfitting con dropout, weight decay y BatchNorm. Hoy cambiamos de tema: en lugar de mejorar el entrenamiento, cambiamos la arquitectura para trabajar mejor con imágenes.

1. ¿Por qué no usar nn.Linear con imágenes?

Una imagen de 200×200 en color tiene 120.000 valores. Una capa Linear conectándolos a 1.000 neuronas necesitaría 120 millones de pesos — solo en la primera capa. Inviable, y además ignora que los píxeles cercanos están relacionados.

Las CNNs resuelven esto con dos ideas: (1) filtros pequeños que se deslizan por toda la imagen reutilizando los mismos pesos, y (2) detectar patrones locales (un borde es un borde esté donde esté). Resultado: muchísimos menos parámetros y mejor para imágenes.

2. La convolución: nn.Conv2d

Un filtro (también llamado kernel) es una cuadrícula pequeña de números, por ejemplo 3×3 (3 filas y 3 columnas). Se coloca sobre un trozo de la imagen, se multiplican los números uno por uno y se suman — eso produce un único número que mide cuánto "encaja" ese patrón en ese trozo. Un filtro aprende a detectar un patrón específico (un borde, una textura).

Las imágenes en PyTorch tienen canales: una imagen en escala de grises tiene 1 canal (solo brillo), una en color tiene 3 canales (rojo, verde, azul). La forma es (lote, canales, alto, ancho):

# imagen ficticia: lote=1, 1 canal (gris), 8x8 píxeles
img = torch.randn(1, 1, 8, 8)

conv = nn.Conv2d(in_channels=1, out_channels=4, kernel_size=3)
out = conv(img)
print(out.shape)
salida realentrada: (1, 1, 8, 8) salida: (1, 4, 6, 6)
✍️ A mano: de dónde sale el 6×6
Fórmula del tamaño de salida de una convolución:
   salida = (entrada − kernel + 2·padding) / stride + 1

Con entrada=8, kernel=3, padding=0, stride=1:
   (8 − 3 + 2·0) / 1 + 1 = 5 + 1 = 6

Se aplica igual al alto y al ancho → 6×6.
Y son 4 canales porque pusimos out_channels=4 (4 filtros). ✓

3. Pooling: reducir tamaño quedándote con lo importante

nn.MaxPool2d reduce el tamaño tomando el máximo de cada región. Resume y hace la red más eficiente y robusta a pequeños desplazamientos:

pool = nn.MaxPool2d(2)   # toma el máximo de cada bloque 2x2
print(pool(out).shape)
salida realde (1, 4, 6, 6) → (1, 4, 3, 3)
✍️ A mano
MaxPool2d(2) divide alto y ancho entre 2:
   6 / 2 = 3   (toma el máximo de cada bloque 2×2)
Los 4 canales NO cambian (el pooling actúa dentro de cada canal).

El alto y ancho se reducen a la mitad (6→3). Los 4 canales se mantienen.

4. La estructura típica de una CNN

Bloques de [Conv → ReLU → Pool] que extraen patrones cada vez más complejos, y al final capas Linear para clasificar:

cnn = nn.Sequential(
    nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),   # bloque 1
    nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),  # bloque 2
    nn.Flatten(),                                          # aplanar tensor 3D a vector 1D
    nn.Linear(32*7*7, 10),                              # 32*7*7=1568: 32 filtros × 7×7 píxeles (ver nota)
)
Las primeras capas detectan patrones simples (bordes); las profundas, patrones complejos (ojos, ruedas…). Es la arquitectura detrás del reconocimiento de imágenes moderno.

¿De dónde sale 32*7*7? Si la imagen de entrada es 28×28 (como el dataset MNIST de dígitos escritos a mano): bloque 1 con MaxPool2d(2) → 28/2=14. Bloque 2 con MaxPool2d(2) → 14/2=7. Con 32 filtros → 32×7×7=1568 valores que Flatten convierte a un vector para Linear.

💡 Nota: nuestro proyecto final es tabular (no imágenes), así que usaremos capas Linear, no CNNs. Pero conocer las CNNs es esencial en tu caja de herramientas de PyTorch — son el tema de visión por computadora.

5. Lo que aprendiste

Lo que aprendiste hoy: las CNNs usan Conv2d (filtros/kernels que se deslizan por la imagen detectando patrones locales) y MaxPool2d (reducir tamaño tomando el máximo de cada región). Apilados en bloques [Conv→ReLU→Pool] y terminando en capas Linear para clasificar. Mucho menos parámetros que conectar todo con todo.

En la próxima lección: transfer learning — reusar redes ya entrenadas en lugar de empezar de cero.