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

[python-package] Model parameters are lost when loading Booster from model serialized string #6851

Open
Samsagax opened this issue Mar 2, 2025 · 4 comments · May be fixed by #6852
Open

Comments

@Samsagax
Copy link

Samsagax commented Mar 2, 2025

Description

After training, saving the model on a string, then retrieving it, the params dictionary are lost

Reproducible example

import lightgbm as lgb
import numpy as np

# Train data
train_x = np.random.rand(1000, 20)
train_y = np.random.randint(0, 1, 1000)
train_data = lgb.Dataset(train_x, train_y)

# Parameters
params = {
    "boosting_type": "gbdt",
    "objective": "binary",
    "metric": "auc",
    "num_leaves": 31,
    "learning_rate": 0.05,
    "feature_fraction": 0.9,
    "bagging_fraction": 0.8,
    "bagging_freq": 5,
    "verbose": -100,
}

# train the model
model = lgb.train(params, train_data)

# print the params, should be the same as above
print(model.params)

# dump the model into a string
model_serialized = model.model_to_string()

# load a new model with the string
new_model = lgb.Booster(model_str=model_serialized)

# print the params, it returns an empty dict
print(new_model.params)

Environment info

LightGBM version or commit hash: 4.6.0

Command(s) you used to install LightGBM

$ pip install lightgbm

ArchLinux system

Additional Comments

I think the fix is pretty simple and stems from the __init__() function last line where the params member is overwritten with whatever is passed (empty, when loading from a serialized string)

I guess the fix is easy, will PR it in a moment.

Samsagax added a commit to Samsagax/LightGBM that referenced this issue Mar 2, 2025
This fixes microsoft#6851 by using the same workaround as when loading the model
from a file.
@Samsagax Samsagax linked a pull request Mar 2, 2025 that will close this issue
@jameslamb
Copy link
Collaborator

Thanks for using LightGBM.

I believe this issue is a duplicate of #5539 and #6821, both of which will be fixed by #6101. There the issue was about "copying", but it is the same root cause because copying a Booster in the Python package involves serializing a model to string and the deserializing from that string.

@Samsagax
Copy link
Author

Samsagax commented Mar 3, 2025

Hello, @jameslamb . Thank you for your feedback. I encountered this issue when saving the string to a database as a CLOB for future use. When I wanted to use the shap package, everything crumbled because it could not retrieve params. The fix in my PR worked it around and I can do a workaround outside to retrieve the params and set them to the python object from outside before calling shap functions.
How can I help to make #6101 ready?

Samsagax added a commit to Samsagax/LightGBM that referenced this issue Mar 3, 2025
Samsagax added a commit to Samsagax/LightGBM that referenced this issue Mar 3, 2025
This fixes microsoft#6851 by using the same workaround as when loading the model
from a file.
@jameslamb
Copy link
Collaborator

jameslamb commented Mar 6, 2025

I encountered this issue when saving the string to a database as a CLOB for future use.

Even after #6852 and #6101 are merged, it will still be a while (on the order of maybe 1-4 months) until there's another release of lightgbm containing those changes.

If you don't want to wait that long or have to build lightgbm from source, the easiest way to get this working in your application would be to store the params alongside the model file in the database. Maybe in a JSONB column (Postgres docs). (suspect you meant something like this by "a workaround outside to retrieve the params")

Then you could retrieve both at the same time and pass the params to Booster.reset_parameter() (as mentioned in #6821 (comment)).

How can I help to make #6101 ready?

#6852 helps! I've already prioritized #6101 and assigned it to myself, will get it into the next release. Sorry it's taken so long... a lot of things in lightgbm are configurable, which means there are some complex interactions with when and how parameters are modified.

@Samsagax
Copy link
Author

Samsagax commented Mar 6, 2025

Even after #6852 and #6101 are merged, it will still be a while (on the order of maybe 1-4 months) until there's another release of lightgbm containing those changes.

If you don't want to wait that long or have to build lightgbm from source, the easiest way to get this working in your application would be to store the params alongside the model file in the database. Maybe in a JSONB column (Postgres docs). (suspect you meant something like this by "a workaround outside to retrieve the params")

Then you could retrieve both at the same time and pass the params to Booster.reset_parameter() (as mentioned in #6821 (comment)).

That is ok. I devised a workaround in my client code around the lines of:

# Workaround for LightGBM matched by version
from packaging.version import Version
from lightgbm import __version__ as lgb_version

...

            # Workaround a bug in LightGBM python package when loading from string
            if Version(lgb_version) <= Version("5.0.0"):
                self.model.params = self.model._get_loaded_param()

And it works! I can wait until next release. But want to make sure it will be there :)
Using another column type is not really an option in my uisecase since I'm working on (rather old) OracleDB (yes it has JSON but our DBAs don't like them) and is a restriction for me.

How can I help to make #6101 ready?

#6852 helps! I've already prioritized #6101 and assigned it to myself, will get it into the next release. Sorry it's taken so long... a lot of things in lightgbm are configurable, which means there are some complex interactions with when and how parameters are modified.

As all good software does :) Feel free to undo some of my workaround code when it is fixed for good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants