Introduction to Classifier Uncertainty
The decision function for multiclass classification has shape (n_samples, n_classes), with one certainty score per class for each data point.
Why a Classifier Can Say More Than Its Answer
A classifier that predicts one of several classes does not have to reveal only its final answer. Behind each prediction it has scored every possible class, and some classifiers expose those internal scores through a method called decision_function. Instead of asking only what the classifier predicts, this article asks what the classifier is weighing when it makes that prediction.
The running setting is a gradient boosted classifier trained on the Iris dataset, which has three classes. After training, we ask this classifier for its decision function on the test set. Before reading the numbers, predict what kind of output you expect: one number per data point, one number per class, or something else.
What do you think happens?
For a test set of several flowers and three classes, how many numbers do you expect decision_function to return for each flower?
Reveal answer
Three numbers, one score per class
In the multiclass case the decision function has shape (n_samples, n_classes), so each data point gets one certainty score for every class. With three classes, each row has three values.
Reading the Shape of the Scores
The decision function for multiclass classification has shape $(n_{\text{samples}}, n_{\text{classes}})$. Each row corresponds to one data point, and each column corresponds to one class. The value stored in a given cell is a certainty score for that class on that data point.
Column order follows the class labels the classifier learned. Column 0 holds the score for class 0, column 1 for class 1, and column 2 for class 2. The number of columns tells you how many classes exist, and the column positions tell you which label each score belongs to. Keep this mapping in mind whenever you debug, because a score in the wrong column is a labeling error, not a model error.
Scores as Certainty Within a Row
A larger certainty score means a class is more likely, and a smaller score means it is less likely. To interpret a data point, read its row one at a time and compare the scores across its columns. The class whose column holds the largest value is the one the classifier considers most likely for that point.
The example row above is a made-up row, chosen only to practice reading scores. It is not output from a real model. Class 2 has the largest value, so it is the class this row favors. Notice that the rule is comparative. The raw values -1.2 and 0.4 are not wrong or invalid simply because they are below some cutoff; they are simply smaller than 2.1 in the same row.
Recovering the Predicted Class with argmax
Because the largest entry in each row marks the most likely class, predictions can be recovered directly from the decision function. You find the index of the maximum entry for each data point. The NumPy call np.argmax(scores, axis=1) does this. It returns, for every row, the column index where that row's maximum sits.
- scores has shape (
n_samples,n_classes); one row per test point - axis=1 takes the maximum across each row, i.e. across the classes
- comparing with gbrt.predict checks that the recovered classes agree entry for entry
The source runs this same kind of check: it prints the argmax of the decision function and the output of gbrt.predict on the same test set, and the two arrays match entry for entry. For this classifier, then, the predicted class is the column with the maximum certainty score in each row. The mapping from column index back to class label works here because the classes are labeled 0, 1, and 2 in the order the classifier learned them.
Recovering one prediction by hand
A single data point has the decision function row [-1.2, 0.4, 2.1]. Which class does the classifier predict for this point?
- 1Identify the row
The row has three values, one per class, so this is one data point in a three-class problem.
- 2Find the maximum
The largest value is 2.1, which sits in column 2.
- 3Apply argmax along axis 1
np.argmaxover this row returns the index 2, the column where the maximum sits. - 4Map the index to a class
Column 2 is the score for class 2, so the predicted class for this point is class 2.
The predicted class is 2, because column 2 holds the largest certainty score in the row.
Decision Function Versus predict_proba
Another method, predict_proba, also returns one value per class for each data point. Its output has the same shape as the decision function, $(n_{\text{samples}}, n_{\text{classes}})$, so the two outputs are laid out the same way. They are not the same quantity, though. The decision function gives certainty scores, while predict_proba gives probabilities whose values for each data point sum to 1. The source does not state that the decision function's values sum to 1, so do not assume it.
Mistakes When Reading Decision Function Output
Common mistake Taking the argmax across the wrong axis
What it looks likeCalling np.argmax(scores) with no axis argument, which searches the whole array rather than each row.
Why it failsThe prediction is defined per data point, so the maximum must be found within each row across the classes.
Do this insteadUse np.argmax(scores, axis=1) so that each row returns the index of its own largest score.
Common mistake Reading a column index as the class label without checking the mapping
What it looks likeAssuming column 2 always means class 2 without checking which labels the classifier learned.
Why it failsColumn order follows the learned class labels, so the mapping from column to label should be confirmed rather than assumed.
Do this insteadConfirm the learned class order before translating an argmax index into a label.
Common mistake Treating decision function values as probabilities
What it looks likeConcluding that a score of 2.1 means a 210 percent chance of a class.
Why it failsThe decision function gives certainty scores that are compared within a row. The source does not describe them as probabilities.
Do this insteadCompare scores within the row to find the most likely class, and use predict_proba if you need probabilities that sum to 1.
Common mistake Judging a score only by whether it is above a fixed cutoff instead of comparing it with the other scores in its row
Debugging a Two-Step Pipeline
The decision function followed by argmax is a two-step pipeline, so you can check each step separately when a prediction looks wrong. First confirm the shape of the decision function. Then confirm that the argmax runs across axis 1. Finally, compare the recovered predictions with gbrt.predict on the same data. If the two arrays disagree, the divergence lies in the argmax step or in how the rows were prepared, not in the classifier itself.
A colleague reports that their recovered predictions do not match gbrt.predict. Their decision function has shape (n_samples, 3), and they call np.argmax(scores) without an axis argument. Using the three-step check described above, which step is most likely the source of the mismatch, and what single change would fix it?
Hints
- Start with the shape check. Does the shape look correct for a three-class problem?
- Then look at the axis argument. What does omitting axis do to a two-dimensional array?
- The fix is a change to the argmax call, not to the classifier.
Key Takeaways
- The decision function for a multiclass classifier has shape (
n_samples,n_classes): each row is one data point and each column is one class. - Each cell is a certainty score, and a larger score means a class is more likely, so you compare scores within each row rather than against a fixed threshold.
np.argmax(scores, axis=1)returns the column index of the largest score in each row, which for this classifier matched gbrt.predict in the source example.predict_probahas the same shape as the decision function but gives per-data-point probabilities that sum to 1, while the decision function is not stated to do so, so the two should not be interpreted the same way.