scikit-learn / scikit-learn/scikit-learn

[BUG] RidgeClassifierCV(scoring="accuracy", cv=None) returns incorrect best_score_ for binary classification

Open
#34,942 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug module:linear_model
Dominant language
Python
Stars
67.3k
Forks
27.4k
Avg merge
1d 15h
Merged PRs (30d)
58

Description

Introduce yourself

hello, I am a professor of computer science and a core developer at aeon . We use RidgeClassifierCV as a classifier head for many of our transformation based pipelines and ensembles, particularly the convolution based ones, and this bug was found by our core dev Matthew (@MatthewMiddlehurst) when reviewing [this PR] (https://github.com/aeon-toolkit/aeon/pull/3655) that adapted one of these pipeline classifiers, the Arsenal, which is a component of our HIVE-COTEv2 classifier and is one of my finest moments in algorithm naming :)

Describe the bug and give evidence about its user-facing impact

RidgeClassifierCV(scoring="accuracy", cv=None) computes an incorrect best_score_ for binary classification problems: it always returns accuracy of 1.0, even when the problem contains substantial label noise and the fitted classifier is clearly not perfect.

We use best_score_ as an estimate of each ensemble member's classification accuracy and use it as an ensemble weight. For multiclass problems this appears to work as expected. For binary problems, however, all ensemble members receive a weight of 1.0, effectively disabling the weighting mechanism.

The behaviour appears specific to the default cv=None path. Using an explicit cross-validation splitter such as cv=5 gives plausible accuracy scores.

Looking at the implementation, the cause may be the classification scoring code in the GCV path reconstructing class labels using y.argmax(axis=1). For binary classification, LabelBinarizer produces a single-column target, so argmax(axis=1) is always zero.

Steps/Code to Reproduce
import numpy as np
from sklearn.linear_model import RidgeClassifierCV
rng = np.random.RandomState(0)
X = rng.normal(size=(200, 20))
y = rng.randint(0, 2, size=200)

clf_loo = RidgeClassifierCV( alphas=[0.1, 1.0, 10.0], scoring="accuracy", ).fit(X, y)
clf_cv = RidgeClassifierCV( alphas=[0.1, 1.0, 10.0], scoring="accuracy", cv=5, ).fit(X, y)
print("cv=None best_score_:", clf_loo.best_score_)
print("cv=5 best_score_:", clf_cv.best_score_)
print("training accuracy:", clf_loo.score(X, y))

import sklearn;
sklearn.show_versions()

Outputs

cv=None best_score_: 1.0
cv=5 best_score_: 0.515
training accuracy: 0.65

on random data.

Expected Results

an estimate of accuracy specific to the training data, not always 1.0.

chatgpt traced this back and thinks it was introduced in 0.23 by PR #14848, merged on 6 January 2020. I have not verified this myself.

Actual Results

For binary classification with cv=None, best_score_ is always reported as 1.0

Versions
System:
    python: 3.12.10 (tags/v3.12.10:0cc8128, Apr  8 2025, 12:21:36) [MSC v.1943 64 bit (AMD64)]
executable: C:\Code\tsml-eval\venv\Scripts\python.exe
   machine: Windows-11-10.0.26200-SP0

Python dependencies:
      sklearn: 1.7.2
          pip: 26.0.1
   setuptools: 82.0.1
        numpy: 2.2.6
        scipy: 1.15.3
       Cython: 3.1.3
       pandas: 2.2.3
   matplotlib: 3.10.5
       joblib: 1.5.1
threadpoolctl: 3.6.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libscipy_openblas
       filepath: C:\Code\tsml-eval\venv\Lib\site-packages\numpy.libs\libscipy_openblas64_-13e2df515630b4a41f92893938845698.dll
        version: 0.3.29
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: vcomp
       filepath: C:\Code\tsml-eval\venv\Lib\site-packages\sklearn\.libs\vcomp140.dll
        version: None

       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libscipy_openblas
       filepath: C:\Code\tsml-eval\venv\Lib\site-packages\scipy.libs\libscipy_openblas-f07f5a5d207a3a47104dca54d6d0c86a.dll
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell
Interest in fixing the bug

Yes I would be happy to contribute, as would @MatthewMiddlehurst

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the RidgeClassifierCV classification scoring path used when cv=None and reproduce the supplied binary-classification example with scoring="accuracy". Check how the best_score_ value is derived for the single-column target, then add regression coverage showing that it reflects the cross-validation accuracy rather than always being 1.0.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
machine-learning
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.