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

Skip to content

Commit 19da1e2

Browse files
jpangasglemaitre
andauthored
API Replace n_iter in Bayesian Ridge and ARDRegression (#25697)
Co-authored-by: Guillaume Lemaitre <[email protected]>
1 parent 097c368 commit 19da1e2

3 files changed

Lines changed: 147 additions & 19 deletions

File tree

doc/whats_new/v1.3.rst

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,21 @@ Changelog
273273
:mod:`sklearn.linear_model`
274274
...........................
275275

276-
- |Enhancement| :class:`SGDClassifier`, :class:`SGDRegressor` and
277-
:class:`SGDOneClassSVM` now preserve dtype for `numpy.float32`.
278-
:pr:`25587` by :user:`Omar Salman <OmarManzoor>`
276+
- |Enhancement| :class:`linear_model.SGDClassifier`,
277+
:class:`linear_model.SGDRegressor` and :class:`linear_model.SGDOneClassSVM`
278+
now preserve dtype for `numpy.float32`.
279+
:pr:`25587` by :user:`Omar Salman <OmarManzoor>`.
280+
281+
- |API| Deprecates `n_iter` in favor of `max_iter` in
282+
:class:`linear_model.BayesianRidge` and :class:`linear_model.ARDRegression`.
283+
`n_iter` will be removed in scikit-learn 1.5. This change makes those
284+
estimators consistent with the rest of estimators.
285+
:pr:`25697` by :user:`John Pangas <jpangas>`.
286+
287+
- |Enhancement| The `n_iter_` attribute has been included in
288+
:class:`linear_model.ARDRegression` to expose the actual number of iterations
289+
required to reach the stopping criterion.
290+
:pr:`25697` by :user:`John Pangas <jpangas>`.
279291

280292
:mod:`sklearn.metrics`
281293
......................

sklearn/linear_model/_bayes.py

Lines changed: 100 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Authors: V. Michel, F. Pedregosa, A. Gramfort
66
# License: BSD 3 clause
77

8+
import warnings
89
from math import log
910
from numbers import Integral, Real
1011
import numpy as np
@@ -15,7 +16,49 @@
1516
from ..utils.extmath import fast_logdet
1617
from scipy.linalg import pinvh
1718
from ..utils.validation import _check_sample_weight
18-
from ..utils._param_validation import Interval
19+
from ..utils._param_validation import Interval, Hidden, StrOptions
20+
21+
22+
# TODO(1.5) Remove
23+
def _deprecate_n_iter(n_iter, max_iter):
24+
"""Deprecates n_iter in favour of max_iter. Checks if the n_iter has been
25+
used instead of max_iter and generates a deprecation warning if True.
26+
27+
Parameters
28+
----------
29+
n_iter : int,
30+
Value of n_iter attribute passed by the estimator.
31+
32+
max_iter : int, default=None
33+
Value of max_iter attribute passed by the estimator.
34+
If `None`, it corresponds to `max_iter=300`.
35+
36+
Returns
37+
-------
38+
max_iter : int,
39+
Value of max_iter which shall further be used by the estimator.
40+
41+
Notes
42+
-----
43+
This function should be completely removed in 1.5.
44+
"""
45+
if n_iter != "deprecated":
46+
if max_iter is not None:
47+
raise ValueError(
48+
"Both `n_iter` and `max_iter` attributes were set. Attribute"
49+
" `n_iter` was deprecated in version 1.3 and will be removed in"
50+
" 1.5. To avoid this error, only set the `max_iter` attribute."
51+
)
52+
warnings.warn(
53+
"'n_iter' was renamed to 'max_iter' in version 1.3 and "
54+
"will be removed in 1.5",
55+
FutureWarning,
56+
)
57+
max_iter = n_iter
58+
elif max_iter is None:
59+
max_iter = 300
60+
return max_iter
61+
1962

2063
###############################################################################
2164
# BayesianRidge regression
@@ -32,8 +75,12 @@ class BayesianRidge(RegressorMixin, LinearModel):
3275
3376
Parameters
3477
----------
35-
n_iter : int, default=300
36-
Maximum number of iterations. Should be greater than or equal to 1.
78+
max_iter : int, default=None
79+
Maximum number of iterations over the complete dataset before
80+
stopping independently of any early stopping criterion. If `None`, it
81+
corresponds to `max_iter=300`.
82+
83+
.. versionchanged:: 1.3
3784
3885
tol : float, default=1e-3
3986
Stop the algorithm if w has converged.
@@ -83,14 +130,21 @@ class BayesianRidge(RegressorMixin, LinearModel):
83130
verbose : bool, default=False
84131
Verbose mode when fitting the model.
85132
133+
n_iter : int
134+
Maximum number of iterations. Should be greater than or equal to 1.
135+
136+
.. deprecated:: 1.3
137+
`n_iter` is deprecated in 1.3 and will be removed in 1.5. Use
138+
`max_iter` instead.
139+
86140
Attributes
87141
----------
88142
coef_ : array-like of shape (n_features,)
89143
Coefficients of the regression model (mean of distribution)
90144
91145
intercept_ : float
92146
Independent term in decision function. Set to 0.0 if
93-
``fit_intercept = False``.
147+
`fit_intercept = False`.
94148
95149
alpha_ : float
96150
Estimated precision of the noise.
@@ -162,7 +216,7 @@ class BayesianRidge(RegressorMixin, LinearModel):
162216
"""
163217

164218
_parameter_constraints: dict = {
165-
"n_iter": [Interval(Integral, 1, None, closed="left")],
219+
"max_iter": [Interval(Integral, 1, None, closed="left"), None],
166220
"tol": [Interval(Real, 0, None, closed="neither")],
167221
"alpha_1": [Interval(Real, 0, None, closed="left")],
168222
"alpha_2": [Interval(Real, 0, None, closed="left")],
@@ -174,12 +228,16 @@ class BayesianRidge(RegressorMixin, LinearModel):
174228
"fit_intercept": ["boolean"],
175229
"copy_X": ["boolean"],
176230
"verbose": ["verbose"],
231+
"n_iter": [
232+
Interval(Integral, 1, None, closed="left"),
233+
Hidden(StrOptions({"deprecated"})),
234+
],
177235
}
178236

179237
def __init__(
180238
self,
181239
*,
182-
n_iter=300,
240+
max_iter=None, # TODO(1.5): Set to 300
183241
tol=1.0e-3,
184242
alpha_1=1.0e-6,
185243
alpha_2=1.0e-6,
@@ -191,8 +249,9 @@ def __init__(
191249
fit_intercept=True,
192250
copy_X=True,
193251
verbose=False,
252+
n_iter="deprecated", # TODO(1.5): Remove
194253
):
195-
self.n_iter = n_iter
254+
self.max_iter = max_iter
196255
self.tol = tol
197256
self.alpha_1 = alpha_1
198257
self.alpha_2 = alpha_2
@@ -204,6 +263,7 @@ def __init__(
204263
self.fit_intercept = fit_intercept
205264
self.copy_X = copy_X
206265
self.verbose = verbose
266+
self.n_iter = n_iter
207267

208268
def fit(self, X, y, sample_weight=None):
209269
"""Fit the model.
@@ -228,6 +288,8 @@ def fit(self, X, y, sample_weight=None):
228288
"""
229289
self._validate_params()
230290

291+
max_iter = _deprecate_n_iter(self.n_iter, self.max_iter)
292+
231293
X, y = self._validate_data(X, y, dtype=[np.float64, np.float32], y_numeric=True)
232294

233295
if sample_weight is not None:
@@ -274,7 +336,7 @@ def fit(self, X, y, sample_weight=None):
274336
eigen_vals_ = S**2
275337

276338
# Convergence loop of the bayesian ridge regression
277-
for iter_ in range(self.n_iter):
339+
for iter_ in range(max_iter):
278340

279341
# update posterior mean coef_ based on alpha_ and lambda_ and
280342
# compute corresponding rmse
@@ -430,8 +492,10 @@ class ARDRegression(RegressorMixin, LinearModel):
430492
431493
Parameters
432494
----------
433-
n_iter : int, default=300
434-
Maximum number of iterations.
495+
max_iter : int, default=None
496+
Maximum number of iterations. If `None`, it corresponds to `max_iter=300`.
497+
498+
.. versionchanged:: 1.3
435499
436500
tol : float, default=1e-3
437501
Stop the algorithm if w has converged.
@@ -470,6 +534,13 @@ class ARDRegression(RegressorMixin, LinearModel):
470534
verbose : bool, default=False
471535
Verbose mode when fitting the model.
472536
537+
n_iter : int
538+
Maximum number of iterations.
539+
540+
.. deprecated:: 1.3
541+
`n_iter` is deprecated in 1.3 and will be removed in 1.5. Use
542+
`max_iter` instead.
543+
473544
Attributes
474545
----------
475546
coef_ : array-like of shape (n_features,)
@@ -487,6 +558,11 @@ class ARDRegression(RegressorMixin, LinearModel):
487558
scores_ : float
488559
if computed, value of the objective function (to be maximized)
489560
561+
n_iter_ : int
562+
The actual number of iterations to reach the stopping criterion.
563+
564+
.. versionadded:: 1.3
565+
490566
intercept_ : float
491567
Independent term in decision function. Set to 0.0 if
492568
``fit_intercept = False``.
@@ -542,7 +618,7 @@ class ARDRegression(RegressorMixin, LinearModel):
542618
"""
543619

544620
_parameter_constraints: dict = {
545-
"n_iter": [Interval(Integral, 1, None, closed="left")],
621+
"max_iter": [Interval(Integral, 1, None, closed="left"), None],
546622
"tol": [Interval(Real, 0, None, closed="left")],
547623
"alpha_1": [Interval(Real, 0, None, closed="left")],
548624
"alpha_2": [Interval(Real, 0, None, closed="left")],
@@ -553,12 +629,16 @@ class ARDRegression(RegressorMixin, LinearModel):
553629
"fit_intercept": ["boolean"],
554630
"copy_X": ["boolean"],
555631
"verbose": ["verbose"],
632+
"n_iter": [
633+
Interval(Integral, 1, None, closed="left"),
634+
Hidden(StrOptions({"deprecated"})),
635+
],
556636
}
557637

558638
def __init__(
559639
self,
560640
*,
561-
n_iter=300,
641+
max_iter=None, # TODO(1.5): Set to 300
562642
tol=1.0e-3,
563643
alpha_1=1.0e-6,
564644
alpha_2=1.0e-6,
@@ -569,8 +649,9 @@ def __init__(
569649
fit_intercept=True,
570650
copy_X=True,
571651
verbose=False,
652+
n_iter="deprecated", # TODO(1.5): Remove
572653
):
573-
self.n_iter = n_iter
654+
self.max_iter = max_iter
574655
self.tol = tol
575656
self.fit_intercept = fit_intercept
576657
self.alpha_1 = alpha_1
@@ -581,6 +662,7 @@ def __init__(
581662
self.threshold_lambda = threshold_lambda
582663
self.copy_X = copy_X
583664
self.verbose = verbose
665+
self.n_iter = n_iter
584666

585667
def fit(self, X, y):
586668
"""Fit the model according to the given training data and parameters.
@@ -603,6 +685,8 @@ def fit(self, X, y):
603685

604686
self._validate_params()
605687

688+
max_iter = _deprecate_n_iter(self.n_iter, self.max_iter)
689+
606690
X, y = self._validate_data(
607691
X, y, dtype=[np.float64, np.float32], y_numeric=True, ensure_min_samples=2
608692
)
@@ -648,7 +732,7 @@ def update_coeff(X, y, coef_, alpha_, keep_lambda, sigma_):
648732
else self._update_sigma_woodbury
649733
)
650734
# Iterative procedure of ARDRegression
651-
for iter_ in range(self.n_iter):
735+
for iter_ in range(max_iter):
652736
sigma_ = update_sigma(X, alpha_, lambda_, keep_lambda)
653737
coef_ = update_coeff(X, y, coef_, alpha_, keep_lambda, sigma_)
654738

@@ -688,6 +772,8 @@ def update_coeff(X, y, coef_, alpha_, keep_lambda, sigma_):
688772
if not keep_lambda.any():
689773
break
690774

775+
self.n_iter_ = iter_ + 1
776+
691777
if keep_lambda.any():
692778
# update sigma and mu using updated params from the last iteration
693779
sigma_ = update_sigma(X, alpha_, lambda_, keep_lambda)

sklearn/linear_model/tests/test_bayes.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_bayesian_ridge_score_values():
7373
alpha_2=alpha_2,
7474
lambda_1=lambda_1,
7575
lambda_2=lambda_2,
76-
n_iter=1,
76+
max_iter=1,
7777
fit_intercept=False,
7878
compute_score=True,
7979
)
@@ -174,7 +174,7 @@ def test_update_of_sigma_in_ard():
174174
# of the ARDRegression algorithm. See issue #10128.
175175
X = np.array([[1, 0], [0, 0]])
176176
y = np.array([0, 0])
177-
clf = ARDRegression(n_iter=1)
177+
clf = ARDRegression(max_iter=1)
178178
clf.fit(X, y)
179179
# With the inputs above, ARDRegression prunes both of the two coefficients
180180
# in the first iteration. Hence, the expected shape of `sigma_` is (0, 0).
@@ -292,3 +292,33 @@ def test_dtype_correctness(Estimator):
292292
coef_32 = model.fit(X.astype(np.float32), y).coef_
293293
coef_64 = model.fit(X.astype(np.float64), y).coef_
294294
np.testing.assert_allclose(coef_32, coef_64, rtol=1e-4)
295+
296+
297+
# TODO(1.5) remove
298+
@pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
299+
def test_bayesian_ridge_ard_n_iter_deprecated(Estimator):
300+
"""Check the deprecation warning of `n_iter`."""
301+
depr_msg = (
302+
"'n_iter' was renamed to 'max_iter' in version 1.3 and will be removed in 1.5"
303+
)
304+
X, y = diabetes.data, diabetes.target
305+
model = Estimator(n_iter=5)
306+
307+
with pytest.warns(FutureWarning, match=depr_msg):
308+
model.fit(X, y)
309+
310+
311+
# TODO(1.5) remove
312+
@pytest.mark.parametrize("Estimator", [BayesianRidge, ARDRegression])
313+
def test_bayesian_ridge_ard_max_iter_and_n_iter_both_set(Estimator):
314+
"""Check that a ValueError is raised when both `max_iter` and `n_iter` are set."""
315+
err_msg = (
316+
"Both `n_iter` and `max_iter` attributes were set. Attribute"
317+
" `n_iter` was deprecated in version 1.3 and will be removed in"
318+
" 1.5. To avoid this error, only set the `max_iter` attribute."
319+
)
320+
X, y = diabetes.data, diabetes.target
321+
model = Estimator(n_iter=5, max_iter=5)
322+
323+
with pytest.raises(ValueError, match=err_msg):
324+
model.fit(X, y)

0 commit comments

Comments
 (0)