Convert pipelined XGBoost 0.90 model to XGBoost 1.0+

Viewed 892

Suppose I have an XGBoost 0.90 model set up like so, using scikit-learn pipelines to do some preprocessing. I want to upgrade to XGBoost 1.0+ and still be able to use this model, without having to refit it.

import pandas as pd
import numpy as np
from sklearn_pandas import DataFrameMapper
from xgboost import XGBRegressor
from sklearn.preprocessing import FunctionTransformer, MinMaxScaler
from sklearn.pipeline import Pipeline


df = pd.DataFrame({
    'a': [0.0, 45.0, 90.0, 135.0, 180.0, np.nan],
    'b': [180.0, 135.0, 90.0, 45.0, 0.0, np.nan],
    'y': [1.0, 3.0, 4.0 ,5.0 ,6.0 ,0.0]
})

pipeline = Pipeline([
    ('mapper', DataFrameMapper([
        (['a', 'b'], [MinMaxScaler(feature_range=(0, 2*np.pi)), FunctionTransformer(np.cos)], ),
        (['a', 'b'], [MinMaxScaler(feature_range=(0, 2*np.pi)), FunctionTransformer(np.sin)], )        
    ])),
    ('regressor', XGBRegressor())
])

pipeline.fit(df[['a', 'b']], df['y'])

If I pickle the pipeline when I have XGBoost 0.90 installed, I can't load it when I subsequently have XGBoost 1.0+ installed. The XGBoost docs suggest that I should convert the 0.90 pickle using a script that they've provided:

https://xgboost.readthedocs.io/en/latest/tutorials/saving_model.html#loading-pickled-file-from-different-version-of-xgboost

The script is available here:

https://github.com/dmlc/xgboost/blob/master/doc/python/convert_090to100.py

However, this script only works for XGBoost Booster objects. So I've tried the following:

  • Extract the model from my pipeline via pipeline.named_steps.regressor (in an env with XGBoost 0.90 installed)
  • Pickle the model (still 0.90)
  • Convert the pickle using the script (still 0.90)
  • Load the model from the file produced by the script (in the new env with XGBoost 1.0+)

Now I'm a bit stuck though. How I can reassemble my pipeline using the mapper from my original 0.90 pickle and the booster loaded from the exported file?

UPDATE

Here's a bit more detail about how I saved and reloaded the booster:

First, I pickled the final estimator from the pipeline:

joblib.dump(pipeline._final_estimator, './final_estimator.pkl')

Next, I ran the conversion script on the pickle:

python3 convert_090to100.py --old-pickle final_estimator.pkl

Then I imported the file produced by the script:

final_estimator_reloaded = XGBRegressor()
final_estimator_reloaded.load_model('./xgboost_native_model_from_final_estimator.pkl-0.bin')

Then I produced a pipeline using this reloaded model and tried to produce predictions with it:

rebuilt_pipeline = Pipeline([
    ('mapper', pipeline[0]),
    ('regressor', final_estimator_reloaded)
])

rebuilt_pipeline.predict(df)

Further update: this worked after all.

1 Answers

You can arbitrary extract pipeline steps by index, and the final estimator as Pipeline._final_estimator_ property.

Constructing a new "pre-fitted" pipeline based on your current one:

mapper = pipeline[0]
print(mapper)

regressor = pipeline._final_estimator
print(regressor)

pipeline2 = Pipeline([
  ("mapper", mapper),
  ("regressor", regressor)
])
print(pipeline2.predict(df[["a", "b"]]))
Related