| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| """Relay to ONNX serialization test cases""" |
| import pytest |
| |
| pytest.importorskip("onnx") |
| pytest.importorskip("onnxruntime") |
| |
| import numpy as np |
| import onnxruntime as rt |
| |
| import tvm |
| from tvm import relay |
| from tvm.contrib.target.onnx import to_onnx |
| |
| |
| def func_to_onnx(func, name): |
| mod = tvm.IRModule() |
| mod["main"] = func |
| onnx_model = to_onnx(mod, {}, name, path=None) |
| return onnx_model.SerializeToString() |
| |
| |
| def run_onnx(onnx_model, input_data): |
| sess = rt.InferenceSession(onnx_model) |
| input_names = {} |
| for input, data in zip(sess.get_inputs(), input_data): |
| input_names[input.name] = data |
| output_names = [out.name for out in sess.get_outputs()] |
| res = sess.run(output_names, input_names) |
| return res |
| |
| |
| def run_relay(func, data_tuple): |
| target = "llvm" |
| ctx = tvm.context("llvm", 0) |
| intrp = relay.create_executor("graph", ctx=ctx, target=target) |
| relay_res = intrp.evaluate(func)(*data_tuple) |
| |
| result = [] |
| relay_res = relay_res if isinstance(relay_res, list) else [relay_res] |
| for res in relay_res: |
| result.append(res.asnumpy()) |
| |
| return result |
| |
| |
| def verify_results(relay_func, indata, test_name, rtol=1e-7, atol=0): |
| relay_results = run_relay(relay_func, indata) |
| onnx_results = run_onnx(func_to_onnx(relay_func, test_name), indata) |
| |
| for relay_res, onnx_res in zip(relay_results, onnx_results): |
| np.testing.assert_allclose(relay_res, onnx_res, rtol=rtol, atol=atol) |
| |
| |
| def test_add(): |
| dtype = "float32" |
| t1 = relay.TensorType((5, 10, 5)) |
| t2 = relay.TensorType((5, 10, 5)) |
| x = relay.var("x", t1, dtype=dtype) |
| y = relay.var("y", t2, dtype=dtype) |
| z = relay.add(x, y) |
| func = relay.Function([x, y], z) |
| |
| x_data = np.random.rand(5, 10, 5).astype(dtype) |
| y_data = np.random.rand(5, 10, 5).astype(dtype) |
| |
| verify_results(func, [x_data, y_data], "test_add") |
| |
| |
| def test_bias_add(): |
| for dtype in ["float16", "float32"]: |
| xshape = (10, 2, 3, 4) |
| bshape = (2,) |
| rtol = 1e-2 if dtype == "float16" else 1e-5 |
| x = relay.var("x", shape=xshape, dtype=dtype) |
| bias = relay.var("bias", dtype=dtype) |
| z = relay.nn.bias_add(x, bias) |
| func = relay.Function([x, bias], z) |
| |
| x_data = np.random.uniform(size=xshape).astype(dtype) |
| y_data = np.random.uniform(size=bshape).astype(dtype) |
| |
| verify_results(func, [x_data, y_data], "test_bias_add", rtol=rtol) |
| |
| |
| def test_conv2d(): |
| def verify_conv2d( |
| dtype, scale, dshape, kshape, padding=(1, 1), groups=1, dilation=(1, 1), **attrs |
| ): |
| x = relay.var("x", shape=dshape, dtype=dtype) |
| w = relay.var("w", shape=kshape, dtype=dtype) |
| y = relay.nn.conv2d(x, w, padding=padding, dilation=dilation, groups=groups, **attrs) |
| func = relay.Function([x, w], y) |
| data = np.random.uniform(-scale, scale, size=dshape).astype(dtype) |
| kernel = np.random.uniform(-scale, scale, size=kshape).astype(dtype) |
| verify_results(func, [data, kernel], "test_conv2d", rtol=1e-5, atol=1e-5) |
| |
| dshape = (1, 32, 18, 18) |
| kshape = (32, 1, 3, 3) |
| verify_conv2d( |
| "float32", 1, dshape, kshape, padding=(1, 1), channels=32, groups=32, kernel_size=(3, 3) |
| ) |
| |
| dshape = (1, 32, 18, 18) |
| kshape = (32, 4, 3, 3) |
| verify_conv2d( |
| "float32", 1, dshape, kshape, padding=(1, 1), channels=32, groups=8, kernel_size=(3, 3) |
| ) |
| |
| # also group conv2d |
| dshape = (1, 32, 18, 18) |
| kshape = (64, 1, 3, 3) |
| verify_conv2d( |
| "float32", 1, dshape, kshape, padding=(1, 1), channels=64, groups=32, kernel_size=(3, 3) |
| ) |
| |
| # normal conv2d |
| dshape = (1, 3, 224, 224) |
| kshape = (10, 3, 3, 3) |
| verify_conv2d("float32", 1, dshape, kshape, padding=(1, 1), channels=10, kernel_size=(3, 3)) |
| |
| dshape = (1, 3, 224, 224) |
| kshape = (10, 3, 3, 3) |
| verify_conv2d("float32", 1, dshape, kshape, padding=(2, 2), channels=10, kernel_size=(3, 3)) |
| |
| dshape = (1, 3, 18, 18) |
| kshape = (10, 3, 3, 3) |
| verify_conv2d( |
| "float32", |
| 1, |
| dshape, |
| kshape, |
| padding=(1, 1), |
| channels=10, |
| kernel_size=(3, 3), |
| dilation=(3, 3), |
| ) |
| |
| dshape = (1, 3, 18, 18) |
| kshape = (10, 3, 2, 2) |
| verify_conv2d( |
| "float32", |
| 1, |
| dshape, |
| kshape, |
| padding=(2, 2), |
| channels=10, |
| kernel_size=(2, 2), |
| dilation=(1, 1), |
| ) |
| |
| dshape = (1, 3, 18, 18) |
| kshape = (10, 3, 4, 4) |
| verify_conv2d("float32", 1, dshape, kshape, padding=(1, 1), channels=10, kernel_size=(4, 4)) |
| |
| dshape = (1, 3, 18, 18) |
| kshape = (10, 3, 4, 4) |
| verify_conv2d("float32", 1, dshape, kshape, padding=(1, 1), channels=10, kernel_size=(4, 4)) |
| |
| |
| def test_reshape(): |
| def verify_reshape(shape, newshape): |
| x = relay.var("x", relay.TensorType(shape, "float32")) |
| z = relay.reshape(x, newshape=newshape) |
| |
| func = relay.Function([x], z) |
| x_data = np.random.uniform(low=-1, high=1, size=shape).astype("float32") |
| verify_results(func, [x_data], "test_reshape", rtol=1e-5, atol=1e-5) |
| |
| verify_reshape((2, 3, 4), tuple(np.array([4, 2, 3], dtype=np.int64))) |
| verify_reshape((2, 3, 4), tuple(np.array([2, 0, 0], dtype=np.int64))) |
| verify_reshape((2, 3, 4), tuple(np.array([0, -1], dtype=np.int64))) |
| verify_reshape((2, 3, 4), tuple(np.array([-1, 0], dtype=np.int64))) |
| |
| |
| def test_transpose(): |
| def verify_reshape(shape, newshape): |
| x = relay.var("x", relay.TensorType(shape, "float32")) |
| z = relay.transpose(x, newshape) |
| func = relay.Function([x], z) |
| x_data = np.random.uniform(low=-1, high=1, size=shape).astype("float32") |
| verify_results(func, [x_data], "test_transpose", rtol=1e-5, atol=1e-5) |
| |
| verify_reshape((1, 2, 3, 4), (0, 2, 3, 1)) |
| verify_reshape((1, 2, 3, 4), (0, 3, 2, 1)) |
| |
| |
| def test_dense(): |
| def verify_dense(d_shape, w_shape): |
| data = relay.var("data", relay.TensorType(d_shape, "float32")) |
| weight = relay.var("weight", relay.TensorType(w_shape, "float32")) |
| func = relay.Function([data, weight], relay.nn.dense(data, weight)) |
| x_data = np.random.uniform(size=d_shape).astype("float32") |
| w_data = np.random.uniform(size=w_shape).astype("float32") |
| verify_results(func, [x_data, w_data], "test_dense", rtol=1e-5, atol=1e-5) |
| |
| verify_dense((1, 8), (16, 8)) |
| verify_dense((1, 4), (3, 4)) |
| |
| |
| def test_max_pool(): |
| def verify_max_pool(x_shape, pool_size, strides, padding, ceil_mode): |
| x = relay.var("x", relay.TensorType(x_shape, "float32")) |
| y = tvm.relay.nn.max_pool2d( |
| x, pool_size=pool_size, strides=strides, padding=padding, ceil_mode=ceil_mode |
| ) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=x_shape).astype("float32") |
| verify_results(func, [x_data], "test_max_pool", rtol=1e-5, atol=1e-5) |
| |
| verify_max_pool( |
| (1, 4, 16, 16), pool_size=(2, 2), strides=(2, 2), padding=(0, 0), ceil_mode=False |
| ) |
| |
| |
| def test_batch_flatten(): |
| def verify_test_batch_flatten(d_shape): |
| data = relay.var("data", relay.TensorType(d_shape, "float32")) |
| func = relay.Function([data], relay.nn.batch_flatten(data)) |
| x_data = np.random.uniform(size=d_shape).astype("float32") |
| verify_results(func, [x_data], "test_batch_flatten", rtol=1e-5, atol=1e-5) |
| |
| verify_test_batch_flatten((1, 2, 3, 4)) |
| verify_test_batch_flatten((1, 8)) |
| |
| |
| def test_batch_norm(): |
| def verify_batch_norm(axis=1): |
| for dtype in ["float16", "float32"]: |
| data = relay.var("data", relay.TensorType((2, 4, 4, 1), dtype)) |
| gamma_shape = (data.type_annotation.shape[axis].value,) |
| beta = relay.var("beta", relay.TensorType(gamma_shape, dtype)) |
| gamma = relay.var("gamma", relay.TensorType(gamma_shape, dtype)) |
| moving_mean = relay.var("moving_mean", relay.TensorType(gamma_shape, dtype)) |
| moving_var = relay.var("moving_var", relay.TensorType(gamma_shape, dtype)) |
| y = relay.nn.batch_norm(data, gamma, beta, moving_mean, moving_var, axis=axis) |
| func = relay.Function([data, gamma, beta, moving_mean, moving_var], y[0]) |
| |
| x_data = np.random.uniform(size=(2, 4, 4, 1)).astype(dtype) |
| beta = np.random.uniform(size=gamma_shape).astype(dtype) |
| gamma = np.random.uniform(size=gamma_shape).astype(dtype) |
| moving_mean = np.random.uniform(size=gamma_shape).astype(dtype) |
| moving_var = np.random.uniform(size=gamma_shape).astype(dtype) |
| verify_results( |
| func, |
| [x_data, gamma, beta, moving_mean, moving_var], |
| "test_batch_norm", |
| rtol=1e-1, |
| atol=1e-1, |
| ) |
| |
| verify_batch_norm(axis=1) |
| verify_batch_norm(axis=3) |
| |
| |
| def test_pad(): |
| def verify_pad(): |
| for dtype in ["float16", "float32"]: |
| dshape = (4, 10, 7, 7) |
| x = relay.var("x", shape=dshape, dtype=dtype) |
| y = relay.nn.pad(x, ((1, 1), (2, 2), (3, 3), (4, 4))) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| verify_results(func, [x_data], "test_pad", rtol=1e-5, atol=1e-5) |
| |
| verify_pad() |
| |
| |
| def test_sofmax(): |
| def verify_sofmax(): |
| for dtype in ["float32"]: |
| shape = (10, 4) |
| x = relay.var("x", shape=shape, dtype=dtype) |
| y = relay.nn.softmax(x, axis=1) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=shape).astype(dtype) |
| verify_results(func, [x_data], "test_softmax", rtol=1e-5, atol=1e-5) |
| |
| verify_sofmax() |
| |
| |
| def test_squeeze(): |
| def verify_squeeze(shape, dtype, axis): |
| x = relay.var("x", relay.TensorType(shape, dtype)) |
| z = relay.squeeze(x, axis=axis) |
| func = relay.Function([x], z) |
| x_data = np.random.random_sample(shape).astype(dtype) |
| verify_results(func, [x_data], "test_squeeze", rtol=1e-5, atol=1e-5) |
| |
| verify_squeeze((1, 3, 2, 5), "float32", None) |
| verify_squeeze( |
| (1, 3, 1), |
| "float32", |
| [ |
| 2, |
| ], |
| ) |
| verify_squeeze((1, 2, 1, 2, 1), "float32", [0, 2]) |
| |
| |
| def test_mean(): |
| def verify_mean(data_shape, axis, exclude, keepdims): |
| dtype = "float32" |
| x = relay.var("x", shape=data_shape, dtype=dtype) |
| y = relay.mean(x, axis, keepdims, exclude) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=data_shape).astype(dtype) |
| verify_results(func, [x_data], "test_mean", rtol=1e-5, atol=1e-5) |
| |
| verify_mean((1, 2), 0, False, False) |
| verify_mean((1, 2), 0, True, False) |
| verify_mean((1, 2), 0, True, True) |
| verify_mean((1, 2), 1, True, True) |
| verify_mean((3, 2, 1), 1, False, True) |
| |
| |
| def test_split(): |
| def verify_split(dshape, indices_or_sections, axis=None): |
| dtype = "float32" |
| x = relay.var("x", relay.ty.TensorType(dshape, "float32")) |
| y = relay.split(x, indices_or_sections, axis=axis) |
| func = relay.Function([x], y.astuple()) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| |
| verify_results(func, [x_data], "test_split", rtol=1e-5, atol=1e-5) |
| |
| verify_split((5, 5, 2, 2), 5, axis=1) |
| verify_split((5, 5, 2, 2), 5, axis=0) |
| verify_split((5, 5, 2, 2), [1, 3, 4], axis=0) |
| verify_split((5, 5, 2, 2), [1, 3, 4], axis=1) |
| |
| |
| def test_concatenate(): |
| def verify_concatenate(shapes, axis, dtype="float32"): |
| in_vars = [] |
| in_data = [] |
| for i, shape in enumerate(shapes): |
| in_vars.append(relay.var("x" + str(i), relay.ty.TensorType(shape, dtype))) |
| in_data.append(np.random.uniform(size=shape).astype(dtype)) |
| |
| out_tensor = relay.concatenate(in_vars, axis) |
| func = relay.Function(in_vars, out_tensor) |
| verify_results(func, in_data, "test_concatenate", rtol=1e-5, atol=1e-5) |
| |
| verify_concatenate([(2,), (2,), (2,)], -1) |
| verify_concatenate([(2, 3, 4), (2, 2, 4), (2, 5, 4)], 1) |
| verify_concatenate([(1, 2, 4), (1, 2, 3), (1, 2, 7), (1, 2, 8), (1, 2, 1)], -1) |
| verify_concatenate([(5, 6, 7, 3), (16, 6, 7, 3), (12, 6, 7, 3), (8, 6, 7, 3), (2, 6, 7, 3)], 0) |
| verify_concatenate([(1, 14400), (1, 2400), (1, 640), (1, 240)], 1) |
| |
| |
| def test_strided_slice(): |
| def verify_strided_slice(dshape, begin, end, strides, mode): |
| x = relay.var("x", relay.TensorType(dshape, "float32")) |
| if mode == "size": |
| strides = None |
| z = relay.strided_slice(x, begin=begin, end=end, strides=strides, slice_mode=mode) |
| func = relay.Function([x], z) |
| x_data = np.random.uniform(size=dshape).astype("float32") |
| verify_results(func, [x_data], "test_strided_slice", rtol=1e-5, atol=1e-5) |
| |
| for mode in ["end", "size"]: |
| verify_strided_slice((3, 4, 3), [1, 1, 0], [4, 2, 3], None, mode) |
| verify_strided_slice((3, 4, 3), [1, -1, 0], [4, -1, 3], [1, 2], mode) |
| verify_strided_slice( |
| (3, 4, 3), |
| [ |
| 1, |
| ], |
| [4, -3], |
| None, |
| mode, |
| ) |
| verify_strided_slice((3, 4, 3), [0, 0, 0], [4, -5, 4], [1, -1, 2], mode) |
| verify_strided_slice((3, 4, 3), [1, 1, 0], [4, 4, -3], [2, 1, 1], mode) |
| verify_strided_slice((3, 4, 3), [1, -1, 0], [4, -5, 3], [2, -1, 1], mode) |
| verify_strided_slice((3, 4, 3), [1, 0, 0], [2, 2, 3], [1, 1, 2], mode) |
| verify_strided_slice((3, 4, 3), [1, -1, 0], [2, -3, 3], [1, -1, 1], mode) |
| |
| verify_strided_slice((3, 4, 3), [1, 1, 0], [4, 1000, 3], None, mode) |
| verify_strided_slice((3, 4, 3), [1, 1, 0], [4, 4], None, mode) |
| verify_strided_slice((3, 4, 3), [1, 1], [4, 4, 3], None, mode) |
| verify_strided_slice((3, 4, 3), [1, 1], [4, 4, 3], [1, 1, 2], mode) |
| |
| |
| def test_cmp_type(): |
| for op, ref in ((relay.greater, np.greater), (relay.less, np.less), (relay.equal, np.equal)): |
| x_shape = (10, 4) |
| y_shape = (5, 10, 1) |
| t1 = relay.TensorType(x_shape) |
| t2 = relay.TensorType(y_shape) |
| x = relay.var("x", t1) |
| y = relay.var("y", t2) |
| z = op(x, y) |
| x_data = np.random.rand(*x_shape).astype(t1.dtype) |
| y_data = np.random.rand(*y_shape).astype(t2.dtype) |
| func = relay.Function([x, y], z) |
| verify_results(func, [x_data, y_data], "test_cmp_type", rtol=1e-5, atol=1e-5) |
| |
| |
| def test_unary_identity(): |
| for dtype in ["int16", "float32", "float64"]: |
| for op, ref in [(relay.zeros_like, np.zeros_like), (relay.ones_like, np.ones_like)]: |
| shape = (8, 9, 4) |
| x = relay.var("x", relay.TensorType(shape, dtype)) |
| y = op(x) |
| func = relay.Function( |
| [ |
| x, |
| ], |
| y, |
| ) |
| x_data = np.random.rand(*shape).astype(dtype) |
| verify_results(func, [x_data], "test_cmp_type", rtol=1e-5, atol=1e-5) |
| |
| |
| def test_binary_op(): |
| def check_binary_op(opfunc, dtype): |
| t1 = relay.TensorType((5, 10, 5)) |
| t2 = relay.TensorType((5, 10, 5)) |
| x = relay.var("x", t1, dtype=dtype) |
| y = relay.var("y", t2, dtype=dtype) |
| z = opfunc(x, y) |
| x_data = np.random.rand(5, 10, 5).astype(dtype) |
| y_data = np.random.rand(5, 10, 5).astype(dtype) |
| func = relay.Function([x, y], z) |
| verify_results(func, [x_data, y_data], "test_binary_op", rtol=1e-5, atol=1e-5) |
| |
| for opfunc, ref in [ |
| (relay.add, np.add), |
| (relay.subtract, np.subtract), |
| (relay.multiply, np.multiply), |
| (relay.divide, np.divide), |
| ]: |
| for dtype in ["float32"]: |
| check_binary_op(opfunc, dtype) |
| |
| |
| def test_tuple_types(): |
| def verify_tuple_types(dshape, indices_or_sections, axis=None, dtype="float32"): |
| x = relay.var("x", relay.ty.TensorType(dshape, dtype)) |
| y = relay.split(x, indices_or_sections, axis=axis) |
| z = relay.concatenate(y, axis=axis) |
| func = relay.Function([x], z) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| verify_results(func, [x_data], "test_tuple_types", rtol=1e-5, atol=1e-5) |
| |
| split_z = relay.split(z, indices_or_sections, axis=axis) |
| func = relay.Function([x], split_z.astuple()) |
| verify_results(func, [x_data], "test_tuple_types", rtol=1e-5, atol=1e-5) |
| |
| out = relay.Tuple([y[0] + y[1], y[0] - y[1]]) |
| func = relay.Function([x], out) |
| verify_results(func, [x_data], "test_tuple_types", rtol=1e-5, atol=1e-5) |
| |
| z = relay.concatenate(out, axis=axis) |
| func = relay.Function([x], z) |
| verify_results(func, [x_data], "test_tuple_types", rtol=1e-5, atol=1e-5) |
| |
| verify_tuple_types((5, 5, 2, 2), 5, axis=1) |
| verify_tuple_types((5, 5, 2, 2), 5, axis=0) |
| verify_tuple_types((5, 5, 2, 2), [1, 3, 4], axis=0) |
| verify_tuple_types((5, 5, 2, 2), [1, 3, 4], axis=1) |
| |
| |
| def test_layout_transform(): |
| def verify_layout_transform(dshape, src_layout, dst_layout, dtype="float32"): |
| x = relay.var("x", relay.ty.TensorType(dshape, dtype)) |
| y = relay.layout_transform(x, src_layout, dst_layout) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| verify_results(func, [x_data], "test_layout_transform", rtol=1e-5, atol=1e-5) |
| |
| verify_layout_transform((1, 3, 8, 8), "NCHW", "NHWC") |
| verify_layout_transform((1, 8, 8, 3), "NHWC", "NCHW") |
| |
| |
| def test_clip(): |
| def verify_clip(dshape, a_min, a_max, dtype="float32"): |
| x = relay.var("x", relay.ty.TensorType(dshape, dtype)) |
| y = relay.clip(x, a_min, a_max) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| verify_results(func, [x_data], "test_clip", rtol=1e-5, atol=1e-5) |
| |
| verify_clip((5, 5, 2, 5), 0, 0.2) |
| verify_clip((5, 5, 2, 5), 0.2, 0.5) |
| |
| |
| def test_expand_dims(): |
| def verify_expand_dims(dshape, axis, num_newaxis, dtype="float32"): |
| x = relay.var("x", relay.ty.TensorType(dshape, dtype)) |
| y = relay.expand_dims(x, axis, num_newaxis) |
| func = relay.Function([x], y) |
| x_data = np.random.uniform(size=dshape).astype(dtype) |
| verify_results(func, [x_data], "test_expand_dims", rtol=1e-5, atol=1e-5) |
| |
| verify_expand_dims((1, 1001), 0, 2) |
| verify_expand_dims((1, 1, 1001), 2, 2) |
| |
| |
| if __name__ == "__main__": |
| test_add() |
| test_bias_add() |
| test_conv2d() |
| test_reshape() |
| test_transpose() |
| test_dense() |
| test_max_pool() |
| test_batch_flatten() |
| test_batch_norm() |
| test_pad() |
| test_mean() |
| test_split() |
| test_concatenate() |
| test_sofmax() |
| test_squeeze() |
| test_strided_slice() |
| test_cmp_type() |
| test_binary_op() |
| test_tuple_types() |
| test_layout_transform() |
| test_clip() |
| test_expand_dims() |