Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MNT Add validation for parameter alphas in RidgeCV #21606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b73561
MNT Add validation of scalar parameters in Ridge
Nov 4, 2021
4a150f6
Add test for validation of `alphas` in `RidgeCV`
Nov 4, 2021
6960fc7
Merge branch 'main' into validations_ridgecv
Nov 17, 2021
cf907ba
Add validation when cv is not None
Nov 18, 2021
c76ac26
Modify test to include case when cv is not None
Nov 18, 2021
c364401
Add tests for `alphas` validation in `RidgeClassifierCV`
Nov 19, 2021
a781866
Merge branch 'main' into validations_ridgecv
Nov 19, 2021
63623b5
Use from instead of specific types
Nov 23, 2021
b0663a9
DOC Add whatsnew entry
Nov 26, 2021
881af91
Factorize `check_scalar` out of `_RidgeGCV` and simplify code
Nov 26, 2021
26a478b
tweak
Dec 1, 2021
f31c4bc
Improve readability of tests
Dec 1, 2021
5745ecf
Merge branch 'main' into validations_ridgecv
Dec 1, 2021
9cf3647
Fix doc format
Dec 1, 2021
6def00e
Merge branch 'main' into validations_ridgecv
glemaitre Dec 1, 2021
f573c5c
Merge tests for `RidgeCV` and `RidgeClassifierCV`
Dec 1, 2021
5e7605a
Merge branch 'main' into validations_ridgecv
Dec 1, 2021
06ee9fb
Update doc/whats_new/v1.1.rst
ArturoAmorQ Dec 1, 2021
01c0017
Create local variable to avoid mutation inside of fit
ArturoAmorQ Dec 1, 2021
9bf14a7
Merge branch 'validations_ridgecv' of github.com:ArturoAmorQ/scikit-l…
Dec 1, 2021
ca7f604
Revert use collections.abc instead of specific types
Dec 1, 2021
4626c45
Improve test
ArturoAmorQ Dec 1, 2021
3489815
Improve test name
Dec 1, 2021
1950fac
Add test to ensure backward compatibility
Dec 1, 2021
dcbd739
Remove case of alphas being a 0d-array
Dec 1, 2021
0997b79
Merge branch 'validations_ridgecv' of github.com:ArturoAmorQ/scikit-l…
Dec 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/whats_new/v1.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ Changelog
multilabel classification.
:pr:`19689` by :user:`Guillaume Lemaitre <glemaitre>`.

- |Enhancement| :class:`linear_model.RidgeCV` and
:class:`linear_model.RidgeClassifierCV` now raise consistent error message
when passed invalid values for `alphas`.
:pr:`21606` by :user:`Arturo Amor <ArturoAmorQ>`.

- |Enhancement| :class:`linear_model.Ridge` and :class:`linear_model.RidgeClassifier`
now raise consistent error message when passed invalid values for `alpha`,
`max_iter` and `tol`.
Expand Down
33 changes: 25 additions & 8 deletions sklearn/linear_model/_ridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


from abc import ABCMeta, abstractmethod
from functools import partial
import warnings

import numpy as np
Expand Down Expand Up @@ -1864,12 +1865,6 @@ def fit(self, X, y, sample_weight=None):

self.alphas = np.asarray(self.alphas)

if np.any(self.alphas <= 0):
raise ValueError(
"alphas must be strictly positive. Got {} containing some "
"negative or null value instead.".format(self.alphas)
)

X, y, X_offset, y_offset, X_scale = LinearModel._preprocess_data(
X,
y,
Expand Down Expand Up @@ -2038,9 +2033,30 @@ def fit(self, X, y, sample_weight=None):
the validation score.
"""
cv = self.cv

check_scalar_alpha = partial(
check_scalar,
target_type=numbers.Real,
min_val=0.0,
include_boundaries="neither",
)

if isinstance(self.alphas, (np.ndarray, list, tuple)):
n_alphas = 1 if np.ndim(self.alphas) == 0 else len(self.alphas)
if n_alphas != 1:
for index, alpha in enumerate(self.alphas):
alpha = check_scalar_alpha(alpha, f"alphas[{index}]")
else:
self.alphas[0] = check_scalar_alpha(self.alphas[0], "alphas")
else:
# check for single non-iterable item
self.alphas = check_scalar_alpha(self.alphas, "alphas")

alphas = np.asarray(self.alphas)

if cv is None:
estimator = _RidgeGCV(
self.alphas,
alphas,
fit_intercept=self.fit_intercept,
normalize=self.normalize,
scoring=self.scoring,
Expand All @@ -2059,7 +2075,8 @@ def fit(self, X, y, sample_weight=None):
raise ValueError("cv!=None and store_cv_values=True are incompatible")
if self.alpha_per_target:
raise ValueError("cv!=None and alpha_per_target=True are incompatible")
parameters = {"alpha": self.alphas}

parameters = {"alpha": alphas}
solver = "sparse_cg" if sparse.issparse(X) else "auto"
model = RidgeClassifier if is_classifier(self) else Ridge
gs = GridSearchCV(
Expand Down
54 changes: 43 additions & 11 deletions sklearn/linear_model/tests/test_ridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1270,19 +1270,51 @@ def test_ridgecv_int_alphas():
ridge.fit(X, y)


def test_ridgecv_negative_alphas():
X = np.array([[-1.0, -1.0], [-1.0, 0], [-0.8, -1.0], [1.0, 1.0], [1.0, 0.0]])
y = [1, 1, 1, -1, -1]
@pytest.mark.parametrize("Estimator", [RidgeCV, RidgeClassifierCV])
@pytest.mark.parametrize(
"params, err_type, err_msg",
[
({"alphas": (1, -1, -100)}, ValueError, r"alphas\[1\] == -1, must be > 0.0"),
(
{"alphas": (-0.1, -1.0, -10.0)},
ValueError,
r"alphas\[0\] == -0.1, must be > 0.0",
),
(
{"alphas": (1, 1.0, "1")},
TypeError,
r"alphas\[2\] must be an instance of <class 'numbers.Real'>, not <class"
r" 'str'>",
),
],
)
def test_ridgecv_alphas_validation(Estimator, params, err_type, err_msg):
"""Check the `alphas` validation in RidgeCV and RidgeClassifierCV."""

# Negative integers
ridge = RidgeCV(alphas=(-1, -10, -100))
with pytest.raises(ValueError, match="alphas must be strictly positive"):
ridge.fit(X, y)
n_samples, n_features = 5, 5
X = rng.randn(n_samples, n_features)
y = rng.randint(0, 2, n_samples)

# Negative floats
ridge = RidgeCV(alphas=(-0.1, -1.0, -10.0))
with pytest.raises(ValueError, match="alphas must be strictly positive"):
ridge.fit(X, y)
with pytest.raises(err_type, match=err_msg):
Estimator(**params).fit(X, y)


@pytest.mark.parametrize("Estimator", [RidgeCV, RidgeClassifierCV])
def test_ridgecv_alphas_scalar(Estimator):
"""Check the case when `alphas` is a scalar.
This case was supported in the past when `alphas` where converted
into array in `__init__`.
We add this test to ensure backward compatibility.
"""

n_samples, n_features = 5, 5
X = rng.randn(n_samples, n_features)
if Estimator is RidgeCV:
y = rng.randn(n_samples)
else:
y = rng.randint(0, 2, n_samples)

Estimator(alphas=1).fit(X, y)


def test_raises_value_error_if_solver_not_supported():
Expand Down