-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
[python-package] Early stopping callback added when early_stopping_round = 0 #6401
Comments
Thanks for using LightGBM and for the report @ddelzell ! I agree, this is a bug. This condition is only checking for the presence of LightGBM/python-package/lightgbm/engine.py Line 239 in 28536a0
... because it's assuming that if LightGBM/python-package/lightgbm/engine.py Lines 185 to 192 in 28536a0
But doing that LightGBM/python-package/lightgbm/engine.py Line 239 in 28536a0
LightGBM/python-package/lightgbm/engine.py Line 763 in 28536a0
To check the value. Are you interested in contributing that fix? It'd mean doing the following:
There are some tips on how to contribute in #6350, and you could open a draft pull request and |
It'd also be worthwhile, before we consider this issue fixed by any PRs, to check whether the R package is also not treating |
Thanks for the quick reply, James. Yes, I would be happy to submit a PR for this. It saves us a PR on our end to not have to code a workaround! I'll give it a whirl and you can see what you think! |
@jameslamb FYI I have my dev environment set up and all the unit tests pass except for those in ALSO, just to confirm we are on the same page. We want to remove the pop so that we always keep the parameter's user-set value and just check if it's >0 before adding the callback. |
That's totally fine... this shouldn't affect Arrow functionality and we can rely on Continuous Integration to confirm that. But if you want to see the entire test suite pass, follow the advice a few more comments down (#6350 (comment)) and install
No, that I was thinking we'd replace this if "early_stopping_round" in params:
callbacks_set.add(
callback.early_stopping( With this: if params.get("early_stopping_round", 0) > 0
callbacks_set.add(
callback.early_stopping( But actually, let's slow down for a second... could you share a minimal example showing a way that you used I'm wondering how this error wasn't raised: LightGBM/python-package/lightgbm/callback.py Lines 283 to 284 in 28536a0
Sorry, forgot about that check until today. |
I think that's precisely the problem, that the documentation says |
OK, so to your first point. Is this Your 2 code chunks are the same. typo? I was going to replace
with
That would work if a |
Ah, just saw new comment. So I think we agree! |
@jameslamb to your last question, that's exactly the exception that was raised for me when |
Sorry, trying to do too many things at once 😅 . I was thinking about this first sentence in your description: "when early_stopping_round=0, the callback that initiates early stopping is added". That couldn't be true, because that error should be raised when calling
Yes sorry, hit ENTER too fast 😅 . I just edited it and I agree with you and @jmoralez , it would be But now that I'm looking closely at it ... I think we also should change the constructor of the early stopping callback like this. Before: if not isinstance(stopping_rounds, int) or stopping_rounds <= 0:
raise ValueError(f"stopping_rounds should be an integer and greater than 0. got: {stopping_rounds}")
self.stopping_rounds = stopping_rounds
self.enabled = True After: if not isinstance(stopping_rounds, int):
raise ValueError(f"stopping_rounds should be an integer. Got {type(stopping_rounds)}")
self.stopping_rounds = stopping_rounds
if stopping_rounds > 0:
self.enabled = True
else:
self.enabled = False It's possible (and actually encouraged) to import Like this
It helps to handle the case where Most
In addition to that, wrappers like the Python and R packages also have keyword arguments in some of their APIs which conflict with the other LightGBM parameters. For example, in the lgb.train(..., num_boost_round=10)
lgb.train(..., params={"num_iterations": 10}) So code in those interfaces often has to resolve multiple competing sources of configuration for the same value. In roughly this order of precedence (latest item in the list, if present, wins):
Since that's done so frequently, we centralized that logic in an internal function LightGBM/python-package/lightgbm/basic.py Line 626 in 28536a0
It takes in a So this call right before the LightGBM/python-package/lightgbm/engine.py Lines 186 to 190 in 28536a0
... will add The |
Hope that helps. Sorry this is so complicated. Parameter resolution is a particularly complex part of using LightGBM, and especially its Python and R packages. There's a higher-than-is-probably-necessary level of flexibility in these interfaces that have been in the libraries for a while, which we've chosen to preserve to avoid breaking existing code relying on them. If you see any opportunities to make non-breaking simplifications while you're looking through the code, we'd welcome that 😊 |
No problem, happy to help! We have an R package here too and lots to do on it if you'd ever like to help out with that 😊 We already have a test checking that error message, so you'll have to just modify that. LightGBM/tests/python_package_test/test_callback.py Lines 24 to 32 in 28536a0
For testing the rest of this change (that passing
Add a new test under that one, but passing Avoid duplicating all the test code for different values (e.g.
|
Alright, draft PR submitted on a forked copy of the repo. @jameslamb it looks like your review was already requested, but just in case I'm leaving a comment so you see it. Thanks! |
🙌🏻 thanks so much! We can move the discussion over there. |
It seems that when
early_stopping_round=0
, the callback that initiates early stopping is added. But setting early_stopping_round to 0 should disable early stopping, according to the docs.This causes an error when trying to fit a model with
early_stopping_round=0
: "ValueError: stopping_rounds should be an integer and greater than 0. got: 0".This seems to be because the
early_stopping_round
is only removed if its value isNone
, when it should also be removed if its value is <=0.The text was updated successfully, but these errors were encountered: