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.
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.
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
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.
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:
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.
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.
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.
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.
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.
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.
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}})\).
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.
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:
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).
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:
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.
Dividimos la matriz en bloques de \(2 \times 2\) sin traslape:
Primer bloque: \[ \begin{bmatrix} 1 & 3 \\ 4 & 6 \end{bmatrix} \quad \Rightarrow \quad \max = 6 \]
Segundo bloque: \[ \begin{bmatrix} 2 & 1 \\ 5 & 2 \end{bmatrix} \quad \Rightarrow \quad \max = 5 \]
Tercer bloque: \[ \begin{bmatrix} 3 & 2 \\ 1 & 2 \end{bmatrix} \quad \Rightarrow \quad \max = 3 \]
Cuarto bloque: \[ \begin{bmatrix} 1 & 0 \\ 3 & 4 \end{bmatrix} \quad \Rightarrow \quad \max = 4 \]
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
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.
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:
\[ y=\begin{bmatrix} 0\\ 1 \end{bmatrix} \]
\[ X = \begin{bmatrix} 1 & 2 & 0 & 1\\ 3 & 1 & 2 & 2\\ 1 & 0 & 1 & 1\\ 2 & 1 & 0 & 2 \end{bmatrix} \]
\[ K = \begin{bmatrix} 1 & 0\\ -1 & 1 \end{bmatrix} \]
Se obtienen las siguientes nueve operaciones (ya calculadas):
\[ C = \begin{bmatrix} -1 & 3 & 0\\ 2 & 2 & 2\\ 0 & -1 & 3 \end{bmatrix} \]
\[ C_{ReLU} = \begin{bmatrix} 0 & 3 & 0\\ 2 & 2 & 2\\ 0 & 0 & 3 \end{bmatrix} \]
La única ventana considerada es:
\[ \begin{bmatrix} 0 & 3\\ 2 & 2 \end{bmatrix} \Rightarrow 3 \]
Por tanto:
\[ P = [3] \]
\[ \text{Flatten}(P) = \begin{bmatrix} 3 \end{bmatrix} \]
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} \]
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} \]
\[ \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} \]
La red predice:
Partimos de los valores del forward:
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}. \]
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}. \]
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}. \]
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}. \]
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}. \]
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}. \]
\[ \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. \]
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}. \]
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.)
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}. \]
\(\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}\)