9
9
import joblib
10
10
11
11
from sklearn.base import is_classifier
12
+ from sklearn.base import clone
12
13
from sklearn.datasets import load_diabetes
13
14
from sklearn.datasets import make_regression
14
15
from sklearn.model_selection import train_test_split
@@ -453,7 +454,7 @@ def test_linear_model_sample_weights_normalize_in_pipeline(
453
454
X_train = sparse.csr_matrix(X_train)
454
455
X_test = _convert_container(X_train, 'sparse')
455
456
456
- sample_weight = rng.rand( X_train.shape[0])
457
+ sample_weight = rng.uniform(low=0.1, high=100, size= X_train.shape[0])
457
458
458
459
# linear estimator with built-in feature normalization
459
460
reg_with_normalize = estimator(normalize=True, fit_intercept=True,
@@ -462,7 +463,12 @@ def test_linear_model_sample_weights_normalize_in_pipeline(
462
463
463
464
# linear estimator in a pipeline with a StandardScaler, normalize=False
464
465
linear_regressor = estimator(normalize=False, fit_intercept=True, **params)
465
- _scale_alpha_inplace(linear_regressor, X_train.shape[0]) # rescale alpha
466
+
467
+ # rescale alpha
468
+ if model_name in ["Lasso", "ElasticNet"]:
469
+ _scale_alpha_inplace(linear_regressor, y_test.shape[0])
470
+ else:
471
+ _scale_alpha_inplace(linear_regressor, sample_weight.sum())
466
472
reg_with_scaler = Pipeline([
467
473
("scaler", StandardScaler(with_mean=with_mean)),
468
474
("linear_regressor", linear_regressor)
@@ -479,7 +485,8 @@ def test_linear_model_sample_weights_normalize_in_pipeline(
479
485
# sense that they predict exactly the same outcome.
480
486
y_pred_normalize = reg_with_normalize.predict(X_test)
481
487
y_pred_scaler = reg_with_scaler.predict(X_test)
482
- assert_allclose(y_pred_normalize, y_pred_scaler)
488
+ assert_allclose(y_pred_normalize, y_pred_scaler)
489
+
483
490
# Check intercept computation when normalize is True
484
491
y_train_mean = np.average(y_train, weights=sample_weight)
485
492
if is_sparse:
@@ -1446,39 +1453,78 @@ def test_enet_ridge_consistency(normalize, ridge_alpha):
1446
1453
# effective_rank are more problematic in particular.
1447
1454
1448
1455
rng = np.random.RandomState(42)
1456
+ n_samples = 300
1449
1457
X, y = make_regression(
1450
- n_samples=100 ,
1451
- n_features=300 ,
1452
- effective_rank=100 ,
1458
+ n_samples=n_samples ,
1459
+ n_features=100 ,
1460
+ effective_rank=10 ,
1453
1461
n_informative=50,
1454
1462
random_state=rng,
1455
1463
)
1456
- sw = rng.uniform(low=0.01, high=2, size=X.shape[0])
1457
-
1458
- ridge = Ridge(
1459
- alpha=ridge_alpha,
1464
+ sw = rng.uniform(low=0.01, high=10, size=X.shape[0])
1465
+ alpha = 1.
1466
+ common_params = dict(
1460
1467
normalize=normalize,
1461
- ).fit(X, y, sample_weight=sw)
1462
-
1463
- enet = ElasticNet(
1464
- alpha=ridge_alpha / sw.sum(),
1465
- normalize=normalize,
1466
- l1_ratio=0.,
1467
- max_iter=1000,
1468
+ tol=1e-12,
1468
1469
)
1469
- # Even when the ElasticNet model has actually converged, the duality gap
1470
- # convergence criterion is never met when l1_ratio is 0 and for any value
1471
- # of the `tol` parameter. The convergence message should point the user to
1472
- # Ridge instead:
1473
- expected_msg = (
1474
- r"Objective did not converge\. .* "
1475
- r"Linear regression models with null weight for the "
1476
- r"l1 regularization term are more efficiently fitted "
1477
- r"using one of the solvers implemented in "
1478
- r"sklearn\.linear_model\.Ridge/RidgeCV instead\."
1470
+ ridge = Ridge(alpha=alpha, **common_params).fit(
1471
+ X, y, sample_weight=sw
1472
+ )
1473
+ if normalize:
1474
+ alpha_enet = alpha / n
10000
_samples
1475
+ else:
1476
+ alpha_enet = alpha / sw.sum()
1477
+ enet = ElasticNet(alpha=alpha_enet, l1_ratio=0, **common_params).fit(
1478
+ X, y, sample_weight=sw
1479
1479
)
1480
- with pytest.warns(ConvergenceWarning, match=expected_msg):
1481
- enet.fit(X, y, sample_weight=sw)
1482
-
1483
1480
assert_allclose(ridge.coef_, enet.coef_)
1484
1481
assert_allclose(ridge.intercept_, enet.intercept_)
1482
+
1483
+
1484
+ @pytest.mark.parametrize(
1485
+ "estimator", [
1486
+ Lasso(alpha=1.),
1487
+ ElasticNet(alpha=1., l1_ratio=0.1),
1488
+ ]
1489
+ )
1490
+ def test_sample_weight_invariance(estimator):
1491
+ rng = np.random.RandomState(42)
1492
+ X, y = make_regression(
1493
+ n_samples=100,
1494
+ n_features=300,
1495
+ effective_rank=10,
1496
+ n_informative=50,
1497
+ random_state=rng,
1498
+ )
1499
+ normalize = False # These tests don't work for normalize=True.
1500
+ sw = rng.uniform(low=0.01, high=2, size=X.shape[0])
1501
+ params = dict(normalize=normalize, tol=1e-12)
1502
+
1503
+ # Check that setting some weights to 0 is equivalent to trimming the
1504
+ # samples:
1505
+ cutoff = X.shape[0] // 3
1506
+ sw_with_null = sw.copy()
1507
+ sw_with_null[:cutoff] = 0.
1508
+ X_trimmed, y_trimmed = X[cutoff:, :], y[cutoff:]
1509
+ sw_trimmed = sw[cutoff:]
1510
+
1511
+ reg_trimmed = clone(estimator).set_params(**params).fit(
1512
+ X_trimmed, y_trimmed, sample_weight=sw_trimmed)
1513
+ reg_null_weighted = clone(estimator).set_params(**params).fit(
1514
+ X, y, sample_weight=sw_with_null)
1515
+ assert_allclose(reg_null_weighted.coef_, reg_trimmed.coef_)
1516
+ assert_allclose(reg_null_weighted.intercept_, reg_trimmed.intercept_)
1517
+
1518
+ # Check that duplicating the training dataset is equivalent to multiplying
1519
+ # the weights by 2:
1520
+ X_dup = np.concatenate([X, X], axis=0)
1521
+ y_dup = np.concatenate([y, y], axis=0)
1522
+ sw_dup = np.concatenate([sw, sw], axis=0)
1523
+
1524
+ reg_2sw = clone(estimator).set_params(**params).fit(
1525
+ X, y, sample_weight=2 * sw)
1526
+ reg_dup = clone(estimator).set_params(**params).fit(
1527
+ X_dup, y_dup, sample_weight=sw_dup)
1528
+
1529
+ assert_allclose(reg_2sw.coef_, reg_dup.coef_)
1530
+ assert_allclose(reg_2sw.intercept_, reg_dup.intercept_)
0 commit comments