domingo, 5 de diciembre de 2010

Nuevo modelo para la red neuronal: Taller de C++

En los anteriores talleres hemos visto que para simular las neuronas creamos una clase o función, en esta ocasión con una sola clase simularemos toda la red, de modo que podemos facilmente definir cuantos elementos de entrada, salida y en la capa oculta queremos.
La ventaja esta en el tiempo de ejecución del entrenamiento de la red: si fuera una sola clase para neurona, en casos en los que sean necesarias 20 o mas neuronas el tiempo de entrenamiento seria enorme.
Por motivos de espacio no pongo todo el código fuente en este post, pero podéis obtenerlo desde mi repositorio en http://github.com/Sauruxum/Sauruxum/ .
Para los que no son prácticos con este tipo de servicios, he subido los archivos en http://www.mediafire.com/?9q6sa7rxv27exsm ( Actualizado )

El código  fuente de la red es red.cpp y red.h. Como podréis ver, definimos las salidas de la red con una variable y la entrada a cada elemento es la misma que la salida de los elementos de la capa anterior, a la cual se les aplica o no la función de transferencia ( yo en este caso he decidido de usar exclusivamente la función sigmoidal )

En esta situación es útil crear una matriz para contener ordenadamente los datos de ingreso a cada elemento y tener en la misma variable un identificador de donde proviene el mismo dato.
Para crear una matriz, de modo que sea variable ( de cursos básicos de lenguaje C sabemos que cuando declaramos un vector o matriz estos no pueden cambiar la cantidad de elementos que contienen ) la cual es miembro de la clase red. La función es create_array:

//Crear una matriz de valores "double"
double **create_array( int Row, int Col )
{
    double **array = new double*[Row];
    for ( int i = 0; i < Row; i++ )
        array[i] = new double[Col];
    return array;
}

Este es un método con el cual podemos declarar matrices dinamias ( sin intención de "reinventar la rueda" esta función podéis encontrarlo en textos básicos de C ) y estas matrices se deben declarar para cada capa de la red neuronal.  Otra cosa es que el bias lo declaramos junto a todas las entradas, pero como hemos dicho, se le da un peso sinaptico aleatorio y entrada siempre 1.
Con esta función se deben crear algunas variables, como las que contendrán los pesos sinapticos, el valor del delta ( para actualizar los pesos sinapticos ).
Como dijimos necesitamos un generador de números aleatorios, pero como sabemos los números aleatorios de la librería estándar "no son tan aleatorios", en caso que el entrenamiento falle, nos encontraremos con que el entrenamiento siempre fallara, por lo tanto necesitamos cambiar la semilla para generar números aleatorios siempre distintos por cada ejecución del programa.
Cada parte del entrenamiento de la red la he dividido en funciones simples, y en la definición de las funciones miembros las he ordenado en orden cronológico de uso durante la ejecución.
Tambien se hace necesario guardar los datos del entrenamiento en un archivo para posteriormente recuperarlos, sin embargo la parte para recuperar los datos de una red ya entrenada esta en el código fuente muestras.h

Como nota final para este post dejo un par de comentarios personales:
  • Creo que aunque si mis habitos de programacion son malos, el código fuente esta ampliamente comentado, facilitando la comprension
  • El código es libre y podéis redistribuirlo o modificarlo. En el código vienen algunos ejemplos del uso de la red para los que quieren examinarlo.

viernes, 19 de noviembre de 2010

Tipo de salida del MLP y su uso

Como dijimos antes el el perceptron multicapa los elementos de entrada y los de la capa oculta deben tener una función de transferencia que no sea lineal, entonces solo nos queda como opción la sigmoide y la tangente hiperbólica.
Sin embargo la salida puede tener una función lineal o no lineal, segun el uso que se le da a la red.
Como sabemos las funciones sigmoidal y tangentoide son funciones limitadas en su conjunto imagen para valores del dominio en todo el campo real ( sus valores van de 0 a 1 para la sigmoidal y de -1 a 1 para la tangente hiperbólica ), en cambio una recta adquiere todos los valores de salida en el campo real.
Esto puede ser útil en muchos casos, como ejemplos tenemos para las redes con salida no lineal la posibilidad de representar la salida en "porcentajes" ( un ejemplo de uso real seria en mecánica, una viga en ciertas condiciones de presión/tracción, a que punto esta respecto la tensión de ruptura de la viga ) o también en la clasificación de patrones con salidas discretas "si" o "no" ( en medicina son ampliamente usadas las redes neuronales en la clasificación de tejidos, de modo que se puede reconocer si es tejido cancerigeno o no ). Una cosa que vale decir acerca del uso de estas redes para el calculo de porcentajes es que son muy imprecisas a valores cercanos a los limites, ya que las funciones usadas tienen como limites esos valores, pero nunca los alcanzan.
El perceptron multicapa puede ser usado con salidas continuas en el caso del calculo del valor de una función compleja ( no me refiero a números complejos, que sea claro ) la cual puede adoptar valores relativamente grandes. Con frecuencia se usan para las predicciones meteorológicas o en predicciones económicas.
En todo caso el uso que se le puede dar a una red neuronal es muy amplio, en campos como la medicina, meteorología y economía ( con los ejemplos anteriores ), reconocimiento de patrones ( actualmente hay software que reconoce la escritura usando redes neuronales ) y biometria.

En las dos siguientes entregas desarrollaremos en código en C++ para tratar de simular el funcionamiento del MLP, explicando un nuevo método que usaremos, en el cual no simularemos cada neurona sino la red completa.

domingo, 7 de noviembre de 2010

Curiosidades acerca de las neuronas II

Como otro off topic  hare otras citaciones acerca de las neuronas y del sistema nervioso en general.

El pulpo tiene 300000 millones de neuronas contra las 100000 millones del hombre. Su capacidad de aprender como evitar obstaculos, resolver simples problemas y memorizar patrones ya han sido comprobados por los cientificos de todo el mundo. De hecho se encuentran los llamados "pulpos imitadores", los cuales imitan a ciertos animales venenosos para no ser molestados.

Los nervios que se difunden por el cuerpo "estan hechos de mas neuronas". Por lo general los nervios que conducen los impulsos motorios estan compuestos en gran parte de axones de las neuronas que salen desde el encefalo y muchos sensores son neuronas mas especializadas.

El cerebro consume entre el 20% y el 25% de la energia metabolica basal. No es un misterio que el cerebro sea el organo que mas energia consume, de hecho tambien es uno de los organos que mas sangre y oxigeno necesitan. Casi toda la energia que necesita el cerebro es suministrada por la glucosa ( basicamente azucar ) la cual la transforman en CO2, piruvato, lactato y agua, los cuales son excretados.
Digamos que el esteriotipo de "el cerebro del grupo tiene bajo peso" queda justificado.

Algoritmo Error Back Propagation

El algoritmo "Error back propagation" o simplemente BP, un algoritmo muy utilizado en el campo de la redes neuronales, es el algoritmo usado para entrenar una red MLP. Esto consiste en calcular, como en el caso del perceptron simple, el error cometido a la salida y asi corregir el error en la capa de salida.
Despues este error se debe propagar a la capa oculta: digamos que "cada elemento en la capa oculta tiene parte de responsabilidad en el error cometido a la salida de la red, por lo tanto se debe corregir este error".
Hay que tener en cuenta que la salida de todos los elementos de la capa oculta se conectan a la entrada de cada uno de los elementos de la cada de salida, por lo tanto el valor del error retropropagado para la capa oculta es la suma ponderada del error de los elementos de salida, multiplicado por su respectivo peso sinaptico.
Asi tenemos que el error en la salida es:



Y el error en la capa oculta es:



Donde Z es la salida de los elementos de la capa oculta.
Asi tenemos que la variacion de los pesos sinapticos de la salida y de la capa oculta es respectivamente:





Donde I es la entrada a los elementos de la capa oculta, ya que como dijimos antes, en la capa de entrada no se realiza proceso alguno.

De este modo y repitiendo el entrenamiento hasta reducir el error de salida a un valor minimo o en cualquier modo aceptable, se hace que el MLP aprenda a dar respuesta segun el modo en el que nosotros lo hemos entrenado.
Este es un tipo de entrenamiento supervisado, ya que ademas de dar un patron de ingresos para el entrenamiento, tambien se dan patrones de la salida que se espera de la red.
Como ultima cosa, solo decir que hay muchas variantes de este algoritmo, algunas de esas variantes se utilizaran en futuro, otras solo se citaran como referencia teorica.

jueves, 4 de noviembre de 2010

Tipos de aprendizaje del perceptron: supervisado y no supervisado

Por ahora todos los ejemplos que hemos dado son de aprendizaje supervisado, pero también existen otros métodos de aprendizaje y que es necesario explicar para entrar completamente en la parte de los algoritmos de entrenamiento.
Cabe distinguir dos tipos de aprendizaje:
  • Aprendizaje supervisado
  • Aprendizaje no supervisado
El primero consiste en hacer como hemos hecho hasta el momento, dar un set de entradas a la ANN y además dar un set de las salida esperadas de parte de la red, de modo que a cada muestra se corrigen los errores entre la salida obtenida y la salida deseada de nuestra red ( seria puro caso no encontrar errores al inicio ).
En cambio, el aprendizaje no supervisado no se da un set de salidas esperadas, se deja que la red aprenda a responder organizadamente a través de las entradas que recibe y que se organice por si misma. De este modo, datos de ingreso similares ( o casi ) dan una determinada respuesta a la salida, consiguiendo una clasificación de las distintas entradas.

martes, 2 de noviembre de 2010

Perceptron multicapa ( multilayer perceptron: MLP )

Como dijimos anteriormente, el perceptron multicapa ( MLP de ahora en adelante ) ha sido una mejora en la arquitectura del perceptron simple. El MLP es capaz de resolver problemas que no son linealmente separables ( como la funcion logica XOR )
El MLP esta formado de multiples capas ( siempre mas de tres ) las cuales se dividen en:
  • Capa de entrada: es donde entra la informacion. En esta capa no se realiza ningun procesamiento.
  • Capa oculta: puede ser una o mas capas, y realizan parte del proceso para enviarlo a la salida.
  • Capa de salida: ultima capa del MLP, es donde se devuelve el resultado.

Cada una de estas capas puede estar formada por mas perceptrones, las cuales salidas estan conectadas unicamente con las capas seguientes, nunca entre la misma capa o una capa anterior. La funcion de transferencia de las salidas de los perceptrones deben ser derivables. Como estamos usando como funcion de transferencia la suncion sigmoide, tenemos una funcion derivable en todo el intervalo real, aunque no puede ser una funcion lineal.
El MLP tiene un algoritmo un poco mas complejo de entrenamiento que el del perceptron simple. Este algoritmo es denomidado BP ( del ingles back propagation, o retropropagacion ).
Como mejora del simple perceptron, el MLP puede emular cualquier funcion continua.
Uno de los problemas que pueda causar este tipo de redes neuronales es el hecho que no esta determinado el numero de capas o neuronas por capa en las capas ocultas. Esto solo se puede determinar mediante la experiencia, pero como regla general:
Si la red no logra aprender un determinado proceso se aumentan neuronas.
Si la red pierde capacidad de generalizacion, se quitan neuronas.
Otro problema en algunos casos seria el hecho que la cantidad de pesos sinapticos de actualizar por cada epoca se vuelve bastante elevado, haciendo que el tiempo de entrenamiento se incremente.

lunes, 1 de noviembre de 2010

Limitaciones del perceptron simple

Si alguno se dio cuenta, en el código de ejemplo anterior no es posible replicar todas las funciones lógicas.
De hecho, examinando la formula de salida del perceptron, por simplicidad a dos entradas:


Podemos ver que si ponemos el limite de la salida a 0, osease, justo al punto en el cual cambia de valor la salida se obtiene una ecuación del tipo:


Que es la ecuación de una recta genérica. Es decir, lo que este arriba de esa recta cuenta como una salida y lo que este abajo cuenta como otra salida ( digamos para el ejemplo 0 y 1 ).


Como se puede ver en las figuras, la función OR puede ser separada por una linea, teniendo los ejes como las entradas y los puntos proyectados como las salidas. Lo mismo ocurre en funcionen tipo AND y NAND o NOR. Sin embargo eso no es posible en la función XOR, que como se ve en la figura, debe ser separado por un mínimo de 2 lineas, o ser encerrado en un ovalo. Esto no es posible de hacer con un perceptron simple.
Por hacer un poco de historia digamos que Marvin Minsky e Seymour Papert demostraron este hecho, lo que causo que el interés por la investigación del perceptron menguara rápidamente.
De hecho para eliminar este problema es necesario agregar mas capas a la red neuronal, así aparece el MLP ( multilayer perceptron: perceptron multicapa ) que es una evolucion del perceptron simple y que además elimina grandes problemas del perceptron como el de la separación lineal.

domingo, 31 de octubre de 2010

Entrenamiento del perceptron: taller en C++

Llegados a este punto podemos crear un perceptron completo.
Pongo el código de uno que tenia guardado hace tiempo y lo paso a explicar:


#include <iostream>
#include <stdlib.h>
#include <math.h>

using namespace std;

#define EPOCA 200  //Numero de iteraciones del entrenamiento
#define K 0.2f     //Taza de aprendizaje del perceptron

float TrainNetwork( float x0, float x1, float target );//Funcion para entrenar el perceptron
float RunNetwork( float x0, float x1 );                //Funcion que entrega las salidas
float sigmoide( float s );                             //Funcion sigmoide
void pesos_init(void);                                 //Inicia los pesos aleatoriamente


float Pesos[2];    // pesos sinapticos
float bias = 1.0f; //El bias lo ajustamos al valor 1

int main()
{
  int i = 0;
  
  pesos_init();
  
  while( i < EPOCA ) // Entrenando la funcion logica AND
  {
    i++;
    
    //Se pasan los patrones para la funcion AND
    cout << "Salida" << endl;
    cout << "1, 1 = " << TrainNetwork( 1, 1, 1 ) << endl;
    cout << "1, 0 = " << TrainNetwork( 1, 0, 0 ) << endl;
    cout << "0, 1 = " << TrainNetwork( 0, 1, 0 ) << endl;
    cout << "0, 0 = " << TrainNetwork( 0, 0, 0 ) << endl;
    
    cout << "\n";
    
    //Se muestran los pesos a cada epoca
    cout << "Peso[0] = " << Pesos[0] << endl;
    cout << "Peso[1] = " << Pesos[1] << endl;
    
    cout << "nbias = " << bias << endl;
    
    cout << "nn" << "epoca = " << i << endl;
    
  }
  
  //Se hace la prueba para ver el estado del entrenamiento
  cout << "Verificar los resultados:" << endl;
  cout << "1, 1 = " << RunNetwork( 1, 1 ) << endl;
  cout << "1, 0 = " << RunNetwork( 1, 0 ) << endl;
  cout << "0, 1 = " << RunNetwork( 0, 1 ) << endl;
  cout << "0, 0 = " << RunNetwork( 0, 0 ) << endl;
  
  cout << "n";
  
  //Se muestran los pesos finales
  cout << "Peso[0] = " << Pesos[0] << endl;
  cout << "Peso[1] = " << Pesos[1] << endl;
  
  //Esto es el bias*W
  cout << "nbias = " << bias << endl;
  
  cout << "nn" << "epoca = " << i << endl;
  return 0;
}

float TrainNetwork( float x0, float x1, float target )
{
  float net = 0;
  float out = 0;
  float delta[2];  //Es la variacion de los pesos sinapticos
  float Error;
  
  net = Pesos[0]*x0 + Pesos[1]*x1-bias;
  net = sigmoide( net );
  
  Error = target - net;
  
  bias -= K*Error;  //Como el bias es siempre 1, pongo que 
                    //el bias incluye ya su peso sinaptico
  
  delta[0] = K*Error * x0;  //la variacion de los pesos sinapticos corresponde 
  delta[1] = K*Error * x1;  //al error cometido, por la entrada correspondiente
  
  Pesos[0] += delta[0];  //Se ajustan los nuevos valores
  Pesos[1] += delta[1];  //de los pesos sinapticos
  
  out=net;
  return out;
}

float RunNetwork( float x0, float x1 )
{
  float net = 0;
  float out = 0;
  
  net = Pesos[0]*x0 + Pesos[1]*x1-bias;
  net=sigmoide( net );
  
  out=net;
  return out;
}


void pesos_init(void)
{
  for( int i = 0; i < 2; i++ )
  {
    Pesos[i] = (float)rand()/RAND_MAX;
  }
}

float sigmoide( float s ){
  return (1/(1+exp(-1*s)));
}

En esta ocasión he dividido cada operación en su respectiva función, de modo que todo quede mas organizado.
Como primera cosa se deben inicializar los pesos sinapticos con valores aleatorios. En este caso no incluyo el peso sinaptico al bias, en cuanto es siempre 1, por lo tanto multiplicado por su peso es siempre igual al peso mismo.
Después la fase de entrenamiento hemos elegido repetirla por 200 veces. El entrenamiento del perceptron suele ser rápido en estos casos, por lo tanto un valor 20 podría estar bien.
Se procede a pasar los parámetros de la función de entrenamiento, este perceptron tiene 2 entradas, por lo tanto los argumento serian la primera entrada, la segunda entrada y la salida respectivamente.
La función hace el calculo del error cometido entre la salida obtenida y la salida deseada ( en la función esta como 'target' ) y en base a las reglas que comentamos en el post anterior, se hace la variación de los pesos sinapticos y del bias ( el cual ya incluye su peso ).
Esta operación debe ser repetida hasta el punto en el que el error sea despreciable, o en todo caso aceptable.
Después se pasa a comprobar el resultado del entrenamiento. Yo obtuve estas salidas:


Verificar los resultados:

1, 1 = 0.854818
1, 0 = 0.0973907
0, 1 = 0.0995641
0, 0 = 0.00202221

Peso[0] = 3.97498
Peso[1] = 3.99946
bias = 6.20154
epoca = 200 

Como se puede observar los valores no son perfectos, aun hay un cierto error, pero podemos decir que es aceptable ya que los valores están bien definidos ( un valor menor de 0.1 seria 0 y un valor mayor de 0.8 seria 1 ).
Esto es debido a que a la salida del perceptron se aplica la función sigmoidal, la cual aunque si esta comprendida entre el 0 y el 1, nunca toca estos valores.
Por lo tanto, nuestro perceptron funciona de manera aceptable.
Podéis cambiar los valores del entrenamiento en el código fuente y verificar que se pueden reproducir en modo muy aproximado muchas funciones lógicas.
Sin embargo el perceptron simple tiene un gran problema, el cual comentaremos en la siguiente entrega.

sábado, 30 de octubre de 2010

Offtopic: repositorio GIT para el codigo fuente

Pensé que seria necesario tener un registro mas ordenado de los códigos que haremos en los distintos talleres en este blog, de modo que usando una cuenta que tenia olvidada en http://github.com, he creado un repositorio donde poner todo el código fuente.
La dirección es:

http://github.com/sauruxum/sauruxum

Hay mucha información acerca el uso de esta herramienta, pero si tenéis dudas especificas, dejar el comentario que responderé lo antes posible.

Saludos

Algoritmo de entrenamiento del perceptron

Como dijimos antes, el perceptron modificando los pesos sinapticos de las entradas es capaz de aprender a dar respuesta según el patrón de entrada que recibe. Para este objetivo existen los algoritmos de entrenamiento.
Básicamente la función de estos algoritmos es el de reducir el error de salida a cada "época" ( una época es un solo ciclo de entrenamiento, ya que en el perceptron el proceso se debe repetir hasta que el error sea mínimo ), de este modo, pasandole ciertas entradas el perceptron da una salida. Sin embargo para el objetivo del entrenamiento esta respuesta puede varía de la respuesta que queremos recibir. De este modo se calcula el error:


Donde:
  • D es la salida deseada
  • S es la salida recibida del perceptron
  • e es el error generado
De este modo la modificacion de los pesos sinapticos sigue la siguiente ley:




De este modo, si la salida obtenida es mas grande que la salida deseada, se tiene que la variación de los pesos de las entradas que dan un valor significativo se hace negativa, de este modo los pesos sinapticos bajan su valor y viceversa. En la formula de la variación de los pesos, k es un coeficiente llamado "taza de aprendizaje". Este coeficiente debe adoptar valores entre 0 y 1, pero se prefieren valores bajos de este coeficiente para evitar oscilaciones.
Este cambio de los pesos debe ser aplicado a todas las entradas del perceptron.

Calculo de las salidas: taller en C++

En este taller nos dedicaremos a mostrar las librerias que utilizaremos en futuro para programar nuestra red neuronal. Ademas de hacer un programa que lea un valor y le aplique la funcion sigmoide, para posteriormente darnos la salida.
Doy por hecho que quien esta interesado en esto sabe compilar un programa escrito en C++, si no es ese el caso dejar un comentario que con gusto respondere segun sea el caso.

Dejo el codigo fuente y paso a explicar una de las funciones que nos interesan:

#include <iostream>
#include <math.h>

float entrada, salida;

using namespace std;

int main(){
cin>>entrada;
salida=1/(1+exp(-1*entrada));
cout<<salida<<endl;
return 0;


Como podreis ver, en el programa se incluye la libreria matematica "math.h" que es una libreria standar en C, y en la linea 10 hacemos una llamada a una funcion de esta libreria, que es la funcion exponencial ( basicamente es el numero de nepper, elevado a la potencia indicada por el argumento de la funcion )


Esta funcion como dijimos antes, da valores de salida que van desde el 0 para numero negativos grandes, y da 1 para numeros positivos grandes. Una curiosidad es que poniendose al centro, pasandole como argumento el 0, obtendremos 0.5 como salida.
Ahora, si queremos obtener un simple perceptron, en el cual le pasamos los datos de ingreso, los pesos sinapticos se inicializan aleatoriamente y da como salida la suma de las entradas por los pesos, aplicandoles la funcion sigmoide, obtendremos un programa de salida como el siguiente:

#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define NUM_ENTRADAS 2

float entrada[NUM_ENTRADAS], 
      pesos[NUM_ENTRADAS],
      salida;
int i;

using namespace std;

int main(){
  srand48(time(NULL));
  for( i=0; i<NUM_ENTRADAS; i++ ){
  pesos[i]=drand48(); //se aplican valores aleatorios a las entradas
  }
  for( i=0; i<NUM_ENTRADAS; i++ ){
    cout<<"Entrada "<<i+1<<": "; 
    cin>>entrada[i]; //se pide al usuario las entradas
  }
  salida=0.0;      //preventivamente se inicia la salida con 0
  for( i=0; i<NUM_ENTRADAS; i++ ){
    salida+=(entrada[i])*(pesos[i]);//se va a la salida la 
  }                                     //suma de las entradas por los pesos
  salida=1/(1+exp(-1*salida));       //se aplica a la salida la funcion sigmoide
  cout<<"Salida: "<<salida<<endl;               //se imprime la salida
  cout<<"Pesos: "<<pesos[1]<<" "<<pesos[2]<<endl; //muestra los pesos usados
  return 0;
}


En este caso es necesario incluir mas librerias, ya que usamos las funciones para generar numeros aleatorios ( srand48() y drand48() ) que estan en la libreria standar "stdlib.h" y para poder generar numeros aleatorios siempre distintos se usa la libreria "time.h", de modo que muy improbablemente se repetira la misma secuencia de numeros aleatorios ( cosa que sucede con el rand() )

viernes, 29 de octubre de 2010

Funcion de salida del perceptron

Como dijimos antes, el perceptron mostrado en el ejemplo tiene una función de salida lineal. Esto no siempre es lo mejor ya que en este caso puede adoptar valores de todos los rangos, mientras que las neuronas realmente emiten impulsos que no superan los 70 mV.
Por este motivo se elijen dos funciones, con las cuales se pueden obtener un numero finito de estados.
Estas funciones son:

La función sigmoide



La función tangente hiperbólica



Como se ve, en el primer caso los valores adoptan los rangos del 0 al 1 mientras que en el segundo caso los valores de -1 a 1.

Dependiendo del valor obtenido de la sumatoria de las entradas por los pesos sinapticos, se debe aplicar una de estas funciones para obtener una salida mas parecida a la realidad.
Existen mas funciones, pero por el momento nos centraremos en estas dos.

La piedra angular: el perceptron

Digamos que este es el componente mas simple en una ANN ( como antes comentamos, artificial neural network ), casi como los atomos en la materia.
Su funcionamiento trata de emular el comportamiento de la neurona. De este modo tenemos que tiene unas entradas ( similar a las dendritas de otras neuronas ), usa los llamados pesos sinapticos ( como se comento con anterioridad, tienen la funcion de estimular o inhibir el impulso que llega desde las dendritas ), tiene una salida ( como el axon, en las neuronas ) y para decidir si permitir o no una salida de respuesta, se hace uso de una funcion de transferencia, la cual por el momento no me detengo a explicar.

El perceptron puede ser esquematizado asi:

En el que las entradas son x y y ( esta claro que las entradas pueden ser mas ), S es la salida del perceptron y el numero '1' que esta arriba representa un valor al que se denomina bias. El bias determina con que dificultad el perceptron da un impulso de salida.
Como dijimos antes, la neurona es un sumador, lo mismo ocurre en el caso del perceptron:

Llamaremos a las entradas x y y en este caso.
Lamaremos a los pesos sinapticos Wx y Wy respectivamente.

La salida se obtiene haciendo la suma de todas las entradas, por sus respectivos pesos sinapticos y restando el valor del bias ( en este caso 1 )



Esto es en el caso mas simple, en el que la salida es lineal.
Los pesos sinapticos normalmente adoptan valores aleatorios entre 0 y 1 ( pero veremos que en algunas ocaciones seran necesarios valores negativos ).
Siendo este el componente principal de una red neuronal, la cual es capaz de aprender. Es logico pensar que este simple componente puede aprender a dar respuesta segun lo deseemos, y para eso estan los algoritmos de aprendizaje para los distintos tipos de perceptron.

Curiosidades acerca de las neuronas

Cabe destacar algunas características de las neuronas y del sistema nervioso en general:

Los procesos mentales no están precisamente localizados. Esto quiere decir que no existe una neurona la cual, como dicen los estadounidenses en son de broma "la neurona de la abuela", o sea una neurona especifica a reconocer a la abuela. La facultad de reconocer el rostro o la voz de la abuela están distribuidos entre muchas neuronas y en algunas zonas del cerebro.

Las neuronas son las unicas celulas que no se regeneran. Aunque mueren cada día y en grandes cantidades, en la practica diaria no perdemos nuestras facultades. Esto es debido a que aumentan las conexiones sinapticas para compensar la perdida de las neuronas.
Esto da al cerebro una gran robusteza a sufrir daños.

Las neuronas son "lentas". Respecto a los microprocesadores las neuronas son como un caracol respecto un cohete. La velocidad de reacción de las neuronas no supera los 100Hz, comparado con los 3GHz de los actuales procesadores. Sin embargo haciendo un calculo veloz:
  • Neuronas en el cerebro: 100 mil millones
  • Numero de sinapsis por neurona: 10000
  • Numero de sinapsis totales: 1000 billones.
Si decimos que cada sinapsis son 4 bits tenemos que la memoria del cerebro es de 4x10^15 bytes, con una velocidad de calculo de 10^17 conexiones por segundo ( recordar que las neuronas trabajan todas al mismo tiempo, mientras que en procesador hace una instrucción a la vez ).
Sin embargo lo que es difícil para el cerebro es fácil para el ordenador y viceversa.

Las neuronas

Las redes de neuronas artificiales (denominadas habitualmente en inglés como ANN ) son un paradigma de aprendizaje y procesamiento automático inspirado en la forma en que funciona el sistema nervioso de los animales. Se trata de un sistema de interconexión de neuronas en una red que colabora para producir un estímulo de salida. En inteligencia artificial es frecuente referirse a ellas como redes de neuronas o redes neuronales.

Para saber el preciso funcionamiento de estas redes se debe como primera cosa dar una explicacion del funcionamiento de las neuronas:

Digamos que del cerebro conocemos bastante poco, pero de lo que podemos estar seguros es del funcionamiento de cada ladrillo que forma el sistema nervioso, esos ladrillos son llamados neuronas. En el cerebro humano hay contenidas alrededor de 100 mil millones de neuronas, las cuales se conectan unas con otras a través de las sinapsis.
Antes de pasar a describir las partes que componen una neurona quiero dejar claro las clases de neuronas presentes en el sistema nervioso:
  • Neuronas sensores: que constituyen el "input" de la red, entre las que encontramos las bastoncillos de la retina, los sensores olfativos, táctiles, etc. Estas neuronas son casi 1 millón.
  • Neuronas motrices: son las encargadas de dar la salida a la red, generalmente para dar el impulso motorio a los músculos. Son otro millón mas
  • Inter-neuronas: son neuronas en un punto medio, están conectadas entre las de entrada, salida y entre ellas mismas.
La constitución de una neurona es relativamente simple:
  • Un cuerpo central llamado soma, que como todas las células, esta dotada del patrimonio genetico de cada individuo.
  • Del soma se extienden unas fibras nerviosas llamada axón, estas pueden ser largas desde 1mm hasta llegar al metro de longitud.
  • Del soma mismo se derivan las dendritas que son las distintas ramificaciones que se pueden ver en cualquier ilustración de la neurona.
  • Finalmente están las sinapsis, que son como botones ubicados al final de las dendritas y del axón y que hacen de puente de comunicacion entre otras neuronas.


Sin estar a explicar como se efectua el paso del impulso electrico entre neuronas, solo decir que las sinapsis pueden tener una acción estimulatoria del impulso ( lo amplifican ) o inhibitorio ( lo atenúan o lo rechazan )

A la neurona llegan muchos señales, provenientes de las sinapsis desde otras neuronas y esta emite una solo señal de salida a través del axón, que a su vez deja el impulso eléctrico en otras neuronas a las que se conecta el axón.

Básicamente la neurona es un sumador, a cada intervalo de tiempo recibe las señales de las sinapsis de otras neuronas y suma todas las entradas ( tener en cuenta que la señal puede ser atenuada por la sinapsis ) y si el valor total supera un cierto limite, la neurona envía un impulso a través del axón, o en caso de no superarlo no da ninguna salida. En un cierto sentido puede parecer a un circuito binario.
El limite de superar para que la neurona de una señal de salida puede cambiar en el tiempo, depende principalmente de la concentración de algunas sustancias química ( por ejemplo, el café puede bajar este limite, un tranquilizante la alza )

Una cosa que hace de la neurona un elemento único es el hecho que puede cambiar en el tiempo la cantidad de las sinapsis con otras neuronas y su carácter estimulatorio/inhibitorio. De esto deriva que el cerebro es "especialmente apto para aprender". Estas conexiones cambian constantemente en el curso de la vida del humano y no dependen del código genético.

Bienvenidos a mi blog

Saludos amables lectores.

En principio este blog tiene la finalidad de poner a dispocision material que personalmente he traducido, acerca de las redes de neurona artificiales ( conocidas con su nombre en ingles artificial neural network o ANN de ahora en adelante ).
Tratare de dar un enfoque no tan matemático como el que podrías encontrar en un libro, ya que este no es un argumento sencillo ni siquiera para los que estudian especialisticamente estos temas ( yo soy ingeniero mecánico, por ende nunca lo hice en la universidad, lo hago por motivacion personal ).
No pretendo ser tampoco el sustituto de un buen libro en el que podrías encontrar mucha mas información, pero a cambio de eso quiero que este tema llegue a mucha mas gente, al menos del lado teorico de las cosas.

Con este propósito y para poder dar ejemplos claros del funcionamiento de las ANN, usare dos herramientas: Matlab ( este no es específicamente un software gratuito, pero seguro que encuentras buenos demos por ahí ) y el uso del lenguaje de programacion C++ ( en el cual no soy tan experto, pero me defiendo bien ).

Sin mas que decir por el momento, espero que encontréis este blog interesante, ya que ese es mi propósito secundario.

Saludos