Skip to content

Multi-class classification

Classification is about predicting an outcome from a fixed list of classes. The prediction is a probability distribution that assigns a probability to each possible outcome.

A labeled classification sample is made up of a bunch of features and a class. The class is a usually a string or a number in the case of multiclass classification. We'll use the image segments dataset as an example.

from river import datasets

dataset = datasets.ImageSegments()
dataset


Image segments classification.

This dataset contains features that describe image segments into 7 classes: brickface, sky,
foliage, cement, window, path, and grass.

    Name  ImageSegments                                               
    Task  Multi-class classification                                  
 Samples  2,310                                                       
Features  18                                                          
 Classes  7                                                           
  Sparse  False                                                       
    Path  /home/runner/work/river/river/river/datasets/segment.csv.zip

This dataset is a streaming dataset which can be looped over.

for x, y in dataset:
    pass

Let's take a look at the first sample.

x, y = next(iter(dataset))
x


{
    'region-centroid-col': 218,
    'region-centroid-row': 178,
    'short-line-density-5': 0.11111111,
    'short-line-density-2': 0.0,
    'vedge-mean': 0.8333326999999999,
    'vegde-sd': 0.54772234,
    'hedge-mean': 1.1111094,
    'hedge-sd': 0.5443307,
    'intensity-mean': 59.629630000000006,
    'rawred-mean': 52.44444300000001,
    'rawblue-mean': 75.22222,
    'rawgreen-mean': 51.22222,
    'exred-mean': -21.555555,
    'exblue-mean': 46.77778,
    'exgreen-mean': -25.222220999999998,
    'value-mean': 75.22222,
    'saturation-mean': 0.31899637,
    'hue-mean': -2.0405545
}
y


'path'

A multiclass classifier's goal is to learn how to predict a class y from a bunch of features x. We'll attempt to do this with a decision tree.

from river import tree

model = tree.HoeffdingTreeClassifier()
model.predict_proba_one(x)


{}

The reason why the output dictionary is empty is because the model hasn't seen any data yet. It isn't aware of the dataset whatsoever. If this were a binary classifier, then it would output a probability of 50% for True and False because the classes are implicit. But in this case we're doing multiclass classification.

Likewise, the predict_one method initially returns None because the model hasn't seen any labeled data yet.

print(model.predict_one(x))
None

If we update the model and try again, then we see that a probability of 100% is assigned to the 'path' class because that's the only one the model is aware of.

model.learn_one(x, y)
model.predict_proba_one(x)


{'path': 1.0}

This is a strength of online classifiers: they're able to deal with new classes appearing in the data stream.

Typically, an online model makes a prediction, and then learns once the ground truth reveals itself. The prediction and the ground truth can be compared to measure the model's correctness. If you have a dataset available, you can loop over it, make a prediction, update the model, and compare the model's output with the ground truth. This is called progressive validation.

from river import metrics

model = tree.HoeffdingTreeClassifier()

metric = metrics.ClassificationReport()

for x, y in dataset:
    y_pred = model.predict_one(x)
    model.learn_one(x, y)
    if y_pred is not None:
        metric.update(y, y_pred)

metric


            Precision   Recall   F1       Support

brickface      77.13%   84.85%   80.81%       330  
   cement      78.92%   83.94%   81.35%       330  
  foliage      65.69%   20.30%   31.02%       330  
    grass     100.00%   96.97%   98.46%       330  
     path      90.63%   91.19%   90.91%       329  
      sky      99.08%   98.18%   98.63%       330  
   window      43.50%   67.88%   53.02%       330

    Macro      79.28%   77.62%   76.31%            
    Micro      77.61%   77.61%   77.61%            
 Weighted      79.27%   77.61%   76.31%

                  77.61% accuracy

This is a common way to evaluate an online model. In fact, there is a dedicated evaluate.progressive_val_score function that does this for you.

from river import evaluate

model = tree.HoeffdingTreeClassifier()
metric = metrics.ClassificationReport()

evaluate.progressive_val_score(dataset, model, metric)


            Precision   Recall   F1       Support

brickface      77.13%   84.85%   80.81%       330  
   cement      78.92%   83.94%   81.35%       330  
  foliage      65.69%   20.30%   31.02%       330  
    grass     100.00%   96.97%   98.46%       330  
     path      90.63%   91.19%   90.91%       329  
      sky      99.08%   98.18%   98.63%       330  
   window      43.50%   67.88%   53.02%       330

    Macro      79.28%   77.62%   76.31%            
    Micro      77.61%   77.61%   77.61%            
 Weighted      79.27%   77.61%   76.31%

                  77.61% accuracy