Validating My Gradient Boosting Regression with C# on Categorical Data using scikit GradientBoostingRegressor

Bottom line: I compared my from-scratch C# implementation of gradient boosting regression on training data with categorical predictor variables with the scikit-learn GradientBoostingRegression library module and got essentially the same results.

The goal of a machine learning regression problem is to predict a single numeric value. For example, you might want to predict the Income of a person based on their Sex, Age, City, Height, and Psychology (timid, alert, brave). There are many different regression techniques. One of the most common techniques is gradient boosting regression.

Gradient boosting regression (GBR) is a tree-based technique. GBR creates a collection of small decision trees (typically about 100) where each tree learns how to predict training items that were poorly predicted by the previous tree.

I wanted to validate my from-scratch C# implementation of GBR against the Python language scikit-learn library GradientBoostingRegressor module. I generated a set of synthetic training data that looks like:

M, 29, Austin, 68.5, timid, 54600.00
F, 51, Juneau, 60.0, brave, 25700.00
M, 32 ,Boston, 73.5, alert, 74900.00
. . .

The goal is to predict Income (in the last column) from Sex, Age, City, Height (in inches), and Psychology (timid, alert, brave). There are 200 training items and 40 test items.

The Sex variable is encoded as M = 0, F = 1. There are 10 possible City values: Austin, Boston, Casper, Denver, Eugene, Fresno, Gallup, Helena, Irvine, Juneau.

I used sorted-target-mean ordinal encoding, where the city that has the smallest average Income (Boston = $30,600.00) is encoded as 0, the next larger average Income city (Casper = $33,100.00) is encoded as 1, and so on to the largest average income city (Helena = $64,194.74) is 9.

I used one-hot encoding for Psychology: timid = 1,0,0 alert = 0,1,0 brave = 0,0,1.

When using tree-based regression, it’s not necessary to normalize numeric predictor variables, but it doesn’t hurt and can be helpful in some scenarios. I divided all Age values by 100, and all Height values by 100. I normalized the target Income variable by dividing all values by 100,000.

The resulting normalized and encoded data looks like:

0, 0.29, 2, 0.685, 1,0,0, 0.54600
1, 0.51, 8, 0.600, 0,0,1, 0.25700
0, 0.32, 0, 0.735, 0,1,0, 0.74900
. . . 

The key parts of the output of my C# implementation of gradient boosting regression are:


Begin C# Gradient Boosting regression demo
Predict Income from Sex, Age, City Height, Psychology

Loading people train (200) and test (40) data
Done

First three raw train X:
M,29,Austin,68.5,timid,54600.00
F,51,Juneau,60.0,brave,25700.00
M,32,Boston,73.5,alert,74900.00

First three train X:
  0.00000  0.29000  2.00000  0.68500  1.00000  0.00000  0.00000
  1.00000  0.51000  8.00000  0.60000  0.00000  0.00000  1.00000
  0.00000  0.32000  0.00000  0.73500  0.00000  1.00000  0.00000

First three train y:
  0.54600
  0.25700
  0.74900

Setting numTrees = 200
Setting maxDepth = 4
Setting minSamples = 2
Setting minLeaf = 1
Setting lrnRate = 0.0500

Creating and training GradientBoostRegression model
Done

Evaluating model

Accuracy train (within 0.10) = 1.0000
Accuracy test (within 0.10) = 0.7750

MSE train = 0.0000
MSE test = 0.0022

R2 train = 0.9993
R2 test = 0.9344

Predicting for x =
   0.0000   0.2900   2.0000   0.6850   1.0000   0.0000   0.0000
Predicted y = 0.5391

Explaining for x =
   0.0000   0.2900   2.0000   0.6850   1.0000   0.0000   0.0000

Initial prediction: 0.4685
t =   0  pred_res =  -0.0342  delta =  -0.0017  pred =  0.4668
t =   1  pred_res =   0.0079  delta =   0.0004  pred =  0.4672
t =   2  pred_res =  -0.0315  delta =  -0.0016  pred =  0.4656
t =   3  pred_res =  -0.0361  delta =  -0.0018  pred =  0.4638
t =   4  pred_res =  -0.0136  delta =  -0.0007  pred =  0.4632
. . .
t = 198  pred_res =  -0.0000  delta =  -0.0000  pred =  0.5391
t = 199  pred_res =   0.0000  delta =   0.0000  pred =  0.5391
Predicted y = 0.5391

End demo

I ran the data through the scikit GradientBoostingRegression module and got essentially the same results:

Begin scikit gradient boost regression demo
Predict Income from Sex, Age, City, Height, Psych

Setting n_estimators = 200
Setting max_depth = 4
Setting min_samples_split = 2
Setting min_samples_leaf = 1
Setting lrn_rate = 0.0500

Training gradient boost regression model
Done

Evaluating model

Acc train (within 0.10): 1.0000
Acc test (within 0.10): 0.8000

MSE train: 0.0000
MSE test: 0.0022

R2 train: 0.9993
R2 test: 0.9347

Predicting for x =
[0.0000 0.2900 2.0000 0.6850 1.0000 0.0000 0.0000]
Predicted y = 0.5391

End demo

A big advantage of my from-scratch C# implementation of GBR is that it was easy for me to add statements that show exactly how a prediction is made.



Machine learning regression models are often beautiful to my eye. But I’m not a good judge of other kinds of models. I’ve noticed that fashion models often look like they walk with an exaggerated limp. Here are three images that were returned by an Internet search for “ugly fashion model on runway”.

Left: She doesn’t look ugly to me. Maybe she is too blonde or maybe the slightly vacuous look on her face isn’t attractive in some model-sense way.

Center: Again, this model isn’t ugly to my eye but who am I to disagree with the Internet? She does kind of have a resting-witch-face maybe.

Right: OK, there isn’t much to disagree about “ugly fashion model on runway” here. I don’t want to sound mean, but she doesn’t belong on a fashion runway. An airport runway maybe, but not a fashion runway.


Demo C# program. Very long and complex. Replace “lt” (less than), “gt”, “lte”, “gte” with Boolean operator symbols (my lame blog editor consistently chokes on symbols).

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

namespace GradientBoostRegression
{
  internal class GradientBoostRegressionProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("\nBegin C# Gradient Boosting" +
        " regression demo ");

      Console.WriteLine("Predict Income from Sex, Age," +
        " City Height, Psychology ");

      // 1. load data
      Console.WriteLine("\nLoading people train" +
        " (200) and test (40) data");
      string trainFile =
        "..\\..\\..\\Data\\People\\people_train_200.txt";
      int[] colsX = new int[] { 0, 1, 2, 3, 4,5,6 };
      double[][] trainX =
        MatLoad(trainFile, colsX, ',', "#");
      double[] trainY =
        MatToVec(MatLoad(trainFile,
        [7], ',', "#"));

      string testFile =
        "..\\..\\..\\Data\\People\\people_test_40.txt";
      double[][] testX =
        MatLoad(testFile, colsX, ',', "#");
      double[] testY =
        MatToVec(MatLoad(testFile,
        [7], ',', "#"));
      Console.WriteLine("Done ");

      Console.WriteLine("\nFirst three raw train X: ");
      Console.WriteLine("M,29,Austin,68.5,timid,54600.00 ");
      Console.WriteLine("F,51,Juneau,60.0,brave,25700.00 ");
      Console.WriteLine("M,32,Boston,73.5,alert,74900.00 ");

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

      Console.WriteLine("\nFirst three train y: ");
      for (int i = 0; i "lt" 3; ++i)
        Console.WriteLine(trainY[i].ToString("F4").
          PadLeft(8));

      // 2. create and train model
      int numTrees = 200;
      int maxDepth = 4;
      int minSamples = 2;
      int minLeaf = 1;
      int numSplitCols = -1; // use all columns
      double lrnRate = 0.10;


      Console.WriteLine("\nSetting numTrees = " +
        numTrees);
      Console.WriteLine("Setting maxDepth = " +
        maxDepth);
      Console.WriteLine("Setting minSamples = " +
        minSamples);
      Console.WriteLine("Setting minLeaf = " +
        minLeaf);
      Console.WriteLine("Setting lrnRate = " +
        lrnRate.ToString("F4"));

      Console.WriteLine("\nCreating and training" +
        " GradientBoostRegression model ");
      GradientBoostRegressor gbr =
        new GradientBoostRegressor(numTrees, maxDepth,
        minSamples, minLeaf, numSplitCols, lrnRate);
      gbr.Train(trainX, trainY);
      Console.WriteLine("Done ");

      // 3. evaluate model
      Console.WriteLine("\nEvaluating model ");
      double accTrain = gbr.Accuracy(trainX, trainY, 0.10);
      Console.WriteLine("\nAccuracy train (within 0.10) = " +
        accTrain.ToString("F4"));
      double accTest = gbr.Accuracy(testX, testY, 0.10);
      Console.WriteLine("Accuracy test (within 0.10) = " +
        accTest.ToString("F4"));

      double mseTrain = gbr.MSE(trainX, trainY);
      Console.WriteLine("\nMSE train = " +
        mseTrain.ToString("F4"));
      double mseTest = gbr.MSE(testX, testY);
      Console.WriteLine("MSE test = " +
        mseTest.ToString("F4"));

      double r2Train = gbr.R2(trainX, trainY);
      Console.WriteLine("\nR2 train = " +
        r2Train.ToString("F4"));
      double r2Test = gbr.R2(testX, testY);
      Console.WriteLine("R2 test = " +
        r2Test.ToString("F4"));

      // 4. use model
      double[] x = trainX[0];
      Console.WriteLine("\nPredicting for x = ");
      VecShow(x, 4, 9);
      double predY = gbr.Predict(x, verbose: true);
      Console.WriteLine("Predicted y = " +
        predY.ToString("F4"));

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

    // ------------------------------------------------------
    // helpers for Main()
    // ------------------------------------------------------

    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();
    }

    static double[] MatToVec(double[][] mat)
    {
      int nRows = mat.Length;
      int nCols = mat[0].Length;
      double[] result = new double[nRows * nCols];
      int k = 0;
      for (int i = 0; i "lt" nRows; ++i)
        for (int j = 0; j "lt" nCols; ++j)
          result[k++] = mat[i][j];
      return result;
    }

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

  } // class Program

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

  public class GradientBoostRegressor
  {
    public double lrnRate;
    public int nTrees;
    public int maxDepth;
    public int minSamples;
    public int minLeaf;
    public int numSplitCols;
    public List"lt"DecisionTreeRegressor"gt" trees;
    public double pred0;  // initial prediction
    private Random rnd;

    public GradientBoostRegressor(int nTrees, int maxDepth,
      int minSamples, int minLeaf, int numSplitCols,
      double lrnRate, int seed=0)
    {
      this.nTrees = nTrees;
      this.maxDepth = maxDepth;
      this.minSamples = minSamples;
      this.minLeaf = minLeaf;
      this.numSplitCols = numSplitCols;
      this.trees = new List"lt"DecisionTreeRegressor"gt"();
      this.lrnRate = lrnRate;
      this.rnd = new Random(seed);
    }

    public void Train(double[][] trainX, double[] trainY)
    {
      int n = trainX.Length;
      this.pred0 = Mean(trainY);

      double[] preds = new double[n]; //each data item
      for (int i = 0; i "lt" n; ++i)
        preds[i] = this.pred0;

      double[] residuals = new double[n]; // for curr tree
      for (int t = 0; t "lt" this.nTrees; ++t) // each tree
      {
        // double[] residuals = new double[n]; // curr tree
        for (int i = 0; i "lt" n; ++i)
          residuals[i] = trainY[i] - preds[i];

        DecisionTreeRegressor dtr =
          new DecisionTreeRegressor(this.maxDepth,
          this.minSamples, this.minLeaf, this.numSplitCols,
          false, this.rnd.Next(0,1_000_000));
        dtr.Train(trainX, residuals); // predict residuals

        for (int i = 0; i "lt" n; ++i)
        {
          double predResidual = dtr.Predict(trainX[i]);
          preds[i] += this.lrnRate * predResidual;
        }
        this.trees.Add(dtr);
      }
    } // Train

    public double Predict(double[] x, bool verbose = false)
    {
      double result = this.pred0;
      if (verbose == true)
      {
        Console.WriteLine("\nInitial prediction: " +
        result.ToString("F4"));
      }
      for (int t = 0; t "lt" this.nTrees; ++t)
      {
        double predResidual = this.trees[t].Predict(x);
        double delta = this.lrnRate * predResidual;
        result += delta;

        if (verbose == true)
        {
          if (t "gte" 0 && t "lte" 4 ||
            t "gte" this.nTrees - 2 &&
            t "lte" this.nTrees - 1)
          {
            Console.Write("t = " + t.ToString().PadLeft(3) +
              "  pred_res = " + predResidual.ToString("F4").
              PadLeft(10));
            Console.Write("  delta = " + delta.ToString("F4").
              PadLeft(10));
            Console.WriteLine("  pred = " +
              result.ToString("F4").PadLeft(7));
          }
          if (t == 5)
            Console.WriteLine(". . . ");
        }
      }
      return result;
    }

    public double Accuracy(double[][] dataX, double[] dataY,
      double pctClose)
    {
      int numCorrect = 0; int numWrong = 0;
      for (int i = 0; i "lt" dataX.Length; ++i)
      {
        double actualY = dataY[i];
        double predY = Predict(dataX[i]);
        if (Math.Abs(predY - actualY) "lt"
          (pctClose * Math.Abs(actualY)))
          ++numCorrect;
        else
          ++numWrong;
      }
      return (numCorrect * 1.0) / (numWrong + numCorrect);
    }

    public double MSE(double[][] dataX,
      double[] dataY)
    {
      int n = dataX.Length;
      double sum = 0.0;
      for (int i = 0; i "lt" n; ++i)
      {
        double actualY = dataY[i];
        double predY = this.Predict(dataX[i]);
        sum += (actualY - predY) * (actualY - predY);
      }
      return sum / n;
    }

    public double R2(double[][] dataX, double[] dataY)
    {
      // coefficient of determination
      int n = dataX.Length;
      double sum = 0.0;

      for (int i = 0; i "lt" n; ++i)
        sum += dataY[i];
      double meanActual = sum / n;

      double ssRes = 0.0;
      double ssTot = 0.0;
      for (int i = 0; i "lt" n; ++i)
      {
        double predY = this.Predict(dataX[i]);
        ssRes += (dataY[i] - predY) * (dataY[i] - predY);
        ssTot += (dataY[i] - meanActual) *
          (dataY[i] - meanActual);
      }
      if (Math.Abs(ssTot) "lt" 1.0e-12)
        return 0.0;
      else
        return 1.0 - (ssRes / ssTot);
    }

    private static double Mean(double[] data)
    {
      int n = data.Length;
      double sum = 0.0;
      for (int i = 0; i "lt" n; ++i)
        sum += data[i];
      return sum / n;
    }

  } // class GradientBoostRegressor

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

  public class DecisionTreeRegressor
  {
    // ChatGPT: "I don't see any fundamental tree-building or
    // indexing bugs. If the inputs are guaranteed to be
    // finite doubles and the parameters are validated 
    // elsewhere, the core regression algorithm is correct."

    public int maxDepth;
    public int minSamples;
    public int minLeaf;
    public int numSplitCols;
    public List"lt"Node"gt" tree;
    public Random rnd;
    public bool saveRows;
    public double[][] trainX;
    public double[] trainY;

    // ............................................

    public class Node
    {
      public int id;
      public int colIdx = -1;
      public double thresh = 0.0;
      public int left = -1;
      public int right = -1;
      public double value = 0.0;
      public bool isLeaf = false;
      public List"lt"int"gt" rows;
    }

    // ............................................

    public DecisionTreeRegressor(int maxDepth = 3,
      int minSamples = 2, int minLeaf = 1,
      int numSplitCols = -1, bool saveRows = false,
      int seed = 0)
    {
      this.maxDepth = maxDepth;
      this.minSamples = minSamples;
      this.minLeaf = minLeaf;
      this.numSplitCols = numSplitCols;
      this.saveRows = saveRows;
      this.tree = new List"lt"Node"gt"();

      int numNodes = (int)Math.Pow(2, (maxDepth + 1)) - 1;
      for (int i = 0; i "lt" numNodes; ++i)
        this.tree.Add(null);
      this.rnd = new Random(seed);
    }

    // ------------------------------------------------------
    // ------------------------------------------------------
    // public: ctor(), Train(), Predict()
    // private: BestSplit(), TreeTargetMean()
    // ------------------------------------------------------

    public void Train(double[][] trainX, double[] trainY)
    {
      this.trainX = trainX;
      this.trainY = trainY;

      int maxParentID = (int)Math.Pow(2, this.maxDepth) - 2;

      List"lt"int"gt" allRows =
        new List"lt"int"gt"(this.trainX.Length);
      for (int i = 0; i "lt" this.trainX.Length; ++i)
        allRows.Add(i);

      // construct root node
      Node root = new Node()
      {
        id = 0,
        value = this.TreeTargetMean(allRows),
        rows = allRows
      };
      this.tree[0] = root;

      for (int i = 0; i "lt" this.tree.Count; ++i)
      {
        Node currNode = this.tree[i];
        if (currNode == null) continue;

        int leftID = currNode.id * 2 + 1;
        int rightID = currNode.id * 2 + 2;

        // 1. check before expensive split calcs
        if (currNode.id "gt" maxParentID ||
            currNode.rows.Count "lt" this.minSamples ||
            rightID "gte" this.tree.Count)
        {
          currNode.isLeaf = true;
          continue;
        }

        // 2. find best split
        double[] splitInfo = this.BestSplit(currNode.rows);
        int colIdx = (int)splitInfo[0];
        double splitVal = splitInfo[1];

        if (colIdx == -1) // unable to split
        {
          currNode.isLeaf = true;
          continue;
        }

        // 3. apply split properties
        currNode.colIdx = colIdx;  // aka split col
        currNode.thresh = splitVal;

        List"lt"int"gt" leftIdxs = new List"lt"int"gt"();
        List"lt"int"gt" rightIdxs = new List"lt"int"gt"();

        for (int k = 0; k "lt" currNode.rows.Count; ++k)
        {
          int r = currNode.rows[k];
          if (this.trainX[r][colIdx] "lte" splitVal)
            leftIdxs.Add(r);
          else
            rightIdxs.Add(r);
        }

        // 4. create and attach children
        currNode.left = leftID;
        this.tree[leftID] = new Node()
        {
          id = leftID,
          rows = leftIdxs,
          value = this.TreeTargetMean(leftIdxs)
        };

        currNode.right = rightID;
        this.tree[rightID] = new Node()
        {
          id = rightID,
          rows = rightIdxs,
          value = this.TreeTargetMean(rightIdxs)
        };
      } // main loop

      // delete row info (for ensembles)
      if (this.saveRows == false)
      {
        for (int i = 0; i "lt" this.tree.Count; ++i)
          if (this.tree[i] != null)
            this.tree[i].rows = null;
      }

    } // Train()

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

    public double Predict(double[] x)
    {
      int p = 0;
      double lastValidValue = 0.0;
      while (p != -1 && p "lt" this.tree.Count)
      {
        Node currNode = this.tree[p];
        if (currNode == null) break;

        lastValidValue = currNode.value;
        if (currNode.isLeaf || 
          currNode.colIdx == -1) break;

        if (x[currNode.colIdx] "lte" currNode.thresh)
          p = currNode.left;
        else
          p = currNode.right;
      }
      return lastValidValue;
    }

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

    private double[] BestSplit(List"lt"int"gt" rows)
    {
      int bestColIdx = -1;
      double bestThresh = 0.0;
      double bestGain = -1.0;
      int nRows = rows.Count;
      int nCols = this.trainX[0].Length;

      int[] colIndices = new int[nCols];
      for (int k = 0; k "lt" nCols; ++k)
        colIndices[k] = k;

      // feature sampling
      for (int i = 0; i "lt" nCols; ++i)
      {
        int ri = rnd.Next(i, nCols);
        int tmp = colIndices[i];
        colIndices[i] = colIndices[ri];
        colIndices[ri] = tmp;
      }

      int numColsToUse;
      if (this.numSplitCols == -1)
        numColsToUse = nCols;
      else
        numColsToUse = Math.Min(this.numSplitCols, nCols);

      int[] sortedRows = new int[nRows];
      double[] featureKeys = new double[nRows];

      double totalSum = 0.0;
      for (int i = 0; i "lt" nRows; ++i)
        totalSum += this.trainY[rows[i]];

      for (int j = 0; j "lt" numColsToUse; ++j)
      {
        int colIdx = colIndices[j];
        for (int i = 0; i "lt" nRows; ++i)
        {
          int r = rows[i];
          sortedRows[i] = r;
          featureKeys[i] = this.trainX[r][colIdx];
        }

        Array.Sort(featureKeys, sortedRows);
        double leftSum = 0.0;

        for (int i = 0; i "lt" nRows - 1; ++i)
        {
          leftSum += this.trainY[sortedRows[i]];

          int leftCount = i + 1;
          int rightCount = nRows - leftCount;

          if (featureKeys[i] == featureKeys[i + 1])
            continue; // duplicate values
          if (leftCount "lt" this.minLeaf ||
            rightCount "lt" this.minLeaf)
            continue;

          double rightSum = totalSum - leftSum;

          // maximizing Gain = (G_L^2 / N_L) + (G_R^2 / N_R)
          // equivalent to maximizing explicit weighted
          // variance reduction
          double gain = (leftSum * leftSum / leftCount) +
            (rightSum * rightSum / rightCount);

          if (gain "gt" bestGain)
          {
            bestGain = gain;
            bestColIdx = colIdx;
            // bestThresh = 
            //  (featureKeys[i] + featureKeys[i + 1]) / 2.0;
            bestThresh = featureKeys[i] +
              (featureKeys[i + 1] - featureKeys[i]) / 2.0;
          }
        }
      }

      // using a tuple would create version dependency
      double[] result = new double[2];
      result[0] = 1.0 * bestColIdx;
      result[1] = bestThresh;
      return result;
    }

    private double TreeTargetMean(List"lt"int"gt" rows)
    {
      if (rows == null || rows.Count == 0)
        return 0.0;
      double sum = 0.0;
      for (int i = 0; i "lt" rows.Count; ++i)
        sum += this.trainY[rows[i]];
      return sum / rows.Count;
    }

  } // DecisionTreeRegressor

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

  

Demo scikit program.

# scikit_gradient_boost_regression.py
# on custom People dataset

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor

# -----------------------------------------------------------

def accuracy(model, data_X, data_y, pct_close):
  # assumes model has a predict(X)
  n = len(data_X)
  n_correct = 0; n_wrong = 0
  for i in range(n):
    x = data_X[i].reshape(1,-1)  # make it a matrix
    y = data_y[i]
    y_pred = model.predict(x)[0]  # predict() expects 2D

    if np.abs(y - y_pred) "lt" np.abs(y) * pct_close:
      n_correct += 1
    else: 
      n_wrong += 1
  # print("Correct = " + str(n_correct))
  # print("Wrong   = " + str(n_wrong))
  return n_correct / (n_correct + n_wrong)

# -----------------------------------------------------------

def MSE(model, data_X, data_y):
  n = len(data_X)
  sum = 0.0
  for i in range(n):
    x = data_X[i].reshape(1,-1)
    y = data_y[i]
    y_pred = model.predict(x)[0]
    sum += (y - y_pred) * (y - y_pred)

  return sum / n

# -----------------------------------------------------------

def main():
  print("\nBegin scikit gradient boost regression demo ")
  print("Predict Income from Sex, Age, City, Height, Psych ")

  np.set_printoptions(precision=4, suppress=True,
    floatmode='fixed')
  np.random.seed(0)  # not used this version

  # 1. load data
  print("\nLoading People train (200), test (400) data ")
 
  train_file = ".\\Data\\People\\people_train_200.txt"
  test_file = ".\\Data\\People\\people_test_40.txt"
  # sex, age, city, height, psych(3)
  x_cols=[0,1,2,3, 4,5,6]
  target_col = 7  # 1.0000 .6413

  train_X = np.loadtxt(train_file, comments="#",
    usecols=x_cols, delimiter=",",  dtype=np.float64)
  train_y = np.loadtxt(train_file, comments="#",
    usecols=target_col, delimiter=",",  dtype=np.float64)

  test_X = np.loadtxt(test_file, comments="#",
    usecols=x_cols, delimiter=",",  dtype=np.float64)
  test_y = np.loadtxt(test_file, comments="#",
    usecols=target_col, delimiter=",",  dtype=np.float64)
  print("Done ")

  print("\nFirst three raw train X: ")
  print("M,29,Austin,68.5,timid,54600.00 ")
  print("F,51,Juneau,60.0,brave,25700.00 ")
  print("M,32,Boston,73.5,alert,74900.00 ")

  print("\nFirst three X predictors: ")
  print(train_X[0:3,:])
  print("\nFirst three y targets: ")
  for i in range(3):
    print("%0.4f" % train_y[i])

  n_estimators = 200
  max_depth = 4
  min_samples_split = 2
  min_samples_leaf = 1
  lrn_rate = 0.05
  seed = 0

  print("\nSetting n_estimators = " + \
    str(n_estimators))
  print("Setting max_depth = " + \
    str(max_depth))
  print("Setting min_samples_split = " + \
    str(min_samples_split))
  print("Setting min_samples_leaf = " + \
    str(min_samples_leaf))
  print("Setting lrn_rate = %0.4f " % lrn_rate)

  # GradientBoostingRegressor(*, loss='squared_error',
  # learning_rate=0.1, n_estimators=100, subsample=1.0,
  # criterion='friedman_mse', min_samples_split=2,
  # min_samples_leaf=1, min_weight_fraction_leaf=0.0,
  # max_depth=3, min_impurity_decrease=0.0, init=None,
  # random_state=None, max_features=None, alpha=0.9,
  # verbose=0, max_leaf_nodes=None, warm_start=False,
  # validation_fraction=0.1, n_iter_no_change=None,
  # tol=0.0001, ccp_alpha=0.0)

  model = \
    GradientBoostingRegressor(max_depth=max_depth, 
    min_samples_split=min_samples_split,
    n_estimators=n_estimators,
    min_samples_leaf=min_samples_leaf,
    learning_rate=lrn_rate, random_state=seed)

  print("\nTraining gradient boost regression model ")
  model.fit(train_X, train_y)
  print("Done " )

  print("\nEvaluating model ")
  acc_train = accuracy(model, train_X, train_y, 0.10)
  print("\nAcc train (within 0.10): %0.4f " % acc_train)
  acc_test = accuracy(model, test_X, test_y, 0.10)
  print("Acc test (within 0.10): %0.4f " % acc_test)

  mse_train = MSE(model, train_X, train_y)
  print("\nMSE train: %0.4f " % mse_train)
  mse_test = MSE(model, test_X, test_y)
  print("MSE test: %0.4f " % mse_test)

  r2_train = model.score(train_X, train_y)
  print("\nR2 train: %0.4f " % r2_train)
  r2_test = model.score(test_X, test_y)
  print("R2 test: %0.4f " % r2_test)

  print("\nPredicting for x = ")
  print(train_X[0])
  pred_y = model.predict(train_X[0].reshape(1,-1))[0]
  print("Predicted y = %0.4f " % pred_y)

  print("\nEnd demo ")

if __name__ == "__main__":
  main()

Training data:

# people_train_200.txt
#
# M = 0,  F = 1
# city_target_encoding = {
#     "Boston": 30600.00,  0
#     "Casper": 33100.00,  1
#     "Austin": 33961.90,  2
#     "Denver": 35535.00,  3
#     "Eugene": 44090.00,  4
#     "Irvine": 51840.00,  5
#     "Fresno": 52950.00,  6
#     "Gallup": 52960.00,  7
#     "Juneau": 58842.86,  8
#     "Helena": 64194.74,  9
# }
# timid = 1,0,0  alert = 0,1,0  brave = 0,0,1
#
# Sex Age City Height Psych Income
#
0, 0.29, 2, 0.685, 1,0,0, 0.54600
1, 0.51, 8, 0.600, 0,0,1, 0.25700
0, 0.32, 0, 0.735, 0,1,0, 0.74900
0, 0.19, 5, 0.610, 0,0,1, 0.30300
1, 0.45, 4, 0.645, 0,1,0, 0.35600
0, 0.58, 6, 0.720, 1,0,0, 0.52100
1, 0.22, 2, 0.630, 0,1,0, 0.27800
0, 0.64, 8, 0.775, 0,0,1, 0.88200
1, 0.38, 3, 0.660, 1,0,0, 0.32400
0, 0.41, 7, 0.700, 0,1,0, 0.51200
1, 0.27, 1, 0.625, 0,0,1, 0.28900
0, 0.53, 9, 0.740, 1,0,0, 0.62300
1, 0.34, 0, 0.650, 0,1,0, 0.33100
0, 0.60, 5, 0.765, 0,0,1, 0.79800
1, 0.21, 4, 0.605, 1,0,0, 0.22400
0, 0.48, 6, 0.690, 0,1,0, 0.48700
1, 0.62, 2, 0.670, 0,0,1, 0.53600
0, 0.25, 8, 0.715, 1,0,0, 0.45200
1, 0.39, 3, 0.640, 0,1,0, 0.31800
0, 0.55, 7, 0.750, 0,0,1, 0.72100
1, 0.31, 1, 0.615, 1,0,0, 0.24500
0, 0.43, 9, 0.705, 0,1,0, 0.52400
1, 0.50, 0, 0.635, 0,0,1, 0.38900
0, 0.28, 5, 0.730, 0,1,0, 0.58100
1, 0.56, 4, 0.665, 1,0,0, 0.47800
0, 0.20, 6, 0.620, 0,0,1, 0.29100
1, 0.42, 2, 0.655, 0,1,0, 0.37200
0, 0.66, 8, 0.780, 1,0,0, 0.85400
1, 0.36, 3, 0.620, 0,0,1, 0.29800
0, 0.49, 7, 0.725, 0,1,0, 0.59300
1, 0.24, 1, 0.600, 0,1,0, 0.21200
0, 0.57, 9, 0.755, 0,0,1, 0.76400
1, 0.46, 0, 0.640, 1,0,0, 0.36100
0, 0.33, 5, 0.680, 0,1,0, 0.44300
1, 0.61, 4, 0.675, 0,0,1, 0.57900
0, 0.23, 6, 0.710, 1,0,0, 0.42800
1, 0.52, 2, 0.630, 0,1,0, 0.39400
0, 0.65, 8, 0.760, 0,0,1, 0.81200
1, 0.30, 3, 0.610, 1,0,0, 0.23100
0, 0.44, 7, 0.695, 0,1,0, 0.49600
1, 0.59, 1, 0.680, 0,0,1, 0.58300
0, 0.35, 9, 0.720, 1,0,0, 0.49800
1, 0.18, 0, 0.605, 0,1,0, 0.21900
0, 0.51, 5, 0.745, 0,0,1, 0.66700
1, 0.40, 4, 0.650, 1,0,0, 0.33500
0, 0.67, 6, 0.785, 0,1,0, 0.94200
1, 0.26, 2, 0.625, 0,0,1, 0.27400
0, 0.37, 8, 0.690, 0,1,0, 0.45100
1, 0.54, 3, 0.660, 1,0,0, 0.43600
0, 0.30, 7, 0.730, 0,0,1, 0.59800
1, 0.47, 1, 0.645, 0,1,0, 0.38200
0, 0.63, 9, 0.770, 1,0,0, 0.83100
1, 0.28, 0, 0.615, 0,0,1, 0.26300
0, 0.42, 5, 0.700, 0,1,0, 0.48900
1, 0.35, 4, 0.635, 1,0,0, 0.28700
0, 0.59, 6, 0.750, 0,0,1, 0.74600
1, 0.20, 2, 0.610, 0,1,0, 0.22800
0, 0.46, 8, 0.720, 1,0,0, 0.53200
1, 0.58, 3, 0.670, 0,1,0, 0.51400
0, 0.31, 7, 0.715, 0,0,1, 0.52900
1, 0.33, 1, 0.620, 1,0,0, 0.25800
0, 0.54, 9, 0.735, 0,1,0, 0.64500
1, 0.41, 0, 0.655, 0,0,1, 0.36700
0, 0.22, 5, 0.630, 1,0,0, 0.26400
1, 0.67, 4, 0.685, 0,1,0, 0.69100
0, 0.38, 6, 0.695, 0,0,1, 0.47900
1, 0.25, 2, 0.600, 1,0,0, 0.19800
0, 0.61, 8, 0.760, 0,1,0, 0.77400
1, 0.43, 3, 0.640, 0,0,1, 0.35200
0, 0.26, 7, 0.700, 1,0,0, 0.43100
1, 0.48, 1, 0.650, 0,1,0, 0.39700
0, 0.50, 9, 0.725, 0,0,1, 0.60200
1, 0.29, 0, 0.620, 1,0,0, 0.24900
0, 0.68, 5, 0.785, 0,1,0, 0.96800
1, 0.37, 4, 0.630, 0,0,1, 0.30600
0, 0.18, 6, 0.615, 1,0,0, 0.25300
1, 0.65, 2, 0.680, 0,1,0, 0.63200
0, 0.34, 8, 0.685, 0,0,1, 0.43800
1, 0.53, 3, 0.655, 1,0,0, 0.41900
0, 0.45, 7, 0.710, 0,1,0, 0.53700
1, 0.23, 1, 0.610, 0,0,1, 0.23600
0, 0.52, 9, 0.740, 1,0,0, 0.61100
1, 0.32, 0, 0.630, 0,1,0, 0.28400
0, 0.62, 5, 0.770, 0,0,1, 0.82500
1, 0.19, 4, 0.600, 1,0,0, 0.20100
0, 0.40, 6, 0.700, 0,1,0, 0.47300
1, 0.60, 2, 0.675, 0,0,1, 0.56800
0, 0.27, 8, 0.695, 1,0,0, 0.41200
1, 0.44, 3, 0.645, 0,1,0, 0.36900
0, 0.56, 7, 0.755, 0,0,1, 0.75300
1, 0.36, 1, 0.635, 1,0,0, 0.29100
0, 0.47, 9, 0.715, 0,1,0, 0.55800
1, 0.55, 0, 0.660, 0,0,1, 0.46200
0, 0.21, 5, 0.625, 0,1,0, 0.28900
1, 0.63, 4, 0.680, 1,0,0, 0.58400
0, 0.39, 6, 0.690, 0,0,1, 0.46100
1, 0.22, 2, 0.615, 1,0,0, 0.21700
0, 0.64, 8, 0.770, 0,1,0, 0.85900
1, 0.38, 3, 0.635, 0,0,1, 0.33800
0, 0.29, 7, 0.705, 1,0,0, 0.45800
1, 0.51, 1, 0.655, 0,1,0, 0.43100
0, 0.58, 9, 0.760, 0,0,1, 0.78200
1, 0.27, 0, 0.610, 0,1,0, 0.24300
0, 0.36, 5, 0.685, 1,0,0, 0.42100
1, 0.66, 4, 0.685, 0,0,1, 0.67500
0, 0.24, 6, 0.640, 0,1,0, 0.31200
1, 0.49, 2, 0.650, 1,0,0, 0.38400
0, 0.60, 8, 0.755, 0,0,1, 0.76900
1, 0.31, 3, 0.625, 0,1,0, 0.26800
0, 0.43, 7, 0.700, 1,0,0, 0.48200
1, 0.57, 1, 0.665, 0,0,1, 0.51900
0, 0.32, 9, 0.690, 0,1,0, 0.43900
1, 0.20, 0, 0.600, 1,0,0, 0.19200
0, 0.53, 5, 0.750, 0,1,0, 0.68400
1, 0.42, 4, 0.640, 0,0,1, 0.36100
0, 0.65, 6, 0.775, 1,0,0, 0.84700
1, 0.28, 2, 0.620, 0,1,0, 0.26200
0, 0.35, 8, 0.680, 1,0,0, 0.39600
1, 0.50, 3, 0.650, 0,0,1, 0.41800
0, 0.25, 7, 0.690, 0,1,0, 0.41300
1, 0.45, 1, 0.640, 1,0,0, 0.34800
0, 0.59, 9, 0.765, 0,1,0, 0.80100
1, 0.33, 0, 0.625, 0,0,1, 0.30200
0, 0.23, 5, 0.635, 0,1,0, 0.29700
1, 0.64, 4, 0.680, 0,0,1, 0.62100
0, 0.41, 6, 0.705, 1,0,0, 0.47100
1, 0.21, 2, 0.605, 0,0,1, 0.21800
0, 0.67, 8, 0.785, 0,1,0, 0.95600
1, 0.37, 3, 0.630, 1,0,0, 0.29200
0, 0.48, 7, 0.720, 0,0,1, 0.59100
1, 0.26, 1, 0.615, 0,1,0, 0.26100
0, 0.55, 9, 0.745, 1,0,0, 0.65800
1, 0.40, 0, 0.645, 0,1,0, 0.34900
0, 0.19, 5, 0.615, 0,0,1, 0.28400
1, 0.68, 4, 0.690, 1,0,0, 0.70200
0, 0.36, 6, 0.680, 0,1,0, 0.42600
1, 0.29, 2, 0.625, 0,0,1, 0.29300
0, 0.62, 8, 0.765, 1,0,0, 0.79300
1, 0.44, 3, 0.640, 0,1,0, 0.35700
0, 0.28, 7, 0.700, 0,0,1, 0.46900
1, 0.52, 1, 0.660, 1,0,0, 0.42100
0, 0.46, 9, 0.710, 0,1,0, 0.53900
1, 0.24, 0, 0.605, 0,0,1, 0.20700
0, 0.57, 5, 0.755, 1,0,0, 0.75100
1, 0.35, 4, 0.630, 0,1,0, 0.29600
0, 0.66, 6, 0.780, 0,0,1, 0.88900
1, 0.30, 2, 0.620, 0,0,1, 0.29700
0, 0.38, 8, 0.690, 1,0,0, 0.42400
1, 0.56, 3, 0.665, 0,1,0, 0.48300
0, 0.27, 7, 0.695, 0,0,1, 0.44800
1, 0.43, 1, 0.635, 1,0,0, 0.32800
0, 0.61, 9, 0.770, 0,1,0, 0.83600
1, 0.31, 0, 0.620, 0,0,1, 0.28100
0, 0.20, 5, 0.620, 1,0,0, 0.24600
1, 0.65, 4, 0.685, 0,1,0, 0.66400
0, 0.42, 6, 0.700, 0,0,1, 0.51200
1, 0.18, 2, 0.600, 0,1,0, 0.18900
0, 0.63, 8, 0.775, 1,0,0, 0.82900
1, 0.39, 3, 0.635, 0,1,0, 0.32600
0, 0.50, 7, 0.725, 0,0,1, 0.63400
1, 0.25, 1, 0.610, 1,0,0, 0.22300
0, 0.54, 9, 0.740, 0,1,0, 0.65200
1, 0.36, 0, 0.635, 1,0,0, 0.29800
0, 0.31, 5, 0.675, 0,0,1, 0.42900
1, 0.58, 4, 0.670, 0,1,0, 0.52800
0, 0.25, 6, 0.645, 1,0,0, 0.31900
1, 0.47, 2, 0.645, 0,0,1, 0.41100
0, 0.59, 8, 0.750, 0,1,0, 0.73800
1, 0.28, 3, 0.615, 1,0,0, 0.23700
0, 0.40, 7, 0.690, 0,1,0, 0.45600
1, 0.54, 1, 0.650, 0,0,1, 0.46300
0, 0.34, 9, 0.695, 1,0,0, 0.43200
1, 0.23, 0, 0.600, 0,1,0, 0.20800
0, 0.52, 5, 0.740, 0,0,1, 0.66100
1, 0.41, 4, 0.640, 1,0,0, 0.32900
0, 0.68, 6, 0.785, 0,1,0, 0.97800
1, 0.26, 2, 0.610, 0,0,1, 0.23800
0, 0.37, 8, 0.685, 0,1,0, 0.40900
1, 0.50, 3, 0.655, 1,0,0, 0.42700
0, 0.29, 7, 0.700, 0,0,1, 0.46300
1, 0.46, 1, 0.640, 0,1,0, 0.35400
0, 0.60, 9, 0.760, 1,0,0, 0.80800
1, 0.28, 0, 0.610, 0,0,1, 0.26800
0, 0.43, 5, 0.705, 0,1,0, 0.47600
1, 0.34, 4, 0.625, 1,0,0, 0.28300
0, 0.56, 6, 0.745, 0,0,1, 0.70400
1, 0.19, 2, 0.600, 0,1,0, 0.17400
0, 0.65, 8, 0.770, 1,0,0, 0.86800
1, 0.42, 3, 0.635, 0,0,1, 0.35900
0, 0.30, 7, 0.695, 0,1,0, 0.42200
1, 0.53, 1, 0.655, 1,0,0, 0.44800
0, 0.47, 9, 0.720, 0,0,1, 0.59400
1, 0.22, 0, 0.600, 0,1,0, 0.19800
0, 0.66, 5, 0.780, 1,0,0, 0.89200
1, 0.38, 4, 0.630, 0,0,1, 0.31800
0, 0.21, 6, 0.620, 0,1,0, 0.27100
1, 0.62, 2, 0.665, 1,0,0, 0.50300
0, 0.33, 8, 0.675, 0,0,1, 0.41200
1, 0.51, 3, 0.650, 0,1,0, 0.41500
0, 0.44, 7, 0.705, 1,0,0, 0.48800

Test data:

# people_test_40.txt
#
1, 0.27, 1, 0.620, 0,0,1, 0.28100
0, 0.58, 9, 0.750, 0,1,0, 0.75900
1, 0.32, 0, 0.625, 1,0,0, 0.25900
0, 0.24, 5, 0.640, 0,0,1, 0.32400
1, 0.60, 4, 0.660, 0,1,0, 0.52100
0, 0.39, 6, 0.685, 1,0,0, 0.42700
0, 0.26, 2, 0.690, 0,1,0, 0.40200
1, 0.49, 8, 0.645, 0,0,1, 0.38800
0, 0.53, 3, 0.730, 1,0,0, 0.61200
1, 0.30, 7, 0.615, 0,1,0, 0.23400
0, 0.61, 1, 0.760, 0,0,1, 0.78900
1, 0.22, 9, 0.610, 1,0,0, 0.21500
0, 0.45, 0, 0.700, 0,1,0, 0.49300
1, 0.57, 5, 0.670, 0,0,1, 0.51100
0, 0.28, 4, 0.695, 1,0,0, 0.40800
1, 0.39, 6, 0.630, 0,1,0, 0.31100
0, 0.51, 2, 0.720, 0,0,1, 0.57400
1, 0.33, 8, 0.625, 1,0,0, 0.26700
0, 0.64, 3, 0.770, 0,1,0, 0.83500
1, 0.18, 7, 0.600, 0,0,1, 0.16200
0, 0.42, 1, 0.695, 1,0,0, 0.46100
1, 0.59, 9, 0.680, 0,1,0, 0.56300
0, 0.36, 0, 0.685, 0,0,1, 0.43400
1, 0.23, 5, 0.610, 1,0,0, 0.22100
0, 0.67, 4, 0.780, 0,1,0, 0.92400
1, 0.45, 6, 0.645, 0,0,1, 0.37800
0, 0.31, 2, 0.700, 1,0,0, 0.43500
1, 0.52, 8, 0.655, 0,1,0, 0.43900
0, 0.20, 3, 0.620, 0,0,1, 0.26200
1, 0.48, 7, 0.650, 1,0,0, 0.37100
0, 0.55, 1, 0.740, 0,1,0, 0.63800
1, 0.29, 9, 0.620, 0,0,1, 0.27300
0, 0.63, 0, 0.765, 1,0,0, 0.80100
1, 0.41, 5, 0.640, 0,1,0, 0.34200
0, 0.22, 4, 0.630, 0,0,1, 0.28600
1, 0.64, 6, 0.675, 1,0,0, 0.61400
0, 0.37, 2, 0.690, 0,1,0, 0.44100
1, 0.50, 8, 0.650, 0,0,1, 0.42200
0, 0.27, 3, 0.695, 1,0,0, 0.39800
1, 0.44, 7, 0.640, 0,1,0, 0.33900
This entry was posted in Machine Learning. Bookmark the permalink.

Leave a Reply