Implementación de un perceptrón con algoritmo de retropropagación

2012-02-07 backpropagation neural-network

Estoy tratando de implementar un perceptrón de dos capas con propagación hacia atrás para resolver el problema de paridad. La red tiene 4 entradas binarias, 4 unidades ocultas en la primera capa y 1 salida en la segunda capa. Estoy usando esto como referencia, pero tengo problemas con la convergencia.

Primero, notaré que estoy usando una función sigmoide para la activación, por lo que la derivada es (por lo que entiendo) el sigmoide (v) * (1 - sigmoide (v)). Entonces, eso se usa al calcular el valor delta.

Entonces, básicamente configuré la red y ejecuté solo unas pocas épocas (repase cada patrón posible, en este caso, 16 patrones de entrada). Después de la primera época, los pesos cambian ligeramente. Después del segundo, los pesos no cambian y permanecen así que no importa cuántas épocas más corro. Estoy usando una tasa de aprendizaje de 0.1 y un sesgo de +1 por ahora.

El proceso de capacitación de la red se encuentra a continuación en pseudocódigo (que creo que es correcto según las fuentes que he verificado):

Paso de avance de alimentación:

v = SUM[weight connecting input to hidden * input value] + bias  
y = Sigmoid(v)  
set hidden.values to y  
v = SUM[weight connecting hidden to output * hidden value] + bias  
y = Sigmoid(v)  
set output value to y

Propagación hacia atrás de la capa de salida:

error = desired - output.value  
outputDelta = error * output.value * (1 - output.value)

Backpropagation of Hidden Layer:

for each hidden neuron h:  
error = outputDelta * weight connecting h to output  
hiddenDelta[i] = error * h.value * (1 - h.value)

Actualizar pesos:

for each hidden neuron h connected to the output layer  
h.weight connecting h to output = learningRate * outputDelta * h.value

for each input neuron x connected to the hidden layer  
x.weight connecting x to h[i] = learningRate * hiddenDelta[i] * x.value

Por supuesto, este proceso se repite a través de las épocas y los cambios de peso persisten. Entonces, mi pregunta es, ¿hay alguna razón para que los pesos permanezcan constantes después de la segunda época? Si es necesario, puedo publicar mi código, pero en este momento espero algo obvio que estoy pasando por alto. ¡Gracias a todos!

EDITAR: Aquí están los enlaces a mi código según lo sugerido por sarnold:
MLP.java: http://codetidy.com/1903
Neuron.java: http://codetidy.com/1904
Pattern.java: http://codetidy.com/1905
input.txt: http://codetidy.com/1906

Answers

Creo que descubrí el problema; Lo suficientemente divertido, lo que encontré es visible en su descripción de alto nivel, pero solo encontré lo que parecía extraño en el código. Primero, la descripción:

for each hidden neuron h connected to the output layer
h.weight connecting h to output = learningRate * outputDelta * h.value

for each input neuron x connected to the hidden layer
x.weight connecting x to h[i] = learningRate * hiddenDelta[i] * x.value

Creo que el h.weight debería actualizarse con respecto al peso anterior. Su mecanismo de actualización lo establece en función de la tasa de aprendizaje, el delta de salida y el valor del nodo. Del mismo modo, el x.weight también se establece en función de la tasa de aprendizaje, el delta oculto y el valor del nodo:

    /*** Weight updates ***/

    // update weights connecting hidden neurons to output layer
    for (i = 0; i < output.size(); i++) {
        for (Neuron h : output.get(i).left) {
            h.weights[i] = learningRate * outputDelta[i] * h.value;
        }
    }

    // update weights connecting input neurons to hidden layer
    for (i = 0; i < hidden.size(); i++) {
        for (Neuron x : hidden.get(i).left) {
            x.weights[i] = learningRate * hiddenDelta[i] * x.value;
        }
    }

No sé cuál es la solución correcta ; pero tengo dos sugerencias:

  1. Reemplace estas líneas:

            h.weights[i] = learningRate * outputDelta[i] * h.value;
            x.weights[i] = learningRate * hiddenDelta[i] * x.value;
    

    con estas líneas:

            h.weights[i] += learningRate * outputDelta[i] * h.value;
            x.weights[i] += learningRate * hiddenDelta[i] * x.value;
    

    ( += lugar de = .)

  2. Reemplace estas líneas:

            h.weights[i] = learningRate * outputDelta[i] * h.value;
            x.weights[i] = learningRate * hiddenDelta[i] * x.value;
    

    con estas líneas:

            h.weights[i] *= learningRate * outputDelta[i];
            x.weights[i] *= learningRate * hiddenDelta[i];
    

    (Ignore el valor y simplemente escale el peso existente. La tasa de aprendizaje debe ser 1.05 lugar de .05 para este cambio).

Related