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
[MRG] Fix selection of solver in ridge_regression when solver=='auto' #13363
Changes from 18 commits
6877342
39a387b
62afd26
d3e085f
596ae7f
b0bfbe1
fdfbc50
73bbd91
70f7560
b494e76
3d44669
9848429
81573aa
a2473ff
10c116c
05ba627
90a7e56
97a5326
0619013
64d21df
1d7ca02
604f7d1
79d04d6
9e1aefb
d4cd280
4ed33b5
File filter...
Jump to…
@@ -368,11 +368,25 @@ def _ridge_regression(X, y, alpha, sample_weight=None, solver='auto', | ||
return_n_iter=False, return_intercept=False, | ||
X_scale=None, X_offset=None): | ||
|
||
if return_intercept and sparse.issparse(X) and solver != 'sag': | ||
if solver != 'auto': | ||
warnings.warn("In Ridge, only 'sag' solver can currently fit the " | ||
"intercept when X is sparse. Solver has been " | ||
"automatically changed into 'sag'.") | ||
has_sw = sample_weight is not None | ||
This conversation was marked as resolved
by GaelVaroquaux
GaelVaroquaux
Member
|
||
|
||
if solver == 'auto': | ||
if return_intercept: | ||
# only sag supports fitting intercept directly | ||
solver = "sag" | ||
elif not sparse.issparse(X): | ||
solver = "cholesky" | ||
else: | ||
solver = "sparse_cg" | ||
|
||
if solver not in ('sparse_cg', 'cholesky', 'svd', 'lsqr', 'sag', 'saga'): | ||
raise ValueError("Known solvers are 'sparse_cg', 'cholesky', 'svd'" | ||
" 'lsqr', 'sag' or 'saga'. Got %s." % solver) | ||
|
||
if return_intercept and solver != 'sag': | ||
warnings.warn("In Ridge, only 'sag' solver can currently fit the " | ||
"intercept. Solver has been " | ||
"automatically changed into 'sag'.") | ||
GaelVaroquaux
Member
|
||
solver = 'sag' | ||
|
||
_dtype = [np.float64, np.float32] | ||
@@ -404,14 +418,7 @@ def _ridge_regression(X, y, alpha, sample_weight=None, solver='auto', | ||
raise ValueError("Number of samples in X and y does not correspond:" | ||
" %d != %d" % (n_samples, n_samples_)) | ||
|
||
has_sw = sample_weight is not None | ||
|
||
if solver == 'auto': | ||
# cholesky if it's a dense array and cg in any other case | ||
if not sparse.issparse(X) or has_sw: | ||
solver = 'cholesky' | ||
else: | ||
solver = 'sparse_cg' | ||
|
||
if has_sw: | ||
if np.atleast_1d(sample_weight).ndim > 1: | ||
@@ -432,8 +439,6 @@ def _ridge_regression(X, y, alpha, sample_weight=None, solver='auto', | ||
if alpha.size == 1 and n_targets > 1: | ||
alpha = np.repeat(alpha, n_targets) | ||
|
||
if solver not in ('sparse_cg', 'cholesky', 'svd', 'lsqr', 'sag', 'saga'): | ||
raise ValueError('Solver %s not understood' % solver) | ||
|
||
n_iter = None | ||
if solver == 'sparse_cg': | ||
@@ -555,7 +560,7 @@ def fit(self, X, y, sample_weight=None): | ||
# add the offset which was subtracted by _preprocess_data | ||
self.intercept_ += y_offset | ||
else: | ||
if sparse.issparse(X): | ||
if sparse.issparse(X) and self.solver == 'sparse_cg': | ||
# required to fit intercept with sparse_cg solver | ||
params = {'X_offset': X_offset, 'X_scale': X_scale} | ||
else: | ||
@@ -7,6 +7,7 @@ | ||
|
||
from sklearn.utils.testing import assert_almost_equal | ||
from sklearn.utils.testing import assert_array_almost_equal | ||
from sklearn.utils.testing import assert_allclose | ||
from sklearn.utils.testing import assert_equal | ||
from sklearn.utils.testing import assert_array_equal | ||
from sklearn.utils.testing import assert_greater | ||
@@ -778,7 +779,8 @@ def test_raises_value_error_if_solver_not_supported(): | ||
wrong_solver = "This is not a solver (MagritteSolveCV QuantumBitcoin)" | ||
|
||
exception = ValueError | ||
message = "Solver %s not understood" % wrong_solver | ||
message = ("Known solvers are 'sparse_cg', 'cholesky', 'svd'" | ||
" 'lsqr', 'sag' or 'saga'. Got %s." % wrong_solver) | ||
|
||
def func(): | ||
X = np.eye(3) | ||
@@ -837,6 +839,62 @@ def test_ridge_fit_intercept_sparse(): | ||
assert_array_almost_equal(dense.coef_, sparse.coef_) | ||
|
||
|
||
@pytest.mark.parametrize('return_intercept', [False, True]) | ||
@pytest.mark.parametrize('sample_weight', [None, np.ones(1000)]) | ||
@pytest.mark.parametrize('arr_type', [np.array, sp.csr_matrix]) | ||
@pytest.mark.parametrize('solver', ['auto', 'sparse_cg', 'cholesky', 'lsqr', | ||
'sag', 'saga']) | ||
def test_ridge_regression_check_arguments_validity(return_intercept, | ||
sample_weight, arr_type, | ||
solver): | ||
"""check if all combinations of arguments give valid estimations""" | ||
|
||
# test excludes 'svd' solver because it raises exception for sparse inputs | ||
|
||
X = np.random.rand(1000, 3) | ||
|
||
true_coefs = [1, 2, 0.1] | ||
y = np.dot(X, true_coefs) | ||
X_testing = arr_type(X) | ||
|
||
target = ridge_regression(X_testing, y, 1, | ||
solver=solver, | ||
sample_weight=sample_weight, | ||
return_intercept=return_intercept | ||
) | ||
if return_intercept: | ||
coef, intercept = target | ||
assert_allclose(coef, true_coefs, rtol=0, atol=0.03) | ||
assert_allclose(intercept, 0, rtol=0, atol=0.03) | ||
else: | ||
jnothman
Member
|
||
assert_allclose(target, true_coefs, rtol=0, atol=0.03) | ||
|
||
|
||
def test_ridge_regression_warns_with_return_intercept(): | ||
# return_itercept is only supported by sag/saga | ||
|
||
X, y = make_regression(n_samples=1000, n_features=2, n_informative=2, | ||
bias=10., random_state=42) | ||
|
||
for solver in ['sparse_cg', 'cholesky', 'svd', 'lsqr', 'saga']: | ||
|
||
with pytest.warns(UserWarning, match="In Ridge, only 'sag' solver"): | ||
target = ridge_regression(X, y, 1, | ||
jeremiedbb
Member
|
||
solver=solver, | ||
return_intercept=True | ||
) | ||
assert len(target) == 2 | ||
assert target[0].shape == (2,) | ||
|
||
with pytest.warns(None) as record: | ||
target = ridge_regression(X, y, 1, | ||
solver="sag", | ||
return_intercept=True | ||
) | ||
assert len(target) == 2 | ||
assert target[0].shape == (2,) | ||
|
||
# no warning should be raised | ||
assert len(record) == 0 | ||
|
||
def test_errors_and_values_helper(): | ||
ridgecv = _RidgeGCV() | ||
rng = check_random_state(42) | ||
This is also a bugfix for
RidgeClassifier
at least