universidad de mÁlaga escuela tÉcnica superior de ... · 13 1 introducciÓn 1.1 motivación la...
TRANSCRIPT
UNIVERSIDAD DE MÁLAGA
ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA
INGENIERO EN INFORMÁTICA
PERCEPTRONES MULTICAPA BASADOS EN EL
ALGORITMO LEVENBERG-MARQUARDT PARA LA
DOCENCIA DE SISTEMAS INTELIGENTES
Realizado por
MIGUEL ÁNGEL RICO BLANCO
Dirigido por
EZEQUIEL LÓPEZ RUBIO
Departamento
LENGUAJES Y CIENCIAS DE LA COMPUTACIÓN
MÁLAGA, DICIEMBRE DE 2017
UNIVERSIDAD DE MÁLAGA
ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA
INGENIERÍA INFORMÁTICA
Reunido el tribunal examinador en el día de la fecha, constituido por:
Presidente/a Dº/Dª. ________________________________________________________
Secretario/a Dº/Dª. _________________________________________________________
Vocal Dº/Dª. _____________________________________________________________
para juzgar el proyecto Fin de Carrera titulado:
PERCEPTRONES MULTICAPA BASADOS EN EL ALGORITMO LEVENBERG-
MARQUARDT PARA LA DOCENCIA DE SISTEMAS INTELIGENTES
realizado por Dº Miguel Ángel Rico Blanco
tutorizado por Dº Ezequiel López Rubio
ACORDÓ POR ________________________________ OTORGAR LA CALIFICACIÓN
DE __________________________________
Y PARA QUE CONSTE, SE EXTIENDE FIRMADA POR LOS COMPARECIENTES
DEL TRIBUNAL, LA PRESENTE DILIGENCIA.
Málaga a _____ de___________________ del 20__
El/La Presidente/a El/La Secretario/a El/La Vocal
Fdo. Fdo. Fdo.
PERCEPTRONES MULTICAPA BASADOS
EN EL ALGORITMO LEVENBERG-
MARQUARDT PARA LA DOCENCIA DE
SISTEMAS INTELIGENTES
A un cateto listo, que es casi una criatura, por todas esas “deciuras” que ahora empiezo a
entender.
A la Chari, por ser la primera en enseñarme métodos numéricos de aproximaciones
sucesivas, aunque ella los llama “la cuenta de la vieja”
A una doctora rubia que no puede recetar medicamentos, por todas esas conversaciones
filosóficas/científicas/teológicas desde el respeto mutuo, o casi.
A un pulpero, el más listo de los hermanos, adalid del pragmatismo, que aunque parecía que
nunca estaba siempre aparecía cuando hacía falta.
A Ezequiel (Ez 34:16)
Al deporte del Rugby en general y al C.D. Axarquía Rugby en particular por enseñarme los
valores del trabajo en equipo, respeto a las normas, sacrificio y humildad.
A Carolina, por comprender perfectamente que la dieta de un programador se basa en una
grácil permutación de dulce y salado, regado todo con mucha cafeína
A María José, por completarme, mimarme, soportarme y siempre darme el punto de vista
que rompe mis cuadrículas, desajusta mis fórmulas y añade entropía a mi mundo.
ÍNDICE
1 Introducción ...................................................................................................................... 13
1.1 Motivación ................................................................................................................. 13
1.2 Objetivos .................................................................................................................... 13
2 Gestión del Proyecto ......................................................................................................... 14
2.1 Recursos..................................................................................................................... 14
2.1.1 Recursos Humanos ............................................................................................. 14
2.1.2 Recursos Físicos ................................................................................................. 14
2.1.3 Recursos Software .............................................................................................. 14
2.1.4 Recursos documentales ...................................................................................... 20
2.2 Planificación .............................................................................................................. 20
2.3 Estudio de Viabilidad y Gestión de Riesgos ............................................................. 21
2.3.1 Viabilidad ........................................................................................................... 21
2.3.2 Riesgos ............................................................................................................... 22
2.4 Modelo Ciclo de Vida................................................................................................ 23
2.5 Metodologías ............................................................................................................. 24
3 Redes Neuronales .............................................................................................................. 26
3.1 Introducción ............................................................................................................... 26
3.2 El Perceptrón Simple ................................................................................................. 26
3.2.1 Introducción ....................................................................................................... 26
3.2.2 El Perceptrón Simple .......................................................................................... 26
3.3 El Perceptrón Multicapa ............................................................................................ 29
3.4 Aplicaciones .............................................................................................................. 31
3.5 Métodos de aprendizaje ............................................................................................. 35
3.5.1 Algoritmo de Retropropagación: la regla delta .................................................. 35
3.5.2 Aprendizaje con Momentos: La regla delta generalizada .................................. 38
3.5.3 Algoritmo de Levenberg-Marquartd .................................................................. 39
4 Desarrollo .......................................................................................................................... 42
4.1 Análisis ...................................................................................................................... 42
4.1.1 Introducción ....................................................................................................... 42
4.1.2 Neural Aproximator Application ....................................................................... 42
4.1.3 Entrada libre de la función a aproximar ............................................................. 47
4.1.4 Requerimientos ................................................................................................... 52
4.1.5 Método de aprendizaje Levenberg-Marquardt ................................................... 52
4.2 Diseño ........................................................................................................................ 53
4.2.1 Interfaz ............................................................................................................... 53
4.3 Implementación ......................................................................................................... 58
4.3.1 Interfaz de usuario .............................................................................................. 59
4.3.2 Clases Modificadas ............................................................................................ 61
4.3.3 Clases Nuevas .................................................................................................... 68
5 Ejemplos de ejecución ...................................................................................................... 75
6 Conclusiones ..................................................................................................................... 81
7 Anexo 1 Software Adicional utilizado en el Proyecto ...................................................... 82
8 Anexo 2 Referencias ......................................................................................................... 83
Tabla de Ilustraciones
Ilustración 1 El perceptrón Simple ........................................................................................... 27
Ilustración 2 El perceptrón multicapa ...................................................................................... 30
Ilustración 3 Cálculo de la salida del MLP .............................................................................. 31
Ilustración 4 Aplicación del MLP: Clasificación de caracteres ............................................... 31
Ilustración 5 Aplicación del MLP: Compresión ...................................................................... 32
Ilustración 6 Aplicación del MLP: Reconocimiento de palabras ............................................. 33
Ilustración 7 Aplicación del MLP: Detección Facial ............................................................... 33
Ilustración 8 Aplicación del MLP: Segmentación de Imágenes .............................................. 34
Ilustración 9 Aplicación del MLP: Aproximación de funciones .............................................. 34
Ilustración 10 Aprendizaje por Momentos ............................................................................... 39
Ilustración 11 Algoritmo de Levenberg-Marquardt ................................................................. 41
Ilustración 12 Diagrama de clases versión anterior: Clases del Entorno ................................. 43
Ilustración 13 Interfaz de usuario versión anterior ................................................................... 44
Ilustración 14 Diagrama de clases versión anterior: MLP y Backpropagation ........................ 45
Ilustración 15 Diagrama de clases versión anterior: Detalle MLP y Layer ............................. 46
Ilustración 16 Caso de uso Versión anterior ............................................................................ 47
Ilustración 17 Caso de uso Nueva versión ............................................................................... 48
Ilustración 18 Diagrama de Secuencia para RUN .................................................................... 48
Ilustración 19 Diagrama de secuencia para STEP ................................................................... 49
Ilustración 20 Diagrama de secuencia Modo función predefinida ........................................... 50
Ilustración 21 Diagrama de secuencia Modo entrada libre de función .................................... 51
Ilustración 22 Diagrama de secuencia Exp4j ........................................................................... 52
Ilustración 23 Diagrama de secuencia LM ............................................................................... 52
Ilustración 24 Diagrama de Máquina de estados LM .............................................................. 53
Ilustración 25 Maqueta del interfaz existente .......................................................................... 54
Ilustración 26 Detalle Maqueta Nuevos Controles .................................................................. 55
Ilustración 27 Detalle Maqueta modo entrada libre de función ............................................... 55
Ilustración 28 Diagrama de clases nueva versión .................................................................... 57
Ilustración 29 Detalle Estructura del Proyecto en Eclipse ....................................................... 58
Ilustración 30 Detalle UI nueva versión: Tipo de función ....................................................... 59
Ilustración 31 Detalle UI nueva versión: Funciones predefinidas ........................................... 59
Ilustración 32 Ejemplo de función libre: x^2*sin(pi*x) .......................................................... 60
Ilustración 33 Ejemplo de función libre: 0.5*e^x + sin(x^2)^2 ............................................... 60
Ilustración 34 Detalle de UI Nueva Versión: Tipo de red ........................................................ 61
Ilustración 35 Ejemplo BP ....................................................................................................... 75
Ilustración 36 Ejemplo de LM ................................................................................................. 76
Ilustración 37 Ejemplo BP paso a paso 1 ................................................................................. 77
Ilustración 38 Ejemplo BP paso a paso 2 ................................................................................. 77
Ilustración 39 Ejemplo BP paso a paso 3 ................................................................................. 77
Ilustración 40 Ejemplo BP paso a paso 4 ................................................................................. 78
Ilustración 41 Ejemplo LM paso a paso 1 ................................................................................ 78
Ilustración 42 Ejemplo LM paso a paso 2 ................................................................................ 79
Ilustración 43Ejemplo LM paso a paso 3 ................................................................................. 79
Ilustración 44 Ejemplo LM paso a paso 4 ................................................................................ 79
Ilustración 45 Ejemplo LM error estancado ............................................................................. 80
13
1 INTRODUCCIÓN
1.1 Motivación
La asignatura Sistemas Inteligentes I , y las asignaturas Introducción a la Ingeniería del
Software y Bases de Datos constituyen la materia "Ingeniería del Software, Sistemas de
Información y Sistemas Inteligentes" dentro del módulo de "Formación Común a la rama de
Informática" que se imparte en el segundo curso del grado.
La asignatura persigue los siguientes objetivos: asimilación de los conceptos y técnicas básicas
de la IA desde el punto de vista teórico; aplicación práctica de lo anterior mediante la realización
de clases prácticas.
Dentro de dicha asignatura se encuentra el bloque temático de Redes neuronales que incluye el
tema de Perceptrón simple y multicapa. Para la realización de las clases prácticas se pretende
usar unas librerías fácilmente modificables por los alumnos para afianzar los conocimientos
sobre perceptrones multicapa y el algoritmo de aprendizaje de Levenberg-Marquardt.
1.2 Objetivos
El objetivo del presente proyecto es el desarrollo de una librería que implemente el algoritmo
de aprendizaje de Levenberg-Marquardt dentro del sistema de clases de Java (AIMA,
[Stuart2009]) que ya se emplean en la asignatura “Sistemas Inteligentes”
Dicha librería debe ser configurable y fácilmente comprensible por el alumno, haciendo
hincapié en la claridad del código, incluso en detrimento de la eficiencia.
Extensión:
Actualmente el entorno gráfico que provee las librería AIMA para las prácticas la aproximación
de funciones codificando la función dentro del código java. Si se quiere mostrar algún aspecto
del algoritmo con determinados tipos de funciones estas deben ser implementadas previamente,
perdiéndose agilidad en la explicación. Por ello, se va a modificar el entorno para que acepte
una función arbitraria de una sola variable escrita en un formato fácilmente entendible por el
alumno.
14
2 GESTIÓN DEL PROYECTO
La gestión incluye las tareas y actividades tácticas realizadas con el propósito de planificar y
controlar las actividades operativas/técnicas para alcanzar un objetivo. Las actividades de
gestión para este proyecto son:
Planificación: Establecimiento de un plan de acción para alcanzar los objetivos.
Seguimiento y control: Evaluación y medición del desempeño de las actividades
mediante los objetivos planeados.
2.1 Recursos
2.1.1 Recursos Humanos
Los recursos humanos de este proyecto son el alumno autor del proyecto y el director.
2.1.2 Recursos Físicos
El recurso físico necesario es un Equipo PC Portátil con Debian GNU/Linux instalado
2.1.3 Recursos Software
2.1.3.1 Entorno de desarrollo
Versión: Neón 3. Release 4.6.3 Fecha: 14/03/2017 (¡El día de pi!)
El entorno de desarrollo elegido para la implementación de la librería es Eclipse. Eclipse es una
plataforma de software compuesto por un conjunto de herramientas de programación de código
abierto multiplataforma para desarrollar lo que el proyecto llama "Aplicaciones de Cliente
Enriquecido", opuesto a las aplicaciones "Cliente-liviano" basadas en navegadores. Esta
plataforma, típicamente ha sido usada para desarrollar entornos de desarrollo integrados (del
inglés IDE), como el IDE de Java llamado Java Development Toolkit (JDT) y el compilador
(ECJ) que se entrega como parte de Eclipse (y que son usados también para desarrollar el mismo
Eclipse).
15
2.1.3.1.1 Motivación para la elección del Entorno
Los motivos para la elección del entorno:
Soporte para Java, que es el lenguaje usado en las prácticas de la asignatura “Sistemas
Inteligentes I”: IDE JDT Java Development Toolkit y compilador ECJ
Disponibilidad: Es de libre descarga ([Eclipse2017]) para las principales plataformas:
Windows, Linux y Mac OS X
Licencia: Eclipse Public License, sólo se diferencia de la General Public License (GPL)
en los puntos relacionados con patentes de los trabajos derivados del código fuente de
Eclipse. Como en el presente proyecto solo se va a usar para desarrollar una nueva
librería no nos afecta esta restricción.
Desarrollo activo: Está mantenido por la fundación Eclipse un consorcio no oficial de
empresas fabricantes de software, liderado por IBM, una entidad legal sin ánimo de
lucro que lleva las riendas y el desarrollo de Eclipse.
2.1.3.2 Librería Aima
Versión: aima3e Fecha: 18/12/2016
Esta librería implementa los algoritmos descritos en [Stuart2009]. Se organiza en 4
subproyectos:
aima-core : contiene todos los algoritmos implementados descritos en AIMA3e.
aima-gui : contiene aplicaciones demo basadas en JavaFX y Swing así como demos
en línea de comandos. Este proyecto depende de aima-core
aimax-osm : contiene la librería Open Street Map (OSM) y aplicaciones. Este Proyecto
depende de aima-core y aima-gui.
aima-all : Es el Proyecto maestro, usado por los desarrolladores java de aima para crear
nuevas versions.
Para esta implementación sólo se han usado: aima-core y aima-gui.
2.1.3.2.1 Motivación para la elección aima
La librería aima se está usando actualmente en las prácticas de la asignatura “Sistemas
Inteligentes I”, tanto aima-core (algoritmo: BackPropLearning) como aima-gui (entorno gráfico
para “Neural Aproximator Application”)
16
Por un lado eliminamos la curva de aprendizaje de una nueva librería por parte del alumnado y
por otro podemos tomar como base las clases y métodos existentes, centrándonos en la
implementación del algoritmo de aprendizaje de Levenberg-Marquardt, sin tener que
implementar desde cero un perceptrón multicapa o, siquiera, la multiplicación de matrices.
Código fuente disponible: [AIMACODE 2017]
Licencia: Licencia MIT
Documentación, disponible en el repositorio de GitHub y en la publicación [Stuart2009]
2.1.3.3 Librería Exp4j
Versión: 0.4.8 Fecha: 01/30/2017
Exp4j es capaz de evaluar expresiones y funciones en el dominio real. Es una librería pequeña
(40KB) sin dependencias externas que implementa el algoritmo de Dijkstra “Shunting Yard”.
Exp4j cuenta con un conjunto estandar de funciones y operadores. Adicionalmente los usuarios
pueden crear operadores y funciones personalizados.
Operadores Incorporados:
Suma: 2 + 2
Resta: 2 - 2
Multiplicación: 2 * 2
División: 2 / 2
Exponenciación: 2 ^ 2
Operadores de signo: +2 - (-2)
Módulo: 2 % 2
Funciones incorporadas:
abs: valor absoluto
acos: arcocoseno
asin: arcoseno
atan: arcotangente
cbrt: raíz cúbica
ceil: entero superior más cercano
cos: coseno
17
cosh: coseno hiperbólico
exp: potencia del número e (constante de eurler) e^x
floor: entero inferior más cercano
log: logaritmo neperiano (base e)
log10: logaritmo (base 10)
log2: logaritmo (base 2)
sin: seno
sinh: seno hiperbólico
sqrt: raíz cuadrada
tan: tangente
tanh: tangente hiperbólica
signum: función signo
Constantes numéricas
Exp4j incorpora las siguientes constantes:pi, π el valor de pi como se define en Math.PI, e la
constante de euler, φ el valor de la proporción aúrea (1.61803398874)
Notación científica
Es possible usar notación científica. El número se divide en base y exponent, de la forma yEx
que se evalúa como y*10^x. Hay que tener en cuenta que 'e/E' no es un operador sino parte de
un número, por lo tanto una expresión como: 1.1e-(x*2) no podría ser evaluada
Funciones personalizadas
Es possible extender la clase abstracta Function para usar funciones personalizadas en
expresiones. Solo hay que que implementar el método apply(double... args). En el
siguiente ejemplo se crea una función para el logaritmo en base 2 y se usa en la siguiente
expresión:
Ejemplo:
Function logb = new Function("logb", 2) {
@Override
public double apply(double... args) {
return Math.log(args[0]) / Math.log(args[1]);
18
}
};
double result = new ExpressionBuilder("logb(8, 2)")
.function(logb)
.build()
.evaluate();
double expected = 3;
assertEquals(expected, result, 0d);
Operadores personalizados
También es posible crear operadores personalizados extendiendo la clase abstracta Operator
para declarar operadores personalizados para ser usados en expresiones. El símbolo del
operador debe ser una cadena: !,#,§,$,&,;,:,~,<,>,|,=
Hay que tener en cuenta que añadir un operador con un símbolo usado previamente sobrescribe
cualquier operador existente, incluyendo los operadores incorporados de serie. Así, es posible
sobreescribir, por ejemplo, el operador +. El constructor de un operador toma hasta 4
argumentos.
El símbolo usado para la operación (!,#,§,$,&,;,:,~,<,>,|,=)
Si la operación es asociativa a la izquierda.
La precedencia de la operación
El número de operandos que tiene el operador (1 ó 2)
Ejemplo para la creación del operador factorial (!)
Operator factorial = new Operator("!", 1, true,
Operator.PRECEDENCE_POWER + 1) {
@Override
public double apply(double... args) {
final int arg = (int) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for
factorial has to be an integer");
}
if (arg < 0) {
19
throw new IllegalArgumentException("The operand of
the factorial can not be less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
double result = new ExpressionBuilder("3!")
.operator(factorial)
.build()
.evaluate();
double expected = 6d;
assertEquals(expected, result, 0d);
2.1.3.3.1 Motivación para la elección de Eval4j
La motivación para elegir esta librería para la evaluación de funciones han sido principalmente:
Lenguaje utilizado: Java. Es el mismo lenguaje que se usa en el resto del proyecto.
Código Fuente disponible. La integración es a nivel de código fuente y no han sido
necesarios artificios para la interconexión con la librería.
Ninguna dependencia externa. Al integrar esta librería no han sido necesarias importar
otras dependencias externas, manteniendo así el código limpio y simple.
Documentación. En la página [Exp4j 2017] hay disponible amplia documentación para
la instalación de la librería en varios entornos, entre los que se incluye Eclipse. Además
tiene ejemplos de uso y un detallado listado de las funciones y operadores soportados
así como las instrucciones para ampliarlos.
Frecuencia de actualización. El proyecto está soportado por una comunidad que lo
actualiza frecuentemente, liberando nuevas versiones cada seis meses
aproximadamente.
20
Licencia. Tiene licencia Apache, por lo que no vamos a tener ningún problema a la hora
de incorporarla al proyecto.
2.1.4 Recursos documentales
Los recursos documentales son las referencias que se detallan en el apartado “Referencias”
aunque con la alta informatización de todas las universidades y entidades académicas cualquier
publicación está disponible online en la WWW.
2.2 Planificación
El plan de trabajo que se ha seguido ha sido el que se muestra en la siguiente tabla de tareas
jerarquizadas:
Anteproyecto
Selección de director de proyecto
*Realizar Reunión Inicial
Elegir Tema
Estudiar Varias Opciones
Seleccionar una Opción
Estudio Preliminar
Estudio del tema actual del tema elegido
Estudio de las tecnologías
Estudio de la complejidad y la viabilidad
Estimación del esfuerzo
Tanteo Inicial de las tecnologías
Instalación de los entornos
Formación inicial
Desarrollo de una aplicación inicial sencilla de prueba con todas las librerías
Escribir documento de anteproyecto
Redactar
Revisar con el director
Entregar
Proyecto
Planificación
Desarrollo de la aplicación (Ciclo de vida iterativo)
Requerimientos
Análisis
Diseño
Implementación
Pruebas
Redacción de la memoria
Documentar/redactar/resumir
Construir diagramas
Presentación
Construir presentación
Ensayar lectura
Entrega
21
Revisar con el director
Encuadernar
Entregar documentación para trámite y depósito (F.Máx 15/12/2017)
Defensa
Lectura con presentación ante tribunal
Es un plan atemporal debido a que el autor, por motivos laborales, no ha podido establecer un
horario estable para dedicarle al proyecto aunque tiene una fecha inamovible: la fecha de
entrega de la documentación.
2.3 Estudio de Viabilidad y Gestión de Riesgos
2.3.1 Viabilidad
La viabilidad de un software es la medida del beneficio obtenido con su desarrollo, se estudia
si va a ser rentable. En este proyecto académico el autor quiere entender la viabilidad en el
sentido de factibilidad, ver si es posible desarrollar la aplicación con éxito, si se va a poder
llevar a buen término con los recursos y tiempo disponible. El estudio de viabilidad no pertenece
al análisis o diseño de un sistema, sino más bien es una actividad que debe de llevarse a lo largo
del ciclo de vida. La complejidad puede cambiar y un proyecto viable se puede hacer inviable.
Se establecen puntos de control para evaluar la viabilidad. En cualquiera de ellos puede
cancelarse el proyecto o revisarse el alcance. Como criterios de viabilidad podemos distinguir
cuatro áreas:
Viabilidad operativa: Mide el funcionamiento correcto de la solución frente a los
problemas de la organización.
Viabilidad técnica: Se refiere a ver si es posible construir la aplicación cumpliendo con
sus funciones, restricciones y con un rendimiento aceptable.
Viabilidad de fechas: Si es posible entregar dentro de los plazos.
Viabilidad económica: Es el análisis de coste y beneficios.
Para este proyecto sólo se ha considerado la viabilidad técnica y de fechas. El estudio de la
viabilidad de fechas ha consistido simplemente en valorar que el desarrollo no llevara más de
6 meses (teniendo en cuenta que se dedican unas 10 horas a la semana, principalmente los fines
de semana).
Para el estudio de la viabilidad técnica se llevaron una serie de tareas al inicio del proyecto:
22
Instalar los entornos de desarrollo junto con la biblioteca AIMA para confirmar que no
hubiera problemas posteriores.
Analizar la dificultad (coste en tiempo) de realizar un desarrollo en Java, puesto que es
un lenguaje que conozco pero a nivel académico (estimar la curva de aprendizaje).
Analizar la complejidad de las técnicas básicas de las redes neuronales
2.3.2 Riesgos
Los riesgos son amenazas a la viabilidad del software. La magnitud al riesgo se calcula como:
(Magnitud de un riesgo) = (Probabilidad de que suceda) x (Efecto dañino que provoca)
Ante el riesgo se puede actuar de varias maneras:
Evitarlo (no siempre es posible).
Atenuarlo (ejemplo: Ante la posibilidad de que se rompa el servidor se compra uno
mejor).
Asumirlo sin más.
Asumirlo con un plan de contingencia.
Hay dos tipos de riesgos:
Riesgos de proyecto: Los riesgos del proyecto amenazan al plan del proyecto. Si los
riesgos del proyecto aparecen las fechas se retrasan.
Riesgos técnicos: Los riesgos técnicos amenazan la calidad y la planificación temporal
del software.
A continuación se muestra el registro de riesgos. El registro de riesgos intenta medir cada riesgo
viendo la probabilidad de que el riesgo aparezca y viendo las consecuencias si ocurriera. El
registro de riesgos se actualiza conforme progresa el proyecto, identificando y analizando
riesgos nuevos que pudieran aparecer, respondiendo con soluciones antes tales riesgos.
Riesgo Prob. Daño Prevención Acc. Contingencia ¿Ocurrido?
Fallo irrecuperable del
portátil
Baja Alto - Adquirir equipo
nuevo
No
Pérdida total de los
ficheros del proyecto
Muy
baja
Muy
Alto
Copia de seguridad
redundante en la
nube y memoria
USB
- No
23
Pérdida parcial del
trabajo desde la última
copia de seguridad
Media Medio Disminuir el tiempo
entre copias
Rehacer el trabajo No
Retraso en el desarrollo
que impide cumplir los
tiempos
Alta Alta Sustituir desarrollos
pendientes por
librerías.
Sí. En un
principio estaba
previsto
desarrollar el
evaluador de
funciones. Al
final se
sustituyó por
Exp4j
Problemas de licencia o
patentes en las librerías
usadas
Alta Alta Estudiar las
licencias de las
librerías a utilizar
Buscar librería
alternativa
Sí. Se han
encontrado
varias librerías
propietarias con
restricciones
para la
distribución
2.4 Modelo Ciclo de Vida
El ciclo de vida son las fases por las que pasa el software que se está desarrollando desde que
nace hasta que es retirado o remplazado. Algunas de las funciones que tiene el ciclo de vida
son:
Establecer el orden de las fases.
Determinar los criterios de transición para pasar entre dos fases consecutivas.
Definir las entradas y salidas en cada fase.
Definir los estados por los que pasa el producto.
Definir las actividades que hay que hacer para transformar el producto.
Para el presente proyecto se ha optado por un ciclo de vida iterativo e incremental por ser el
más adecuado, ya que se parte de una sencilla aplicación inicial, con requerimientos aún por
definir y a la que se le van añadiendo las mejoras que se le van ocurriendo al autor y que le
parecen oportunas.
24
Un desarrollo iterativo e incremental se caracteriza porque se van generando versiones
entregables periódicamente, de forma iterativa, cada entrega supone un incremento respecto a
la anterior. Cada iteración del ciclo de vida incluye: planificación, análisis de requisitos, diseño,
codificación, pruebas y documentación.
El desarrollo iterativo no implica que sea incremental, ni tampoco lo contrario. Un desarrollo
iterativo se basa en refactorizaciones, en cada ciclo mejora más la calidad del producto. Pero
no implica incrementar la funcionalidad. Por otro lado, un desarrollo puramente incremental
puede ser, por ejemplo, agregar módulos en varias fases. Cada módulo añade más funcionalidad
al sistema.Si unimos el ciclo de vida iterativo y el incremental tras finalizar cada iteración se
consigue una versión más estable, con más calidad, y con nuevas funcionalidades respecto a
versiones anteriores.
No hay que confundir el ciclo de vida iterativo e incremental con el ciclo de vida en espiral. En
el ciclo de vida en espiral se generan prototipos y no una versión reducida del producto final.
Las fases siguen siendo lineales y sólo se realizan una única vez.
2.5 Metodologías
Se define una metodología como un conjunto de técnicas y métodos integrados que permiten
abordar de forma abierta y homogénea cada actividad del ciclo de vida de un proyecto. Es un
proceso completo y detallado. Las metodologías se apoyan en una combinación de los modelos
de ciclo de vida genéricos. Definen actividades, artefactos y roles, junto con técnicas y prácticas
recomendadas.
Se pueden clasificar las metodologías en dos grupos según la filosofía de desarrollo. Las
metodologías tradicionales, que requieren una planificación muy exhaustiva durante todo el
desarrollo, y las metodologías ágiles, en las que el desarrollo es sencillo, incremental,
cooperativo y adaptado.
A continuación se enumeran algunas diferencias entre los dos grupos de métodos:
Métodos Ágiles Métodos Tradicionales
Basados en heurísticos provenientes de la
práctica
Basados en normas provenientes de
estándares
Impuestos internamente Impuestos externamente
El cliente forma parte del equipo La comunicación con el cliente se realiza
mediante reuniones
Grupos pequeños Grupos grandes
+ artefactos - artefactos
+ roles - roles
25
+ énfasis en la arquitectura - énfasis en la arquitectura
+ flexibilidad - flexibilidad
Para el desarrollo del software del presente proyecto se ha optado por las metodologías ágiles,
pero no se ha utilizado ninguna en concreto, se han seguido los principios y la filosofía de
algunos de los métodos más usados, como la programación extrema (XP) o Scrum.
Los principios en los que se basa el desarrollo son:
Desarrollo iterativo e incremental.
Simplicidad en el código: Se simplifica el diseño para agilizar el desarrollo y facilitar el
mantenimiento.
Refactorización del código: Para que sea más fácil de mantener pero sin modificar su
comportamiento
Pruebas unitarias continuas y pruebas de regresión (cuando se hace refactorización).
Corrección los errores antes de añadir una nueva funcionalidad.
26
3 REDES NEURONALES
3.1 Introducción
Dentro de las redes neuronales artificiales, destaca el grupo de las redes supervisadas, en las
que existe un sistema supervisor que evalúa el comportamiento de la red, indicando si éste es
adecuado o no. Se necesitan un conjunto de datos de entrada previamente clasificado o cuya
respuesta objetivo se conoce. Entre los modelos más importantes dentro de este paradigma de
aprendizaje supervisado podemos encontrar a los perceptrones multicapa.
3.2 El Perceptrón Simple
3.2.1 Introducción
Una de las características más significativas de las redes neuronales es su capacidad para
aprender a partir de alguna fuente de información interactuando con su entorno. En 1958 el
psicólogo Frank Ronsenblant desarrolló un modelo simple de neurona basado en el modelo de
McCulloch y Pitts y en una regla de aprendizaje basada en la corrección del error. A este modelo
le llamó Perceptrón. Una de las características que más interés despertó de este modelo fue su
capacidad de aprender a reconocer patrones. El Perceptrón está constituido por un conjunto de
sensores de entrada que reciben los patrones de entrada a reconocer o clasificar y una neurona
de salida que se ocupa de clasificar a los patrones de entrada en dos clases, según que la salida
de la misma sea 1 (activada) o 0 (desactivada). Sin embargo, dicho modelo tenía muchas
limitaciones, como por ejemplo, no es capaz de aprender la función lógica XOR. Tuvieron que
pasar unos años hasta que se propusiera la regla de aprendizaje de retropropagación del error
para demostrarse que el Perceptrón multicapa es un aproximador universal.
3.2.2 El Perceptrón Simple
Supongamos que tenemos una función f de Rn en {-1,1}, que aplica un patrón de entrada x =
(x1,x2,…,xn)T ∈ Rn en la salida deseada z ∈ {-1,1}, es decir, f(x) = z. La información de que
disponemos sobre dicha función viene dada por p pares de patrones de entrenamiento
{x1,z1}, {x2,z2},…,{xp,zp}
donde xi∈Rn y f(xi) = zii∈{-1,1}, i=1,2,…,p. Dicha función realiza una partición en el espacio
Rn de patrones de entrada; por una parte estarían los patrones con salida +1 y por otra parte los
patrones con salida -1. Por lo tanto, diremos que la función f clasifica a los patrones de entrada
en dos clases. Ejemplos de funciones f de este tipo son la función lógica OR o la función par.
27
Ahora vamos a construir un dispositivo sencillo que aprenda dicha función a partir de
un conjunto conocido de patrones (relaciones) de entrenamiento. Para ello vamos a utilizar una
unidad de proceso bipolar que como vimos es una función matemática con dominio el
conjunto n-dimensional {-1,1}n y rango el conjunto {-1,1}, definida por la siguiente expresión:
Ilustración 1 El perceptrón Simple
donde los parámetros w1,w2,…,wn, se llaman pesos sinápticos y son los pesos con los que se
ponderan los valores de entrada x1,x2,…,xn, o argumentos de la función; la suma ponderada
se llama potencial sináptico y el parámetro se llama umbral o
sesgo. También se puede expresar la función f mediante la función signo, es decir,
siendo la función signo,
y diremos que en este caso la función de transferencia es la función signo. Análogamente, se
define una unidad de proceso binaria como una función matemática con dominio el conjunto
n-dimensional {0,1}n y rango el conjunto {0,1}, definida por la siguiente expresión:
28
Cuando la salida de la unidad de proceso es igual a 1 se dice que dicha unidad de proceso está
activada o encendida y presenta el estado 1, mientras que si su salida es igual a cero se dice
que está desactivada o apagada, presentando el estado 0.
Para la determinación de los pesos sinápticos y del umbral vamos a seguir un proceso adaptativo
que consiste en comenzar con unos valores iniciales aleatorios e ir modificándolos
iterativamente cuando la salida de la unidad no coincide con la salida deseada. La regla que
vamos a seguir para modificar los pesos sinápticos se conoce con el nombre de regla de
aprendizaje del Perceptrón simple y viene dada por la expresión: (con k=1,2,…)
siendo
esto nos indica que la variación del peso wj es proporcional al producto del error z(k) – y(k) por
la componente j-ésima del patrón de entrada que hemos introducido en la iteración k, es decir,
xj(k). La constante de proporcionalidad η(k) es un parámetro positivo que se llama tasa de
aprendizaje puesto que cuanto mayor es más se modifica el peso sináptico y viceversa. Es
decir, es el parámetro que controla el proceso de aprendizaje. Cuando es muy pequeño la red
aprende poco a poco. Cuando se toma constante en todas las iteraciones, η(k) = η > 0 tendremos
la regla de adaptación con incremento fijo.
Cuando la función de transferencia usada es la función signo (valores bipolares) la regla de
aprendizaje se puede escribir de la forma:
Por lo tanto, esta regla de aprendizaje es un método de detección del error y corrección. Solo
aprende, es decir, modifica los pesos, cuando se equivoca. Cuando tenemos un patrón que
pertenece a la primera clase (z(k)=1) y no es asignado a la misma, entonces corrige el valor del
peso sináptico añadiéndole una cantidad proporcional al valor de entrada, es decir lo refuerza,
mientras que si el patrón de entrada no pertenece a esta clase y el Perceptrón lo asigna a ella, lo
que hace es debilitar el peso restándole una cantidad proporcional al patrón de entrada . No
modificaremos los pesos cuando el valor deseado coincida con la salida de la red.
29
¿Cómo se modifica el sesgo? De la misma manera, teniendo en cuenta que el sesgo se puede
considerar como el peso sináptico correspondiente a un nuevo sensor de entrada que tiene
siempre una entrada igual a xn+1=−1, y como peso sináptico el valor del umbral, pues
cuando wn+1 =θ y xn+1= −1. Así, la red equivalente tendría n+1 sensores, su umbral sería
siempre cero, los patrones de entrada (x1, x2, …, xn) serán ahora (x1, x2, …, xn, -1), los pesos
asociados (w1, w2, …, wn) serán (w1, w2, …, wn, wn+1) con wn+1 = θ y la regla de aprendizaje:
, k=1,2
A partir de ahora vamos a considerar el umbral como un peso sináptico más.
Algoritmo de aprendizaje del Perceptrón con entrenamiento individualizado
Paso 0: Inicialización
Inicializar los pesos sinápticos con números aleatorios del intervalo [-1,1]. Ir al paso 1 con k=1
Paso 1: (k-ésima iteración)
Calcular
Paso 2: Corrección de los pesos sinápticos
Si z(k)y(k) modificar los pesos sinápticos según la expresión:
Paso 3: Parada
Si no se han modificado los pesos en las últimas p iteraciones, es decir,
, parar. La red se ha estabilizado.
En otro caso, ir al Paso 1 con k=k+1.
3.3 El Perceptrón Multicapa
El perceptrón multicapa (en adelante MLP, Multilayer Perceptron) es una red que utiliza
aprendizaje supervisado y por lo tanto es necesario conocer los valores que se corresponden
30
con cada una de las entradas para que así la red aprenda. Su objetivo es minimizar el error
cometido entre la salida obtenida de la red y la deseada, para ello utiliza el proceso de
retropropagación del error.
Ilustración 2 El perceptrón multicapa
Este tipo de red es un aproximador universal, siendo la función a aproximar la dada por los
pares: valor de entrada (dominio de la función) y valor de salida deseados (imagen de la
función). Esta característica diferencia al MLP de otras redes como las de aprendizaje no-
supervisado que tratan de descubrir patrones o rasgos en los datos de entrada sin necesidad de
un supervisor. Su topología consiste en una capa de entrada, otra de salida y una o más capas
ocultas, cada una de las capas tiene conexiones con la anterior y la posterior, salvo la primera
y la última que sólo tienen con la posterior y la anterior respectivamente.
31
Ilustración 3 Cálculo de la salida del MLP
3.4 Aplicaciones
Las múltiples aplicaciones del MLP pueden ser adaptadas como prácticas de laboratorio de la
asignatura, permitiendo usar las librerías que vamos a desarrollar como base para la
construcción de la solución. Dada la naturaleza parametrizable de dichas librerías, permitirá al
usuario abstraerse de la implementación, permitiendo trabajar a un nivel conceptual mayor.
Entre otras aplicaciones susceptibles de ser tratadas en el laboratorio podemos citar:
Clasificación de caracteres (segmentación). Se entrena el MLP con puntos de
segmentación predefinidos, se realiza la segmentación de la palabra usando algoritmos
heurísticos y se evalúan los puntos de segmentación con el MLP entrenado previamente,
eliminando los incorrectos. De esta manera conseguimos separar los caracteres.
Ilustración 4 Aplicación del MLP: Clasificación de caracteres
32
Compresión y codificación de la información. Considérese un MLP de 3 capas, una
de entrada, una oculta y la de salida. La capa de entrada está formada por N neuronas,
la capa oculta por M (M < N) neuronas y la capa de salida posee N neuronas al igual
que la capa de entrada. Se entrena dicho MLP para que cuando se le dé como entrada
un vector de datos (x1, x2,..., xN) devuelva ese mismo vector con M datos como salida,
con ello estamos enseñando al MLP a transformar un vector de N componentes en uno
de M componentes (recordemos que M < N) y a recuperar el vector original a partir del
vector "comprimido".
Una vez que el MLP esté entrenado se procede de la siguiente forma:
•Compresión: Para comprimir los datos utilizamos un MLP de dos capas, la de
entrada con N neuronas y la de salida con M, los pesos de estas dos capas son
los de la capa de entrada y oculta respectivamente, del MLP que entrenamos
anteriormente.
•Descompresión: Para descomprimir los datos utilizamos un MLP de dos capas,
la de entrada con M neuronas y la de salida con N, los pesos de estas dos capas
son los de la capa oculta y la de salida respectivamente, del MLP que entrenamos
anteriormente.
El MLP no conseguirá (al menos normalmente) un error nulo durante el entrenamiento, por lo
que se trata de un sistema de compresión con pérdidas. Obviamente cuanto mayor queramos
que sea el factor de compresión, más error se cometerá.
Ilustración 5 Aplicación del MLP: Compresión
33
Reconocimiento de palabras. Definimos un MLP con una capa de entrada con N
neuronas (la longitud de la palabra mas larga en el diccionario) y una capa de salida con
el número de palabras en el diccionario, indicando cada una la probabilidad de la
palabra. Para convertir la palabra en valores de entrada se usa el número de carácter
ASCII dividido por cien.
Ilustración 6 Aplicación del MLP: Reconocimiento de palabras
Detección facial. Después de una fase de preproceso se extraen las caras candidatas en
una matriz de NxN pixeles, siendo esta la capa de entrada . La decisión de asignar una
u otra identidad generalmente se basa en la aplicación de valores umbrales. Obviamente,
el MLP debe ser entrenado con un grupo de las caras de muestra.
Ilustración 7 Aplicación del MLP: Detección Facial
34
Segmentación de imágenes. Se trabaja con ventanas de (2n+1) pixeles (con n>0)
siendo el pixel analizado el de las coordenadas (n+1,n+1). El tamaño de la capa de
entrada queda estableido a (2n+1)2. El número de nodos en la capa oculta es sumamente
importante y condiciona el rendimiento de la red durante la clasificación , es necesario
un número grande de neuronas ocultas directamente proporcional al tamaño del
vecindario elegido. Finalmente, cada nodo de la capa de salida representa una clase
distinta. La red se entrena para que en cada instante sólo una neurona arroje el valor 1,
mientras que la otra permanece en cero.
Ilustración 8 Aplicación del MLP: Segmentación de Imágenes
Aproximación de funciones. Para aproximar una función f: ℝ𝑛 ⇒ ℝ𝑚 definimos un
MLP con tantas n entradas y m salidas. Entrenamos a la función con un subconjunto
de los valores de la función, incluso añadiéndole un pequeño porcentaje de error. Para
n=1 y m=1, tendremos un MLP con una única entrada, capa oculta y una única salida:
Ilustración 9 Aplicación del MLP: Aproximación de funciones
35
3.5 Métodos de aprendizaje
3.5.1 Algoritmo de Retropropagación: la regla delta
Se trata de determinar los pesos de las conexiones sinápticas entre las unidades de proceso de
manera que las salidas de la red coincidan con las salidas deseadas, o por lo menos, sean lo más
próximas posibles. Es decir, se trata de determinar los pesos de manera que el error total sea
mínimo:
Aunque minimizar dicha expresión es igual que minimizarla sin dividir por dos, pero hemos
divido por dos para que resulte más simplificada la derivación posterior que vamos a realizar.
El algoritmo de retropropagación utiliza el método del descenso por el gradiente y realiza un
ajuste de los pesos comenzando por la capa de salida, según el error cometido, y se procede
propagando el error a las capas anteriores, de atrás hacia delante, hasta llegar a la capa de las
unidades de entrada.
Una característica importante de este algoritmo es su capacidad para organizar el conocimiento
de la capa intermedia de manera que se pueda conseguir cualquier correspondencia entre la
capa de entrada y la de salida.
El funcionamiento del algoritmo de retropropagación del error es el siguiente: Dado un patrón
de entrada se aplica como estímulo a la primera capa de neuronas de la red, se va propagando
por las siguientes capas hasta que llega a la capa de salida, donde se compara la salida obtenida
con la deseada. A continuación se calcula el error para cada neurona de salida, y este valor de
error es transmitido hacia atrás, por todas las capas intermedias, y se van modificando sus pesos
sinápticos según dicho error y los valores de las salidas de las unidades de proceso precedentes
ponderados por sus pesos sinápticos.
Supongamos que estamos en la iteración k donde hemos introducido el patrón cuya salida de
unidad i es yi(k) y la salida deseada zi(k), siendo los pesos sinápticos wij(k) y tjr(k). i=1,2,…,M,
36
j=1,2,…,L, r=1,2,…,N. Entonces la regla de modificación de los pesos sinápticos de la capa de
salida será:
donde
Obsérvese que si tomamos como función de transferencia g1 la función logística entonces:
si tomamos la función tangente hiperbólica entonces
y si tomamos la función identidad,
Vamos a llamar el término delta a la siguiente expresión:
Es la cantidad que se va a ir propagándose hacia atrás.
Por lo tanto,
Ahora vamos a ver la variación de pesos de la capa anterior, utilizando la regla de derivación
en cadena,
37
Si llamamos
tenemos que la variación de peso tjr viene dada por la siguiente expresión
El error que hemos calculado es el cometido al introducir el k-ésimo patrón, es decir, es un error
individualizado porque se realiza para cada patrón, por ello diremos que es la regla de
entrenamiento individualizado.
También podemos realizar un entrenamiento por lotes, en cuyo caso se introducen los p
patrones simultáneamente y se evalúan las salidas de la red comparándolas con las salidas
deseadas. En este caso, los pesos sinápticos no dependen de k pues se cambian solo después de
evaluar todos los patrones. En este caso tomamos también la función de error total, pero la
dividiremos por p para interpretarla como error cuadrático medio por patrón:
Así,
38
3.5.2 Aprendizaje con Momentos: La regla delta generalizada
Hemos visto que el algoritmo de retropropagación se basa en el método del gradiente, con este
método nos vamos acercando al mínimo de la función de error cuadrático mediante el descenso
por el gradiente. Sin embargo, la función de error suele tener un gran número de mínimos
locales y el algoritmo de aprendizaje puede quedar atrapado en un mínimo local no deseado,
puesto que en él el gradiente vale cero. Para prevenir que el algoritmo de aprendizaje quede
atrapado en mínimos locales podemos hacer que los cambios de los pesos sinápticos dependan
del gradiente medio de los puntos de un entorno, en lugar de depender del gradiente de un solo
punto, pero dicha modificación suele requerir un gran esfuerzo computacional y no resultar
eficiente. Por ello, Hinton y Williams (1986) propusieron que se tuviera en cuenta también el
gradiente de la iteración anterior y realizar un promedio con el gradiente de la iteración actual.
Dicho promedio produce un efecto de reducción drástica de las fluctuaciones del gradiente en
iteraciones consecutivas, puesto que con frecuencia se produce cierto zigzagueo en el descenso
por el gradiente que hace que el algoritmo sea lento (poco eficiente). Por lo tanto, para atenuar
en cierta manera trayectos de descenso en zigzag se modifica la regla de retropropagación
teniendo en cuenta el descenso seguido en el paso anterior y descendiendo por una dirección
intermedia entre la dirección marcada por el 95 gradiente (en sentido opuesto) y la dirección
seguida en la iteración anterior, como se expresa en la siguiente ecuación:
que corresponde al incremento del peso sináptico de la componente j de la unidad de proceso.
Llamamos a α constante de momentos (0 ≤α < 1), pues es la que controla el grado de
modificación de los pesos teniendo en cuenta la modificación en la etapa anterior. Cuandoα =
0 tenemos la regla delta (por eso se la conoce como regla delta generalizada).
La inclusión del término momento en el algoritmo de retropropagación tiende a acelerar la
bajada en las direcciones similares de descenso al ir acumulando dichos valores (Δwij(k)).
39
Mientras que si tenemos direcciones con oscilaciones de signo en iteraciones consecutivas se
ajustaran los pesos en cantidades pequeñas, es decir, tendrá un efecto estabilizador.
Ilustración 10 Aprendizaje por Momentos
3.5.3 Algoritmo de Levenberg-Marquartd
El algoritmo de Levenberg-Maquardt es una técnica de segundo orden para resolver problemas
de optimización que suele ser más eficiente que el método clásico del descenso por gradiente
aunque requiere más memoria. Hagan y Menhaj [Hagan 94] lo han aplicado a la determinación
de los pesos sinápticos en un Perceptrón Multicapa. Si el Perceptrón tiene N unidades de salida
entonces se tratará de minimizar la función:
con respecto al vector , donde
La derivada de la función E(w) tiene la siguiente forma:
donde J es la matriz Jacobiana de e(w):
40
La matriz Hessiana viene dada por:
El término S(w) es la parte de H(w) que es una función de las derivadas segundas de e(w).
Como g(w) es el gradiente de la función de error E(w), si tomamos
entonces, para M(w)=HI y , obtenemos el método del descenso de mayor pendiente, y
para y , el método de Newton.
Un compromiso entre estos dos métodos se consigue al tomar donde es
algún valor no negativo. Siempre hay un E que hace que M sea definida positiva. Así,
tomaremos, en la iteración k, la dirección de descenso
y determinaremos los nuevos pesos según la expresión
Un valor ideal de sería aquel que minimizara .
Ante la dificultad que presenta a veces el cálculo de la matriz E, si sustituimos la matriz H por
el término de primer orden , suponiendo así que , obtenemos el método
de Levenberg-Maquardt, en el que:
41
El parámetro se incrementa o disminuye en cada paso:
Si , entonces en la siguiente iteración el parámetro se divide
por algún factor (se suele tomar =10).
Si entonces en la siguiente iteración el parámetro se
multiplica por el factor (se suele tomar de partida =0.01).
Asimismo, si en el método de Newton aproximamos la matriz Hessiana H(w) por
(suponiendo ), obtenemos el método de Gauss-Newton, donde
Obsérvese que cuando es pequeño el algoritmo de Levenberg-Marquardt se corresponde con
el algoritmo de Gauss-Newton y cuando es grande se corresponde con el algoritmo del
descenso de mayor pendiente (con longitud de paso 1/ ).
Ilustración 11 Algoritmo de Levenberg-Marquardt
42
4 DESARROLLO
4.1 Análisis
4.1.1 Introducción
El primer requisito de la librería es que debe integrarse con el framework actual de las prácticas,
que se basa en la librería de AIMA. El segundo es que debe implementar el algoritmo de
aprendizaje de Levenberg-Marquartd.
Dentro del Análisis se distinguirán los siguientes apartados:
Aplicación existente “Neural aproximator Application” que hace un uso intensivo de la
librería AIMA [AIMACODE 2017] (aima-core y aima-gui) y requiere un análisis en
profundidad para detectar posibles modificaciones y/o incompatibilidades.
Interfaz gráfico: Entrada libre de la función a aproximar por parte del usuario y la
elección del tipo de aprendizaje.
Método de aprendizaje de Levenberg-Marquardt
4.1.2 Neural Aproximator Application
La aplicación existente que vamos a modificar para que trabaje con nuestra librería se llama
NeuralApp, la cual hereda de la clase base SimpleAgentApp del paquete
aima.gui.framework
43
Ilustración 12 Diagrama de clases versión anterior: Clases del Entorno
Estas clases son las encargadas de mostrar el interfaz gráfico con los diferentes parámetros,
ejecutar la rutina de aprendizaje de la red neuronal y dibujar la gráfica con los resultados:
NeuralView: muestra la gráfica con los resultados y el log de la derecha.
NeuralFrame: define el nº de combos del frame superior y el contenido de estos. Las
rutinas para mostrar los combos y los botones se realizan en una clase superior de aima-
gui.
NeuralController: recibe los eventos de “Clear”, “Prepare”, “Run” y “Step”.
Dependiendo de los valores de los combos del frame superior genera los valores de la
función que servirán como entrenamiento y se los pasa a la red neuronal. Aquí es donde
se crea la red neuronal.
44
Ilustración 13 Interfaz de usuario versión anterior
Hasta ahora todos las clases son de la librería aima-gui, ahora veremos la interconexión con la
librería aima-core, esta se produce entre la clase NeuralEnvironment y la clase
FeedFordwardNetwork (el perceptrón multicapa, no confundir con la clase Perceptron
que implementa el perceptrón simple). Como podemos ver en el diagrama de clases, la red
neuronal tiene como parámetro la clase abstracta NNTrainingScheme que es implementada
por la clase BackPropLearning (aprendizaje por la regla de retropropagación)
45
Ilustración 14 Diagrama de clases versión anterior: MLP y Backpropagation
Si profundizamos en la implementación de FeedFordwardNetwork vemos que implementa
un red neuronal con una capa de entrada, una capa oculta y otra de salida. Esta restricción es
importante ya que nos impide realizar uno de los objetivos “deseables” de la aplicación:
parametrizar el número de capas ocultas. La opción de modificar la red neuronal para permitir
N capas ocultas se desechó porque implicaba modificar también el método de retropropagación
lo cual queda fuera de los objetivos de este proyecto.
46
Ilustración 15 Diagrama de clases versión anterior: Detalle MLP y Layer
La clase LayerSensitivity está asociada a Layer y guarda los valores de las derivadas
parciales que serán usadas en la retropropagación.
Como último punto a tener en cuenta, el método de aprendizaje de Levenberg-Marquardt está
optimizado para entrenamiento por Lotes (todos los patrones a la vez) y el de Retropropagación
es online (un patrón en cada iteración). Al analizar la implementación de
FeedFordwardNeuralNetwork se comprueba que no contempla el entrenamiento por
lotes.
public void trainOn(NNDataSet innds, int numberofEpochs) {
for (int i = 0; i < numberofEpochs; i++) {
innds.refreshDataset();
while (innds.hasMoreExamples()) {
NNExample nne = innds.getExampleAtRandom();
processInput(nne.getInput());
Vector error = getOutputLayer()
.errorVectorFrom(nne.getTarget());
47
processError(error);
}
}
}
4.1.2.1 Requerimientos
Tras analizar las librerías aima, se llega a la conclusión de que el proyecto debe cumplir los
siguientes requisitos:
Debe trabajar con 1 capa de entrada, 1 capa oculta y 1 capa de salida.
Debe heredar de la clase abstracta NNTrainingScheme e implementar sus métodos.
FeedFordwardNeuralNetwork debe permitir el entrenamiento por lotes a la vez
que el entrenamiento online, ya que queremos que ambos métodos puedan ejecutarse.
Para la creación del interfaz gráfico se debe modificar la clase NeuralFrame: aquí deben
crearse el combo y la caja de texto para introducir la función a aproximar.
La creación de la red neuronal teniendo en cuenta los nuevos parámetros añadidos al
interfaz debe realizarse en NeuralController
4.1.3 Entrada libre de la función a aproximar
Vamos a definir los casos de uso actuales, que debemos mantener, y los nuevos asociados
a los requisitos.
Ilustración 16 Caso de uso Versión anterior
48
Con la inclusión del requisito de la entrada libre de funciones, añadimos también el caso de
uso:
Ilustración 17 Caso de uso Nueva versión
Diagramas de Secuencia
El diagrama de secuencia para el comportamiento inicial de la aplicación para RUN:
Ilustración 18 Diagrama de Secuencia para RUN
Vemos que el usuario debe seleccionar la función predefinida y el nº de neuronas en la capa
oculta y al pulsar en el botón PREPARE se actualiza la gráfica de la función, mostrando los
valores que se van a usar como entrenamiento (los de la función más un % de error) Para la
opción de STEP (un solo paso) tenemos el diagrama de secuencia:
49
Ilustración 19 Diagrama de secuencia para STEP
Como uno de los objetivos del proyecto consiste en permitir la entrada libre de la función a
aproximar, esto es, escrita en una caja de texto, tenemos que añadir un paso más en el
comportamiento para que el usuario decida si va a usar alguna de las funciones predefinidas o
la entrada libre. Para el caso de STEP cambian el último paso, como en el diagrama de secuencia
anterior.
50
Ilustración 20 Diagrama de secuencia Modo función predefinida
El Diagrama de secuencia para la selección del modo de entrada libre de función:
51
Ilustración 21 Diagrama de secuencia Modo entrada libre de función
En lo que respecta a la evaluación de la función, vemos en el diagrama de clases que se realiza
en FunctionNNDataSet. Aquí se construyen los conjuntos de datos para el entrenamiento
(lo de la función mas un % de error) y el resto de valores de test (los de la función). Esta clase
implementa las funciones predefinidas a evaluar y tendremos que heredar de ella para generar
nuestra propia clase que use la librería Exp4j, evitando así tener que codificar en java cada
función a aproximar.
La librería Exp4j no necesita un análisis exhaustivo ya que funciona como una caja negra, sin
dependencias y con una entrada y salida bien definidas:
52
Ilustración 22 Diagrama de secuencia Exp4j
4.1.4 Requerimientos
Con los casos de uso y los diagramas de secuencia generados desarrollados llegamos a la
conclusión de que la modificación necesaria en el interfaz de usuario es un selector (combobox)
que indique si vamos a seleccionar una de las funciones predefinidas (el comportamiento
anterior) o si vamos a permitir la entrada de la función como texto libre.
Si se selecciona el modo de entrada de texto libre para la función debe mostrar un campo de
texto donde el usuario escriba la función.
El resto del comportamiento para PREPARE, RUN; STEP permanece igual que en la aplicación
original.
4.1.5 Método de aprendizaje Levenberg-Marquardt
El método de aprendizaje por lotes, a priori, tiene un comportamiento al estilo “caja negra”.
Tiene como entrada una red neuronal y unos valores de entrenamiento y como salida la red
neuronal con los pesos sinápticos ajustados.
Ilustración 23 Diagrama de secuencia LM
53
Debido a que el entorno de pruebas permite ejecuciones paso a paso “STEP” tenemos que
cambiar este comportamiento del método LM para que se asemeje al que implementa el método
de retropropagación. En lugar de dar como salida la red neuronal entrenada llegando al error
mínimo posible, guardaremos el estado de la red y la devolveremos en cuanto se produzca un
decrecimiento del error. Al guardar el estado podremos continuar en la siguiente iteración
“STEP” fácilmente.
Ilustración 24 Diagrama de Máquina de estados LM
4.2 Diseño
4.2.1 Interfaz
De los requerimientos surgidos en el análisis surge la necesidad de modificar el interfaz gráfico
para que permita la entrada libre de la función a aproximar. Por otro lado, y aplicando el
principio básico de UX (experiencia de usuario) de familiaridad, el usuario debe seguir
trabajando como en la versión anterior, seleccionando las funciones predefinidas de forma
sencilla.
Para diseñar el nuevo interfaz partidos de la maqueta del anterior:
54
Ilustración 25 Maqueta del interfaz existente
Las secciones marcadas con 1 y 2 son las de la gráfica con los resultados y del log,
respectivamente, y no se van a ver alteradas.
Las opciones posibles del combo de las funciones predefinidas en la aplicación original:
Squared Cosine
Humps Function
Las opciones posibles del combo del tipo de red en la aplicación original:
FeedFordward (10 hidden neurons)
FeedFordward (20 hidden neurons)
FeedFordward (50 hidden neurons)
Para el diseño del nuevo interfaz tenemos que tener en cuenta que debemos añadir un combo
para seleccionar el tipo de función a aproximar (libre o las predefinidas) y por otro lado
seleccionar el método de aprendizaje (Retropropagación o LM).
Como se detectó durante la fase del análisis la clase NeuralFrame se encarga de crear los
combos en la pantalla principal así como de crear su contenido.
55
Ilustración 26 Detalle Maqueta Nuevos Controles
Las opciones posibles del primer combo son:
Predefined Function
Evaluate Expression
Las opciones posibles del combo de las funciones predefinidas quedan inalteradas.
Las opciones posibles del combo del tipo de red se han modificado y ampliado para permitir
elegir entre el método de Retropropagación (Backpropagation BP) o Levenberg-Marquardt
(LM). Además se han abreviado los nombres para no tene que aumentar el ancho de la ventana.
Las opciones posibles quedan de la siguiente manera:
BP (10 hn)
BP (20 hn)
BP (50 hn)
LM (10 hn)
LM (20 hn)
LM (50 hn)
Donde hn es la abreviatura de “hidden neurons”.
Al seleccionar en el primer combo la opción de “Evaluate expression”, se oculta el segundo
combo con las funciones predefinidas, y aparece un campo de texto libre en el que el usuario
puede introducir la función a aproximar.
Ilustración 27 Detalle Maqueta modo entrada libre de función
La validación de la función se delega en la ejecución, así conseguimos simplificar las
interacciones con el interfaz de usuario. En caso de error, sintáctico o de evaluación, la librería
Exp4j lanza una excepción que se puede capturar y mostrarla en la sección del log. Esto no
supone ningún coste adicional ya que siempre hay que controlar los errores de evaluación, ya
que, por ejemplo, la función 1/x es correcta sintácticamente pero no puede ser evaluada en x=0.
56
4.2.1.1 Clases
En el análisis de la biblioteca aima se determinaron los requisitos de herencia y que clases
habría que modificar para soportar las especificaciones.
En el diagrama de clases siguiente se han marcado las clases nuevas con un círculo rojo y con
un cuadrado verde las clases ya existentes que han necesitado ser modificadas.
4.2.1.1.1 Clases a modificar
NeuralFrame: Aquí hay que crear los nuevos controles de usuario y definir el
contenido de los combos. En los nuevos controles se declara LMActionListener
como receptor de los eventos.
NeuralController: Aquí hay que leer el contenido de los controles y crear la red
neuronal, con el método de aprendizaje y los datos del entrenamiento, que dependen del
tipo de función seleccionada. En el caso de ser funciones predefinidas se generarán
como en la versión anterior (FunctionNNDataset) y si el usuario ha seleccionado
la entrada libre de la función se tendrá que llamar a CustomFunctionNNDataset
NNTrainingScheme: Aquí es necesario añadir un método que devuelva si el método
de entrenamiento es online o por lotes. Este método lo usará la red neuronal
FeedForwardNeuralNetwork.
FeedForwardNeuralNetwork: A la hora de realizar el entrenamiento es necesario saber
si se es online o por lotes, y realizar las llamadas patrón a patrón o todos a la vez.
57
Ilustración 28 Diagrama de clases nueva versión
4.2.1.1.2 Clases Nuevas
LMActionListener. Para controlar el cambio del Nuevo combo en el interfaz del
tipo de función (predefinida o entrada libre) necesitamos una clase que herede de
ActionListener y reciba el evento al cambiar la opción seleccionada. Al
seleccionar funciones predefinidas ocultará el cuadro de texto de entrada libre y
mostrará el combo de funciones predefinidas. Al seleccionar función personalizada se
hará justo lo contrario.
CustomFunctionNNDataset: La clase FunctionNNDataSet genera un
NNDataSet para el entrenamiento y pruebas de la red neuronal, pero lo hace utilizando
las funciones que tiene codificadas. Para poder utilizar los valores resultantes de la
evaluación de la función introducida por el usuario crearemos una clase hija que evalúe
la función realizando llamadas a la librería Exp4j. No se ha heredado directamente de
NNDataSet porque hay varios métodos en NNTrainingScheme y
BackPropLearning que tienen como parámetro un objeto de la clase
FunctionNNDataSet
58
LM: Aquí se implementará el algoritmo de Levenberg-Marquardt propiamente dicho.
Para que pueda funcionar con el resto de clases es necesario que implemente el interfaz
NNTrainingScheme.
LMSensitivity: Debido a que el proceso de cálculo hacia atrás del hessiano es
necesario ir guardando en cada capa los valores de la derivada segunda, se creará esta
clase de manera simétrica a LayerSensitivy que ya usa BackPropLearning.
4.3 Implementación
Para la implementación se ha usado el IDE eclipse [Eclipse2017] con la extensión JDT (Java
Development Toolkit). La estructura del proyecto quedaría de la siguiente manera:
Ilustración 29 Detalle Estructura del Proyecto en Eclipse
59
A continuación se muestra los detalles más significativos de la de cada una de las clases
definidas durante el diseño, se ocultan algunos detalles poco relevantes que restarían claridad a
la explicación y que pueden ser consultados en el código fuente que acompaña a este proyecto.
4.3.1 Interfaz de usuario
Como se ha descrito en las maquetas del interfaz durante la fase de diseño se van a añadir
nuevos controles en el interfaz de usuario.
El selector de tipo de función:
Ilustración 30 Detalle UI nueva versión: Tipo de función
Predefined function: Función predefinida (por defecto). Si se selecciona se evalúa la
función seleccionada en el siguiente combo
Evaluate expression: Entrada libre de cadena. Si se selecciona se muestra una caja de
texto para introducir la función a evaluar.
Si se selecciona “Predefined function” se muestra el selector de funciones predefinidas, ya
implementadas en la versión anterior
Ilustración 31 Detalle UI nueva versión: Funciones predefinidas
Si se selecciona “Evaluate expression”, se muestra la control caja de texto que permite
introducir la función y se oculta el combo con las funciones predefinidas.
A continuación se muestran varias funciones usadas durante la depuración de los diferentes
módulos del programa que sirven para mostrar la potencia a la hora de evaluar funciones de la
librería Exp4j
Función: x^2*sin(pi*x)
60
Ilustración 32 Ejemplo de función libre: x^2*sin(pi*x)
Función: 0.5*e^x + sin(x^2)^2
Ilustración 33 Ejemplo de función libre: 0.5*e^x + sin(x^2)^2
61
El selector de tipo de red y topología:
Ilustración 34 Detalle de UI Nueva Versión: Tipo de red
Con un mismo control nos permite elegir el método de entrenamiento (Backpropagation BP o
Levenberg-Marquardt LM) y el número de neuronas en la capa oculta (hn = hidden neurons)
4.3.2 Clases Modificadas
4.3.2.1 NeuralFrame
Método orginal:
public NeuralFrame() {
setTitle("Neural Aproximator Application");
setSelectors(new String[] {DATASET_SEL, NEURAL_SEL},
new String[] {"Select DataSet", "Select Neural Network "});
setSelectorItems(DATASET_SEL, FunctionNNDataSet.FCN_NAME,
0);
setSelectorItems(NEURAL_SEL, new String[] {
"FeedForward (10 hidden neurons)",
"FeedForward (10 hidden neurons)",
"FeedForward (10 hidden neurons)",
}, 0);
setEnvView(new NeuralView());
setSize(640,480);
}
Método modificado:
Siguiendo el diseño, en el constructor de la clase NeuralFrame se deben añadir los nuevos
controles. La clase padre implementa métodos para agregar fácilmente nuevos combos en la
62
parte superior de la ventana, así como para recoger sus eventos. Para no añadir código de esta
implementación en las clases bases de aima se crean los nuevos controles en el constructor de
esta clase y se establece la clase que escuchará los eventos del cambio en el combobox de
selección de tipo de función. También se modificar el tamaño de la ventana, para que haya
espacio suficiente para los nuevos controles.
public NeuralFrame() {
setTitle("Neural Aproximator Application");
setSelectors(new String[] { FUNCTION_SEL, DATASET_SEL,
NEURAL_SEL},
new String[] {"Function Type","Select DataSet", "Select
Neural Network (BP=BackPropagation, LM=Levenberg-Marquartd"});
setSelectorItems(FUNCTION_SEL, new String[] {
"Predefined function",
"Evaluate expression"
}, 0);
setSelectorItems(DATASET_SEL, FunctionNNDataSet.FCN_NAME,
0);
setSelectorItems(NEURAL_SEL, new String[] {
"BP (10 hn)",
"BP (20 hn)",
"BP (50 hn)",
"LM (10 hn)",
"LM (20 hn)",
"LM (50 hn)",
}, 0);
JPanel cont = (JPanel) this.getContentPane();
JPanel p1 = (JPanel) cont.getComponents()[0];
JToolBar tb = (JToolBar) p1.getComponent(0);
function_textfield = new javax.swing.JTextField();
TypeFunctionCombo = (JComboBox) tb.getComponent(0);
PredefinedFunctionsCombo = (JComboBox) tb.getComponent(1);
LMActionListener al = new LMActionListener();
TypeFunctionCombo.addActionListener(al);
63
function_textfield.setVisible(false);
tb.add(function_textfield,1);
setEnvView(new NeuralView());
setSize(1024,768);
}
Por otro lado, se añaden métodos para recuperar los valores de los controles del interfaz, que
usarán la clase a la hora NeuralController de crear la red neuronal:
public String getFormulaString(){
if (this.function_textfield != null){
return function_textfield.getText();
}else{
return "";
}
public boolean customformula(){
return this.TypeFunctionCombo.getSelectedIndex() == 1;
}
4.3.2.2 NeuralController
Método original
public void prepare(String changedSelector) {
epochs = 0;
if(changedSelector != null) clear();
else {
try {
NNDataSet ds;
NNDataSet ds_test;
NeuralFrame Nframe =((NeuralFrame) frame);
ds = new FunctionNNDataSet(
frame.getSelection().getIndex(NeuralFrame.DATASET_SEL));
ds_test = new FunctionNNDataSet(
2+frame.getSelection().getIndex(NeuralFrame.DATASET_SEL));
NNConfig config = new NNConfig();
64
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_INPUTS, 1);
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_OUTPUTS, 1);
config.setConfig(
FeedForwardNeuralNetwork.LOWER_LIMIT_WEIGHTS, -2.0);
config.setConfig(
FeedForwardNeuralNetwork.UPPER_LIMIT_WEIGHTS, 2.0);
switch( frame.getSelection().getIndex(
NeuralFrame.NEURAL_SEL)) {
case 0: // 10 hidden neurons
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_HIDDEN_NEURONS,10);
break;
case 1: // 20 hidden neurons
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_HIDDEN_NEURONS, 20);
break;
case 2: // 50 hidden neurons
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_HIDDEN_NEURONS, number_hd);
break;
}
FeedForwardNeuralNetwork ffnn = new
FeedForwardNeuralNetwork(config);
ffnn.setTrainingScheme(new
BackPropLearning(0.1, 0.9));
env = new NeuralEnvironment(ds, ds_test,
ffnn);
frame.getEnvView(). setEnvironment(env);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Método modificado
65
Para crear la red neuronal se consulta el valor de los controles, haciendo uso de los métodos
creados en NeuralFrame para tal fin. Aquí también se llama la clase
CustomFunctionNNDataset para la evaluación de la función a evaluar escrita por el
usuario, que es el punto de unión con la librería Exp4j.
public void prepare(String changedSelector) {
epochs = 0;
if(changedSelector != null) clear();
else {
try {
NNDataSet ds;
NNDataSet ds_test;
NeuralFrame Nframe =((NeuralFrame) frame);
String f;
if (Nframe.customformula()){
f = Nframe.getFormulaString();
try{
ds_test = new
CustomFunctionNNDataSet(f,false);
ds = new
CustomFunctionNNDataSet(f,true);
}catch (Exception ex){
this.frame.getMessageLogger().log("Error:
Something went wrong evaluating expression:" +
ex.getMessage());
return;
}
}else{
ds = new FunctionNNDataSet(
frame.getSelection().getIndex(NeuralFrame.DATASET_SEL));
ds_test = new FunctionNNDataSet(
2+frame.getSelection().getIndex(NeuralFrame.DATASET_SEL));
}
NNConfig config = new NNConfig();
66
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_INPUTS, 1);
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_OUTPUTS, 1);
config.setConfig(
FeedForwardNeuralNetwork.LOWER_LIMIT_WEIGHTS, -2.0);
config.setConfig(
FeedForwardNeuralNetwork.UPPER_LIMIT_WEIGHTS, 2.0);
int number_hd=10;
NNTrainingScheme ts = null;
NNTrainingScheme bp = new BackPropLearning(0.1,
0.9);
NNTrainingScheme lm = new LM(0.1,5, 0.9);
switch( frame.getSelection().getIndex(
NeuralFrame.NEURAL_SEL)) {
case 0: // 10 hidden neurons
number_hd = 10;
ts = bp;
break;
case 1: // 20 hidden neurons
number_hd = 20;
ts = bp;
break;
case 2: // 50 hidden neurons
number_hd = 50;
ts = bp;
break;
case 3: // 10 hidden neurons
number_hd = 10;
ts = lm;
break;
case 4: // 20 hidden neurons
number_hd = 20;
ts = lm;
67
break;
case 5: // 50 hidden neurons
number_hd = 50;
ts = lm;
break;
}
config.setConfig(
FeedForwardNeuralNetwork.NUMBER_OF_HIDDEN_NEURONS, number_hd);
FeedForwardNeuralNetwork ffnn = new
FeedForwardNeuralNetwork(config);
ffnn.setTrainingScheme(ts);
env = new NeuralEnvironment(ds, ds_test,
ffnn);
frame.getEnvView(). setEnvironment(env);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.3.2.3 NNTrainingScheme
Interfaz original:
public interface NNTrainingScheme {
Vector processInput(FeedForwardNeuralNetwork network,
Vector input);
void processError(FeedForwardNeuralNetwork network, Vector
error);
void setNeuralNetwork(FunctionApproximator ffnn);
}
Para dar la funcionalidad de aprendizaje por lotes se añaden métodos en el interfaz que indican
el tipo de aprendizaje que implementa cada método. Para no tener que modificar los ya
existentes se proporciona una implementación por defecto, que es justo la que tiene, de manera
implícita BackPropLearning.
public interface NNTrainingScheme {
68
Vector processInput(FeedForwardNeuralNetwork network,
Vector input);
void processError(FeedForwardNeuralNetwork network, Vector
error);
void setNeuralNetwork(FunctionApproximator ffnn);
default boolean batchTraining(){
return false;
}
default void initializeBatchTraining(){
if (!this.batchTraining()){
throw new IllegalArgumentException(
"Batch Training must be set to use this
function!.");
}
}
default void trainOn(FeedForwardNeuralNetwork network,
NNDataSet innds){
if (!this.batchTraining()){
throw new IllegalArgumentException(
"Batch Training must be set to use this
function!.");
}
}
}
4.3.3 Clases Nuevas
4.3.3.1 LMActionListener
Esta clase se utiliza para controlar el comportamiento del interfaz cuando el usuario cambia el
combo de tipo de función. Si selecciona las funciones predefinidas se muestra el combo de las
funciones predefinidas y se oculta la caja de texto para la introducción de la función. En caso
de que se seleccione función personalizada se procede al contrario.
protected class LMActionListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
69
try{
if (source == TypeFunctionCombo) {
int index =
TypeFunctionCombo.getSelectedIndex();
switch (index){
case 0://predefined function
function_textfield.setVisible
(false);
PredefinedFunctionsCombo.setVisible(
true);
break;
case 1://custom function
function_textfield.setVisible(
true);
PredefinedFunctionsCombo.setVisible(
false);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.3.3.2 CustomFunctionNNDataset
Esta clase es la que realiza la llamada a la librería Exp4j, que se encarga de evaluar la función
en forma de cadena de texto introducida por el usuario. Para cumplir con los requisitos de la
herencia tiene que implementar también algunos métodos auxiliares de la clase padre. Debido
a que en la clase padre estaban declarados como private no ha sido posible llamarlos, así
que se han copiado en la clase hija, de esta manera se minimizan los cambios en las clases
existentes.
import objecthunter.exp4j.Expression;
import objecthunter.exp4j.ExpressionBuilder;
70
public CustomFunctionNNDataSet(String function, boolean
addNoise) throws Exception{
super(0);
str_function = function;
DataSetSpecification dss = new DataSetSpecification();
dss.defineNumericAttribute("x");
dss.defineNumericAttribute("y");
dss.setTarget("y");
DataSet ds = new DataSet(dss);
Expression e = new ExpressionBuilder( str_function).
variables("x").build();
for(double x = -2; x<= 2; x += .1) {
double y = e.setVariable("x", x).evaluate();
if (addNoise){
y += 0.1*Math.random();
}
ds.add(DataSetFactory.exampleFromString(x + ","
+ y, dss, ","));
}
Numerizer numerizer = new FunctionDataSetNumerizer();
createExamplesFromDataSet(ds, numerizer);
adjustLimits();
}
4.3.3.3 LM
Este es el objetivo principal del proyecto. Por simplicidad solo mostraremos el método principal
del entrenamiento.
public void trainOn(FeedForwardNeuralNetwork network, NNDataSet innds){
boolean evaluateJacobian;
boolean just_an_epoch = false;
evaluateJacobian = true;
Error =Double.MAX_VALUE;
Error1 = Double.MAX_VALUE;
71
int numparameters = (outputLayer.numberOfInputs() + 1)
*outputLayer.numberOfNeurons() +
(hiddenLayer.numberOfInputs() + 1) *hiddenLayer.numberOfNeurons();
int N = network.getOutputLayer().numberOfNeurons();
int Np = innds.howManyExamplesLeft();
Matrix errors = new Matrix(N*Np,1);
Matrix errors1 = new Matrix(N*Np,1);
Matrix J = new Matrix (N*Np, numparameters);
Matrix Jt=J.transpose();
Matrix I = Matrix.identity(numparameters,numparameters);
Matrix Wn;
Matrix g=new Matrix (N*Np, numparameters);
Matrix H;
Matrix w_hidden, w_output;
Vector w_h_bias, w_o_bias;
if (success){ return;}
do{
if (evaluateJacobian){
iteration=1;
EvaluateJacobian(network, innds, N, J, errors);
Error = evaluateMSE(errors, Np, N);
Jt = J.transpose();
}
g = Jt.times(errors);
try{
H = Jt.times(J).plus(I.times(micro)).inverse();
last_successfull_micro = micro;
}catch (java.lang.RuntimeException ex ){
// var micro is too litle!!!
micro = last_successfull_micro;
H = Jt.times(J).plus(I.times(micro)).inverse();
}
72
Wn = H.times(g);
w_hidden = calculateHiddenUpdateWeight(Wn);
w_h_bias = calculateHiddenUpdateBias(Wn);
w_output = calculateOutputUpdateWeight(Wn);
w_o_bias = calculateOutputUpdateBias(Wn);
// Setting the new weights. The old ones could be restored if the error isn't
decreased
this.hiddenSensitivity.tryNewWeights(w_hidden, w_h_bias);
this.outputSensitivity.tryNewWeights(w_output, w_o_bias);
//calculate new error
EvaluateError(network, innds, N, errors1);
Error1 = evaluateMSE(errors1, Np, N);
if (Error1<=Emax){ // goal!!!
success= true;
}else if(Error1<Error){
if (micro>BOTTOM_LIMIT_MICRO){
micro = micro / STEP_MICRO;
}
evaluateJacobian = true;
just_an_epoch= true;
}else if(Error1 > Error){
if (iteration>this.IterationMax){
evaluateJacobian=true;
}else{
evaluateJacobian = true;
iteration++;
Error=Error1;
micro = micro * STEP_MICRO;
this.hiddenSensitivity.restoreweights();
this.outputSensitivity.restoreweights();
}
}else{ // El error no cambia
73
success=true;
}
}while (!success && !just_an_epoch);
}
Cabe destacar ciertos aspectos en la implementación:
Hay dos variables en la condición de salida del bucle sucess y just_an_epoch, esto es
así debido al objetivo de mantener comparables los métodos de aprendizaje
BackPropLearning y LM. Para simular lo que sería una época de entrenamiento en BP,
cuando se consigue disminuir el error con respecto a la iteración anterior se sale del bucle y se
devuelve el resultado al interfaz. Como las variables principales se guardan a nivel de objeto
no se pierden entre llamada y llamada.
El parámetro micro (µ en la definición formal) se va decrementando (se multiplica 0.1) al
disminuir el error. Desde un punto de vista teórico nunca llega a cero, pero en la práctica al
llegar a un valor suficientemente pequeño la limitación de representación de coma flotante de
cualquier microprocesador lo hace convertirse en 0, lo cual provoca que en el cálculo del
Hessiano la matriz no sea invertible, lanzando una excepción. Por este motivo hay que añadir
controles para que el valor de micro no pase de un umbral y, en el peor de los casos, si se captura
la excepción de matriz no invertible devolverlo a un valor aceptable (el último que no provocó
un error) y repetir los cálculos.
En el algoritmo teórico no se tiene en cuenta el caso detectado en las pruebas de que el error se
estanque, es decir, que el error obtenido en una iteración sea igual al de la anterior. En este caso
estamos ante un mínimo en la función de error (local o no), por lo que salimos del bucle.
4.3.3.4 LMSensitivity
Siguiendo el diseño en las clases existentes relacionadas con BackPropLearning (Layer y
LayerSensitivy) se define la clase LMSensitivy encargada de los cálculos de la
retropropagación del valor de delta (δ) para el cálculo del Jacobiano. Se detallan los métodos
más importantes de la clase.
El cálculo de la derivada de la capa de salida (hay un método equivalente para los sesgos)
public Matrix sensitivityMatrixOutputLayer(){
sensitivityMatrix = this.layer.getWeightMatrix().copy();
74
for (int nn=0; nn < sensitivityMatrix.getRowDimension();
nn++ ){
for (int ni=0;
ni<sensitivityMatrix.getColumnDimension();ni++){
sensitivityMatrix.set(nn, ni,
layer.getActivationFunction().deriv(layer.getLastInducedField(
).getValue(nn)));
}
}
return sensitivityMatrix;
}
El cálculo de la derivada de la capa oculta (hay un método equivalente para los sesgos):
public Matrix sensitivityMatrixFromSucceedingLayer(
LMSensitivity nextLayerSensitivity) {
int nn,ni;
Layer nextLayer = nextLayerSensitivity.getLayer();
Matrix delta_next_layer =
nextLayerSensitivity.getSensitivityMatrix();
sensitivityMatrix = layer.getWeightMatrix().copy();
Matrix wd1 = nextLayer.getWeightMatrix();
for (nn=0; nn<layer.numberOfNeurons();nn++){
for (ni=0; ni<layer.numberOfInputs(); ni++){
sensitivityMatrix.set(nn, ni, wd1.get(0,
nn)*delta_next_layer.get(0, nn)*
layer.getActivationFunction().
deriv(layer.getLastInducedField().getValue(nn)));
}
}
return sensitivityMatrix;
}
75
5 EJEMPLOS DE EJECUCIÓN
Aunque realizar un comparación exhaustiva entre el método de aprendizaje de retropropagación
y el de Levenberg-Marquardt queda fuera del ámbito de este proyecto, si es necesario
comprobar que la implementación de los algoritmos se comportan de la manera esperada, sobre
todo a nivel de error:
Para la función predefinida “squared cosine”.
Para el método de aprendizaje de Retropropagación (BP):
Ilustración 35 Ejemplo BP
Para el método de aprendizaje de Levenberg-Marquardt (LM)
76
Ilustración 36 Ejemplo de LM
Vemos que el error cometido por LM es mucho menor que el de BP, aunque la comparación
del número de épocas puede llevarnos a error ya que LM se basa en entrenamiento por lotes, es
decir, en cada época entrena con todos los patrones, al contrario que BP que solo entrena un
patrón por época.
Como esta comparación exhaustiva queda fuera del ámbito de este proyecto, dejo como idea
para las prácticas de la asignatura “Sistemas Inteligentes” dicha comparación y la búsqueda de
la equivalencia entre una época de entrenamiento de BP y otra de LM.
Otra característica del método de LM es la estabilidad a la hora de acercarse al resultado,
tomaremos como ejemplo una función difícil de aproximar y veremos cómo se comportan paso
a paso cada uno de los métodos. Para mostrarlo se han realizado capturas de pantalla durante la
ejecución paso a paso en los momentos más significativos.
Función: x^2 + e^x + sin(pi*x), algoritmo BP 50 neuronas en la capa oculta. Ejecución paso a
paso:
77
Ilustración 37 Ejemplo BP paso a paso 1
Ilustración 38 Ejemplo BP paso a paso 2
Ilustración 39 Ejemplo BP paso a paso 3
78
Ilustración 40 Ejemplo BP paso a paso 4
Vemos que según avanzan el número de épocas de entrenamiento la aproximación va
“saltando” arriba y abajo de la función objetivo. Estas capturas han sido realizadas alrededor
de la época 100 para descartar las variaciones típicas en las primeras épocas de aprendizaje.
Función: x^2 + e^x + sin(pi*x), algoritmo LM 50 neuronas en la capa oculta. Ejecución paso a
paso:
Ilustración 41 Ejemplo LM paso a paso 1
79
Ilustración 42 Ejemplo LM paso a paso 2
Ilustración 43Ejemplo LM paso a paso 3
Ilustración 44 Ejemplo LM paso a paso 4
80
Vemos que, según lo previsto, no se producen los saltos en la búsqueda de la mejor
aproximación. Esto es debido al diseño del método que siempre busca el camino más rápido
para minizar el error y si en uno de los pasos intermedios el error aumenta, deshace los cambios
y ajusta los parámetros.
Durante la fase de pruebas, durante la implementación, se detectó que podría existir una
condición que provocara un bucle infinito en la rutina principal. Dicha rutina itera hasta que se
alcanza un error menor del máximo permitido o se disminuye el error. En algunas situaciones,
como en la función del siguiente ejemplo, el error se estanca y es igual iteración tras iteración.
Para tener en cuenta este caso se añadió una condición para la salida del bucle.
Ilustración 45 Ejemplo LM error estancado
81
6 CONCLUSIONES
Con la realización de este proyecto he adquirido conocimientos sobre nuevos métodos de
aprendizaje en redes neuronales (LM) ya que cuando cursé la asignatura correspondiente no
estaba incluido. Me he familiarizado con la librería aima y con Exp4j y sobre todo con un
proyecto orientado a la docencia. Esto lo he tenido en mente desde el primer momento ya que
los métodos y comentarios del código se han realizado en inglés, al igual que la librería aima y
Exp4j, dándole uniformidad al proyecto y permitiendo que cualquier alumno con un nivel
mínimo exigle de inglés para un estudiante universitario pueda comprenderlo.
El uso de librerías externas ha sido un ahorro importante de tiempo ya que me he abstraído de
algoritmos del tipo multiplicación o inversión de matrices que no aportan nada al proyecto y
hubieran conllevado una carga de trabajo importante y de posibles fuentes de errores.
El resultado final es una aplicación entendible, fácil de usar que compara los dos métodos de
aprendizaje de redes neuronales (BP y LM). Pueden aproximarse funciones con ambos métodos
de aprendizaje y comparar visualmente el resultado así como el error cometido por cada uno de
ellos.
Por otro lado, con los mecanismos de herencia e implementación de interfaces el proyecto es
fácilmente ampliable a otros métodos de aprendizaje de redes neuronales.
También he redescubierto las redes neuronales dentro del campo de la inteligencia artificial. He
de reconocer que cuando estudié estos “artificios matemáticos” no tomé conciencia de la
potencia a la hora de aproximar soluciones a problemas en la que un enfoque imperativo sería
imposible. Esto nos lleva a áreas como la robótica, detección automática y clasificación de todo
tipo de objetos que ya son una realidad en la industria.
Como un ejemplo actual (6/12/2017) tenemos la noticia de que AlphaZero, un programa basado
en redes neuronales propiedad de Google, ha logrado vencer StockFish, uno de los motores de
ajedrez más potentes del mundo, con tan solo 24 horas de aprendizaje, basándose únicamente
en las reglas del juego [Cornell 2017]
82
7 ANEXO 1 SOFTWARE ADICIONAL UTILIZADO EN EL
PROYECTO
Sistema Operativo: Debian GNU/Linux 8.7 https://www.debian.org/index.es.html
Ofimática: LibreOffice https://es.libreoffice.org/
Editor Gráfico: GIMP http://www.gimp.org.es/
Plugin para Eclipse: ObjectAid Class Diagram http://www.objectaid.com/class-diagram
Prototipado Interfaz Gráfico: Evolus Pencil https://pencil.evolus.vn/
83
8 ANEXO 2 REFERENCIAS
[AIMACODE 2017] Code for the book "Artificial Intelligence: A Modern Approach",
https://github.com/aimacode/aima-java Berkely CA, 2017
[Bishop 95] C. Bishop, “Neural Networks for Pattern Recognition” Oxford, U.K.:
Clarendon,1995
[Eclipse2017] Entorno Eclipse: https://www.eclipse.org
[Exp4j 2017] Librería Exp4j: https://lallafa.objecthunter.net/exp4j/
[Hagan 94] M.T. Hagan, M.B.Menhaj, “Training feedforward networks with the Marquardt
algorithm”, IEEE Transactions on Neural Networks, 5(6) 989-993 Noviembre 1994
[Haykin99] S. Haykin, “Neural Networks: A Comprehensive Foundation”, 2nd ed. Upper
Saddle River, NJ: Prentice-Hall, 1999
[Levenberg 44] Kenneth Levenberg, “A method for the solution of certain problems in least
squares, Quarterly of Applied Mathematics”, 5, 164–168, 1944.
[Marquardt 63] Donald W. Marquardt, “An algorithm for least-squares estimation of nonlinear
parameters”, SIAM Journal on Applied Mathematics, 11(2), 431–441, Junio 1963
[Montero2011] Roberto Montero Miguel, “Java 7 Guía Práctica”, Anaya, 2011
[Poggio89] T. Poggio and F. Girosi, A theory of networks for approximation and learning
Massachusetts Inst. Tech., Artificial Intelligence ab., Cambridge, MA, Jul. 1989, AI Memo
1140
[Silver2017] David Silver, Thomas Hubert, Julian Schrittwieser, Ioannis Antonoglou, Matthew
Lai, Arthur Guez, Marc Lanctot, Laurent Sifre, Dharshan Kumaran, Thore Graepel, Timothy
Lillicrap, Karen Simonyan, Demis Hassabis, Cornell University Library,
https://arxiv.org/abs/1712.01815
[Stuart2009] Stuart Russel, Peter Norvig, “Artificial Intelligence: A Modern Approach”, 3rd
Edition, Prentice Hall, 2009
[Yu2011] Hao Yu, B. M. Wilamowski, “Industrial Electronics Handbook”, 2ª Edition. Pp12-1
a 12-15