Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d4cd14

Browse filesBrowse files
jpangasglemaitre
authored and
Itay
committed
API Replace n_iter in Bayesian Ridge and ARDRegression (scikit-learn#25697)
Co-authored-by: Guillaume Lemaitre <g.lemaitre58@gmail.com>
1 parent 05fdbae commit 7d4cd14
Copy full SHA for 7d4cd14

File tree

Expand file treeCollapse file tree

3 files changed

+147
-19
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+147
-19
lines changed

‎doc/whats_new/v1.3.rst

Copy file name to clipboardExpand all lines: doc/whats_new/v1.3.rst
+15-3Lines 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

Copy file name to clipboardExpand all lines: sklearn/linear_model/_bayes.py
+100-14Lines 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

Copy file name to clipboardExpand all lines: sklearn/linear_model/tests/test_bayes.py
+32-2Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.