Skip to content

LightGBM 4.0+ Compatibility: TypeError when early_stopping_rounds=None #2226

@Olcmyk

Description

@Olcmyk

🐛 Bug Description

qlib fails with TypeError when using LightGBM 4.0+ because the code passes None to lgb.early_stopping(), which no longer accepts None starting from LightGBM 4.0.

Error message:

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

To Reproduce

Prerequisites: This bug is currently masked by issue #2130. You need to apply the fix from PR #2213 first.

Steps to reproduce the behavior:

  1. Apply the fix from PR fix(unpickler): allow Alpha158/Alpha360 handlers and the standard dataset chain #2213 (or wait for it to be merged)
  2. Navigate to the DDG-DA example directory:
    cd examples/benchmarks_dynamic/DDG-DA
  3. Clean previous runs:
    rm -rf mlruns
  4. Run the workflow:
    python workflow.py run

Full stack trace:

File "qlib/contrib/rolling/ddgda.py", line 249, in _dump_meta_ipt
    internal_data.setup(trainer=TrainerR)
File "qlib/contrib/meta/data_selection/dataset.py", line 85, in setup
    trainer.train(gen_task)
File "qlib/model/trainer.py", line 271, in train
    rec = train_func(task, experiment_name, recorder_name=self.default_rec_name, **kwargs)
File "qlib/model/trainer.py", line 127, in task_train
    _exe_task(task_config)
File "qlib/model/trainer.py", line 49, in _exe_task
    auto_filter_kwargs(model.fit)(dataset, reweighter=reweighter)
File "qlib/contrib/model/gbdt.py", line 71, in fit
    early_stopping_callback = lgb.early_stopping(
        self.early_stopping_rounds if early_stopping_rounds is None else early_stopping_rounds
    )
TypeError: early_stopping_round should be an integer. Got 'NoneType'

Expected Behavior

The DDG-DA workflow should run successfully without raising a TypeError. When early_stopping_rounds=None, the model should train without early stopping (for the full num_boost_round iterations).

Screenshot

N/A (Error is in terminal output)

Environment

  • Qlib version: main branch (commit: a55d499)
  • Python version: 3.8
  • OS: Linux (Ubuntu, kernel 6.2.0-26-generic)
  • LightGBM version: 4.6.0
  • Hardware: NVIDIA GeForce RTX 4080 SUPER (cloud instance)
  • Commit number: a55d499 (fix/2130-pickle-safelist-alpha-handlers branch)
System Information (collected via scripts/collect_info.py)
Platform: Linux-6.2.0-26-generic-x86_64-with-glibc2.31
Python: 3.8
LightGBM: 4.6.0
NumPy: (version from environment)
Pandas: (version from environment)

Additional Notes

Root Cause

File: qlib/contrib/model/gbdt.py (lines 71-73)

The code directly passes early_stopping_rounds to lgb.early_stopping() without checking if it's None:

early_stopping_callback = lgb.early_stopping(
    self.early_stopping_rounds if early_stopping_rounds is None else early_stopping_rounds
)

File: qlib/contrib/rolling/ddgda.py (line 244)

The DDG-DA workflow explicitly sets early_stopping_rounds: None to disable early stopping:

sim_task["model"]["kwargs"].update({"early_stopping_rounds": None, "num_boost_round": 150})

LightGBM Breaking Change

Starting from LightGBM 4.0, the lgb.early_stopping() function enforces type checking and no longer accepts None:

  • LightGBM < 4.0: Accepted None to disable early stopping
  • LightGBM >= 4.0: Requires an integer, raises TypeError on None

Reference: https://github.com/microsoft/LightGBM/releases/tag/v4.0.0

Affected Files

  1. qlib/contrib/model/gbdt.py (line 71-73) - must fix
  2. qlib/contrib/rolling/ddgda.py (line 244) - must fix
  3. qlib/contrib/model/highfreq_gdbt_model.py (line 127) - should fix (for robustness)

Note: qlib/contrib/model/double_ensemble.py (line 110-111) already handles this correctly by checking if early_stopping_rounds is truthy before creating the callback.

Relationship to Other Issues

These are two independent bugs that both affect the DDG-DA workflow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions