Proyecto Perceptron Simple (Código C++)
INTRODUCCIÓN
En 1936 Alan Turing estudio el cerebro como una forma de ver
el mundo de la computación. Sin embargo, los primeros teóricos que concibieron
los fundamentos de la computación neuronal fueron Warren McCulloch un neurofisiología
y Walter Pitts un matemático quienes en 1943 lanzaron una teoría acerca de la
forma de trabajar de las neuronas. Ellos modelaron una red neuronal simple
mediante circuitos eléctricos. Años más tarde en 1949 Donald Hebb escribió un
importante libro en el que explicaba los procesos del aprendizaje desde un
punto de vista psicológico, desarrollando una regla de como el aprendizaje
ocurría. Su idea fue que el aprendizaje ocurría cuando ciertos cambios en una
neurona eran activados. Los trabajos de Hebb formaron las bases de la Teoría de
las Redes Neuronales.
Luego en 1957 Frank Rosenblatt desarrolla el Perceptrón,
Esta es la red neuronal más antigua utilizándose hoy en día para aplicación
como reconocedor de patrones. Este modelo era capaz de generalizar, es decir,
después de haber aprendido una serie de patrones podía reconocer otros
similares, aunque no se le hubiesen presentado anteriormente. Sin embargo,
tenía una serie de limitaciones, por ejemplo, su incapacidad para resolver el
problema de la función OR-exclusiva y, en general, era incapaz de clasificar
clases no separables linealmente.
Diez años más tarde Stephen Grossberg realizó una red
llamada Avalancha, que consistía en elementos discretos con actividad que varía
en el tiempo que satisface ecuaciones diferenciales continuas, para resolver
actividades como reconocimiento continúo de habla y aprendizaje de los brazos
de un robot. En 1969 Marvin Minsky y Seymour Papert probaron (matemáticamente)
que el Perceptrón no era capaz de resolver problemas relativamente fáciles,
tales como el aprendizaje de una función no-lineal. Esto demostró que el
Perceptrón era muy débil, dado que las funciones no-lineales son extensamente
empleadas en computación y en los problemas del mundo real. Después de varios
avances en 1980 Kunihiko Fukushima desarrolló un modelo neuronal para el
reconocimiento de patrones visuales. Al instante en 1986 - David Rumelhart y G.
Hinton redescubrieron el algoritmo de aprendizaje de propagación hacia atrás
backpropagation.
En la actualidad, las redes neuronales se componen de
numerosas capas de procesamiento, en las cuales se emplean métodos para obtener
modelos de representación de los datos de entrada, a través de módulos simples
no lineales de representación, que se encargan de extraer las características y
patrones de los datos en bruto y transferirlas a capas posteriores más
abstractas. Esta forma de trabajar, dota a la red la capacidad de deducir por
sí misma, la mejor manera de representar los datos, sin necesidad de haber sido
programada explícitamente para ello.
La forma más frecuente de aprendizaje automático, es el
aprendizaje supervisado, en el cual el algoritmo requiere de una función
objetivo, para ajustar los pesos de modo tal que las salidas del algoritmo se
acerquen a la meta. De tal forma, las redes neuronales de aprendizaje
supervisado, necesitan de una función objetivo, la cual es comparada con las
salidas de la red, para calcular el valor de la función de costos, y así poder
ajustar los pesos de las capas anteriores, de modo tal que en cada iteración se
disminuya el error.
Debido a que una red neuronal profunda puede tener millones
de neuronas, y centenares de millones de conexiones (incluso miles de
millones), resulta impráctico ajustar uno a uno los pesos, incluso se puede
intentar calcular el error mediante un procedimiento analítico empleando
derivadas, pero dado el elevado número de variables de la red el proceso sería
extremadamente complicado. Es por eso que, el efecto de aprendizaje (reducción
del error) se consigue empleando el método de gradiente descendente.
Una red neuronal artificial es un grupo interconectado de
nodos similar a la vasta red de neuronas en un cerebro biológico. Cada nodo
circular representa una neurona artificial y cada flecha representa una
conexión desde la salida de una neurona a la entrada de otra.
Cada unidad neuronal está conectada con muchas otras y los
enlaces entre ellas pueden incrementar o inhibir el estado de activación de las
neuronas adyacentes. Cada unidad neuronal, de forma individual, opera empleando
funciones de suma. Puede existir una función limitadora o umbral en cada
conexión y en la propia unidad, de tal modo que la señal debe sobrepasar un
límite antes de propagarse a otra neurona. Estos sistemas aprenden y se forman
a sí mismos, en lugar de ser programados de forma explícita, y sobresalen en
áreas donde la detección de soluciones o características es difícil de expresar
con la programación convencional.
Las redes neuronales manejan dos tipos de información. La
primera, es la información volátil, que se refiere a los datos que se están
usando y varían con la dinámica de la computación de la red, se encuentra
almacenada en el estado dinámico de las neuronas, el segundo tipo de
información que manejan las redes neuronales, es la información no volátil que
se mantiene para recordar los patrones aprendidos y se encuentra almacenada en
los pesos sinápticos.
El aprendizaje de las redes neuronales, es el proceso de presentar los patrones a aprender, a la red y el cambio de los pesos de las conexiones sinápticas usando una regla de aprendizaje.
La regla de aprendizaje consiste en algoritmos basados en fórmulas matemáticas, que usando técnicas como minimización del error o la optimización de alguna "función de energía", modifican el valor de los pesos sinápticos en función de las entradas disponibles y con ello optimizan la respuesta de la red a las salidas que deseamos.
Podemos distinguir tres tipos de aprendizaje, el modo más intuitivo es el Aprendizaje supervisado, que consiste en que la red dispone de los patrones de entrada y los patrones de salida que deseamos para esa entrada y en función de ellos se modifican los pesos de las sinapsis para ajustar la entrada a esa salida.
Otro modo de aprendizaje, Aprendizaje no supervisado, consiste en no presentar patrones objetivos, sino solo patrones de entrada, y dejar a la red clasificar dichos patrones en función de las características comunes de los patrones.
Y por último el aprendizaje reforzado, que usa una formula
híbrida, el supervisor no enseña patrones objetivos si no que solo le dice si
acierta o falla en su respuesta ante un patrón de entrada.
La topología o arquitectura de una red consiste en la
organización y disposición de las neuronas en la red. Las neuronas se agrupan
formando capas, que pueden tener muy distintas características.
Además las
capas se organizan para formar la estructura de la red. Se puede observar en la
siguiente figura.
La jerarquía de las redes neuronales:
Donde podemos ver que las neuronas se agrupan para formar capas y las capas se unen entre ellas formando redes neuronales. Para clasificar por la topología usaremos el número de capas en Redes Monocapas o Redes Multicapas.
Las redes monocapa son redes con una sola capa, para unirse las neuronas crean conexiones laterales para conectar con otras neuronas de su capa. Las redes más representativas son la red de Hopfield, la red BRAIN-STATE-IN-A-BOX o memoria asociativa y las maquinas estocásticas de Botzmann y Cauchy.
Entre las redes neuronales monocapa, existen algunas que permiten que las neuronas tengan conexiones a sí mismas y se denominan auto recurrentes.
Las redes multicapa están formadas por varias capas de
neuronas (2,3...). Estas redes se pueden a su vez clasificar atendiendo a la
manera en que se conexionan sus capas.
Usualmente, las capas están ordenadas por el orden en que reciben la señal desde la entrada hasta la salida y están unidas en ese orden. Ese tipo de conexiones se denominan conexiones feedforward o hacia delante.
Por el contrario existen algunas redes en que las capas aparte del orden normal algunas capas están también unidas desde la salida hasta la entrada en el orden inverso en que viajan las señales de información. Las conexiones de este tipo se llaman conexiones hacia atrás, feedback o retroalimentadas.
Usualmente, las capas están ordenadas por el orden en que reciben la señal desde la entrada hasta la salida y están unidas en ese orden. Ese tipo de conexiones se denominan conexiones feedforward o hacia delante.
Por el contrario existen algunas redes en que las capas aparte del orden normal algunas capas están también unidas desde la salida hasta la entrada en el orden inverso en que viajan las señales de información. Las conexiones de este tipo se llaman conexiones hacia atrás, feedback o retroalimentadas.
ANÁLISIS
El perceptrón es una red de alimentación directa, esto es la
información fluye desde la capa de entrada hacia la capa de salida. Fue
desarrollado por F. Rosenblatt hacia final de la década de los cincuenta
basándose en la regla de aprendizaje de hebb y de los modelos de neuronas
biológicas de McCulloch y Pitts.
El Perceptrón es un clasificador, asigna a un vector de N
valores un valor binario, usando una transformación no lineal. Así cada vector
pertenece a una de las particiones que crea el perceptrón.
El perceptrón simple consta de dos capas de neuronas.
Esta red admite valores binarios o bipolares como entrada
para los sensores y los valores de su salida están en el mismo rango que los de
entrada.
El funcionamiento para ejecutar un patrón de la red es el
siguiente:
1. Se establece
el patrón de entrada en los sensores, la capa de entrada.
2. Se actualizan
las neuronas de la capa de Salida.
Los pasos para que la red aprenda una lista de patrones son
los siguientes:
1. Tomar un
patrón al azar de la lista.
2. Se establece
el patrón de entrada en los sensores, la capa de entrada.
3. Se establecen
los valores deseados en las neuronas de la capa de salida
4. Se actualizan
las neuronas de la capa de Salida.
5. Solicitar que
aprendan todas las sinapsis.
6. Si las
sinapsis han cambiado volver al paso 1.
Si no han cambiado la red se ha estabilizado y paramos.
Hay que destacar que el perceptrón aprende solo cuando se
equivoca al clasificar el patrón.
Diseño
Implementación
Se tiene una neurona para clasificar frutas en comestibles y
no comestibles, nuestra neurona estará en la capacidad de decirnos por medio de
las entradas que frutas son comestibles y cuales no son comestibles.
|
Banano
|
pera
|
limón
|
mora
|
maracuyá
|
manzana
|
Piel
|
-1
|
1
|
-1
|
1
|
-1
|
1
|
Semilla
|
1
|
-1
|
-1
|
1
|
1
|
-1
|
Sabor
|
1
|
1
|
-1
|
-1
|
-1
|
1
|
Color
|
1
|
1
|
1
|
-1
|
1
|
1
|
Estado
|
1
|
1
|
1
|
1
|
1
|
1
|
Olor
|
1
|
1
|
1
|
1
|
1
|
1
|
Resultado
|
1
|
1
|
-1
|
-1
|
-1
|
1
|
Cabe aclarar que 1 es si y -1 es no.
El primer paso será declarar las entradas de cada fruta y la
salida final en base de si es comestible o no lo siguiente que haremos será
declarar el umbral que podrá ir de 1 a -1 de forma aleatoria, los rangos
posibles de los pesos será tomado de manera aleatoria para cada entrada y el
nivel de aprendizaje hará lo mismo dependiendo de cada época.
Luego entramos al entrenamiento del perceptron para lo cual
tomaremos cada entrada la multiplicaremos por el peso y las sumaremos si el
resultado es diferente a la salida esperada se ejecutara las veces necesarias
hasta que el perceptron este entrenado.
Codigo C++ (frutas)
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
void Perceptron(){
srand(time(NULL));
float intervalos[] = {-0.98,-0.87,-0.65,-0.75,0.45,0.95,-0.32,-0.28,-0.11,-0.06,0.023,0.23,0.23,0.46,0.67,0.78, 0.75,0.82,0.98};
float aprendizaje[] = {0.023,0.23,0.233,0.467,0.675,0.788, 0.752,0.823,0.987};
float Umbral,defumbral,factor;
int i;
int x1[] = {-1,1,-1,1,-1,1};
int x2[] = {1,-1,-1,1,1,-1};
int x3[] = {1,1,-1,-1,-1,1};
int x4[] = {1,1,1,-1,1,1};
int x5[] = {1,1,1,1,1,1};
int x6[] = {1,1,1,1,1,1};
int result[] = {1,1,-1,-1,-1,1};
float w[6] = {intervalos[rand() % 18],intervalos[rand() % 18],intervalos[rand() % 18],intervalos[rand() % 18],intervalos[rand() % 18],intervalos[rand() % 18]};
factor = intervalos[rand() % 18];
Umbral = intervalos[rand() % 18];
defumbral = 1;
int verdad = 0;
int op;
int cont = 0;
int n = 0;
//////////// ENTRENAMIENTO DEL PERCEPTRON;
while(verdad == 0){
n++;
for(i=0;i<6;i++){
op = ((x1[i]*w[0])+(x2[i]*w[1])+(x3[i]*w[2])+(x4[i]*w[3])+(x5[i]*w[4])+(x6[i]*w[5])) + (defumbral*Umbral);
if(op >= 0){
op = 1;
}
else{
op = -1;
}
if(op != result[i]){
w[0] = w[0] + (6*factor)*(x1[i]*result[i]);
w[1] = w[1] + (6*factor)*(x2[i]*result[i]);
w[2] = w[2] + (6*factor)*(x3[i]*result[i]);
w[3] = w[3] + (6*factor)*(x4[i]*result[i]);
w[4] = w[4] + (6*factor)*(x5[i]*result[i]);
w[5] = w[5] + (6*factor)*(x6[i]*result[i]);
Umbral = Umbral + (6*factor)*(defumbral*result[i]);
}
}
for(i=0;i<6;i++){
op = ((x1[i]*w[0])+(x2[i]*w[1])+(x3[i]*w[2])+(x4[i]*w[3])+(x5[i]*w[4])+(x6[i]*w[5])) + (defumbral*Umbral);
if(op == result[i]){
cont++;
}
if(cont == 6){
verdad = 1;
}
}
if(n > 900000000){
printf("[!] Demasiadas epocas realizadas!\n");
printf("[!] Intente nuevamente con otros pesos.\n");
exit(1);
}
}
/////////// MOSTRAR RESULTADOS FINALES
//system("clear");
printf("\n[*] FINALIZANDO ENTRENAMIENTO...\n");
printf("[*] PERCEPTRON SIMPLE [ENTRENADO]\n");
printf("\n--------------- DATOS FINALES --------------------------------\n\n");
printf("[*] Total de epocas: (%i)\n",n);
printf("[*] Peso 1 w[1]\t\t--> %2.2f\n",w[0]);
printf("[*] Peso 2 w[2]\t\t--> %2.2f\n",w[1]);
printf("[*] Peso 3 w[3]\t\t--> %2.2f\n",w[2]);
printf("[*] Peso 4 w[4]\t\t--> %2.2f\n",w[3]);
printf("[*] Peso 5 w[5]\t\t--> %2.2f\n",w[4]);
printf("[*] Peso 6 w[6]\t\t--> %2.2f\n",w[5]);
printf("[*] Umbral(Polarizacion)\t\t--> %2.2f\n",Umbral);
printf("\n\n--------------- VERFICIACION DE ENTRENAMIENTO ------------------\n\n");
for(i=0;i<6;i++){
op = ((x1[i]*w[0])+(x2[i]*w[1])+(x3[i]*w[2])+(x4[i]*w[3])+(x5[i]*w[4])+(x6[i]*w[5])) + (defumbral*Umbral);
if(op >= 0){
op = 1;
}
else{
op = -1;
}
printf("\n[*] Entradas: (%2i,%2i,%2i,%2i,%2i,%2i) --> Salida: %2i",x1[i],x2[i],x3[i],x4[i],x5[i],x6[i],op);
if(op != result[i]){
//system("clear");
printf("\n[!] Demasiadas epocas realizadas!\n");
printf("[!] Intente nuevamente con otros pesos.\n");
exit(1);
Perceptron();
}
}
}
int main(int argc, char **argv)
{
Perceptron();
return 0;
}
Resultados
Para iniciarse en este tema, me ha resultado muy útil, gracias por tu dedicación
ResponderBorrar