Spaces:
Sleeping
Sleeping
# 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): | |
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 | |
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) | |
def run_model(cls, model: onnx.ModelProto, inputs, device=None, **kwargs): | |
return super().run_model(model, inputs, device=device, **kwargs) | |
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() | |