ONE - On-device Neural Engine
Loading...
Searching...
No Matches
validate_onnx2circle.py
Go to the documentation of this file.
1#!/usr/bin/env bash
2''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
3''''export PY_PATH=${SCRIPT_PATH}/../bin/venv/bin/python # '''
4''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
5''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
6''''exit 255 # '''
7
8# Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved
9#
10# Licensed under the Apache License, Version 2.0 (the "License");
11# you may not use this file except in compliance with the License.
12# You may obtain a copy of the License at
13#
14# http://www.apache.org/licenses/LICENSE-2.0
15#
16# Unless required by applicable law or agreed to in writing, software
17# distributed under the License is distributed on an "AS IS" BASIS,
18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19# See the License for the specific language governing permissions and
20# limitations under the License.
21
22# NOTE This is an experimental script to evaluate onnx-circle conversion
23# by running onnxruntime and luci-interpreter.
24# Plan is to run this regularly in CI
25
26import subprocess
27import argparse
28import numpy as np
29import torch
30import onnx
31import onnxruntime as ort
32
33parser = argparse.ArgumentParser()
34parser.add_argument('--driver', type=str, required=True)
35parser.add_argument('--onnx', type=str, required=True)
36parser.add_argument('--circle', type=str, required=True)
37args = parser.parse_args()
38
39driver = args.driver
40onnx_filepath = args.onnx
41circle_filepath = args.circle
42
43
44def to_numpy(tensor):
45 return tensor.cpu().numpy()
46
47
48def to_nhwc(tensor):
49 if (tensor.ndim == 4):
50 return np.transpose(tensor, (0, 2, 3, 1))
51 return tensor
52
53
55 def __init__(self, filepath):
56 self.filepath = filepath
57 self.session = None
58 self.inputs = None
59 self.inputs_size = None
60 self.inputs_data = None
61 self.outputs = None
62 self.outputs_size = None
63
64 def load(self):
65 model = onnx.load(self.filepath)
66 onnx.checker.check_model(model)
67 options = ort.SessionOptions()
68 # NOTE this is needed for U18.04
69 # referenced: https://github.com/microsoft/onnxruntime/issues/10113
70 options.intra_op_num_threads = 4
71 # NOTE set `providers` for https://github.com/microsoft/onnxruntime/issues/17631
72 providers = ort.get_available_providers()
73 self.session = ort.InferenceSession(self.filepath,
74 sess_options=options,
75 providers=providers)
76
78 self.inputs = self.session.get_inputs()
79 self.inputs_size = len(self.inputs)
80 # reset input dictionary
81 self.inputs_data = {}
82 for in_idx in range(self.inputs_size):
83 input_shape = self.inputs[in_idx].shape
84 input_type = self.inputs[in_idx].type
85 if input_type == 'tensor(float)':
86 torch_type = torch.float32
87 else:
88 # TODO support other dtype
89 raise SystemExit("Unsupported input dtype")
90
91 x = torch.randn(input_shape, dtype=torch_type)
92 input_npa = to_numpy(x)
93 self.inputs_data.update({self.inputs[in_idx].name: input_npa})
94
95 # save NHWC form of input for luci-interpreter
96 input_npa_nhwc = to_nhwc(input_npa)
97 input_npa_nhwc.tofile(circle_filepath + ".input" + str(in_idx))
98
99 def run(self):
100 self.outs = self.session.run(None, self.inputs_data)
101
102 def get_outputs(self):
103 self.outputs = self.session.get_outputs()
104 self.outputs_size = len(self.outputs)
105
106
107# Run ONNX model
108print("Run ONNX...")
109onnx_runner = OnnxRunner(onnx_filepath)
110onnx_runner.load()
111onnx_runner.feed_random_inputs()
112onnx_runner.run()
113onnx_runner.get_outputs()
114
115# Execute luci interpreter
116print("Run luci-interpreter...")
117process = subprocess.run([
118 driver, circle_filepath,
119 str(onnx_runner.inputs_size), circle_filepath + ".input", circle_filepath + ".output"
120],
121 check=True)
122
123# Compare results
124rtolerance = 1e-03
125atolerance = 1e-04
126result_compare = True
127for idx in range(onnx_runner.outputs_size):
128 output_shape = onnx_runner.outputs[idx].shape
129 output_type = onnx_runner.outputs[idx].type
130 if output_type == 'tensor(float)':
131 output_np_type = np.float32
132 else:
133 # TODO support other dtype
134 raise SystemExit("Unsupported output dtype")
135
136 # output of luci-interpreter
137 output_data = np.fromfile(circle_filepath + ".output" + str(idx), output_np_type)
138 shape_file = open(circle_filepath + ".output" + str(idx) + ".shape", 'r')
139 output_shape = [int(i) for i in shape_file.read().split(',')]
140 luci_output_data = np.reshape(output_data, output_shape)
141
142 # output of onnx runtime
143 output_nchw = onnx_runner.outs[idx]
144 output_nhwc = to_nhwc(output_nchw)
145
146 # diff has tensor of boolean for each values within tolerance or not
147 diff = np.isclose(output_nhwc, luci_output_data, rtol=rtolerance, atol=atolerance)
148 # get one boolean if all are True then True
149 result_compare_one = np.all(diff)
150 print("Compare", idx, result_compare_one)
151 if (not result_compare_one):
152 diff_val = np.subtract(output_nhwc, luci_output_data)
153 print("ONNX Result", output_nhwc)
154 print("Diff", diff_val)
155 print("Diff Max", np.ndarray.max(diff_val))
156
157 result_compare = result_compare and result_compare_one
158
159if (not result_compare):
160 exit(-1)
161
162exit(0)