Extraña convergencia en red neuronal simple

He estado luchando durante algún tiempo con la construcción de un NN simplista en Java. He estado trabajando en este proyecto durante unos meses y quiero terminarlo. Mi problema principal es que no sé cómo implementar la propagación hacia atrás correctamente (todas las fuentes usan Python, jerga matemática o explican la idea demasiado brevemente). Hoy intenté deducir la ideología por mí mismo y la regla que estoy usando es:

la actualización de peso = error * sigmoidDerivative (error) * peso en sí mismo;
error = salida - real; (última capa)
error = sigmoide Derivativo (error de la capa anterior) * peso que une esta neurona a la neurona que da el error (capa intermedia)

Mis problemas principales son que las salidas convergen hacia un valor promedio y mi problema secundario es que los pesos se actualizan hacia un valor extremadamente extraño. (probablemente el problema de los pesos está causando la convergencia)

Lo que estoy tratando de entrenar: para las entradas 1-9, la salida esperada es: (x * 1.2 + 1) / 10. Esta es solo una regla que me llegó al azar. Estoy usando un NN con la estructura 1-1-1 (3 capas, 1 red / capa). En el siguiente enlace adjunto dos carreras: una en la que estoy usando el conjunto de entrenamiento que sigue la regla (x * 1.2 + 1) / 10 y en la otra estoy usando (x * 1.2 + 1) / 100. Con la división por 10, el primer peso va hacia el infinito; con la división por 100, el segundo peso tiende a 0. Seguí tratando de depurarlo, pero no tengo idea de lo que debería estar buscando o qué está mal. Cualquier sugerencia es muy apreciada. ¡Gracias de antemano y un gran día para todos ustedes!

https://wetransfer.com/downloads/55be9e3e10c56ab0d6b3f36ad990ebe120171210162746/1a7b6f

Tengo como ejemplos de entrenamiento 1-> 9 y sus respectivos resultados siguiendo la regla anterior y los ejecuto durante 100_000 épocas. Registro el error cada 100 épocas ya que es más fácil trazar con menos puntos de datos, mientras que todavía tengo 1000 puntos de datos para cada salida esperada del 9. Código para actualizaciones de peso y retropropagación:

    //for each layer in the Dweights array
    for(int k=deltaWeights.length-1; k >= 0; k--)
    {
        for(int i=0; i<deltaWeights[k][0].length; i++)     // for each neuron in the layer
        {
            if(k == network.length-2)      // if we're on the last layer, we calculate the errors directly
            {
                outputErrors[k][i] = outputs[i] - network[k+1][i].result;
                errors[i] = outputErrors[k][i];
            }
            else        // otherwise the error is actually the sum of errors feeding backwards into the neuron currently being processed * their respective weight
            {
                for(int j=0; j<outputErrors[k+1].length; j++)
                {                         // S'(error from previous layer) * weight attached to it
                    outputErrors[k][i] += sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j];
                }
            }
        }

        for (int i=0; i<deltaWeights[k].length; i++)           // for each neuron
        {
            for(int j=0; j<deltaWeights[k][i].length; j++)     // for each weight attached to that respective neuron
            {                        // error                S'(error)                                  weight connected to respective neuron                
                deltaWeights[k][i][j] = outputErrors[k][j] * sigmoidDerivative(outputErrors[k][j])[0] * network[k][i].emergingWeights[j];
            }
        }
    }

    // we use the learning rate as an order of magnitude, to scale how drastic the changes in this iteration are
    for(int k=deltaWeights.length-1; k >= 0; k--)       // for each layer
    {
        for (int i=0; i<deltaWeights[k].length; i++)            // for each neuron
        {
            for(int j=0; j<deltaWeights[k][i].length; j++)     // for each weight attached to that respective neuron
            {
                deltaWeights[k][i][j] *=  1;       // previously was learningRate; MSEAvgSlope

                network[k][i].emergingWeights[j] += deltaWeights[k][i][j];
            }
        }
    }

    return errors;

Editar: una pregunta rápida que me viene a la mente: dado que estoy usando sigmoid como mi función de activación, ¿deberían mis neuronas de entrada y salida estar solo entre 0-1? Mi salida está entre 0-1 pero mis entradas literalmente son 1-9.

Edit2: normalizó los valores de entrada a 0.1-0.9 y cambió:

    outputErrors[k][i] += sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j];     

a:

    outputErrors[k][i] = sigmoidDerivative(outputErrors[k+1][j])[0] * network[k+1][i].emergingWeights[j]* outputErrors[k+1][j];       

para mantener el signo del error de salida en sí. Esto reparó la tendencia Infinity en el primer peso. Ahora, con la ejecución de / 10, el primer peso tiende a 0 y con la ejecución de / 100, el segundo peso tiende a 0. Todavía espero que alguien brote para aclararme las cosas. :(

Respuestas a la pregunta(1)

Su respuesta a la pregunta