public class Tester
{
public static void main(String[] args)
{
double[][] inputs = {
{0.547361, -2.04845, -2.71647},
{1.9708, -1.16141, -0.0485735},
{-3.18799, 2.97068, 0.26499},
{-0.498425, -1.04703, 1.61744}
};
double[][] outputs = {
{1.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 1.0}
};
Example[] examples = new Example[inputs.length];
for (int i = 0; i < examples.length; i++)
examples[i] = new Example(inputs[i], outputs[i]);
int[] hiddenLayers = {4,4}; // sizes of hidden layers
Network net = new Network();
net.backPropLearning(examples, hiddenLayers, 10000, 1.0);
}
}
public class Network
{
private Layer[] layers;
static double reportMultiplier = -1.0;
private void setLayer (Layer layer, int i)
{
layers[i] = layer;
}
private void createLayers (int numInputs, int numOutputs, int[] hiddenLayers)
{
layers = new Layer[hiddenLayers.length + 2];
Layer inputLayer = new Layer(numInputs);
setLayer(inputLayer, 0);
for (int i = 0; i < hiddenLayers.length; i++)
setLayer(new Layer(layers[i], hiddenLayers[i]), i + 1);
Layer outputLayer = new Layer(layers[hiddenLayers.length], numOutputs);
setLayer(outputLayer, layers.length - 1);
for (int i = 0; i < layers.length; i++)
layers[i].createNeurons();
}
private double activation (double x)
{
return 1.0 / (1.0 + Math.exp(-x)); // sigmoid
}
private double activationDerivative (double x)
{
double exp = Math.exp(x);
return exp / Math.pow((exp + 1.0), 2); // d/dx sigmoid
}
private void printReport (int n, Example[] examples)
{
String s = "";
double errorSum = 0.0;
int m = layers.length - 1;
for (int i = 0; i < layers[m].getSize(); i++)
for (int j = 0; j < examples.length; j++)
errorSum += Math.abs(examples[j].getOutput(i) - (layers[m].getNeuron(i)).getOutput());
s = "n:" + n;
while (s.length() < 10)
s += " ";
s += "error:" + (float) errorSum;
while (s.length() < 30)
s += " ";
System.out.print(s);
if (reportMultiplier < 0)
reportMultiplier = 50 / errorSum;
for (int i = 0; i < errorSum * reportMultiplier; i++)
System.out.print("|");
System.out.println();
}
public void backPropLearning (Example[] examples, int[] hiddenLayers, int steps, double learningFactor)
{
createLayers(examples[0].numInputs(), examples[0].numOutputs(), hiddenLayers);
int n = 0;
int numPlots = 50;
int plotStep = Math.max(1, steps / numPlots);
while (n < steps) {
for (int e = 0; e < examples.length; e++) {
Example example = examples[e];
// Propagate the inputs forward to compute the output
for (int i = 0; i < layers[0].getSize(); i++)
layers[0].getNeuron(i).setOutput(example.getInput(i));
for (int i = 1; i < layers.length; i++) {
for (int j = 0; j < layers[i].getSize(); j++) {
Neuron nj = layers[i].getNeuron(j);
double in = 0.0;
for (int k = 0; k < nj.numInputs(); k++) {
Axon akj = nj.getInput(k);
Neuron nk = akj.getStart();
in += akj.getWeight() * nk.getOutput();
}
nj.setInput(in);
nj.setOutput(activation(in));
}
}
// Propagate deltas backward from output to input layer
int m = layers.length - 1;
for (int i = 0; i < layers[m].getSize(); i++) {
Neuron ni = layers[m].getNeuron(i);
double errori = example.getOutput(i) - ni.getOutput();
ni.setDelta(activationDerivative(ni.getInput()) * errori);
}
for (int i = m - 1; i >= 0; i--) {
for (int j = 0; j < layers[i].getSize(); j++) {
Neuron nj = layers[i].getNeuron(j);
double delta = 0.0;
for (int k = 0; k < nj.numOutputs(); k++) {
Axon ajk = nj.getOutput(k);
Neuron nk = ajk.getEnd();
delta += ajk.getWeight() * nk.getDelta();
}
delta *= activationDerivative(nj.getInput());
nj.setDelta(delta);
}
}
// Update every weight in network using deltas
for (int i = 0; i < layers.length - 1; i++) {
for (int j = 0; j < layers[i].getSize(); j++) {
Neuron nj = layers[i].getNeuron(j);
for (int k = 0; k < nj.numOutputs(); k++) {
Axon ajk = nj.getOutput(k);
Neuron nk = ajk.getEnd();
ajk.modifyWeight(learningFactor * nk.getDelta() * nj.getOutput());
ajk.modifyWeight(learningFactor * nj.getDelta() * nk.getOutput());
}
}
}
}
if (n % plotStep == 0)
printReport(n, examples);
n++;
}
printReport(n, examples);
}
}
public class Layer
{
private Neuron[] neurons;
private Layer previous;
private Layer next;
public Layer (int n)
{
neurons = new Neuron[n];
}
public Layer (Layer l, int n)
{
this(n);
previous = l;
previous.next = this;
}
public void createNeurons ()
{
int n = previous == null ? 0 : previous.neurons.length;
int m = next == null ? 0 : next.neurons.length;
for (int i = 0; i < neurons.length; i++) {
neurons[i] = new Neuron(n, m);
for (int j = 0; j < n; j++)
neurons[i].addInput(previous.neurons[j]);
}
}
public int getSize ()
{
return neurons.length;
}
public Neuron getNeuron (int i)
{
return neurons[i];
}
}
public class Neuron
{
private Axon[] inputs;
private Axon[] outputs;
private double input;
private double output;
private double delta;
public Neuron () // dummy constructor
{
inputs = new Axon[0];
outputs = new Axon[1];
input = 1.0;
output = 1.0;
delta = 0.0;
}
public Neuron (int n, int m)
{
this();
n += 1; // add the dummy input
inputs = new Axon[n];
outputs = new Axon[m];
Neuron dummy = new Neuron();
addInput(dummy);
}
private void append (Axon[] arr, Axon a)
{
int i = 0;
while (i < arr.length && arr[i] != null)
i++;
if (i < arr.length)
arr[i] = a;
}
private void addOutput (Axon a)
{
append(outputs, a);
}
public void addInput (Neuron n)
{
Axon a = new Axon(n, this);
append(inputs, a);
n.addOutput(a);
}
public double getInput ()
{
return input;
}
public void setInput (double in)
{
input = in;
}
public double getOutput ()
{
return output;
}
public void setOutput (double out)
{
output = out;
}
public double getDelta ()
{
return delta;
}
public void setDelta (double newDelta)
{
delta = newDelta;
}
public int numInputs ()
{
return inputs.length;
}
public int numOutputs ()
{
return outputs.length;
}
public Axon getInput (int i)
{
return inputs[i];
}
public Axon getOutput (int i)
{
return outputs[i];
}
}
public class Axon
{
private double weight;
private Neuron start;
private Neuron end;
public Axon (Neuron s, Neuron e)
{
start = s;
end = e;
weight = 2.0 * Math.random() - 1.0; // random initial weight between -1 and 1
}
public Neuron getStart ()
{
return start;
}
public Neuron getEnd ()
{
return end;
}
public double getWeight ()
{
return weight;
}
public void modifyWeight (double x)
{
weight += x;
}
}
public class Example
{
double[] inputs;
double[] outputs;
public Example (double[] in, double[] out)
{
inputs = in;
outputs = out;
}
public double getInput (int i)
{
return inputs[i];
}
public double getOutput (int i)
{
return outputs[i];
}
public int numInputs ()
{
return inputs.length;
}
public int numOutputs ()
{
return outputs.length;
}
}