Redes neuronales y algoritmos evolutivos II
En este segundo artículo sobre la aplicación de los algoritmos evolutivos a la optimización del diseño de redes neuronales, voy a proporcionar una pequeña aplicación de ejemplo que permite construir y entrenar redes, además de utilizar este tipo de algoritmos para buscar la mejor configuración para un determinado conjunto de datos. La aplicación permite generar datos artificiales de prueba, y también proporciono el código fuente para poder modificarla a voluntad.
En este enlace podéis acceder al primer artículo de esta serie, donde explico las ideas básicas en las que está basada esta aplicación. En este otro enlace podéis descargar el código fuente de la aplicación GANNTuning, escrita en csharp con Visual Studio 2017.
La aplicación consta de una barra de herramientas con los botones correspondientes a las diferentes acciones que realiza el programa, y algunas pestañas para introducir parámetros o comprobar los resultados:
Carga de datos
Lo primero que es necesario hacer es cargar los datos con los que entrenaremos las redes neuronales. Existen dos posibilidades: por un lado, podemos leerlos desde un archivo de texto en formato csv, por otro lado, se pueden generar conjuntos de datos ficticios con las características que deseemos. Yo he puesto unos pocos tipos de ejemplo, pero resulta sencillo modificar el código para añadirle otros adicionales.
Para no complicar mucho el código, las redes neuronales que construye el programa tienen una sola neurona de salida, y están compuestas de neuronas con una función de activación sigmoide bipolar; su objetivo es discriminar patrones usando aprendizaje supervisado, mediante entrenamiento con el algoritmo de retro propagación. Respecto a los datos a introducir, esto significa que cada línea del archivo csv representa una muestra, cuya última columna representa el identificador del patrón que le corresponde. Este identificador debe ser un número entero, aunque su valor no tiene importancia, ya que a cada patrón se le asigna un valor que se reparte según el número de patrones diferentes dentro del rango (-1, 1), el rango de salida de la última neurona, para maximizar la diferencia de resultados entre patrones; por ejemplo, si hay solo dos patrones diferentes, la red esperará el valor 0.99 para el primero y -0,99 para el segundo. Los datos se normalizan de manera que tomen valores en el rango [-1, 1], si se desea otro tipo de normalización, o ninguna, hay que modificar el código fuente.
Para cargar datos desde un archivo csv, hay que utilizar el segundo botón por la izquierda de la barra de herramientas, y seleccionar el archivo en el cuadro de diálogo. Con el primer botón por la izquierda, es posible generar conjuntos de datos artificiales, según la configuración que apliquemos en la pestaña Training:
En el desplegable Fake se puede seleccionar uno de estos tipos de conjuntos de datos, estos son los siguientes:
- Random: Todos los valores se generan de forma aleatoria, se trata de un conjunto de datos que no debería presentar ningún patrón determinado.
- Pulse: se divide el número de valores por muestra entre el número de patrones y, para cada patrón, se genera una muestra con ese número de posiciones con valor negativo y el resto con valor positivo. Para cada patrón se elige una posición diferente para esos valores negativos. En cada muestra se selecciona un solo valor aleatoriamente, que se usa tanto para las posiciones negativas como para las positivas, formando un patrón en forma de escalón.
- RandPulse: es como la anterior, pero cada valor, positivo o negativo, es diferente y se obtiene aleatoriamente.
- Equal: Se genera solo un patrón y todos los demás son una copia idéntica de este primero. Se trata de un conjunto imposible de separar.
- Noise: es como RandPulse pero añadiendo ruido aleatorio con una determinada probabilidad.
El número de patrones diferentes que queramos generar se debe escribir en el cuadro de texto Patterns, el número de muestras por patrón en Samples, y el número de valores que compondrán cada muestra en Values.
Construcción de la red
En la pestaña Network se introducen los parámetros de diseño la red, para el caso de que queramos hacer alguna prueba con una determinada configuración. Para la selección con algoritmos genéticos no es necesario introducir nada, ya que es el propio programa el que se encarga de ir generando las diferentes configuraciones.
En Input Layer se introduce el número de neuronas en la capa de entrada. Hidden Layers se puede dejar en blanco si no queremos capas ocultas, o bien poner el número de neuronas en cada capa separado por comas. Alpha es el parámetro para configurar la función sigmoide de activación de las neuronas.
Entrenamiento de la red
En la pestaña Training se introducen los parámetros de entrenamiento para la red. Estos parámetros se utilizan tanto para entrenar una única red de forma manual como durante el proceso de selección genética:
En Iterations se introduce el número de iteraciones que realizará el algoritmo de entrenamiento antes de terminar. Learning rate es el ritmo de aprendizaje, y Momentum es el momento, o peso que se le da en el cálculo de los nuevos pesos de las entradas al valor calculado en la anterior iteración, para tratar de evitar caer en mínimos locales.
También es necesario indicar el porcentaje de muestras que se utilizarán para el entrenamiento. El programa tratará de proporcionar igual número de muestras para cada uno de los patrones.
Con el cuarto botón por la izquierda de la barra de herramientas se puede inicializar la red para que el entrenamiento comience desde cero, de lo contrario, se continuará entrenando la última red entrenada sin reinicializar los pesos. El quinto botón lanza el proceso de entrenamiento, cuya evolución se puede ver en la pestaña Training Monitor:
Esta monitorización se puede desactivar con el quitando la marca Track, ya que durante la selección genética puede interesarnos más ganar en velocidad de proceso. El error que se muestra es el promedio de los errores cometidos en todas las muestras de la iteración actual.
Durante el entrenamiento, solo queda activo el botón Cancelar, que detendrá el entrenamiento en la iteración en curso.
La red neuronal entrenada se puede guardar en un fichero utilizando el tercer botón por la izquierda de la barra de herramientas, seleccionando el tipo de archivo correspondiente. Posteriormente, se puede volver a cargar utilizando el segundo botón por la izquierda.
Selección genética
El algoritmo genético se configura en la pestaña Genes:
En el cuadro de texto Population se introduce la población máxima, que tiene que ser por lo menos de 100 redes. También se deben introducir el máximo número de neuronas por capa y el máximo número de capas ocultas. Trials es el número de veces que se entrenará cada red en cada generación. La efectividad de una red depende bastante de los valores iniciales, que se seleccionan aleatoriamente, por lo que puede resultar buena idea repetir la valoración de la red unas cuantas veces, para quedarnos con el mejor valor, si el tiempo que lleva cada entrenamiento lo permite.
Para lanzar la selección genética, se debe pulsar el último botón de la barra de herramientas. En primer lugar, se generará la población inicial, entrenando cada red tantas veces como hayamos indicado. Podremos ver información sobre el proceso en la pestaña Results. También se puede cancelar el proceso, con el mismo botón Cancel que sirve para cancelar el entrenamiento, pero habrá que esperar a que termine de procesarse la generación actual para que el algoritmo termine.
La población de genes actual se puede guardar en un archivo mediante el botón de Guardar, seleccionando el tipo de archivo correspondiente; también se puede guardar un fichero csv con un informe de las diferentes genes que componen la población. Los genes se pueden cargar posteriormente de nuevo para continuar con el procedimiento. El programa guarda también, al terminar cada generación, la población de genes en un archivo temporal llamado gentmp.gen, en el directorio donde está el ejecutable. La red neuronal que corresponde a la mejor valoración también se guarda en el archivo gentmp.net.
El criterio seguido para seleccionar la mejor red lo explicaré en el siguiente artículo, junto con el código fuente más relevante, de manera que puedas modificarlo como te parezca oportuno.
Comprobar el resultado
Para comprobar la eficiencia de la red entrenada, utilizaremos el séptimo botón por la izquierda de la barra de herramientas. La red actual tratará de clasificar el conjunto completo de todos los datos. El resultado se puede ver en la pestaña Results:
En la parte superior se muestran el porcentaje de aciertos y fallos y su distribución entre los diferentes patrones, además de una serie de estadísticos referentes a los errores de la red: error máximo, mediano, mínimo, medio y su desviación típica, además del valor de la función de coste de los genes para la red.
En la parte inferior se puede ver un histograma con la distribución de los errores, en orden de magnitud de izquierda a derecha, en rojo los errores que producen clasificaciones incorrectas.
En el próximo artículo de la serie, explicaré las partes más relevantes del código fuente.