diff --git a/sklearn/linear_model/_passive_aggressive.py b/sklearn/linear_model/_passive_aggressive.py index 65f754ba35f55..0b029fe781376 100644 --- a/sklearn/linear_model/_passive_aggressive.py +++ b/sklearn/linear_model/_passive_aggressive.py @@ -1,9 +1,11 @@ # Authors: Rob Zinkov, Mathieu Blondel # License: BSD 3 clause +from numbers import Real from ._stochastic_gradient import BaseSGDClassifier from ._stochastic_gradient import BaseSGDRegressor from ._stochastic_gradient import DEFAULT_EPSILON +from ..utils._param_validation import Interval, StrOptions class PassiveAggressiveClassifier(BaseSGDClassifier): @@ -172,6 +174,12 @@ class PassiveAggressiveClassifier(BaseSGDClassifier): [1] """ + _parameter_constraints = { + **BaseSGDClassifier._parameter_constraints, + "loss": [StrOptions({"hinge", "squared_hinge"})], + "C": [Interval(Real, 0, None, closed="right")], + } + def __init__( self, *, @@ -236,19 +244,23 @@ def partial_fit(self, X, y, classes=None): self : object Fitted estimator. """ - self._validate_params(for_partial_fit=True) - if self.class_weight == "balanced": - raise ValueError( - "class_weight 'balanced' is not supported for " - "partial_fit. For 'balanced' weights, use " - "`sklearn.utils.compute_class_weight` with " - "`class_weight='balanced'`. In place of y you " - "can use a large enough subset of the full " - "training set target to properly estimate the " - "class frequency distributions. Pass the " - "resulting weights as the class_weight " - "parameter." - ) + if not hasattr(self, "classes_"): + self._validate_params() + self._more_validate_params(for_partial_fit=True) + + if self.class_weight == "balanced": + raise ValueError( + "class_weight 'balanced' is not supported for " + "partial_fit. For 'balanced' weights, use " + "`sklearn.utils.compute_class_weight` with " + "`class_weight='balanced'`. In place of y you " + "can use a large enough subset of the full " + "training set target to properly estimate the " + "class frequency distributions. Pass the " + "resulting weights as the class_weight " + "parameter." + ) + lr = "pa1" if self.loss == "hinge" else "pa2" return self._partial_fit( X, @@ -287,6 +299,8 @@ def fit(self, X, y, coef_init=None, intercept_init=None): Fitted estimator. """ self._validate_params() + self._more_validate_params() + lr = "pa1" if self.loss == "hinge" else "pa2" return self._fit( X, @@ -445,6 +459,13 @@ class PassiveAggressiveRegressor(BaseSGDRegressor): [-0.02306214] """ + _parameter_constraints = { + **BaseSGDRegressor._parameter_constraints, + "loss": [StrOptions({"epsilon_insensitive", "squared_epsilon_insensitive"})], + "C": [Interval(Real, 0, None, closed="right")], + "epsilon": [Interval(Real, 0, None, closed="left")], + } + def __init__( self, *, @@ -499,7 +520,10 @@ def partial_fit(self, X, y): self : object Fitted estimator. """ - self._validate_params(for_partial_fit=True) + if not hasattr(self, "coef_"): + self._validate_params() + self._more_validate_params(for_partial_fit=True) + lr = "pa1" if self.loss == "epsilon_insensitive" else "pa2" return self._partial_fit( X, @@ -537,6 +561,8 @@ def fit(self, X, y, coef_init=None, intercept_init=None): Fitted estimator. """ self._validate_params() + self._more_validate_params() + lr = "pa1" if self.loss == "epsilon_insensitive" else "pa2" return self._fit( X, diff --git a/sklearn/linear_model/_perceptron.py b/sklearn/linear_model/_perceptron.py index 2e2eb963cc551..d47e17a92d08b 100644 --- a/sklearn/linear_model/_perceptron.py +++ b/sklearn/linear_model/_perceptron.py @@ -1,7 +1,9 @@ # Author: Mathieu Blondel # License: BSD 3 clause +from numbers import Real from ._stochastic_gradient import BaseSGDClassifier +from ..utils._param_validation import StrOptions, Interval class Perceptron(BaseSGDClassifier): @@ -37,7 +39,7 @@ class Perceptron(BaseSGDClassifier): .. versionadded:: 0.19 - tol : float, default=1e-3 + tol : float or None, default=1e-3 The stopping criterion. If it is not None, the iterations will stop when (loss > previous_loss - tol). @@ -164,6 +166,18 @@ class Perceptron(BaseSGDClassifier): 0.939... """ + _parameter_constraints = {**BaseSGDClassifier._parameter_constraints} + _parameter_constraints.pop("loss") + _parameter_constraints.pop("average") + _parameter_constraints.update( + { + "penalty": [StrOptions({"l2", "l1", "elasticnet"}), None], + "alpha": [Interval(Real, 0, None, closed="left")], + "l1_ratio": [Interval(Real, 0, 1, closed="both")], + "eta0": [Interval(Real, 0, None, closed="left")], + } + ) + def __init__( self, *, diff --git a/sklearn/linear_model/_stochastic_gradient.py b/sklearn/linear_model/_stochastic_gradient.py index faabfb042d9db..ad69a0b828f6b 100644 --- a/sklearn/linear_model/_stochastic_gradient.py +++ b/sklearn/linear_model/_stochastic_gradient.py @@ -10,6 +10,7 @@ import warnings from abc import ABCMeta, abstractmethod +from numbers import Integral, Real from joblib import Parallel @@ -22,6 +23,9 @@ from ..utils.extmath import safe_sparse_dot from ..utils.multiclass import _check_partial_fit_first_call from ..utils.validation import check_is_fitted, _check_sample_weight +from ..utils._param_validation import Interval +from ..utils._param_validation import StrOptions +from ..utils._param_validation import Hidden from ..utils.fixes import delayed from ..exceptions import ConvergenceWarning from ..model_selection import StratifiedShuffleSplit, ShuffleSplit @@ -76,6 +80,17 @@ def __call__(self, coef, intercept): class BaseSGD(SparseCoefMixin, BaseEstimator, metaclass=ABCMeta): """Base class for SGD classification and regression.""" + _parameter_constraints = { + "fit_intercept": ["boolean"], + "max_iter": [Interval(Integral, 1, None, closed="left")], + "tol": [Interval(Real, 0, None, closed="left"), None], + "shuffle": ["boolean"], + "verbose": ["verbose"], + "random_state": ["random_state"], + "warm_start": ["boolean"], + "average": [Interval(Integral, 0, None, closed="left"), bool, np.bool_], + } + def __init__( self, loss, @@ -125,27 +140,15 @@ def __init__( def fit(self, X, y): """Fit model.""" - def _validate_params(self, for_partial_fit=False): + def _more_validate_params(self, for_partial_fit=False): """Validate input params.""" - if not isinstance(self.shuffle, bool): - raise ValueError("shuffle must be either True or False") - if not isinstance(self.early_stopping, bool): - raise ValueError("early_stopping must be either True or False") if self.early_stopping and for_partial_fit: raise ValueError("early_stopping should be False with partial_fit") - if self.max_iter is not None and self.max_iter <= 0: - raise ValueError("max_iter must be > zero. Got %f" % self.max_iter) - if not (0.0 <= self.l1_ratio <= 1.0): - raise ValueError("l1_ratio must be in [0, 1]") - if not isinstance(self, SGDOneClassSVM) and self.alpha < 0.0: - raise ValueError("alpha must be >= 0") - if self.n_iter_no_change < 1: - raise ValueError("n_iter_no_change must be >= 1") - if not (0.0 < self.validation_fraction < 1.0): - raise ValueError("validation_fraction must be in range (0, 1)") - if self.learning_rate in ("constant", "invscaling", "adaptive"): - if self.eta0 <= 0.0: - raise ValueError("eta0 must be > 0") + if ( + self.learning_rate in ("constant", "invscaling", "adaptive") + and self.eta0 <= 0.0 + ): + raise ValueError("eta0 must be > 0") if self.learning_rate == "optimal" and self.alpha == 0: raise ValueError( "alpha must be > 0 since " @@ -157,9 +160,6 @@ def _validate_params(self, for_partial_fit=False): self._get_penalty_type(self.penalty) self._get_learning_rate_type(self.learning_rate) - if self.loss not in self.loss_functions: - raise ValueError("The loss %s is not supported. " % self.loss) - # TODO(1.2): remove "squared_loss" if self.loss == "squared_loss": warnings.warn( @@ -178,29 +178,18 @@ def _validate_params(self, for_partial_fit=False): def _get_loss_function(self, loss): """Get concrete ``LossFunction`` object for str ``loss``.""" - try: - loss_ = self.loss_functions[loss] - loss_class, args = loss_[0], loss_[1:] - if loss in ("huber", "epsilon_insensitive", "squared_epsilon_insensitive"): - args = (self.epsilon,) - return loss_class(*args) - except KeyError as e: - raise ValueError("The loss %s is not supported. " % loss) from e + loss_ = self.loss_functions[loss] + loss_class, args = loss_[0], loss_[1:] + if loss in ("huber", "epsilon_insensitive", "squared_epsilon_insensitive"): + args = (self.epsilon,) + return loss_class(*args) def _get_learning_rate_type(self, learning_rate): - try: - return LEARNING_RATE_TYPES[learning_rate] - except KeyError as e: - raise ValueError( - "learning rate %s is not supported. " % learning_rate - ) from e + return LEARNING_RATE_TYPES[learning_rate] def _get_penalty_type(self, penalty): penalty = str(penalty).lower() - try: - return PENALTY_TYPES[penalty] - except KeyError as e: - raise ValueError("Penalty %s is not supported. " % penalty) from e + return PENALTY_TYPES[penalty] def _allocate_parameter_mem( self, n_classes, n_features, coef_init=None, intercept_init=None, one_class=0 @@ -512,6 +501,21 @@ class BaseSGDClassifier(LinearClassifierMixin, BaseSGD, metaclass=ABCMeta): "squared_epsilon_insensitive": (SquaredEpsilonInsensitive, DEFAULT_EPSILON), } + _parameter_constraints = { + **BaseSGD._parameter_constraints, + "loss": [ + StrOptions( + set(loss_functions), + deprecated={"squared_loss", "log"}, + ) + ], + "early_stopping": ["boolean"], + "validation_fraction": [Interval(Real, 0, 1, closed="neither")], + "n_iter_no_change": [Interval(Integral, 1, None, closed="left")], + "n_jobs": [Integral, None], + "class_weight": [StrOptions({"balanced"}), dict, None], + } + @abstractmethod def __init__( self, @@ -655,7 +659,6 @@ def _fit( intercept_init=None, sample_weight=None, ): - self._validate_params() if hasattr(self, "classes_"): # delete the attribute otherwise _partial_fit thinks it's not the first call delattr(self, "classes_") @@ -832,19 +835,23 @@ def partial_fit(self, X, y, classes=None, sample_weight=None): self : object Returns an instance of self. """ - self._validate_params(for_partial_fit=True) - if self.class_weight in ["balanced"]: - raise ValueError( - "class_weight '{0}' is not supported for " - "partial_fit. In order to use 'balanced' weights," - " use compute_class_weight('{0}', " - "classes=classes, y=y). " - "In place of y you can us a large enough sample " - "of the full training set target to properly " - "estimate the class frequency distributions. " - "Pass the resulting weights as the class_weight " - "parameter.".format(self.class_weight) - ) + if not hasattr(self, "classes_"): + self._validate_params() + self._more_validate_params(for_partial_fit=True) + + if self.class_weight == "balanced": + raise ValueError( + "class_weight '{0}' is not supported for " + "partial_fit. In order to use 'balanced' weights," + " use compute_class_weight('{0}', " + "classes=classes, y=y). " + "In place of y you can use a large enough sample " + "of the full training set target to properly " + "estimate the class frequency distributions. " + "Pass the resulting weights as the class_weight " + "parameter.".format(self.class_weight) + ) + return self._partial_fit( X, y, @@ -887,6 +894,9 @@ def fit(self, X, y, coef_init=None, intercept_init=None, sample_weight=None): self : object Returns an instance of self. """ + self._validate_params() + self._more_validate_params() + return self._fit( X, y, @@ -985,7 +995,7 @@ class SGDClassifier(BaseSGDClassifier): .. versionadded:: 0.19 - tol : float, default=1e-3 + tol : float or None, default=1e-3 The stopping criterion. If it is not None, training will stop when (loss > best_loss - tol) for ``n_iter_no_change`` consecutive epochs. @@ -1167,6 +1177,20 @@ class SGDClassifier(BaseSGDClassifier): [1] """ + _parameter_constraints = { + **BaseSGDClassifier._parameter_constraints, + "penalty": [StrOptions({"l2", "l1", "elasticnet"}), None], + "alpha": [Interval(Real, 0, None, closed="left")], + "l1_ratio": [Interval(Real, 0, 1, closed="both")], + "power_t": [Interval(Real, None, None, closed="neither")], + "epsilon": [Interval(Real, 0, None, closed="left")], + "learning_rate": [ + StrOptions({"constant", "optimal", "invscaling", "adaptive"}), + Hidden(StrOptions({"pa1", "pa2"})), + ], + "eta0": [Interval(Real, 0, None, closed="left")], + } + def __init__( self, loss="hinge", @@ -1353,6 +1377,19 @@ class BaseSGDRegressor(RegressorMixin, BaseSGD): "squared_epsilon_insensitive": (SquaredEpsilonInsensitive, DEFAULT_EPSILON), } + _parameter_constraints = { + **BaseSGD._parameter_constraints, + "loss": [ + StrOptions( + set(loss_functions), + deprecated={"squared_loss"}, + ) + ], + "early_stopping": ["boolean"], + "validation_fraction": [Interval(Real, 0, 1, closed="neither")], + "n_iter_no_change": [Interval(Integral, 1, None, closed="left")], + } + @abstractmethod def __init__( self, @@ -1467,7 +1504,10 @@ def partial_fit(self, X, y, sample_weight=None): self : object Returns an instance of self. """ - self._validate_params(for_partial_fit=True) + if not hasattr(self, "coef_"): + self._validate_params() + self._more_validate_params(for_partial_fit=True) + return self._partial_fit( X, y, @@ -1493,7 +1533,6 @@ def _fit( intercept_init=None, sample_weight=None, ): - self._validate_params() if self.warm_start and getattr(self, "coef_", None) is not None: if coef_init is None: coef_init = self.coef_ @@ -1558,6 +1597,9 @@ def fit(self, X, y, coef_init=None, intercept_init=None, sample_weight=None): self : object Fitted `SGDRegressor` estimator. """ + self._validate_params() + self._more_validate_params() + return self._fit( X, y, @@ -1759,7 +1801,7 @@ class SGDRegressor(BaseSGDRegressor): .. versionadded:: 0.19 - tol : float, default=1e-3 + tol : float or None, default=1e-3 The stopping criterion. If it is not None, training will stop when (loss > best_loss - tol) for ``n_iter_no_change`` consecutive epochs. @@ -1911,6 +1953,20 @@ class SGDRegressor(BaseSGDRegressor): ('sgdregressor', SGDRegressor())]) """ + _parameter_constraints = { + **BaseSGDRegressor._parameter_constraints, + "penalty": [StrOptions({"l2", "l1", "elasticnet"}), None], + "alpha": [Interval(Real, 0, None, closed="left")], + "l1_ratio": [Interval(Real, 0, 1, closed="both")], + "power_t": [Interval(Real, None, None, closed="neither")], + "learning_rate": [ + StrOptions({"constant", "optimal", "invscaling", "adaptive"}), + Hidden(StrOptions({"pa1", "pa2"})), + ], + "epsilon": [Interval(Real, 0, None, closed="left")], + "eta0": [Interval(Real, 0, None, closed="left")], + } + def __init__( self, loss="squared_error", @@ -2108,6 +2164,17 @@ class SGDOneClassSVM(BaseSGD, OutlierMixin): loss_functions = {"hinge": (Hinge, 1.0)} + _parameter_constraints = { + **BaseSGD._parameter_constraints, + "nu": [Interval(Real, 0.0, 1.0, closed="right")], + "learning_rate": [ + StrOptions({"constant", "optimal", "invscaling", "adaptive"}), + Hidden(StrOptions({"pa1", "pa2"})), + ], + "eta0": [Interval(Real, 0, None, closed="left")], + "power_t": [Interval(Real, None, None, closed="neither")], + } + def __init__( self, nu=0.5, @@ -2149,13 +2216,6 @@ def __init__( average=average, ) - def _validate_params(self, for_partial_fit=False): - """Validate input params.""" - if not (0 < self.nu <= 1): - raise ValueError("nu must be in (0, 1], got nu=%f" % self.nu) - - super(SGDOneClassSVM, self)._validate_params(for_partial_fit=for_partial_fit) - def _fit_one_class(self, X, alpha, C, sample_weight, learning_rate, max_iter): """Uses SGD implementation with X and y=np.ones(n_samples).""" @@ -2330,10 +2390,11 @@ def partial_fit(self, X, y=None, sample_weight=None): self : object Returns a fitted instance of self. """ + if not hasattr(self, "coef_"): + self._validate_params() + self._more_validate_params(for_partial_fit=True) alpha = self.nu / 2 - self._validate_params(for_partial_fit=True) - return self._partial_fit( X, alpha, @@ -2357,8 +2418,6 @@ def _fit( offset_init=None, sample_weight=None, ): - self._validate_params() - if self.warm_start and hasattr(self, "coef_"): if coef_init is None: coef_init = self.coef_ @@ -2429,6 +2488,8 @@ def fit(self, X, y=None, coef_init=None, offset_init=None, sample_weight=None): self : object Returns a fitted instance of self. """ + self._validate_params() + self._more_validate_params() alpha = self.nu / 2 self._fit( diff --git a/sklearn/linear_model/tests/test_logistic.py b/sklearn/linear_model/tests/test_logistic.py index 4c8c9daf78731..a4598e709a54f 100644 --- a/sklearn/linear_model/tests/test_logistic.py +++ b/sklearn/linear_model/tests/test_logistic.py @@ -1771,7 +1771,7 @@ def test_elastic_net_versus_sgd(C, l1_ratio): penalty="elasticnet", random_state=1, fit_intercept=False, - tol=-np.inf, + tol=None, max_iter=2000, l1_ratio=l1_ratio, alpha=1.0 / C / n_samples, diff --git a/sklearn/linear_model/tests/test_passive_aggressive.py b/sklearn/linear_model/tests/test_passive_aggressive.py index 3ff92bd69a43b..06b6bd5b84cb1 100644 --- a/sklearn/linear_model/tests/test_passive_aggressive.py +++ b/sklearn/linear_model/tests/test_passive_aggressive.py @@ -3,7 +3,6 @@ import pytest -from sklearn.base import is_classifier from sklearn.utils._testing import assert_array_almost_equal from sklearn.utils._testing import assert_array_equal from sklearn.utils._testing import assert_almost_equal @@ -205,20 +204,6 @@ def test_wrong_class_weight_label(): clf.fit(X2, y2) -def test_wrong_class_weight_format(): - # ValueError due to wrong class_weight argument type. - X2 = np.array([[-1.0, -1.0], [-1.0, 0], [-0.8, -1.0], [1.0, 1.0], [1.0, 0.0]]) - y2 = [1, 1, 1, -1, -1] - - clf = PassiveAggressiveClassifier(class_weight=[0.5], max_iter=100) - with pytest.raises(ValueError): - clf.fit(X2, y2) - - clf = PassiveAggressiveClassifier(class_weight="the larch", max_iter=100) - with pytest.raises(ValueError): - clf.fit(X2, y2) - - def test_regressor_mse(): y_bin = y.copy() y_bin[y != 1] = -1 @@ -284,35 +269,3 @@ def test_regressor_undefined_methods(): reg = PassiveAggressiveRegressor(max_iter=100) with pytest.raises(AttributeError): reg.transform(X) - - -@pytest.mark.parametrize( - "klass", [PassiveAggressiveClassifier, PassiveAggressiveRegressor] -) -@pytest.mark.parametrize("fit_method", ["fit", "partial_fit"]) -@pytest.mark.parametrize( - "params, err_msg", - [ - ({"loss": "foobar"}, "The loss foobar is not supported"), - ({"max_iter": -1}, "max_iter must be > zero"), - ({"shuffle": "false"}, "shuffle must be either True or False"), - ({"early_stopping": "false"}, "early_stopping must be either True or False"), - ( - {"validation_fraction": -0.1}, - r"validation_fraction must be in range \(0, 1\)", - ), - ({"n_iter_no_change": 0}, "n_iter_no_change must be >= 1"), - ], -) -def test_passive_aggressive_estimator_params_validation( - klass, fit_method, params, err_msg -): - """Validate parameters in the different PassiveAggressive estimators.""" - sgd_estimator = klass(**params) - - with pytest.raises(ValueError, match=err_msg): - if is_classifier(sgd_estimator) and fit_method == "partial_fit": - fit_params = {"classes": np.unique(y)} - else: - fit_params = {} - getattr(sgd_estimator, fit_method)(X, y, **fit_params) diff --git a/sklearn/linear_model/tests/test_sgd.py b/sklearn/linear_model/tests/test_sgd.py index 1a48afeeb48db..b665abf6c31bd 100644 --- a/sklearn/linear_model/tests/test_sgd.py +++ b/sklearn/linear_model/tests/test_sgd.py @@ -216,61 +216,6 @@ def asgd(klass, X, y, eta, alpha, weight_init=None, intercept_init=0.0): return average_weights, average_intercept -@pytest.mark.parametrize( - "klass", - [ - SGDClassifier, - SparseSGDClassifier, - SGDRegressor, - SparseSGDRegressor, - SGDOneClassSVM, - SparseSGDOneClassSVM, - ], -) -@pytest.mark.parametrize("fit_method", ["fit", "partial_fit"]) -@pytest.mark.parametrize( - "params, err_msg", - [ - ({"alpha": -0.1}, "alpha must be >= 0"), - ({"penalty": "foobar", "l1_ratio": 0.85}, "Penalty foobar is not supported"), - ({"loss": "foobar"}, "The loss foobar is not supported"), - ({"l1_ratio": 1.1}, r"l1_ratio must be in \[0, 1\]"), - ({"learning_rate": ""}, "learning rate is not supported"), - ({"nu": -0.5}, r"nu must be in \(0, 1]"), - ({"nu": 2}, r"nu must be in \(0, 1]"), - ({"alpha": 0, "learning_rate": "optimal"}, "alpha must be > 0"), - ({"eta0": 0, "learning_rate": "constant"}, "eta0 must be > 0"), - ({"max_iter": -1}, "max_iter must be > zero"), - ({"shuffle": "false"}, "shuffle must be either True or False"), - ({"early_stopping": "false"}, "early_stopping must be either True or False"), - ( - {"validation_fraction": -0.1}, - r"validation_fraction must be in range \(0, 1\)", - ), - ({"n_iter_no_change": 0}, "n_iter_no_change must be >= 1"), - ], - # Avoid long error messages in test names: - # https://github.com/scikit-learn/scikit-learn/issues/21362 - ids=lambda x: x[:10].replace("]", "") if isinstance(x, str) else x, -) -def test_sgd_estimator_params_validation(klass, fit_method, params, err_msg): - """Validate parameters in the different SGD estimators.""" - try: - sgd_estimator = klass(**params) - except TypeError as err: - if "unexpected keyword argument" in str(err): - # skip test if the parameter is not supported by the estimator - return - raise err - - with pytest.raises(ValueError, match=err_msg): - if is_classifier(sgd_estimator) and fit_method == "partial_fit": - fit_params = {"classes": np.unique(Y)} - else: - fit_params = {} - getattr(sgd_estimator, fit_method)(X, Y, **fit_params) - - def _test_warm_start(klass, X, Y, lr): # Test that explicit warm restart... clf = klass(alpha=0.01, eta0=0.01, shuffle=False, learning_rate=lr) @@ -670,7 +615,7 @@ def test_partial_fit_weight_class_balanced(klass): r"class_weight 'balanced' is not supported for " r"partial_fit\. In order to use 'balanced' weights, " r"use compute_class_weight\('balanced', classes=classes, y=y\). " - r"In place of y you can us a large enough sample " + r"In place of y you can use a large enough sample " r"of the full training set target to properly " r"estimate the class frequency distributions\. " r"Pass the resulting weights as the class_weight " @@ -1741,7 +1686,7 @@ def test_ocsvm_vs_sgdocsvm(): fit_intercept=True, max_iter=max_iter, random_state=random_state, - tol=-np.inf, + tol=None, ) pipe_sgd = make_pipeline(transform, clf_sgd) pipe_sgd.fit(X_train) diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index 0577a8c73a8bd..a6750ff9078a1 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -572,10 +572,7 @@ def test_estimators_do_not_raise_errors_in_init_or_set_params(Estimator): "PLSCanonical", "PLSRegression", "PLSSVD", - "PassiveAggressiveClassifier", - "PassiveAggressiveRegressor", "PatchExtractor", - "Perceptron", "PoissonRegressor", "PolynomialCountSketch", "PolynomialFeatures", @@ -599,9 +596,6 @@ def test_estimators_do_not_raise_errors_in_init_or_set_params(Estimator): "RidgeClassifier", "RidgeClassifierCV", "RobustScaler", - "SGDClassifier", - "SGDOneClassSVM", - "SGDRegressor", "SVC", "SVR", "SelectFdr",