FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Supports operator Scan when exporting an onnx graph to onnx code by xadupre · Pull Request #337 · sdpython/mlprodict · GitHub

This repository was archived by the owner on Jan 13, 2024. It is now read-only.

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .onnx  (1) .py  (2) .tmpl  (1) All 3 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
Binary file not shown.
20 changes: 17 additions & 3 deletions _unittests/ut_tools/test_export_onnx.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1331,9 +1331,23 @@ def onnx_rfft_2d_any_test(x, fft_length):
def test_sub_graph(self):
data = os.path.abspath(os.path.dirname(__file__))
debug = os.path.join(data, "data", "debug.onnx")
self.assertRaise(lambda: export2onnx(debug), NotImplementedError)
# new_onnx = export2onnx(debug)
# _, loc = self.verify(new_onnx)
code = export2onnx(debug)
self.assertIn("def _create_Sc_Scan1_body():", code)

def test_scan_knn(self):
x = numpy.random.randn(3, 4).astype(numpy.float32)
data = os.path.abspath(os.path.dirname(__file__))
knn = os.path.join(
data, "data", "SklearnKNeighborsRegressor2.model.onnx")
onx = OnnxInference(knn)
y1 = onx.run({'input': x})['variable']
new_onnx = export2onnx(knn)
_, loc = self.verify(new_onnx)
model = loc['onnx_model']
oinf = OnnxInference(model)
y2 = oinf.run({'input': x})['variable']
self.assertEqual(y1, y2)
print(new_onnx)


if __name__ == "__main__":
Expand Down
25 changes: 22 additions & 3 deletions mlprodict/onnx_tools/_onnx_export_templates_onnx.tmpl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ from onnx.helper import (
make_tensor_value_info)


def create_model():
def {{ function_name }}():
'''
Converted ``{{ name }}``.

Expand All @@ -16,17 +16,30 @@ def create_model():
* {{ key }}: {{ val }}
{%- endfor %}
'''
# subgraphs
{%- for code, name in subgraphs: -%}
{{ indent(code, ' ') }}
{%- endfor %}

# containers
print('[containers]') # verbose
initializers = []
nodes = []
inputs = []
outputs = []

{% if ir_version %}
# opsets
print('[opsets]') # verbose
opsets = {{ opsets }}
target_opset = {{ target_opset }}
{%- endif -%}

# subgraphs
print('[subgraphs]') # verbose
{%- for code, name in subgraphs: -%}
{{ name }} = subgraph_{{ name }}
{%- endfor %}

# initializers
print('[initializers]') # verbose
Expand Down Expand Up @@ -73,6 +86,10 @@ def create_model():
# graph
print('[graph]') # verbose
graph = make_graph(nodes, '{{ name }}', inputs, outputs, initializers)
# '{{ ir_version }}'
{% if not ir_version %}
return graph
{% else %}
onnx_model = make_model(graph)
onnx_model.ir_version = {{ ir_version }}
onnx_model.producer_name = '{{ producer_name }}'
Expand All @@ -91,6 +108,8 @@ def create_model():
op_set.version = value

return onnx_model
{% endif %}


onnx_model = create_model()
{% if ir_version %}
onnx_model = {{ function_name }}()
{% endif %}
95 changes: 67 additions & 28 deletions mlprodict/onnx_tools/onnx_export.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

.. versionadded:: 0.7
"""
from textwrap import indent
import numpy
import onnx
from onnx import numpy_helper
Expand All @@ -18,7 +19,7 @@

def export_template(model_onnx, templates, opset=None, verbose=True, name=None,
rename=False, use_onnx_tensor=False,
autopep_options=None):
autopep_options=None, function_name='create_model'):
"""
Exports an ONNX model to the onnx syntax.

Expand All @@ -33,6 +34,7 @@ def export_template(model_onnx, templates, opset=None, verbose=True, name=None,
and its name is `'value'`, it converts that array into an
ONNX tensor to avoid type mismatch, (operator *ConstantOfShape*, ...)
:param autopep_options: :epkg:`autopep8` options
:param function_name: main function name in the code
:return: python code
"""
# delayed import to avoid raising an exception if not installed.
Expand Down Expand Up @@ -72,26 +74,31 @@ def rename_name(name):
used = {}

# opset
opsets = {}
for oimp in model_onnx.opset_import:
if oimp.domain == '' and opset is None:
opsets[oimp.domain] = oimp.version
opset = oimp.version
else:
opsets[oimp.domain] = opset
context['opsets'] = opsets
context['target_opset'] = opset

if hasattr(model_onnx, 'opset_import'):
opsets = {}
for oimp in model_onnx.opset_import:
if oimp.domain == '' and opset is None:
opsets[oimp.domain] = oimp.version
opset = oimp.version
else:
opsets[oimp.domain] = opset
context['opsets'] = opsets
context['target_opset'] = opset

if hasattr(model_onnx, 'graph'):
graph = model_onnx.graph
else:
graph = model_onnx
dict_names = {}
if rename:
for o in model_onnx.graph.input:
for o in graph.input:
dict_names[o.name] = o.name
for o in model_onnx.graph.output:
for o in graph.output:
dict_names[o.name] = o.name

# inits
initializers = []
for init in model_onnx.graph.initializer:
for init in graph.initializer:
init_name = rename_name(init.name)
value = numpy_helper.to_array(init)
initializers.append((init_name, value))
Expand All @@ -100,7 +107,7 @@ def rename_name(name):

# inputs
inputs = []
for inp in model_onnx.graph.input:
for inp in graph.input:
t = inp.type.tensor_type
dims = []
for d in t.shape.dim:
Expand All @@ -118,17 +125,23 @@ def rename_name(name):

# outputs
outputs = []
for inp in model_onnx.graph.output:
for inp in graph.output:
t = inp.type.tensor_type
dims = tuple(t.shape.dim)
dims = []
for d in t.shape.dim:
dd = d.dim_value
if dd == 0:
dd = None
dims.append(dd)
if len(dims) == 0:
dims = None
outputs.append((inp.name, t.elem_type, dims))
context['outputs'] = outputs

# node
subgraphs = []
nodes = []
for node in model_onnx.graph.node:
for node in graph.node:
for i_raw_name in node.input:
i = rename_name(i_raw_name)
if i not in used:
Expand All @@ -138,9 +151,19 @@ def rename_name(name):
for at in node.attribute:
temp = _var_as_dict(at)
value = temp['value']
if node.op_type in {'Scan', 'Loop', 'If'}:
if node.op_type == 'Scan' and at.name == 'body':
fname = "_create_" + node.name + "_body"
body = export_template(
value, templates, opset=opset, verbose=verbose,
name=name, rename=rename, use_onnx_tensor=use_onnx_tensor,
autopep_options=autopep_options,
function_name=fname)
subgraphs.append((body, node.name + "_body"))
attributes.append((at.name, fname + "()"))
continue
if node.op_type in {'Loop', 'If'}:
raise NotImplementedError(
"Subgraph are not yet implemented (operator=%r)."
"Subgraphs are not yet implemented (operator=%r)."
"" % node.op_type)
if use_onnx_tensor:
if node.op_type == 'Cast' and at.name == 'to':
Expand Down Expand Up @@ -174,15 +197,31 @@ def rename_name(name):
context['nodes'] = nodes

# graph
context['name'] = name or model_onnx.graph.name
context['name'] = name or graph.name
context['name'] = context['name'].replace("(", "_").replace(")", "")
context['ir_version'] = model_onnx.ir_version
context['producer_name'] = model_onnx.producer_name
context['domain'] = model_onnx.domain
context['model_version'] = model_onnx.model_version
context['doc_string'] = model_onnx.doc_string
context['metadata'] = {p.key: p.value for p in model_onnx.metadata_props}
context['skip_inits'] = {}
context['function_name'] = function_name
context['indent'] = indent
if hasattr(model_onnx, 'graph'):
context['ir_version'] = model_onnx.ir_version
context['producer_name'] = model_onnx.producer_name
context['domain'] = model_onnx.domain
context['model_version'] = model_onnx.model_version
context['doc_string'] = model_onnx.doc_string
context['metadata'] = {
p.key: p.value for p in model_onnx.metadata_props}
context['skip_inits'] = {}
context['subgraphs'] = subgraphs
else:
# subgraph
context['ir_version'] = None
context['producer_name'] = None
context['domain'] = None
context['model_version'] = None
context['doc_string'] = ""
context['metadata'] = {}
context['skip_inits'] = {}
context['subgraphs'] = subgraphs

mark_inits = {}

# First rendering to detect any unused or replaced initializer.
Expand Down

Back | FazBrowse Home | New Git URL