Skip to content

fix: LightGBM 4.0+ compatibility for early_stopping_rounds=None#2227

Closed
Olcmyk wants to merge 1 commit into
microsoft:mainfrom
Olcmyk:fix/lightgbm-4-early-stopping-none
Closed

fix: LightGBM 4.0+ compatibility for early_stopping_rounds=None#2227
Olcmyk wants to merge 1 commit into
microsoft:mainfrom
Olcmyk:fix/lightgbm-4-early-stopping-none

Conversation

@Olcmyk
Copy link
Copy Markdown

@Olcmyk Olcmyk commented May 23, 2026

Description

This PR fixes a compatibility issue with LightGBM 4.0+ where passing early_stopping_rounds=None causes a TypeError.

Changes:

  • Only create early_stopping callback when rounds is not None
  • LightGBM 4.0+ requires stopping_rounds to be an integer, not None
  • Apply fix to gbdt.py, ddgda.py, and highfreq_gdbt_model.py
  • Follow the pattern already used in double_ensemble.py

Affected files:

  • qlib/contrib/model/gbdt.py: Core LGBModel fix
  • qlib/contrib/rolling/ddgda.py: Remove explicit None assignment
  • qlib/contrib/model/highfreq_gdbt_model.py: Add robustness check

Motivation and Context

Fixes #2226

Problem: Starting from LightGBM 4.0, the lgb.early_stopping() function requires stopping_rounds to be an integer and no longer accepts None. This breaks qlib's code that attempts to disable early stopping by passing None.

Error:

TypeError: early_stopping_round should be an integer. Got 'NoneType'

Root cause:

  • qlib/contrib/model/gbdt.py:71-73 directly passes early_stopping_rounds to lgb.early_stopping() without checking for None
  • qlib/contrib/rolling/ddgda.py:244 explicitly sets early_stopping_rounds: None

Solution: Only create the early_stopping callback when the value is not None, following the pattern already correctly implemented in double_ensemble.py.

How Has This Been Tested?

  • If you are adding a new feature, test on your own test scripts.

Testing environment:

  • LightGBM: 4.6.0
  • Python: 3.8
  • OS: Linux (Ubuntu)

Test method:

  1. Applied fix from PR fix(unpickler): allow Alpha158/Alpha360 handlers and the standard dataset chain #2213 (required to reach this code path)
  2. Ran DDG-DA example:
    cd examples/benchmarks_dynamic/DDG-DA
    rm -rf mlruns
    python workflow.py run
  3. Verified that the TypeError: early_stopping_round should be an integer no longer occurs
  4. Confirmed training completes successfully (154/154 tasks)

Note: The standard pytest qlib/tests/test_all_pipeline.py does not cover this specific scenario as it requires:

  • LightGBM 4.0+
  • Explicit early_stopping_rounds=None usage
  • The DDG-DA workflow

Screenshots of Test Results (if appropriate):

Before fix:

TypeError: early_stopping_round should be an integer. Got 'NoneType'
  File "qlib/contrib/model/gbdt.py", line 71, in fit
    early_stopping_callback = lgb.early_stopping(...)

After fix:

train tasks: 100%|████████████████████████████| 154/154 [05:23<00:00,  2.10s/it]
calc: 100%|█████████████████████████████████████| 154/154 [00:01<00:00, 101.63it/s]

Training completes successfully without TypeError.

Types of changes

  • Fix bugs
  • Add new feature
  • Update documentation

Additional Notes

Known issue discovered during testing:

After fixing this LightGBM bug, the DDG-DA workflow encounters another bug:

TypeError: unhashable type: 'slice'
  File "qlib/contrib/meta/data_selection/dataset.py", line 101, in setup

This is a separate issue in the DDG-DA code itself (not related to LightGBM) where a slice object is being used as a dictionary key. This should be tracked and fixed in a separate issue/PR.

References

- Only create early_stopping callback when rounds is not None
- LightGBM 4.0+ requires stopping_rounds to be an integer, not None
- Apply fix to gbdt.py, ddgda.py, and highfreq_gdbt_model.py
- Follow the pattern already used in double_ensemble.py

This fixes the TypeError that occurs when early_stopping_rounds=None
is passed to lgb.early_stopping() in LightGBM 4.0+.

Affected files:
- qlib/contrib/model/gbdt.py: Core LGBModel fix
- qlib/contrib/rolling/ddgda.py: Remove explicit None assignment
- qlib/contrib/model/highfreq_gdbt_model.py: Add robustness check
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LightGBM 4.0+ Compatibility: TypeError when early_stopping_rounds=None

1 participant