Execute ONNX graphs#

This package implements a python runtime for ONNX in class OnnxInference. It does not depend on scikit-learn, only numpy and this module. However, this module was not really developped to get the fastest python runtime but mostly to easily develop converters.

Python Runtime for ONNX#

Class OnnxInference implements a python runtime for a subset of ONNX operators needed to convert many scikit-learn models.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='python')
print(oinf.run({'X': X_test[:5]}))

>>>

    {'label': array([2, 2, 2, 2, 1]), 'scores': array([[2.74 , 2.872, 0.94 ],
           [3.308, 1.995, 0.364],
           [3.996, 1.43 , 1.168],
           [3.964, 1.395, 0.632],
           [4.615, 1.005, 1.444]], dtype=float32)}

It is usually useful to get information on intermediate results in the graph itself to understand where the discrepencies begin.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='python')
print(oinf.run({'X': X_test[:5]}, verbose=1, fLOG=print))

>>>

    +ki='Ad_Addcst': (3,) (dtype=float32 min=39.08943176269531 max=94.06978607177734)
    +ki='Ge_Gemmcst': (3, 4) (dtype=float32 min=0.25 max=6.860714435577393)
    +ki='Mu_Mulcst': (1,) (dtype=float32 min=0.0 max=0.0)
    -- OnnxInference: run 7 nodes
    Onnx-ReduceSumSquare(X) -> Re_reduced0    (name='Re_ReduceSumSquare')
    +kr='Re_reduced0': (5, 1) (dtype=float32 min=34.77000045776367 max=73.05999755859375)
    Onnx-Mul(Re_reduced0, Mu_Mulcst) -> Mu_C0    (name='Mu_Mul')
    +kr='Mu_C0': (5, 1) (dtype=float32 min=0.0 max=0.0)
    Onnx-Gemm(X, Ge_Gemmcst, Mu_C0) -> Ge_Y0    (name='Ge_Gemm')
    +kr='Ge_Y0': (5, 3) (dtype=float32 min=-165.67855834960938 max=-73.67118072509766)
    Onnx-Add(Re_reduced0, Ge_Y0) -> Ad_C01    (name='Ad_Add')
    +kr='Ad_C01': (5, 3) (dtype=float32 min=-92.61856079101562 max=-22.302940368652344)
    Onnx-Add(Ad_Addcst, Ad_C01) -> Ad_C0    (name='Ad_Add1')
    +kr='Ad_C0': (5, 3) (dtype=float32 min=0.008258819580078125 max=27.46263885498047)
    Onnx-Sqrt(Ad_C0) -> scores    (name='Sq_Sqrt')
    +kr='scores': (5, 3) (dtype=float32 min=0.09087804704904556 max=5.240480899810791)
    Onnx-ArgMin(Ad_C0) -> label    (name='Ar_ArgMin')
    +kr='label': (5,) (dtype=int64 min=1 max=2)
    {'label': array([1, 1, 2, 1, 1]), 'scores': array([[5.032, 0.091, 3.304],
           [5.24 , 0.434, 3.478],
           [1.205, 4.097, 0.857],
           [4.78 , 0.524, 3.025],
           [4.628, 0.626, 2.976]], dtype=float32)}

The verbosity can be increased.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='python')
print(oinf.run({'X': X_test[:5]}, verbose=3, fLOG=print))

>>>

    +ki='Ad_Addcst': (3,) (dtype=float32 min=38.65431213378906 max=92.83167266845703
    [38.654 92.832 63.403]
    +ki='Ge_Gemmcst': (3, 4) (dtype=float32 min=0.2349998950958252 max=6.82424259185791
    [[4.975 3.432 1.438 0.235]
     [6.824 3.073 5.718 2.03 ]
     [5.867 2.746 4.395 1.459]]
    +ki='Mu_Mulcst': (1,) (dtype=float32 min=0.0 max=0.0
    [0.]
    -kv='X' shape=(5, 4) dtype=float32 min=0.30000001192092896 max=5.699999809265137
    -- OnnxInference: run 7 nodes
    Onnx-ReduceSumSquare(X) -> Re_reduced0    (name='Re_ReduceSumSquare')
    +kr='Re_reduced0': (5, 1) (dtype=float32 min=34.09000015258789 max=62.269996643066406)
    [[43.13]
     [62.27]
     [53.23]
     [52.5 ]
     [34.09]]
    Onnx-Mul(Re_reduced0, Mu_Mulcst) -> Mu_C0    (name='Mu_Mul')
    +kr='Mu_C0': (5, 1) (dtype=float32 min=0.0 max=0.0)
    [[0.]
     [0.]
     [0.]
     [0.]
     [0.]]
    Onnx-Gemm(X, Ge_Gemmcst, Mu_C0) -> Ge_Y0    (name='Ge_Gemm')
    +kr='Ge_Y0': (5, 3) (dtype=float32 min=-151.74606323242188 max=-72.52100372314453)
    [[ -81.572 -113.375  -96.386]
     [ -89.485 -151.746 -125.606]
     [ -82.625 -140.225 -116.118]
     [ -85.096 -137.862 -114.842]
     [ -72.521 -101.178  -85.978]]
    Onnx-Add(Re_reduced0, Ge_Y0) -> Ad_C01    (name='Ad_Add')
    +kr='Ad_C01': (5, 3) (dtype=float32 min=-89.47606658935547 max=-27.215499877929688)
    [[-38.442 -70.245 -53.256]
     [-27.215 -89.476 -63.336]
     [-29.395 -86.995 -62.888]
     [-32.597 -85.362 -62.342]
     [-38.431 -67.088 -51.888]]
    Onnx-Add(Ad_Addcst, Ad_C01) -> Ad_C0    (name='Ad_Add1')
    +kr='Ad_C0': (5, 3) (dtype=float32 min=0.0670013427734375 max=25.74347686767578)
    [[ 0.213 22.587 10.147]
     [11.439  3.356  0.067]
     [ 9.259  5.836  0.515]
     [ 6.058  7.469  1.061]
     [ 0.223 25.743 11.515]]
    Onnx-Sqrt(Ad_C0) -> scores    (name='Sq_Sqrt')
    +kr='scores': (5, 3) (dtype=float32 min=0.2588461637496948 max=5.073802947998047)
    [[0.461 4.753 3.185]
     [3.382 1.832 0.259]
     [3.043 2.416 0.717]
     [2.461 2.733 1.03 ]
     [0.473 5.074 3.393]]
    Onnx-ArgMin(Ad_C0) -> label    (name='Ar_ArgMin')
    +kr='label': (5,) (dtype=int64 min=0 max=2)
    [0 2 2 2 0]
    {'label': array([0, 2, 2, 2, 0]), 'scores': array([[0.461, 4.753, 3.185],
           [3.382, 1.832, 0.259],
           [3.043, 2.416, 0.717],
           [2.461, 2.733, 1.03 ],
           [0.473, 5.074, 3.393]], dtype=float32)}

Other runtimes with OnnxInference#

OnnxInference can also call onnxruntime to compute the predictions by using runtime='onnxruntime1'.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='onnxruntime1')
print(oinf.run({'X': X_test[:5]}))

>>>

    {'label': array([1, 1, 1, 0, 0], dtype=int64), 'scores': array([[3.058, 0.531, 2.286],
           [3.616, 0.26 , 1.582],
           [4.191, 0.774, 1.224],
           [0.5  , 3.546, 5.236],
           [1.301, 3.766, 5.51 ]], dtype=float32)}

Intermediate cannot be seen but the class may decompose the ONNX graph into smaller graphs, one per operator, to look into intermediate results.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='onnxruntime2')
print(oinf.run({'X': X_test[:5]}, verbose=1, fLOG=print))

>>>

    +ki='Ad_Addcst': (3,) (dtype=float32 min=39.569862365722656 max=94.56828308105469)
    +ki='Ge_Gemmcst': (3, 4) (dtype=float32 min=0.2558826208114624 max=6.900000095367432)
    +ki='Mu_Mulcst': (1,) (dtype=float32 min=0.0 max=0.0)
    -- OnnxInference: run 7 nodes
    Onnx-ReduceSumSquare(X) -> Re_reduced0    (name='Re_ReduceSumSquare')
    +kr='Re_reduced0': (5, 1) (dtype=float32 min=46.1099967956543 max=69.32999420166016)
    Onnx-Mul(Re_reduced0, Mu_Mulcst) -> Mu_C0    (name='Mu_Mul')
    +kr='Mu_C0': (5, 1) (dtype=float32 min=0.0 max=0.0)
    Onnx-Gemm(X, Ge_Gemmcst, Mu_C0) -> Ge_Y0    (name='Ge_Gemm')
    +kr='Ge_Y0': (5, 3) (dtype=float32 min=-161.4214324951172 max=-85.22058868408203)
    Onnx-Add(Re_reduced0, Ge_Y0) -> Ad_C01    (name='Ad_Add')
    +kr='Ad_C01': (5, 3) (dtype=float32 min=-92.2057113647461 max=-22.64588165283203)
    Onnx-Add(Ad_Addcst, Ad_C01) -> Ad_C0    (name='Ad_Add1')
    +kr='Ad_C0': (5, 3) (dtype=float32 min=0.13265228271484375 max=26.228988647460938)
    Onnx-ArgMin(Ad_C0) -> label    (name='Ar_ArgMin')
    +kr='label': (5,) (dtype=int64 min=0 max=1)
    Onnx-Sqrt(Ad_C0) -> scores    (name='Sq_Sqrt')
    +kr='scores': (5, 3) (dtype=float32 min=0.36421459913253784 max=5.121424674987793)
    {'label': array([1, 1, 0, 1, 1], dtype=int64), 'scores': array([[3.363, 0.44 , 1.913],
           [2.929, 0.516, 2.261],
           [0.678, 3.52 , 5.121],
           [4.114, 0.919, 1.537],
           [3.535, 0.364, 1.574]], dtype=float32)}

Finally, a last runtime ‘python_compiled’ converts some part of the class OnnxInference into python code then dynamically compiled. As a consequence, interdiate results cannot be seen anymore.

<<<

import numpy
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from skl2onnx import to_onnx
from mlprodict.onnxrt import OnnxInference

iris = load_iris()
X = iris.data.astype(numpy.float32)
X_train, X_test = train_test_split(X)
clr = KMeans(n_clusters=3)
clr.fit(X_train)

model_def = to_onnx(clr, X_train.astype(numpy.float32),
                    target_opset=12)

oinf = OnnxInference(model_def, runtime='python_compiled')
print(oinf.run({'X': X_test[:5]}))

>>>

    {'label': array([1, 1, 0, 0, 1]), 'scores': array([[3.345, 0.578, 2.043],
           [2.95 , 0.5  , 2.296],
           [1.086, 3.648, 5.148],
           [0.396, 3.319, 4.968],
           [3.488, 0.208, 1.66 ]], dtype=float32)}