using confusion matrix as scoring metric in cross validation in scikit learn
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A confusion matrix is one of the most useful ways to inspect a classifier, because it shows exactly where predictions land: true positives, false positives, true negatives, and false negatives. The catch in scikit-learn is that cross-validation APIs usually expect scalar scores, while a confusion matrix is a table.
Why a Confusion Matrix Does Not Fit cross_val_score
The scoring argument in cross_val_score is designed for values that can be compared and averaged across folds. Metrics such as accuracy, recall, and ROC AUC work well because each fold produces one number. A confusion matrix produces multiple counts, so there is no obvious built-in way to average it inside cross_val_score.
That means this does not do what most people want:
confusion_matrix expects y_true and y_pred, not an estimator, features, and labels. More importantly, it returns an array rather than a single score.
The Practical Options
You usually want one of three patterns.
The first pattern is to score with a scalar derived from the confusion matrix. For example, precision, recall, specificity, balanced accuracy, and F1 are all fold-friendly.
The second pattern is to collect confusion matrix terms per fold with cross_validate, which supports callables returning a dictionary of named scores.
The third pattern is to generate out-of-fold predictions with cross_val_predict and then build one confusion matrix from those predictions. That gives you a single matrix for the entire dataset without evaluating on the training data.
Option 1: Use a Derived Scalar Metric
If your real goal is model selection, a scalar metric is usually best:
This is the cleanest choice when you need a single number for comparison or hyperparameter search.
Option 2: Return Confusion Matrix Counts Per Fold
If you specifically want confusion matrix information from each fold, use cross_validate with a custom callable:
This gives you the raw ingredients. From there you can compute sensitivity, specificity, or any business-specific cost formula.
Option 3: Build One Honest Confusion Matrix With Out-of-Fold Predictions
This is often the most interpretable approach for reports:
Each prediction is produced by a model that did not see that sample during training, so the matrix is much more honest than fitting once on the full dataset and evaluating on the same data.
Common Pitfalls
Trying to pass confusion_matrix directly as scoring is the most common mistake. The scoring API expects either a named scorer or a callable with the estimator-style signature.
Another mistake is averaging confusion matrices without thinking about class balance. If fold sizes differ, summing counts across folds is usually more meaningful than averaging raw cells.
Multiclass problems also trip people up. In binary classification, ravel() works because the matrix is 2 x 2. In multiclass classification, the matrix is larger, so write code that handles the full array instead of unpacking four values.
Finally, remember the difference between model selection and diagnosis. If you need to rank models, use a scalar metric. If you need to understand failure modes, inspect the confusion matrix after you have a sensible evaluation workflow.
Summary
- A confusion matrix is not a direct drop-in scorer for
cross_val_scorebecause it is not a single scalar. - Use derived scalar metrics such as recall or F1 when you need one number per fold.
- Use
cross_validatewith a custom callable if you want fold-wise confusion matrix counts. - Use
cross_val_predictwhen you want one aggregate confusion matrix built from out-of-fold predictions. - Treat binary and multiclass cases differently, especially if you unpack matrix cells.

