Problemas para comprender el algoritmo de retropropagación en la red neuronal

Annie Caron:

Tengo problemas para entender el algoritmo de retropropagación. Leí mucho y busqué mucho, pero no puedo entender por qué mi red neuronal no funciona. Quiero confirmar que estoy haciendo cada parte de la manera correcta.

Aquí está mi red neuronal cuando se inicializa y cuando se establece la primera línea de entradas [1, 1] y la salida [0] (como puede ver, estoy tratando de hacer la red neuronal XOR):

Mi red neuronal

Tengo 3 capas: entrada, oculta y salida. La primera capa (entrada) y la capa oculta contienen 2 neuronas en las que hay 2 sinapsis cada una. La última capa (salida) contiene una neurona con 2 sinapsis también.

Una sinapsis contiene un peso y su delta anterior (al principio, es 0). La salida conectada a la sinapsis se puede encontrar con la fuente de neurona asociada con la sinapsis o en la matriz de entradas si no hay fuente de neurona (como en la capa de entrada).

La clase Layer.java contiene una lista de neuronas. En mi NeuralNetwork.java , inicializo la Red Neural y luego entro en mi conjunto de entrenamiento. En cada iteración, reemplazo las entradas y los valores de salida y llamo al tren en mi Algoritmo de BackPropagation y el algoritmo ejecuta cierto número de tiempo (época de 1000 veces por ahora) para el conjunto actual.

La función de activación que uso es el sigmoide.

El conjunto de entrenamiento Y el conjunto de validación es (entrada1, entrada2, salida):

1,1,0
0,1,1
1,0,1
0,0,0

Aquí está mi implementación Neuron.java :

public class Neuron {

    private IActivation activation;
    private ArrayList<Synapse> synapses; // Inputs
    private double output; // Output
    private double errorToPropagate;

    public Neuron(IActivation activation) {
        this.activation = activation;
        this.synapses = new ArrayList<Synapse>();
        this.output = 0;
        this.errorToPropagate = 0;
    }

    public void updateOutput(double[] inputs) {
        double sumWeights = this.calculateSumWeights(inputs);

        this.output = this.activation.activate(sumWeights);
    }

    public double calculateSumWeights(double[] inputs) {
        double sumWeights = 0;

        int index = 0;
        for (Synapse synapse : this.getSynapses()) {
            if (inputs != null) {
                sumWeights += synapse.getWeight() * inputs[index];
            } else {
                sumWeights += synapse.getWeight() * synapse.getSourceNeuron().getOutput();
            }

            index++;
        }

        return sumWeights;
    }

    public double getDerivative() {
        return this.activation.derivative(this.output);
    }

    [...]
}

El Synapse.java contiene:

public Synapse(Neuron sourceNeuron) {
    this.sourceNeuron = sourceNeuron;
    Random r = new Random();
    this.weight = (-0.5) + (0.5 - (-0.5)) * r.nextDouble();
    this.delta = 0;
}

[... getter and setter ...]

El método de entrenamiento en mi clase BackpropagationStrategy.java ejecuta un ciclo while y se detiene después de 1000 veces (época) con una línea del conjunto de entrenamiento. Se parece a esto:

this.forwardPropagation(neuralNetwork, inputs);

this.backwardPropagation(neuralNetwork, expectedOutput);

this.updateWeights(neuralNetwork);

Aquí está toda la implementación de los métodos anteriores (learningRate = 0.45 y momentum = 0.9):

public void forwardPropagation(NeuralNetwork neuralNetwork, double[] inputs) {

    for (Layer layer : neuralNetwork.getLayers()) {

        for (Neuron neuron : layer.getNeurons()) {
            if (layer.isInput()) {
                neuron.updateOutput(inputs);
            } else {
                neuron.updateOutput(null);
            }
        }
    }
}

public void backwardPropagation(NeuralNetwork neuralNetwork, double realOutput) {

    Layer lastLayer = null;

    // Loop à travers les hidden layers et le output layer uniquement
    ArrayList<Layer> layers = neuralNetwork.getLayers();
    for (int i = layers.size() - 1; i > 0; i--) {
        Layer layer = layers.get(i);

        for (Neuron neuron : layer.getNeurons()) {

            double errorToPropagate = neuron.getDerivative();

            // Output layer
            if (layer.isOutput()) {

                errorToPropagate *= (realOutput - neuron.getOutput());
            }
            // Hidden layers
            else {
                double sumFromLastLayer = 0;

                for (Neuron lastLayerNeuron : lastLayer.getNeurons()) {
                    for (Synapse synapse : lastLayerNeuron.getSynapses()) {
                        if (synapse.getSourceNeuron() == neuron) {
                            sumFromLastLayer += (synapse.getWeight() * lastLayerNeuron.getErrorToPropagate());

                            break;
                        }
                    }
                }

                errorToPropagate *= sumFromLastLayer;
            }

            neuron.setErrorToPropagate(errorToPropagate);
        }

        lastLayer = layer;
    }
}

public void updateWeights(NeuralNetwork neuralNetwork) {

    for (int i = neuralNetwork.getLayers().size() - 1; i > 0; i--) {

        Layer layer = neuralNetwork.getLayers().get(i);

        for (Neuron neuron : layer.getNeurons()) {

            for (Synapse synapse : neuron.getSynapses()) {

                double delta = this.learningRate * neuron.getError() * synapse.getSourceNeuron().getOutput();

                synapse.setWeight(synapse.getWeight() + delta + this.momentum * synapse.getDelta());

                synapse.setDelta(delta);
            }
        }
    }
}

Para el conjunto de validación, solo ejecuto esto:

this.forwardPropagation(neuralNetwork, inputs);

Y luego verifique la salida de la neurona en mi capa de salida.

¿Hice algo mal? Necesito algunas explicaciones ...

Aquí están mis resultados después de 1000 épocas:

Real: 0.0
Current: 0.025012156926937503
Real: 1.0
Current: 0.022566830709341495
Real: 1.0
Current: 0.02768416343491415
Real: 0.0
Current: 0.024903432706154027

¿Por qué las sinapsis en la capa de entrada no se actualizan? En todas partes está escrito para actualizar solo las capas ocultas y de salida.

Como puedes ver, ¡está totalmente mal! No va al 1.0 solo a la salida del primer conjunto de trenes (0.0).

ACTUALIZACIÓN 1

Aquí hay una iteración sobre la red con este conjunto: [1.0,1.0,0.0]. Aquí está el resultado para el método de propagación directa:

=== Input Layer

== Neuron #1

= Synapse #1
Weight: -0.19283583155573614
Input: 1.0

= Synapse #2
Weight: 0.04023817185601586
Input: 1.0

Sum: -0.15259765969972028
Output: 0.461924442180935

== Neuron #2

= Synapse #1
Weight: -0.3281099260608612
Input: 1.0

= Synapse #2
Weight: -0.4388250065958519
Input: 1.0

Sum: -0.7669349326567131
Output: 0.31714251453174147

=== Hidden Layer

== Neuron #1

= Synapse #1
Weight: 0.16703288052854093
Input: 0.461924442180935

= Synapse #2
Weight: 0.31683996162148054
Input: 0.31714251453174147

Sum: 0.17763999229679783
Output: 0.5442935820534444

== Neuron #2

= Synapse #1
Weight: -0.45330313978424686
Input: 0.461924442180935

= Synapse #2
Weight: 0.3287014377113835
Input: 0.31714251453174147

Sum: -0.10514659949771789
Output: 0.47373754172497556

=== Output Layer

== Neuron #1

= Synapse #1
Weight: 0.08643751629154495
Input: 0.5442935820534444

= Synapse #2
Weight: -0.29715579267218695
Input: 0.47373754172497556

Sum: -0.09372646936373039
Output: 0.47658552081912403

Actualización 2

Probablemente tengo un problema de sesgo. Lo investigaré con la ayuda de esta respuesta: Rol del sesgo en las redes neuronales . No retrocede en el siguiente conjunto de datos, así que ...

Annie Caron:

Finalmente encontré el problema. Para el XOR, no necesitaba ningún sesgo y estaba convergiendo a los valores esperados. Obtuve exactamente la salida cuando redondeas la salida final. Lo que se necesitaba es entrenar y luego validar, luego entrenar nuevamente hasta que la Red Neural sea satisfactoria. Estaba entrenando cada set hasta la satisfacción, pero no TODO el set una y otra vez.

// Initialize the Neural Network
algorithm.initialize(this.numberOfInputs);

int index = 0;
double errorRate = 0;

// Loop until satisfaction or after some iterations
do {
    // Train the Neural Network
    algorithm.train(this.trainingDataSets, this.numberOfInputs);

    // Validate the Neural Network and return the error rate
    errorRate = algorithm.run(this.validationDataSets, this.numberOfInputs);

    index++;
} while (errorRate > minErrorRate && index < numberOfTrainValidateIteration);

Con los datos reales, necesito un sesgo porque las salidas comenzaron a divergir. Así es como agregué el sesgo:

En la clase Neuron.java , agregué una sinapsis de sesgo con un peso y una salida de 1.0. Lo sumo con todas las otras sinapsis y luego lo pongo en mi función de activación.

public class Neuron implements Serializable {

    [...]

    private Synapse bias;

    public Neuron(IActivation activation) {
        [...]
        this.bias = new Synapse(this);
        this.bias.setWeight(0.5); // Set initial weight OR keep the random number already set
    }

    public void updateOutput(double[] inputs) {
        double sumWeights = this.calculateSumWeights(inputs);

        this.output = this.activation.activate(sumWeights + this.bias.getWeight() * 1.0);
    }

    [...]

En BackPropagationStrategy.java , cambio el peso y el delta de cada sesgo en el método updateWeights que renombré updateWeightsAndBias.

public class BackPropagationStrategy implements IStrategy, Serializable {

    [...]

    public void updateWeightsAndBias(NeuralNetwork neuralNetwork, double[] inputs) {

        for (int i = neuralNetwork.getLayers().size() - 1; i >= 0; i--) {

            Layer layer = neuralNetwork.getLayers().get(i);

            for (Neuron neuron : layer.getNeurons()) {

                [...]

                Synapse bias = neuron.getBias();
                double delta = learning * 1.0;
                bias.setWeight(bias.getWeight() + delta + this.momentum * bias.getDelta());

                bias.setDelta(delta);
            }
        }
    }

    [...]

Con los datos reales, la red está convergiendo. Ahora es un trabajo de poda encontrar el combo de variables perfectas (si es posible) de tasa de aprendizaje, momento, tasa de error, cantidad de neuronas, cantidad de capas ocultas, etc.

Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.

En caso de infracción, por favor [email protected] Eliminar

Editado en
0

Déjame decir algunas palabras

0Comentarios
Iniciar sesiónRevisión de participación posterior

Artículos relacionados

Trabajar con el algoritmo de retropropagación usando la función softmax en la red neuronal

Problemas para comprender la red neuronal convolucional

sobre la red neuronal profunda de retropropagación en tensorflow

La red neuronal de retropropagación no funciona

Algoritmo de red neuronal de retropropagación simple (Python)

Problemas para comprender el orden de ejecución en un algoritmo java (k = y ++ k)

Problemas para comprender la red de convolución total

Red neuronal para problemas de optimización

Comprender el efecto sobre la resolución mediante el uso de agrupación promedio en la red neuronal de convolución

Actualización de pesos en el algoritmo de retropropagación

El algoritmo de propagación hacia atrás de la red neuronal se atasca en el patrón de entrenamiento XOR

Cómo comprender las imágenes de entrada para la red neuronal convolucional en este código

Comprender el uso de la memoria de la red neuronal entrenada

Problemas para optimizar el algoritmo iterativo de la hora punta de profundización en JavaScript

Problemas para comprender la división de datos en Scala usando "randomSplit" para el aprendizaje automático

Retropropagación en una red neuronal Tensorflow.js

Retropropagación en una red neuronal Tensorflow.js

retropropagación - red neuronal - derivada

Elección de variables para la red neuronal utilizada para el reconocimiento de imágenes

Comprender el algoritmo de clasificación para C

Cómo comprender la salida de una red neuronal multiclase

Problemas para comprender el bucle en el orden de selección

El modelo de juguete de red neuronal para adaptarse a la función sinusoidal falla, ¿qué pasa?

Problemas para comprender el uso de ʻinterface {} `en Go

¿Cómo elegir el número de unidades para la capa densa en la red neuronal enrevesada para un problema de clasificación de imágenes?

Problemas para comprender el retorno de la cadena

MNIST de la red neuronal: la retropropagación es correcta, pero la precisión del entrenamiento / prueba es muy baja

Cómo eliminar el ruido de deconvolución en la red neuronal de transferencia de estilo

¿Por qué se debe utilizar una función de activación no lineal en una red neuronal de retropropagación?