.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gyexamples/plot_kcustom_converter_wrapper.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_kcustom_converter_wrapper.py: .. _l-plot-custom-converter-wrapper: Implement a new converter using other converters ================================================ .. index:: custom converter In many cases, a custom models leverages existing models which already have an associated converter. To convert this patchwork, existing converters must be called. This example shows how to do that. Example :ref:`l-plot-custom-converter` can be rewritten by using a `PCA `_. We could then reuse the converter associated to this model. .. contents:: :local: Custom model ++++++++++++ Let's implement a simple custom model using :epkg:`scikit-learn` API. The model is preprocessing which decorrelates correlated random variables. If *X* is a matrix of features, :math:`V=\frac{1}{n}X'X` is the covariance matrix. We compute :math:`X V^{1/2}`. .. GENERATED FROM PYTHON SOURCE LINES 29-100 .. code-block:: default from mlprodict.onnxrt import OnnxInference from pyquickhelper.helpgen.graphviz_helper import plot_graphviz import pickle from io import BytesIO import numpy from numpy.testing import assert_almost_equal from onnxruntime import InferenceSession from sklearn.base import TransformerMixin, BaseEstimator from sklearn.datasets import load_iris from sklearn.decomposition import PCA from skl2onnx import update_registered_converter from skl2onnx.algebra.onnx_operator import OnnxSubEstimator 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): self.pca_ = PCA(X.shape[1]) self.pca_.fit(X) return self def transform(self, X): return self.pca_.transform(X) def test_decorrelate_transformer(): data = load_iris() X = data.data dec = DecorrelateTransformer() dec.fit(X) pred = dec.transform(X) cov = pred.T @ pred for i in range(cov.shape[0]): cov[i, i] = 1. assert_almost_equal(numpy.identity(4), cov) st = BytesIO() pickle.dump(dec, st) dec2 = pickle.load(BytesIO(st.getvalue())) assert_almost_equal(dec.transform(X), dec2.transform(X)) test_decorrelate_transformer() 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 [[-2.68412563e+00 3.19397247e-01 -2.79148276e-02 -2.26243707e-03] [-2.71414169e+00 -1.77001225e-01 -2.10464272e-01 -9.90265503e-02] [-2.88899057e+00 -1.44949426e-01 1.79002563e-02 -1.99683897e-02] [-2.74534286e+00 -3.18298979e-01 3.15593736e-02 7.55758166e-02] [-2.72871654e+00 3.26754513e-01 9.00792406e-02 6.12585926e-02]] .. GENERATED FROM PYTHON SOURCE LINES 101-105 Conversion into ONNX ++++++++++++++++++++ Let's try to convert it and see what happens. .. GENERATED FROM PYTHON SOURCE LINES 105-112 .. code-block:: default try: to_onnx(dec, X.astype(numpy.float32)) except Exception as e: print(e) .. rst-class:: sphx-glr-script-out .. code-block:: none Unable to find a shape calculator for type ''. It usually means the pipeline being converted contains a transformer or a predictor with no corresponding converter implemented in sklearn-onnx. If the converted is implemented in another library, you need to register the converted so that it can be used by sklearn-onnx (function update_registered_converter). If the model is not yet covered by sklearn-onnx, you may raise an issue to https://github.com/onnx/sklearn-onnx/issues to get the converter implemented or even contribute to the project. If the model is a custom model, a new converter must be implemented. Examples can be found in the gallery. .. GENERATED FROM PYTHON SOURCE LINES 113-121 This error means there is no converter associated to *DecorrelateTransformer*. Let's do it. It requires to implement the two following functions, a shape calculator and a converter with the same signature as below. First the shape calculator. We retrieve the input type add tells the output type has the same type, the same number of rows and a specific number of columns. .. GENERATED FROM PYTHON SOURCE LINES 121-131 .. 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.pca_.components_.shape[1]]) operator.outputs[0].type = output_type .. GENERATED FROM PYTHON SOURCE LINES 132-136 The converter. One thing we need to pay attention to is the target opset. This information is important to make sure that every node is defined following the specifications of that opset. .. GENERATED FROM PYTHON SOURCE LINES 136-152 .. code-block:: default def decorrelate_transformer_converter(scope, operator, container): op = operator.raw_operator opv = container.target_opset out = operator.outputs # We retrieve the unique input. X = operator.inputs[0] # We tell in ONNX language how to compute the unique output. # op_version=opv tells which opset is requested Y = OnnxSubEstimator(op.pca_, X, op_version=opv, output_names=out[:1]) Y.add_to(scope, container) .. GENERATED FROM PYTHON SOURCE LINES 153-154 We need to let *skl2onnx* know about the new converter. .. GENERATED FROM PYTHON SOURCE LINES 154-180 .. code-block:: default update_registered_converter( DecorrelateTransformer, "SklearnDecorrelateTransformer", decorrelate_transformer_shape_calculator, decorrelate_transformer_converter) 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 (3.8700562665638927e-07, 0.0008087741270943213) .. GENERATED FROM PYTHON SOURCE LINES 181-182 Let's check it works as well with double. .. GENERATED FROM PYTHON SOURCE LINES 182-192 .. code-block:: default onx = to_onnx(dec, X.astype(numpy.float64)) sess = InferenceSession(onx.SerializeToString(), providers=['CPUExecutionProvider']) exp = dec.transform(X.astype(numpy.float64)) got = sess.run(None, {'X': X.astype(numpy.float64)})[0] print(diff(exp, got)) .. rst-class:: sphx-glr-script-out .. code-block:: none (2.220446049250313e-16, 1.168076932317175e-16) .. GENERATED FROM PYTHON SOURCE LINES 193-194 The differences are smaller with double as expected. .. GENERATED FROM PYTHON SOURCE LINES 197-199 Final graph +++++++++++ .. GENERATED FROM PYTHON SOURCE LINES 199-204 .. code-block:: default oinf = OnnxInference(onx) 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_kcustom_converter_wrapper_001.png :alt: plot kcustom converter wrapper :srcset: /gyexamples/images/sphx_glr_plot_kcustom_converter_wrapper_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** ( 0 minutes 1.145 seconds) .. _sphx_glr_download_gyexamples_plot_kcustom_converter_wrapper.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_kcustom_converter_wrapper.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_kcustom_converter_wrapper.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_