File size: 14,709 Bytes
dc2106c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# Copyright (c) ONNX Project Contributors

# SPDX-License-Identifier: Apache-2.0

from __future__ import annotations

import platform
import unittest
from typing import Any

import numpy
from packaging.version import Version

import onnx.backend.base
import onnx.backend.test
import onnx.shape_inference
import onnx.version_converter
from onnx.backend.base import Device, DeviceType

try:
    import onnxruntime as ort

    ort_version = Version(ort.__version__)
except ImportError:
    # onnxruntime is not installed, all tests are skipped.
    ort: Any = None  # type: ignore[no-redef]
    ort_version: Any = None  # type: ignore[no-redef]


# The following just executes a backend based on InferenceSession through the backend test


class InferenceSessionBackendRep(onnx.backend.base.BackendRep):
    def __init__(self, session):
        self._session = session

    def run(self, inputs, **kwargs):
        del kwargs  # Unused
        if isinstance(inputs, numpy.ndarray):
            inputs = [inputs]
        if isinstance(inputs, list):
            input_names = [i.name for i in self._session.get_inputs()]
            input_shapes = [i.shape for i in self._session.get_inputs()]
            if len(inputs) == len(input_names):
                feeds = dict(zip(input_names, inputs))
            else:
                feeds = {}
                pos_inputs = 0
                for inp, shape in zip(input_names, input_shapes):
                    if shape == inputs[pos_inputs].shape:
                        feeds[inp] = inputs[pos_inputs]
                        pos_inputs += 1
                        if pos_inputs >= len(inputs):
                            break
        elif isinstance(inputs, dict):
            feeds = inputs
        else:
            raise TypeError(f"Unexpected input type {type(inputs)!r}.")
        outs = self._session.run(None, feeds)
        return outs


def _create_inference_session(model: onnx.ModelProto, device: str):
    if device == "CPU":
        providers = ("CPUExecutionProvider",)
    elif device == "CUDA":
        providers = ("CUDAExecutionProvider",)
    else:
        raise ValueError(f"Unexpected device {device!r}.")
    try:
        session = ort.InferenceSession(model.SerializeToString(), providers=providers)
    except Exception as e:
        raise RuntimeError(
            f"Unable to create inference session. Model is:\n\n{onnx.printer.to_text(model)}"
        ) from e
    return session


class InferenceSessionBackend(onnx.backend.base.Backend):
    @classmethod
    def supports_device(cls, device: str) -> bool:
        providers = set(ort.get_available_providers())
        d = Device(device)
        if d.type == DeviceType.CPU and "CPUExecutionProvider" in providers:
            return True
        if d.type == DeviceType.CUDA and "CUDAExecutionProvider" in providers:
            return True
        return False

    @classmethod
    def prepare(

        cls, model: onnx.ModelProto, device: str = "CPU", **kwargs: Any

    ) -> InferenceSessionBackendRep:
        del kwargs  # Unused
        if not isinstance(model, (str, bytes, onnx.ModelProto)):
            raise TypeError(f"Unexpected type {type(model)} for model.")

        session = _create_inference_session(model, device)
        return InferenceSessionBackendRep(session)

    @classmethod
    def run_model(cls, model: onnx.ModelProto, inputs, device=None, **kwargs):
        return super().run_model(model, inputs, device=device, **kwargs)

    @classmethod
    def run_node(cls, node, inputs, device=None, outputs_info=None, **kwargs):
        raise NotImplementedError("Unable to run the model node by node.")


if ort is not None:
    backend_test = onnx.backend.test.BackendTest(InferenceSessionBackend, __name__)

    if platform.architecture()[0] == "32bit":
        backend_test.exclude("(test_vgg19|test_zfnet|test_bvlc_alexnet)")
    if platform.system() == "Windows":
        backend_test.exclude("test_sequence_model")

    # The following tests cannot pass because they consists in generating random number.
    backend_test.exclude("(test_bernoulli)")

    # The following tests are not supported by onnxruntime.
    backend_test.exclude(
        "("
        "test_adagrad"
        "|test_adam"
        "|test_add_uint8"
        "|bitshift_left_uint16"
        "|bitshift_right_uint16"
        "|cast_BFLOAT16_to_FLOAT"
        "|cast_FLOAT_to_BFLOAT16"
        "|castlike_BFLOAT16_to_FLOAT"
        "|castlike_FLOAT_to_BFLOAT16"
        "|clip_default_int8_min_expanded"
        "|clip_default_int8_max_expanded"
        "|div_uint8"
        "|gru_batchwise"  # Batchwise recurrent operations (layout == 1) are not supported.
        "|loop16_seq_none"  # The graph is missing type information needed to construct the ORT tensor.
        "|lstm_batchwise"  # Batchwise recurrent operations (layout == 1) are not supported.
        "|m(in|ax)_u?int(16|8)"
        "|momentum"
        "|mul_uint8"
        "|pow_types_float32_uint32"
        "|pow_types_float32_uint64"
        "|simple_rnn_batchwise"  # Batchwise recurrent operations (layout == 1) are not supported.
        "|sub_uint8"
        "|gradient_of_add"
        "|test_batchnorm_epsilon_training_mode"  # Training mode does not support BN opset 14 (or higher) yet.
        "|test_batchnorm_example_training_mode"  # Training mode does not support BN opset 14 (or higher) yet.
        "|_to_FLOAT8E4M3FN"  # No corresponding Numpy type for Tensor Type.
        "|_to_FLOAT8E5M2"  # No corresponding Numpy type for Tensor Type.
        "|cast_FLOAT8E"  # No corresponding Numpy type for Tensor Type.
        "|castlike_FLOAT8E"  # No corresponding Numpy type for Tensor Type.
        "|test_dequantizelinear_axis"  # y_scale must be a scalar or 1D tensor of size 1.
        "|test_dequantizelinear"  # No corresponding Numpy type for Tensor Type.
        "|test_quantizelinear_axis"  # y_scale must be a scalar or 1D tensor of size 1.
        "|test_quantizelinear"  # No corresponding Numpy type for Tensor Type.
        "|test_affine_grid_"  # new IR version 9 and opset version 20 not supported yet.
        "|test_quantizelinear_uint4"  # No corresponding Numpy type for Tensor Type.
        "|test_quantizelinear_int4"  # No corresponding Numpy type for Tensor Type.
        "|test_dequantizelinear_uint4"  # No corresponding Numpy type for Tensor Type.
        "|test_dequantizelinear_int4"  # No corresponding Numpy type for Tensor Type.
        "|test_cast_UINT4_to_FLOAT"  # No corresponding Numpy type for Tensor Type.
        "|test_cast_INT4_to_FLOAT"  # No corresponding Numpy type for Tensor Type.
        "|test_cast_UINT4_to_FLOAT16"  # No corresponding Numpy type for Tensor Type.
        "|test_cast_INT4_to_FLOAT16"  # No corresponding Numpy type for Tensor Type.
        "|test_maxpool_2d_ceil_output_size_reduce_by_one"  # TODO: remove after https://github.com/microsoft/onnxruntime/pull/18377 in Ort release.
        ")"
    )

    # Exclude all tests that require IR10 until onnxruntime aligns
    # TODO: Unwaive tests once onnxruntime supports Opset21/IR10 https://github.com/onnx/onnx/issues/5840
    backend_test.exclude(
        "("
        "test_cast_"
        "|test_castlike_"
        "|test_constant"
        "|test_edge_pad_cpu"
        "|test_flatten_"
        "|test_identity"
        "|test_reflect_pad"
        "|test_reshape_"
        "|test_shape_"
        "|test_size_"
        "|test_squeeze_"
        "|test_transpose_"
        "|test_unsqueeze_"
        "|test_wrap_pad_"
        ")"
    )

    # The following tests fail due to small discrepancies.
    backend_test.exclude("(cast_FLOAT_to_STRING|castlike_FLOAT_to_STRING|stft)")

    # The following tests fail due to huge discrepancies.
    backend_test.exclude(
        "("
        "resize_downsample_scales_cubic_align_corners"
        "|resize_downsample_scales_linear_align_corners"
        "|training_dropout"
        ")"
    )

    # The followiing tests fail due to a bug in onnxruntime in handling reduction
    # ops that perform reduction over an empty set of values.
    backend_test.exclude(
        "("
        "test_reduce_sum_empty_set"
        "|test_reduce_prod_empty_set"
        "|test_reduce_min_empty_set"
        "|test_reduce_max_empty_set"
        "|test_reduce_sum_square_empty_set"
        "|test_reduce_log_sum_empty_set"
        "|test_reduce_log_sum_exp_empty_set"
        "|test_reduce_l1_empty_set"
        "|test_reduce_l2_empty_set"
        ")"
    )

    # The following tests fail for no obvious reason.
    backend_test.exclude(
        "("
        "maxunpool_export_with_output_shape"  # not the same expected output
        "|softplus_example_expanded"  # Could not find an implementation for Exp(1) node with name ''
        "|softplus_expanded"  # Could not find an implementation for Exp(1) node with name ''
        "|AvgPool[1-3]d"  # Could not find an implementation for AveragePool(1) node with name ''
        "|BatchNorm1d_3d_input_eval"  # Could not find an implementation for BatchNormalization(6) node with name ''
        "|BatchNorm[2-3]d_eval"  # Could not find an implementation for BatchNormalization(6) node with name ''
        "|GLU"  # Could not find an implementation for Mul(6) node with name ''
        "|Linear"  # Could not find an implementation for Gemm(6) node with name ''
        "|PReLU"  # Could not find an implementation for PRelu(6) node with name ''
        "|PoissonNLL"  # Could not find an implementation for Mul(6) node with name ''
        "|Softsign"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_add_broadcast"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_add_size1"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_addconstant"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_addmm"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_basic"  # Could not find an implementation for Add(6) node with name ''
        "|operator_mm"  # Could not find an implementation for Gemm(6) node with name ''
        "|operator_non_float_params"  # Could not find an implementation for Add(6) node with name ''
        "|operator_params"  # Could not find an implementation for Add(6) node with name ''
        "|operator_pow"  # Could not find an implementation for Pow(1) node with name ''
        ")"
    )

    # The following tests are new with opset 19 and 20, or ai.onnx.ml 4
    if ort_version is not None and ort_version < Version("1.16"):
        backend_test.exclude(
            "("
            "averagepool"
            "|_pad_"
            "|_resize_"
            "|_size_"
            "|cast"
            "|castlike"
            "|equal_string_broadcast"
            "|equal_string"
            "|equal"
            "|half_pixel_symmetric"
            "|identity"
            "|reshape"
            ")"
        )
    if ort_version is not None and ort_version < Version("1.17"):
        backend_test.exclude(
            "("
            "deform_conv"
            "|dequantizelinear_uint16"
            "|dequantizelinear_int16"
            "|quantizelinear_uint16"
            "|quantizelinear_int16"
            "|dft"
            "|gelu"
            "|gridsample"
            "|group_normalization"
            "|identity_opt"
            "|image_decoder"
            "|isinf_float16"
            "|label_encoder"
            "|optional_get_element_optional_sequence"
            "|qlinearmatmul_2D_int8"
            "|qlinearmatmul_2D_uint8_float16"
            "|qlinearmatmul_3D_int8"
            "|qlinearmatmul_3D_uint8_float16"
            "|reduce_max_bool_inputs"
            "|reduce_min_bool_inputs"
            "|regex_full_match"
            "|string_concat"
            "|string_split"
            "|constantofshape_float_ones"
            "|constantofshape_int_shape_zero"
            "|constantofshape_int_zeros"
            "|isinf"
            "|isinf_negative"
            "|isinf_positive"
            "|isnan"
            "|isnan_float16"
            "|qlinearmatmul_2D_uint8_float32"
            "|qlinearmatmul_3D_uint8_float32"
            ")"
        )
    if ort_version is not None and ort_version < Version("1.18"):
        # when adding new tests to the list, please add a comment with the reason for exclusion
        # for tests that "not supported by onnxruntime 1.17", it will be solved in the next
        # onnxruntime release with ONNX 1.16.0 integrated. The work is covered in ONNX integration procedure.
        backend_test.exclude(
            "("
            "deform_conv"  # deform_conv is not supported in onnxruntime
            "|dft"  # Max absolute difference > atol=1e-07. shall be able to set atol (https://github.com/onnx/onnx/issues/5897)
            "|group_normalization"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|identity_opt"  # fixed in ort 1.18 (https://github.com/microsoft/onnxruntime/pull/19273)
            "|image_decoder"  # image_decoder is not supported in onnxruntime
            "|optional_get_element_optional_sequence"  # fixed in ort 1.18 (https://github.com/microsoft/onnxruntime/pull/19273)
            "|qlinearmatmul_2D_int8"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|qlinearmatmul_2D_uint8_float16"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|qlinearmatmul_3D_int8"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|qlinearmatmul_3D_uint8_float16"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|qlinearmatmul_2D_uint8_float32"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|qlinearmatmul_3D_uint8_float32"  # new/updated test cases with opset and/or IR version not supported by onnxruntime 1.17
            "|tree_ensemble"  # tree_ensemble not yet implemented in ort
            ")"
        )

    # Import all test cases at global scope to make them visible to python.unittest
    globals().update(backend_test.test_cases)


if __name__ == "__main__":
    unittest.main()