.. |gitlogo| image:: _static/git_logo.png
:height: 20
.. image:: https://github.com/sdpython/mlprodict/blob/master/_doc/sphinxdoc/source/_static/project_ico.png?raw=true
:target: https://github.com/sdpython/mlprodict/
mlprodict
=========
**Links:** `github `_,
`documentation `_,
:ref:`l-README`,
:ref:`blog `
.. image:: https://travis-ci.com/sdpython/mlprodict.svg?branch=master
:target: https://app.travis-ci.com/github/sdpython/mlprodict/
:alt: Build status
.. image:: https://ci.appveyor.com/api/projects/status/g8chk1ufyk1m8uep?svg=true
:target: https://ci.appveyor.com/project/sdpython/mlprodict
:alt: Build Status Windows
.. image:: https://circleci.com/gh/sdpython/mlprodict/tree/master.svg?style=svg
:target: https://circleci.com/gh/sdpython/mlprodict/tree/master
.. image:: https://dev.azure.com/xavierdupre3/mlprodict/_apis/build/status/sdpython.mlprodict
:target: https://dev.azure.com/xavierdupre3/mlprodict/
.. image:: https://badge.fury.io/py/mlprodict.svg
:target: https://pypi.org/project/mlprodict/
.. image:: https://img.shields.io/badge/license-MIT-blue.svg
:alt: MIT License
:target: http://opensource.org/licenses/MIT
.. image:: https://codecov.io/github/sdpython/mlprodict/coverage.svg?branch=master
:target: https://codecov.io/github/sdpython/mlprodict?branch=master
.. image:: http://img.shields.io/github/issues/sdpython/mlprodict.png
:alt: GitHub Issues
:target: https://github.com/sdpython/mlprodict/issues
.. image:: http://www.xavierdupre.fr/app/mlprodict/helpsphinx/_images/nbcov.png
:target: http://www.xavierdupre.fr/app/mlprodict/helpsphinx/all_notebooks_coverage.html
:alt: Notebook Coverage
.. image:: https://pepy.tech/badge/mlprodict
:target: https://pypi.org/project/mlprodict/
:alt: Downloads
.. image:: https://img.shields.io/github/forks/sdpython/mlprodict.svg
:target: https://github.com/sdpython/pyquickhelper/
:alt: Forks
.. image:: https://img.shields.io/github/stars/sdpython/mlprodict.svg
:target: https://github.com/sdpython/mlprodict/
:alt: Stars
.. image:: https://mybinder.org/badge_logo.svg
:target: https://mybinder.org/v2/gh/sdpython/mlprodict/master?filepath=_doc%2Fnotebooks
.. image:: https://img.shields.io/github/repo-size/sdpython/mlprodict
:target: https://github.com/sdpython/mlprodict/
:alt: size
.. toctree::
:maxdepth: 1
installation
tutorial/index
api/index
onnx
onnx_bench
i_cmd
i_ex
i_index
gyexamples/index
all_notebooks
HISTORY
*mlprodict* was initially started to help implementing converters
to :epkg:`ONNX`. The main feature is a python runtime for
:epkg:`ONNX`. It gives more feedback than :epkg:`onnxruntime`
when the execution fails.
.. runpython::
:showcode:
:warningout: DeprecationWarning
import numpy
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_iris
from mlprodict.onnxrt import OnnxInference
from mlprodict.onnxrt.validate.validate_difference import measure_relative_difference
from mlprodict import get_ir_version
iris = load_iris()
X = iris.data[:, :2]
y = iris.target
lr = LinearRegression()
lr.fit(X, y)
# Predictions with scikit-learn.
expected = lr.predict(X[:5])
print(expected)
# Conversion into ONNX.
from mlprodict.onnx_conv import to_onnx
model_onnx = to_onnx(lr, X.astype(numpy.float32),
black_op={'LinearRegressor'},
target_opset=15)
print("ONNX:", str(model_onnx)[:200] + "\n...")
# Predictions with onnxruntime
model_onnx.ir_version = get_ir_version(15)
oinf = OnnxInference(model_onnx, runtime='onnxruntime1')
ypred = oinf.run({'X': X[:5].astype(numpy.float32)})
print("ONNX output:", ypred)
# Measuring the maximum difference.
print("max abs diff:", measure_relative_difference(expected, ypred['variable']))
# And the python runtime
oinf = OnnxInference(model_onnx, runtime='python')
ypred = oinf.run({'X': X[:5].astype(numpy.float32)},
verbose=1, fLOG=print)
print("ONNX output:", ypred)
These predictions are obtained with the
following :epkg:`ONNX` graph.
.. gdot::
:script: DOT-SECTION
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_iris
from mlprodict.onnxrt import OnnxInference
import numpy
iris = load_iris()
X = iris.data[:, :2]
y = iris.target
lr = LinearRegression()
lr.fit(X, y)
from mlprodict.onnx_conv import to_onnx
model_onnx = to_onnx(lr, X.astype(numpy.float32))
oinf = OnnxInference(model_onnx)
print("DOT-SECTION", oinf.to_dot())
Notebook :ref:`onnxvisualizationrst`
shows how to visualize an :epkg:`ONNX` pipeline.
The package also contains a collection of tools
to help converting code to ONNX. A short list of
them:
* **Python runtime for ONNX:**
:class:`OnnxInference `,
it is mostly used to check that an ONNX graph produces the expected output.
If it fails, it fails within a python code and not inside C++ code.
This class can also be used to call :epkg:`onnxruntime` by
using ``runtime=='onnxruntime1'``. A last runtime
``runtime=='python_compiled'`` compiles a python function equivalent
to code calling operator one by one. It makes easier to read the ONNX
graph (see :ref:`l-onnx-tutorial`).
* **Intermediate results:**
the python runtime may display all intermediate results,
their shape if `verbosity == 1`, their value if `verbosity > 10`,
see :ref:`l-onnx-tutorial`. This cannot be done with ``runtime=='onnxruntime1'``
but it is still possible to get the intermediate results
(see :meth:`OnnxInference.run `).
The class will build all subgraphs from the inputs to every intermediate
results. If the graph has *N* operators, the cost of this will be
:math:`O(N^2)`.
* **Extract a subpart of an ONNX graph:**
hen an ONNX graph does not load, it is possible to modify, to extract
some subpart to check a tiny part of it. Function
:func:`select_model_inputs_outputs
`
may be used to change the inputs and/or the outputs.
* **Change the opset**: function
:func:`overwrite_opset
`
overwrites the opset, it is used to check for which opset (ONNX version)
a graph is valid. ...
* **Visualization in a notebook**: a magic command to display
small ONNX graph in notebooks :ref:`onnxvisualizationrst`.
* **Text visualization for ONNX:** a way to visualize ONNX graph only
with text :func:`onnx_text_plot `.
* **Text visualization of TreeEnsemble:** a way to visualize the graph
described by a on operator TreeEnsembleRegressor or TreeEnsembleClassifier,
see :func:`onnx_text_plot `.
* **Export ONNX graph to numpy:** the numpy code produces the same
results as the ONNX graph (see :func:`export2numpy
`)
* **Export ONNX graph to ONNX API:** this produces a
a code based on ONNX API which replicates the ONNX graph
(see :func:`export2onnx
`)
* **Export ONNX graph to** :epkg:`tf2onnx`: still a function which
creates an ONNX graph but based on :epkg:`tf2onnx` API
(see :func:`export2tf2onnx
`)
* **Xop API:** (ONNX operators API), see :ref:`l-xop-api`,
most of the converting libraries uses :epkg:`onnx` to create ONNX graphs.
The API is quite verbose and that is why most of them implement a second
API wrapping the first one. They are not necessarily meant to be used
by users to create ONNX graphs as they are specialized for the training
framework they are developped for.
* **Numpy API for ONNX:** many functions doing computation are
written with :epkg:`numpy` and converting them to ONNX may take
quite some time for users not familiar with ONNX. This API implements
many functions from :epkg:`numpy` with ONNX and allows the user
to combine them. It is as if numpy function where exectued by an
ONNX runtime: :ref:`l-numpy-api-for-onnx`.
* **Benchmark scikit-learn models converted into ONNX:** a simple function to
benchmark ONNX against *scikit-learn* for a simple model:
:ref:`l-example-onnx-benchmark`
* **Accelerate scikit-learn prediction:**,
what if *transform* or *predict* is replaced by an implementation
based on ONNX, or a numpy version of it, would it be faster?
:ref:`l-Speedup-pca`
* **Profiling onnxruntime:** :epkg:`onnxruntime` can memorize the time
spent in each operator. The following notebook shows how to retreive
the results and display them :ref:`onnxprofileortrst`.
This package supports ONNX opsets to the latest opset stored
in `mlprodict.__max_supported_opset__` which is:
.. runpython::
:showcode:
import mlprodict
print(mlprodict.__max_supported_opset__)
Any opset beyond that value is not supported and could fail.
That's for the main set of ONNX functions or domain.
Converters for :epkg:`scikit-learn` requires another domain,
`'ai.onnxml'` to implement tree. Latest supported options
are defined here:
.. runpython::
:showcode:
import pprint
import mlprodict
pprint.pprint(mlprodict.__max_supported_opsets__)
+----------------------+---------------------+---------------------+--------------------+------------------------+------------------------------------------------+
| :ref:`l-modules` | :ref:`l-functions` | :ref:`l-classes` | :ref:`l-methods` | :ref:`l-staticmethods` | :ref:`l-properties` |
+----------------------+---------------------+---------------------+--------------------+------------------------+------------------------------------------------+
| :ref:`modindex` | :ref:`l-EX2` | :ref:`search` | :ref:`l-license` | :ref:`l-changes` | :ref:`l-README` |
+----------------------+---------------------+---------------------+--------------------+------------------------+------------------------------------------------+
| :ref:`genindex` | :ref:`l-FAQ2` | :ref:`l-notebooks` | | :ref:`l-statcode` | `Unit Test Coverage `_ |
+----------------------+---------------------+---------------------+--------------------+------------------------+------------------------------------------------+