lunes, 13 de abril de 2015

Aprendizaje funcional automático


Hace poco, mi hija comenzó en el colegio a aprender a realizar sumas de una cifra. Me resultó muy interesante observar el proceso que siguió para conseguirlo: ni más ni menos que un refuerzo paulatino de aprendizaje por ensayo y error. Esto me recordó un ejemplo de aprendizaje automático por ordenador que realicé hace ya años, y me propuse hacer algo similar adecuándolo al modo en que parece que mi hija ha conseguido aprender a sumar. El resultado lo tenéis a continuación.

Simulando los procesos neurológicos:

El campo más prometedor en IA desde hace décadas, es la simulación neuronal del cerebro animal mediante redes neuronales artificiales. Mediante esta simulación se han conseguido los avances más espectaculares en el terreno de la inteligencia artificial. Baste nombrar uno de los avances más notorios conseguidos por un equipo de desarrollo de Google, donde han conseguido que una única red neuronal artificial sea capaz de aprender de manera autónoma a jugar a 43 juegos diferentes y con un nivel de habilidad mayor al alcanzado por la media de personas. La red neuronal recibe como entrada únicamente los píxeles de colores de la pantalla del juego, y resuelve el modo en que hay que pulsar los distintos botones del mando para jugar bien.

Si este algoritmo se implantase en un robot con una cámara visual, y con "dedos" capaces de manipular un mando de videojuego, tendríamos el equivalente de un chaval jugando (de hecho, en realidad el robot jugaría a esos 43 juegos mejor que la media de chavales).

¿Y cómo se ha conseguido esta hazaña? Pues simplemente simulando por ordenador el funcionamiento de las neuronas del cerebro animal. Se crean nodos artificiales, que son el equivalente a las neuronas biológicas, y se unen unos con otros mediante enlaces o pesos wij (que son el equivalente a las interconexiones sinápticas entre neuronas naturales). Al igual que las neuronas, los nodos artificiales poseen un umbral de activación y un nivel de transmisión o inhibición similar al voltaje que una neurona emite y transmite mediante las dendritas.

Y aunque parezca ser una simulación complicada, en realidad computacionalmente es algo muy sencillo de hacer...¡y funciona!

Reforzando la red neuronal:

Una red neuronal artificial por sí sola no sirve de gran cosa. Si sus pesos y valores de umbral y transmisión contienen cualquier valor posible, la salida que produzca será aleatoria y sin sentido. no será funcional. Es necesario reforzar o entrenar esta red, de modo que se puedan ajustar los pesos y umbrales del modo adecuado para que la red neuronal pueda realizar una función útil.

Existen varios modos de ajustar una red neuronal, pero sin duda la más eficiente y sencilla, al menos en mi opinión, es mediante el uso de un proceso evolutivo.

Simplemente se trata de poner a prueba un gran conjunto de redes neuronales similares, y de ir seleccionando aquellas que mejor aproximen el resultado deseado. En cada generación se crearán nuevas redes a partir de las anteriores, las cuales podrán sufrir leves variaciones en sus parámetros (mutaciones). Esta simple iteración permite ajustar cualquier red neuronal de un modo eficiente para conseguir el fin deseado.

¿Aprendemos nosotros de un modo parecido?:

Tras estudiar el modo en que mi hija aprendió a sumar, yo creo, sin duda, que siguió un esquema muy similar al indicado arriba. Personalmente, creo que una gran parte (si no todo) del aprendizaje no instintivo que consiguen los animales (ser humano incluido), sigue un proceso de refuerzo por ensayo y error sobre un conjunto concreto de neuronas de nuestro cerebro.

Durante millones de años, el proceso evolutivo biológico habría sentado la base neuronal y la plasticidad necesaria para ajustar partes independientes de la masa neuronal para poder, literalmente, reforzar o inhibir las interconexiones y los umbrales de las neuronas de modo que se puedan conseguir salidas funcionales parciales. De este modo, el cerebro sería capaz de poseer cientos de miles de heurísticos (algoritmos) independientes pero conectados, los cuales darían lugar a toda la conducta en su complejidad.

El proceso de aprendizaje (aproximado y simplificado) que probablemente siguió mi hija pudo ser el siguiente:

Los números llegaron a su cerebro en forma de impulsos eléctricos gracias al sentido de la vista. Posiblemente algún heurístico innato (o varios) convirtieron y separaron esos impulsos en entradas para un determinado subconjunto neuronal concreto, el cual produjo una salida que otros heurísticos transformaron en un acto conductual: escribiendo la salida en un papel o diciendo en voz alta la respuesta. Cuando la respuesta fue correcta, nuestra aprobación (o la del profesor) llegó a su cerebro en forma de refuerzo positivo mediante los sentidos de la vista y el oído, y el subconjunto neuronal que se encargó de la tarea se vio reforzado. Cuando la respuesta fue errónea, le indicamos cual era la solución correcta, la cual llegó también a su cerebro y alteró a continuación el ajuste del subconjunto neuronal implicado en este asunto. En el momento que la salida del subconjunto fue  ya siempre correcta, no se recibieron nuevas correcciones, y el aprendizaje habría terminado.

Un nuevo heurístico ha aparecido en el cerebro de mi niña: ahora ya es capaz de sumar :).

El movimiento se demuestra andando:

Como es habitual, no me voy a quedar en la simple teoría, y voy a poner en práctica todo lo dicho mediante un ejemplo que yo mismo he desarrollado, y que podréis probar a continuación desde vuestro propio navegador:

Mediante un proceso de computación evolutiva, veremos como una red neuronal artificial de sólo 40 nodos, es capaz de aprender de manera autónoma a realizar sumas de una cifra (el aprendizaje es autónomo en el sentido de que en ningún momento al algoritmo se le indica cómo hay que sumar, y ni siquiera se le pasan los números en formato decimal. Tampoco se ajusta manualmente en modo alguno las variables de la red neuronal).

Técnicamente hablando, he utilizado una red neuronal con conexión hacia delante y una capa oculta (hidden layer). La capa de entrada contiene 20 nodos que se inicializan a 1 ó 0 según sea el input de entrada (el par de números a sumar). Cada nodo de la capa de entrada se une a cada nodo de la capa intermedia (la cual consta de otros 20 nodos), lo que da un total de 400 enlaces (pesos wij). Un último nodo de salida es el responsable de devolver el resultado del proceso neuronal expresando con su nivel de activación el resultado de la suma .

Inicialmente, los pesos wij de la red neuronal son marcados aleatoriamente, por lo que la respuesta de la red neuronal ante el problema será también aleatoria y casi siempre errónea. Hay pues que entrenar la red para que aprenda a evaluar bien la entrada, lo que vamos a conseguir ajustando evolutivamente los pesos de la red neuronal utilizada, el umbral de activación de cada nodo, y el nivel de transmisicón o inhibición asociado. Dicho entrenamiento evolutivo se realizará mediante una estrategia evolutiva.

Comenzamos con una población de 750 redes neuronales aleatorias (n = 750 individuos). Cada generación producirá n nuevos individuos que podrán sufrir una variación exclusiva por mutación -sin recombinación- y cuya función de desempeño (fitness fuction) será calculada mediante competición -selección por torneo-. Para el proceso de mutación, hay que tener en cuenta que cada individuo; además de un vector de pesos, contiene un vector de variables de ajuste (umbral de activación y valor de transmisión), que también irá evolucionando junto con los pesos. La mutación es de la forma:


Con alpha igual 0.2f, y donde xi indica el peso en la posición i del vector de pesos, y N(0,1) indica un valor tomado aleatoriamente de una distribución normal de desviación típica igual a 1, y media igual a 0. La otra variable que interviene en el proceso se corresponde con la variable de ajuste del elemento i, que; como se puede ver, muta antes de que lo haga el peso xi. La evaluación de un individuo se realiza mediante q pruebas (con q = 100) en donde se comprueba la capacidad del individuo para resolver las 100 combinaciones posibles en que se pueden sumar dos números en el dominio [0-9]. En el paso final de cada generación, se seleccionan aquellos n individuos que mejor han aproximado su respuesta ante las q sumas.

A continuación podéis probar este ejemplo de computación evolutiva que he desarrollado. Para alcanzar un aprendizaje completo, ajusta el campo "Número de generaciones para el aprendizaje" al menos en 150, en lugar de 50 como lo he puesto por defecto. Con el botón "Realizar prueba" podrás probar la funcionalidad de la mejor red neuronal alcanzada tras finalizar el proceso evolutivo:



Podéis descargar el código fuente del ejemplo desde este enlace.

El poder del proceso evolutivo:

Es interesante notar la enorme eficiencia y capacidad que tiene la computación evolutiva para encontrar, dentro de un gigantesco conjunto de posibilidades, los valores adecuados para conseguir un fin concreto. En este ejemplo que estamos estudiando, las combinaciones y los valores posibles para los pesos wij de los 400 enlaces, así como para los umbrales de activación y los valores de transmisión son astronómicos. Para que un proceso aleatorio consiguiese ajustar finamente estos valores de modo que la red neuronal pudiese sumar correctamente, probablemente harían falta cientos de miles de años, y sin embargo, el algoritmo evolutivo lo consigue en unos pocos minutos.


No hay comentarios:

Publicar un comentario en la entrada