Skip to content

Instantly share code, notes, and snippets.

@FlorianCassayre
Last active July 4, 2017 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FlorianCassayre/3f4e6f5c1497a2e64f3ac6493d97daba to your computer and use it in GitHub Desktop.
Save FlorianCassayre/3f4e6f5c1497a2e64f3ac6493d97daba to your computer and use it in GitHub Desktop.
final int HIDDEN_LAYERS = 1;
final int INPUTS = 4 * 2;
final int OUTPUTS = 4;
final int NEURONS_PER_LAYER = 16;
final float LEARNING_RATE = 0.1;
final float[][] neurons = new float[2 + HIDDEN_LAYERS][]; // [layer][neuron]
final float[][][] axons = new float[1 + HIDDEN_LAYERS][][]; // [layer][previous_neuron][next_neuron]
final int MARGIN = 15, NEURON_DIAMETER = 50, AXON_LENGTH = 200;
final int INITIAL_ITERATIONS = 1000000;
int iterations = 0;
float error;
int exp;
int actual;
void setup()
{
neurons[0] = new float[INPUTS];
for(int i = 1; i < neurons.length; i++)
neurons[i] = new float[NEURONS_PER_LAYER];
neurons[neurons.length - 1] = new float[OUTPUTS];
axons[0] = new float[INPUTS][NEURONS_PER_LAYER];
for(int i = 1; i < axons.length - 1; i++)
axons[i] = new float[NEURONS_PER_LAYER][NEURONS_PER_LAYER];
axons[axons.length - 1] = new float[NEURONS_PER_LAYER][OUTPUTS];
surface.setSize(
2 * MARGIN + neurons.length * NEURON_DIAMETER + axons.length * AXON_LENGTH,
2 * MARGIN + max(max(INPUTS, OUTPUTS), NEURONS_PER_LAYER) * (MARGIN + NEURON_DIAMETER) - MARGIN
);
for(int i = 0; i < axons.length; i++)
{
for(int j = 0; j < axons[i].length; j++)
{
for(int k = 0; k < axons[i][j].length; k++)
{
axons[i][j][k] = random(-1, 1);
}
}
}
for(int i = 0; i < INPUTS; i++) // 0 and 1 representing the bits
neurons[0][i] = random(0, 1);
computeResult();
frameRate(60);
for(int i = 0; i < INITIAL_ITERATIONS; i++) // Initial training
update();
}
void draw()
{
background(255);
for(int layer = 0; layer < axons.length; layer++)
{
for(int neuron = 0; neuron < neurons[layer].length; neuron++)
{
final float x = getNeuronX(layer), y = getNeuronY(layer, neuron);
for(int axon = 0; axon < axons[layer][neuron].length; axon++)
{
final float x1 = x + NEURON_DIAMETER + AXON_LENGTH;
final float y1 = getNeuronY(layer + 1, axon);
strokeWeight(3.0);
stroke(getColor(sigmoid(axons[layer][neuron][axon]) * 2 - 1));
line(x, y, x1, y1);
}
}
}
for(int layer = 0; layer < neurons.length; layer++) // No other way to iterate again in order to avoid overlapping
{
for(int neuron = 0; neuron < neurons[layer].length; neuron++)
{
final float x = getNeuronX(layer), y = getNeuronY(layer, neuron);
final float value = neurons[layer][neuron];
strokeWeight(3.0);
stroke(0);
fill(getColor(value * 2 - 1));
ellipse(x, y, NEURON_DIAMETER, NEURON_DIAMETER);
fill(255);
textAlign(CENTER, CENTER);
textSize(15.0);
text(numberFormat(value), x - NEURON_DIAMETER / 2.0, y - NEURON_DIAMETER / 2.0, NEURON_DIAMETER, NEURON_DIAMETER);
}
}
fill(0);
textAlign(LEFT);
text(iterations + " iterations", 3, 15);
if(exp == actual)
fill(0, 255, 0);
else
fill(255, 0, 0);
text(((exp >> 3) & 1) + "" + ((exp >> 2) & 1) + "" + ((exp >> 1) & 1) + "" + (exp & 1), 3, 30);
}
void keyPressed()
{
update();
}
void mouseClicked()
{
update();
}
void update()
{
for(int i = 0; i < INPUTS; i++)
neurons[0][i] = random(0, 1) >= 0.5 ? 1.0 : 0.0;
computeResult();
backpropagate();
}
void computeResult()
{
for(int i = 1; i < neurons.length; i++)
{
for(int j = 0; j < neurons[i].length; j++)
{
float sum = 0;
for(int k = 0; k < neurons[i - 1].length; k++)
{
sum += neurons[i - 1][k] * axons[i - 1][k][j];
}
neurons[i][j] = sigmoid(sum);
}
}
}
int input(int i)
{
return round(neurons[0][i]);
}
int output(int i)
{
return round(neurons[neurons.length - 1][i]);
}
void backpropagate()
{
final int i1 = (input(4) << 3) | (input(5) << 2) | (input(6) << 1) | input(7), i2 = (input(0) << 3) | (input(1) << 2) | (input(2) << 1) | input(3);
exp = (i1 + i2) & 0xf;
final float[] expected = {(exp >> 3) & 1, (exp >> 2) & 1, (exp >> 1) & 1, exp & 1};
actual = (output(0) << 3) | (output(1) << 2) | (output(2) << 1) | output(3);
final float[][] errors = new float[neurons.length - 1][];
for(int i = 0; i < errors.length - 1; i++)
errors[i] = new float[NEURONS_PER_LAYER];
errors[errors.length - 1] = new float[OUTPUTS];
for(int axon = 0; axon < OUTPUTS; axon++)
{
final float neuronError = (neurons[neurons.length - 1][axon] - expected[axon]) * neurons[neurons.length - 1][axon] * (1 - neurons[neurons.length - 1][axon]);
errors[errors.length - 1][axon] = neuronError;
for(int neuron = 0; neuron < neurons[neurons.length - 2].length; neuron++)
{
axons[axons.length - 1][neuron][axon] -= neuronError * neurons[neurons.length - 2][neuron] * LEARNING_RATE;
}
}
for(int layer = errors.length - 2; layer >= 0; layer--)
{
for(int neuron = 0; neuron < NEURONS_PER_LAYER; neuron++)
{
float d = 0;
for(int axon = 0; axon < axons[layer + 1][neuron].length; axon++)
{
d += errors[layer + 1][axon] * axons[layer + 1][neuron][axon];
}
d *= neurons[layer + 1][neuron] * (1 - neurons[layer + 1][neuron]);
errors[layer][neuron] = d;
for(int axon = 0; axon < neurons[layer].length; axon++)
{
axons[layer][axon][neuron] -= d * neurons[layer][axon] * LEARNING_RATE;
}
}
}
error = abs(expected[0] - neurons[2][0]);
iterations++;
}
String numberFormat(float f)
{
return nf(f, 1, 2).replace(",", ".");
}
float sigmoid(float x)
{
return 1.0 / (1.0 + exp(-x));
}
color getColor(float x)
{
return color(255 * (1 + x) / 1.5, 0, 255 * (1 - x) / 1.5); // Blue => -1.0, Red => 1.0 (overflow allowed)
}
float getNeuronX(int layer)
{
return MARGIN + NEURON_DIAMETER / 2.0 + layer * (AXON_LENGTH + NEURON_DIAMETER);
}
float getNeuronY(int layer, int neuron)
{
final float border = (height - neurons[layer].length * (NEURON_DIAMETER + MARGIN) - MARGIN) / 2.0;
return border + MARGIN + NEURON_DIAMETER / 2.0 + (NEURON_DIAMETER + MARGIN) * neuron;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment