EDIT2:
New training kit ...
Inputs
[ [0.0, 0.0], [0.0, 1.0], [0.0, 2.0], [0.0, 3.0], [0.0, 4.0], [1.0, 0.0], [1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0], [2.0, 0.0], [2.0, 1.0], [2.0, 2.0], [2.0, 3.0], [2.0, 4.0], [3.0, 0.0], [3.0, 1.0], [3.0, 2.0], [3.0, 3.0], [3.0, 4.0], [4.0, 0.0], [4.0, 1.0], [4.0, 2.0], [4.0, 3.0], [4.0, 4.0] ]
Outputs:
[ [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [1.0], [1.0], [0.0], [0.0], [0.0], [1.0], [1.0] ]
EDIT1:
I updated the question with my latest code. I fixed a few minor issues, but still get the same result for all input combinations after the network found out.
Below is the backprop algorithm : Backprop algorithm
Yes, it is homework to make it clear at the very beginning.
I suppose to implement a simple backpropagation algorithm on a simple neural network.
I chose Python as the language of choice for this task, and I selected the neural network as follows:
3 layers: 1 input, 1 hidden, 1 output layer:
OO O OO
On the output neuron there is an integer on the neutrons inptut and 1 or 0.
Here is my whole implementation (a bit long). Bellow it I will select only shorter matching fragments where, I think, the error can be found at:
import os import math import Image import random from random import sample #------------------------------ class definitions class Weight: def __init__(self, fromNeuron, toNeuron): self.value = random.uniform(-0.5, 0.5) self.fromNeuron = fromNeuron self.toNeuron = toNeuron fromNeuron.outputWeights.append(self) toNeuron.inputWeights.append(self) self.delta = 0.0 # delta value, this will accumulate and after each training cycle used to adjust the weight value def calculateDelta(self, network): self.delta += self.fromNeuron.value * self.toNeuron.error class Neuron: def __init__(self): self.value = 0.0 # the output self.idealValue = 0.0 # the ideal output self.error = 0.0 # error between output and ideal output self.inputWeights = [] self.outputWeights = [] def activate(self, network): x = 0.0; for weight in self.inputWeights: x += weight.value * weight.fromNeuron.value # sigmoid function if x < -320: self.value = 0 elif x > 320: self.value = 1 else: self.value = 1 / (1 + math.exp(-x)) class Layer: def __init__(self, neurons): self.neurons = neurons def activate(self, network): for neuron in self.neurons: neuron.activate(network) class Network: def __init__(self, layers, learningRate): self.layers = layers self.learningRate = learningRate # the rate at which the network learns self.weights = [] for hiddenNeuron in self.layers[1].neurons: for inputNeuron in self.layers[0].neurons: self.weights.append(Weight(inputNeuron, hiddenNeuron)) for outputNeuron in self.layers[2].neurons: self.weights.append(Weight(hiddenNeuron, outputNeuron)) def setInputs(self, inputs): self.layers[0].neurons[0].value = float(inputs[0]) self.layers[0].neurons[1].value = float(inputs[1]) def setExpectedOutputs(self, expectedOutputs): self.layers[2].neurons[0].idealValue = expectedOutputs[0] def calculateOutputs(self, expectedOutputs): self.setExpectedOutputs(expectedOutputs) self.layers[1].activate(self) # activation function for hidden layer self.layers[2].activate(self) # activation function for output layer def calculateOutputErrors(self): for neuron in self.layers[2].neurons: neuron.error = (neuron.idealValue - neuron.value) * neuron.value * (1 - neuron.value) def calculateHiddenErrors(self): for neuron in self.layers[1].neurons: error = 0.0 for weight in neuron.outputWeights: error += weight.toNeuron.error * weight.value neuron.error = error * neuron.value * (1 - neuron.value) def calculateDeltas(self): for weight in self.weights: weight.calculateDelta(self) def train(self, inputs, expectedOutputs): self.setInputs(inputs) self.calculateOutputs(expectedOutputs) self.calculateOutputErrors() self.calculateHiddenErrors() self.calculateDeltas() def learn(self): for weight in self.weights: weight.value += self.learningRate * weight.delta def calculateSingleOutput(self, inputs): self.setInputs(inputs) self.layers[1].activate(self) self.layers[2].activate(self) #return round(self.layers[2].neurons[0].value, 0) return self.layers[2].neurons[0].value #------------------------------ initialize objects etc inputLayer = Layer([Neuron() for n in range(2)]) hiddenLayer = Layer([Neuron() for n in range(100)]) outputLayer = Layer([Neuron() for n in range(1)]) learningRate = 0.5 network = Network([inputLayer, hiddenLayer, outputLayer], learningRate) # just for debugging, the real training set is much larger trainingInputs = [ [0.0, 0.0], [1.0, 0.0], [2.0, 0.0], [0.0, 1.0], [1.0, 1.0], [2.0, 1.0], [0.0, 2.0], [1.0, 2.0], [2.0, 2.0] ] trainingOutputs = [ [0.0], [1.0], [1.0], [0.0], [1.0], [0.0], [0.0], [0.0], [1.0] ] #------------------------------ let train for i in range(500): for j in range(len(trainingOutputs)): network.train(trainingInputs[j], trainingOutputs[j]) network.learn() #------------------------------ let check for pattern in trainingInputs: print network.calculateSingleOutput(pattern)
Now the problem is that after exploring the network, it seems to return a floating-point number very close to 0.0 for all input combinations, even those that should be close to 1.0.
I train the network in 100 cycles, in each cycle I do:
For each set of inputs in the training set:
- Set network inputs
- Calculate outputs using sigmoid function
- Calculate errors in output level
- Calculate hidden layer errors
- Calculate weight tacks
Then I adjust the scales based on learning speed and accumulated deltas.
Here is my neuron activation function:
def activationFunction(self, network): """ Calculate an activation function of a neuron which is a sum of all input weights * neurons where those weights start """ x = 0.0; for weight in self.inputWeights: x += weight.value * weight.getFromNeuron(network).value
This is how I calculate deltas:
def calculateDelta(self, network): self.delta += self.getFromNeuron(network).value * self.getToNeuron(network).error
This is the general flow of my algorithm:
for i in range(numberOfIterations): for k,expectedOutput in trainingSet.iteritems(): coordinates = k.split(",") network.setInputs((float(coordinates[0]), float(coordinates[1]))) network.calculateOutputs([float(expectedOutput)]) network.calculateOutputErrors() network.calculateHiddenErrors() network.calculateDeltas() oldWeights = network.weights network.adjustWeights() network.resetDeltas() print "Iteration ", i j = 0 for weight in network.weights: print "Weight W", weight.i, weight.j, ": ", oldWeights[j].value, " ............ Adjusted value : ", weight.value j += j
The last two lines of output:
0.552785449458
It actually returns the exit number for all input combinations.
Did I miss something?