Skip to content
This repository was archived by the owner on Jan 13, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions _doc/sphinxdoc/source/api/npy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
Numpy revisited with ONNX
=========================

.. contents::
:local:

Introduction
++++++++++++

Converting custom code into :epkg:`ONNX` is not necessarily easy.
One big obstacle is :epkg:`ONNX` does not represent all numpy functions
with a single operator. One possible option is to provide a
Expand All @@ -22,7 +28,7 @@ is called.
import numpy
from typing import Any
from mlprodict.npy import onnxnumpy_default, NDArray
import mlprodict.npy.numpy_impl as nxnp
import mlprodict.npy.numpy_onnx_impl as nxnp

@onnxnumpy_default
def custom_fct(x: NDArray[Any, numpy.float32],
Expand All @@ -40,13 +46,10 @@ as opposed to numpy. This approach is similar to what
:epkg:`tensorflow` with `autograph
<https://www.tensorflow.org/api_docs/python/tf/autograph>`_.

.. contents::
:local:

NDArray
+++++++

.. autosignature:: mlprodict.npy.onnx_numpy_compiler.NDArray
.. autosignature:: mlprodict.npy.onnx_numpy_annotation.NDArray
:members:

onnxnumpy
Expand All @@ -68,9 +71,20 @@ OnnxVar
.. autosignature:: mlprodict.npy.onnx_variable.OnnxVar
:members:

Available numpy functions
+++++++++++++++++++++++++
Available numpy functions implemented with ONNX operators
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.. autosignature:: mlprodict.npy.numpy_onnx_impl.abs

.. autosignature:: mlprodict.npy.numpy_onnx_impl.log

.. autosignature:: mlprodict.npy.numpy_onnx_impl.sum

ONNX functions executed python ONNX runtime
+++++++++++++++++++++++++++++++++++++++++++

.. autosignature:: mlprodict.npy.numpy_onnx_pyrt.abs

.. autosignature:: mlprodict.npy.numpy_impl.abs
.. autosignature:: mlprodict.npy.numpy_onnx_pyrt.log

.. autosignature:: mlprodict.npy.numpy_impl.sum
.. autosignature:: mlprodict.npy.numpy_onnx_pyrt.sum
25 changes: 23 additions & 2 deletions _unittests/ut_npy/test_function_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
from logging import getLogger
from typing import Any
import numpy
from sklearn.preprocessing import FunctionTransformer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer, StandardScaler
from pyquickhelper.pycode import ExtTestCase, ignore_warnings
from mlprodict.onnx_conv import register_rewritten_operators, to_onnx
from mlprodict.onnxrt import OnnxInference
from mlprodict.npy import onnxnumpy_default
import mlprodict.npy.numpy_impl as nxnp
import mlprodict.npy.numpy_onnx_impl as nxnp
import mlprodict.npy.numpy_onnx_pyrt as nxnpy
from mlprodict.npy import NDArray


Expand Down Expand Up @@ -50,6 +52,25 @@ def test_function_transformer(self):
y_onx = oinf.run({'X': x})
self.assertEqualArray(y_exp, y_onx['variable'])

@ignore_warnings(DeprecationWarning)
def test_function_transformer_numpy_log(self):
x = numpy.array([[6.1, -5], [3.5, -7.8]], dtype=numpy.float32)
tr = make_pipeline(FunctionTransformer(numpy.log), StandardScaler())
tr.fit(x)
self.assertRaise(lambda: to_onnx(tr, x), TypeError)

@ignore_warnings(DeprecationWarning)
def test_function_transformer_nxnp_log(self):
x = numpy.array([[6.1, 5], [3.5, 7.8]], dtype=numpy.float32)
self.assertIsInstance(nxnpy.log(x), numpy.ndarray)
tr = make_pipeline(FunctionTransformer(nxnpy.log), StandardScaler())
tr.fit(x)
y_exp = tr.transform(x)
onnx_model = to_onnx(tr, x)
oinf = OnnxInference(onnx_model)
y_onx = oinf.run({'X': x})
self.assertEqualArray(y_exp, y_onx['variable'], decimal=5)


if __name__ == "__main__":
unittest.main()
45 changes: 45 additions & 0 deletions _unittests/ut_npy/test_numpy_onnx_pyrt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""
@brief test log(time=3s)
"""
import unittest
import numpy
from pyquickhelper.pycode import ExtTestCase
import mlprodict.npy.numpy_onnx_pyrt as nxnpy


class TestNumpyOnnxFunction(ExtTestCase):

def common_test1(self, x, npfct, nxfct, dtype, **kwargs):
xt = x.astype(dtype)
if kwargs is None or len(kwargs) == 0:
expected = npfct(xt)
got = nxfct[dtype](xt)
else:
expected = npfct(xt, **kwargs)
kwargs['dtype_onnx'] = dtype
got = nxfct[kwargs](xt)
self.assertEqualArray(expected, got)

def test_abs_float32(self):
x = numpy.array([[-6.1, 5], [-3.5, 7.8]], dtype=numpy.float32)
self.common_test1(x, numpy.abs, nxnpy.abs, numpy.float32)

def test_log_float32(self):
x = numpy.array([[6.1, 5], [3.5, 7.8]], dtype=numpy.float32)
self.common_test1(x, numpy.log, nxnpy.log, numpy.float32)

def test_log_float64(self):
x = numpy.array([[6.1, 5], [3.5, 7.8]], dtype=numpy.float64)
self.common_test1(x, numpy.log, nxnpy.log, numpy.float64)

def test_sum_float32(self):
kwargs = [{'axis': 0}, {}, {'axis': 1}]
for kw in kwargs:
with self.subTest(kw=kw):
x = numpy.array([[-6.1, 5], [-3.5, 7.8]], dtype=numpy.float32)
self.common_test1(x, numpy.sum, nxnpy.sum, numpy.float32, **kw)


if __name__ == "__main__":
unittest.main()
30 changes: 27 additions & 3 deletions _unittests/ut_npy/test_onnx_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from typing import Any
import numpy
from pyquickhelper.pycode import ExtTestCase, ignore_warnings
from mlprodict.npy import onnxnumpy, onnxnumpy_default
import mlprodict.npy.numpy_impl as nxnp
from mlprodict.npy import OnnxNumpyCompiler as ONC, NDArray
from mlprodict.npy import onnxnumpy, onnxnumpy_default, onnxnumpy_np
import mlprodict.npy.numpy_onnx_impl as nxnp
from mlprodict.npy import (
OnnxNumpyCompiler as ONC, NDArray, NDArraySameTypeSameShape)


@ignore_warnings(DeprecationWarning)
Expand Down Expand Up @@ -260,6 +261,19 @@ def test_abs_set3(x: NDArray[Any, numpy.float32],
return temp


@onnxnumpy_default
def test_log(x: NDArray[Any, numpy.float32],
) -> NDArray[Any, numpy.float32]:
"onnx numpy log"
return nxnp.log(x)


@onnxnumpy_np(signature=NDArraySameTypeSameShape("floats"))
def test_abs_log_multi(x):
"onnx numpy log multiple type"
return nxnp.log(nxnp.abs(x))


class TestOnnxVariable(ExtTestCase):

def test_onnx_variable_abs(self):
Expand Down Expand Up @@ -446,6 +460,16 @@ def test_onnx_variable_abs_set3(self):
temp[:, 0] = -1.5
self.assertEqualArray(y, temp)

def test_onnx_variable_log(self):
x = numpy.array([[6.1, 5], [3.5, 7.8]], dtype=numpy.float32)
y = test_log(x)
self.assertEqualArray(y, numpy.log(x))

def test_onnx_variable_abs_log_multi(self):
x = numpy.array([[6.1, -5], [-3.5, 7.8]], dtype=numpy.float32)
y = test_abs_log_multi(x)
self.assertEqualArray(y, numpy.log(numpy.abs(x)))


if __name__ == "__main__":
unittest.main()
4 changes: 2 additions & 2 deletions _unittests/ut_npy/test_onnxpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def onnx_abs(x: NDArray[Any, numpy.float32],

def test_annotation(self):
cl = ONC(TestOnnxPy.onnx_abs, op_version=12)
ann = cl._parse_annotation() # pylint: disable=W0212
inputs, outputs = ann
ann = cl._parse_annotation(None, None) # pylint: disable=W0212
inputs, outputs, _ = ann
self.assertIsInstance(inputs, list)
self.assertIsInstance(outputs, list)
self.assertEqual(len(inputs), 1)
Expand Down
6 changes: 4 additions & 2 deletions mlprodict/npy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@

.. versionadded:: 0.6
"""
from .onnx_numpy_compiler import OnnxNumpyCompiler, NDArray
from .onnx_numpy_wrapper import onnxnumpy, onnxnumpy_default
from .onnx_numpy_annotation import (
NDArray, NDArraySameType, NDArraySameTypeSameShape)
from .onnx_numpy_compiler import OnnxNumpyCompiler
from .onnx_numpy_wrapper import onnxnumpy, onnxnumpy_default, onnxnumpy_np
13 changes: 11 additions & 2 deletions mlprodict/npy/numpy_impl.py → mlprodict/npy/numpy_onnx_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"""
import numpy
from skl2onnx.algebra.onnx_ops import ( # pylint: disable=E0611
OnnxAbs, OnnxReduceSum)
OnnxAbs,
OnnxLog,
OnnxReduceSum)
from .onnx_variable import OnnxVar


Expand All @@ -15,7 +17,14 @@ def abs(x):
return OnnxVar(x, op=OnnxAbs)


def sum(x, axis=0, keepdims=0):
def log(x):
"See :epkg:`numpy:log`."
return OnnxVar(x, op=OnnxLog)


def sum(x, axis=None, keepdims=0):
"See :epkg:`numpy:sum`."
if axis is None:
return OnnxVar(x, op=OnnxReduceSum, keepdims=keepdims)
return OnnxVar(x, numpy.array([axis], dtype=numpy.int64),
op=OnnxReduceSum, keepdims=keepdims)
33 changes: 33 additions & 0 deletions mlprodict/npy/numpy_onnx_pyrt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
@file
@brief :epkg:`numpy` functions implemented with :epkg:`onnx`
and compiled with this python runtime.

.. versionadded:: 0.6
"""
from .onnx_numpy_annotation import (
NDArraySameType,
NDArraySameTypeSameShape)
from .numpy_onnx_impl import (
abs as nx_abs,
log as nx_log,
sum as nx_sum)
from .onnx_numpy_wrapper import onnxnumpy_np


@onnxnumpy_np(signature=NDArraySameTypeSameShape("all"))
def abs(x):
"abs"
return nx_abs(x)


@onnxnumpy_np(signature=NDArraySameTypeSameShape("floats"))
def log(x):
"log"
return nx_log(x)


@onnxnumpy_np(signature=NDArraySameType("all"))
def sum(x, axis=None, keepdims=0):
"sum"
return nx_sum(x, axis=axis, keepdims=keepdims)
Loading