Calibrating a C# Neural Network Binary Classifier Using Particle Swarm Optimization

A few days ago, I implemented a calibration error function for my standard C# neural network binary classifier code. But then, as usual, one idea leads to another and I wondered about tuning a trained network model to improve the calibration error. Could I do that and would it help? Answer: I used particle swarm optimization to tune a trained binary classifier to improve the calibration, and the calibrated network had better prediction accuracy than the untuned model — in other words, my experiment worked very well and exceeded my expectations.

Bear with me, there are a lot of ideas involved here.

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 (the “other” class).

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 (pp) 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. Higher calibration error is worse than lower calibration error. 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.

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.

The key calling statements in the demo are:

. . .
NeuralNetwork nn =
  new NeuralNetwork(8, 10, 1, seed: 0);  // create classifier
nn.Train(trainX, trainY, lrnRate, batSize, maxEpochs);  // train
double[] modelWeights = nn.GetWeights();  // trained weights
double[] wtsBias = nn.Calibrate(50, 5000,
  modelWeights, trainX, trainY);  // improved weights
nn.SetWeights(wtsBias);  // modify the classifier
. . .

The demo creates an 8-10-1 binary classifier and trains it using standard SGD back-propagation. The resulting weights and biases are fetched and used as a starting point for swarm optimization tuning to improve calibration error — as opposed to starting the calibration from scratch. The particle swarm optimization calibration tuning function uses 50 Particles and 5,000 tuning iterations. The resulting tuned weights and biases are then placed back into the neural network

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.6918  acc = 0.5300
epoch:   80  MBCCE = 0.6839  acc = 0.5400
epoch:  160  MBCCE = 0.6553  acc = 0.6300
epoch:  240  MBCCE = 0.5785  acc = 0.7100
epoch:  320  MBCCE = 0.3240  acc = 0.9000
Done

Accuracy on train data = 0.8650
Accuracy on test data  = 0.7000

Calibration error training data, all predictions
0.6788

Calibration error test data, all predictions
0.6457

Starting calibration using swarm optimization
iter =        0  calibration err =   0.3376  acc = 0.8450
iter =     1000  calibration err =   0.2478  acc = 0.8850
iter =     2000  calibration err =   0.2461  acc = 0.8850
iter =     3000  calibration err =   0.2451  acc = 0.8850
iter =     4000  calibration err =   0.2450  acc = 0.8850

New accuracy on train data = 0.8900
New accuracy on test data  = 0.7500

New calibration error training data
0.2401

New calibration error test data
0.5603

End demo

To summarize the demo output: standard training gave a prediction model with reasonably good accuracy (86.50% = 173 out of 200 correct) but very poor calibration (0.6788 error). After tuning the network using particle swarm optimization, accuracy improved significantly to 89.00% and the calibration error improved dramatically to 0.2401 error.

The key component of the calibration tuning system is particle swarm optimization (PSO). Calibration error is not calculus-differentiable so ordinary math techniques such as gradient descent won’t work.



The demo calibration system uses particle swarm optimization (PSO).


For a neural network, a solution is a set of weights and biases. The demo 8-10-1 neural network has 8*10 input-to-hidden weights, 10 hidden biases, 10*1 hidden-to-output weights, 1 hidden bias, for a total of 101 values.

Each solution is encapsulated Particle. In very high-level pseudo-code:

create a collection (swarm) of possible solutions (particles)
loop max_iterations times
  for-each particle/solution in swarm
    compute a velocity
    use velocity to update particle/solution
  end-for  
  keep track of best solution/particle found
end-loop
return best particle/solution found by any particle

Each particle “moves” to a new solution based on a combination of its current “direction”, the best solution/position found by the particle so far, and the best solution/position found by any particle in the swarm so far. The particles tend to quickly spiral towards a common result/position/solution.

My demo program is long and complex because there are many ideas involved — neural networks, calibration error, particle swarm optimization. But all the ideas can be understood with effort. I was able to put together my demo in only a couple of hours, but understanding the all underlying ideas took me many years of (enjoyable) study effort.



One way of thinking about calibration is that it’s a way to match expectation and reality. Here are three examples of children’s clowns with high calibration error. Many years ago, for my son’s first birthday party extravaganza, I rented “Steve-O the Clown”. The less said about that entertainment episode, the better.


Demo program. Replace “lt” (less than), “gt”, “lte”, “gte”, “and” with Boolean operator symbols. My blog editor 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, 8, 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, 10, 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("\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("\nStarting calibration using" +
        " swarm optimization ");
      double[] modelWeights = nn.GetWeights();
      double[] wtsBias = nn.Calibrate(50, 5000,
        modelWeights, trainX, trainY);
      nn.SetWeights(wtsBias);

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

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

      Console.WriteLine("\nNew calibration error" +
        " training data ");
      calErrTrainAll =
        nn.CalibrationErrorAll(trainX, trainY);
      Console.WriteLine(calErrTrainAll.ToString("F4"));

      Console.WriteLine("\nNew calibration error" +
        " test data  ");
      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 class Particle
    {
      public double[] position; // soln = wts + bias
      public double error;
      public double[] velocity; // to determine next position

      public double[] bestPosition; // best seen
      public double bestError;

      public Particle(int solnLen)
      {
        this.position = new double[solnLen];
        this.velocity = new double[solnLen];
        this.bestPosition = new double[solnLen];
      }
    } // Particle

    // ======================================================

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

    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 p = 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[p++];
      for (int j = 0; j "lt" this.nh; ++j)
        this.hBiases[j] = wts[p++];
      for (int j = 0; j "lt" this.nh; ++j)
        for (int k = 0; k "lt" this.no; ++k)
          this.hoWeights[j][k] = wts[p++];
      for (int k = 0; k "lt" this.no; ++k)
        this.oBiases[k] = wts[p++];
    }

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

    public double[] GetWeights()
    {
      int numWts = (this.ni * this.nh) +
        (this.nh * this.no) + this.nh + this.no;
      double[] result = new double[numWts];

      int p = 0;
      // ih-weights
      for (int i = 0; i "lt" this.ni; ++i)
        for (int j = 0; j "lt" this.nh; ++j)
          result[p++] = this.ihWeights[i][j];
      // h-biases
      for (int j = 0; j "lt" this.nh; ++j)
        result[p++] = this.hBiases[j];
      // ho-weights
      for (int j = 0; j "lt" this.nh; ++j)
        for (int k = 0; k "lt" this.no; ++k)
          result[p++] = this.hoWeights[j][k];
      // o-biases
      for (int k = 0; k "lt" this.no; ++k)
        result[p++] = this.oBiases[k];
      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 j = 0; j "lt" this.nh; ++j)
        hSums[j] += this.hBiases[j];

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

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

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

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

      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 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;

      return calibErr;
    } // CalibrationErrorAll()

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

    public double CalibrationErrorUsing(double[] wtsBias,
      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
        double predY = this.PredictUsing(wtsBias, x)[0];

        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];
      calibErr /= dataX.Length;

      return calibErr;
    } // CalibrationErrorUsing()

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

    public double[] PredictUsing(double[] wtsBias,
      double[] x)
    {
      // assumes ih-wts, h-biases, ho-wts, o-biases
      double[] hNodes = new double[nh];
      double[] oNodes = new double[no];

      int ptr = 0;  // ptr into wtsBias vector
      // the indexing is very tricky

      for (int i = 0; i "lt" this.ni; ++i) // hNodes
        for (int j = 0; j "lt" this.nh; ++j)
          hNodes[j] += x[i] * wtsBias[ptr++];

      for (int j = 0; j "lt" this.nh; ++j)
      {
        hNodes[j] += wtsBias[ptr++];
        hNodes[j] = HyperTan(hNodes[j]);
      }

      for (int j = 0; j "lt" this.nh; ++j) // oNodes
        for (int k = 0; k "lt" this.no; ++k)
          oNodes[k] += hNodes[j] * wtsBias[ptr++];

      for (int k = 0; k "lt" this.no; ++k)
      {
        oNodes[k] += wtsBias[ptr++];
        oNodes[k] = LogSigmoid(oNodes[k]);
      }

      double[] result = new double[no];
      for (int k = 0; k "lt" no; ++k)
        result[k] = oNodes[k];
      return result;
    } // PredictUsing()

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

    public double AccuracyUsing(double[] wtsBias,
      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[] x = dataX[i];
        double predY = this.PredictUsing(wtsBias, x)[0];
        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);
    } // AccuracyUsing()

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

    public double[] Calibrate(int numParticles, int maxIter,
      double[] startWeights, double[][] trainX,
      double[] trainY)
    {
      // 1. create a swarm of particles based on startWeights
      int solnLen = startWeights.Length;
      double lo = -0.10; double hi = +0.10;

      double[] globalBestPosition = new double[solnLen];
      double globalBestError = double.MaxValue;

      double w = 0.729; // inertia weight
      double c1 = 1.49445; // cognitive weight
      double c2 = 1.49445; // social weight

      Particle[] swarm = new Particle[numParticles];
      for (int i = 0; i "lt" numParticles; ++i)
      {
        swarm[i] = new Particle(solnLen);
        for (int j = 0; j "lt" solnLen; ++j)
          swarm[i].position[j] = startWeights[j] +
            ((hi - lo) * this.rnd.NextDouble() + lo);

        swarm[i].error = 
          this.CalibrationErrorUsing(swarm[i].position,
          trainX, trainY);

        for (int j = 0; j "lt" solnLen; ++j)
          swarm[i].velocity[j] = (hi - lo) *
            this.rnd.NextDouble() + lo;
        for (int j = 0; j "lt" solnLen; ++j)
          swarm[i].bestPosition[j] = swarm[i].position[j];
        swarm[i].bestError = swarm[i].error;
      }

      // 2. set global bests
      for (int i = 0; i "lt" numParticles; ++i)
      {
        if (swarm[i].error "lt" globalBestError)
        {
          globalBestError = swarm[i].error;
          for (int j = 0; j "lt" solnLen; ++j)
            globalBestPosition[j] = swarm[i].position[j];
        }
      }

      // main processing loop
      for (int iter = 0; iter "lt" maxIter; ++iter)
      {
        for (int i = 0; i "lt" numParticles; ++i)
        {
          Particle currP = swarm[i];  // ref for clarity

          for (int j = 0; j "lt" solnLen; ++j) // 1. velocity
          {
            double r1 = this.rnd.NextDouble();
            double r2 = this.rnd.NextDouble();

            currP.velocity[j] = (w * currP.velocity[j]) +
              (c1 * r1 * (currP.bestPosition[j] -
              currP.position[j])) +
              (c2 * r2 * (globalBestPosition[j] -
              currP.position[j]));
          }

          for (int j = 0; j "lt" solnLen; ++j) // 2. position
          {
            currP.position[j] = currP.position[j] +
              currP.velocity[j];
          }

          // 3. update particle's error
          currP.error = 
            this.CalibrationErrorUsing(currP.position,
            trainX, trainY);

          // 4. check if particle new best
          if (currP.error "lt" currP.bestError)
          {
            currP.bestError = currP.error;
            for (int j = 0; j "lt" solnLen; ++j)
              currP.bestPosition[j] = currP.position[j];
          }

          // 5. check if new global best found
          if (currP.error "lt" globalBestError)
          {
            globalBestError = currP.error;
            for (int j = 0; j "lt" solnLen; ++j)
              globalBestPosition[j] = currP.position[j];
          }

        } // each particle

        if (iter % (maxIter / 5) == 0) // display progress
        {
          double bestAcc = 
            this.AccuracyUsing(globalBestPosition,
            trainX, trainY);
          string s1 = "iter = " + 
            iter.ToString().PadLeft(8);
          string s2 = "  calibration err = " +  
            globalBestError.ToString("F4").PadLeft(8);
          string s3 = "  acc = " + 
            bestAcc.ToString("F4");
          Console.WriteLine(s1 + s2 + s3);
        }
      } // main loop

      // return best position/solution found
      double[] result = new double[solnLen];
      for (int j = 0; j "lt" solnLen; ++j)
        result[j] = globalBestPosition[j];
      return result;

    } // Calibrate

    // ******************************************************

  } // 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