Skip to main content

sklearn-migrator 🧪

A Python library to serialize and migrate scikit-learn models across incompatible versions.

PyPI version Python versions License: MIT CI codecov DOI

sklearn-migrator


🚀 Motivation

Machine learning teams frequently store trained scikit-learn models using pickle or joblib.
However:

❌ These serialized models break when scikit-learn versions change

  • Internal attributes change
  • APIs evolve (e.g., affinity → metric)
  • Tree and boosting internals get reorganized
  • New default parameters appear

❌ This creates real problems:

  • Production services fail after dependency upgrades
  • Research becomes non-reproducible
  • Long-term model governance becomes impossible
  • Models can't be migrated or audited reliably

✅ What sklearn-migrator provides

✔ One serialize / deserialize pair for every model — the library detects the model for you

✔ Serialize any supported model into a JSON-compatible dictionary

✔ Deserialize and reconstruct the model in a different scikit-learn version

✔ Remove dependency on pickle/joblib for long-term storage

✔ Enable reproducible ML pipelines across environments

This library has been validated across 1,024 version migration pairs (from → to), covering:

0.21.3 → 1.7.2


💡 Supported Models (21 models)

sklearn-migrator supports 21 core models across classification, regression, clustering, and dimensionality reduction.

📘 Classification

Model Supported
DecisionTreeClassifier ✅
RandomForestClassifier ✅
GradientBoostingClassifier ✅
LogisticRegression ✅
KNeighborsClassifier ✅
SVC (Support Vector Classifier) ✅
MLPClassifier ✅

📗 Regression

Model Supported
DecisionTreeRegressor ✅
RandomForestRegressor ✅
GradientBoostingRegressor ✅
LinearRegression ✅
Ridge ✅
Lasso ✅
KNeighborsRegressor ✅
SVR (Support Vector Regressor) ✅
AdaBoostRegressor ✅
MLPRegressor ✅

📙 Clustering

Model Supported
KMeans ✅
MiniBatchKMeans ✅
Agglomerative ✅

📘 Dimensionality Reduction

Model Supported
PCA ✅

🔢 Version Compatibility Matrix

The library supports model migrations across the full matrix:

  • 32 versions
  • 1,024 migration pairs
  • Fully tested using automated environments via CI/CD on every push
versions = [
    '0.21.3', '0.22.0', '0.22.1', '0.23.0', '0.23.1', '0.23.2',
    '0.24.0', '0.24.1', '0.24.2', '1.0.0', '1.0.1', '1.0.2',
    '1.1.0', '1.1.1', '1.1.2', '1.1.3', '1.2.0', '1.2.1', '1.2.2',
    '1.3.0', '1.3.1', '1.3.2', '1.4.0', '1.4.2', '1.5.0', '1.5.1',
    '1.5.2', '1.6.0', '1.6.1', '1.7.0', '1.7.1', '1.7.2'
]
From \ To 0.21.3 0.22.0 ... 1.7.2
0.21.3 ✅ ✅ ... ✅
0.22.0 ✅ ✅ ... ✅
... ... ... ... ...
1.7.2 ✅ ✅ ... ✅

📊 Validation Metric

Each migration pair (version_in, version_out) is validated using:

$$\max |y_{\text{in}} - y_{\text{out}}| < 10^{-2}$$

Where:

  • $y_{\text{in}}$: predictions from the model in the source version
  • $y_{\text{out}}$: predictions from the migrated model in the target version

The worst case across all 1,024 pairs is obtained via:

df_performance.abs().max().max()  # global worst case (32x32 matrix)

⚠️ All 1,024 combinations and 21 models are automatically tested on every push via CI/CD, using isolated Docker environments for each sklearn version. Each model is validated under a representative parameter configuration; exhaustive combinatorial testing of all parameter combinations is outside the current scope.


📂 Installation

pip install sklearn-migrator

⚡ Quick Start

Two functions cover all 21 models. You don't need to know which serializer matches your model: serialize detects it from the model itself, and deserialize reads it from the serialized dictionary.

from sklearn_migrator import serialize, deserialize

data = serialize(model)        # in the source environment -> JSON-compatible dict
new_model = deserialize(data)  # in the target environment -> model for the installed scikit-learn

Both functions use the installed scikit-learn version by default. You can list the supported models with supported_models().


📚 API Documentation

For full API documentation covering serialize, deserialize, all 21 models, parameters, return types, and usage examples, see API.md.


💥 Use Cases

  • Long-term model storage: Store models in a future-proof format across teams and systems.
  • Production model migration: Move models safely across major scikit-learn upgrades.
  • Auditing and inspection: Read serialized models as JSON, inspect structure, hyperparameters, and internals.
  • Cross-platform inference: Serialize in Python, serve elsewhere (e.g., microservices).

1. Using two python environments

You can serialize the model from an environment with a scikit-learn version (for example 1.5.0) and then deserialize the model from another environment with a different version (for example 1.7.0).

The deserialized model has the version of the environment where you deserialized it. In this case 1.7.0.

It is important to understand what version of scikit-learn you want to migrate from, and what version you want to migrate to, in order to create the appropriate environments for serialization and deserialization.

a. Serialize the model

import json
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn_migrator import serialize

X, y = make_regression(n_samples=200, n_features=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = RandomForestRegressor().fit(X_train, y_train)
predictions = model.predict(X_test)
data = serialize(model)

with open("model.json", "w") as f:
    json.dump(data, f)

b. Deserialize the model

import json
from sklearn_migrator import deserialize

with open("model.json") as f:
    data = json.load(f)

new_model = deserialize(data)
new_predictions = new_model.predict(X_test)

2. Docker: Step by Step

You have a Random Forest Classifier saved in a .pkl format and it is called model.pkl. The version of this model is 1.5.0.

i. Create in your Desktop the next folder:

/test_github

And copy your model.pkl in this folder.

ii. The Dockerfiles and requirements for all supported input versions are available in the integration/environments/input/ directory of this repository. Copy the files for your input version (e.g., 1.5.0):

/test_github/input/1.5.0/Dockerfile_input
/test_github/input/1.5.0/requirements_input.txt

iii. The Dockerfiles and requirements for all supported output versions are available in the integration/environments/output/ directory of this repository. Copy the files for your output version (e.g., 1.7.0):

/test_github/output/1.7.0/Dockerfile_output
/test_github/output/1.7.0/requirements_output.txt

iv. Now you create your input.py:

import json
import numpy as np
import pandas as pd
from joblib import load

from sklearn_migrator import serialize

model = load('model.pkl')

all_data = serialize(model)

with open("input_model/all_data.json", "w") as f:
    json.dump(all_data, f)

fake_row = np.array([[0.5, -1.2, 0.3, 1.1, -0.7, 0.9, 0.0, -0.3, 1.5, 0.2]])

y_pred = pd.DataFrame(model.predict_proba(fake_row))
y_pred.to_csv('input_model/y_pred.csv', index=False)

v. Now you create your output.py:

import json
import joblib
import numpy as np
import pandas as pd

from sklearn_migrator import deserialize

with open("input_model/all_data.json", "r") as f:
    all_data = json.load(f)

new_model = deserialize(all_data)

joblib.dump(new_model, 'output_model/new_model.pkl')

fake_row = np.array([[0.5, -1.2, 0.3, 1.1, -0.7, 0.9, 0.0, -0.3, 1.5, 0.2]])

y_pred_new = pd.DataFrame(new_model.predict_proba(fake_row))
y_pred_new.to_csv('output_model/y_pred_new.csv', index=False)

vi. Now you copy all the files:

cp input/1.5.0/* output/1.7.0/* .

vii. Now you create two folders: input_model/ and output_model/.

viii. Execute the next commands in your terminal (you should be in the root of test_github/ folder):

docker build -f Dockerfile_input -t image_input_1.5.0 .
docker build -f Dockerfile_output -t image_output_1.7.0 .

docker run --rm \
  -v "$(pwd)/input_model:/app/input_model" \
  -v "$(pwd)/model.pkl:/app/model.pkl" \
  image_input_1.5.0

docker run --rm \
  -v "$(pwd)/input_model:/app/input_model" \
  -v "$(pwd)/output_model:/app/output_model" \
  image_output_1.7.0

ix. Finally you can find your migrated model in the folder /output_model and its name is new_model.pkl. This model is a scikit-learn model of version 1.7.0.


🔧 Development

Run tests locally

pytest tests/

Integration tests run automatically on every push via CI/CD.


🤝 Contributing

  1. Fork the repository
  2. Create a new branch feature/my-feature
  3. Open a pull request
  4. Please ensure your pull request is tested for all combinations of functions; otherwise, it may be rejected.

We welcome bug reports, suggestions, and contributions of new models.


📄 License

MIT License — see LICENSE for details.


🔍 Author

Alberto Valdés

ML/AI Engineer | MLOps Engineer | Open Source Contributor

GitHub: @anvaldes


Metadata

Release files for sklearn-migrator 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sklearn-migrator 1.0.0
File Size Uploaded
sklearn_migrator-1.0.0.tar.gz 31.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sklearn-migrator 1.0.0
File Interpreter ABI Platform
sklearn_migrator-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.3 kB

Release files / sklearn_migrator-1.0.0.tar.gz

Download URL sklearn_migrator-1.0.0.tar.gz
Size 31.0 kB
Tags Source
SHA-256 checksum
How to use checksums
b93bf139d414f22818aca47918ba27bda5878ebc32a073b0156314e14f918a34
BLAKE2b-256 checksum
How to use checksums
c02fcae96859b0925b31b5d140524ea1d443e4225ec2b9a35e61aff2e53cc1d9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Release files / sklearn_migrator-1.0.0-py3-none-any.whl

Download URL sklearn_migrator-1.0.0-py3-none-any.whl
Size 41.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6f1a480c026ae994325a35ee1e63d7e048aa567824892f44e12d2f0e71ebd972
BLAKE2b-256 checksum
How to use checksums
4a9f24770fe1cf760ecb83d20d831f556b25ce55c87b0cb46e3e16cd0f80385a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.22.1

2 release files

0.21.1

2 release files

0.16.0

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page