Deep LearningScratch Network

Neural network from Scratch: Convolution Neural Network: Pooling (Parte 10)

By 03/11/2018 febrero 23rd, 2020 No Comments

En este post seguiremos donde lo dejamos sobre las Convolutional Neural Networks. Si os acordáis del anterior tema, nos centramos en el funcionamiento de la capa más importante: la Convolucion.
¿Pero eso es todo? ¿Solo existe esta famosa capa en las CNN? No, otra de las capas que también se usa mucho junto a la convolución es la capa de pooling.

Capa de Pooling:

La capa de pooling es una capa bastante sencilla y está la vamos a encontrar prácticamente en todas las arquitecturas de CNN. Esta capa, actuá de la misma forma que lo hace la convolución (cogiendo trozos de la entrada) pero en este caso realiza una operación que no tiene parámetros (siempre es la misma operación).  Por lo tanto no hay que aprender ningún peso a corregir.
Veamos la siguiente figura para ver como actúa:
Como veis por ahora hace un mecanismo muy similar a la capa de convolución… ¿Entonces que la hace distinta? La forma en la que mezcla los datos y en que no aprende ningún peso.
Existen básicamente dos formas distintas en las que encontraremos esta capa:

  • Max-pooling: En este caso la operación que se realiza será la de coger el máximo.
  • Avg-pooling: En este caso la operación que se realiza será la media.

En el caso del max-pooling, solo deberemos coger el valor más elevado.
y = max(x)

En el caso del avg-pooling, tendremos que hacer la media de todos los valores del recuadro.
y = \frac{1}{N} \sum_i x_i

 
¿Que beneficios tiene la capa de pooling?

  • Aporta invariancia a traslación a la capa de Convolución.
  • Es muy rápida de computar.
  • Reduce la dimensionalidad de la entrada.

¿Las operaciones son bastantes simples no? Veamos que diferencias nos puede ofrecer cada una de estas operaciones.

Max-pooling Avg-pooling
  • Extrae características concretas.
  • Aumenta la «sparsidad» de la red, solo la neurona máxima va a ser la seleccionada.
  • Como consecuencia solo la neurona seleccionada será la que sea corregida mediante backprogragation.
  • No es lineal.
  • Extrae características globales y reduce la varianza.
  • Contribuyen todos los valores de la ventana, ofrece una solución no tan brusca y más relajada.
  • Todas las neuronas serán corregidas.
  • Es lineal.

 
Hasta aquí podríamos pensar que es bastante simple, pues bien la implementación no lo es ya tanto… Trabajar con algoritmos que aplican una «sliding windows» acostumbra a ser sinónimo de complejidad. Eso se debe a que como estas ventanas se solapan entre sí existen neuronas que pueden ser contribuir en más de una ventana. Provocando que la derivada sea la suma de todas estas contribuciones.

Max-pooling:

En este apartado nos vamos a centrar en el forward y backward de la operación de max-pooling.
El forward es bastante simple, pero no nos es suficiente con coger el valor máximo porque de ser así ¿Que neurona es la que deberemos corregir en el backward?
Veamos en pseudo-código y para una ventana en concreto:

En el caso del forward:

Vamos a hacer dos cosas:

  • Poner en la salida (y) el valor máximo.
  • Guardarnos la posición de esa neurona para que podamos corregir los pesos después.

En el caso del backward:

Volveremos a usar la posición de la neurona activada y solo le propagaremos las correcciones a esta.
Recordar que la derivada del máximo (y = max(x)) es:
 
\frac{\partial y}{\partial x_i} = \begin{cases}<br /> 0, & si\ i \neq argmax(x) \\<br /> 1, & si\ i = argmax(x)<br /> \end{cases}  

Avg-pooling:

En este apartado nos vamos a centrar en el forward y backward de la operación de avg-pooling.
En este caso todas las neuronas contribuyen por igual.

En el caso del forward:

Solamente deberemos realizar la media de todos los datos.

En el caso del backward:

El gradiente pasará por todos los datos, lo único que hay que tener en cuenta es que la derivada de y = \frac{1}{N} \sum_i x_i respecto x_i es \frac{1}{N}.

Código completo:

Bien ahora es hora de coger todas las anteriores ideas y pasarlas al codigo que nos permitirá usarlo en nuestro framework. Por un tema de optimización, vamos a separar el código en Python y Cython. Como siempre la parte Python hará de wrapper y Cython será el codigo compilado de forma que nos irá «algo» más rápido.
Python:

Cython:

Y hasta aquí ha sido todo, con esto cerramos la base de las redes convolucionales.
Espero que os haya gustado este post 😉

Deja un comentario