.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gyexamples/plot_lcustom_options.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note Click :ref:`here ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_gyexamples_plot_lcustom_options.py: .. _l-plot-custom-options: A new converter with options ============================ .. index:: options Options are used to implement different conversion for a same model. The options can be used to replace an operator *MatMul* by the *Gemm* operator and compare the processing time for both graph. Let's see how to retrieve the options within a converter. Example :ref:`l-plot-custom-converter` implements a converter which uses operator *MatMul*. Option *use_gemm* is used to replace *MatMul* by *Gemm*. .. contents:: :local: Custom model ++++++++++++ .. GENERATED FROM PYTHON SOURCE LINES 26-88 .. code-block:: default from mlprodict.onnxrt import OnnxInference from pyquickhelper.helpgen.graphviz_helper import plot_graphviz from pandas import DataFrame from onnxcustom.utils import measure_time import numpy from onnxruntime import InferenceSession from sklearn.base import TransformerMixin, BaseEstimator from sklearn.datasets import load_iris from skl2onnx import update_registered_converter from skl2onnx.common.data_types import guess_numpy_type from skl2onnx.algebra.onnx_ops import ( OnnxSub, OnnxMatMul, OnnxGemm) from skl2onnx import to_onnx class DecorrelateTransformer(TransformerMixin, BaseEstimator): """ Decorrelates correlated gaussian features. :param alpha: avoids non inversible matrices by adding *alpha* identity matrix *Attributes* * `self.mean_`: average * `self.coef_`: square root of the coveriance matrix """ def __init__(self, alpha=0.): BaseEstimator.__init__(self) TransformerMixin.__init__(self) self.alpha = alpha def fit(self, X, y=None, sample_weights=None): if sample_weights is not None: raise NotImplementedError( "sample_weights != None is not implemented.") self.mean_ = numpy.mean(X, axis=0, keepdims=True) X = X - self.mean_ V = X.T @ X / X.shape[0] if self.alpha != 0: V += numpy.identity(V.shape[0]) * self.alpha L, P = numpy.linalg.eig(V) Linv = L ** (-0.5) diag = numpy.diag(Linv) root = P @ diag @ P.transpose() self.coef_ = root return self def transform(self, X): return (X - self.mean_) @ self.coef_ data = load_iris() X = data.data dec = DecorrelateTransformer() dec.fit(X) pred = dec.transform(X[:5]) print(pred) .. rst-class:: sphx-glr-script-out .. code-block:: none [[ 0.0167562 0.52111756 -1.24946737 -0.56194325] [-0.0727878 -0.80853732 -1.43841018 -0.37441392] [-0.69971891 -0.09950908 -1.2138161 -0.3499275 ] [-1.13063404 -0.13540568 -0.79087008 -0.73938966] [-0.35790036 0.91900236 -1.04034399 -0.6509266 ]] .. GENERATED FROM PYTHON SOURCE LINES 89-93 Conversion into ONNX ++++++++++++++++++++ Let's try to convert it and see what happens. .. GENERATED FROM PYTHON SOURCE LINES 93-128 .. code-block:: default def decorrelate_transformer_shape_calculator(operator): op = operator.raw_operator input_type = operator.inputs[0].type.__class__ input_dim = operator.inputs[0].type.shape[0] output_type = input_type([input_dim, op.coef_.shape[1]]) operator.outputs[0].type = output_type def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs X = operator.inputs[0] dtype = guess_numpy_type(X.type) options = container.get_options(op, dict(use_gemm=False)) use_gemm = options['use_gemm'] print('conversion: use_gemm=', use_gemm) if use_gemm: Y = OnnxGemm(X, op.coef_.astype(dtype), (- op.mean_ @ op.coef_).astype(dtype), op_version=opv, alpha=1., beta=1., output_names=out[:1]) else: Y = OnnxMatMul( OnnxSub(X, op.mean_.astype(dtype), op_version=opv), op.coef_.astype(dtype), op_version=opv, output_names=out[:1]) Y.add_to(scope, container) .. GENERATED FROM PYTHON SOURCE LINES 129-131 The registration needs to declare the options supported by the converted. .. GENERATED FROM PYTHON SOURCE LINES 131-158 .. code-block:: default update_registered_converter( DecorrelateTransformer, "SklearnDecorrelateTransformer", decorrelate_transformer_shape_calculator, decorrelate_transformer_converter, options={'use_gemm': [True, False]}) onx = to_onnx(dec, X.astype(numpy.float32)) sess = InferenceSession(onx.SerializeToString(), providers=['CPUExecutionProvider']) exp = dec.transform(X.astype(numpy.float32)) got = sess.run(None, {'X': X.astype(numpy.float32)})[0] def diff(p1, p2): p1 = p1.ravel() p2 = p2.ravel() d = numpy.abs(p2 - p1) return d.max(), (d / numpy.abs(p1)).max() print(diff(exp, got)) .. rst-class:: sphx-glr-script-out .. code-block:: none conversion: use_gemm= False (1.0531815934911037e-06, 0.0003497831114202993) .. GENERATED FROM PYTHON SOURCE LINES 159-160 We try the non default option, `use_gemm: True`. .. GENERATED FROM PYTHON SOURCE LINES 160-172 .. code-block:: default onx2 = to_onnx(dec, X.astype(numpy.float32), options={'use_gemm': True}) sess2 = InferenceSession(onx2.SerializeToString(), providers=['CPUExecutionProvider']) exp = dec.transform(X.astype(numpy.float32)) got2 = sess2.run(None, {'X': X.astype(numpy.float32)})[0] print(diff(exp, got2)) .. rst-class:: sphx-glr-script-out .. code-block:: none conversion: use_gemm= True (2.0175704165126263e-06, 0.0005483764974245708) .. GENERATED FROM PYTHON SOURCE LINES 173-174 Visually. .. GENERATED FROM PYTHON SOURCE LINES 174-182 .. code-block:: default oinf = OnnxInference(onx2) ax = plot_graphviz(oinf.to_dot()) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) .. image-sg:: /gyexamples/images/sphx_glr_plot_lcustom_options_001.png :alt: plot lcustom options :srcset: /gyexamples/images/sphx_glr_plot_lcustom_options_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 183-187 Time comparison +++++++++++++++ Let's compare the two computation. .. GENERATED FROM PYTHON SOURCE LINES 187-207 .. code-block:: default X32 = X.astype(numpy.float32) obs = [] context = {'sess': sess, 'X32': X32} mt = measure_time( "sess.run(None, {'X': X32})", context, div_by_number=True, number=100, repeat=1000) mt['use_gemm'] = False obs.append(mt) context = {'sess2': sess2, 'X32': X32} mt2 = measure_time( "sess2.run(None, {'X': X32})", context, div_by_number=True, number=10, repeat=100) mt2['use_gemm'] = True obs.append(mt2) DataFrame(obs).T .. raw:: html
0 1
average 0.000063 0.000049
deviation 0.0 0.000001
min_exec 0.000063 0.000049
max_exec 0.000067 0.000058
repeat 1000 100
number 100 10
use_gemm False True


.. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 7.149 seconds) .. _sphx_glr_download_gyexamples_plot_lcustom_options.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_lcustom_options.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_lcustom_options.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_