Introducción

El verdadero nacimiento de las redes neuronales convolucionales se atribuye a Yann LeCun a finales de la década de 1980 y principios de los 90, cuando desarrolló LeNet-5, la primera CNN funcional capaz de reconocer dígitos escritos a mano. Su trabajo combinó ideas de neurociencia, visión por computadora y aprendizaje automático para crear una arquitectura que imitaba la forma en que el cerebro procesa patrones visuales. Este avance permitió que una computadora aprendiera a identificar números en imágenes de cheques bancarios, demostrando por primera vez que una máquina podía aprender características visuales complejas sin necesidad de programación manual. El éxito de LeNet marcó el inicio de las CNN modernas y abrió el camino para el reconocimiento visual automático que hoy utilizan desde teléfonos inteligentes hasta autos autónomos.

En muchos ambitos de la vida humana las máquinas reconocen diferentes objetos, figuras y situaciones, por ejemplo: el reconocimiento facial de los celulares inteligentes, fotomultas, entre otros; pero alguna vez se han preguntado: ¿Cómo la máquina reconoce lo que está a nuestro alrededor?, la respuesta sencilla son las redes neuronales convolucionales, por tanto, en el siguiente documento se estarán revisando los principios básicos de las redes neuronales convolucionales, explicando cómo estas redes procesan la información visual, cómo aprenden características mediante filtros y capas, y cómo estos elementos permiten que un sistema interprete patrones complejos en imágenes de manera similar a como lo hace el cerebro humano. También se abordarán ejemplos prácticos y aplicaciones donde estas redes juegan un papel fundamental, con el fin de ofrecer una comprensión clara y accesible de su funcionamiento.

¿Qué son las redes convolucionales?

Las redes neuronales convolucionales (CNN, Convolutional Neural Networks) son un tipo de red neuronal diseñada específicamente para procesar y analizar datos con estructura espacial, como imágenes, videos o mapas de características. Son el modelo que usan hoy en día los sistemas de: reconocimiento facial, autos autónomos, fotomultas, clasificación de objetos, detección de enfermedades en imágenes médicas, etc.

Las redes neuronales convolucionales se distinguen de otras redes neuronales por su rendimiento superior con entradas de señal de imagen, voz o audio. Se componen de tres tipos principales de capas:

La capa convolucional es la primera capa de una red convolucional. Si bien las capas convolucionales pueden ir seguidas de otras capas convolucionales o de capas de agrupación, la capa final es la capa totalmente conectada. Con cada capa, la CNN aumenta su complejidad, identificando mayores porciones de la imagen. Las primeras capas se centran en características simples, como colores y bordes. A medida que los datos de la imagen avanzan a través de las capas, la CNN comienza a reconocer elementos o formas más grandes hasta que finalmente identifica el objeto esperado.

Características:

Componentes clave de una red neuronal Convolucional

Capa convolucional:

La capa convolucional es el bloque de creación principal de una CNN y es donde se realizan la mayoría de los cálculos. Requiere algunos componentes, como los datos de entrada, un filtro y un mapa de características. Cada capa juega el rol de identificar las características de la imagen, las primeras capas de la red convolucional extraen características generales y las siguientes extraen propiedades más específicas.
En la capa convolucional utilizamos pequeñas cuadrículas (llamadas filtros o núcleos) que se mueven sobre la imagen. Cada pequeña cuadrícula es como una minilupa que busca patrones específicos en la foto, como líneas, curvas o formas. A medida que se desplaza por la foto, crea una nueva cuadrícula que resalta dónde encontró estos patrones, el resultado de aplicar estos filtros es el llamado mapa de características, que no es más que una versión transformada de la imagen donde quedan marcadas las zonas relevantes. Estos mapas permiten que la red comprenda qué regiones contienen información útil para reconocer un objeto.

Luego, al pasar estos mapas por varias capas, la red aprende características progresivamente más complejas: primero detecta bordes, luego formas básicas y finalmente partes completas del objeto. De esta forma, cada capa va construyendo una representación más rica y abstracta de la imagen original, lo que permite que al final la red pueda clasificar o identificar qué aparece en la imagen. Finalmente, estas características aprendidas se envían a capas posteriores, normalmente densas, que toman la decisión final sobre el contenido de la imagen. La siguiente imagen muestra como cambia una imagen algo hacer una convolución.

Imagen procesada por filtro

Imagen procesada por filtro

Pasos de la operación de convolución en una CNN

A continuación, se presenta la descripción clara y ordenada de todos los pasos que se realizan en una convolución dentro de una red neuronal convolucional. Estos pasos corresponden a lo que hace un filtro al aplicarse sobre una imagen.

  1. Seleccionar un filtro (kernel)
  • Se elige una pequeña matriz (por ejemplo 3x3 o 5x5).
  • Este filtro contiene valores numéricos que la red aprenderá durante el entrenamiento.
  • Cada filtro busca un patrón distinto, como bordes, líneas o texturas.
  1. Colocar el filtro en la esquina superior izquierda de la imagen
  • La primera celda del filtro se alinea con el primer píxel de la imagen.
  • Este es el punto de inicio del proceso.
  1. Tomar el bloque local de la imagen
  • Se extrae un pequeño cuadrado de la imagen del mismo tamaño que el filtro.
  • Ese bloque contiene los píxeles que se multiplicarán con el filtro.
  1. Multiplicar elemento por elemento
  • Cada número del filtro se multiplica con el píxel correspondiente debajo de él.
  • Este paso compara el patrón del filtro con esa región local de la imagen.
  1. Sumar todos los productos
  • Los valores de las multiplicaciones se suman.
  • Esta suma produce un solo número, que mide qué tan presente está el patrón del filtro en esa zona.
  1. Colocar el resultado en el mapa de características
  • El valor obtenido se deposita en el mapa de características (feature map).
  • Ese valor indica la presencia e intensidad del patrón detectado por el filtro.
  1. Desplazar el filtro (stride)
  • El filtro se desplaza a la derecha según el valor del stride.
  • Se repiten los pasos 3–6.
  • Cuando ya no cabe más hacia la derecha, el filtro baja y se continúa el proceso.
  1. Continuar hasta recorrer toda la imagen
  • El filtro avanza horizontal y verticalmente cubriendo todas las posiciones válidas.
  • Se generan nuevos valores del mapa de características.
  1. Obtener el mapa de características final
  • Al finalizar, se obtiene una matriz que muestra dónde y con qué intensidad aparece el patrón buscado.
  • Este mapa es una versión transformada de la imagen que resalta lo que el filtro detecta.

Ejemplo de una convolución paso a paso

A continuación se mostrará un ejemplo sencillo de cómo se realiza una convolución entre una matriz que representa una imagen y un filtro.

1. Definimos la imagen (matriz de entrada)

Usaremos una matriz de \(5 \times 5\):

\[ I = \begin{bmatrix} 1 & 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 1 & 0 \\ 0 & 0 & 1 & 1 & 1 \\ 0 & 0 & 1 & 1 & 0 \\ 1 & 1 & 0 & 0 & 0 \\ \end{bmatrix} \]

2. Definimos el filtro o kernel

Usaremos un filtro de detección de bordes:

\[ K = \begin{bmatrix} 1 & 0 & -1\\ 1 & 0 & -1\\ 1 & 0 & -1 \end{bmatrix} \]

3. Realizamos la convolución

El filtro se desplaza sobre la matriz de entrada.
Para cada posición:

  1. Se multiplica cada elemento del filtro por la sección correspondiente de la imagen.
  2. Se suman todos los productos.
  3. Se coloca el resultado en el mapa de características.

Como la entrada es \(5 \times 5\) y el kernel es \(3 \times 3\), el resultado será de tamaño:

\[ (5 - 3 + 1) \times (5 - 3 + 1) = 3 \times 3 \]

4. Cálculo manual de cada posición

4.1. Primera posición (arriba a la izquierda)

Ventana de imagen:

\[ \begin{bmatrix} 1 & 1 & 1\\ 0 & 1 & 1\\ 0 & 0 & 1 \end{bmatrix} \]

Multiplicamos con el filtro:

\[ \begin{bmatrix} 1\cdot1 & 1\cdot0 & 1\cdot(-1)\\ 0\cdot1 & 1\cdot0 & 1\cdot(-1)\\ 0\cdot1 & 0\cdot0 & 1\cdot(-1) \end{bmatrix} = \begin{bmatrix} 1 & 0 & -1\\ 0 & 0 & -1\\ 0 & 0 & -1 \end{bmatrix} \]

Sumamos:

\[ 1 + 0 - 1 + 0 + 0 - 1 + 0 + 0 - 1 = -2 \]

Resultado en la posición \((1,1)\):

\[ -2 \]

4.2 Resultado completo

Aplicando el filtro a todas las posiciones, se tiene el siguiente mapa de características:

\[ \begin{bmatrix} -2 & -2 & 0 \\ -1 & -1 & -2 \\ 1 & 1 & 0 \end{bmatrix} \]

5. Resultado final

La matriz resultante representa las zonas de la imagen donde el filtro detectó bordes verticales.

Explicación de cada elemento:

Kernel o filtro:

Los kernels son pequeñas matrices que se utilizan para la extracción de características de datos de entrada, como imágenes. Cada kernel está diseñado para detectar características específicas, lo que permite a la CNN comprender e interpretar el contenido de la imagen. Los valores de los kernels se aprenden durante el proceso de entrenamiento mediante retropropagación , que actualiza los valores del kernel para detectar mejor las características importantes de la imagen. Esto facilita que la CNN realice tareas como la clasificación de imágenes, la detección de objetos y otras tareas de visión artificial, al reconocer patrones de diferentes maneras.

Estrucutura:

Tamaño: Los kernels suelen ser pequeños, como matrices de 3x3, 5x5 o 7x7, lo cual es muy pequeño en comparación con el tamaño de los datos de entrada. El tamaño del kernel afecta la cantidad de datos de entrada que se consideran simultáneamente para cualquier operación de extracción de características.
Profundidad: La profundidad de un kernel depende de la profundidad del volumen de entrada. Por ejemplo, si los datos de entrada son una imagen en color con tres canales de color (RGB), el kernel también tendrá tres canales.
Proceso de aprendizaje: los valores de los kernels no están predeterminados, sino que se aprenden durante el proceso de entrenamiento mediante repropagación y descenso del gradiente, con el fin, de buscar minimizar el erro de la función de pérdida de la red.
Mapa de características: Se utilizan múltiples núcleos en cada capa de una CNN, lo que permite a la red extraer diversas características en cada capa. Las salidas de estos núcleos se pueden apilar para formar la entrada de la siguiente capa, lo que ayuda a crear una jerarquía de características, de simples a complejas, a medida que se profundiza en la red.

Tipos de filtros:

Edge Detection Kernels: están diseñados para resaltar los bordes verticales, horizontales o diagonales de una imagen, algunos de ellos son: Filtro Sobel(Enfoca los píxeles con cambios repentinos de intensidad), Filtro Prewitt(utiliza una ponderación diferente en la matriz para detectar bordes) y filtro Laplaciano(detecta bordes según la segunda derivada de la imagen).
Sharpening Kernels: Mejora los bordes y detalles de una imagen, haciéndola más nítida y definida.
Smoothing (Blurring) Kernels: Los núcleos de suavizado se utilizan para reducir el ruido y los detalles en las imágenes, lo que resulta útil en el preprocesamiento de imágenes antes de extraer características de nivel superior o para eliminar ruido. Núcleos de relieve: Se utilizan para crear un efecto 3D resaltando los bordes y aplicando una sombra al otro lado. Esto facilita el análisis de texturas o la mejora de las imágenes visuales.
Kernels personalizados: Los kernels personalizados aprenden de los propios datos durante el entrenamiento, lo que ayuda a la red a mejorar y aprender las mejores características para la tarea, como reconocer rostros o detectar objetos. El aprendizaje de kernels a partir de los datos permite que la red se adapte a diferentes tareas y mejore su rendimiento con el tiempo.

Número de filtros:

El número de filtros es un hiperparámetro de diseño, lo que significa que el programador o diseñador de la red decide cuantos filtros tendrá cada convolución, aunque un mayor número de filtros permite detectar patrones más variados, el costo computacional aumenta, entonces el número de kernels depende del problema que se desea solucionar, por ejemplo: identificar un rostro en una imagen tiene menos filtros que identificar un rostro de una persona en específico para el sistema de bloqueo del celular.
Los filtros que se suelen depender también del nivel de la red, por ejemplo entre más profunda la red mayor números de filtros, por ejemplo: para la primera capa convolucional se pueden usar 8, 16 o 32, pero para capas más profundas pueden usarse 256, 512 o más.

Tamaño del kernel:

Dependen de lo grande de los patrones que se desean detectar, por ejemplo para patrones pequeños como esquinas o tecturas finas se usan filtros pequeños de 3x3 o 5x5, pero con patronces grandes como formas grandes o regiones amplias se usan filtros de tamaño 7x7 o 11x11. Sin embargo, es usar tomar filtros pequeños para que haya un menor número de parámetros en la siguiente convolución, además con filtros más pequeños hay menor complejidad en el proceso.

Inicialización del kernel:

Se usan técnicas de inicialización estándar de redes neuronales:
Inialización aleatoria normal uniforme: los valores del kernel se eligen de una distribución normal con media 0 y desviación pequeña.
Inicialización Xavier: Muy usada cuando se utiliza la activación tanh o sigmoide. La idea es que la varianza del peso sea: \(Var(w)=\frac{2}{n_{in}+n_{out}}\) y los pesos se generan acorde a esta varianza.
Inicialización He (Kaiming): Es la más usada hoy, especialmente cuando la activación es ReLU. Aquí \(Var(w)=\frac{2}{n_{in}}\) permite que los valores de activación se mantengan estables y no desaparezcan.

Ejemplo: para un kernel 3x3 con 3 canales (RGB) tiene: \(n_{in}=3 \times 3 \times 3=27\). Si se usa He, \(Var(w)=\frac{2}{27}\). Luego, cada entrada del kernel se toma de una normal: \(w \sim N(0,\sqrt{\frac{2}{27}})\).

Stride o tamaño de paso:

Aunque el stride es un hiperparámetro elegido por el diseñador de la red, su selección está influenciada por varios factores técnicos importantes.
Nivel de reducción del tamaño de la imagen - Si se desea mantener la mayor información posible, se usa stride = 1. - Si se desea reducir el tamaño espacial de la imagen, se usa stride = 2. - Strides más grandes (≥ 3) se usan para reducciones agresivas del tamaño.

Nivel de abstracción de la capa - En capas iniciales de la red, se prefiere stride = 1 para conservar detalles finos. - En capas más profundas, se utiliza stride = 2 para reducir dimensionalidad y aumentar el campo receptivo.

Presencia o ausencia de max pooling - Si la arquitectura utiliza max pooling, la convolución suele usar stride = 1, dejando la reducción de tamaño al pooling. - Si no hay pooling, una convolución con stride = 2 puede encargarse de reducir el tamaño de las representaciones.

Costo computacional - Strides pequeños (como 1) aumentan el número de operaciones, ya que producen mapas de características más grandes. - Strides más grandes reducen la complejidad computacional al disminuir la resolución de la salida.

Zero-padding:

suele utilizarse cuando los filtros no se ajustan a la imagen de entrada. Esto pone a cero todos los elementos que quedan fuera de la matriz de entrada, produciendo una salida de mayor o igual tamaño. Existen tres tipos de padding:

  • Valid padding: también conocido como no padding. En este caso, la última convolución se descarta si las dimensiones no se alinean.
  • Same padding: este garantiza que la capa de salida tenga el mismo tamaño que la capa de entrada.
  • Full padding: este tipo de padding aumenta el tamaño de la salida añadiendo ceros al borde de la entrada.

Función de activación

Se aplica una función de activación ReLU después de cada operación de convolución. Esta función ayuda a la red a aprender relaciones no lineales entre las características de la imagen, lo que hace la red más robusta para identificar distintos patrones. También ayuda a mitigar el problema de desvanecimiento de gradiente. En pocas palabras, la función de activación se le aplica a cada elemento del mapa de características resultante de la convolución, esto con el fin de ir desactivando elementos de la matriz que sean negativos e ir “apagando” esa entrada. Dado que, en las convoluciones se usa esta función de activación ReLU porque acelera y estabiliza el entrenamiento al evitar el problema del gradiente desvanecido que sí ocurre con funciones como sigmoide o tanh, ya que ReLU no se satura para valores positivos y mantiene gradientes grandes y estables en redes profundas. Además, es computacionalmente muy eficiente porque solo aplica una operación de máximo, lo cual reduce el costo de calcular activaciones en capas convolucionales que ya son pesadas por sí mismas. Gracias a estas ventajas, ReLU permite que las CNN aprendan más rápido, con mayor estabilidad y con mejor desempeño en tareas de visión.

A continuación se tiene un ejemplo de cómo se aplica la función de activación a un mapa de características.

\[ \begin{bmatrix} -1 & -2 & 1\\ -3 & 5 & 1\\ -1 & 0 & 1 \end{bmatrix} \]

después de aplicar la función ReLu se obtiene lo siguiente:

\[ \begin{bmatrix} 0 & 0 & 1\\ 0 & 5 & 1\\ 0 & 0 & 1 \end{bmatrix} \]

Esta matriz resultante son los datos de entrada para la próxima convolución o capa totalmente conectada (si el mapa es el resultado de la última convolución hecha).

Capa de agrupamiento

El objetivo de la capa de agrupamiento es extraer las características más significativas de la matriz convolucionada. Esto se hace aplicando algunas operaciones de agregación, que reducen la dimensión del mapa de características (matriz convolucionada), reduciendo así la memoria utilizada durante el entrenamiento de la red. El agrupamiento también es relevante para mitigar el sobreajuste.
Las funciones de agregación más comunes que pueden aplicarse son:

  • Max-pooling, que es el valor máximo del mapa de características
  • El sum-pooling es la suma de todos los valores del mapa de características
  • El average-pooling es la media de todos los valores.

El max-pooling es el más usado en redes convolucionales modernas, se usa cuando se necesitan características fuertes como detección de objetos, reconocimiento de imágenes y demás, porque conserva la característica más importante de cada región (bordes, esquinas, texturas). Esto mejora la invariancia a traslaciones y hace que la red sea más robusta. Por otro lado, el sum-pooling se usa en modelos antiguos porque genera valores grandes que cambian la escala del entrenamiento y no aporta mejores representaciones que max o average pooling, se usa en casos donde importa la intensidad total del área analizada. Por último, está el average-pooling que es frecuentemente usado en etapas finales de las redes modernas porque reduce cada mapa de características a un solo valor promedio, eliminando necesidad de capas densas grandes, disminuyendo parámetros y evitando sobreajuste.

Ejemplo de Max Pooling (Capa de Agrupamiento): supongamos que tenemos un mapa de características de tamaño 4 × 4 después de una convolución:

\[ X = \begin{bmatrix} 1 & 3 & 2 & 1 \\ 4 & 6 & 5 & 2 \\ 3 & 2 & 1 & 0 \\ 1 & 2 & 3 & 4 \end{bmatrix} \]

Aplicamos Max Pooling 2×2 con stride = 2.

Cálculo paso a paso

Dividimos la matriz en bloques de \(2 \times 2\) sin traslape:

  1. Primer bloque: \[ \begin{bmatrix} 1 & 3 \\ 4 & 6 \end{bmatrix} \quad \Rightarrow \quad \max = 6 \]

  2. Segundo bloque: \[ \begin{bmatrix} 2 & 1 \\ 5 & 2 \end{bmatrix} \quad \Rightarrow \quad \max = 5 \]

  3. Tercer bloque: \[ \begin{bmatrix} 3 & 2 \\ 1 & 2 \end{bmatrix} \quad \Rightarrow \quad \max = 3 \]

  4. Cuarto bloque: \[ \begin{bmatrix} 1 & 0 \\ 3 & 4 \end{bmatrix} \quad \Rightarrow \quad \max = 4 \]

Resultado final

La salida del Max Pooling es:

\[ \text{MaxPool}(X) = \begin{bmatrix} 6 & 5 \\ 3 & 4 \end{bmatrix} \]

En este caso, el tamaño de paso (stride) y el tamaño del max-pooling no los determina automáticamente la red neuronal, sino que son hiperparámetros fijados por el diseñador del modelo. Es decir, el programador elige cuánto se desplazará el filtro en cada convolución y qué tan grandes serán las ventanas de agrupamiento, dependiendo de cuánta reducción espacial quieras lograr y del nivel de detalle que desees conservar. La red no aprende estos valores; simplemente utiliza los que fueron definidos al construir la arquitectura, es decir, son fijos.

Algunas observaciones que se tienen es que si se aumenta el stride o el tamaño, el filtro se mueve más rápido por la imagen y, como consecuencia, la salida se vuelve más pequeña porque se realizan menos operaciones de convolución. Esto implica que la red pierde detalle espacial, ya que se saltan más píxeles en cada desplazamiento, pero a cambio se reduce el costo computacional y se obtiene una representación más compacta. En resumen, un stride mayor hace que la red sea más rápida y reduzca más la resolución, pero también puede perder información importante si el valor es demasiado grande.

Imagen procesada por filtro

Imagen procesada por filtro

Capas totalmente conectadas

El nombre de la capa totalmente conectada describe con precisión la capa en sí. Como se ha mencionado anteriormente, los valores de píxel de la imagen de entrada no están conectados directamente con la capa de salida en las capas parcialmente conectadas. Sin embargo, en la capa totalmente conectada, cada nodo de la capa de salida sí está conectado directamente a un nodo de la capa anterior.

Después de que una imagen pasa por todas las capas convolucionales y de pooling, se obtiene un conjunto de mapas de características que contienen toda la información visual que la red ha aprendido. Estos mapas siguen teniendo forma de “bloques” tridimensionales (alto × ancho × canales), por lo que antes de enviarlos a una red neuronal tradicional deben transformarse. Aquí ocurre el proceso de aplanado (flattening).

Primero, el aplanado convierte todos esos mapas de características en un único vector largo. Esto significa que la estructura 3D se “desenrolla” fila por fila hasta quedar como una lista de números. Este vector contiene toda la información extraída por las convoluciones, pero ahora en un formato que las capas densas pueden procesar.

Luego de aplanar, ese vector entra en la red neuronal tradicional (por lo general este vector tiene muchos datos, por ejemplo si tenemos después de las convoluciones 256 matrices 7x7, el vector resultante del aplanado tendrá 12.544 valores), también llamada capa totalmente conectada (fully connected layer). En esta parte, cada neurona se conecta con todos los valores del vector. Aquí la red combina las características extraídas para tomar una decisión final. Esta parte funciona igual que una red neuronal clásica: aplica pesos, sumas, funciones de activación (como ReLU, tanh o la que elijas) y va reduciendo la dimensión hasta llegar a la última capa.

Finalmente, llega el output, que depende de la tarea que esté resolviendo la CNN. Si es una clasificación multiclase, la última capa suele usar softmax para producir probabilidades por cada clase; si es una clasificación binaria, puede usar sigmoide para dar un valor entre 0 y 1; y si es un problema de regresión, la salida será un número real. En este punto, la red traduce toda la información procesada en una decisión o predicción concreta.

En la mayoría de CNN modernas, la parte totalmente conectada suele tener entre 1 y 3 capas ocultas. Más de eso casi nunca mejora el rendimiento y aumenta el riesgo de sobreajuste, además de elevar demasiado el número de parámetros.
En la parte totalmente conectada de una red neuronal convolucional, lo más recomendable es usar un número de neuronas que vaya disminuyendo progresivamente en cada capa, ya que esta sección actúa como un clasificador que resume y combina la información extraída por las convoluciones. En la práctica, suele emplearse entre 128 y 512 neuronas en la primera capa densa después del aplanado, ya que esta capa recibe la mayor cantidad de información. Si se agrega una segunda capa oculta, normalmente se utilizan 64 a 128 neuronas, reduciendo la dimensionalidad para evitar sobreajuste y mejorar la generalización. La cantidad final de neuronas depende del número de clases del problema. Este diseño escalonado ayuda a equilibrar capacidad de aprendizaje, eficiencia computacional y estabilidad del entrenamiento.

Backpropagation

El backpropagation en una red neuronal convolucional (CNN) es el proceso mediante el cual la red ajusta sus pesos tanto los de los kernels como los de las capas totalmente conectadas para aprender a reconocer patrones. Aunque la idea general es la misma que en una red neuronal tradicional, en las CNN el cálculo del gradiente tiene particularidades debido a las operaciones de convolución y pooling.

En una CNN, el proceso inicia comparando la salida final de la red con la etiqueta real usando una función de pérdida. A partir de ese error, se calcula el gradiente de la pérdida respecto a la salida y se comienza a “retropropagar” este gradiente capa por capa hacia atrás. En las capas densas, el procedimiento es el mismo que en redes tradicionales: se obtienen los gradientes de los pesos y sesgos y se ajustan con descenso de gradiente.

En las capas convolucionales, el backpropagation debe considerar que un mismo filtro se reutiliza en todas las posiciones de la imagen. Por eso, el gradiente del kernel se obtiene “sumando” la contribución de todos los lugares donde ese kernel se aplicó. Además, se calcula el gradiente correspondiente a las entradas de la convolución para continuar propagando el error hacia capas anteriores. Si se usa stride o padding, estos también influyen en cómo se alinean los gradientes.

Para las capas de max pooling, el backpropagation no distribuye gradientes uniformemente; solo retropropaga el error al valor que fue el máximo en cada región del pooling (los demás reciben gradiente cero). En average pooling, el gradiente se reparte equitativamente entre todos los elementos de la ventana.

Finalmente, después de obtener todos los gradientes, los pesos de los kernels, los sesgos y los parámetros de las capas densas se actualizan mediante un optimizador como SGD o Adam. Así, en cada iteración la CNN mejora su capacidad para extraer características relevantes y clasificar correctamente las imágenes.

Usaremos una red neuronal convolucional muy simple:

  • Imagen de entrada: 4×4
  • Convolución: kernel 2×2, stride 1
  • Activación ReLU
  • Max pooling 2×2, stride 2
  • Aplanado
  • Capa totalmente conectada con 2 neuronas
  • Salida final de 2 clases
  • Salida esperada: clase 2 con mayor probabilidad, es decir la salida esperada es el vector:

\[ y=\begin{bmatrix} 0\\ 1 \end{bmatrix} \]

1.Imagen de entrada (4×4)

\[ X = \begin{bmatrix} 1 & 2 & 0 & 1\\ 3 & 1 & 2 & 2\\ 1 & 0 & 1 & 1\\ 2 & 1 & 0 & 2 \end{bmatrix} \]

2.Kernel de convolución (2×2)

\[ K = \begin{bmatrix} 1 & 0\\ -1 & 1 \end{bmatrix} \]

3.Mapa convolucional (stride = 1)

Se obtienen las siguientes nueve operaciones (ya calculadas):

\[ C = \begin{bmatrix} -1 & 3 & 0\\ 2 & 2 & 2\\ 0 & -1 & 3 \end{bmatrix} \]

4.Aplicación de ReLU

\[ C_{ReLU} = \begin{bmatrix} 0 & 3 & 0\\ 2 & 2 & 2\\ 0 & 0 & 3 \end{bmatrix} \]

5.Max Pooling (2×2, stride 2)

La única ventana considerada es:

\[ \begin{bmatrix} 0 & 3\\ 2 & 2 \end{bmatrix} \Rightarrow 3 \]

Por tanto:

\[ P = [3] \]

6.Aplanado

\[ \text{Flatten}(P) = \begin{bmatrix} 3 \end{bmatrix} \]

7.Capa totalmente conectada (2 neuronas ocultas)

Pesos y bias definidos explícitamente:

\[ W_1 = \begin{bmatrix} 0.5\\ -0.3 \end{bmatrix}, \quad b_1 = \begin{bmatrix} 0.1\\ 0.2 \end{bmatrix} \]

Cálculo:

\[ W_1 \cdot 3 = \begin{bmatrix} 1.5\\ -0.9 \end{bmatrix} \]

\[ W_1 x + b_1 = \begin{bmatrix} 1.5 + 0.1\\ -0.9 + 0.2 \end{bmatrix} = \begin{bmatrix} 1.6\\ -0.7 \end{bmatrix} \]

Aplicamos ReLU:

\[ h = \begin{bmatrix} 1.6\\ 0 \end{bmatrix} \]

8.Capa de salida (2 neuronas)

Pesos y bias:

\[ W_2 = \begin{bmatrix} 1 & -1\\ 0.5 & 0.2 \end{bmatrix}, \quad b_2 = \begin{bmatrix} 0.1\\ -0.2 \end{bmatrix} \]

Multiplicación:

\[ W_2 h = \begin{bmatrix} 1(1.6) + (-1)(0)\\[4pt] 0.5(1.6) + 0.2(0) \end{bmatrix} = \begin{bmatrix} 1.6\\ 0.8 \end{bmatrix} \]

Sumamos bias:

\[ z = \begin{bmatrix} 1.6 + 0.1\\ 0.8 - 0.2 \end{bmatrix} = \begin{bmatrix} 1.7\\ 0.6 \end{bmatrix} \]

9.Softmax final

\[ \hat{y}_1 = \frac{e^{1.7}}{e^{1.7} + e^{0.6}}, \quad \hat{y}_2 = \frac{e^{0.6}}{e^{1.7} + e^{0.6}} \]

\[ \hat{y} \approx \begin{bmatrix} 0.76\\ 0.24 \end{bmatrix} \]

Resultado final

La red predice:

  • Clase 1 con probabilidad 0.76
  • Clase 2 con probabilidad 0.24

Backpropagation

Partimos de los valores del forward:

1) Gradiente de la pérdida respecto a \(z\) (softmax + cross-entropy)

Para cross-entropy con softmax la derivada vectorial es:

\[ \frac{\partial L}{\partial z} = \hat y - y. \]

Numéricamente:

\[ \delta^{(2)} := \frac{\partial L}{\partial z} = \begin{bmatrix}0.76\\[4pt]0.24\end{bmatrix} - \begin{bmatrix}0\\[4pt]1\end{bmatrix} = \begin{bmatrix}0.76\\[4pt]-0.76\end{bmatrix}. \]

2) Gradiente respecto a \(W_2\) (elemento a elemento, con regla de la cadena)

Fórmula general (elemental):

La salida \(z_i = \sum_j W_{2,ij} h_j + b_{2,i}\).
Por la regla de la cadena:

\[ \frac{\partial L}{\partial W_{2,ij}} = \frac{\partial L}{\partial z_i}\cdot\frac{\partial z_i}{\partial W_{2,ij}} = \delta^{(2)}_i \cdot h_j. \]

Calculemos cada entrada:

  • \(i=1,j=1\): \[ \frac{\partial L}{\partial W_{2,11}} = \delta^{(2)}_1 \cdot h_1 = 0.76 \cdot 1.6 = 1.216. \]

  • \(i=1,j=2\): \[ \frac{\partial L}{\partial W_{2,12}} = \delta^{(2)}_1 \cdot h_2 = 0.76 \cdot 0 = 0. \]

  • \(i=2,j=1\): \[ \frac{\partial L}{\partial W_{2,21}} = \delta^{(2)}_2 \cdot h_1 = (-0.76) \cdot 1.6 = -1.216. \]

  • \(i=2,j=2\): \[ \frac{\partial L}{\partial W_{2,22}} = \delta^{(2)}_2 \cdot h_2 = (-0.76)\cdot 0 = 0. \]

Agrupando:

\[ \frac{\partial L}{\partial W_2} = \begin{bmatrix} 1.216 & 0\\[4pt] -1.216 & 0 \end{bmatrix}. \]

3) Gradiente respecto a \(b_2\)

Fórmula elemental:

\[ \frac{\partial L}{\partial b_{2,i}} = \frac{\partial L}{\partial z_i} = \delta^{(2)}_i. \]

Numérico:

\[ \frac{\partial L}{\partial b_2} = \begin{bmatrix}0.76\\[4pt]-0.76\end{bmatrix}. \]

4) Gradiente respecto a la activación oculta \(h\) (cadena elemental)

Queremos \(\partial L / \partial h_j\). Por la regla de la cadena:

\[ \frac{\partial L}{\partial h_j} = \sum_{i} \frac{\partial L}{\partial z_i}\cdot\frac{\partial z_i}{\partial h_j} = \sum_i \delta^{(2)}_i \, W_{2,ij}. \]

Esto es exactamente la multiplicación matricial \(W_2^T \delta^{(2)}\). Calculemos por componentes:

  • Para \(j=1\): \[ \frac{\partial L}{\partial h_1} = \delta^{(2)}_1 W_{2,11} + \delta^{(2)}_2 W_{2,21} = 0.76\cdot 1 + (-0.76)\cdot 0.5 = 0.76 - 0.38 = 0.38. \]

  • Para \(j=2\): \[ \frac{\partial L}{\partial h_2} = \delta^{(2)}_1 W_{2,12} + \delta^{(2)}_2 W_{2,22} = 0.76\cdot(-1) + (-0.76)\cdot 0.2 = -0.76 - 0.152 = -0.912. \]

Por tanto:

\[ \frac{\partial L}{\partial h} = \begin{bmatrix}0.38\\[4pt]-0.912\end{bmatrix}. \]

5)Paso a través de ReLU en la capa oculta (elemental)

La capa oculta aplicó \(h = \text{ReLU}(u)\) con \(u = W_1 x + b_1\).
La derivada elemento a elemento:

\[ \frac{\partial h_j}{\partial u_j} = \begin{cases} 1 & \text{si } u_j > 0,\\ 0 & \text{si } u_j \le 0. \end{cases} \]

En nuestro caso \(u = [1.6,\; -0.7]^T\), por tanto:

\[ \text{ReLU}'(u) = \begin{bmatrix}1\\[4pt]0\end{bmatrix}. \]

Usando la regla de la cadena elemento a elemento:

\[ \delta^{(1)}_j := \frac{\partial L}{\partial u_j} = \frac{\partial L}{\partial h_j}\cdot \frac{\partial h_j}{\partial u_j} = \frac{\partial L}{\partial h_j} \cdot \text{ReLU}'(u_j). \]

Numéricamente:

\[ \delta^{(1)} = \begin{bmatrix} 0.38 \cdot 1\\[4pt] -0.912 \cdot 0 \end{bmatrix} = \begin{bmatrix} 0.38\\[4pt]0 \end{bmatrix}. \]

6)Gradientes para \(W_1\) y \(b_1\) (elemental)

Recordando que \(u_j = W_{1,j} x_{\text{flat}} + b_{1,j}\) (aquí \(W_1\) es columna porque cada neurona oculta tiene un vector de pesos, pero con 1 entrada se ve como escalar por neurona).

Derivada elemento a elemento:

\[ \frac{\partial L}{\partial W_{1,j}} = \frac{\partial L}{\partial u_j} \cdot \frac{\partial u_j}{\partial W_{1,j}} = \delta^{(1)}_j \cdot x_{\text{flat}}. \]

\[ \frac{\partial L}{\partial b_{1,j}} = \delta^{(1)}_j. \]

Numéricamente (con \(x_{\text{flat}}=3\)):

  • Para la neurona 1: \[ \frac{\partial L}{\partial W_{1,1}} = 0.38 \cdot 3 = 1.14,\qquad \frac{\partial L}{\partial b_{1,1}} = 0.38. \]

  • Para la neurona 2: \[ \frac{\partial L}{\partial W_{1,2}} = 0 \cdot 3 = 0,\qquad \frac{\partial L}{\partial b_{1,2}} = 0. \]

Es decir:

\[ \frac{\partial L}{\partial W_1} = \begin{bmatrix}1.14\\[4pt]0\end{bmatrix},\qquad \frac{\partial L}{\partial b_1} = \begin{bmatrix}0.38\\[4pt]0\end{bmatrix}. \]

7)Gradiente respecto a la entrada aplanada (retropropagación hacia el bloque convolucional)

\[ \frac{\partial L}{\partial x_{\text{flat}}} = \sum_j \frac{\partial L}{\partial u_j} \frac{\partial u_j}{\partial x_{\text{flat}}} = \sum_j \delta^{(1)}_j \cdot W_{1,j}. \]

Numéricamente:

\[ \frac{\partial L}{\partial x_{\text{flat}}} = 0.38\cdot 0.5 + 0 \cdot (-0.3) = 0.19. \]

8)Retropropagación por Max Pooling (muy explícito)

El MaxPool tomó la región \(2\times 2\):

\[ R = \begin{bmatrix}0 & 3\\[4pt]2 & 2\end{bmatrix}, \]

y el valor máximo fue \(3\) en la posición \((1,2)\) dentro de esa región. Dado que sólo esa entrada contribuyó al output \(P\), todo el gradiente \(\partial L/\partial x_{\text{flat}} = 0.19\) debe asignarse a la posición que fue el máximo.

Si denoteos \(dP = \partial L/\partial P = 0.19\), entonces la matriz de gradientes sobre la región es:

\[ \frac{\partial L}{\partial R} = \begin{bmatrix}0 & 0.19\\[4pt]0 & 0\end{bmatrix}. \]

Al ubicar esa región en el mapa \(C_{ReLU}\) (es la esquina superior izquierda 2×2 de \(C_{ReLU}\)), la contribución al gradiente del mapa de salida es:

\[ G = \begin{bmatrix} 0 & 0.19 & 0\\[4pt] 0 & 0 & 0\\[4pt] 0 & 0 & 0 \end{bmatrix}. \]

9)Retropropagación a través de ReLU en el mapa convolucional

La relación entre \(C\) (pre-activation conv) y \(C_{ReLU}\) es \(C_{ReLU} = \text{ReLU}(C)\). La derivada es una máscara:

\[ \text{ReLU}'(C) = \begin{bmatrix} 0 & 1 & 0\\[4pt] 1 & 1 & 1\\[4pt] 0 & 0 & 1 \end{bmatrix}\quad(\text{1 donde } C>0). \]

Por regla de la cadena elemento a elemento:

\[ \frac{\partial L}{\partial C} = G \odot \text{ReLU}'(C) = \begin{bmatrix} 0 & 0.19 & 0\\[4pt] 0 & 0 & 0\\[4pt] 0 & 0 & 0 \end{bmatrix} \odot \begin{bmatrix} 0 & 1 & 0\\[4pt] 1 & 1 & 1\\[4pt] 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 0 & 0.19 & 0\\[4pt] 0 & 0 & 0\\[4pt] 0 & 0 & 0 \end{bmatrix}. \]

(Se mantienen ceros fuera de la posición que nos interesa.)

10)Gradiente respecto al kernel \(K\) (cadena completa y elemental)

La salida de la operación de convolución en la posición \((i,j)\) es:

\[ C_{ij} = \sum_{a=1}^{2}\sum_{b=1}^{2} K_{ab}\cdot X_{i+a-1,\; j+b-1}. \]

Por la regla de la cadena:

\[ \frac{\partial L}{\partial K_{ab}} = \sum_{i,j} \frac{\partial L}{\partial C_{ij}} \cdot \frac{\partial C_{ij}}{\partial K_{ab}} = \sum_{i,j} \left(\frac{\partial L}{\partial C_{ij}}\right) \cdot X_{i+a-1,\; j+b-1}. \]

En nuestro caso solo \(\partial L / \partial C_{1,2} = 0.19\) es distinto de cero, por lo que la suma queda reducida a la ventana que generó \(C_{1,2}\).

La ventana de entrada correspondiente (la que usamos en el forward para producir \(C_{1,2}=3\)) fue:

\[ X_{\text{patch}} = \begin{bmatrix} 2 & 0\\[4pt] 1 & 2 \end{bmatrix} \]

Por tanto:

\[ \frac{\partial L}{\partial K} = 0.19 \cdot \begin{bmatrix} 2 & 0\\[4pt] 1 & 2 \end{bmatrix} = \begin{bmatrix} 0.38 & 0\\[4pt] 0.19 & 0.38 \end{bmatrix}. \]

Resumen final (todos los gradientes explícitos)

  • \(\displaystyle \frac{\partial L}{\partial z} = \delta^{(2)} = \begin{bmatrix}0.76\\[4pt]-0.76\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial W_2} = \begin{bmatrix}1.216 & 0\\[4pt]-1.216 & 0\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial b_2} = \begin{bmatrix}0.76\\[4pt]-0.76\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial h} = \begin{bmatrix}0.38\\[4pt]-0.912\end{bmatrix}\)

  • \(\displaystyle \delta^{(1)} = \frac{\partial L}{\partial u} = \begin{bmatrix}0.38\\[4pt]0\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial W_1} = \begin{bmatrix}1.14\\[4pt]0\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial b_1} = \begin{bmatrix}0.38\\[4pt]0\end{bmatrix}\)

  • \(\displaystyle \frac{\partial L}{\partial x_{\text{flat}}} = 0.19\)

  • \(\displaystyle \frac{\partial L}{\partial K} = \begin{bmatrix}0.38 & 0\\[4pt]0.19 & 0.38\end{bmatrix}\)