Computing Calibration Error for a C# Neural Network Binary Classifier

If you have a neural network binary classifier, the output prediction is a number between 0 and 1 where a value less than 0.5 means the prediction is class 0 (whatever that might be) and a value greater than 0.5 means the prediction is class 1.

Suppose you are trying to predict if a person is male (class 0) or female (class 1). If the output pseudo-probability is 0.51, the prediction is (just barely) class 1 = female. If the output pseudo-probability is 0.94, the prediction is still just class 1 = female. But in the pp = 0.94 case, you hope that the likelihood of being correct is much greater than the likelihood of being correct when pp = 0.51.

Calibration error is a metric that measures how well pseudo-probability outputs match the actual frequency of being correct. Briefly, you construct 10 bins of output pseudo-probabilities. For each bin, you track how many data items fall in the bin, the average of the pseudo-probabilities in each bin, and the accuracy for each bin. Calibration error is the weighted sum of the absolute value differences between pseudo-probabilities and accuracy.

I was sitting on a two-hour flight from Las Vegas to Seattle and figured I’d make use of my time by implementing a calibration error function for my standard C# neural network binary classifier code.

For my demo, I used synthetic data that looks like:

1, 0.24, 1, 0, 0, 0.2950, 0, 0, 1
0, 0.39, 0, 0, 1, 0.5120, 0, 1, 0
1, 0.63, 0, 1, 0, 0.7580, 1, 0, 0
0, 0.36, 1, 0, 0, 0.4450, 0, 1, 0
. . .

Each line represents a person. The fields are sex (male = 0, female = 1), age (divided by 100), State (Michigan = 100, Nebraska = 010, Oklahoma = 001), income (divided by 100,000), political leaning (conservative = 100, moderate = 010, liberal = 001). The goal is to predict sex from age, State, income, and political leaning.

There are just 200 training items and 40 test items, which is barely enough for a useable neural network.

For my experiment, I decided to implement two different calibration error functions — one that considers only correct predictions, the second that considers all predictions (correct and wrong).

The output of my demo is:

Neural network binary classification using C#
Predict sex from age, State, income, political leaning

First three X data:
 0.2400   1.0000  0.0000  0.0000  0.2950   0.0000  0.0000  1.0000
 0.3900   0.0000  0.0000  1.0000  0.5120   0.0000  1.0000  0.0000
 0.6300   0.0000  1.0000  0.0000  0.7580   1.0000  0.0000  0.0000

First three target Y:
1.0
0.0
1.0

Creating 8-10-1 tanh() sigmoid() neural network

Setting maxEpochs = 400
Setting lrnRate = 0.010
Setting batSize = 10

Starting training
epoch:    0  MBCCE = 0.6922  acc = 0.5050
epoch:   80  MBCCE = 0.6732  acc = 0.6000
epoch:  160  MBCCE = 0.6006  acc = 0.7000
epoch:  240  MBCCE = 0.4977  acc = 0.7900
epoch:  320  MBCCE = 0.4058  acc = 0.8250
Done

Accuracy on train data = 0.8900
Accuracy on test data  = 0.7500

Calibration error training data, correct predictions
Calibration bin counts:
  43  17   8   6   6   7   6  28  14  43
0.1360

Calibration error test data, correct predictions
Calibration bin counts:
  10   3   0   2   2   0   0   3   0  10
0.0874

Calibration error training data, all predictions
Calibration bin counts:
  43  17  10   8  10  12  10  33  14  43
0.3221

Calibration error test data, all predictions
Calibration bin counts:
  10   3   0   2   3   4   0   4   2  12
0.5388

Predicting sex (0 = M, 1 = F) for 30 Oklahoma $40,000 moderate
Predicted gender = 0.1626

End demo

As a very general rule of thumb, calibration error less than 0.20 is good, between 0.20 and 0.40 is so-so, and greater than 0.40 is weak. In the demo, the calibration error values are quite good, except when taking a predictions into account, for the 40-tem test data, the calibration error is 0.5388. The poor calibration is almost certainly because the test dataset is so small and a few incorrect predictions can have a big impact o the calibration error.

An interesting way to kill some time on an Alaska Airlines flight.



I’ve always been fascinated by electro-mechanical arcade games. On summer when I was a teenager, I worked at a miniature golf course and arcade in Anaheim, California, on La Palma Ave (I think — that was a long time ago). One of my jobs was to maintain the various game machines in the arcade. The machines needed daily calibration, but doing that each morning before the arcade opened was a relatively fun part of my job.

Left: “Southland Little Pro” (1964) is a golf game that was at the arcade I worked at. The inner mechanisms were super interesting. It was a fairly reliable machine and it didn’t give me too many problems.

Center: This is “Williams Hollywood Driving Range” (1965).

Right: “Bromley Little Pro” (1991) was an updated Southland Little Pro with added miniature golf like obstacles. Notice all three machines used the same little man. It was common for arcade game manufacturers to sell parts to each other.


Demo code. Replace “lt” (less than), “gt”, “lte”, “gte”, “and” with Boolean operator symbols. (My lame blog editor consistently chokes on symbols).

using System;
using System.IO;
using System.Collections.Generic;

namespace NeuralNetworkBinaryCalibration
{
  internal class NeuralNetworkBinaryCalibrationProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("\nNeural network " +
        "binary classification using C# ");

      Console.WriteLine("Predict sex from age," +
        " State, income, political leaning ");

      string trainFile =
        "..\\..\\..\\Data\\people_train.txt";
      // sex, age,  State,  income,  politics
      //  1   0.24  1 0 0   0.2950   0 0 1
      //  0   0.39  0 0 1   0.5120   0 1 0

      double[][] trainX = Utils.MatLoad(trainFile,
        new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }, ',', "#");
      double[] trainY =
        Utils.MatToVec(Utils.MatLoad(trainFile,
        new int[] { 0 }, ',', "#"));

      string testFile =
        "..\\..\\..\\Data\\people_test.txt";
      double[][] testX = Utils.MatLoad(testFile,
        new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }, ',', "#");
      double[] testY =
        Utils.MatToVec(Utils.MatLoad(testFile,
        new int[] { 0 }, ',', "#"));

      Console.WriteLine("\nFirst three X data: ");
      for (int i = 0; i "lt" 3; ++i)
        Utils.VecShow(trainX[i], 4, 9, true);

      Console.WriteLine("\nFirst three target Y: ");
      for (int i = 0; i "lt" 3; ++i)
        Console.WriteLine(trainY[i].ToString("F1"));

      Console.WriteLine("\nCreating 8-10-1 tanh()" +
        " sigmoid() neural network ");
      NeuralNetwork nn =
        new NeuralNetwork(8, 100, 1, seed: 0);

      int maxEpochs = 400;
      double lrnRate = 0.01;  // if divide grads by batSize
      int batSize = 10;

      Console.WriteLine("\nSetting maxEpochs = " +
        maxEpochs);
      Console.WriteLine("Setting lrnRate = " +
        lrnRate.ToString("F3"));
      Console.WriteLine("Setting batSize = " + batSize);

      // ------------------------------------------------------

      Console.WriteLine("\nStarting training ");
      nn.Train(trainX, trainY, lrnRate, batSize, maxEpochs);
      Console.WriteLine("Done ");

      double trainAcc = nn.Accuracy(trainX, trainY);
      Console.WriteLine("\nAccuracy on train data = " +
        trainAcc.ToString("F4"));

      double testAcc = nn.Accuracy(testX, testY);
      Console.WriteLine("Accuracy on test data  = " +
        testAcc.ToString("F4"));

      //Console.WriteLine("\nConfusion matrix: ");
      //int[][] cm = nn.ConfusionMatrix(testX, testY);
      //nn.ShowConfusion(cm);

      Console.WriteLine("\nCalibration error training data," +
        " correct predictions ");
      double calErrTrainCorrects = 
        nn.CalibrationErrorCorrects(trainX, trainY);
      Console.WriteLine(calErrTrainCorrects.ToString("F4"));

      Console.WriteLine("\nCalibration error test data," +
        " correct predictions ");
      double calErrTestCorrects =
        nn.CalibrationErrorCorrects(testX, testY);
      Console.WriteLine(calErrTestCorrects.ToString("F4"));

      Console.WriteLine("\nCalibration error training data," +
        " all predictions ");
      double calErrTrainAll =
        nn.CalibrationErrorAll(trainX, trainY);
      Console.WriteLine(calErrTrainAll.ToString("F4"));

      Console.WriteLine("\nCalibration error test data," +
        " all predictions ");
      double calErrTestAll =
        nn.CalibrationErrorAll(testX, testY);
      Console.WriteLine(calErrTestAll.ToString("F4"));

      Console.WriteLine("\nPredicting sex (0 = male, " +
        "1 = female) for 30 Oklahoma $40,000 moderate ");
      double[] X =
        new double[] { 0.30, 0, 0, 1, 0.40000, 0, 1, 0 };
      double y = nn.ComputeOutput(X);
      Console.WriteLine("Predicted gender = " +
        y.ToString("F4"));

      Console.WriteLine("\nEnd demo ");
      Console.ReadLine();
    } // Main

  } // Program

  public class NeuralNetwork
  {
    private int ni; // number input nodes
    private int nh;
    private int no;

    private double[] iNodes;
    private double[][] ihWeights; // input-hidden
    private double[] hBiases;
    private double[] hNodes;

    private double[][] hoWeights; // hidden-output
    private double[] oBiases;
    private double[] oNodes;  // single val as array

    // gradients
    private double[][] ihGrads;
    private double[] hbGrads;
    private double[][] hoGrads;
    private double[] obGrads;

    private Random rnd;

    // --------------------------------------------------------

    public NeuralNetwork(int numIn, int numHid,
      int numOut, int seed)
    {
      this.ni = numIn;
      this.nh = numHid;
      this.no = numOut;  // 1 for binary classification

      this.iNodes = new double[numIn];

      this.ihWeights = Utils.MatCreate(numIn, numHid);
      this.hBiases = new double[numHid];
      this.hNodes = new double[numHid];

      this.hoWeights = Utils.MatCreate(numHid, numOut);
      this.oBiases = new double[numOut];  // [1]
      this.oNodes = new double[numOut];  // [1]

      this.ihGrads = Utils.MatCreate(numIn, numHid);
      this.hbGrads = new double[numHid];
      this.hoGrads = Utils.MatCreate(numHid, numOut);
      this.obGrads = new double[numOut];

      this.rnd = new Random(seed);
      this.InitWeights(); // all weights and biases
    } // ctor

    // --------------------------------------------------------

    private void InitWeights() // helper for ctor
    {
      // weights and biases to small random values
      double lo = -0.10; double hi = +0.10;
      int numWts = (this.ni * this.nh) +
        (this.nh * this.no) + this.nh + this.no;
      double[] initialWeights = new double[numWts];
      for (int i = 0; i "lt" initialWeights.Length; ++i)
        initialWeights[i] =
          (hi - lo) * rnd.NextDouble() + lo;
      this.SetWeights(initialWeights);
    }

    // --------------------------------------------------------

    public void SetWeights(double[] wts)
    {
      // copy serialized weights and biases in wts[] 
      // to ih weights, ih biases, ho weights, ho biases
      int numWts = (this.ni * this.nh) +
        (this.nh * this.no) + this.nh + this.no;
      if (wts.Length != numWts)
        throw new Exception("Bad array in SetWeights");

      int k = 0; // points into wts param

      for (int i = 0; i "lt" this.ni; ++i)
        for (int j = 0; j "lt" this.nh; ++j)
          this.ihWeights[i][j] = wts[k++];
      for (int i = 0; i "lt" this.nh; ++i)
        this.hBiases[i] = wts[k++];
      for (int i = 0; i "lt" this.nh; ++i)
        for (int j = 0; j "lt" this.no; ++j)
          this.hoWeights[i][j] = wts[k++];
      for (int i = 0; i "lt" this.no; ++i)
        this.oBiases[i] = wts[k++];
    }

    // --------------------------------------------------------

    public double[] GetWeights()
    {
      int numWts = (this.ni * this.nh) +
        (this.nh * this.no) + this.nh + this.no;
      double[] result = new double[numWts];
      int k = 0;
      for (int i = 0; i "lt" ihWeights.Length; ++i)
        for (int j = 0; j "lt" this.ihWeights[0].Length; ++j)
          result[k++] = this.ihWeights[i][j];
      for (int i = 0; i "lt" this.hBiases.Length; ++i)
        result[k++] = this.hBiases[i];
      for (int i = 0; i "lt" this.hoWeights.Length; ++i)
        for (int j = 0; j "lt" this.hoWeights[0].Length; ++j)
          result[k++] = this.hoWeights[i][j];
      for (int i = 0; i "lt" this.oBiases.Length; ++i)
        result[k++] = this.oBiases[i];
      return result;
    }

    // --------------------------------------------------------

    public double ComputeOutput(double[] x)
    {
      // aka Predict()
      double[] hSums = new double[this.nh]; // scratch 
      double[] oSums = new double[this.no]; // out sums

      for (int i = 0; i "lt" x.Length; ++i)
        this.iNodes[i] = x[i];
      // note: no need to copy x-values unless
      // you implement a ToString.
      // more efficient to simply use the x[] directly
      // or copy by reference.

      // 1. compute i-h sum of weights * inputs
      for (int j = 0; j "lt" this.nh; ++j)
        for (int i = 0; i "lt" this.ni; ++i)
          hSums[j] += this.iNodes[i] *
            this.ihWeights[i][j]; // note +=

      // 2. add biases to hidden sums
      for (int i = 0; i "lt" this.nh; ++i)
        hSums[i] += this.hBiases[i];

      // 3. apply hidden activation
      for (int i = 0; i "lt" this.nh; ++i)
        this.hNodes[i] = HyperTan(hSums[i]);

      // 4. compute h-o sum of wts * hOutputs
      for (int j = 0; j "lt" this.no; ++j)
        for (int i = 0; i "lt" this.nh; ++i)
          oSums[j] += this.hNodes[i] *
            this.hoWeights[i][j];  // [1]

      // 5. add biases to output sums
      for (int i = 0; i "lt" this.no; ++i)
        oSums[i] += this.oBiases[i];

      // 6. apply output activation
      for (int i = 0; i "lt" this.no; ++i)
        this.oNodes[i] = LogSigmoid(oSums[i]);

      return this.oNodes[0];  // single value
    }

    // --------------------------------------------------------

    private static double HyperTan(double x)
    {
      if (x "lt" -10.0) return -1.0;
      else if (x "gt" 10.0) return 1.0;
      else return Math.Tanh(x);
    }

    // --------------------------------------------------------

    private static double LogSigmoid(double x)
    {
      if (x "lt" -10.0) return 0.0;
      else if (x "gt" 10.0) return 1.0;
      else return 1.0 / (1.0 + Math.Exp(-x));
    }

    // --------------------------------------------------------

    private void ZeroOutGrads()
    {
      for (int i = 0; i "lt" this.ni; ++i)
        for (int j = 0; j "lt" this.nh; ++j)
          this.ihGrads[i][j] = 0.0;

      for (int j = 0; j "lt" this.nh; ++j)
        this.hbGrads[j] = 0.0;

      for (int j = 0; j "lt" this.nh; ++j)
        for (int k = 0; k "lt" this.no; ++k)
          this.hoGrads[j][k] = 0.0;

      for (int k = 0; k "lt" this.no; ++k)
        this.obGrads[k] = 0.0;
    }  // ZeroOutGrads()

    // ------------------------------------------------------

    private void AccumGrads(double y)
    {
      double[] oSignals = new double[this.no];
      double[] hSignals = new double[this.nh];

      // 1. compute output node signals
      for (int k = 0; k "lt" this.no; ++k)
      {
        double derivative = 1.0;  // for LogSig with BCEE
        //double derivative = (1 - oNodes[k]) * oNodes[k];
        // MSE
        oSignals[k] = derivative * (this.oNodes[k] - y);
      }

      // 2. accum hidden-to-output gradients 
      for (int j = 0; j "lt" this.nh; ++j)
        for (int k = 0; k "lt" this.no; ++k)
          hoGrads[j][k] +=
           oSignals[k] * this.hNodes[j];

      // 3. accum output node bias gradients
      for (int k = 0; k "lt" this.no; ++k)
        obGrads[k] +=
         oSignals[k] * 1.0;  // 1.0 dummy 

      // 4. compute hidden node signals
      for (int j = 0; j "lt" this.nh; ++j)
      {
        double sum = 0.0;
        for (int k = 0; k "lt" this.no; ++k)
          sum += oSignals[k] * this.hoWeights[j][k];

        double derivative =
          (1 - this.hNodes[j]) *
          (1 + this.hNodes[j]);  // assumes tanh
        hSignals[j] = derivative * sum;
      }

      // 5. accum input-to-hidden gradients
      for (int i = 0; i "lt" this.ni; ++i)
        for (int j = 0; j "lt" this.nh; ++j)
          this.ihGrads[i][j] +=
            hSignals[j] * this.iNodes[i];

      // 6. accum hidden node bias gradients
      for (int j = 0; j "lt" this.nh; ++j)
        this.hbGrads[j] +=
          hSignals[j] * 1.0;  // 1.0 dummy
    } // AccumGrads

    // ------------------------------------------------------

    private void UpdateWeights(double lrnRate)
    {
      // assumes all gradients computed
      // 1. update input-to-hidden weights
      for (int i = 0; i "lt" this.ni; ++i)
      {
        for (int j = 0; j "lt" this.nh; ++j)
        {
          double delta = -1.0 * lrnRate *
            this.ihGrads[i][j];
          this.ihWeights[i][j] += delta;
        }
      }

      // 2. update hidden node biases
      for (int j = 0; j "lt" this.nh; ++j)
      {
        double delta = -1.0 * lrnRate *
          this.hbGrads[j];
        this.hBiases[j] += delta;
      }

      // 3. update hidden-to-output weights
      for (int j = 0; j "lt" this.nh; ++j)
      {
        for (int k = 0; k "lt" this.no; ++k)
        {
          double delta = -1.0 * lrnRate *
            this.hoGrads[j][k];
          this.hoWeights[j][k] += delta;
        }
      }

      // 4. update output node biases
      for (int k = 0; k "lt" this.no; ++k)
      {
        double delta = -1.0 * lrnRate *
          this.obGrads[k];
        this.oBiases[k] += delta;
      }
    } // UpdateWeights()

    public void Train(double[][] trainX, double[] trainY,
      double lrnRate, int batSize, int maxEpochs)
    {
      // modular, several helpers version
      int n = trainX.Length;  // 200
      int batchesPerEpoch = n / batSize;  // 20
      int freq = maxEpochs / 5;  // to show progress

      int[] indices = new int[n];
      for (int i = 0; i "lt" n; ++i)
        indices[i] = i;

      for (int epoch = 0; epoch "lt" maxEpochs; ++epoch)
      {
        Shuffle(indices);
        int ptr = 0;  // points into indices
        for (int batIdx = 0; batIdx "lt" batchesPerEpoch;
          ++batIdx) // 0, 1, . . 19
        {
          for (int i = 0; i "lt" batSize; ++i) // 0 . . 9
          {
            int ii = indices[ptr++];  // compute output
            double[] x = trainX[ii];
            double y = trainY[ii];
            this.ComputeOutput(x);  // into this.oNoodes
            this.AccumGrads(y);
          }
          this.UpdateWeights(lrnRate);
          this.ZeroOutGrads(); // prep for next batch
        } // batches

        if (epoch % freq == 0)  // progress every few epochs
        {
          double mbcee = this.MeanBCE(trainX, trainY);
          double acc = this.Accuracy(trainX, trainY);

          string s1 = "epoch: " + epoch.ToString().PadLeft(4);
          string s2 = "  MBCCE = " + mbcee.ToString("F4");
          string s3 = "  acc = " + acc.ToString("F4");
          Console.WriteLine(s1 + s2 + s3);
        }
      } // epoch
    } // Train()

    // --------------------------------------------------------

    private void Shuffle(int[] sequence)
    {
      for (int i = 0; i "lt" sequence.Length; ++i)
      {
        int r = this.rnd.Next(i, sequence.Length);
        int tmp = sequence[r];
        sequence[r] = sequence[i];
        sequence[i] = tmp;
        //sequence[i] = i; // for testing
      }
    } // Shuffle

    // --------------------------------------------------------

    public double Error(double[][] trainX, double[] trainY)
    {
      // MSE -- useful for progress
      int n = trainX.Length;
      double sumSquaredError = 0.0;
      for (int i = 0; i "lt" n; ++i)
      {
        double predY = this.ComputeOutput(trainX[i]);
        double actualY = trainY[i];
        sumSquaredError += (predY - actualY) *
          (predY - actualY);
      }
      return sumSquaredError / n;  // consider RMSE
    } // Error

    // ------------------------------------------------------

    public double MeanBCE(double[][] trainX, double[] trainY)
    {
      // mean binary cross entropy error
      // for arbitary target yi and predicted pi:
      // bcee = -1 * [ (yi * log(pi)) + ((1-yi) * log(1-pi)) ]
      // 
      // if yi target == 0 or 1 only: 
      // when yi == 1, bcee = -1 * [log(pi)]
      // when yi == 0, bcee = -1 * [log(1-pi)]
      int n = trainX.Length;
      double err = 0.0;
      for (int i = 0; i "lt" n; ++i)
      {
        double predY = this.ComputeOutput(trainX[i]);
        double actualY = trainY[i];   // 0.0 or 1.0
        if ((int)actualY == 1)        // target == 1
          err += -Math.Log(predY);
        else                          // target == 0
          err += -Math.Log(1.0 - predY);
      }
      return err / n;
    } // meanBCE

    // --------------------------------------------------------

    public double Accuracy(double[][] dataX, double[] dataY)
    {
      int n = dataX.Length;
      int nCorrect = 0;
      int nWrong = 0;
      for (int i = 0; i "lt" n; ++i)
      {
        double predY = this.ComputeOutput(dataX[i]);
        double actualY = dataY[i];  // 0.0 or 1.0

        if ((int)actualY == 0 "and" predY "lt" 0.5)
          ++nCorrect;
        else if ((int)actualY == 1 "and" predY "gte" 0.5)
          ++nCorrect;
        else
          ++nWrong;
      }
      return (nCorrect * 1.0) / (nCorrect + nWrong);
    }

    // ------------------------------------------------------

    public double CalibrationErrorCorrects(double[][] dataX,
      double[] dataY)
    {
      // just for correct predictions
      int[] counts = new int[10];
      double[] sums = new double[10];
      int[] numCorrects = new int[10];
      int[] numWrongs = new int[10];
      double[] accuracies = new double[10];
      double[] avgPseudoProbs = new double[10];
      double[] absDiffs = new double[10];

      for (int i = 0; i "lt" dataX.Length; ++i)
      {
        double[] x = dataX[i];
        int target = (int)dataY[i];  // 0 or 1
        double predY = this.ComputeOutput(x);  // like 0.67

        bool correct = false;
        if (target == 1 "and" predY "gte" 0.5)
          correct = true;
        else if (target == 0 "and" predY "lt" 0.5)
          correct = true;

        if (correct == true)
        {
          int bin = -1;
          if (predY "gte" 0.0 "and" predY "lt" 0.1) bin = 0;
          else if (predY "gte" 0.1 "and" predY
            "lt" 0.2) bin = 1;
          else if (predY "gte" 0.2 "and" predY
            "lt" 0.3) bin = 2;
          else if (predY "gte" 0.3 "and" predY
            "lt" 0.4) bin = 3;
          else if (predY "gte" 0.4 "and" predY
            "lt" 0.5) bin = 4;
          else if (predY "gte" 0.5 "and" predY
            "lt" 0.6) bin = 5;
          else if (predY "gte" 0.6 "and" predY
            "lt" 0.7) bin = 6;
          else if (predY "gte" 0.7 "and" predY
            "lt" 0.8) bin = 7;
          else if (predY "gte" 0.8 "and" predY
            "lt" 0.9) bin = 8;
          else if (predY "gte" 0.9 "and" predY
            "lte" 1.0) bin = 9;

          counts[bin] += 1;
          sums[bin] += predY;
          numCorrects[bin] += 1;
        }

      } // i

      for (int bin = 0; bin "lt" 10; ++bin)
        if (counts[bin] == 0) accuracies[bin] = 0.0;
        else accuracies[bin] = numCorrects[bin] / counts[bin];

      for (int bin = 0; bin "lt" 10; ++bin)
        if (counts[bin] == 0) avgPseudoProbs[bin] = 0.0;
        else avgPseudoProbs[bin] = sums[bin] / counts[bin];

      for (int bin = 0; bin "lt" 10; ++bin)
      {
        if (bin "lte" 4)  // class 0
        {
          absDiffs[bin] = 
            Math.Abs((1.0 - avgPseudoProbs[bin]) -
            accuracies[bin]);
        }
        else if (bin "gte" 5)
        {
          absDiffs[bin] = Math.Abs(avgPseudoProbs[bin] -
            accuracies[bin]);
        }
      } // bin 

      double calibErr = 0.0;
      for (int bin = 0; bin "lt" 10; ++bin)
        calibErr += counts[bin] * absDiffs[bin];  // weights
      calibErr /= dataX.Length;

      Console.WriteLine("Calibration bin counts: ");
      for (int bin = 0; bin "lt" 10; ++bin)
        Console.Write(counts[bin].ToString().PadLeft(4));
      Console.WriteLine("");
      return calibErr;

    } // CalibrationErrorCorrects()

    // ------------------------------------------------------

    public double CalibrationErrorAll(double[][] dataX,
      double[] dataY)
    {
      // for all (correct and incorrect) predictions
      int[] counts = new int[10];
      double[] sums = new double[10];
      int[] numCorrects = new int[10];
      int[] numWrongs = new int[10]; // not used
      double[] accuracies = new double[10];
      double[] avgPseudoProbs = new double[10];
      double[] absDiffs = new double[10];

      for (int i = 0; i "lt" dataX.Length; ++i)
      {
        double[] x = dataX[i];
        int target = (int)dataY[i];  // 0 or 1
        double predY = this.ComputeOutput(x);  // like 0.67

        bool correct = false;
        if (target == 1 "and" predY "gte" 0.5)
          correct = true;
        else if (target == 0 "and" predY "lt" 0.5)
          correct = true;

        int bin = -1;
        if (predY "gte" 0.0 "and" predY "lt" 0.1) bin = 0;
        else if (predY "gte" 0.1 "and" predY
          "lt" 0.2) bin = 1;
        else if (predY "gte" 0.2 "and" predY
          "lt" 0.3) bin = 2;
        else if (predY "gte" 0.3 "and" predY
          "lt" 0.4) bin = 3;
        else if (predY "gte" 0.4 "and" predY
          "lt" 0.5) bin = 4;
        else if (predY "gte" 0.5 "and" predY
          "lt" 0.6) bin = 5;
        else if (predY "gte" 0.6 "and" predY
          "lt" 0.7) bin = 6;
        else if (predY "gte" 0.7 "and" predY
          "lt" 0.8) bin = 7;
        else if (predY "gte" 0.8 "and" predY
          "lt" 0.9) bin = 8;
        else if (predY "gte" 0.9 "and" predY
          "lte" 1.0) bin = 9;

        counts[bin] += 1; // just for fun style
        sums[bin] += predY;
        if (correct == true)
          numCorrects[bin] += 1;
      } // i

      for (int bin = 0; bin "lt" 10; ++bin)
        if (counts[bin] == 0) accuracies[bin] = 0.0;
        else accuracies[bin] = numCorrects[bin] / counts[bin];

      for (int bin = 0; bin "lt" 10; ++bin)
        if (counts[bin] == 0) avgPseudoProbs[bin] = 0.0;
        else avgPseudoProbs[bin] = sums[bin] / counts[bin];

      for (int bin = 0; bin "lt" 10; ++bin)
      {
        if (bin "lte" 4)  // class 0
        {
          absDiffs[bin] =
            Math.Abs((1.0 - avgPseudoProbs[bin]) -
            accuracies[bin]);
        }
        else if (bin "gte" 5)
        {
          absDiffs[bin] = Math.Abs(avgPseudoProbs[bin] -
            accuracies[bin]);
        }
      } // bin 

      double calibErr = 0.0;
      for (int bin = 0; bin "lt" 10; ++bin)
        calibErr += counts[bin] * absDiffs[bin];  // weighted
      calibErr /= dataX.Length;

      Console.WriteLine("Calibration bin counts: ");
      for (int bin = 0; bin "lt" 10; ++bin)
        Console.Write(counts[bin].ToString().PadLeft(4));
      Console.WriteLine("");
      return calibErr;
    } // CalibrationErrorAll()


    // ------------------------------------------------------

  } // NeuralNetwork class

  public class Utils
  {
    public static double[][] VecToMat(double[] vec,
      int rows, int cols)
    {
      // vector to row vec/matrix
      double[][] result = MatCreate(rows, cols);
      int k = 0;
      for (int i = 0; i "lt" rows; ++i)
        for (int j = 0; j "lt" cols; ++j)
          result[i][j] = vec[k++];
      return result;
    }

    // ------------------------------------------------------

    public static double[][] MatCreate(int rows,
      int cols)
    {
      double[][] result = new double[rows][];
      for (int i = 0; i "lt" rows; ++i)
        result[i] = new double[cols];
      return result;
    }

    // ------------------------------------------------------

    public static double[][] MatLoad(string fn, int[] usecols,
      char sep, string comment)
    {
      List"lt"double[]"gt" result = new List"lt"double[]"gt"();
      string line = "";
      FileStream ifs = new FileStream(fn, FileMode.Open);
      StreamReader sr = new StreamReader(ifs);
      while ((line = sr.ReadLine()) != null)
      {
        if (line.StartsWith(comment) == true)
          continue;
        string[] tokens = line.Split(sep);
        List"lt"double"gt" lst = new List"lt"double"gt"();
        for (int j = 0; j "lt" usecols.Length; ++j)
          lst.Add(double.Parse(tokens[usecols[j]]));
        double[] row = lst.ToArray();
        result.Add(row);
      }
      sr.Close(); ifs.Close();
      return result.ToArray();
    }

    // ------------------------------------------------------

    public static double[] MatToVec(double[][] m)
    {
      int rows = m.Length;
      int cols = m[0].Length;
      double[] result = new double[rows * cols];
      int k = 0;
      for (int i = 0; i "lt" rows; ++i)
        for (int j = 0; j "lt" cols; ++j)
          result[k++] = m[i][j];

      return result;
    }

    // ------------------------------------------------------

    public static double[][] MatToOneHot(double[][] m,
      int n)
    {
      // convert ordinal (0,1,2 . .) to one-hot
      int rows = m.Length;
      int cols = m[0].Length;  // assumed 1
      double[][] result = MatCreate(rows, n);
      for (int i = 0; i "lt" rows; ++i)
      {
        int k = (int)m[i][0];  // 0,1,2 . .
        result[i] = new double[n];  // [0.0  0.0  0.0]
        result[i][k] = 1.0;  // [ 0.0  1.0  0.0]
      }

      return result;
    }

    // ------------------------------------------------------

    public static void MatShow(double[][] m,
      int dec, int wid)
    {
      for (int i = 0; i "lt" m.Length; ++i)
      {
        for (int j = 0; j "lt" m[0].Length; ++j)
        {
          double v = m[i][j];
          if (Math.Abs(v) "lt" 1.0e-8) v = 0.0; // hack
          Console.Write(v.ToString("F" +
            dec).PadLeft(wid));
        }
        Console.WriteLine("");
      }
    }

    // ------------------------------------------------------

    public static void VecShow(int[] vec, int wid)
    {
      for (int i = 0; i "lt" vec.Length; ++i)
        Console.Write(vec[i].ToString().PadLeft(wid));
      Console.WriteLine("");
    }

    // ------------------------------------------------------

    public static void VecShow(double[] vec,
      int dec, int wid, bool newLine)
    {
      for (int i = 0; i "lt" vec.Length; ++i)
      {
        double x = vec[i];
        if (Math.Abs(x) "lt" 1.0e-8) x = 0.0;
        Console.Write(x.ToString("F" +
          dec).PadLeft(wid));
      }
      if (newLine == true)
        Console.WriteLine("");
    }

  } // Utils class

} // ns

Training data:

# people_train.txt
# sex (0 = male, 1 = female) - dependent variable
# age, state (michigan, nebraska, oklahoma), income,
# politics type (conservative, moderate, liberal)
#
1, 0.24, 1, 0, 0, 0.2950, 0, 0, 1
0, 0.39, 0, 0, 1, 0.5120, 0, 1, 0
1, 0.63, 0, 1, 0, 0.7580, 1, 0, 0
0, 0.36, 1, 0, 0, 0.4450, 0, 1, 0
1, 0.27, 0, 1, 0, 0.2860, 0, 0, 1
1, 0.50, 0, 1, 0, 0.5650, 0, 1, 0
1, 0.50, 0, 0, 1, 0.5500, 0, 1, 0
0, 0.19, 0, 0, 1, 0.3270, 1, 0, 0
1, 0.22, 0, 1, 0, 0.2770, 0, 1, 0
0, 0.39, 0, 0, 1, 0.4710, 0, 0, 1
1, 0.34, 1, 0, 0, 0.3940, 0, 1, 0
0, 0.22, 1, 0, 0, 0.3350, 1, 0, 0
1, 0.35, 0, 0, 1, 0.3520, 0, 0, 1
0, 0.33, 0, 1, 0, 0.4640, 0, 1, 0
1, 0.45, 0, 1, 0, 0.5410, 0, 1, 0
1, 0.42, 0, 1, 0, 0.5070, 0, 1, 0
0, 0.33, 0, 1, 0, 0.4680, 0, 1, 0
1, 0.25, 0, 0, 1, 0.3000, 0, 1, 0
0, 0.31, 0, 1, 0, 0.4640, 1, 0, 0
1, 0.27, 1, 0, 0, 0.3250, 0, 0, 1
1, 0.48, 1, 0, 0, 0.5400, 0, 1, 0
0, 0.64, 0, 1, 0, 0.7130, 0, 0, 1
1, 0.61, 0, 1, 0, 0.7240, 1, 0, 0
1, 0.54, 0, 0, 1, 0.6100, 1, 0, 0
1, 0.29, 1, 0, 0, 0.3630, 1, 0, 0
1, 0.50, 0, 0, 1, 0.5500, 0, 1, 0
1, 0.55, 0, 0, 1, 0.6250, 1, 0, 0
1, 0.40, 1, 0, 0, 0.5240, 1, 0, 0
1, 0.22, 1, 0, 0, 0.2360, 0, 0, 1
1, 0.68, 0, 1, 0, 0.7840, 1, 0, 0
0, 0.60, 1, 0, 0, 0.7170, 0, 0, 1
0, 0.34, 0, 0, 1, 0.4650, 0, 1, 0
0, 0.25, 0, 0, 1, 0.3710, 1, 0, 0
0, 0.31, 0, 1, 0, 0.4890, 0, 1, 0
1, 0.43, 0, 0, 1, 0.4800, 0, 1, 0
1, 0.58, 0, 1, 0, 0.6540, 0, 0, 1
0, 0.55, 0, 1, 0, 0.6070, 0, 0, 1
0, 0.43, 0, 1, 0, 0.5110, 0, 1, 0
0, 0.43, 0, 0, 1, 0.5320, 0, 1, 0
0, 0.21, 1, 0, 0, 0.3720, 1, 0, 0
1, 0.55, 0, 0, 1, 0.6460, 1, 0, 0
1, 0.64, 0, 1, 0, 0.7480, 1, 0, 0
0, 0.41, 1, 0, 0, 0.5880, 0, 1, 0
1, 0.64, 0, 0, 1, 0.7270, 1, 0, 0
0, 0.56, 0, 0, 1, 0.6660, 0, 0, 1
1, 0.31, 0, 0, 1, 0.3600, 0, 1, 0
0, 0.65, 0, 0, 1, 0.7010, 0, 0, 1
1, 0.55, 0, 0, 1, 0.6430, 1, 0, 0
0, 0.25, 1, 0, 0, 0.4030, 1, 0, 0
1, 0.46, 0, 0, 1, 0.5100, 0, 1, 0
0, 0.36, 1, 0, 0, 0.5350, 1, 0, 0
1, 0.52, 0, 1, 0, 0.5810, 0, 1, 0
1, 0.61, 0, 0, 1, 0.6790, 1, 0, 0
1, 0.57, 0, 0, 1, 0.6570, 1, 0, 0
0, 0.46, 0, 1, 0, 0.5260, 0, 1, 0
0, 0.62, 1, 0, 0, 0.6680, 0, 0, 1
1, 0.55, 0, 0, 1, 0.6270, 1, 0, 0
0, 0.22, 0, 0, 1, 0.2770, 0, 1, 0
0, 0.50, 1, 0, 0, 0.6290, 1, 0, 0
0, 0.32, 0, 1, 0, 0.4180, 0, 1, 0
0, 0.21, 0, 0, 1, 0.3560, 1, 0, 0
1, 0.44, 0, 1, 0, 0.5200, 0, 1, 0
1, 0.46, 0, 1, 0, 0.5170, 0, 1, 0
1, 0.62, 0, 1, 0, 0.6970, 1, 0, 0
1, 0.57, 0, 1, 0, 0.6640, 1, 0, 0
0, 0.67, 0, 0, 1, 0.7580, 0, 0, 1
1, 0.29, 1, 0, 0, 0.3430, 0, 0, 1
1, 0.53, 1, 0, 0, 0.6010, 1, 0, 0
0, 0.44, 1, 0, 0, 0.5480, 0, 1, 0
1, 0.46, 0, 1, 0, 0.5230, 0, 1, 0
0, 0.20, 0, 1, 0, 0.3010, 0, 1, 0
0, 0.38, 1, 0, 0, 0.5350, 0, 1, 0
1, 0.50, 0, 1, 0, 0.5860, 0, 1, 0
1, 0.33, 0, 1, 0, 0.4250, 0, 1, 0
0, 0.33, 0, 1, 0, 0.3930, 0, 1, 0
1, 0.26, 0, 1, 0, 0.4040, 1, 0, 0
1, 0.58, 1, 0, 0, 0.7070, 1, 0, 0
1, 0.43, 0, 0, 1, 0.4800, 0, 1, 0
0, 0.46, 1, 0, 0, 0.6440, 1, 0, 0
1, 0.60, 1, 0, 0, 0.7170, 1, 0, 0
0, 0.42, 1, 0, 0, 0.4890, 0, 1, 0
0, 0.56, 0, 0, 1, 0.5640, 0, 0, 1
0, 0.62, 0, 1, 0, 0.6630, 0, 0, 1
0, 0.50, 1, 0, 0, 0.6480, 0, 1, 0
1, 0.47, 0, 0, 1, 0.5200, 0, 1, 0
0, 0.67, 0, 1, 0, 0.8040, 0, 0, 1
0, 0.40, 0, 0, 1, 0.5040, 0, 1, 0
1, 0.42, 0, 1, 0, 0.4840, 0, 1, 0
1, 0.64, 1, 0, 0, 0.7200, 1, 0, 0
0, 0.47, 1, 0, 0, 0.5870, 0, 0, 1
1, 0.45, 0, 1, 0, 0.5280, 0, 1, 0
0, 0.25, 0, 0, 1, 0.4090, 1, 0, 0
1, 0.38, 1, 0, 0, 0.4840, 1, 0, 0
1, 0.55, 0, 0, 1, 0.6000, 0, 1, 0
0, 0.44, 1, 0, 0, 0.6060, 0, 1, 0
1, 0.33, 1, 0, 0, 0.4100, 0, 1, 0
1, 0.34, 0, 0, 1, 0.3900, 0, 1, 0
1, 0.27, 0, 1, 0, 0.3370, 0, 0, 1
1, 0.32, 0, 1, 0, 0.4070, 0, 1, 0
1, 0.42, 0, 0, 1, 0.4700, 0, 1, 0
0, 0.24, 0, 0, 1, 0.4030, 1, 0, 0
1, 0.42, 0, 1, 0, 0.5030, 0, 1, 0
1, 0.25, 0, 0, 1, 0.2800, 0, 0, 1
1, 0.51, 0, 1, 0, 0.5800, 0, 1, 0
0, 0.55, 0, 1, 0, 0.6350, 0, 0, 1
1, 0.44, 1, 0, 0, 0.4780, 0, 0, 1
0, 0.18, 1, 0, 0, 0.3980, 1, 0, 0
0, 0.67, 0, 1, 0, 0.7160, 0, 0, 1
1, 0.45, 0, 0, 1, 0.5000, 0, 1, 0
1, 0.48, 1, 0, 0, 0.5580, 0, 1, 0
0, 0.25, 0, 1, 0, 0.3900, 0, 1, 0
0, 0.67, 1, 0, 0, 0.7830, 0, 1, 0
1, 0.37, 0, 0, 1, 0.4200, 0, 1, 0
0, 0.32, 1, 0, 0, 0.4270, 0, 1, 0
1, 0.48, 1, 0, 0, 0.5700, 0, 1, 0
0, 0.66, 0, 0, 1, 0.7500, 0, 0, 1
1, 0.61, 1, 0, 0, 0.7000, 1, 0, 0
0, 0.58, 0, 0, 1, 0.6890, 0, 1, 0
1, 0.19, 1, 0, 0, 0.2400, 0, 0, 1
1, 0.38, 0, 0, 1, 0.4300, 0, 1, 0
0, 0.27, 1, 0, 0, 0.3640, 0, 1, 0
1, 0.42, 1, 0, 0, 0.4800, 0, 1, 0
1, 0.60, 1, 0, 0, 0.7130, 1, 0, 0
0, 0.27, 0, 0, 1, 0.3480, 1, 0, 0
1, 0.29, 0, 1, 0, 0.3710, 1, 0, 0
0, 0.43, 1, 0, 0, 0.5670, 0, 1, 0
1, 0.48, 1, 0, 0, 0.5670, 0, 1, 0
1, 0.27, 0, 0, 1, 0.2940, 0, 0, 1
0, 0.44, 1, 0, 0, 0.5520, 1, 0, 0
1, 0.23, 0, 1, 0, 0.2630, 0, 0, 1
0, 0.36, 0, 1, 0, 0.5300, 0, 0, 1
1, 0.64, 0, 0, 1, 0.7250, 1, 0, 0
1, 0.29, 0, 0, 1, 0.3000, 0, 0, 1
0, 0.33, 1, 0, 0, 0.4930, 0, 1, 0
0, 0.66, 0, 1, 0, 0.7500, 0, 0, 1
0, 0.21, 0, 0, 1, 0.3430, 1, 0, 0
1, 0.27, 1, 0, 0, 0.3270, 0, 0, 1
1, 0.29, 1, 0, 0, 0.3180, 0, 0, 1
0, 0.31, 1, 0, 0, 0.4860, 0, 1, 0
1, 0.36, 0, 0, 1, 0.4100, 0, 1, 0
1, 0.49, 0, 1, 0, 0.5570, 0, 1, 0
0, 0.28, 1, 0, 0, 0.3840, 1, 0, 0
0, 0.43, 0, 0, 1, 0.5660, 0, 1, 0
0, 0.46, 0, 1, 0, 0.5880, 0, 1, 0
1, 0.57, 1, 0, 0, 0.6980, 1, 0, 0
0, 0.52, 0, 0, 1, 0.5940, 0, 1, 0
0, 0.31, 0, 0, 1, 0.4350, 0, 1, 0
0, 0.55, 1, 0, 0, 0.6200, 0, 0, 1
1, 0.50, 1, 0, 0, 0.5640, 0, 1, 0
1, 0.48, 0, 1, 0, 0.5590, 0, 1, 0
0, 0.22, 0, 0, 1, 0.3450, 1, 0, 0
1, 0.59, 0, 0, 1, 0.6670, 1, 0, 0
1, 0.34, 1, 0, 0, 0.4280, 0, 0, 1
0, 0.64, 1, 0, 0, 0.7720, 0, 0, 1
1, 0.29, 0, 0, 1, 0.3350, 0, 0, 1
0, 0.34, 0, 1, 0, 0.4320, 0, 1, 0
0, 0.61, 1, 0, 0, 0.7500, 0, 0, 1
1, 0.64, 0, 0, 1, 0.7110, 1, 0, 0
0, 0.29, 1, 0, 0, 0.4130, 1, 0, 0
1, 0.63, 0, 1, 0, 0.7060, 1, 0, 0
0, 0.29, 0, 1, 0, 0.4000, 1, 0, 0
0, 0.51, 1, 0, 0, 0.6270, 0, 1, 0
0, 0.24, 0, 0, 1, 0.3770, 1, 0, 0
1, 0.48, 0, 1, 0, 0.5750, 0, 1, 0
1, 0.18, 1, 0, 0, 0.2740, 1, 0, 0
1, 0.18, 1, 0, 0, 0.2030, 0, 0, 1
1, 0.33, 0, 1, 0, 0.3820, 0, 0, 1
0, 0.20, 0, 0, 1, 0.3480, 1, 0, 0
1, 0.29, 0, 0, 1, 0.3300, 0, 0, 1
0, 0.44, 0, 0, 1, 0.6300, 1, 0, 0
0, 0.65, 0, 0, 1, 0.8180, 1, 0, 0
0, 0.56, 1, 0, 0, 0.6370, 0, 0, 1
0, 0.52, 0, 0, 1, 0.5840, 0, 1, 0
0, 0.29, 0, 1, 0, 0.4860, 1, 0, 0
0, 0.47, 0, 1, 0, 0.5890, 0, 1, 0
1, 0.68, 1, 0, 0, 0.7260, 0, 0, 1
1, 0.31, 0, 0, 1, 0.3600, 0, 1, 0
1, 0.61, 0, 1, 0, 0.6250, 0, 0, 1
1, 0.19, 0, 1, 0, 0.2150, 0, 0, 1
1, 0.38, 0, 0, 1, 0.4300, 0, 1, 0
0, 0.26, 1, 0, 0, 0.4230, 1, 0, 0
1, 0.61, 0, 1, 0, 0.6740, 1, 0, 0
1, 0.40, 1, 0, 0, 0.4650, 0, 1, 0
0, 0.49, 1, 0, 0, 0.6520, 0, 1, 0
1, 0.56, 1, 0, 0, 0.6750, 1, 0, 0
0, 0.48, 0, 1, 0, 0.6600, 0, 1, 0
1, 0.52, 1, 0, 0, 0.5630, 0, 0, 1
0, 0.18, 1, 0, 0, 0.2980, 1, 0, 0
0, 0.56, 0, 0, 1, 0.5930, 0, 0, 1
0, 0.52, 0, 1, 0, 0.6440, 0, 1, 0
0, 0.18, 0, 1, 0, 0.2860, 0, 1, 0
0, 0.58, 1, 0, 0, 0.6620, 0, 0, 1
0, 0.39, 0, 1, 0, 0.5510, 0, 1, 0
0, 0.46, 1, 0, 0, 0.6290, 0, 1, 0
0, 0.40, 0, 1, 0, 0.4620, 0, 1, 0
0, 0.60, 1, 0, 0, 0.7270, 0, 0, 1
1, 0.36, 0, 1, 0, 0.4070, 0, 0, 1
1, 0.44, 1, 0, 0, 0.5230, 0, 1, 0
1, 0.28, 1, 0, 0, 0.3130, 0, 0, 1
1, 0.54, 0, 0, 1, 0.6260, 1, 0, 0

Test data:

# people_test.txt
#
0, 0.51, 1, 0, 0, 0.6120, 0, 1, 0
0, 0.32, 0, 1, 0, 0.4610, 0, 1, 0
1, 0.55, 1, 0, 0, 0.6270, 1, 0, 0
1, 0.25, 0, 0, 1, 0.2620, 0, 0, 1
1, 0.33, 0, 0, 1, 0.3730, 0, 0, 1
0, 0.29, 0, 1, 0, 0.4620, 1, 0, 0
1, 0.65, 1, 0, 0, 0.7270, 1, 0, 0
0, 0.43, 0, 1, 0, 0.5140, 0, 1, 0
0, 0.54, 0, 1, 0, 0.6480, 0, 0, 1
1, 0.61, 0, 1, 0, 0.7270, 1, 0, 0
1, 0.52, 0, 1, 0, 0.6360, 1, 0, 0
1, 0.30, 0, 1, 0, 0.3350, 0, 0, 1
1, 0.29, 1, 0, 0, 0.3140, 0, 0, 1
0, 0.47, 0, 0, 1, 0.5940, 0, 1, 0
1, 0.39, 0, 1, 0, 0.4780, 0, 1, 0
1, 0.47, 0, 0, 1, 0.5200, 0, 1, 0
0, 0.49, 1, 0, 0, 0.5860, 0, 1, 0
0, 0.63, 0, 0, 1, 0.6740, 0, 0, 1
0, 0.30, 1, 0, 0, 0.3920, 1, 0, 0
0, 0.61, 0, 0, 1, 0.6960, 0, 0, 1
0, 0.47, 0, 0, 1, 0.5870, 0, 1, 0
1, 0.30, 0, 0, 1, 0.3450, 0, 0, 1
0, 0.51, 0, 0, 1, 0.5800, 0, 1, 0
0, 0.24, 1, 0, 0, 0.3880, 0, 1, 0
0, 0.49, 1, 0, 0, 0.6450, 0, 1, 0
1, 0.66, 0, 0, 1, 0.7450, 1, 0, 0
0, 0.65, 1, 0, 0, 0.7690, 1, 0, 0
0, 0.46, 0, 1, 0, 0.5800, 1, 0, 0
0, 0.45, 0, 0, 1, 0.5180, 0, 1, 0
0, 0.47, 1, 0, 0, 0.6360, 1, 0, 0
0, 0.29, 1, 0, 0, 0.4480, 1, 0, 0
0, 0.57, 0, 0, 1, 0.6930, 0, 0, 1
0, 0.20, 1, 0, 0, 0.2870, 0, 0, 1
0, 0.35, 1, 0, 0, 0.4340, 0, 1, 0
0, 0.61, 0, 0, 1, 0.6700, 0, 0, 1
0, 0.31, 0, 0, 1, 0.3730, 0, 1, 0
1, 0.18, 1, 0, 0, 0.2080, 0, 0, 1
1, 0.26, 0, 0, 1, 0.2920, 0, 0, 1
0, 0.28, 1, 0, 0, 0.3640, 0, 0, 1
0, 0.59, 0, 0, 1, 0.6940, 0, 0, 1
This entry was posted in Machine Learning. Bookmark the permalink.

Leave a Reply