-
-
Notifications
You must be signed in to change notification settings - Fork 25.8k
[MRG] Adds FutureWarning changing default solver to 'lbfgs' and multi_class to 'multinomial' in LogisticRegression #10001
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
[MRG] Adds FutureWarning changing default solver to 'lbfgs' and multi_class to 'multinomial' in LogisticRegression #10001
Conversation
@jnothman Is this PR in the right direction? I am not sure if this is what you meant by using 'auto'. |
sklearn/linear_model/logistic.py
Outdated
@@ -1198,6 +1198,14 @@ def fit(self, X, y, sample_weight=None): | |||
self : object | |||
Returns self. | |||
""" | |||
if self.solver == 'auto': | |||
self.solver = 'liblinear' | |||
warnings.warn("Default solver will be changed to 'lbfgs' in 0.22", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only for L2 penalty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead of default say 'auto'
But you also now give the user no easy way to silence the warning. We will need a temporary setting like 'default' to manage the warning.
sklearn/linear_model/logistic.py
Outdated
@@ -1198,6 +1198,14 @@ def fit(self, X, y, sample_weight=None): | |||
self : object | |||
Returns self. | |||
""" | |||
if self.solver == 'auto': | |||
self.solver = 'liblinear' | |||
warnings.warn("Default solver will be changed to 'lbfgs' in 0.22", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead of default say 'auto'
But you also now give the user no easy way to silence the warning. We will need a temporary setting like 'default' to manage the warning.
sklearn/linear_model/logistic.py
Outdated
FutureWarning) | ||
if self.multi_class == 'auto': | ||
self.multi_class = 'ovr' | ||
warnings.warn("Default multi_class will be changed to " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only when solver is not liblinear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But when solver is indeed liblinear, multi_class remains as auto and thus raises an error. Am I missing out something here?
…into logistic_solver
@jnothman Really Sorry, I forget that I had an open PR here, all because of my semester examinations. I somehow feel that the search option in github's open PR does not give full list :P |
https://github.com/scikit-learn/scikit-learn/pulls/thechargedneutron should help. You're getting an error in CI:
|
@jnothman Do you think we need to convert |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again if you wrote tests or at least documented correct behaviour first, with a premise that 'auto' must always work, you would have an easier time getting the logic right, and a green tick to say you had done so
sklearn/linear_model/logistic.py
Outdated
@@ -1198,6 +1202,14 @@ def fit(self, X, y, sample_weight=None): | |||
self : object | |||
Returns self. | |||
""" | |||
if self.solver == 'auto' and self.penalty == 'l2': | |||
self.solver = 'liblinear' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't allow fit to change constructor parameters. You can store the validated parameter as _solver
if it should be private, or solver_
if it should be public (assumed useful to users).
…into logistic_solver
@jnothman I guess this is a better implementation and indeed passes tests (except probably a doctest failure). |
Pulled the latest version to resolve conflicts. |
@pytest.mark.parametrize('model', [LogisticRegression, LogisticRegressionCV])
def test_logistic_regression_warnings(model):
... Same for |
@TomDLT Parametrize added.
That would require me to have a return parameter in |
Why? But if you prefer you can also merge them into a new function. I just find it redundant and prone to future mistakes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You really need to improve the docs here. I would consider documenting what 'auto' means to be something you'd do before labelling the PR as MRG.
@@ -500,6 +500,7 @@ def logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True, | |||
number for verbosity. | |||
|
|||
solver : {'lbfgs', 'newton-cg', 'liblinear', 'sag', 'saga'} | |||
default: 'default'. Will be changed to 'auto' solver in 0.22. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default 'default' doesn't mean anything. If you're going to say such a thing, you need to explain what it means. Better to say it is lbfgs by default, even if the string 'default' is used as a placeholder. 'auto' needs to be added to the list of options and described.
@@ -587,6 +589,19 @@ def logistic_regression_path(X, y, pos_class=None, Cs=10, fit_intercept=True, | |||
.. versionchanged:: 0.19 | |||
The "copy" parameter was removed. | |||
""" | |||
if solver == 'default': | |||
solver = 'lbfgs' | |||
warnings.warn("Default solver will be changed from 'lbfgs' " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably only warn if auto would choose a solver other than lbfgs.
@@ -1606,7 +1660,24 @@ def fit(self, X, y, sample_weight=None): | |||
------- | |||
self : object | |||
""" | |||
_check_solver_option(self.solver, self.multi_class, self.penalty, | |||
if self.solver == 'default': | |||
_solver = 'lbfgs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it strange to have these preceding undescores. This is just a plain old local variable.
what's the status here. I'd really like this to happen
|
I'm marking this as stalled / help wanted. But maybe we need a core dev to take it on, as it really is bad that we have ovr and intercept regularisation as default. |
Do we really want L-BFGS as default or SAG or depend on the data to decide? I think if we change the default we should change to a solver that's automatic, as we did for PCA, and that is picked according to the penalty. |
yes agreed. This needs a careful benchmark though.
|
So can someone write down the decision tree of settings we're working towards? SAG and L-BFGS should at least reach the same coefficients given reasonable amounts of data? I think this should be added as a priority for 0.20. |
Another question related to this: should |
@TomDLT, given your previous work in this space, would you mind working on defining appropriate defaults? If I also wonder if we can say that for something named 'auto' we give no strong assurances of API stability across versions: we make a weak assurance that with sufficient (i.e. infinite) |
I'll do it |
Hm SAG is pretty unstable if you don't scale the data. We could always pre-scale the data but that seems a bit un-scikit-learn. |
@TomDLT are you still on this? Otherwise @NicolasHug will take it up. Also: are you at scipy? |
So do we just want to change the default of solver as a first step or do we want to do an automagic "auto"? It's really not clear to me how auto would really work, since good values for tol and max_iter and dual depend on the solver, right? If someone sets "dual=True" does that mean they'll always get liblinear? Or do we ignore that unless they set the solver to liblinear? On the one hand it would be annoying to make a backward-incompatible change twice, on the other hand, getting "auto" right seems very hairy and probably not doable for 0.20, even without benchmarks. If we want to run benchmarks, we would need to benchmark at least these cases, right? That would be 18 possible regimes already. Well maybe the decision tree for the optimum solver doesn't involve all combinations.
If the data is scaled badly that's probably not gonna happen, I think? |
We can advertise that 'auto' behaviour is still being finalised, or will
have no long-term stability assured.
The important thing here is to advertise that untegularised intercept and
ovr are defaults that users should not be accepting where there is a choice.
|
Reference Issues/PRs
Fixes #9997
What does this implement/fix? Explain your changes.
Adds FutureWarning that default of solver and multi_class will be changed to 'lbfgs' and 'multinomial' respectively.
Any other comments?