Реализация персептрона с алгоритмом обратного распространения ошибки

Я пытаюсь реализовать двухслойный перцептрон с обратным распространением, чтобы решить проблему четности. Сеть имеет 4 двоичных входа, 4 скрытых блока на первом уровне и 1 выход на втором уровне. Я использую это для справки, но у меня возникли проблемы со схождением.

Во-первых, я отмечу, что я использую сигмовидную функцию для активации, и поэтому производной является (насколько я понимаю) сигмоид (v) * (1 - sigmoid (v)). Итак, это используется при вычислении значения дельты.

Итак, в основном я настраиваю сеть и прохожу всего несколько эпох (просматриваю все возможные шаблоны - в данном случае 16 шаблонов ввода). После первой эпохи веса немного меняются. После второго веса не меняются и остаются такими, сколько бы эпох я ни пробежал. Сейчас я использую скорость обучения 0,1 и смещение +1.

Процесс обучения сети представлен ниже в псевдокоде (который я считаю правильным согласно источникам, которые я проверил):

Шаг вперед:

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

Обратное распространение выходного слоя:

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

Обратное распространение скрытого слоя:

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

Обновить веса:

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

Этот процесс, конечно же, проходит через эпохи, и изменения веса сохраняются. Итак, мой вопрос: есть ли какие-либо причины, по которым веса остаются постоянными после второй эпохи? При необходимости я могу опубликовать свой код, но в данный момент я надеюсь на что-то очевидное, что я не замечаю. Спасибо всем!

РЕДАКТИРОВАТЬ: Вот ссылки на мой код, предложенные sarnold:
MLP.java: http://codetidy.com/1903
Neuron.java: http://codetidy.com/1904
Шаблон .java: http://codetidy.com/1905
input.txt: http://codetidy.com/1906


person Aaron    schedule 07.02.2012    source источник
comment
У меня была очень похожая ошибка в моем курсе нейронных сетей много лет назад: значения не менялись в течение многих десятков итераций, затем все переходило на NaN выходы без явной провокации. Ошибка заключалась в том, что где-то была одна неправильная строка, на которую мне потребовались часы. Вам, вероятно, следует опубликовать фрагмент своего кода, может быть, кто-то еще заметит, как я полагаю, однострочную ошибку в вашей программе. :)   -  person sarnold    schedule 07.02.2012
comment
Спасибо, я только что обновил свой пост   -  person Aaron    schedule 07.02.2012


Ответы (1)


Думаю, я заметил проблему; Как ни странно, то, что я нашел, видно в вашем высокоуровневом описании, но я нашел только то, что выглядело странным в коде. Во-первых, описание:

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

Я считаю, что h.weight следует обновить по сравнению с предыдущим весом. Ваш механизм обновления устанавливает его на основе только скорости обучения, выходной дельты и значения узла. Точно так же x.weight также устанавливается на основе скорости обучения, скрытой дельты и значения узла:

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

Я не знаю, какое правильное решение; но у меня есть два предложения:

  1. Замените эти строки:

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

    с этими строками:

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

    (+= вместо =.)

  2. Замените эти строки:

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

    с этими строками:

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

    (Не обращайте внимания на значение и просто масштабируйте существующий вес. Для этого изменения скорость обучения должна быть 1.05 вместо .05.)

person sarnold    schedule 07.02.2012
comment
Да, должно быть + =. Не могу поверить, что пропустил что-то подобное. Я ценю время, которое вы потратили, чтобы помочь мне, большое спасибо! - person Aaron; 07.02.2012