C#: backpropagation learns XOR
Backpropagation computes how the loss changes with each weight by applying the chain rule from the output back to the inputs. For a sigmoid unit with output a, the derivative is a·(1 − a). The output error signal is (a − target)·a·(1 − a), and each hidden unit's signal is its share of the output signal, passed back through the weight and its own derivative.
C#
using System;
using System.Globalization;
class Program {
static double Sigmoid(double z) {
return 1.0 / (1.0 + Math.Exp(-z));
}
static void Main() {
double[,] inputs = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } };
double[] targets = { 0, 1, 1, 0 };
double[,] w1 = { { 0.5, -0.4 }, { 0.9, 0.7 } };
double[] b1 = { 0.1, -0.3 };
double[] w2 = { 0.8, -0.6 };
double b2 = 0.2;
double rate = 0.5;
for (int epoch = 0; epoch < 20000; epoch++) {
for (int n = 0; n < 4; n++) {
double[] h = new double[2];
for (int j = 0; j < 2; j++) {
h[j] = Sigmoid(inputs[n, 0] * w1[0, j] + inputs[n, 1] * w1[1, j] + b1[j]);
}
double o = Sigmoid(h[0] * w2[0] + h[1] * w2[1] + b2);
double dOut = (o - targets[n]) * o * (1 - o);
for (int j = 0; j < 2; j++) {
double dHidden = dOut * w2[j] * h[j] * (1 - h[j]);
w2[j] -= rate * dOut * h[j];
w1[0, j] -= rate * dHidden * inputs[n, 0];
w1[1, j] -= rate * dHidden * inputs[n, 1];
b1[j] -= rate * dHidden;
}
b2 -= rate * dOut;
}
}
for (int n = 0; n < 4; n++) {
double[] h = new double[2];
for (int j = 0; j < 2; j++) {
h[j] = Sigmoid(inputs[n, 0] * w1[0, j] + inputs[n, 1] * w1[1, j] + b1[j]);
}
double o = Sigmoid(h[0] * w2[0] + h[1] * w2[1] + b2);
Console.WriteLine(inputs[n, 0] + " XOR " + inputs[n, 1] + " -> " + Math.Round(o) + " (" + o.ToString("F3", CultureInfo.InvariantCulture) + ")");
}
}
}