-
-
Notifications
You must be signed in to change notification settings - Fork 25.9k
Fix elasticnet cv sample weight #29308
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
Changes from all commits
8d4b501
2f494db
fa2c821
75e6584
8b6cfc0
2ba4c57
5d1f5e7
dce169c
380c21f
c187cf7
40d8b30
85062c0
c649d36
41fcb5f
335137d
36cc847
fec4f74
c41a8ee
ac9f090
bf62b35
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -48,6 +48,7 @@ | |||||
assert_almost_equal, | ||||||
assert_array_almost_equal, | ||||||
assert_array_equal, | ||||||
assert_array_less, | ||||||
ignore_warnings, | ||||||
) | ||||||
from sklearn.utils.fixes import COO_CONTAINERS, CSC_CONTAINERS, CSR_CONTAINERS | ||||||
|
@@ -1319,39 +1320,34 @@ def test_enet_cv_sample_weight_correctness(fit_intercept, sparse_container): | |||||
X = sparse_container(X) | ||||||
params = dict(tol=1e-6) | ||||||
|
||||||
# Set alphas, otherwise the two cv models might use different ones. | ||||||
if fit_intercept: | ||||||
alphas = np.linspace(0.001, 0.01, num=91) | ||||||
else: | ||||||
alphas = np.linspace(0.01, 0.1, num=91) | ||||||
|
||||||
# We weight the first fold 2 times more. | ||||||
sw[:n_samples] = 2 | ||||||
# We weight the first fold n times more. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
sw[:n_samples] = rng.randint(0, 5, size=sw[:n_samples].shape[0]) | ||||||
groups_sw = np.r_[ | ||||||
np.full(n_samples, 0), np.full(n_samples, 1), np.full(n_samples, 2) | ||||||
] | ||||||
splits_sw = list(LeaveOneGroupOut().split(X, groups=groups_sw)) | ||||||
reg_sw = ElasticNetCV( | ||||||
alphas=alphas, cv=splits_sw, fit_intercept=fit_intercept, **params | ||||||
) | ||||||
reg_sw = ElasticNetCV(cv=splits_sw, fit_intercept=fit_intercept, **params) | ||||||
reg_sw.fit(X, y, sample_weight=sw) | ||||||
|
||||||
# We repeat the first fold 2 times and provide splits ourselves | ||||||
if sparse_container is not None: | ||||||
X = X.toarray() | ||||||
X = np.r_[X[:n_samples], X] | ||||||
X_rep = np.repeat(X, sw.astype(int), axis=0) | ||||||
##Need to know number of repitions made in total | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I think that computing the number of repetitions is not needed, see the other suggestions below. |
||||||
n_reps = X_rep.shape[0] - X.shape[0] | ||||||
X = X_rep | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather not rename change the Maybe you could instead name the variables There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And similarly for the names of the 2 cross-validation splitters (if you adapt the code to use metadata routing) or the results of their splits if you prefer to precompute them instead of leveraging metadata routing. |
||||||
if sparse_container is not None: | ||||||
X = sparse_container(X) | ||||||
y = np.r_[y[:n_samples], y] | ||||||
y = np.repeat(y, sw.astype(int), axis=0) | ||||||
groups = np.r_[ | ||||||
np.full(2 * n_samples, 0), np.full(n_samples, 1), np.full(n_samples, 2) | ||||||
np.full(n_reps + n_samples, 0), np.full(n_samples, 1), np.full(n_samples, 2) | ||||||
] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using groups_with_repetitions = np.repeat(groups_with_weights, sw.astype(int), axis=0) as is done for |
||||||
splits = list(LeaveOneGroupOut().split(X, groups=groups)) | ||||||
reg = ElasticNetCV(alphas=alphas, cv=splits, fit_intercept=fit_intercept, **params) | ||||||
reg = ElasticNetCV(cv=splits, fit_intercept=fit_intercept, **params) | ||||||
reg.fit(X, y) | ||||||
|
||||||
# ensure that we chose meaningful alphas, i.e. not boundaries | ||||||
assert alphas[0] < reg.alpha_ < alphas[-1] | ||||||
assert_allclose(reg_sw.alphas_, reg.alphas_) | ||||||
assert reg_sw.alpha_ == reg.alpha_ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also compare the values of the |
||||||
assert_allclose(reg_sw.coef_, reg.coef_) | ||||||
assert reg_sw.intercept_ == pytest.approx(reg.intercept_) | ||||||
|
@@ -1392,6 +1388,26 @@ def test_enet_cv_grid_search(sample_weight): | |||||
assert reg.alpha_ == pytest.approx(gs.best_params_["alpha"]) | ||||||
|
||||||
|
||||||
@pytest.mark.parametrize("sparseX", [False, True]) | ||||||
@pytest.mark.parametrize("fit_intercept", [False, True]) | ||||||
@pytest.mark.parametrize("sample_weight", [np.array([10, 1, 10, 1]), None]) | ||||||
def test_enet_alpha_max_sample_weight(sparseX, fit_intercept, sample_weight): | ||||||
X = np.array([[3.0, 1.0], [2.0, 5.0], [5.0, 3.0], [1.0, 4.0]]) | ||||||
beta = np.array([1, 1]) | ||||||
y = X @ beta | ||||||
if sparseX: | ||||||
X = sparse.csc_matrix(X) | ||||||
# Test alpha_max makes coefs zero. | ||||||
reg = ElasticNetCV(n_alphas=1, cv=2, eps=1, fit_intercept=fit_intercept) | ||||||
reg.fit(X, y, sample_weight=sample_weight) | ||||||
assert_almost_equal(reg.coef_, 0) | ||||||
alpha_max = reg.alpha_ | ||||||
# Test smaller alpha makes coefs nonzero. | ||||||
reg = ElasticNet(alpha=0.99 * alpha_max, fit_intercept=fit_intercept) | ||||||
reg.fit(X, y, sample_weight=sample_weight) | ||||||
assert_array_less(1e-3, np.max(np.abs(reg.coef_))) | ||||||
|
||||||
|
||||||
@pytest.mark.parametrize("fit_intercept", [True, False]) | ||||||
@pytest.mark.parametrize("l1_ratio", [0, 0.5, 1]) | ||||||
@pytest.mark.parametrize("precompute", [False, True]) | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the removal of this line and the other
force_writeable=True
lines below were not intentional (maybe when resolving conflicts withmain
)?I think this is what causes the test failures on the CI.