# JPMML-SkLearn moves a fitted scikit-learn pipeline into a PMML file

> JPMML-SkLearn is a Java library and command line application that converts a fitted Scikit-Learn pipeline, serialized as a pickle, into the PMML interchange format. It suits teams with Python-trained models that need to run in a Java or non-Python runtime, and it carries a broad, explicitly versioned support surface.

**jpmml/jpmml-sklearn** — Java library and command-line application for converting Scikit-Learn pipelines to PMML

- Repository: https://github.com/jpmml/jpmml-sklearn
- Stars: 541 · Forks: 116
- Language: Java
- License: AGPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jpmml-jpmml-sklearn

## The conversion starts from a pickle, not from a model object

The workflow is three steps and the middle one is unusual. You use Scikit-Learn to assemble and fit a pipeline, you serialize that pipeline in `pickle` data format to a file in a local filesystem, and then you use the JPMML-SkLearn command line application to convert the pickle file to a PMML file. The serialization is described as Joblib-flavoured `pickle`, and the example serializes with a single call:

```python
import joblib

joblib.dump(pipeline, "pipeline.pkl")
```

This is the mechanism that makes the project work across languages. A Python pickle is not a portable artifact, since unpickling it requires the same Python version and the same library versions that produced it, which is exactly the coupling that a Java deployment cannot accept. PMML is a document format, so the conversion step is a one-way translation from an interpreter-bound object graph into a self-describing XML file. The practical consequence for a pipeline is that the tree you fitted in Python becomes a static description of preprocessing and prediction that another runtime can execute without Scikit-Learn present at all.

## Feature information is collected, analysed and encoded, not guessed

The feature list is the part of the conversion that most affects whether the deployed model behaves like the trained one, and the documentation is unusually specific about it. The claim is a thorough collection, analysis and encoding of feature information, and the enumerated items are names, data and operational types, valid, invalid and missing value spaces, and descriptive statistics. Each of those corresponds to something that goes wrong silently otherwise. If the deployed system does not know a column is an integer and not a string, a coercion happens at scoring time and the prediction changes. If it does not know which values are invalid, a bad row is scored instead of rejected. Descriptive statistics travel with the model so that the deployment can sanity check the data it is given, which is how you notice a feature order change or a unit change before a customer does. None of this is scikit-learn's job, because scikit-learn fits in memory and the data is right there, so the conversion is where the contract with the outside world has to be written down.

## Pipeline extensions cover pruning, prediction post-processing and verification

Beyond straight conversion, the library adds three things to the pipeline, and each addresses a real gap left by the fitted object. The first is pruning, which removes the parts of a fitted pipeline that a scoring deployment does not need. The second is decision engineering, described as prediction post-processing, which is where a model gets a probability turned into an action, and where the business rule that a fitted pipeline does not contain has to be added. The third is model verification, which is the part that catches a mismatch between training and deployment. Without it, a converted model can be syntactically valid and still score differently, because a transformation was applied in a different order or with a different default. The documentation also lists conversion options as a separate feature category, which is the user-facing surface for all of this. Taken together, these are the reasons to use a dedicated converter rather than hand-writing the PMML, because the fiddly parts are the parts a hand-written file gets wrong.

## Custom converters are discovered through a properties resource file

The extensibility story matters for anyone with a private estimator, and it is built on a resource file rather than a registration API. The documentation describes rich Java APIs for developing custom converters, automatic discovery and registration of custom converters based on `META-INF/sklearn2pmml.properties` resource files, and direct interfacing with other JPMML conversion libraries. The mechanism is ordinary Java resource loading. A converter you write is packaged with a properties file describing which Python classes it handles, and the library finds it on the classpath at conversion time, so no code change and no hardcoded list is needed to teach the converter about a new estimator. That is why the repository root contains sibling modules for H2O, LightGBM, StatsModels and XGBoost, named `pmml-sklearn-h2o/`, `pmml-sklearn-lightgbm/`, `pmml-sklearn-statsmodels/` and `pmml-sklearn-xgboost/`, alongside the core `pmml-sklearn/`, the `pmml-sklearn-extension/` module and the `pmml-sklearn-evaluator/`. The sibling modules are separate artifacts that a user adds, and each of the linked integrations in the documentation shows a pipeline from one of those libraries being converted.

## Building is one Maven command, and the artifact names carry the version

Installation is a build from source, and the documentation says to enter the project root directory and build using Apache Maven:

```bash
mvn clean install
```

The build produces two JAR files, and the names matter because the usage instructions refer to them by path. The library JAR is `pmml-sklearn/target/pmml-sklearn-1.9-SNAPSHOT.jar` and the executable uber-JAR is `pmml-sklearn-example/target/pmml-sklearn-example-executable-1.9-SNAPSHOT.jar`. A multi-module Maven build at the root, with a `pom.xml` and one directory per module, is the standard JPMML layout. Note that the paths carry `1.9-SNAPSHOT` while the tagged releases are numbered 1.9.41 and 1.9.40, so a build from a checkout of a given tag produces a jar named after the branch rather than the tag, and the usage example below is written against the snapshot naming. The prerequisites are modest on both sides: Python 2.7, 3.4 or newer with Scikit-Learn 0.16.0 or newer on the Python side, and Java 11 or newer on the Java side. The documentation adds a reassuring note that this is not a typo, since all Scikit-Learn versions from the past ten years, 2015 or newer, should work equally fine.

## One command turns the pickle into PMML, and another prints the help

The conversion step is a single java invocation with two arguments:

```bash
java -jar pmml-sklearn-example/target/pmml-sklearn-example-executable-1.9-SNAPSHOT.jar --pkl-input pipeline.pkl --pmml-output pipeline.pmml
```

The flags are named for what they are, `--pkl-input` and `--pmml-output`, and the uber-JAR is self-contained, so no classpath assembly is needed. The application also documents a help flag:

```bash
java -jar pmml-sklearn-example/target/pmml-sklearn-example-executable-1.9-SNAPSHOT.jar --help
```

The example pipeline the conversion is tested against is a reasonably complete one rather than a trivial single model. It loads the iris dataset as a frame, strips the unit suffix from the column names, and builds a `Pipeline` with a `ColumnTransformer` doing column-oriented feature engineering with a `StandardScaler` over the four measurement columns and the remainder dropped, a commented-out PCA step for table-oriented feature engineering, and a `LogisticRegression` as the final model. The comment structure makes the intent legible: a transformer stage and a model stage, with the alternative stage left in place but disabled. The documentation also points at a test script file, `main.py`, for more classification cases including binary and multi-class, and for regression workflows.

## The support claim is a list you can check, and it is maintained in features.md

The headline claim about coverage is that there are three times more supported Python packages, transformers and estimators than all the competitors combined. That is a comparative number without a named baseline, so the useful thing is the mechanism behind it rather than the ratio. The documentation directs you to a `features.md` file for the full list of supported transformer and estimator classes, and that file is present at the repository root alongside the project documentation, the `LICENSE.txt` and a `NOTICE.txt`. The point for an adopter is that the support surface is enumerated and inspectable before you commit. You can look up whether your one-of-a-kind estimator is in the list, and if it is not, you know immediately that you need a custom converter rather than discovering it when the build fails. The production quality claims are the other two items worth reading closely: complete test coverage, and full compliance with the JPMML-Evaluator library, which is the reference implementation for reading PMML back, and therefore the way the round trip is checked.

## Where this approach stops, and what the alternative shape is

Two limits are structural rather than fixable. The first is the one-wayness of the translation. A pipeline that has been converted is a document, and the things Scikit-Learn lets you do at training time, hyperparameter search, cross-validation, feature selection driven by the estimator, are all training-time concerns that the PMML file simply does not carry. If your deployment target is a Python service, the conversion buys you nothing and costs you a build step and a second artifact to version. The second is the AGPL-3.0 licence, which is a genuine constraint for a library you embed in a Java application, and worth reading rather than assuming, since copyleft obligations attach to distribution. The alternative to weigh against is a native scoring path, where the model stays in the runtime that trained it, either by keeping the Python service or by using an in-process scoring engine. The difference in approach is that JPMML-SkLearn deliberately breaks the runtime dependency by translating the model into a document, buying portability and auditability at the cost of a conversion step and the loss of everything the training framework did for you. For a model that has to be scored by a Java service that must never depend on Python, that trade is usually worth it. For a model that stays in Python, it is pure overhead.

## Conclusion

JPMML-SkLearn is worth adopting when a Python-trained model has to be scored inside a JVM or another non-Python runtime, because it produces a single PMML document that carries the feature information, the transformations and the model together, and because the conversion is repeatable from a pickle that already exists in your training pipeline. It is a poor fit if your model stays in Python, since the conversion is an extra step with an extra artifact, and a poor fit if you need a model class that the converter does not yet support, since the supported classes are enumerated in `features.md` rather than discovered at runtime. Before adopting it, check `features.md` for your exact estimator and transformer classes, confirm your scikit-learn version falls inside the stated range, and note that the licence is AGPL-3.0, with the last push to the repository on 2026-08-30 and release 1.9.41 published the same day.

## FAQ

### What does JPMML-SkLearn do?

It is a Java library and command line application for converting Scikit-Learn pipelines to PMML. The workflow is to fit a pipeline in Python, serialize it in Joblib-flavoured pickle format to a file, and then convert that pickle file to a PMML file with the command line application.

### How do I build JPMML-SkLearn and what does the build produce?

Enter the project root directory and build with Apache Maven using mvn clean install. The build produces a library JAR at pmml-sklearn/target/pmml-sklearn-1.9-SNAPSHOT.jar and an executable uber-JAR at pmml-sklearn-example/target/pmml-sklearn-example-executable-1.9-SNAPSHOT.jar.

### How do I convert a scikit-learn pickle to PMML from the command line?

Run the executable uber-JAR with the --pkl-input flag pointing at the pickle and the --pmml-output flag pointing at the PMML file you want. The same application takes a --help flag, and the documentation points at a test script, main.py, for binary and multi-class classification and regression workflows.

### How do I convert a custom scikit-learn estimator that is not supported?

The library provides rich Java APIs for developing custom converters and automatically discovers and registers them based on META-INF/sklearn2pmml.properties resource files, so a converter you write can be packaged and picked up from the classpath without changing the library. The full list of supported classes is in features.md.

## Sources

- [Issues](https://github.com/jpmml/jpmml-sklearn/issues)
- [jpmml/jpmml-sklearn on GitHub](https://github.com/jpmml/jpmml-sklearn)
- [License: AGPL-3.0](https://github.com/jpmml/jpmml-sklearn/blob/master/LICENSE)
- [README](https://github.com/jpmml/jpmml-sklearn/blob/master/README.md)
- [Releases](https://github.com/jpmml/jpmml-sklearn/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jpmml-jpmml-sklearn
