Обзор
Итак, я пытаюсь понять механизм нейронных сетей. Я все еще не полностью понимаю математику за ней, но я думаю, что понимаю, как ее реализовать. В настоящее время у меня есть нейронная сеть, которая может изучать шаблоны обучения AND, OR и NOR. Однако я не могу заставить его реализовать шаблон XOR. Моя нейронная сеть вперед состоит из 2 входов, 3 скрытых и 1 вывода. Весы и предубеждения произвольно устанавливаются между -0,5 и 0,5, а выходы генерируются с помощью сигмоидальной функции активации
Алгоритм
До сих пор я предполагаю, что допустил ошибку в моем алгоритме обучения, который описан ниже:
- Для каждого нейрона в выходном слое укажите
error
значениеdesiredOutput - actualOutput
--go на шаг 3 - Для каждого нейрона в скрытом или входном слое (работа назад) укажите значение
error
, которое является суммой всехforward connection weights * the errorGradient of the neuron at the other end of the connection
--go до шага 3 - Для каждого нейрона, используя предоставленное значение
error
, создайтеerror gradient
, равныйoutput * (1-output) * error
. - перейти к шагу 4 - Для каждого нейрона отрегулируйте смещение равным
current bias + LEARNING_RATE * errorGradient
. Затем настройте каждый вес обратного соединения равнымcurrent weight + LEARNING_RATE * output of neuron at other end of connection * this neuron 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;
}
}
}
Результаты
Когда я тренируюсь для AND, OR или NOR, сеть обычно может сходиться в течение примерно 1000 эпох, однако, когда я тренируюсь с XOR, выходы фиксируются и никогда не сходятся. Итак, что я делаю неправильно? Любые идеи?
Edit
Следуя советам других, я начал и реализовал свою нейронную сеть без классов... и это работает. Я все еще не уверен, где моя проблема лежит в приведенном выше коде, но там где-то там.