« Algoritmos de hormiga… « || Inicio || » Optimización en el es… »

Redes Neuronales: una visión superficial

Última modificación: 16 de Marzo de 2022, y ha tenido 39856 vistas

Etiquetas utilizadas: || || ||

Una Red Neuronal Artificial (RNA) es un modelo matemático inspirado en el comportamiento biológico de las neuronas y en cómo se organizan formando la estructura del cerebro. El cerebro puede considerarse un sistema altamente complejo, donde se calcula que hay aproximadamente 100 mil millones (\(10^{11}\)) neuronas en la corteza cerebral (humana) y que forman un entramado de más de 500 billones de conexiones neuronales (una neurona puede llegar a tener 100 mil conexiones, aunque la media se sitúa entre 5000 y 10000 conexiones).

Respecto a su funcionamiento, el cerebro puede ser visto como un sistema inteligente que lleva a cabo tareas de manera distinta a como lo hacen las computadoras actuales. Si bien estas últimas son muy rápidas en el procesamiento de la información, existen tareas muy complejas, como el reconocimiento y clasificación de patrones, que demandan demasiado tiempo y esfuerzo aún en las computadoras más potentes de la actualidad, pero que el cerebro humano es más apto para resolverlas, muchas veces sin aparente esfuerzo (por ejemplo, el reconocimiento de un rostro familiar entre una multitud de rostros).

Si bien hay distintos tipos de neuronas biológicas, la imagen de la izquierda muestra un esquema simplificado del tipo más común, y donde podemos reconocer diferentes partes:

  • El cuerpo central, llamado soma, que contiene el núcleo celular.
  • Una prolongación del soma, el axón.
  • Un conjunto de ramificaciones terminales, las dendritas.
  • Zonas de conexión entre una neurona y otra, conocidas como sinapsis.

La función principal de las neuronas es la transmisión de impulsos nerviosos. Estos viajan por toda la neurona comenzando por las dendritas hasta llegar a las terminaciones del axón, donde pasan a otra neurona por medio de la conexión sináptica.

La manera en que respondemos ante los estímulos del mundo exterior, y el aprendizaje que podemos realizar del mismo, está directamente relacionado con las conexiones neuronales del cerebro, y las RNAs son un intento de emular este hecho.

Modelo neuronal de McCulloch-Pitts

El primer modelo matemático de una neurona artificial, creado con el fin de llevar a cabo tareas simples, fue presentado en el año 1943 en un trabajo conjunto entre el psiquiatra y neuroanatomista Warren McCulloch y el matemático Walter Pitts.

La siguiente figura muestra un ejemplo de modelo neuronal con \(n\) entradas, que consta de:

  • Un conjunto de entradas \(x_1,\dots x_n\).
  • Los pesos sinápticos \(w_1,\dots w_n\), correspondientes a cada entrada.
  • Una función de agregación, \(\Sigma\).
  • Una función de activación, \(f\).
  • Una salida, \(Y\).

Las entradas son el estímulo que la neurona artificial recibe del entorno que la rodea, y la salida es la respuesta a tal estímulo. La neurona puede adaptarse al medio circundante y aprender de él modificando el valor de sus pesos sinápticos, y por ello son conocidos como los parámetros libres del modelo, ya que pueden ser modificados y adaptados para realizar una tarea determinada.

En este modelo, la salida neuronal \(Y\) está dada por:

\[Y = f(\sum_{i=1}^n w_i x_i)\]

La función de activación se elige de acuerdo a la tarea realizada por la neurona. Entre las más comunes dentro del campo de las RNAs podemos destacar (extraído de aquí):

El Perceptron como clasificador en el plano

Aplicaremos el modelo neuronal de la sección anterior para realizar tareas de clasificación en el plano (por lo que solo haremos uso de dos entradas, \(x_1\) y \(x_2\)). Para ello, vamos a considerar que existe una entrada de peso constante 1, y valor de entrada \(b\) y consideraremos como función de activación a la función signo definida por:

$$\Gamma(s) =\left\{\begin{array}{cl} 1, & \mbox{si } s \geq 0 \\ -1, & \mbox{si } s < 0 \end{array} \right.$$

Por lo tanto, la salida neuronal \(Y\) estará dada en este caso por:

$$Y =\left\{ \begin{array}{cl} 1, & \mbox{si } w_1 x_1 + w_2 x_2 + b \geq 0 \\ -1, & \mbox{si } w_1 x_1 + w_2 x_2 + b < 0 \end{array} \right.$$

Supongamos que tenemos dos clases en el plano: la clase \(C_1\), formada por los círculos rojos, y la clase \(C_2\), formada por los círculos azules, donde cada elemento de estas clases está representado por un punto \((x,y)\) en el plano. Supondremos además que tales clases son separables linealmente, es decir, es posible trazar una recta que separe estrictamente ambas clases.

Diremos que la neurona artificial clasifica correctamente las clases \(C_1\) y \(C_2\) si dados los pesos sinápticos \(w_1\) y \(w_2\) y el término aditivo \(b\), la recta con ecuación

$$y = -\frac{w_1}{w_2} x - \frac{b}{w_2}$$

es una recta que separa las dos clases. La ecuación implícita de la recta es \(w_1x + w_2y + b = 0\). Obsérvese que si el punto \((x_0,y_0) \in C_1\), entonces \(w_1x_0+ w_2y_0+ b < 0\) y si \((x_0,y_0) \in C_2\), entonces \(w_1x_0+ w_2y_0+ b > 0\). Por lo tanto, dado el par \((x_0,y_0) \in C_1\cup C_2\), la neurona clasifica de la siguiente manera:

$$(x_0,y_0) \in C_1 \Leftrightarrow Y = -1$$

$$(x_0,y_0) \in C_2 \Leftrightarrow Y = 1$$

Si ahora tomamos dos clases \(C^*_1\) y \(C^∗_2\) (separables linealmente) distintas a las anteriores, entonces la neurona puede no clasificar correctamente a estas clases, pues la recta anterior puede no ser una recta válida para separarlas. Sin embargo, es posible modificar los parámetros anteriores y obtener nuevos parámetros \(w^*_1, w^*_2\) y \(b^*\) de forma que la recta \(y = -\frac{w^*_1}{w^*_2} x - \frac{b^*}{w^*_2}\) sí sirva como recta de separación entre ellos. El proceso por el cual la neurona pasa de los parámetros \(w_1, w_2, b\) a los parámetros \(w^*_1, w^*_2, b^*\) se conoce como aprendizaje. Este proceso es el que permite modificar los parámetros libres con el fin de que la neurona se adapte y sea capaz de realizar diversas tareas.

El método de aprendizaje que detallaremos a continuación y que utilizaremos para adaptar los parámetros libres con el fin de clasificar correctamente las clases \(C_1\) y \(C_2\) se conoce como método de error-corrección. Para aplicarlo es necesario:

  • Un conjunto de entrenamiento, \(D\).
  • Un instructor.
  • Valores iniciales, \(w'_1, w'_2, b'\), arbitrarios de los parámetros libres.

El conjunto de entrenamiento es definido por \(D = C_1\cup C_2\). El entrenamiento consiste en lo siguiente:

  • El instructor toma un elemento \((x_0,y_0) \in D\) al azar y presenta éste a la neurona.
  • Si la neurona clasifica mal este punto, es decir, si la salida de la neurona es \(Y = -1\) cuando \((x_0,y_0) \in C_2\) o \(Y = 1\) cuando \((x_0,y_0) \in C_1\), entonces se aplica la siguiente corrección a los parámetros libres iniciales

$$w_1= w'_1+ d x_0$$

$$w_2= w'_2+ d y_0$$

$$b = b'+ d$$

donde el valor de \(d\) se obtiene de la siguiente manera:

$$d =\left\{ \begin{array}{cl} 1 & \mbox{si } Y=-1 \mbox{ y } (x_0,y_0)\in C_2 \\ -1 & \mbox{si } Y=1 \mbox{ y } (X_0,y_0)\in C_1 \end{array} \right.$$

  • Si la neurona clasifica bien el punto \((x_0,y_0)\), entonces no se realiza ninguna corrección.
  • El procedimiento se repite pasando a la neurona otro punto del conjunto \(D\) y usando los últimos parámetros \(w_1 , w_2, b\) corregidos (no los iniciales). Nuevamente, si la neurona clasifica mal el punto, entonces se aplica una corrección similar a la anterior.
  • Esta tarea se repite con todos los puntos del conjunto \(D\). Si en el proceso hubo correcciones, entonces el procedimiento es repetido nuevamente con todos los puntos de \(D\).
  • El entrenamiento termina cuando la neurona clasifica correctamente todos los elementos del conjunto de entrenamiento.

Si los conjuntos son separables, este procedimiento converge, es decir, en un número finito de pasos es posible obtener los parámetros finales que separan entre sí los conjuntos. Si no lo son, entonces oscila entre valores de parámetros que separan lo mejor posible los conjuntos, cometiendo errores ya que no hay solución perfecta en este caso.

Aplicación de las Redes Neuronales a la Identificación de Patrones

Redes Neuronales

Un modelo neuronal utilizado para clasificación, cuya salida está dada de la forma anterior y que utiliza el método de error-corrección para modificar sus parámetros libres se conoce como Perceptron (el nombre deriva de la palabra en inglés “perception”). Estas neuronas pueden agruparse formando una RNA conocida como Perceptron múltiple.

El Perceptron Multicapa

Los perceptrones individuales pueden agruparse formando redes de neuronas conectadas entre sí (de forma que la salida de unas se convierte en la entrada de otras). Aunque la forma en que se pueden agrupar es completamente libre y variada, el caso más común de agrupación se corresponde con una organización en capas ordenadas de manera que las salidas de las neuronas de una capa son las entradas de las neuronas de la capa siguiente.

Así, tenemos la capa de entrada formada por las entradas a la red (que realmente no son neuronas), la capa de salida formada por las neuronas que constituyen la salida final de la red, y las capas ocultas formadas por las neuronas que se encuentran entre los nodos de entrada y de salida. Una RNA puede tener varias capas ocultas o no tener ninguna de ellas. Las conexiones sinápticas (las conexiones que llegan y salen de las neuronas) indican el flujo de la señal a través de la red, y tienen asociadas el peso sináptico correspondiente. Si la salida de una neurona va dirigida hacia dos o más neuronas de la siguiente capa, cada una de estas últimas recibe la salida neta de la neurona anterior. La cantidad de capas de una RNA es la suma de las capas ocultas más la capa de salida. En el caso de existir capas ocultas nos referimos a la RNA como un Perceptron multicapa.

Una vez fijada una red neuronal concreta, se puede observar que los únicos parámetros que podemos modificar para obtener distintos comportamientos son los pesos sinápticos entre neuronas de distintas capas. Por ello, el problema habitual es el de, dado un conjunto de datos de los que se conoce la salida deseada, encontrar los pesos adecuados de la red para que se obtenga una aproximación correcta de las salidas si la red recibe los datos de entrada. Es decir, encontrar los pesos que hacen que la función que calcula la red sea la mejor aproximación posible para los datos de nuestro conjunto.

Obsérvese que este problema (como la mayoría de los problemas asociados al aprendizaje automático) se puede plantear de forma natural como un problema de optimización, donde el objetivo es encontrar los pesos de la red que minimiza el error cometido por la misma sobre un conjunto de datos. Como hemos visto en entradas anteriores, el procedimiento más habitual para esta optimización suele ser realizar una búsqueda en el espacio de parámetros (pesos) del modelo por medio de una sucesión de pasos que va proporcionando estados cada vez mejores hasta encontrar un mínimo (normalmente, local, no podemos asgurar que sea global). En cada uno de estos pasos se hace uso del conjunto de entrenamiento, una porción de los datos originales que permiten medir el comportamiento puntual de la red durante la búsqueda y ayudan a decidir en qué dirección dirigir los nuevos pasos. Y, posteriormente, para se comprueba la bondad de la red obtenida por medio del conjunto de test, otra porción de los datos originales que permiten calcular un error aproximado de la red sobre datos que no vio durante la búsqueda, para evitar que la medición del error sea excesivamente optimista debido a un sobreajuste en los datos que sirvieron de entrenamiento.

El algoritmo general de este proceso de entrenamiento se podría escribir pues como:

Train_ANN(R,D) [D: Dataset; R: Red Neuronal]
W pesos elegidos al azar para R (normalmente, en [-1,1])
Repetir:
Para cada (x,y) D:
Introducir x en Capa Entrada de R
Propagar los valores de las neuronas desde la capa de entrada (Forward)
Leer la salida: o(x) = Capa Salida de R
Calcular el error: e(x) = |o(x) - y|
Usar el error e(x) para ajustar W y minimizar el error en x
Devolver W

Donde $W$ es el conjunto de pesos que intervienen en la estructura de la red neuronal $R$. Aunque en el algoritmo anterior no se indica, puede haber dependencia de otros parámetros, como la tasa de aprendizaje, que intervienen en algunos de los procedimientos internos.

Además, suele ser habitual considerar un algoritmo un poco modificado que trabaja por lotes (batchs) de datos. En ellos, en vez de realizar la actualización de los pesos para cada dato de entrenamiento, se divide $D$ en lotes de datos y se promedia el error de un bloque de datos conjuntamente, actualizando los pesos para minimizar ese error promedio. De esta forma, la red es menos sensible a errores puntuales y da mejores resultados promedio (además de que puede funcionar de manera más eficiente).

En el caso de las redes neuronales, hemos de tener en cuenta que el número de parámetros con los que se hace el entrenamiento es considerablemente elevado incluso en redes relativamente pequeñas (por ejemplo, una red con 5 datos de entrada, una sola capa oculta de 10 neuronas, y 2 datos de salida, dispone ya de 82 pesos que ajustar, por lo que la búsqueda se haría en una espacio de dimensión 82), así que cualquier suposición inicial que ayude a reducir la complejidad de esa tarea será de gran ayuda.

Entrenamiento por Propagación hacia Atrás

A mediados de los años 80 se ofreció un algoritmo, llamado de Propagación hacia atrás (Back-Propagation, en inglés), que bajo ciertas suposiciones aproxima rápida y eficazmente los pesos para minimizar el error de entrenamiento, y que en la actualidad se ha convertido en la piedra fundamental sobre la que se basa el éxito de las redes neuronales:

Los fundamentos de la Propagación hacia atrás son sencillos:

  1. Las redes neuronales se construyen a partir del apilamiento de unidades de cálculo simples (neuronas). Por lo que la función que calculan se puede expresar por medio de la combinación y composición de las funciones que las neuronas calculan.
  2. Las funciones que intervienen en ese proceso son: las funciones de activación de cada neurona, y operaciones lineales vectoriales (sumas y productos de vectores).
  3. La función a minimizar es el error en la salida (ya sea unitario, o en lotes).
  4. Si todos los elementos anteriores se escogen adecuadamente (matemáticamente, que sean diferenciables), entonces la minimización del error se puede calcular por medio de un procedimiento de gradiente, siguiendo protocolos similares a los métodos de ascenso del gradiente.

Un ejemplo completo de un paso aplicando este algoritmo lo puedes encontrar aquí (en inglés). 

Consideraciones Finales

Ahora puedes entender algunas consideraciones acerca de las redes neuronales, de porqué están funcionando tan bien, y de las características que debes tener en cuenta para trabajar con ellas:

  1. Si la función de activación de las neuronas es lineal, entonces la red completa solo podría calcular una función lineal, da lo mismo la profundidad y anchura que tenga, ya que el resultado sería la combinación/composición lineal de funciones lineales, que siempre resulta ser lineal. En consecuencia, una red neuronal lineal solo podría aproximar funciones lineales, lo que no sería muy útil. Así pues, las activaciones son las que introducen el factor de no linealidad que hace que las redes neuronales sean tan versátiles.
  2. El valor de salida de la red dependerá de los valores de los pesos y de los rangos de salida de las funciones de activación, por lo que debes tener en cuenta dos detalles: o bien escalas (si puedes) los resultados de tu dataset para que se ajuste al rango de valores de la activación usada, o bien usas una función de activación que se ajuste al rango de valores deseados en tu problema (o ambas cosas).
  3. Bajo condiciones determinadas (activación no lineal, rango acotado,...) se puede probar el llamado Teorema de Aproximación Universal (TAU), que en corto dice que toda función continua se puede aproximar por una red neuronal con la estructura adecuada. 
  4. Es más, se puede probar que el TAU se obtiene usando redes neuronales con una única capa oculta. Pero ha de tenerse en cuenta que el número de neuronas en esa sola capa puede ser muy elevado (cientos de miles). Se sabe que lo importante es el número de conexiones, por lo que el número de neuronas totales se puede reducir drásticamente al introducir más capas.
  5. Durante el proceso de backpropagation se usa la derivada de la función de activación, por lo que no conviene usar funciones de activación muy complicadas, ya que entonces la fase de backpropagation sería muy costosa y el entrenamiento sería muy lento (ten en cuenta que hacen falta miles de ciclos, en el mejor de los casos, para que la red entrene bien).
  6. Siguiendo con el punto anterior, es deseable usar funciones de activación en las que la derivada se pueda calcular de forma sencilla. La sigmoide se introdujo precisamente por ello, ya que su derivada en un punto cuaquiera, $x$ se puede calcular como: $$\sigma'(x)=\sigma(x)(1-\sigma(x))$$ Es decir, que no solo es sencilla, sino que además se puede expresar en función del valor de $\sigma$ en el mismo punto... y este valor lo hemos tenido que calcular en el paso de propagación hacia adelante, así que basta almacenarlo a la ida, y lo reusamos a la vuelta. En la tabla anterior puedes ver las funciones de activación más usadas junto con sus derivadas y los rangos de valores en los que trabajan.
  7. Aunque hemos impuesto que la función de activación sea derivable para poder usar métodos de gradiente, en realidad solo es necesario que sea derivable "casi" siempre (incluso hace falta menos, pero no nos vamos a meter en eso ahora).
  8. La modificación de los pesos depende de la derivada de la función en el punto y del producto de las derivadas de las capas posteriores, por lo que si esta derivada es muy pequeña (por ejemplo, en la sigmoide ocurre en los valores extremos, que es casi plana) entonces los pesos apenas se modificarán y el entrenamiento de la red precisará de mucho tiempo. Es lo que se llama Vanishing Gradient Problem.
  9. Este problema es lo que ha dificultado que durante mucho tiempo las redes neuronales pudieran tener muchas capas apiladas, ya que la capacidad de aprendizaje (modificación de los pesos) decae exponencialmente a medida que nos acercamos a la capa de entrada.
  10. Los grandes avances en Redes Neuronales de los últimos años se pueden resumir en dos líneas: mejores algoritmos de optimización basados en gradiente (extensiones del algoritmo básico que se ha mostrado aquí), y la aparición de librerías específicas para manejar estas redes desde un punto de vista computacional por medio de hardware específico que permite el paralelismo. Estas dos líneas han permitido manipular redes de mayor tamaño (más profundas y anchas, y por tanto con muchos más parámetros) y con estructuras más flexibles, y realizar computaciones más largas (más pasos de entrenamiento) sobre conjuntos de datos considerablemente más grandes (cuando mayor sea el número de parámetros a ajustar, mayor tiene que ser el dataset de entrenamiento). El gran exponente de estos dos avances toman cuerpo en lo que hoy se conoce como Deep Learning (que es simplemente el nuevo nombre comercial-académico que se le ha dado al campo de las Redes Neuronales para diferenciarlo de la aproximación clásica).

Se puede encontrar una implementación concreta de una Red Neuronal básica en esta otra entrada, usando NetLogo, donde es natural implementarlo por medio de las estructuras de grafos que se consiguen con tortugas y links.

RNP

Back Propagation

Para saber más...

Universidad Carlos III: Redes Neuronales

Wikipedia: Redes Neuronales Artificiales

Xataka: Redes Neuronales Artificiales, ¿qué son y porqué están volviendo?

Introducción a las Redes Neuronales Artificiales aplicadas

« Algoritmos de hormiga… « || Inicio || » Optimización en el es… »