Predicted Probabilities in Multiclass Classification
For a multiclass classifier, decision_function returns an array of shape (n_samples, n_classes), where each column gives a certainty score for one class.
Asking a Model How Strongly It Favors Each Class
Once a classifier has been trained, you usually ask it one of two questions. The first is which class a data point belongs to. The second is how strongly the model favors each possible class. The predict method answers the first question with a single label per sample. The predicted probabilities and decision scores answer the second question with a set of numbers, one per class. This article focuses on that second kind of output, because it carries more information than the final label and because understanding it makes the predicted label easier to explain.
The running example comes from the Iris dataset, which has three classes of flower. The data is split into training and test portions, and a GradientBoostingClassifier is fitted on the training portion. The output you will read is produced by calling predict_proba on the test portion of that trained model.
What do you think happens?
One test sample receives three probabilities, 0.107, 0.784, and 0.109, one for each class. Which class would the model report for this sample if it predicted from these values alone?
Reveal answer
The middle class, at column 1
The largest value, 0.784, sits in the middle column. For a single row, the model reports the class whose column holds the largest value. The other two classes still receive some probability, but they are not the reported class.
Reading the decision_function Grid
For a multiclass classifier, decision_function returns an array of shape (n_samples, n_classes). Each row belongs to one sample, and each column belongs to one class. Each entry is a certainty score: the score in row i and column k says how strongly the model's output for sample i points toward class k. Because the classes lie along the second dimension of the array, the column axis is the one that indexes classes, and the row axis indexes samples.
The key to reading a decision score is that a larger value means the class is more likely for that data point. Scores are compared within a row, not read as absolute values. Within one sample's row, the column with the highest score is the class the model leans toward most. The scores are not restricted to a fixed range, and they do not need to add up to any particular total. A row with one column well above the others means the model is decisive about that sample. A row where the top two columns are close together means the model is less decisive. Negative values are normal and do not signal anything wrong by themselves; only the ordering within the row matters for the comparison.
Imagine three test flowers scored on three classes. For the first flower, one column stands far above the other two, and the other two values are negative. For the second flower, the top two columns are nearly equal. Reading each row against itself gives you the model's preference: the first flower is a clear case for the top column, while the second flower is a borderline case between two classes. The raw numbers themselves were never compared across rows.
Recovering a Label with argmax Along Columns
A row of scores is informative, but often you want one label per sample. The predicted class for each data point is the column with the maximum entry in that row. NumPy's argmax function returns the index of the maximum, and its axis argument tells it which direction to search. To search across the columns for each row, use axis=1, because the classes lie along the second dimension of the array.
Recovering predicted columns for three samples
Three samples each receive three decision scores. Find the predicted column for each sample using argmax across columns. The numbers are illustrative and are not taken from the Iris model.
- 1Lay out the scores
Sample 0 has scores [-0.4, 1.2, 0.1]. Sample 1 has scores [0.3, -0.9, 0.5]. Sample 2 has scores [-1.1, -0.2, 0.8].
- 2Search each row across columns
For sample 0, the largest value is 1.2, at column index 1. For sample 1, the largest value is 0.5, at column index 2. For sample 2, the largest value is 0.8, at column index 2.
- 3Collect the winning positions
Applying argmax with axis=1 gives the array [1, 2, 2], one column position per sample.
- 4Recognize that positions are not labels
The values 1 and 2 are column positions. They become class names only after they are mapped through classes_, which is covered below.
The predicted column positions are [1, 2, 2].
decision_functionreturns one row per sample and one column per class- argmax with axis=1 searches across the columns of each row
- classes_ converts column positions into the real labels the model learned
- Comparing against predict checks that the argmax reproduces the model's own predicted labels
Reading predict_proba Rows as Distributions
predict_proba returns an array of shape (n_samples, n_classes), with one row per sample and one column per class. The rows are probabilities, and for each sample they add up to 1. In mathematical terms, for sample i with K classes, the entries satisfy $\sum_{k=1}^{K} p_{ik} = 1$, so each row can be read as a distribution over the classes.
In the source run, calling predict_proba on the first six test samples produces six rows with three values each. The first row contains values near 0.107, 0.784, and 0.109 across the three columns. The sample is most strongly associated with the middle class, but the other two classes still receive some probability. Summing each row gives 1, which is the central property of this output.
- The shape is (6, 3) for six samples and three classes
- Summing along axis=1 checks that each row adds up to 1
- argmax along axis=1 recovers the most probable column for each sample
Mapping Column Positions Back with classes_
The argmax gives you a column index, not a class name. A column index is meaningful only if you know which class each column stands for. A fitted classifier stores the class labels it learned in an attribute called classes_. The position of each label in classes_ corresponds to the position of the matching column in the output. To turn a winning column into a real label, index classes_ with the argmax result.
| Column position | Entry in classes_ | Meaning for sample 0 |
|---|---|---|
| 0 | classes_[0] | score or probability for the first class |
| 1 | classes_[1] | score or probability for the second class |
| 2 | classes_[2] | score or probability for the third class |
Column positions map to labels only through classes_, which is generated here to show the pattern.
This mapping is easy to skip. When the labels are plain integers that start at 0 and run consecutively, positions and labels look identical, so it can seem unnecessary to consult classes_. The source warns that the mapping must be used especially for string labels or non-consecutive integers. In those cases, using the raw argmax as if it were a label produces wrong answers without any error message.
decision_function versus predict_proba Shapes
In the multiclass case, decision_function and predict_proba have the same shape. Both return one row per sample and one column per class. What differs is the meaning of the numbers. predict_proba values are probabilities that sum to 1 within each row. decision_function values are raw certainty scores whose only reliable information is their relative ordering within a row.
| Property | decision_function (multiclass) | predict_proba (multiclass) |
|---|---|---|
| Shape | (n_samples, n_classes) | (n_samples, n_classes) |
| Meaning of values | Raw certainty scores | Probabilities |
| Row constraint | No fixed total | Each row sums to 1 |
| Recovering the label | argmax along axis=1, then classes_ | argmax along axis=1, then classes_ |
The two outputs share a shape and a recovery procedure, but their values mean different things.
Mistakes When Reading Multiclass Output
Common mistake Treating the argmax position as the class label
What it looks likePrinting np.argmax(proba, axis=1) and reporting 1 as the predicted class.
Why it failsargmax returns a column position. The position says nothing about the label unless it is mapped through classes_.
Do this insteadIndex classes_ with the argmax result, for example model.classes_[np.argmax(proba, axis=1)].
Common mistake Searching across rows instead of across columns
What it looks likeCalling np.argmax(scores, axis=0) and expecting one prediction per sample.
Why it failsaxis=0 searches down each column, which compares samples against each other for a single class. Classes lie along the second dimension.
Do this insteadUse axis=1 so the search runs across the columns of each row.
Common mistake Assuming decision scores sum to 1
What it looks likeConverting decision scores to probabilities by reading them as if each row totals 1.
Why it failsDecision scores have no fixed total. Only their relative ordering within a row carries meaning.
Do this insteadUse predict_proba when you need values that sum to 1, or compare decision scores only within each row.
Common mistake Skipping classes_ because the labels look like positions
What it looks likeAssuming column 2 always means the label 2 when the model was trained on string labels.
Why it failsclasses_ is the only reliable record of which label sits in which column, and it matters most when labels are strings or non-consecutive integers.
Do this insteadAlways map positions through classes_ before reporting a class name.
Common mistake Applying the multiclass argmax recipe to the binary decision_function output
Why it failsThe binary
decision_functionreturns a single column for classes_[1], so there are no competing columns to compare.Do this insteadInterpret the single column directly, and remember that the positive class is classes_[1].
Predicting the Winning Column for New Rows
A fitted three-class model returns the following predict_proba row for one new sample: 0.22, 0.31, 0.47. Without running any code, state the column position that argmax would return for this row, and explain what must be done before you can report a class name.
Hints
- Find the largest value in the row and count its column position starting from 0.
- The position is only half the answer. Consider what classes_ provides.
Explain, in your own words, why a decision score of 2.0 in one row does not by itself prove that the class is more likely than a score of 1.0 in a different row for another sample. Then explain why comparing the two scores within the same row is meaningful.
Hints
- Consider whether scores are comparable across rows.
- Recall that the ordering within a row is what the model is comparing.
Key Takeaways
decision_functionreturns an array of shape (n_samples,n_classes) in the multiclass case, where rows are samples and columns are classes, and each entry is a certainty score.- A larger score in a row means the class is more likely for that sample, so scores are compared within each row rather than read as absolute values.
- argmax along axis=1 recovers the predicted column for each sample from either
decision_functionorpredict_proba, and that column must be mapped through classes_ to obtain a real label. predict_probahas the same shape asdecision_function, but its rows are probabilities that sum to 1, while decision scores carry only relative ordering; in the binary casedecision_functionreturns a single column for classes_[1].