Skip to main content

modelkeep

Keep scikit-learn model files in a usable condition.

A saved model is a file that you must be able to load again next year. Four things go wrong:

Problem modelkeep
You upgrade scikit-learn and the file does not load load and fix repair the file
The file loads after an upgrade, but the results are different, and nothing tells you save keeps proof of the results, verify checks it
You get a model file and do not know what is in it check and describe read it and run no code from it
A request sends bad input and the model stops with an unclear error expects and validate name the bad value
pip install modelkeep

It has no dependencies of its own. It uses the scikit-learn that you have installed: 1.4 or later, on Python 3.10 or later.

Save with proof, verify after an upgrade

import modelkeep

modelkeep.save(model, "model.joblib", X_test[:20])

This writes model.joblib and a small file model.joblib.card.json. The card holds the 20 rows, the results that the model gave for them, and the versions of Python, scikit-learn and NumPy. Keep the two files together.

After you upgrade scikit-learn, or on another computer:

$ modelkeep verify model.joblib
model.joblib
  Result       The model gives the saved results.
  Saved with   scikit-learn 1.2.2, Python 3.10.13
  Now          scikit-learn 1.9.1, Python 3.13.1
  Repairs      10 repairs were necessary to load the file
    predict            same
    predict_proba      same

The exit code is 0 when the results are the same, so you can use modelkeep verify models/ in a test or a deploy step. The sample can be an array, a pandas table or a list of texts.

Load a file that an old version saved

$ python -c "import pickle; pickle.load(open('old.pkl', 'rb'))"
ValueError: node array from the pickle has an incompatible dtype

$ modelkeep check old.pkl
old.pkl
  Saved with   scikit-learn 1.2.2 (pickle)
  Installed    scikit-learn 1.9.1
  Model        RandomForestClassifier > DecisionTreeClassifier
  Result       A plain load fails or gives wrong results. modelkeep can repair this file.
  Repairs
    stops a plain load  5 x DecisionTreeClassifier: tree data changed to the new layout; new field set to 0: missing_go_to_left
    wrong results       5 x DecisionTreeClassifier: class counts in the tree changed to fractions; without this, predict_proba returns counts
    default value       7 settings that the old version did not have
  Other way    install scikit-learn 1.2.2 on Python 3.8 to 3.11
  Next         modelkeep fix old.pkl

$ modelkeep fix old.pkl

old.fixed.pkl now loads with a plain pickle.load. The old file stays as it was. If the old file has a card, fix writes the new file only when the repaired model gives the saved results.

model = modelkeep.load("old.pkl")      # the repaired model, with no new file

modelkeep check models/ --json reads all model files in a folder and prints the reports as JSON.

Some old models load and are wrong

An error is the good case, because you see it. These two changes give no error:

  • A tree classifier that scikit-learn 1.3 saved loads in 1.4 and later, but predict_proba returns sample counts such as [3.0, 0.0], not probabilities. This is also true for forests.
  • A TfidfVectorizer from 1.4 or earlier loads in 1.5 and later, but its word weights are not applied.

check shows both as "wrong results". load and fix repair them.

What is tested

The tests hold 315 model files that scikit-learn 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 and 1.8 saved, with the results that each version gave. Each file must give the same results in scikit-learn 1.9.1, to 8 decimal places.

Model Problem in a plain load with 1.9 modelkeep
Decision tree, random forest, extra trees, isolation forest error for files from 1.2 or earlier; wrong predict_proba for classifiers from 1.3 or earlier repaired
Gradient boosting, histogram gradient boosting error for every file from 1.8 or earlier repaired
k nearest neighbors error for files from 1.2 or earlier repaired
Text pipeline with TfidfVectorizer wrong results for files from 1.4 or earlier repaired
Logistic regression, ridge, SVC, k-means, scalers none loads as it is
AdaBoost classifier with the SAMME.R method, the default until 1.5 wrong results refused

A pipeline or a search object that holds these models is repaired too.

Describe a file

$ modelkeep describe model.joblib
model.joblib
  File         joblib, 8 KB, saved with scikit-learn 1.2.2
  Proof card   no
  Parts
    Pipeline  (memory=None, verbose=False)
      StandardScaler  (with_mean=True, with_std=True, copy=True)
      RandomForestClassifier  (n_estimators=5, bootstrap=True, oob_score=False, n_jobs=None, random_state=0, +13)
        5 x DecisionTreeClassifier
  Input
    x0                       number
    x1                       number
  Classes      0, 1

Check the input

A fitted model knows the names of its columns. A ColumnTransformer also knows which columns are numbers and which values each category can have. modelkeep reads this from the model.

print(modelkeep.expects(model))
# age                      number
# city                     category: LA, NY, SF
# income                   number

modelkeep.validate(model, [{"age": "abc", "city": "Paris", "income": 5}])
# ["row 0, column age: 'abc' is not a number",
#  "row 0, column city: 'Paris' is not a known value (LA, NY, SF)"]

modelkeep.expects(model).json_schema()     # JSON Schema for one row, for an API

An empty list means that the rows are correct. Rows can be a pandas table, a list of dictionaries, a list of lists or a list of texts.

Limits

  • AdaBoost with SAMME.R. scikit-learn removed this method in 1.6, so the new version cannot give the old results. modelkeep stops with an explanation. allow_changed=True loads the model with the new method.
  • Models that are not in the table. They load if scikit-learn did not change them. modelkeep fills settings that the old version did not have with their default values, and it lists each one. It does not know about other changes. Use save and verify to get proof for your own model.
  • scikit-learn 0.24. These files load in my tests on real models, but 0.24 has no build for the computer that makes the test files, so no test compares their results.
  • A new file on an old scikit-learn. modelkeep goes forward only.
  • Missing values in old trees. Trees from before 1.3 did not accept missing values. A repaired tree sends them to the right branch. The old version stopped with an error.
  • Input checks. validate knows names, numbers and categories. It does not know the range of a number, because a model does not keep it.

Safety

Python can run any code when it loads a pickle file. check and describe read a file with stand-in classes, so no code from the file runs. If the file names code that is not from scikit-learn, NumPy, SciPy or joblib, modelkeep reports it and does not load the file. Use trust=True only for a file from a source that you trust.

This is a check of names, not a sandbox. For models from other people, use skops.

How a repair works

  1. Scan. A pickle reader gives a stand-in for each class in the file. The stand-ins keep the settings and the scikit-learn version of each part. Nothing is imported from scikit-learn.
  2. Read. A second reader loads the file. It gives the new name to each class that moved. It keeps apart the parts that the new version does not accept, such as the node table of a tree.
  3. Repair. Each of these parts is made again in the new format. The report lists each repair as one of four kinds: stops a plain load, wrong results, removed part, default value.

Develop

uv venv && uv pip install -e . --group dev
.venv/bin/pytest                 # 352 tests
./tools/make_all_fixtures.sh     # save the test models again; uv downloads each old version
python tools/matrix.py           # one line for each old version

To add a scikit-learn version or a model, add it in tools/make_all_fixtures.sh or tools/make_fixtures.py. Run the script, then the tests. A test that fails shows the next repair to write.

Metadata

Release files for modelkeep 0.2.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 modelkeep 0.2.0
File Size Uploaded Provenance Actions
modelkeep-0.2.0.tar.gz 26.4 kB With provenance Details for modelkeep-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for modelkeep 0.2.0
File Interpreter ABI Platform Actions
modelkeep-0.2.0-py3-none-any.whl Python 3 none any Details for modelkeep-0.2.0-py3-none-any.whl

Total release size: 53.7 kB

Release files / modelkeep-0.2.0.tar.gz

Download URL modelkeep-0.2.0.tar.gz
Size 26.4 kB
Tags Source
SHA-256 checksum
How to use checksums
726ab6e29cc12f2c50047a010476a56e3a85fc1c19634f3d9c90d8056e5538ae
BLAKE2b-256 checksum
How to use checksums
c6d13b36f2fc99b256d1697bc4011a13d7a45964af011114e581a90e7189fbb4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / modelkeep-0.2.0-py3-none-any.whl

Download URL modelkeep-0.2.0-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
736c3a9baee0c614e5603fb7a7fc25fdf3166380b6c9d5a6e908163c823f6a8d
BLAKE2b-256 checksum
How to use checksums
facfbb21d943e1d8396fbcbb661d246e4ad07194dd3de19619b2136cb591394c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Security for modelkeep 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for modelkeep-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

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