\require{cancel} Retomemos nuevamente «Neural network from Scratch», hoy hablaremos sobre las redes neuronales convolucionales (Convolutional Neural Network).

¿Que són las Convolutional Neural Network?

Como visteis en anteriores tutoriales las redes MLP tienen muchísimos parámetros (demasiados) por lo que aun ser una red neuronal muy potente, aprender todos esos parámetros supone un coste muy elevado y muchas veces es imposible ponerlo en la practica.
Las redes neuronales convolucionales surgen de esa necesidad, de aportar una alternativa viable a problemas de clasificación o regresión, eliminando esa inmensidad de parámetros pero aplicando una añadiendo una restricción: Buscaremos un patrón que se encuentra en un dominio temporal o espacial de la señal de entrada. ¿Que quiere decir eso? Que estamos buscando un patrón en la entrada que puede estar en diferente/s posiciones de la señal de entrada. Eso nos va a permitir usar una y otra vez los mismos parámetros pero buscando en distintos sitios de la señal (a eso se le conoce como correlación).
Existen distintas capas en las redes CNN, aunque en este caso nos centraremos en las convoluciones.

¿Que es una correlación?

Una correlación es una medida de similaridad que compara una señal A con otra B en distintos puntos de la señal A. La comparación entre ambas se hace con lo que se conoce como producto de señales, donde el valor más alto de la correlación será el punto donde más similares sean (este proceso es el mismo que se usa en la transformada de fourier, solo que él prueba infinitos armónicos).
corr(A, B)(x) = \int_{k =-\infty}^{\infty} A(w + x)\cdot B(k) Supongamos las siguientes funciones A y B:

  • Se desplaza B por A y se compara.
  • La comparación es la suma del producto de señales: cmp(A, B) = \sum_k A(k) \cdot B_{desplazada}(k)
  • El valor máximo se dará donde las señales sean mas similares, ideal para buscar patrones.

Si estáis familiarizados con las redes CNN sabréis que actualmente se usan muchísimo para problemas que involucran imágenes.
Una imagen no es más que una señal 2D. Los problemas habituales de las imágenes es buscar algún patron/objeto en ellas, estos pueden estar en diferentes tamaños y posiciones de modo que las correlaciones son el elemento perfecto para este trabajo. En este tipo de redes, estas capas se conocen como capas de convolución, en aspectos técnicos el nombre no es del todo correcto ya que se deberían llamar capas de correlación. Pero se debe a que la correlación y la convolución tienen una relación y se puede aplicar una convolución usando una correlación o viceversa.
¿Como funciona cuando tenemos 2 dimensiones? Pues realmente funciona exactamente igual (más adelante lo veremos exactamente), se tiene una señal A que es nuestra imagen de entrada y otra señal B que es el filtro o patrón que estamos buscando (color rojo).

Nuestra red será la encargada de buscar aquellos patrones (señales B) que nos permitirán identificar ciertos patrones en la imagen.

Vale muy bonito pero… ¿Y ya esta?

No, la gracia no es solo buscar un filtro, sino aplicar el concepto anterior a lo GRANDE.
En vez de aplicar un solo filtro a la imagen aplicaremos varios a la vez. Y después volveremos aplicar otros, luego otros y otros, … Aquí es donde reside la potencia de estas redes en la concatenación de capas que detectarán ciertos patrones. Fijaros en la siguiente red CNN (FC son capas como fully-connected las que usábamos en MLP):

Podemos observar como hay distintas capas de convolución, cada nivel de este conjunto de capas será capaz de detectar ciertos patrones.

  • Las primeras detectarán patrones muy simples (esquinas, lineas, cambios de contraste, etc).
  • Las intermedias detectarán combinaciones de las anteriores siendo capaces de detectar (cuadrados, esferas, triángulos, formas geométricas).
  • Las mas profundas serán capaces de detectar los patrones mas complejos (caras, objetos, gatos, más gatos, gatos negros, gatos blancos, gatos lilas)…

Es decir, cada capa de nuestra red convolucional supone un nivel de atracción mas alto. En el siguiente vídeo se puede ver lo que ve exactamente cada capa de nuestra red (muy interesante):

Profundicemos en la «capa de convolucion»

Ahora que ya tenemos algunos conocimientos sobre que es una convolución vamos a entrar más en detalle. Las capas de convolución en las redes CNN para imagenes se desplazan en 2 dimensiones pero trabajan en 3 dimensiones.

Se que parece confuso, pero es muy sencillo. Los filtros son realmente cubos de tamaño (ancho filtro, alto filtro, tamaño entrada), estos solo se mueven en 2 dimensiones (arriba-abajo, izquierda-derecha) como la animación anterior (si se mueven en 3 dimensiones se conoce como convolucion 3d). Todos los valores que quedan dentro del cubo en la entrada se multiplican por los pesos del filtro y luego se suman dándonos un valor (o coeficiente de similitud respecto al patrón que hay en el filtro).
o_{x,y} = \sum_{m=-\frac{wk}{2}}^{\frac{wk}{2}} \sum_{n=\frac{-hk}{2}}^{\frac{hk}{2}} \sum_p i_{x+m, y+n, p} \cdot k_{p,m,n} donde i es la entrada y k es el filtro que tiene el 0,0 en el centro del mismo.

Veamos un ejemplo con un solo filtro:

  • Tengo una imagen: 10x10x3.
  • Aplico un filtro: 3×3
  • Obtengo una salida: 8x8x1

Aplicar un solo filtro no es muy potente y probablemente querramos aplicar muchos más a la vez. La capa de convolución se define como una lista de filtros todos del mismo tamaño y ancho, de forma que todos se aplican a la vez.
o_{x,y,d} = \sum_{m=-\frac{wk}{2}}^{\frac{wk}{2}} \sum_{n=\frac{-hk}{2}}^{\frac{hk}{2}} \sum_p i_{x+m, y+n, p} \cdot k_{p,m,n,d} Si volvemos al anterior ejemplo:

  • Tengo una imagen: 10x10x3.
  • Aplico una capa de convolucion de 96 filtros de 3×3
  • Obtengo una salida: 8x8x96.

Fijaros que aplicar un filtro siempre nos supondrá perder dimensiones en el alto y ancho de la entrada. Eso se debe a que sino el filtro sobresaldría fuera de la imagen (nos iríamos a indices inferiores a 0 o superiores a los de la imagen).

Más detalles sobre los filtros…

En los filtros existen tres parámetros más que aún no hemos comentado:

  • Padding.
  • Stride.
  • Bias.

El padding se encarga de controlar los limites entre la entrada y el filtro, existen dos configuraciones posibles:

  • Valido (valid): Solo se calculan los pixeles en los que el filtro y la entrada encajan.
  • Igual (same): Se calculan mientras el pixel central del filtro este dentro de la entrada, esto da como resultado una salida igual que la entrada. Los pixeles que no existen se rellenan con 0’s.

Es stride es muy sencillo, va a controlar el incremento en el desplazamiento. Por lo general es 1, pero si un stride por ejemplo es: 2, querrá decir que un pixel se evalua, otro no.

 

El stride es útil cuando queremos hacer una disminución de la dimensionalidad a lo bruto, suponemos que perder un punto de cada dos no nos supondrá perder información vital.
El bias se añade por cada filtro, dejando la ecuación de la correlación de la siguiente forma:
o_{x,y,d} = \sum_{m=-\frac{wk}{2}}^{\frac{wk}{2}} \sum_{n=\frac{-hk}{2}}^{\frac{hk}{2}} \sum_p i_{x+m, y+n, p} \cdot k_{p,m,n,d} + b_d

Con tantos parámetros… ¿Como calculamos el tamaño de la salida?
Si el padding es valido, solamente debemos aplicar la siguiente ecuación (sx y sy son el stride horizontal y vertical):
wo = \frac{wi - wk}{sx + 1} ho = \frac{hi - hk}{sy + 1} Si el padding es same, querrá decir que la debemos forzar que la salida sea igual que la entrada por lo tanto ambas serán iguales.

Programando el forward

Dicho todo esto… ¿Como queda resumido todos estos conceptos en el código?
Primero de todo se añaden el padding necesario si el método es «same».
Después para simplificar el algoritmo, es más fácil recorrer la imagen de salida e ir calculando el producto y la acumulación entre la entrada y los filtros.

Programando el backward:

Como siempre las cosas se complican un poco en el backward. En la convolución tendremos que derivar respecto los pesos internos: kernel y bias; y respecto la entrada: input. Los gráficos que se mostrarán a continuación son para un solo filtro y un ejemplo en concreto, perdonar que no sean de mejor calidad.
Primero recordemos como se aplica el filtro:

Basado en: https://becominghuman.ai/back-propagation-in-convolutional-neural-networks-intuition-and-code-714ef1c38199

Derivada respecto los pesos (k):

Derivar respecto K, supone de derivar cada salida o respecto cada filtro k. Realmente estamos computando la matriz jacobiana de la función o_{x, y} con respecto k, en formato matricial el resultado seria el siguiente:
\frac{\partial L}{\partial k} = \frac{\partial L}{\partial o} J_{o_k} Veamos el ejemplo anterior en detalle:

Como podéis ver \frac{\partial o_{x,y}}{\partial k_{m,n}} tiene múltiples valores, acordaros que la derivada es la suma de todas estas contribuciones.

Si desarrollamos las ecuaciones obtenemos:
\frac{\partial L}{\partial k_{0,0}} =\frac{\partial L}{\partial o_{0,0}} i_{0, 0} + \frac{\partial L}{\partial o_{0,1}} i_{0, 1} + \frac{\partial L}{\partial o_{1,0}} i_{1, 0} + \frac{\partial L}{\partial o_{1,1}} i_{1, 1} \frac{\partial L}{\partial k_{0,1}} =\frac{\partial L}{\partial o_{0,0}} i_{0, 1} + \frac{\partial L}{\partial o_{0,1}} i_{0, 2} + \frac{\partial L}{\partial o_{1,0}} i_{1, 1} + \frac{\partial L}{\partial o_{1,1}} i_{1, 2} \frac{\partial L}{\partial k_{1,0}} =\frac{\partial L}{\partial o_{0,0}} i_{1, 0} + \frac{\partial L}{\partial o_{0,1}} i_{1, 1} + \frac{\partial L}{\partial o_{1,0}} i_{2, 0} + \frac{\partial L}{\partial o_{1,1}} i_{2, 1} \frac{\partial L}{\partial k_{1,1}} =\frac{\partial L}{\partial o_{0,0}} i_{1, 1} + \frac{\partial L}{\partial o_{0,1}} i_{1, 2} + \frac{\partial L}{\partial o_{1,0}} i_{2, 1} + \frac{\partial L}{\partial o_{1,1}} i_{2, 2} Cuesta un poco ver el patrón, pero simplemente deberemos recorrer como en el forward e ir acumulando los productos entre \frac{\partial L}{\partial o{m,n}} y su entrada i_{x, y} asociada.

Derivada respecto la entrada (i):

Este caso, es similar al anterior pero esta vez deberemos derivar respecto la entrada i, la derivada de la salida respecto la entrada corresponderán a la posición del filtro en el que ha participado la entrada i.

Derivada respecto el bías (b):

En el caso del bias, más simple no podía ser. El bias es un único valor que se suma a la convolución.
\frac{\partial o_{x,y,d}}{\partial b_k} = \cancel{ \sum_{m=-\frac{wk}{2}}^{\frac{wk}{2}} \sum_{n=\frac{-hk}{2}}^{\frac{hk}{2}} \sum_p i_{x+m, y+n, p} \cdot k_{p,m,n,d} } + 1= 1 \frac{\partial L}{\partial b_k} = \sum_x \sum_y \frac{\partial L}{\partial o_{x,y,d}}\frac{\partial o_{x,y,d}}{\partial b_k} = \sum_x \sum_y \frac{\partial L}{\partial o_{x,y,d}}

 

Juntando las piezas

El resultado final se ha separado en dos codigos distintos ya que python no es el lenguaje que mejor procesa los bucles. Lo separaremos en Python que hará de wrapper y Cython que hará los calculos.

Codigo Python:

 

Codigo Cython:

En este articulo hemos visto la punta del iceberg de las redes convolucionales. En los siguientes artículos veremos las otras capas que también nos son útiles en las redes CNN.

Referencias:

 

Deja un comentario