[mathjax] En este articulo hablaremos sobre como debemos abordar ciertos aspectos de la red para que luego en los próximos artículos el forward y backward de nuestra red neuronal sea funcional. Como sabeis en programaciòn antes de empezar a implementar es aconsejable definir las estructuras para poder manejar todo estos datos.
Por ello usaremos un sencillo diagrama de clases que nos puede aproximar como acabará siendo el proyecto:

Diagrama clases red.


Podemos observar que la red contiene nodos y a su vez esos nodos tienen una capa asociada.
Para algunas de las capas más representativas de una NN, podríamos ver el siguiente diagrama:

UML de las capas.

 
Vamos a definir una clase capa, que tendrá funciones que usaremos (aunque en el futuro probablemente debamos añadir algunas más funcionalidades).

En la clase genérica Layer encontramos distintos métodos interesantes:

  • El constructor crea una nueva capa define:
    • Al que nodo pertenece.
    • Inicializa el puntero de los pesos y los valores intermedios, pero no carga nada.
    • Carga los parámetros.
  • La copia simplemente copia una capa a otro nodo.
  • El método computeSize se encarga de calcular el tamaño de salida.
  • El método compile se encarga de generar las estructuras de datos que necesita la capa, normalmente son pesos.
  • El método forward usa la capa y envía el resultado.
  • El método derivatives, calcula la derivada respecto a la entrada y opcionalmente si tiene pesos la derivada por cada peso que tiene la capa. La propagación de esto, se encargará el propio nodo.

Compilación:

El proceso de compilación empieza en la clase red (Network) y se encarga de realizar distintos pasos tanto para asegurarse que la red se podrá ejecutar como preparar las estructuras necesarias (lo comentamos en el anterior post).

La compilación recibe como parámetros, las losses que se encargan de minimizar el error, las métricas adicionales y finalmente el optimizador (hablaremos más adelante, por ahora podríamos decir que es el encargado de actualizar el peso, en el caso de un descenso de gradiente puro es solamente el learning rate $\mathbf{t}$ en $W = W – \mathbf{t} \frac{\partial L} {\partial W}$ ).
Internamente la compilación realiza:

  • Limpia los valores temporales de los nodos.
  • Determina el tipo de nodo que es (Entrada, salida o intermedio en la red).
  • Realiza unos tests que puede ejecutarse antes de la ejecución como por ejemplo evitar bucles en el grafo.
  • Calcula constantes que se usaran a lo largo de todo el aprendizaje.
  • Calcula todos los tamaños.
  • Compila las capas (Guarda espacio para los pesos, estructuras temporales, etc).
  • Se introducen los pesos de cada loss o se ponen pesos por defecto iguales para todas.

Uno de los pasos es calcular los tamaños que entran y salen en cada capa. El funcionamiento para calcularlos es similar al forward y se inicia en Network. Con esto queremos evitar inconsistencias entre los tamaños, futuros problemas en los cálculos y alertar de que hay un problema en la red al usuario.

Por cada nodo borramos sus tamaños y procedemos a calcularlos. En el Node el proceso de calculo de tamaños es exactamente igual al forward (que explicamos en el primer articulo) pero esta vez en vez de realizar la función del nodo, calculamos los tamaños que entrarán y saldrán de esa capa en concreto.
En cada nodo realizaremos las siguientes operaciones:

La función computeSize es la encargada de calcular tanto los tamaños de entrada (que pueden ser varios) y la salida (que solo puede ser una única salida que puede ir a distintos nodos). Y luego propagar el calculo hacia los posteriores nodos.
La función checkComputeSizeDependences impide realizar el calculo sin antes no haber calculado los tamaños de los nodos del que ese nodo depende. En la siguiente figura C, debe esperar a A y B.

C depende de A y B para calcular su tamaño.

 
Una vez hemos calculado los tamaños podemos realizar comprobaciones interesantes dependiendo de la capa en la que estemos trabajando. Por ejemplo podemos forzar a que todos los tamaños de entrada sean iguales y si no devolver un error.

En el próximo articulo realizaremos el forward de los nodos.

Referencias:

Deja un comentario