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. Все еще надеясь, что кто-то подойдет, чтобы прояснить ситуацию для меня. :(