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

Skip to content
Merged
5 changes: 5 additions & 0 deletions doc/whats_new/v1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,11 @@ Changelog
(only supported by `lbfgs`).
:pr:`20231` by :user:`Toshihiro Nakae <tnakae>`.

- |Fix| The dictionary `params` in :func:`linear_model.enet_path` and
:func:`linear_model.lasso_path` should only contain parameter of the
coordinate descent solver. Otherwise, an error will be raised.
:pr:`19391` by :user:`Shao Yang Hong <hongshaoyang>`.

:mod:`sklearn.manifold`
.......................

Expand Down
35 changes: 22 additions & 13 deletions sklearn/linear_model/_coordinate_descent.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,16 @@ def enet_path(
:ref:`examples/linear_model/plot_lasso_coordinate_descent_path.py
<sphx_glr_auto_examples_linear_model_plot_lasso_coordinate_descent_path.py>`.
"""
X_offset_param = params.pop("X_offset", None)
X_scale_param = params.pop("X_scale", None)
tol = params.pop("tol", 1e-4)
max_iter = params.pop("max_iter", 1000)
random_state = params.pop("random_state", None)
selection = params.pop("selection", "cyclic")

if len(params) > 0:
raise ValueError("Unexpected parameters in params", params.keys())

# We expect X and y to be already Fortran ordered when bypassing
# checks
if check_input:
Expand Down Expand Up @@ -532,10 +542,10 @@ def enet_path(

# MultiTaskElasticNet does not support sparse matrices
if not multi_output and sparse.isspmatrix(X):
if "X_offset" in params:
if X_offset_param is not None:
# As sparse matrices are not actually centered we need this
# to be passed to the CD solver.
X_sparse_scaling = params["X_offset"] / params["X_scale"]
X_sparse_scaling = X_offset_param / X_scale_param
X_sparse_scaling = np.asarray(X_sparse_scaling, dtype=X.dtype)
else:
X_sparse_scaling = np.zeros(n_features, dtype=X.dtype)
Expand Down Expand Up @@ -571,13 +581,10 @@ def enet_path(
alphas = np.sort(alphas)[::-1] # make sure alphas are properly ordered

n_alphas = len(alphas)
tol = params.get("tol", 1e-4)
max_iter = params.get("max_iter", 1000)
dual_gaps = np.empty(n_alphas)
n_iters = []

rng = check_random_state(params.get("random_state", None))
selection = params.get("selection", "cyclic")
rng = check_random_state(random_state)
if selection not in ["random", "cyclic"]:
raise ValueError("selection should be either random or cyclic.")
random = selection == "random"
Expand Down Expand Up @@ -1016,7 +1023,6 @@ def fit(self, X, y, sample_weight=None, check_input=True):
dual_gaps_ = np.zeros(n_targets, dtype=X.dtype)
self.n_iter_ = []

# FIXME: 'normalize' to be removed in 1.2
for k in range(n_targets):
if Xy is not None:
this_Xy = Xy[:, k]
Expand All @@ -1031,8 +1037,6 @@ def fit(self, X, y, sample_weight=None, check_input=True):
alphas=[alpha],
precompute=precompute,
Xy=this_Xy,
fit_intercept=False,
normalize=False,
copy_X=True,
verbose=False,
tol=self.tol,
Expand Down Expand Up @@ -1267,6 +1271,8 @@ def _path_residuals(
sample_weight,
train,
test,
normalize,
fit_intercept,
path,
path_params,
alphas=None,
Expand Down Expand Up @@ -1348,9 +1354,6 @@ def _path_residuals(
# for read-only memmaps (cf. numpy#14132).
array.setflags(write=True)

fit_intercept = path_params["fit_intercept"]
normalize = path_params["normalize"]

if y.ndim == 1:
precompute = path_params["precompute"]
else:
Expand Down Expand Up @@ -1577,7 +1580,11 @@ def fit(self, X, y, sample_weight=None):
path_params = self.get_params()

# FIXME: 'normalize' to be removed in 1.2
path_params["normalize"] = _normalize
# path_params["normalize"] = _normalize
# Pop `intercept` and `normalize` that are not parameter of the path
# function
path_params.pop("normalize", None)
path_params.pop("fit_intercept", None)

if "l1_ratio" in path_params:
l1_ratios = np.atleast_1d(path_params["l1_ratio"])
Expand Down Expand Up @@ -1635,6 +1642,8 @@ def fit(self, X, y, sample_weight=None):
sample_weight,
train,
test,
_normalize,
self.fit_intercept,
self.path,
path_params,
alphas=this_alphas,
Expand Down
21 changes: 14 additions & 7 deletions sklearn/linear_model/tests/test_coordinate_descent.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,7 @@ def test_lasso_path_return_models_vs_new_return_gives_same_coefficients():
coef_path_cont_lars = interpolate.interp1d(
alphas_lars[::-1], coef_path_lars[:, ::-1]
)
alphas_lasso2, coef_path_lasso2, _ = lasso_path(
X, y, alphas=alphas, return_models=False
)
alphas_lasso2, coef_path_lasso2, _ = lasso_path(X, y, alphas=alphas)
coef_path_cont_lasso = interpolate.interp1d(
alphas_lasso2[::-1], coef_path_lasso2[:, ::-1]
)
Expand Down Expand Up @@ -1113,11 +1111,21 @@ def test_sparse_dense_descent_paths():
X, y, _, _ = build_dataset(n_samples=50, n_features=20)
csr = sparse.csr_matrix(X)
for path in [enet_path, lasso_path]:
_, coefs, _ = path(X, y, fit_intercept=False)
_, sparse_coefs, _ = path(csr, y, fit_intercept=False)
_, coefs, _ = path(X, y)
_, sparse_coefs, _ = path(csr, y)
assert_array_almost_equal(coefs, sparse_coefs)


@pytest.mark.parametrize("path_func", [enet_path, lasso_path])
def test_path_unknown_parameter(path_func):
"""Check that passing parameter not used by the coordinate descent solver
will raise an error."""
X, y, _, _ = build_dataset(n_samples=50, n_features=20)
err_msg = "Unexpected parameters in params"
with pytest.raises(ValueError, match=err_msg):
path_func(X, y, normalize=True, fit_intercept=True)


def test_check_input_false():
X, y, _, _ = build_dataset(n_samples=20, n_features=10)
X = check_array(X, order="F", dtype="float64")
Expand Down Expand Up @@ -1522,8 +1530,7 @@ def test_enet_cv_sample_weight_correctness(fit_intercept):
assert alphas[0] < reg.alpha_ < alphas[-1]
assert reg_sw.alpha_ == reg.alpha_
assert_allclose(reg_sw.coef_, reg.coef_)
if fit_intercept is not None:
assert reg_sw.intercept_ == pytest.approx(reg.intercept_)
assert reg_sw.intercept_ == pytest.approx(reg.intercept_)


@pytest.mark.parametrize("sample_weight", [False, True])
Expand Down
4 changes: 1 addition & 3 deletions sklearn/linear_model/tests/test_least_angle.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,7 @@ def test_lasso_lars_vs_lasso_cd_ill_conditioned():
y = y.squeeze()
lars_alphas, _, lars_coef = linear_model.lars_path(X, y, method="lasso")

_, lasso_coef2, _ = linear_model.lasso_path(
X, y, alphas=lars_alphas, tol=1e-6, fit_intercept=False
)
_, lasso_coef2, _ = linear_model.lasso_path(X, y, alphas=lars_alphas, tol=1e-6)

assert_array_almost_equal(lars_coef, lasso_coef2, decimal=1)

Expand Down