Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEP deprecate multi_class in LogisticRegression #28703

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

lorentzenchr
Copy link
Member

@lorentzenchr lorentzenchr commented Mar 26, 2024

Reference Issues/PRs

Towards #11865.

What does this implement/fix? Explain your changes.

This PR deprecates the multi_class parameter in LogisticRegression. Using that option is equivalent to OneVsRestClassifier(LogisticRegression()), so no functionality is lost and, once gone, it would simplify the code of logreg quite a bit and make in more maintainable.

Any other comments?

This PR starts very simple with only LogisticRegression. In case of positive feedback, I'll extend it to LogisticRegressionCV and adapt all the docstrings and so on.

@lorentzenchr lorentzenchr added this to the 1.5 milestone Mar 26, 2024
Copy link

github-actions bot commented Mar 26, 2024

✔️ Linting Passed

All linting checks passed. Your pull request is in excellent shape! ☀️

Generated for commit: 63a9399. Link to the linter CI: here

@lorentzenchr lorentzenchr added the Needs Decision Requires decision label Mar 26, 2024
@lorentzenchr
Copy link
Member Author

@scikit-learn/core-devs friendly ping for visibility.

@jjerphan
Copy link
Member

I have not had time to look at this PR at all, but before deprecating, can we have tests to be sure they are equivalent in results and potentially in UX (e.g. would accessing fitted attributes still be possible)?

@agramfort
Copy link
Member

would be ok for me. I think it's ok to live with

OneVsRestClassifier(LogisticRegression(..))

when using liblinear.

@GaelVaroquaux
Copy link
Member

Just to make sure that I understand things correctly: the plan is that by default multi-class is supported via multinomial loss but if the solver is liblinear multi-class raises an error, and the user must use a OvR?

If so, that strategy is fine by me, but I think that the deprecation message should first suggest to use solvers that support multinomial or user OvR if liblinear is desired.

@lorentzenchr
Copy link
Member Author

Just to make sure that I understand things correctly: the plan is that by default multi-class is supported via multinomial loss but if the solver is liblinear multi-class raises an error, and the user must use a OvR?

Yes, exactly. And yes, with an informative deprecation warning, later an error advertising solvers that support the multinomial loss.

Copy link
Member

@thomasjpfan thomasjpfan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also looks like solver="newton-cholesky" does not support ovr .

From a high level API point of view, LinearSVC only does OVR with liblinear and LogisticRegression will no longer support it. In the future, should LinearSVC also force users to use OneVsRestClassifier for multi-class problems?

Over the last few years, we are slowly making meta-estimators more necessary for certain task. (i.e., the removal of normalize or this PR). It kind of goes against the history of "lets make estimators easy to use". For example, the classifiers encodes string labels to "make these easy". This is my observation and I am undecided on the current path.

Comment on lines +1221 to +1222
"'multi_class' was deprecated in version 1.5 and will be removed in"
" 1.7. Use OneVsRestClassifier(LogisticRegression(..)) instead."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If one specifically sets multi_class="multinomial", then this warning seems out of place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, the problem is now that this warning message would be off when calling LogisticRegression(multi_class="auto").fit(X, y) on multiclass data.

Maybe we can just use a generic all ecompassing message such as:

        if self.multi_class != "deprecated":
            warnings.warn(
                (
                    "'multi_class' was deprecated in version 1.5 and will be removed in"
                    " 1.7. For solvers and penalties that support it, the multinomial"
                    " scheme is used automatically when the data has more than two"
                    " classes. The one-vs-rest scheme can be implemented with"
                    " OneVsRestClassifier(LogisticRegression(...)) instead."
                    " See the docstring for more details."
                ),
                FutureWarning,
             )
         else:
             # Set to old default value.
             multi_class = "auto"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it off? The parameter multi_class should best not be used anymore, also not with value "auto".

@GaelVaroquaux
Copy link
Member

GaelVaroquaux commented Mar 28, 2024 via email

@lorentzenchr
Copy link
Member Author

Over the last few years, we are slowly making meta-estimators more necessary for certain task.

You raise good points. For this particular case, I have 3 answers:

  • Out of the box, logreg will continue to just work. Only if a user wants to use a specific solver, he/she will be directed to the meta-estimator. (We can even extent the newton-cholesky one to support multinomial, no big deal. Then, only liblinear would be left.)
  • From a statistical point of view, multinomial logreg should clearly be favored over OvR logreg. (In fact, I find it even hard to provide good statistical reference for OvR logreg.)
  • The logic of LogisticRegression will become significantly less „opaque“ and the code more maintainable. (And yes, this is a concern as the code complexity has been a problem in several PRs, making contributing hard.)

@lorentzenchr
Copy link
Member Author

I interpret the conversation so far as decision to deprecate multi_class in LogisticRegression.

I have an open questions: Shall we raise an error for multiclass liblinear or internally switch to OvR? (And state this in the docs)?

@ogrisel
Copy link
Member

ogrisel commented Apr 10, 2024

I am fine for deprecating the multi_class thingy in LogisticRegression but not in LinearSVC because there is no good default alternative to handle the multiclass case for the latter so that would be a regression from a UX point of view.

@lorentzenchr lorentzenchr added API and removed Needs Decision Requires decision labels Apr 11, 2024
@lorentzenchr
Copy link
Member Author

Ready for review.

Copy link
Member

@ogrisel ogrisel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback. The most important comments are related to the contents of the docstrings and the warning message to address Thomas' remark.

The other things are more minor and overall LGTM.

Thanks for the PR.

sklearn/linear_model/_logistic.py Outdated Show resolved Hide resolved
sklearn/linear_model/_logistic.py Outdated Show resolved Hide resolved
sklearn/linear_model/_logistic.py Outdated Show resolved Hide resolved
),
FutureWarning,
)
elif self.multi_class != "deprecated":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elif self.multi_class != "deprecated":
elif self.multi_class != "ovr":

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean == "ovr, right?

Comment on lines +1221 to +1222
"'multi_class' was deprecated in version 1.5 and will be removed in"
" 1.7. Use OneVsRestClassifier(LogisticRegression(..)) instead."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically, the problem is now that this warning message would be off when calling LogisticRegression(multi_class="auto").fit(X, y) on multiclass data.

Maybe we can just use a generic all ecompassing message such as:

        if self.multi_class != "deprecated":
            warnings.warn(
                (
                    "'multi_class' was deprecated in version 1.5 and will be removed in"
                    " 1.7. For solvers and penalties that support it, the multinomial"
                    " scheme is used automatically when the data has more than two"
                    " classes. The one-vs-rest scheme can be implemented with"
                    " OneVsRestClassifier(LogisticRegression(...)) instead."
                    " See the docstring for more details."
                ),
                FutureWarning,
             )
         else:
             # Set to old default value.
             multi_class = "auto"

sklearn/linear_model/_logistic.py Show resolved Hide resolved
)
else:
# Set to old default value.
multi_class = "auto"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment about the warning messages, especially when multi_class="auto" is passed explicitly.

@@ -1598,6 +1564,9 @@ def test_LogisticRegressionCV_GridSearchCV_elastic_net(multi_class):
assert gs.best_params_["C"] == lrcv.C_[0]


# TODO(1.7): remove filterwarnings after the deprecation of multi_class
# Maybe remove whole test after removal of the deprecated multi_class.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, I am +1 for this suggestion.

tol = 0.00001
max_iter = 40
tol = 1e-5
max_iter = 70
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this was needed because the previous max_iter value was too RNG sensitive?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I checked the new (and old) value with all global random seeds. SAG and SAGA work better for larger datasets,

sklearn/linear_model/tests/test_sag.py Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants