Implementación de un Perceptrón

Un perceptrón es la unidad básica de inferencia.  Esta unidad puede realizar tarea de clasificación mediante el aprendizaje de series de datos de entradas, asociados con una salida.  




Por ejemplo piense en la función AND  lógica está tiene entradas (Subscript[x, 1]), (Subscript[x, 2])  y una salida y. La función está dada por la siguiente tabla del verdad

TableForm[{{(Subscript[x, 1]),(Subscript[x, 2]), y}, {-1,-1, -1},{-1,1,-1},{1,-1,-1},{1,1,1}},TableAlignments->Right]

Note que se reemplaza el valor 0 con -1, esto para hacer más claro el hecho de utilizar función signo como función de activación.  Es decir la función que determina a que clase pertenece la salida (en este caso si es 1 o -1).

Función Signo(z) ({{{, ({{    1, y≥0}, {-1, y<0}})}})

en Mathematica  la función signo ya está implementada como Sign[x].

Sign[3]

Sign[-5]

Sign[0]

Plot[ Sign[t],{t,-3,3},PlotRange->All]

La función Sing regresa 0 para t=0

Table[Sign[t],{t,-3,3}]

Podemos implementar la función signo como sigue:

MySign[x_]:= If[x≥0,1,-1];

Plot[ MySign[t],{t,-5,5},PlotRange->All]

La idea general de un preceptrón consiste en combinar las entradas de forma poderada para generar la salida z.

Table[{i, Sign[i]},{i,-5,5,1}]

(*Tomada de wikipedia*)

Donde:
(Subscript[w, i]) Son los pesos del perceptrón
(Subscript[x, i]) Los valores de entrada
z   La salida esperada
Σf[x]  Es la función que combina los valores de utilizando g[x,(Subscript[w, i])]. Para esto puede utilizarse la función lineal
         Σf[x]=(Subscript[w, 1]).(Subscript[x, 1])+(Subscript[w, 2]).(Subscript[x, 2])+ ... + (Subscript[w, n]).(Subscript[x, n])  
En notación vectorial
F[X]=X*W’

Algoritmo de aprendizaje

El algoritmo de aprendizaje básico esta dado de la siguiente forma: 
1. Inicializamos con pesos aleatorios para cado uno de los (Subscript[w, i])
2. Calculamos el valor esperado (sp) utilizando los pesos (Subscript[w, i]) en Σf[x] 
	a. Si y≠sp actualizamos los pesos  (Subscript[w, i])con la función
	    (Subscript[w, i])(k+1)=(Subscript[w, i])(k)+η[y-sp](Subscript[x, i]) 
	    En notación vectorial
	    W(k+1)=W+η(y-sp)X
3. Si y=sp para todos los caso la red ya se ha estabilizado y se termina el aprendizaje

La función (Subscript[w, i])(k+1)=(Subscript[w, i])(k)+η[y-sp](Subscript[x, i])  nos indica que la modificación al peso (Subscript[w, i]) será proporcional al producto del error [y-sp](Subscript[x, i]) por la í-esima componente  (Subscript[x, i]) de la entrada. La constante  η(k) es un parámetro
positivo conocido como  tasa de aprendizaje puesto que regula como se modifican los pesos asociados a cada entrada.

Implementación

Clear[Perceptron]
Perceptron[X_,Y_, η_:1,maxiter_:1000]:=Block[{W,rows, cols, SP,j=1},
	{rows,cols}=Dimensions[X];
	W= Table[RandomReal[{-1,1}], {i, 1, cols}]; (* Generamos los pesos iniciales*)
	SP=Sign[Dot[X, W]]; (* Calculamos la clasificación con los pesos iniciales *)
	While[Norm[SP-Y]≠0 && j< maxiter, (* Mientras no clasifique correctamente *)
        (*  Print["Y-SP ",Y-SP];  
         Print["W ",W];
	Print["SP ",SP];*)
	SP= Sign[Dot[X,W]];
         (* Print["(Y-SP)*X ",Dot[Y-SP, X]];*)
	W=W+η*(Dot[Y-SP, X]); (* Se actualizan los pesos *)
	j=j+1;
   ];
	Print[j];
	W
]

Uso del algoritmo

Generamos algunos datos aleatorios

nbd1=DeleteDuplicates[Table[{RandomInteger[{1, 10}],RandomInteger[{1, 10}]},{i,RandomInteger[100]}]];
nbd2=DeleteDuplicates[Table[{RandomInteger[{-10, -1}],RandomInteger[{-10, -1}]},{i,RandomInteger[100]}]];

Si los graficamos es evidente que existe una clase en el cuadrante positivo (+,+)   y otra en el cuadrante negativo (-,-)

ListPlot[{nbd1,nbd2}]

Para generar el clasificador utilizando nuestro perceptron simple debemos etiquetar las muestras. Los azules serán 1, los naranjas -1

X1=Join[nbd1,nbd2]; (* se juntan todos los datos*)
y1=Table[1,{i, Length[nbd1]}]; (* etiquetas de la clase 1*)
y2=Table[-1,{i, Length[nbd2]}]; (*etiquetas de la clase -1*)
Y1=Join[y1,y2];(* se unen la s etiquetas *)

p1=Perceptron[X1,Y1]

Podemos obtener la recta que divede como:

e1=Plot[-p1[[1]]/p1[[2]]*t, {t, -11,11} ]

Si la mostramos junto con los datos obtenemos

Show[e1,ListPlot[{nbd1,nbd2}], PlotRange->All]

Ejecute nuevamente el perceptron, grafique y explique que sucede.

Ejemplo con bias

Regresando al ejemplo de la función AND tenemos los siguientes datos

X2={{-1,-1},{-1,1},{1,-1},{1,1}};
(* X={{1},{1},{1},{-1}};*)
Y2={-1,-1,-1,1};

Determinanos los pesos

p2=Perceptron[X2,Y2]

la recta

e2=Plot[-p2[[1]]/p2[[2]]*t, {t, -1.2,1.2} ]

los datos

f2=ListPlot[{X2[[1;;3]],X2[[4;;]]}]

si los ponemos juntos

Show[e2,f2, PlotRange->All]

¿Qué ocurrió?

Si modificamos los datos y agregamos una entrada a cada muestra (Subscript[x, i]) obtenemos:

X3={{1,-1,-1},{1,-1,1},{1,1,-1},{1,1,1}};

Entrenamos...

p3=Perceptron[X3,Y2](*Se conservan las mismas etiquetas *)

Como tenemos un peso más que no depende de las entradas ahora tenemos que la funcion esta dada por:
Σf[x]=(Subscript[w, 1]).(Subscript[x, 1])+(Subscript[w, 2]).(Subscript[x, 2])+ ... + (Subscript[w, n]).(Subscript[x, n])+(Subscript[w, o]) 

lo anterior debido a que (Subscript[x, o]) es siempre 1.

para el caso de dos variables tenemos:
(Subscript[w, 1]).(Subscript[x, 1])+(Subscript[w, 2]).(Subscript[x, 2])+(Subscript[w, o])

como (Subscript[x, 2]) es la variable independiente la recta esta dada por:
(Subscript[x, 2])=-(Subscript[w, 1])/(Subscript[w, 2])(Subscript[x, 1])-(Subscript[w, 1])/(Subscript[w, 0])(Subscript[x, 2])

la obtenemos como:

e3=Plot[-p3[[2]]/p3[[3]]*t -p3[[1]]/p3[[3]], {t, -1.1,1.1} ]

las mostramos juntas

Show[e3,f2, PlotRange->All]

el tercer dato  que incluimos se denomina bias.

¿Que función tiene el dato adicional? 
Hint: Observe la diferencia entre las rectas generadas con y sin ese dato.

Ejercicio 1: Modifique el método del perceptron  para que agregue el bias.
Ejercicio 2: Pruebe su función con la funcion OR y XOR. Explíque lo que ocurre en cada caso.
Ejercicio 3: Implementar un método que utilice los pesos generados por  percetron  y que dado como entrada un dato  diga a que clase pertenece. Por ejmplo para p1  introduzca los dato las entrada {11, 11} y {-11, -11} (su método debería regresar 1 y -1 respectivamente).