Introducción

XOR compara dos bits de entrada y genera un bit de salida. Si los bits son iguales, el resultado es 0. Si los bits son diferentes, el resultado es 1. Los resultados de esta operación pueden resumirse en la tabla de abajo:

##   x1 x2 y
## 1  0  0 0
## 2  0  1 1
## 3  1  1 0
## 4  1  0 1

Sea desea utilizar una red neuronal con dos inputs (basados en la tabla anterior), dos neuronas ocultas y una neurona de salida, en este caso será hará el ejercicio con la fila dos y el proceso solo tendrá dos épocas . Además, tendremos en cuenta la función logística como función de activación. De esta forma, se aplicará el método de retropropagación, cuyo objetivo es optimizar los pesos para que la red neuronal pueda aprender a asignar correctamente entradas arbitrarias a salidas. Para empezar, debemos ver qué predice actualmente la red neuronal dados unos pesos, unos sesgos y algunos valores de las variables como entradas iniciales. Con estos valores, alimentaremos esas entradas a través de la red. En este sentido, calculamos la entrada neta total a cada neurona de la capa oculta, obtenemos la salida correspondiente de cada neurona transformando la entrada neta total con ayuda de una función de activación (aquí utilizamos la función logística) y, a continuación, repetimos el proceso con las neuronas de la capa de salida. Para este caso, se usarán los siguientes pesos y sesgos:
- W1=0.1
- W2=0.5
- W3=-0.7
- W4=0.3
- W5=0.2
- W6=0.4
- b1=0
- b2=0
- b3=0

En este sentido, la estructura básica será:

Desarrollo del algoritmo

El algoritmo que se plantea está pensado para el caso que tengamos las neuronas de la imagen. De esta forma, el usuario puede hacer el ejercicio con los pesos, sesgos, epocas y entradas que desee; para ello, se plantearán las siguientes funciones que serán llamadas para ejecutarse en una función principal:

Las funciones que se harán para automarizar estas tareas serán hechas en python para una mayor facilidad en el manejo de matrices y operaciones. De esta forma, para este problema se plantea la creación de las siguientes funciones.

Nota: en el ejercicio hecho a mano en clase hubo un error con el calculo de las derivadas del error con respecto a z3, porque al hacer la derivada del error total con respecto a f3, es decir,
\[ \frac{\partial E_{total}}{\partial f_3} \cdot \frac{\partial f_3}{\partial z_3} = (O_1 - \hat{O}_1)(1 - f_3) f_3 \] Se hace la derivada del exponente pero no la interior porque el resultado debe ser el siguiente:
\[ \frac{\partial E_{total}}{\partial f_3} \cdot \frac{\partial f_3}{\partial z_3} = -(O_1 - \hat{O}_1)(1 - f_3) f_3 \]

Por tanto, se hará el ejercicio con este ultimo resultado y luego con la versión hecha en clase.

Primera epoca

La primera época se harán con los pesos y sesgos definidos anteriormente. Haremos esta primera época por pasos.

1. Se inicializa una matriz con los pesos

Pesos iniciales
Valor
W1 0.1
W2 0.5
W3 -0.7
W4 0.3
W5 0.2
W6 0.4
B1 0.0
B2 0.0
B3 0.0

2. Se realiza la propagación hacia adelante

Valores de los z y f(zi)
Valor
Z1 -0.7000000
F1 0.3318122
Z2 0.3000000
F2 0.5744425
Z3 0.2961395
F3 0.5734985

3. Se calcula el error total

## [1] 0.09095176

4. Se realiza la progragación hacia atrás

En este caso se usan las dos funciones para notar que funcionan igual, solo que una es automátizada y la otra solo sirve para esta red.

##           W1           W2           W3           W4           W5           W6 
##  0.000000000  0.000000000 -0.004625879 -0.010200893 -0.034615116 -0.059926648 
##           B1           B2           B3 
## -0.004625879 -0.010200893 -0.104321400
Gradientes de los pesos y sesgos
Parámetro Valor
dE_dW1 ∂E/∂W1 0.0000000
dE_dW2 ∂E/∂W2 0.0000000
dE_dW3 ∂E/∂W3 -0.0046259
dE_dW4 ∂E/∂W4 -0.0102009
dE_dW5 ∂E/∂W5 -0.0346151
dE_dW6 ∂E/∂W6 -0.0599266
dE_dB1 ∂E/∂B1 -0.0046259
dE_dB2 ∂E/∂B2 -0.0102009
dE_dB3 ∂E/∂B3 -0.1043214

5. Se actualizan los pesos

Pesos actualizados
Valor
W1 0.1000000
W2 0.5000000
W3 -0.6988435
W4 0.3025502
W5 0.2086538
W6 0.4149817
B1 0.0000000
B2 0.0000000
B3 0.0000000

Resultado

La salida del la red neural para esta epoca fue 0.5734 y el error total fue 0.090951 y la gráfica de la red es la siguiente:

Segunda Época

Se ejecutará las mismas funciones que en la primer época pero solo se mostrará la propagación hacia adelante, la propagación hacia atrás, los pesos actualizados y el error total.

Progragación hacia adelante

Valores de los z y f(zi)
Valor
Z1 -0.6988435
F1 0.3320687
Z2 0.3025502
F2 0.5750658
Z3 0.3079292
F3 0.5763797

Propagación hacia atrás

Gradientes de los pesos y sesgos
Parámetro Valor
dE_dW1 ∂E/∂W1 0.0000000
dE_dW2 ∂E/∂W2 0.0000000
dE_dW3 ∂E/∂W3 -0.0047868
dE_dW4 ∂E/∂W4 -0.0104889
dE_dW5 ∂E/∂W5 -0.0343471
dE_dW6 ∂E/∂W6 -0.0594812
dE_dB1 ∂E/∂B1 -0.0047868
dE_dB2 ∂E/∂B2 -0.0104889
dE_dB3 ∂E/∂B3 -0.1034337

Pesos actualizados

Pesos actualizados
Valor
W1 0.1000000
W2 0.5000000
W3 -0.6976468
W4 0.3051725
W5 0.2172406
W6 0.4298520
B1 0.0000000
B2 0.0000000
B3 0.0000000

Error total

## [1] 0.08972707

Resultado

La salida del la red neural para esta epoca fue 0.5763 y el error total fue 0.08972. Es decir, el error disminuyó y la salida de la red se acercó más a la salida esperada 1.
La gráfica de la red después de la segunda época es la siguiente:

Prueba con los resultados hechos en clase

Se realizará el mismo ejercicio pero con la observación que se hizo en las derivadas del error total. Entonces se modificará la función gradiente_manual_nombres(). Los resultados resumidos para las dos epocas son los siguientes:

## 
## ====== Época 1 ======
## Salida (y): 0.5735 
## Error: 0.090952 
## Pesos actuales:
## $W1
## [1] 0.1
## 
## $W2
## [1] 0.5
## 
## $W3
## [1] -0.7
## 
## $W4
## [1] 0.3
## 
## $W5
## [1] 0.2
## 
## $W6
## [1] 0.4
## 
## $B1
## [1] 0
## 
## $B2
## [1] 0
## 
## $B3
## [1] 0
## 
## Gradientes:
## $dE_dW1
## [1] 0
## 
## $dE_dW2
## [1] 0
## 
## $dE_dW3
## [1] 0.004625879
## 
## $dE_dW4
## [1] 0.01020089
## 
## $dE_dW5
## [1] 0.03461512
## 
## $dE_dW6
## [1] 0.05992665
## 
## $dE_dB1
## [1] 0.004625879
## 
## $dE_dB2
## [1] 0.01020089
## 
## $dE_dB3
## [1] 0.1043214
## 
## 
## ====== Época 2 ======
## Salida (y): 0.5706 
## Error: 0.092184 
## Pesos actuales:
## $W1
## [1] 0.1
## 
## $W2
## [1] 0.5
## 
## $W3
## [1] -0.7011565
## 
## $W4
## [1] 0.2974498
## 
## $W5
## [1] 0.1913462
## 
## $W6
## [1] 0.3850183
## 
## $B1
## [1] 0
## 
## $B2
## [1] 0
## 
## $B3
## [1] 0
## 
## Gradientes:
## $dE_dW1
## [1] 0
## 
## $dE_dW2
## [1] 0
## 
## $dE_dW3
## [1] 0.004461439
## 
## $dE_dW4
## [1] 0.009905665
## 
## $dE_dW5
## [1] 0.03488108
## 
## $dE_dW6
## [1] 0.06036818
## 
## $dE_dB1
## [1] 0.004461439
## 
## $dE_dB2
## [1] 0.009905665
## 
## $dE_dB3
## [1] 0.1052042
## 
## Pesos finales:
## $W1
## [1] 0.1
## 
## $W2
## [1] 0.5
## 
## $W3
## [1] -0.7022718
## 
## $W4
## [1] 0.2949734
## 
## $W5
## [1] 0.182626
## 
## $W6
## [1] 0.3699263
## 
## $B1
## [1] 0
## 
## $B2
## [1] 0
## 
## $B3
## [1] 0

Notemos que el error aumenta en lugar de disminuir, entonces puede que ocurra un problema con estos resultados de la clase.