sklearn-porter
Transpile trained scikit-learn estimators to C, Go, Java, JavaScript, PHP and Ruby.
Use the generated code to make predictions where Python isn't available or too heavy, e.g. on embedded systems, in web browsers or in existing applications. The ported estimators make the same predictions as the original estimators of scikit-learn.
Navigation: Estimators • Installation • Usage • Known Issues • Contributing • Citation • License
Estimators
Classifiers
- svm.SVC
- svm.NuSVC
- svm.LinearSVC
- tree.DecisionTreeClassifier
- ensemble.RandomForestClassifier
- ensemble.ExtraTreesClassifier
- ensemble.AdaBoostClassifier
- neighbors.KNeighborsClassifier
- naive_bayes.BernoulliNB
- naive_bayes.GaussianNB
- neural_network.MLPClassifier
Regressors
- tree.DecisionTreeRegressor
- ensemble.RandomForestRegressor
- ensemble.ExtraTreesRegressor
- neural_network.MLPRegressor
Languages, templates and methods
The estimators are ported to C, Go, Java, JavaScript, PHP and Ruby. The template defines how the model data is stored in the generated code:
attached: the model data is part of the code.exported: the model data is saved in a separate JSON file.combined: the model data is compiled into the logic of the code (e.g. nested if-else statements).
All estimators support the method predict, most classifiers also predict_proba. Not every estimator supports every combination of language, template and method yet. Check a combination with Estimator.can or show all supported combinations with show or porter show.
Installation
# with uv:
uv add sklearn-porter
# or with pip:
pip install sklearn-porter
sklearn-porter requires Python 3.11 or newer and scikit-learn 1.8 or newer. It's tested with Python 3.11, 3.12, 3.13 and 3.14. For older versions of Python and scikit-learn use sklearn-porter 0.7.4.
You only need a compiler or interpreter of the target language (e.g. node for JavaScript) if you want to run the ported estimator with make or test. port and save work without it.
Usage
Basics
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn_porter import make, port, save, test
# 1. Load the data and train a classifier:
X, y = load_iris(return_X_y=True)
clf = DecisionTreeClassifier()
clf.fit(X, y)
# 2. Port (transpile) the classifier and get the source code:
output = port(clf, language='js', template='attached')
print(output)
# 3. Save the ported classifier to files:
src_path, json_path = save(
clf, language='js', template='exported', directory='/tmp'
)
print(src_path, json_path)
# 4. Make predictions with the ported classifier:
y_classes, y_probas = make(clf, X[:10], language='js', template='exported')
print(y_classes, y_probas)
# 5. Check the ported classifier: the score is the share of predictions
# which are equal to the predictions of the original classifier.
score = test(clf, X[:10], language='js', template='exported')
print(score)
Object-oriented
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn_porter import Estimator
# 1. Load the data and train a classifier:
X, y = load_iris(return_X_y=True)
clf = DecisionTreeClassifier()
clf.fit(X, y)
# 2. Port (transpile) the classifier and get the source code:
est = Estimator(clf, language='js', template='attached')
output = est.port()
print(output)
# 3. Save the ported classifier to files:
est.template = 'exported'
src_path, json_path = est.save(directory='/tmp')
print(src_path, json_path)
# 4. Make predictions with the ported classifier:
y_classes, y_probas = est.make(X[:10])
print(y_classes, y_probas)
# 5. Check the ported classifier: the score is the share of predictions
# which are equal to the predictions of the original classifier.
score = est.test(X[:10])
print(score)
Regressors
Regressors are ported the same way. make returns the predicted values and None instead of probabilities. test returns the share of predictions which are close to the predictions of the original regressor (see numpy.isclose), because the floating point arithmetic of the target languages can differ slightly:
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestRegressor
from sklearn_porter import Estimator
X, y = load_diabetes(return_X_y=True)
reg = RandomForestRegressor(n_estimators=10, max_depth=5)
reg.fit(X, y)
est = Estimator(reg, language='java', template='attached')
y_values, _ = est.make(X[:10])
print(y_values)
print(est.test(X[:10]))
Options
port, save, make, test and Estimator accept these additional arguments:
class_name: the name of the class (or function) in the generated code. By default it's the name of the estimator, e.g.DecisionTreeClassifier.converter: a function which converts each floating point number of the model data to a string. By default it'sstr. Fewer digits make the code smaller, but can change the predictions, so check the result withtest:
output = port(
clf,
language='c',
template='attached',
class_name='Iris',
converter=lambda value: f'{value:.6f}',
)
Supported combinations
show prints the supported estimators, languages and templates, Estimator.can checks a single combination:
from sklearn_porter import Estimator, show
print(show()) # or e.g. show(language='js')
est = Estimator(clf)
est.can(language='js', template='combined') # True
est.can(language='js', template='combined', method='predict_proba') # True
Pipelines and optimizers
You can pass a fitted Pipeline, GridSearchCV or RandomizedSearchCV. sklearn-porter extracts the final estimator of the pipeline or the best estimator of the optimizer and ports only this estimator (see Known Issues).
Command line
After the installation the command porter is available:
porter {show,port,save} [-h] [-v]
porter show [-l {c,go,java,js,php,ruby}] [-h]
porter port [-l {c,go,java,js,php,ruby}]
[-t {attached,combined,exported}]
[--skip-warnings] [-h]
porter save [-l {c,go,java,js,php,ruby}]
[-t {attached,combined,exported}]
[--directory DIRECTORY]
[--skip-warnings] [-h]
First save the trained estimator to a file (see model persistence):
from joblib import dump
dump(clf, 'estimator.joblib', compress=0)
Then port the saved estimator with the subcommand port:
porter port estimator.joblib -l js -t attached > estimator.js
The output can be passed to other tools, e.g. to terser to minify the code:
porter port estimator.joblib -l js -t attached | npx terser --compress -o estimator.min.js
Logging
Change the option logging.level to see what sklearn-porter does:
from logging import DEBUG
from sklearn_porter import options
options['logging.level'] = DEBUG
Examples
The example notebook shows the functional and the object-oriented API step by step.
Known Issues
- Only the final estimator of a
Pipelineis ported. Preprocessing steps like aStandardScalerare dropped without a warning, so you have to apply them to the input data of the ported estimator yourself. Otherwise the predictions differ from the predictions of the pipeline. - The ported support vector machines, SVC and NuSVC, can make different predictions than the original estimators in rare cases. Therefore sklearn-porter raises a
QualityWarningfor them: check the ported estimator with thetestmethod. - The AdaBoostClassifier is ported with the
SAMMEalgorithm, because scikit-learn removed theSAMME.Ralgorithm.
Contributing
Contributions are welcome! See CONTRIBUTING.md for the development setup, the tests and the release process.
Citation
If you use sklearn-porter in your work, please cite it. You can use this BibTeX entry:
@misc{sklearn_porter,
author = {Darius Morawiec},
title = {sklearn-porter},
note = {Transpile trained scikit-learn estimators to C, Go, Java, JavaScript, PHP and Ruby},
url = {https://github.com/nok/sklearn-porter}
}
License
sklearn-porter is open source software released under the BSD 3-Clause license.