Обзор
Итак, я пытаюсь понять механику нейронных сетей. Я все еще не совсем понимаю математику, стоящую за этим, но я думаю, что понимаю, как это реализовать. В настоящее время у меня есть нейронная сеть, которая может изучать шаблоны обучения И, ИЛИ и ИЛИ. Однако я не могу заставить его реализовать шаблон XOR. Моя нейронная сеть с прямой связью состоит из 2 входов, 3 скрытых и 1 выход. Веса и смещения случайным образом устанавливаются между -0,5 и 0.5 , а выходы генерируются с помощью сигмоидальной функции активации
Алгоритм
Пока что я предполагаю, что допустил ошибку в своем алгоритме обучения, который описан ниже:
, которое является желаемым выходом - фактическим выходом
- , перейдите к шагу 3
, которое представляет собой сумму всех весов прямого соединения * errorGradient нейрона на другом конце соединения
- переходите к шагу 3
, сгенерируйте градиент ошибки
, который равен выход * (1-выход) * ошибка
. - перейти к шагу 4 текущему смещению + LEARNING_RATE * errorGradient
. Затем настройте вес каждого обратного соединения так, чтобы он равнялся текущему весу + LEARNING_RATE * выходу нейрона на другом конце соединения * errorGradient этого нейрона
Я тренирую свою нейронную сеть в режиме онлайн, поэтому это выполняется после каждой обучающей выборки.
Код
Это основной код, который запускает нейронную сеть:
private void simulate(double maximumError) {
int errorRepeatCount = 0;
double prevError = 0;
double error; // summed squares of errors
int trialCount = 0;
do {
error = 0;
// loop through each training set
for(int index = 0; index < Parameters.INPUT_TRAINING_SET.length; index++) {
double[] currentInput = Parameters.INPUT_TRAINING_SET[index];
double[] expectedOutput = Parameters.OUTPUT_TRAINING_SET[index];
double[] output = getOutput(currentInput);
train(expectedOutput);
// Subtracts the expected and actual outputs, gets the average of those outputs, and then squares it.
error += Math.pow(getAverage(subtractArray(output, expectedOutput)), 2);
}
} while(error > maximumError);
Теперь функция train ()
:
public void train(double[] expected) {
layers.outputLayer().calculateErrors(expected);
for(int i = Parameters.NUM_HIDDEN_LAYERS; i >= 0; i--) {
layers.allLayers[i].calculateErrors();
}
}
Функция выходного слоя calculateErrors ()
:
public void calculateErrors(double[] expectedOutput) {
for(int i = 0; i < numNeurons; i++) {
Neuron neuron = neurons[i];
double error = expectedOutput[i] - neuron.getOutput();
neuron.train(error);
}
}
Нормальный (скрытый и входной) слой calculateErrors ()
функция:
public void calculateErrors() {
for(int i = 0; i < neurons.length; i++) {
Neuron neuron = neurons[i];
double error = 0;
for(Connection connection : neuron.forwardConnections) {
error += connection.output.errorGradient * connection.weight;
}
neuron.train(error);
}
}
Полный класс нейрона:
package neuralNet.layers.neurons;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import neuralNet.Parameters;
import neuralNet.layers.NeuronLayer;
public class Neuron {
private double output, bias;
public List<Connection> forwardConnections = new ArrayList<Connection>(); // Forward = layer closer to input -> layer closer to output
public List<Connection> backwardConnections = new ArrayList<Connection>(); // Backward = layer closer to output -> layer closer to input
public double errorGradient;
public Neuron() {
Random random = new Random();
bias = random.nextDouble() - 0.5;
}
public void addConnections(NeuronLayer prevLayer) {
// This is true for input layers. They create their connections differently. (See InputLayer class)
if(prevLayer == null) return;
for(Neuron neuron : prevLayer.neurons) {
Connection.createConnection(neuron, this);
}
}
public void calcOutput() {
output = bias;
for(Connection connection : backwardConnections) {
connection.input.calcOutput();
output += connection.input.getOutput() * connection.weight;
}
output = sigmoid(output);
}
private double sigmoid(double output) {
return 1 / (1 + Math.exp(-1*output));
}
public double getOutput() {
return output;
}
public void train(double error) {
this.errorGradient = output * (1-output) * error;
bias += Parameters.LEARNING_RATE * errorGradient;
for(Connection connection : backwardConnections) {
// for clarification: connection.input refers to a neuron that outputs to this neuron
connection.weight += Parameters.LEARNING_RATE * connection.input.getOutput() * errorGradient;
}
}
}
Результаты
Когда я тренируюсь для И, ИЛИ или ИЛИ, сеть может обычно сходятся в течение примерно 1000 эпох, однако, когда я тренируюсь с XOR, выходы становятся фиксированными и никогда не сходятся. Итак, что я делаю не так? Есть идеи?
Править
Следуя совету других, я начал заново и реализовал свою нейронную сеть без классов ... и она работает. Я все еще не уверен, где моя проблема в приведенном выше коде, но она где-то там.