d_m «m» здесь представляет вход нейрона m (из выходного слоя)?

орое время я боролся с созданием упрощенного NN в Java. Я работал над этим проектом и выключался в течение нескольких месяцев, и я хочу закончить его. Моя главная проблема заключается в том, что я не знаю, как правильно реализовать обратное распространение (все источники используют Python, математический жаргон или объясняют идею слишком кратко). Сегодня я попытался вывести идеологию самостоятельно, и я использую следующее правило:

обновление веса = ошибка * sigmoidDerivative (ошибка) * вес самого;
ошибка = вывод - фактическая; (последний слой)
error = sigmoidDerivative (ошибка предыдущего слоя) * вес, прикрепляющий этот нейрон к нейрону, дающий ошибку (промежуточный слой)

Мои главные проблемы состоят в том, что выходные данные сходятся к среднему значению, а моя вторичная проблема заключается в том, что веса обновляются до чрезвычайно странного значения. (вероятно, проблема весов вызывает сближение)

То, что я пытаюсь обучить: для входов 1-9 ожидаемый результат: (x * 1.2 + 1) / 10. Это просто правило, которое пришло ко мне случайно. Я использую NN со структурой 1-1-1 (3 слоя, 1 сеть / слой). В приведенной ниже ссылке я приложил два прогона: в одном я использую тренировочный набор, который следует правилу (x * 1.2 + 1) / 10, а в другом я использую (x * 1.2 + 1) / 100. При делении на 10 первый вес уходит в бесконечность; при делении на 100 второй вес стремится к 0. Я продолжал пытаться отлаживать его, но я понятия не имею, что мне нужно искать или что не так. Любые предложения очень ценятся. Заранее спасибо и отличного дня всем вам!

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

В качестве обучающих примеров я использую 1-> 9 и соответствующие им выводы, следуя приведенному выше правилу, и пробую их в течение 100_000 эпох. Я регистрирую ошибку каждые 100 эпох, так как ее легче построить с меньшим количеством точек данных, при этом все еще имея 1000 точек данных для каждого ожидаемого выхода 9. Код для обратного распространения и обновлений веса:

    //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;

Изменить: быстрый вопрос, который приходит на ум: так как я использую сигмоид в качестве своей функции активации, должны ли мои входные и выходные нейроны быть только между 0-1? Мой вывод находится между 0-1, но мои входы буквально 1-9.

Edit2: нормализовал входные значения на 0,1-0,9 и изменил:

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

чтобы:

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

так что я держу знак самой ошибки вывода. Это исправило тенденцию Бесконечности в первом весе. Теперь, с пробегом / 10, первый вес стремится к 0, а с пробегом / 100, второй вес стремится к 0. Все еще надеясь, что кто-то подойдет, чтобы прояснить ситуацию для меня. :(

Ответы на вопрос(1)

Ваш ответ на вопрос