diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/31474.api.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/31474.api.rst new file mode 100644 index 0000000000000..845b9b502b9f1 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.linear_model/31474.api.rst @@ -0,0 +1,6 @@ +- :class:`linear_model.SGDClassifier`, :class:`linear_model.SGDRegressor`, and + :class:`linear_model.SGDOneClassSVM` now deprecate negative values for the + `power_t` parameter. Using a negative value will raise a warning in version 1.8 + and will raise an error in version 1.10. A value in the range [0.0, inf) must be used + instead. + By :user:`Ritvi Alagusankar ` \ No newline at end of file diff --git a/sklearn/linear_model/_stochastic_gradient.py b/sklearn/linear_model/_stochastic_gradient.py index 8f7c814000614..859e527fb3c3b 100644 --- a/sklearn/linear_model/_stochastic_gradient.py +++ b/sklearn/linear_model/_stochastic_gradient.py @@ -731,6 +731,15 @@ def _fit( ), ConvergenceWarning, ) + + if self.power_t < 0: + warnings.warn( + "Negative values for `power_t` are deprecated in version 1.8 " + "and will raise an error in 1.10. " + "Use values in the range [0.0, inf) instead.", + FutureWarning, + ) + return self def _fit_binary(self, X, y, alpha, C, sample_weight, learning_rate, max_iter): @@ -1082,7 +1091,11 @@ class SGDClassifier(BaseSGDClassifier): power_t : float, default=0.5 The exponent for inverse scaling learning rate. - Values must be in the range `(-inf, inf)`. + Values must be in the range `[0.0, inf)`. + + .. deprecated:: 1.8 + Negative values for `power_t` are deprecated in version 1.8 and will raise + an error in 1.10. Use values in the range [0.0, inf) instead. early_stopping : bool, default=False Whether to use early stopping to terminate training when validation @@ -1585,6 +1598,14 @@ def _fit( ConvergenceWarning, ) + if self.power_t < 0: + warnings.warn( + "Negative values for `power_t` are deprecated in version 1.8 " + "and will raise an error in 1.10. " + "Use values in the range [0.0, inf) instead.", + FutureWarning, + ) + return self @_fit_context(prefer_skip_nested_validation=True) @@ -1880,7 +1901,11 @@ class SGDRegressor(BaseSGDRegressor): power_t : float, default=0.25 The exponent for inverse scaling learning rate. - Values must be in the range `(-inf, inf)`. + Values must be in the range `[0.0, inf)`. + + .. deprecated:: 1.8 + Negative values for `power_t` are deprecated in version 1.8 and will raise + an error in 1.10. Use values in the range [0.0, inf) instead. early_stopping : bool, default=False Whether to use early stopping to terminate training when validation @@ -2118,7 +2143,11 @@ class SGDOneClassSVM(OutlierMixin, BaseSGD): power_t : float, default=0.5 The exponent for inverse scaling learning rate. - Values must be in the range `(-inf, inf)`. + Values must be in the range `[0.0, inf)`. + + .. deprecated:: 1.8 + Negative values for `power_t` are deprecated in version 1.8 and will raise + an error in 1.10. Use values in the range [0.0, inf) instead. warm_start : bool, default=False When set to True, reuse the solution of the previous call to fit as @@ -2490,6 +2519,14 @@ def _fit( ConvergenceWarning, ) + if self.power_t < 0: + warnings.warn( + "Negative values for `power_t` are deprecated in version 1.8 " + "and will raise an error in 1.10. " + "Use values in the range [0.0, inf) instead.", + FutureWarning, + ) + return self @_fit_context(prefer_skip_nested_validation=True) diff --git a/sklearn/linear_model/tests/test_sgd.py b/sklearn/linear_model/tests/test_sgd.py index 26d138ae3649b..80b69adf99b99 100644 --- a/sklearn/linear_model/tests/test_sgd.py +++ b/sklearn/linear_model/tests/test_sgd.py @@ -1,4 +1,5 @@ import pickle +import warnings from unittest.mock import Mock import joblib @@ -507,6 +508,35 @@ def test_sgd_failing_penalty_validation(Estimator): clf.fit(X, Y) +# TODO(1.10): remove this test +@pytest.mark.parametrize( + "klass", + [ + SGDClassifier, + SparseSGDClassifier, + SGDRegressor, + SparseSGDRegressor, + SGDOneClassSVM, + SparseSGDOneClassSVM, + ], +) +def test_power_t_limits(klass): + """Check that a warning is raised when `power_t` is negative.""" + + # Check that negative values of `power_t` raise a warning + clf = klass(power_t=-1.0) + with pytest.warns( + FutureWarning, match="Negative values for `power_t` are deprecated" + ): + clf.fit(X, Y) + + # Check that values of 'power_t in range [0, inf) do not raise a warning + with warnings.catch_warnings(record=True) as w: + clf = klass(power_t=0.5) + clf.fit(X, Y) + assert len(w) == 0 + + ############################################################################### # Classification Test Case