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) + ")");
    }
  }
}
Read the full lesson