Redes convolucionales para visión por computadora

Capítulo 9

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

Motivación estructural: por qué las convoluciones funcionan tan bien en imágenes

En visión por computadora, una imagen no es solo un vector largo: tiene estructura espacial (pixeles vecinos suelen estar relacionados) y patrones locales (bordes, esquinas, texturas) que se repiten en distintas posiciones. Una red convolucional (CNN) aprovecha esto con dos ideas clave:

  • Conectividad local: una neurona “mira” solo una región pequeña (ventana) de la imagen, en lugar de conectarse a todos los pixeles.
  • Pesos compartidos: el mismo conjunto de pesos (un kernel o filtro) se aplica en todas las posiciones. Así, el modelo aprende “detector de borde horizontal”, “textura”, etc., independientemente de dónde aparezca.

Esto reduce drásticamente el número de parámetros frente a una capa totalmente conectada sobre la imagen completa y, además, introduce una propiedad útil: si un patrón aparece en otra zona, el filtro puede detectarlo igual.

Componentes principales de una CNN

Capas convolucionales (Conv2D): filtros, kernels, canales y mapas de activación

Una capa convolucional aplica varios filtros a la entrada. Cada filtro produce un mapa de activación (también llamado feature map) que indica “dónde” se detecta ese patrón.

  • Kernel (filtro): matriz pequeña, por ejemplo 3x3 o 5x5. En imágenes RGB, el kernel realmente tiene forma kH x kW x C_in (incluye todos los canales de entrada).
  • Canales: una imagen RGB tiene C=3 canales. Tras una convolución con F filtros, la salida tendrá C_out=F canales (uno por filtro).
  • Mapa de activación: tensor de salida por filtro. Si la entrada es H x W x C, la salida será H_out x W_out x F.

Intuición: cada filtro aprende una plantilla. Al deslizarse por la imagen, calcula una respuesta alta donde la plantilla “encaja”. En capas iniciales suelen aparecer bordes y contrastes; en capas más profundas, combinaciones más complejas (partes, texturas específicas, etc.).

Stride (paso)

El stride es cuánto se desplaza el kernel cada vez. Con stride=1 se evalúan posiciones contiguas; con stride=2 se “salta” una posición, reduciendo la resolución espacial y el costo computacional.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

Padding (relleno)

El padding agrega bordes (normalmente ceros) alrededor de la imagen para controlar el tamaño de salida:

  • Valid: sin padding, la salida se hace más pequeña.
  • Same: padding elegido para mantener aproximadamente el mismo H y W (con stride=1 se mantiene igual).

El padding ayuda a no “perder” información de los bordes y a apilar muchas capas sin que el mapa se reduzca demasiado rápido.

Pooling (submuestreo): max pooling y average pooling

El pooling reduce la resolución espacial agregando información local. El más común es max pooling (toma el máximo en una ventana, por ejemplo 2x2), aunque también existe average pooling (promedio).

  • Ventaja práctica: reduce cómputo y hace la representación más robusta a pequeñas traslaciones.
  • Alternativa moderna: usar convoluciones con stride>1 en lugar de pooling explícito, según la arquitectura.

Capas totalmente conectadas al final (clasificación)

En clasificación, una CNN típica termina convirtiendo los mapas de activación en un vector y luego en probabilidades por clase:

  • Flatten: aplana H x W x C a un vector largo.
  • Dense: combina globalmente características para decidir la clase.
  • Global Average Pooling (GAP): alternativa a Flatten; promedia cada canal y reduce parámetros, a menudo mejorando estabilidad.

Flujo de tensores y arquitectura típica para clasificación

Una arquitectura didáctica (estilo “bloques conv + pooling”) podría verse así:

Entrada: (H, W, 3)  # imagen RGB
Bloque 1: Conv(3x3, F=16, stride=1, padding=same) -> Activación -> Pool(2x2)
Bloque 2: Conv(3x3, F=32, stride=1, padding=same) -> Activación -> Pool(2x2)
Bloque 3: Conv(3x3, F=64, stride=1, padding=same) -> Activación
Cabeza:    GAP o Flatten -> Dense(64) -> Dense(#clases)

Ejemplo de dimensiones (imagen pequeña 32x32x3, pooling 2x2):

EtapaOperaciónForma del tensor
EntradaImagen(32, 32, 3)
Conv13x3, F=16, same(32, 32, 16)
Pool12x2(16, 16, 16)
Conv23x3, F=32, same(16, 16, 32)
Pool22x2(8, 8, 32)
Conv33x3, F=64, same(8, 8, 64)
GAPPromedio espacial(64)
SalidaDense(#clases)(#clases)

Lectura: a medida que avanzas, disminuye la resolución espacial (H, W) y aumenta la profundidad (C): se pasa de detalles locales a descriptores más abstractos.

Normalización y regularización frecuentes en CNN (en la práctica)

En CNN modernas es común incluir componentes que estabilizan el entrenamiento y mejoran el desempeño:

  • Batch Normalization: suele colocarse entre la convolución y la activación. Ayuda a estabilizar escalas internas y permite entrenamientos más robustos.
  • Dropout: más habitual en la “cabeza” (capas densas) que en las capas conv, aunque también se usa SpatialDropout para apagar canales completos.
  • Data augmentation: en visión es especialmente importante (rotaciones pequeñas, flips, crops, cambios de brillo/contraste). Se aplica al cargar datos, no como capa “aprendible”.
  • Weight decay (L2): muy usado en convoluciones para controlar complejidad efectiva.

En plataformas como TensorFlow/Keras o PyTorch, estos elementos se integran como capas o parámetros del optimizador y del dataloader.

Ejemplo didáctico paso a paso: clasificación simple con imágenes pequeñas

Objetivo: construir una CNN pequeña para clasificar imágenes 32x32 en pocas clases (por ejemplo, “círculo vs cuadrado” en imágenes sintéticas, o un subconjunto pequeño de un dataset educativo). La idea es entender el flujo y luego inspeccionar qué aprende.

Paso 1: preparar datos (forma y normalización)

  • Organiza tus datos como tensores (N, 32, 32, 1) si son en escala de grises o (N, 32, 32, 3) si son RGB.
  • Escala los pixeles a [0, 1] (por ejemplo, dividir por 255) para que las activaciones trabajen en rangos manejables.
  • Divide en entrenamiento/validación (por ejemplo 80/20).

Paso 2: definir una CNN pequeña (Keras)

import tensorflow as tf
from tensorflow.keras import layers, models

num_classes = 2

model = models.Sequential([
    layers.Input(shape=(32, 32, 1)),

    layers.Conv2D(16, (3,3), padding="same", use_bias=False),
    layers.BatchNormalization(),
    layers.ReLU(),
    layers.MaxPooling2D((2,2)),

    layers.Conv2D(32, (3,3), padding="same", use_bias=False),
    layers.BatchNormalization(),
    layers.ReLU(),
    layers.MaxPooling2D((2,2)),

    layers.Conv2D(64, (3,3), padding="same", use_bias=False),
    layers.BatchNormalization(),
    layers.ReLU(),

    layers.GlobalAveragePooling2D(),
    layers.Dropout(0.2),
    layers.Dense(num_classes, activation="softmax")
])

model.summary()

Notas didácticas:

  • use_bias=False es común cuando usas BatchNorm, porque BN ya incorpora un desplazamiento aprendible.
  • GlobalAveragePooling2D reduce parámetros y hace la cabeza más simple para un ejemplo pequeño.

Paso 3: compilar y entrenar

model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"]
)

history = model.fit(
    x_train, y_train,
    validation_data=(x_val, y_val),
    epochs=10,
    batch_size=64
)

Si tus etiquetas están en one-hot, cambia la pérdida a categorical_crossentropy. Para un dataset pequeño, prueba también data augmentation (por ejemplo con tf.keras.layers.RandomFlip, RandomRotation) al inicio del modelo o en el pipeline de datos.

Paso 4: verificar el flujo de tensores con un ejemplo

Una forma rápida de “ver” el flujo es pasar un batch y revisar formas intermedias. En Keras puedes crear un modelo que exponga salidas de capas:

layer_outputs = [layer.output for layer in model.layers if isinstance(layer, (layers.Conv2D, layers.MaxPooling2D))]
probe = tf.keras.Model(inputs=model.input, outputs=layer_outputs)

acts = probe(x_val[:1])
for a in acts:
    print(a.shape)

Interpretación: cada salida corresponde a un bloque; observa cómo cambian H, W y el número de canales.

Paso 5: analizar características aprendidas (filtros y mapas de activación)

Dos inspecciones simples y muy instructivas:

5.1 Visualizar kernels de la primera capa

En la primera capa, los filtros suelen parecer detectores de bordes/gradientes. Puedes extraer pesos y graficarlos (en escala de grises si C_in=1):

import numpy as np

w = model.layers[1].get_weights()[0]  # Conv2D: (3,3,C_in,F)
print(w.shape)

# Ejemplo: tomar el filtro 0
k0 = w[:, :, 0, 0]
# Luego lo graficas con matplotlib (imshow) normalizando a [0,1]

Qué buscar: patrones tipo “borde horizontal/vertical”, “diagonal”, “centro-surround”. Si no aparecen, revisa si el modelo está aprendiendo (accuracy estancada) o si el dataset es demasiado simple/ruidoso.

5.2 Visualizar mapas de activación para una imagen

Los mapas muestran dónde responde cada filtro. Para una imagen de validación:

# Tomar la salida del primer bloque conv (por ejemplo, después de ReLU)
# Ajusta el índice según tu modelo
conv1_out_model = tf.keras.Model(model.input, model.layers[3].output)
feat = conv1_out_model(x_val[:1])  # (1, 32, 32, 16)

# feat[0, :, :, i] es el mapa del canal i

Interpretación práctica:

  • Si un canal se activa en contornos del objeto, probablemente aprendió un detector de borde.
  • Si se activa en regiones repetitivas, puede estar capturando textura.
  • En capas más profundas, los mapas suelen ser más “abstractos” y menos interpretables visualmente, pero aún reflejan partes relevantes.

Paso 6: pequeñas variaciones para experimentar (y entender el rol de cada componente)

  • Stride vs pooling: reemplaza MaxPooling2D por Conv2D(..., strides=2) y compara precisión y velocidad.
  • Padding: cambia same por valid y observa cómo se reduce el tamaño y cómo afecta el rendimiento.
  • Número de filtros: duplica F en cada bloque (16-32-64) vs mantenerlo constante; observa capacidad y sobreajuste.
  • GAP vs Flatten: prueba Flatten + Dense(128) y compara número de parámetros y desempeño.

Ahora responde el ejercicio sobre el contenido:

¿Qué combinación de mecanismos explica mejor por qué una CNN puede detectar el mismo patrón visual (por ejemplo, un borde) en distintas posiciones de una imagen con menos parámetros que una capa totalmente conectada?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

Las CNN aprovechan la estructura espacial mediante conectividad local (ventanas pequeñas) y pesos compartidos (mismo kernel en todas las posiciones), lo que reduce parámetros y permite detectar el mismo patrón aunque cambie de ubicación.

Siguiente capítulo

Redes recurrentes y modelos para secuencias

Arrow Right Icon
Portada de libro electrónico gratuitaIntroducción a las Redes Neuronales: Del Perceptrón al Deep Learning
69%

Introducción a las Redes Neuronales: Del Perceptrón al Deep Learning

Nuevo curso

13 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.