8000 [MRG+1] Deprecated logistic_regression_path (#12821) · scikit-learn/scikit-learn@55a98ab · GitHub
[go: up one dir, main page]

Skip to content

Commit 55a98ab

Browse files
NicolasHugamueller
authored andcommitted
[MRG+1] Deprecated logistic_regression_path (#12821)
<!-- Thanks for contributing a pull request! Please ensure you have taken a look at the contribution guidelines: https://github.com/scikit-learn/scikit-learn/blob/master/CONTRIBUTING.md#pull-request-checklist --> #### Reference Issues/PRs <!-- Example: Fixes #1234. See also #3456. Please use keywords (e.g., Fixes) to create link to the issues or pull requests you resolved, so that they will automatically be closed when your pull request is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests --> Closes #12798 #### What does this implement/fix? Explain your changes. This PR deprecates the use of `logistic_regression_path` and makes it private. #### Any other comments? <!-- Please be aware that we are a loose team of volunteers so patience is necessary; assistance handling other issues is very welcome. We value all user contributions, no matter how minor they are. If we are slow to review, either the pull request needs some benchmarking, tinkering, convincing, etc. or more likely the reviewers are simply busy. In either case, we ask for your understanding during the review process. For more information, see our FAQ on this topic: http://scikit-learn.org/dev/faq.html#why-is-my-pull-request-not-getting-any-attention. Thanks for contributing! -->
1 parent e73acef commit 55a98ab

File tree

4 files changed

+196
-11
lines changed

4 files changed

+196
-11
lines changed

doc/modules/classes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,6 @@ Kernels:
756756
linear_model.enet_path
757757
linear_model.lars_path
758758
linear_model.lasso_path
759-
linear_model.logistic_regression_path
760759
linear_model.orthogonal_mp
761760
linear_model.orthogonal_mp_gram
762761
linear_model.ridge_regression
@@ -1507,6 +1506,7 @@ To be removed in 0.23
15071506
utils.cpu_count
15081507
utils.delayed
15091508
metrics.calinski_harabaz_score
1509+
linear_model.logistic_regression_path
15101510

15111511

15121512
To be removed in 0.22

doc/whats_new/v0.21.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ Support for Python 3.4 and below has been officially dropped.
9393
:class:`linear_model.MultiTaskLasso` which were breaking when
9494
``warm_start = True``. :issue:`12360` by :user:`Aakanksha Joshi <joaak>`.
9595

96+
- |API| :func:`linear_model.logistic_regression_path` is deprecated
97+
in version 0.21 and will be removed in version 0.23.
98+
:issue:`12821` by :user:`Nicolas Hug <NicolasHug>`.
99+
96100
:mod:`sklearn.manifold`
97101
............................
98102

sklearn/linear_model/logistic.py

Lines changed: 176 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from ..utils.fixes import logsumexp
3030
from ..utils.optimize import newton_cg
3131
from ..utils.validation import check_X_y
32+
from ..utils import deprecated
3233
from ..exceptions import (NotFittedError, ConvergenceWarning,
3334
ChangedBehaviorWarning)
3435
from ..utils.multiclass import check_classification_targets
@@ -478,6 +479,8 @@ def _check_multi_class(multi_class, solver, n_classes):
478479
return multi_class
479480

480481

482+
@deprecated('logistic_regression_path was deprecated in version 0.21 and '
483+
'will be removed in version 0.23.0')
481484
def logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True,
482485
max_iter=100, tol=1e-4, verbose=0,
483486
solver='lbfgs', coef=None,
@@ -495,6 +498,176 @@ def logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True,
495498
Note that there will be no speedup with liblinear solver, since it does
496499
not handle warm-starting.
497500
501+
.. deprecated:: 0.21
502+
``logistic_regression_path`` was deprecated in version 0.21 and will
503+
be removed in 0.23.
504+
505+
Read more in the :ref:`User Guide <logistic_regression>`.
506+
507+
Parameters
508+
----------
509+
X : array-like or sparse matrix, shape (n_samples, n_features)
510+
Input data.
511+
512+
y : array-like, shape (n_samples,) or (n_samples, n_targets)
513+
Input data, target values.
514+
515+
pos_class : int, None
516+
The class with respect to which we perform a one-vs-all fit.
517+
If None, then it is assumed that the given problem is binary.
518+
519+
Cs : int | array-like, shape (n_cs,)
520+
List of values for the regularization parameter or integer specifying
521+
the number of regularization parameters that should be used. In this
522+
case, the parameters will be chosen in a logarithmic scale between
523+
1e-4 and 1e4.
524+
525+
fit_intercept : bool
526+
Whether to fit an intercept for the model. In this case the shape of
527+
the returned array is (n_cs, n_features + 1).
528+
529+
max_iter : int
530+
Maximum number of iterations for the solver.
531+
532+
tol : float
533+
Stopping criterion. For the newton-cg and lbfgs solvers, the iteration
534+
will stop when ``max{|g_i | i = 1, ..., n} <= tol``
535+
where ``g_i`` is the i-th component of the gradient.
536+
537+
verbose : int
538+
For the liblinear and lbfgs solvers set verbose to any positive
539+
number for verbosity.
540+
541+
solver : {'lbfgs', 'newton-cg', 'liblinear', 'sag', 'saga'}
542+
Numerical solver to use.
543+
544+
coef : array-like, shape (n_features,), default None
545+
Initialization value for coefficients of logistic regression.
546+
Useless for liblinear solver.
547+
548+
class_weight : dict or 'balanced', optional
549+
Weights associated with classes in the form ``{class_label: weight}``.
550+
If not given, all classes are supposed to have weight one.
551+
552+
The "balanced" mode uses the values of y to automatically adjust
553+
weights inversely proportional to class frequencies in the input data
554+
as ``n_samples / (n_classes * np.bincount(y))``.
555+
556+
Note that these weights will be multiplied with sample_weight (passed
557+
through the fit method) if sample_weight is specified.
558+
559+
dual : bool
560+
Dual or primal formulation. Dual formulation is only implemented for
561+
l2 penalty with liblinear solver. Prefer dual=False when
562+
n_samples > n_features.
563+
564+
penalty : str, 'l1', 'l2', or 'elasticnet'
565+
Used to specify the norm used in the penalization. The 'newton-cg',
566+
'sag' and 'lbfgs' solvers support only l2 penalties. 'elasticnet' is
567+
only supported by the 'saga' solver.
568+
569+
intercept_scaling : float, default 1.
570+
Useful only when the solver 'liblinear' is used
571+
and self.fit_intercept is set to True. In this case, x becomes
572+
[x, self.intercept_scaling],
573+
i.e. a "synthetic" feature with constant value equal to
574+
intercept_scaling is appended to the instance vector.
575+
The intercept becomes ``intercept_scaling * synthetic_feature_weight``.
576+
577+
Note! the synthetic feature weight is subject to l1/l2 regularization
578+
as all other features.
579+
To lessen the effect of regularization on synthetic feature weight
580+
(and therefore on the intercept) intercept_scaling has to be increased.
581+
582+
multi_class : str, {'ovr', 'multinomial', 'auto'}, default: 'ovr'
583+
If the option chosen is 'ovr', then a binary problem is fit for each
584+
label. For 'multinomial' the loss minimised is the multinomial loss fit
585+
across the entire probability distribution, *even when the data is
586+
binary*. 'multinomial' is unavailable when solver='liblinear'.
587+
'auto' selects 'ovr' if the data is binary, or if solver='liblinear',
588+
and otherwise selects 'multinomial'.
589+
590+
.. versionadded:: 0.18
591+
Stochastic Average Gradient descent solver for 'multinomial' case.
592+
.. versionchanged:: 0.20
593+
Default will change from 'ovr' to 'auto' in 0.22.
594+
595+
random_state : int, RandomState instance or None, optional, default None
596+
The seed of the pseudo random number generator to use when shuffling
597+
the data. If int, random_state is the seed used by the random number
598+
generator; If RandomState instance, random_state is the random number
599+
generator; If None, the random number generator is the RandomState
600+
instance used by `np.random`. Used when ``solver`` == 'sag' or
601+
'liblinear'.
602+
603+
check_input : bool, default True
604+
If False, the input arrays X and y will not be checked.
605+
606+
max_squared_sum : float, default None
607+
Maximum squared sum of X over samples. Used only in SAG solver.
608+
If None, it will be computed, going through all the samples.
609+
The value should be precomputed to speed up cross validation.
610+
611+
sample_weight : array-like, shape(n_samples,) optional
612+
Array of weights that are assigned to individual samples.
613+
If not provided, then each sample is given unit weight.
614+
615+
l1_ratio : float or None, optional (default=None)
616+
The Elastic-Net mixing parameter, with ``0 <= l1_ratio <= 1``. Only
617+
used if ``penalty='elasticnet'``. Setting ``l1_ratio=0`` is equivalent
618+
to using ``penalty='l2'``, while setting ``l1_ratio=1`` is equivalent
619+
to using ``penalty='l1'``. For ``0 < l1_ratio <1``, the penalty is a
620+
combination of L1 and L2.
621+
622+
Returns
623+
-------
624+
coefs : ndarray, shape (n_cs, n_features) or (n_cs, n_features + 1)
625+
List of coefficients for the Logistic Regression model. If
626+
fit_intercept is set to True then the second dimension will be
627+
n_features + 1, where the last item represents the intercept. For
628+
``multiclass='multinomial'``, the shape is (n_classes, n_cs,
629+
n_features) or (n_classes, n_cs, n_features + 1).
630+
631+
Cs : ndarray
632+
Grid of Cs used for cross-validation.
633+
634+
n_iter : array, shape (n_cs,)
635+
Actual number of iteration for each Cs.
636+
637+
Notes
638+
-----
639+
You might get slightly different results with the solver liblinear than
640+
with the others since this uses LIBLINEAR which penalizes the intercept.
641+
642+
.. versionchanged:: 0.19
643+
The "copy" parameter was removed.
644+
"""
645+
646+
return _logistic_regression_path(
647+
X, y, pos_class=None, Cs=10, fit_intercept=True, max_iter=100,
648+
tol=1e-4, verbose=0, solver='lbfgs', coef=None, class_weight=None,
649+
dual=False, penalty='l2', intercept_scaling=1., multi_class='warn',
650+
random_state=None, check_input=True, max_squared_sum=None,
651+
sample_weight=None, l1_ratio=None)
652+
653+
654+
def _logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True,
655+
max_iter=100, tol=1e-4, verbose=0,
656+
solver='lbfgs', coef=None,
657+
class_weight=None, dual=False, penalty='l2',
658+
intercept_scaling=1., multi_class='warn',
659+
random_state=None, check_input=True,
660+
max_squared_sum=None, sample_weight=None,
661+
l1_ratio=None):
662+
"""Compute a Logistic Regression model for a list of regularization
663+
parameters.
664+
665+
This is an implementation that uses the result of the previous model
666+
to speed up computations along the set of solutions, making it faster
667+
than sequentially calling LogisticRegression for the different parameters.
668+
Note that there will be no speedup with liblinear solver, since it does
669+
not handle warm-starting.
670+
498671
Read more in the :ref:`User Guide <logistic_regression>`.
499672
500673
Parameters
@@ -975,7 +1148,7 @@ def _log_reg_scoring_path(X, y, train, test, pos_class=None, Cs=10,
9751148

9761149
sample_weight = sample_weight[train]
9771150

978-
coefs, Cs, n_iter = logistic_regression_path(
1151+
coefs, Cs, n_iter = _logistic_regression_path(
9791152
X_train, y_train, Cs=Cs, l1_ratio=l1_ratio,
9801153
fit_intercept=fit_intercept, solver=solver, max_iter=max_iter,
9811154
class_weight=class_weight, pos_class=pos_class,
@@ -1388,7 +1561,7 @@ def fit(self, X, y, sample_weight=None):
13881561
if warm_start_coef is None:
13891562
warm_start_coef = [None] * n_classes
13901563

1391-
path_func = delayed(logistic_regression_path)
1564+
path_func = delayed(_logistic_regression_path)
13921565

13931566
# The SAG solver releases the GIL so it's more efficient to use
13941567
# threads for this solver.
@@ -1965,7 +2138,7 @@ def fit(self, X, y, sample_weight=None):
19652138

19662139
# Note that y is label encoded and hence pos_class must be
19672140
# the encoded label / None (for 'multinomial')
1968-
w, _, _ = logistic_regression_path(
2141+
w, _, _ = _logistic_regression_path(
19692142
X, y, pos_class=encoded_label, Cs=[C_], solver=solver,
19702143
fit_intercept=self.fit_intercept, coef=coef_init,
19712144
max_iter=self.max_iter, tol=self.tol,

sklearn/linear_model/tests/test_logistic.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
from sklearn.exceptions import ChangedBehaviorWarning
3636
from sklearn.linear_model.logistic import (
3737
LogisticRegression,
38-
logistic_regression_path, LogisticRegressionCV,
38+
logistic_regression_path,
39+
_logistic_regression_path, LogisticRegressionCV,
3940
_logistic_loss_and_grad, _logistic_grad_hess,
4041
_multinomial_grad_hess, _logistic_loss,
4142
_log_reg_scoring_path)
@@ -395,7 +396,7 @@ def test_consistency_path():
395396
# can't test with fit_intercept=True since LIBLINEAR
396397
# penalizes the intercept
397398
for solver in ['sag', 'saga']:
398-
coefs, Cs, _ = f(logistic_regression_path)(
399+
coefs, Cs, _ = f(_logistic_regression_path)(
399400
X, y, Cs=Cs, fit_intercept=False, tol=1e-5, solver=solver,
400401
max_iter=1000, multi_class='ovr', random_state=0)
401402
for i, C in enumerate(Cs):
@@ -410,7 +411,7 @@ def test_consistency_path():
410411
# test for fit_intercept=True
411412
for solver in ('lbfgs', 'newton-cg', 'liblinear', 'sag', 'saga'):
412413
Cs = [1e3]
413-
coefs, Cs, _ = f(logistic_regression_path)(
414+
coefs, Cs, _ = f(_logistic_regression_path)(
414415
X, y, Cs=Cs, fit_intercept=True, tol=1e-6, solver=solver,
415416
intercept_scaling=10000., random_state=0, multi_class='ovr')
416417
lr = LogisticRegression(C=Cs[0], fit_intercept=True, tol=1e-4,
@@ -427,7 +428,7 @@ def test_logistic_regression_path_convergence_fail():
427428
X = np.concatenate((rng.randn(100, 2) + [1, 1], rng.randn(100, 2)))
428429
y = [1] * 100 + [-1] * 100
429430
Cs = [1e3]
430-
assert_warns(ConvergenceWarning, logistic_regression_path,
431+
assert_warns(ConvergenceWarning, _logistic_regression_path,
431432
X, y, Cs=Cs, tol=0., max_iter=1, random_state=0, verbose=1)
432433

433434

@@ -1689,9 +1690,9 @@ def test_logistic_regression_path_coefs_multinomial():
16891690
n_redundant=0, n_clusters_per_class=1,
16901691
random_state=0, n_features=2)
16911692
Cs = [.00001, 1, 10000]
1692-
coefs, _, _ = logistic_regression_path(X, y, penalty='l1', Cs=Cs,
1693-
solver='saga', random_state=0,
1694-
multi_class='multinomial')
1693+
coefs, _, _ = _logistic_regression_path(X, y, penalty='l1', Cs=Cs,
1694+
solver='saga', random_state=0,
1695+
multi_class='multinomial')
16951696

16961697
with pytest.raises(AssertionError):
16971698
assert_array_almost_equal(coefs[0], coefs[1], decimal=1)
@@ -1746,3 +1747,10 @@ def fit(X, y, **kw):
17461747
assert not np.allclose(est_auto_bin.coef_,
17471748
fit(X, y_multi, multi_class='multinomial',
17481749
solver=solver).coef_)
1750+
1751+
1752+
def test_logistic_regression_path_deprecation():
1753+
1754+
assert_warns_message(DeprecationWarning,
1755+
"logistic_regression_path was deprecated",
1756+
logistic_regression_path, X, Y1)

0 commit comments

Comments
 (0)
0