Как будто вы пытаетесь получить доступ к объекту, который является null
. Рассмотрим ниже пример:
TypeA objA;
. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException
, что имеет смысл.
См. Также этот пример:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Взгляните на 15 Шаги по реализации нейросети , это должно привести вас к началу работы.
Мне кажется, что Вы боретесь с бэкпропом, и то, что Вы описываете выше, не совсем соответствует тому, как я понимаю его работу, и Ваше описание немного двусмысленно.
Вы вычисляете термин выходной ошибки для бэкпропагирования как разницу между предсказанием и фактическим значением, умноженным на производную передаточной функции. Именно это значение ошибки вы затем распространяете в обратном направлении. Производная сигмоида вычисляется достаточно просто как y(1-y), где y - ваше выходное значение. Доказательств этому есть в Интернете.
Для узла на внутреннем слое вы умножаете эту ошибку вывода на вес между двумя узлами, и суммируете все эти продукты как общую ошибку от внешнего слоя, распространяющуюся на узел на внутреннем слое. Ошибка, связанная с внутренним узлом, затем умножается на производную передаточной функции, примененной к исходному выходному значению. Вот псевдокод:
total_error = sum(output_errors * weights)
node_error = sigmoid_derivative(node_output) * total_error
Затем эта ошибка передается обратно таким же образом, как и во входных данных весов слоя.
Весы корректируются с использованием этих терминов ошибки, а выходные значения узлов
weight_change = outer_error * inner_output_value
скорость обучения важна, так как изменение веса вычисляется для каждого шаблона/ряда/наблюдения во входных данных. Вы хотите умерять изменение веса для каждого ряда, чтобы веса не менялись неоправданно ни для одного ряда, и чтобы все ряды влияли на веса. Скорость обучения дает вам это, и вы корректируете изменение веса, умножая на него
weight_change = outer_error * inner_output_value * learning_rate
Это также нормально помнить эти изменения между эпохами (итерации) и добавлять к ним долю. Добавленная доля называется импульсом и должна ускорять вас через те области поверхности ошибок, где не так уж много изменений, и замедлять вас там, где есть детали.
weight_change = (outer_error*inner_output_value*learning_rate) + (last_change*momentum)
Есть алгоритмы для регулировки скорости обучения и импульса по мере продолжения обучения.
Вес затем обновляется путем добавления изменения
new_weight = old_weight + weight_change
Я просмотрел ваш код, но вместо того, чтобы исправлять его и написал, что я подумал, что лучше описать для вас обратный реквизит, чтобы вы могли сами его закодировать. Если вы поймете это, вы сможете настроить его и для ваших обстоятельств.
HTH и удачи.