diff --git a/benchmarks/bench_20newsgroups.py b/benchmarks/bench_20newsgroups.py index a559bc59b5f8a..44a117f1ad42d 100644 --- a/benchmarks/bench_20newsgroups.py +++ b/benchmarks/bench_20newsgroups.py @@ -21,7 +21,7 @@ "extra_trees": ExtraTreesClassifier(max_features="sqrt", min_samples_split=10), "logistic_regression": LogisticRegression(), "naive_bayes": MultinomialNB(), - "adaboost": AdaBoostClassifier(n_estimators=10), + "adaboost": AdaBoostClassifier(n_estimators=10, algorithm="SAMME"), } diff --git a/doc/conf.py b/doc/conf.py index bfeb7a1b05940..55d5c5984e5e0 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -300,6 +300,9 @@ "auto_examples/decomposition/plot_beta_divergence": ( "auto_examples/applications/plot_topics_extraction_with_nmf_lda" ), + "auto_examples/ensemble/plot_adaboost_hastie_10_2": ( + "auto_examples/ensemble/plot_adaboost_multiclass" + ), } html_context["redirects"] = redirects for old_link in redirects: diff --git a/doc/modules/ensemble.rst b/doc/modules/ensemble.rst index 36eed98da0f6b..a9c3daa74bce5 100644 --- a/doc/modules/ensemble.rst +++ b/doc/modules/ensemble.rst @@ -1570,15 +1570,15 @@ ever-increasing influence. Each subsequent weak learner is thereby forced to concentrate on the examples that are missed by the previous ones in the sequence [HTF]_. -.. figure:: ../auto_examples/ensemble/images/sphx_glr_plot_adaboost_hastie_10_2_001.png - :target: ../auto_examples/ensemble/plot_adaboost_hastie_10_2.html +.. figure:: ../auto_examples/ensemble/images/sphx_glr_plot_adaboost_multiclass_001.png + :target: ../auto_examples/ensemble/plot_adaboost_multiclass.html :align: center :scale: 75 AdaBoost can be used both for classification and regression problems: - For multi-class classification, :class:`AdaBoostClassifier` implements - AdaBoost-SAMME and AdaBoost-SAMME.R [ZZRH2009]_. + AdaBoost.SAMME [ZZRH2009]_. - For regression, :class:`AdaBoostRegressor` implements AdaBoost.R2 [D1997]_. @@ -1593,7 +1593,7 @@ learners:: >>> from sklearn.ensemble import AdaBoostClassifier >>> X, y = load_iris(return_X_y=True) - >>> clf = AdaBoostClassifier(n_estimators=100) + >>> clf = AdaBoostClassifier(n_estimators=100, algorithm="SAMME",) >>> scores = cross_val_score(clf, X, y, cv=5) >>> scores.mean() 0.9... @@ -1608,12 +1608,8 @@ minimum required number of samples to consider a split ``min_samples_split``). .. topic:: Examples: - * :ref:`sphx_glr_auto_examples_ensemble_plot_adaboost_hastie_10_2.py` compares the - classification error of a decision stump, decision tree, and a boosted - decision stump using AdaBoost-SAMME and AdaBoost-SAMME.R. - * :ref:`sphx_glr_auto_examples_ensemble_plot_adaboost_multiclass.py` shows the performance - of AdaBoost-SAMME and AdaBoost-SAMME.R on a multi-class problem. + of AdaBoost on a multi-class problem. * :ref:`sphx_glr_auto_examples_ensemble_plot_adaboost_twoclass.py` shows the decision boundary and decision function values for a non-linearly separable two-class problem @@ -1634,4 +1630,3 @@ minimum required number of samples to consider a split ``min_samples_split``). .. [HTF] T. Hastie, R. Tibshirani and J. Friedman, "Elements of Statistical Learning Ed. 2", Springer, 2009. - diff --git a/doc/whats_new/v1.4.rst b/doc/whats_new/v1.4.rst index fdb0e6011d4ed..d54c276b3292b 100644 --- a/doc/whats_new/v1.4.rst +++ b/doc/whats_new/v1.4.rst @@ -150,6 +150,10 @@ Changelog :pr:`13649` by :user:`Samuel Ronsin `, initiated by :user:`Patrick O'Reilly `. +- |API| In :class:`AdaBoostClassifier`, the `algorithm` argument `SAMME.R` was + deprecated and will be removed in 1.6. :pr:`26830` by :user:`Stefanie Senger + `. + - |Efficiency| :class:`ensemble.GradientBoostingClassifier` is faster, for binary and in particular for multiclass problems thanks to the private loss function module. diff --git a/examples/classification/plot_classifier_comparison.py b/examples/classification/plot_classifier_comparison.py index 37dfa49af0bfd..6a4a4cb60db88 100644 --- a/examples/classification/plot_classifier_comparison.py +++ b/examples/classification/plot_classifier_comparison.py @@ -66,7 +66,7 @@ max_depth=5, n_estimators=10, max_features=1, random_state=42 ), MLPClassifier(alpha=1, max_iter=1000, random_state=42), - AdaBoostClassifier(random_state=42), + AdaBoostClassifier(algorithm="SAMME", random_state=42), GaussianNB(), QuadraticDiscriminantAnalysis(), ] diff --git a/examples/ensemble/plot_adaboost_hastie_10_2.py b/examples/ensemble/plot_adaboost_hastie_10_2.py deleted file mode 100644 index 313056286f6ba..0000000000000 --- a/examples/ensemble/plot_adaboost_hastie_10_2.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -============================= -Discrete versus Real AdaBoost -============================= - -This notebook is based on Figure 10.2 from Hastie et al 2009 [1]_ and -illustrates the difference in performance between the discrete SAMME [2]_ -boosting algorithm and real SAMME.R boosting algorithm. Both algorithms are -evaluated on a binary classification task where the target Y is a non-linear -function of 10 input features. - -Discrete SAMME AdaBoost adapts based on errors in predicted class labels -whereas real SAMME.R uses the predicted class probabilities. - -.. [1] T. Hastie, R. Tibshirani and J. Friedman, "Elements of Statistical - Learning Ed. 2", Springer, 2009. - -.. [2] J Zhu, H. Zou, S. Rosset, T. Hastie, "Multi-class AdaBoost", - Statistics and Its Interface, 2009. - -""" - -# %% -# Preparing the data and baseline models -# -------------------------------------- -# We start by generating the binary classification dataset -# used in Hastie et al. 2009, Example 10.2. - -# Authors: Peter Prettenhofer , -# Noel Dawe -# -# License: BSD 3 clause - -from sklearn import datasets - -X, y = datasets.make_hastie_10_2(n_samples=12_000, random_state=1) - -# %% -# Now, we set the hyperparameters for our AdaBoost classifiers. -# Be aware, a learning rate of 1.0 may not be optimal for both SAMME and SAMME.R - -n_estimators = 400 -learning_rate = 1.0 - -# %% -# We split the data into a training and a test set. -# Then, we train our baseline classifiers, a `DecisionTreeClassifier` with `depth=9` -# and a "stump" `DecisionTreeClassifier` with `depth=1` and compute the test error. - -from sklearn.model_selection import train_test_split -from sklearn.tree import DecisionTreeClassifier - -X_train, X_test, y_train, y_test = train_test_split( - X, y, test_size=2_000, shuffle=False -) - -dt_stump = DecisionTreeClassifier(max_depth=1, min_samples_leaf=1) -dt_stump.fit(X_train, y_train) -dt_stump_err = 1.0 - dt_stump.score(X_test, y_test) - -dt = DecisionTreeClassifier(max_depth=9, min_samples_leaf=1) -dt.fit(X_train, y_train) -dt_err = 1.0 - dt.score(X_test, y_test) - -# %% -# Adaboost with discrete SAMME and real SAMME.R -# --------------------------------------------- -# We now define the discrete and real AdaBoost classifiers -# and fit them to the training set. - -from sklearn.ensemble import AdaBoostClassifier - -ada_discrete = AdaBoostClassifier( - estimator=dt_stump, - learning_rate=learning_rate, - n_estimators=n_estimators, - algorithm="SAMME", -) -ada_discrete.fit(X_train, y_train) - -# %% - -ada_real = AdaBoostClassifier( - estimator=dt_stump, - learning_rate=learning_rate, - n_estimators=n_estimators, - algorithm="SAMME.R", -) -ada_real.fit(X_train, y_train) - -# %% -# Now, let's compute the test error of the discrete and -# real AdaBoost classifiers for each new stump in `n_estimators` -# added to the ensemble. - -import numpy as np - -from sklearn.metrics import zero_one_loss - -ada_discrete_err = np.zeros((n_estimators,)) -for i, y_pred in enumerate(ada_discrete.staged_predict(X_test)): - ada_discrete_err[i] = zero_one_loss(y_pred, y_test) - -ada_discrete_err_train = np.zeros((n_estimators,)) -for i, y_pred in enumerate(ada_discrete.staged_predict(X_train)): - ada_discrete_err_train[i] = zero_one_loss(y_pred, y_train) - -ada_real_err = np.zeros((n_estimators,)) -for i, y_pred in enumerate(ada_real.staged_predict(X_test)): - ada_real_err[i] = zero_one_loss(y_pred, y_test) - -ada_real_err_train = np.zeros((n_estimators,)) -for i, y_pred in enumerate(ada_real.staged_predict(X_train)): - ada_real_err_train[i] = zero_one_loss(y_pred, y_train) - -# %% -# Plotting the results -# -------------------- -# Finally, we plot the train and test errors of our baselines -# and of the discrete and real AdaBoost classifiers - -import matplotlib.pyplot as plt -import seaborn as sns - -fig = plt.figure() -ax = fig.add_subplot(111) - -ax.plot([1, n_estimators], [dt_stump_err] * 2, "k-", label="Decision Stump Error") -ax.plot([1, n_estimators], [dt_err] * 2, "k--", label="Decision Tree Error") - -colors = sns.color_palette("colorblind") - -ax.plot( - np.arange(n_estimators) + 1, - ada_discrete_err, - label="Discrete AdaBoost Test Error", - color=colors[0], -) -ax.plot( - np.arange(n_estimators) + 1, - ada_discrete_err_train, - label="Discrete AdaBoost Train Error", - color=colors[1], -) -ax.plot( - np.arange(n_estimators) + 1, - ada_real_err, - label="Real AdaBoost Test Error", - color=colors[2], -) -ax.plot( - np.arange(n_estimators) + 1, - ada_real_err_train, - label="Real AdaBoost Train Error", - color=colors[4], -) - -ax.set_ylim((0.0, 0.5)) -ax.set_xlabel("Number of weak learners") -ax.set_ylabel("error rate") - -leg = ax.legend(loc="upper right", fancybox=True) -leg.get_frame().set_alpha(0.7) - -plt.show() -# %% -# -# Concluding remarks -# ------------------ -# -# We observe that the error rate for both train and test sets of real AdaBoost -# is lower than that of discrete AdaBoost. diff --git a/examples/ensemble/plot_forest_iris.py b/examples/ensemble/plot_forest_iris.py index 6aaceea88efd2..c2056ce1905d1 100644 --- a/examples/ensemble/plot_forest_iris.py +++ b/examples/ensemble/plot_forest_iris.py @@ -71,7 +71,11 @@ DecisionTreeClassifier(max_depth=None), RandomForestClassifier(n_estimators=n_estimators), ExtraTreesClassifier(n_estimators=n_estimators), - AdaBoostClassifier(DecisionTreeClassifier(max_depth=3), n_estimators=n_estimators), + AdaBoostClassifier( + DecisionTreeClassifier(max_depth=3), + n_estimators=n_estimators, + algorithm="SAMME", + ), ] for pair in ([0, 1], [0, 2], [2, 3]): diff --git a/sklearn/ensemble/_weight_boosting.py b/sklearn/ensemble/_weight_boosting.py index 45a87ad6521fd..3072d7f6749b8 100644 --- a/sklearn/ensemble/_weight_boosting.py +++ b/sklearn/ensemble/_weight_boosting.py @@ -341,13 +341,13 @@ def _samme_proba(estimator, n_classes, X): class AdaBoostClassifier(ClassifierMixin, BaseWeightBoosting): """An AdaBoost classifier. - An AdaBoost [1] classifier is a meta-estimator that begins by fitting a + An AdaBoost [1]_ classifier is a meta-estimator that begins by fitting a classifier on the original dataset and then fits additional copies of the classifier on the same dataset but where the weights of incorrectly classified instances are adjusted such that subsequent classifiers focus more on difficult cases. - This class implements the algorithm known as AdaBoost-SAMME [2]. + This class implements the algorithm based on [2]_. Read more in the :ref:`User Guide `. @@ -383,6 +383,10 @@ class AdaBoostClassifier(ClassifierMixin, BaseWeightBoosting): The SAMME.R algorithm typically converges faster than SAMME, achieving a lower test error with fewer boosting iterations. + .. deprecated:: 1.4 + `"SAMME.R"` is deprecated and will be removed in version 1.6. + '"SAMME"' will become the default. + random_state : int, RandomState instance or None, default=None Controls the random seed given at each `estimator` at each boosting iteration. @@ -474,7 +478,9 @@ class AdaBoostClassifier(ClassifierMixin, BaseWeightBoosting): .. [1] Y. Freund, R. Schapire, "A Decision-Theoretic Generalization of on-Line Learning and an Application to Boosting", 1995. - .. [2] J. Zhu, H. Zou, S. Rosset, T. Hastie, "Multi-class AdaBoost", 2009. + .. [2] :doi:`J. Zhu, H. Zou, S. Rosset, T. Hastie, "Multi-class adaboost." + Statistics and its Interface 2.3 (2009): 349-360. + <10.4310/SII.2009.v2.n3.a8>` Examples -------- @@ -483,20 +489,25 @@ class AdaBoostClassifier(ClassifierMixin, BaseWeightBoosting): >>> X, y = make_classification(n_samples=1000, n_features=4, ... n_informative=2, n_redundant=0, ... random_state=0, shuffle=False) - >>> clf = AdaBoostClassifier(n_estimators=100, random_state=0) + >>> clf = AdaBoostClassifier(n_estimators=100, algorithm="SAMME", random_state=0) >>> clf.fit(X, y) - AdaBoostClassifier(n_estimators=100, random_state=0) + AdaBoostClassifier(algorithm='SAMME', n_estimators=100, random_state=0) >>> clf.predict([[0, 0, 0, 0]]) array([1]) >>> clf.score(X, y) - 0.983... + 0.96... """ + # TODO(1.6): Modify _parameter_constraints for "algorithm" to only check + # for "SAMME" _parameter_constraints: dict = { **BaseWeightBoosting._parameter_constraints, - "algorithm": [StrOptions({"SAMME", "SAMME.R"})], + "algorithm": [ + StrOptions({"SAMME", "SAMME.R"}), + ], } + # TODO(1.6): Change default "algorithm" value to "SAMME" def __init__( self, estimator=None, @@ -521,8 +532,18 @@ def _validate_estimator(self): """Check the estimator and set the estimator_ attribute.""" super()._validate_estimator(default=DecisionTreeClassifier(max_depth=1)) - # SAMME-R requires predict_proba-enabled base estimators - if self.algorithm == "SAMME.R": + # TODO(1.6): Remove, as "SAMME.R" value for "algorithm" param will be + # removed in 1.6 + # SAMME-R requires predict_proba-enabled base estimators + if self.algorithm != "SAMME": + warnings.warn( + ( + "The SAMME.R algorithm (the default) is deprecated and will be" + " removed in 1.6. Use the SAMME algorithm to circumvent this" + " warning." + ), + FutureWarning, + ) if not hasattr(self.estimator_, "predict_proba"): raise TypeError( "AdaBoostClassifier with algorithm='SAMME.R' requires " @@ -531,11 +552,17 @@ def _validate_estimator(self): "Please change the base estimator or set " "algorithm='SAMME' instead." ) + if not has_fit_parameter(self.estimator_, "sample_weight"): raise ValueError( f"{self.estimator.__class__.__name__} doesn't support sample_weight." ) + # TODO(1.6): Redefine the scope of the `_boost` and `_boost_discrete` + # functions to be the same since SAMME will be the default value for the + # "algorithm" parameter in version 1.6. Thus, a distinguishing function is + # no longer needed. (Or adjust code here, if another algorithm, shall be + # used instead of SAMME.R.) def _boost(self, iboost, X, y, sample_weight, random_state): """Implement a single boost. @@ -581,6 +608,8 @@ def _boost(self, iboost, X, y, sample_weight, random_state): else: # elif self.algorithm == "SAMME": return self._boost_discrete(iboost, X, y, sample_weight, random_state) + # TODO(1.6): Remove function. The `_boost_real` function won't be used any + # longer, because the SAMME.R algorithm will be deprecated in 1.6. def _boost_real(self, iboost, X, y, sample_weight, random_state): """Implement a single boost using the SAMME.R real algorithm.""" estimator = self._make_estimator(random_state=random_state) @@ -773,6 +802,7 @@ class in ``classes_``, respectively. n_classes = self.n_classes_ classes = self.classes_[:, np.newaxis] + # TODO(1.6): Remove, because "algorithm" param will be deprecated in 1.6 if self.algorithm == "SAMME.R": # The weights are all 1. for SAMME.R pred = sum( @@ -827,6 +857,8 @@ class in ``classes_``, respectively. for weight, estimator in zip(self.estimator_weights_, self.estimators_): norm += weight + # TODO(1.6): Remove, because "algorithm" param will be deprecated in + # 1.6 if self.algorithm == "SAMME.R": # The weights are all 1. for SAMME.R current_pred = _samme_proba(estimator, n_classes, X) diff --git a/sklearn/ensemble/tests/test_weight_boosting.py b/sklearn/ensemble/tests/test_weight_boosting.py index 12c21660cdef7..95f0ea69fe7c2 100755 --- a/sklearn/ensemble/tests/test_weight_boosting.py +++ b/sklearn/ensemble/tests/test_weight_boosting.py @@ -87,10 +87,14 @@ def test_oneclass_adaboost_proba(): # In response to issue #7501 # https://github.com/scikit-learn/scikit-learn/issues/7501 y_t = np.ones(len(X)) - clf = AdaBoostClassifier().fit(X, y_t) + clf = AdaBoostClassifier(algorithm="SAMME").fit(X, y_t) assert_array_almost_equal(clf.predict_proba(X), np.ones((len(X), 1))) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_classification_toy(algorithm): # Check classification on a toy dataset. @@ -109,6 +113,10 @@ def test_regression_toy(): assert_array_equal(clf.predict(T), y_t_regr) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") def test_iris(): # Check consistency on dataset iris. classes = np.unique(iris.target) @@ -157,6 +165,10 @@ def test_diabetes(loss): assert len(set(est.random_state for est in reg.estimators_)) == len(reg.estimators_) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_staged_predict(algorithm): # Check staged predictions. @@ -222,6 +234,10 @@ def test_gridsearch(): clf.fit(diabetes.data, diabetes.target) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") def test_pickle(): # Check pickability. import pickle @@ -250,6 +266,10 @@ def test_pickle(): assert score == score2 +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") def test_importances(): # Check variable importances. X, y = datasets.make_classification( @@ -286,7 +306,7 @@ def test_estimator(): # XXX doesn't work with y_class because RF doesn't support classes_ # Shouldn't AdaBoost run a LabelBinarizer? - clf = AdaBoostClassifier(RandomForestClassifier()) + clf = AdaBoostClassifier(RandomForestClassifier(), algorithm="SAMME") clf.fit(X, y_regr) clf = AdaBoostClassifier(SVC(), algorithm="SAMME") @@ -510,7 +530,9 @@ def test_multidimensional_X(): yc = rng.choice([0, 1], 51) yr = rng.randn(51) - boost = AdaBoostClassifier(DummyClassifier(strategy="most_frequent")) + boost = AdaBoostClassifier( + DummyClassifier(strategy="most_frequent"), algorithm="SAMME" + ) boost.fit(X, yc) boost.predict(X) boost.predict_proba(X) @@ -520,6 +542,10 @@ def test_multidimensional_X(): boost.predict(X) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_adaboostclassifier_without_sample_weight(algorithm): X, y = iris.data, iris.target @@ -568,6 +594,10 @@ def test_adaboostregressor_sample_weight(): assert score_no_outlier == pytest.approx(score_with_weight) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_adaboost_consistent_predict(algorithm): # check that predict_proba and predict give consistent results @@ -612,7 +642,9 @@ def test_adaboost_numerically_stable_feature_importance_with_small_weights(): y = rng.choice([0, 1], size=1000) sample_weight = np.ones_like(y) * 1e-263 tree = DecisionTreeClassifier(max_depth=10, random_state=12) - ada_model = AdaBoostClassifier(estimator=tree, n_estimators=20, random_state=12) + ada_model = AdaBoostClassifier( + estimator=tree, n_estimators=20, algorithm="SAMME", random_state=12 + ) ada_model.fit(X, y, sample_weight=sample_weight) assert np.isnan(ada_model.feature_importances_).sum() == 0 @@ -659,6 +691,10 @@ def test_base_estimator_argument_deprecated_none(AdaBoost): model.fit(X, y) +# TODO(1.6): remove "@pytest.mark.filterwarnings" (or when the test is +# removed), as SAMME.R will be removed and substituted with the SAMME algrithm +# as a default) +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") # TODO(1.4): remove in 1.4 @pytest.mark.parametrize( "AdaBoost", @@ -696,6 +732,10 @@ def test_deprecated_base_estimator_parameters_can_be_set(): clf.set_params(base_estimator__max_depth=2) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default; also re-write test to +# only consider "SAMME" +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_adaboost_decision_function(algorithm, global_random_seed): """Check that the decision function respects the symmetric constraint for weak @@ -736,3 +776,13 @@ def test_adaboost_decision_function(algorithm, global_random_seed): for y_score in clf.staged_decision_function(X): assert_allclose(y_score.sum(axis=1), 0, atol=1e-8) + + +# TODO(1.6): remove +def test_deprecated_samme_r_algorithm(): + adaboost_clf = AdaBoostClassifier(n_estimators=1) + with pytest.warns( + FutureWarning, + match=re.escape("The SAMME.R algorithm (the default) is deprecated"), + ): + adaboost_clf.fit(X, y_class) diff --git a/sklearn/tests/test_docstring_parameters.py b/sklearn/tests/test_docstring_parameters.py index c2ef66e74e097..9169f24310856 100644 --- a/sklearn/tests/test_docstring_parameters.py +++ b/sklearn/tests/test_docstring_parameters.py @@ -201,6 +201,9 @@ def _construct_sparse_coder(Estimator): @ignore_warnings(category=sklearn.exceptions.ConvergenceWarning) +# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed +# and substituted with the SAMME algorithm as a default +@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("name, Estimator", all_estimators()) def test_fit_docstring_attributes(name, Estimator): pytest.importorskip("numpydoc")