From 87c6d3da70d83e1a98dcaecdea383ea742476aba Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Sun, 26 Sep 2021 11:39:21 +0000 Subject: [PATCH 01/24] add part of operators --- python/tvm/relay/frontend/paddlepaddle.py | 898 +++++++++++++++--- .../frontend/paddlepaddle/test_forward.py | 1 - 2 files changed, 749 insertions(+), 150 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 76a12691d2bf..b404bab1dbd9 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -17,7 +17,6 @@ # pylint: disable=invalid-name, import-self, len-as-condition, unused-argument, too-many-lines # pylint: disable=import-outside-toplevel """Paddle: PArallel Distributed Deep LEarning.""" -import warnings import numpy as np @@ -25,12 +24,14 @@ from tvm.ir import IRModule from .. import analysis +from .. import ty as _ty from .. import expr as _expr from .. import function as _function from .. import ty as _ty from .. import op as _op from .common import ( fold_constant, + get_relay_op, infer_shape, infer_type, infer_value, @@ -40,28 +41,122 @@ __all__ = ["from_paddle"] +def _get_pad_size(in_size, dilated_kernel_size, stride_size): + """calculate the paddings size""" + + if stride_size == 1 or in_size % stride_size == 0: + pad = max(dilated_kernel_size - stride_size, 0) + else: + pad = max(dilated_kernel_size - (in_size % stride_size), 0) + + pad_before = pad // 2 + pad_after = pad - pad_before + + return [pad_before, pad_after] + + +def _dtype_shape_promotion(inputs): + """promote data type and shape for list of tensors.""" + + dtype_order = ["bool", "int8", "int16", "int32", "int64", "float32", "float64"] + + ranks = [len(infer_shape(x)) for x in inputs] + if set(ranks) == set([1, 0]): + for i, r in enumerate(ranks): + if r == 0: + inputs[i] = _op.expand_dims(inputs[i], axis=0) + + dtypes = set(dtype_order.index(infer_type(x).checked_type.dtype) for x in inputs) + if len(dtypes) == 1: + return inputs + max_dtype = dtype_order[max(dtypes)] + for i, input_op in enumerate(inputs): + if infer_type(input_op).checked_type.dtype != max_dtype: + inputs[i] = input_op.astype(max_dtype) + return inputs + + def shape_of(x, dtype="int32"): """Get shape of a tensor""" ttype = infer_type(x).checked_type if not _ty.is_dynamic(ttype): shape = list(ttype.shape) - return _expr.const(shape, dtype) + return _expr.const(np.array(shape), dtype) return _op.shape_of(x, dtype) -def _get_pad_size(in_size, dilated_kernel_size, stride_size): - """calculate the paddings size""" +def _infer_value(x, params): + """Try running infer_value, and if successful, return the inferred value. + Otherwise, return input""" - if stride_size == 1 or in_size % stride_size == 0: - pad = max(dilated_kernel_size - stride_size, 0) + try: + value = infer_value(x, params) + return value.numpy().tolist() + except Exception: # pylint: disable=broad-except + return x + + +def _convert_dtype_value(val): + """converts a Paddle type id to a string.""" + + convert_dtype_map = { + 21: "int8", + 20: "uint8", + 6: "float64", + 5: "float32", + 4: "float16", + 3: "int64", + 2: "int32", + 1: "int16", + 0: "bool", + } + if val not in convert_dtype_map: + msg = "Paddle data type value %d is not handled yet." % (val) + raise NotImplementedError(msg) + return convert_dtype_map[val] + + +def convert_unary_op(g, op, block): + """Operator converter for all the activation.""" + + op_map = { + "isinf_v2": _op.isinf, + "isfinite_v2": _op.isfinite, + "isnan_v2": _op.isnan, + } + if op.type in op_map: + unary_func = op_map[op.type] else: - pad = max(dilated_kernel_size - (in_size % stride_size), 0) + unary_func = get_relay_op(op.type) + out = unary_func(g.get_node(op.input("X")[0])) + g.add_node(op.output("Out")[0], out) - pad_before = pad // 2 - pad_after = pad - pad_before - return [pad_before, pad_after] +def convert_addmm(g, op, block): + """Operator converter for addmm.""" + + input_x = g.get_node(op.input("Input")[0]) + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) + + alpha = op.attr("Alpha") + beta = op.attr("Beta") + dtype = block.var(op.output("Out")[0]).dtype + dtype = str(dtype).strip().split(".")[1] + + if not isinstance(alpha, _expr.Expr) and alpha != 1: + alpha = _expr.const(alpha, dtype) + x *= alpha + + if not isinstance(beta, _expr.Expr) and beta != 1: + beta = _expr.const(beta, dtype) + input_x *= beta + + transposed_y = _op.transpose(y, axes=[1, 0]) + dense_out = _op.nn.dense(x, transposed_y) + out = dense_out + input_x + g.add_node(op.output("Out")[0], out) def convert_arg_max(g, op, block): @@ -70,6 +165,8 @@ def convert_arg_max(g, op, block): axis = op.attr("axis") keepdims = op.attr("keepdims") flatten = op.attr("flatten") + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) x = g.get_node(op.input("X")[0]) if axis is None or flatten: @@ -77,13 +174,64 @@ def convert_arg_max(g, op, block): out = _op.argmax(x, axis=None, keepdims=True) else: out = _op.argmax(x, axis=axis, keepdims=keepdims) + if dtype != infer_type(out).checked_type.dtype: + out = _op.cast(out, dtype) g.add_node(op.output("Out")[0], out) +def convert_arg_min(g, op, block): + """Operator converter for arg_min.""" + + axis = op.attr("axis") + keepdims = op.attr("keepdims") + flatten = op.attr("flatten") + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + + x = g.get_node(op.input("X")[0]) + if axis is None or flatten: + x = _op.reshape(x, [-1]) + out = _op.argmin(x, axis=None, keepdims=True) + else: + out = _op.argmin(x, axis=axis, keepdims=keepdims) + if dtype != infer_type(out).checked_type.dtype: + out = _op.cast(out, dtype) + g.add_node(op.output("Out")[0], out) + + +def convert_argsort(g, op, block): + """Operator converter for argsort.""" + + x = g.get_node(op.input("X")[0]) + axis = op.attr("axis") + descending = op.attr("descending") + + out = _op.sort(x, axis, not descending) + out_indice = _op.argsort(x, axis, not descending, dtype="int64") + g.add_node(op.output("Out")[0], out) + g.add_node(op.output("Indices")[0], out_indice) + + def convert_assign(g, op, block): """Operator converter for assign.""" - out = _op.copy(g.get_node(op.input("X")[0])) + out = g.get_node(op.input("X")[0]) + g.add_node(op.output("Out")[0], out) + + +def convert_assign_value(g, op, block): + """Operator converter for assign_value.""" + + keys = ["bool_values", "fp32_values", "int32_values", "int64_values"] + dtypes = ["bool", "float32", "int32", "int64"] + for i, key in enumerate(keys): + dtype = dtypes[i] + value = np.array(op.attr(key)).astype(dtype) + if value is not None and value.size >= 1: + break + shape = op.attr("shape") + value = value.reshape(shape) + out = _op.const(value, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -107,21 +255,71 @@ def convert_batch_norm(g, op, block): g.add_node(op.output("Y")[0], out[0]) +def convert_bmm(g, op, block): + """Operator converter for bmm.""" + + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) + y = _op.transpose(y, [0, 2, 1]) + out = _op.nn.batch_matmul(x, y) + g.add_node(op.output("Out")[0], out) + + def convert_cast(g, op, block): """Operator converter for cast.""" - dtype = block.var(op.output("Out")[0]).dtype - dtype = str(dtype).strip().split(".")[1] + dtype = op.attr("out_dtype") + dtype = _convert_dtype_value(dtype) x = g.get_node(op.input("X")[0]) out = _op.cast(x, dtype=dtype) g.add_node(op.output("Out")[0], out) +def convert_clip(g, op, block): + """Operator converter for clip.""" + + x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype + is_dynamic = False + if op.input("Min"): + min_value = g.get_node(op.input("Min")[0]) + min_value = _infer_value(min_value, g.get_params()) + if isinstance(min_value, _expr.Expr): + is_dynamic = True + else: + min_value = min_value[0] + else: + min_value = op.attr("min") + if op.input("Max"): + max_value = g.get_node(op.input("Max")[0]) + max_value = _infer_value(max_value, g.get_params()) + if isinstance(max_value, _expr.Expr): + if not is_dynamic: + is_dynamic = True + min_value = _op.const(min_value, dtype) + else: + max_value = max_value[0] + if is_dynamic: + max_value = _op.const(max_value, dtype) + else: + max_value = op.attr("max") + if is_dynamic: + max_value = _op.const(max_value, dtype) + + if not is_dynamic: + out = _op.clip(x, min_value, max_value) + else: + out = _op.maximum(x, min_value) + out = _op.minimum(out, max_value) + g.add_node(op.output("Out")[0], out) + + def convert_concat(g, op, block): """Operator converter for concat.""" inputs = [g.get_node(op.input("X")[i]) for i in range(len(op.input("X")))] axis = op.attr("axis") + inputs = _dtype_shape_promotion(inputs) out = _op.concatenate(inputs, axis=axis) g.add_node(op.output("Out")[0], out) @@ -138,12 +336,22 @@ def convert_conv2d(g, op, block): kernel = g.get_node(op.input("Filter")[0]) input_x = g.get_node(op.input("Input")[0]) out_channels, _, k_h, k_w = infer_shape(kernel) - in_h, in_w = infer_shape(input_x)[2:] if padding_algorithm == "VALID": paddings = [0, 0] elif padding_algorithm == "SAME": - pad_h = _get_pad_size(in_h, (k_h - 1) * dilations[0] + 1, strides[0]) - pad_w = _get_pad_size(in_w, (k_w - 1) * dilations[1] + 1, strides[1]) + if strides[0] == 1 and strides[1] == 1: + pad_h = _get_pad_size(0, (k_h - 1) * dilations[0] + 1, strides[0]) + pad_w = _get_pad_size(0, (k_w - 1) * dilations[1] + 1, strides[1]) + else: + input_shape = shape_of(input_x) + h_w = _op.strided_slice(input_shape, [2], [4]) + try: + in_h, in_w = infer_value(h_w, g.get_params()).numpy().tolist() + except Exception as e: + msg = "Dynamic shape is not supported in SAME padding algorithm while stride!=1" + raise tvm.error.OpAttributeInvalid(msg) from e + pad_h = _get_pad_size(in_h, (k_h - 1) * dilations[0] + 1, strides[0]) + pad_w = _get_pad_size(in_w, (k_w - 1) * dilations[1] + 1, strides[1]) paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: @@ -167,6 +375,37 @@ def convert_conv2d(g, op, block): g.add_node(op.output("Output")[0], out) +def convert_crop(g, op, block): + """Operator converter for crop.""" + + x = g.get_node(op.input("X")[0]) + dims = len(infer_shape(x)) + input_shape = op.input("Shape") + input_offsets = op.input("Offsets") + if input_shape: + shape = g.get_node(input_shape[0]) + shape = _infer_value(shape, g.get_params()) + else: + shape = op.attr("shape") + + if input_offsets: + offsets = g.get_node(input_offsets[0]) + offsets = _infer_value(offsets, g.get_params()) + else: + offsets = op.attr("offsets") + + if not isinstance(shape, _expr.Expr): + shape = _op.const(shape, "int32") + if not isinstance(offsets, _expr.Expr): + offsets = _op.const(offsets, "int32") + slice_start = offsets + slice_end = _op.add(shape, offsets) + strides = _op.const([1] * dims, dtype="int32") + + out = _op.strided_slice(x, slice_start, slice_end, strides) + g.add_node(op.output("Out")[0], out) + + def convert_cumsum(g, op, block): """Operator converter for cumsum.""" @@ -191,7 +430,51 @@ def convert_dropout(g, op, block): """Operator converter for dropout.""" x = g.get_node(op.input("X")[0]) - out = _op.copy(x) + g.add_node(op.output("Out")[0], x) + + +def convert_elu(g, op, block): + """Operator converter for elu.""" + + x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype + alpha = op.attr("alpha") + alpha = _expr.const(-1.0 * alpha, dtype=dtype) + out = alpha * _op.nn.relu(_expr.const(1, dtype=dtype) - _op.exp(x)) + _op.nn.relu(x) + g.add_node(op.output("Out")[0], out) + + +def convert_dist(g, op, block): + """Operator converter for dist.""" + + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) + dtype = infer_type(x).checked_type.dtype + p = op.attr("p") + + x -= y + if p == np.inf: + out = _op.reduce.max(_op.abs(x)) + elif p == np.NINF: + out = _op.reduce.min(_op.abs(x)) + else: + reci_order = _expr.const(1.0 / p, dtype=dtype) + p = _expr.const(p) + out = _op.power( + _op.reduce.sum(_op.power(_op.abs(x), p)), + reci_order, + ) + out = _op.expand_dims(out, axis=0) + g.add_node(op.output("Out")[0], out) + + +def convert_dot(g, op, block): + """Operator converter for dot.""" + + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) + + out = _op.sum(_op.multiply(x, y), axis=[-1], keepdims=True) g.add_node(op.output("Out")[0], out) @@ -199,49 +482,59 @@ def convert_elementwise_op(g, op, block): """Operator converter for all the elementwise operators.""" op_map = { - "elementwise_div": lambda x, y: x / y, - "elementwise_add": lambda x, y: x + y, - "elementwise_mul": lambda x, y: x * y, - "elementwise_sub": lambda x, y: x - y, - "elementwise_mod": lambda x, y: x % y, + "elementwise_div": "divide", + "elementwise_add": "add", + "elementwise_mul": "multiply", + "elementwise_sub": "subtract", + "elementwise_mod": "mod", + "elementwise_max": "maximum", + "elementwise_min": "minimum", + "elementwise_pow": "power", + "elementwise_floordiv": "floor_divide", + "floor_mod": "floor_mod", + "equal": "equal", + "greater_equal": "greater_equal", + "greater_than": "greater", + "less_equal": "less_equal", + "less_than": "less", + "not_equal": "not_equal", } op_func = op_map[op.type] ipt0 = g.get_node(op.input("X")[0]) ipt1 = g.get_node(op.input("Y")[0]) - ipt0_shape = block.var(op.input("X")[0]).shape - ipt1_shape = block.var(op.input("Y")[0]).shape + ipt0_shape = infer_shape(ipt0) + ipt1_shape = infer_shape(ipt1) axis = op.attr("axis") if len(ipt0_shape) != len(ipt1_shape): if axis < 0: axis = axis + len(ipt0_shape) if axis != len(ipt0_shape) - 1: ipt1 = _op.expand_dims(ipt1, axis=axis, num_newaxis=(len(ipt0_shape) - axis - 1)) + op_func = get_relay_op(op_func) out = op_func(ipt0, ipt1) g.add_node(op.output("Out")[0], out) -def convert_equal(g, op, block): - """Operator converter for equal.""" +def convert_expand(g, op, block): + """Operator converter for expand.""" x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Y")[0]) - out = _op.equal(x, y) + if op.input("Shape"): + sizes = g.get_node(op.input("Shape")[0]) + sizes = _infer_value(sizes, g.get_params()) + else: + sizes = op.attr("shape") + + out = _op.broadcast_to(x, sizes) g.add_node(op.output("Out")[0], out) -def convert_activation(g, op, block): - """Operator converter for all the activation.""" +def convert_expand_as(g, op, block): + """Operator converter for expand_as.""" - op_map = { - "exp": _op.exp, - "relu": _op.nn.relu, - "tanh": _op.tanh, - "sqrt": _op.sqrt, - "erf": _op.erf, - "abs": _op.abs, - } - act_func = op_map[op.type] - out = act_func(g.get_node(op.input("X")[0])) + x = g.get_node(op.input("X")[0]) + target_shape = op.attr("target_shape") + out = _op.broadcast_to(x, target_shape) g.add_node(op.output("Out")[0], out) @@ -250,15 +543,20 @@ def convert_feed(g, op, block): if block is not None: ipt_name = op.output("Out")[0] - ipt_shape = block.var(ipt_name).shape - ipt_dtype = block.var(ipt_name).dtype - ipt_dtype = str(ipt_dtype).strip().split(".")[1] + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) else: ipt_shape = op.shape ipt_dtype = str(op.dtype).strip().split(".")[1] ipt_name = op.name if g.shape_dict is not None: ipt_shape = g.shape_dict[ipt_name] + + if isinstance(ipt_shape, tuple): + ipt_shape = list(ipt_shape) + for i, s in enumerate(ipt_shape): + if s < 0: + ipt_shape[i] = _ty.Any() out = new_var(ipt_name, shape=ipt_shape, dtype=ipt_dtype) g.add_node(ipt_name, out) @@ -266,18 +564,11 @@ def convert_feed(g, op, block): def convert_fill_any_like(g, op, block): """Operator converter for fill_any_like.""" - out_name = op.output("Out")[0] - out_dtype = block.var(out_name).dtype - out_dtype = str(out_dtype).strip().split(".")[1] + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) x = g.get_node(op.input("X")[0]) - ipt_type = infer_type(x).checked_type - value = op.attr("value") - if not _ty.is_dynamic(ipt_type): - shape = infer_shape(x) - const = np.ones(shape) * value - out = _expr.const(const.astype(out_dtype)) - else: - out = _op.transform.full_like(x, value).astype(out_dtype) + value = _expr.const(op.attr("value"), dtype=dtype) + out = _op.transform.full_like(x, value) g.add_node(op.output("Out")[0], out) @@ -286,16 +577,17 @@ def convert_fill_constant(g, op, block): value = op.attr("value") shape = block.var(op.output("Out")[0]).shape - dtype = block.var(op.output("Out")[0]).dtype - dtype = str(dtype).strip().split(".")[1] - if op.input("ValueTensor"): + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + value = _expr.const(value).astype(dtype) + if "ValueTensor" in op.input_names and op.input("ValueTensor"): shape = g.get_node(op.input("ValueTensor")[0]) - shape = infer_value(shape, g.get_params()).numpy() - if op.input("ShapeTensor"): + shape = _infer_value(shape, g.get_params()) + if "ShapeTensor" in op.input_names and op.input("ShapeTensor"): shape = g.get_node(op.input("ShapeTensor")[0]) - shape = infer_value(shape, g.get_params()).numpy() - value = np.full(shape, value, dtype) - out = _expr.const(value.astype(dtype)).astype(dtype) + shape = _infer_value(shape, g.get_params()) + + out = _op.full(value, shape=shape, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -310,12 +602,25 @@ def convert_gelu(g, op, block): g.add_node(op.output("Out")[0], out) +def convert_hard_shrink(g, op, block): + """Operator converter for hard_shrink.""" + + x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype + threshold = op.attr("threshold") + threshold = _op.const(threshold, dtype) + out = _op.logical_or(x < _op.const(-1.0, dtype) * threshold, x > threshold) + out = _op.cast(out, dtype) * x + g.add_node(op.output("Out")[0], out) + + def convert_hard_sigmoid(g, op, block): """Operator converter for hard_sigmoid.""" slope = op.attr("slope") x = g.get_node(op.input("X")[0]) - out = x * _expr.const(slope) + _expr.const(0.5) + dtype = infer_type(x).checked_type.dtype + out = x * _expr.const(slope, dtype) + _expr.const(0.5, dtype) out = _op.clip(out, 0, 1) g.add_node(op.output("Out")[0], out) @@ -330,12 +635,23 @@ def convert_hard_swish(g, op, block): assert np.isclose(scale, 6.0), "Only support scale==6.0 for PaddlePaddle's hard_swish" assert np.isclose(threshold, 6.0), "Only support threshold==6.0 for PaddlePaddle's hard_swish" x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype out = _op.clip(x, -1 * offset, offset) - out = out / _expr.const(threshold) + _expr.const(0.5) + out = out / _expr.const(threshold, dtype) + _expr.const(0.5, dtype) out = x * out g.add_node(op.output("Out")[0], out) +def convert_hard_tanh(g, op, block): + """Operator converter for hard_tanh.""" + + x = g.get_node(op.input("X")[0]) + t_max = op.attr("t_max") + t_min = op.attr("t_min") + out = _op.tensor.clip(x, t_min, t_max) + g.add_node(op.output("Out")[0], out) + + def convert_layer_norm(g, op, block): """Operator converter for layer_norm.""" @@ -376,16 +692,55 @@ def convert_leaky_relu(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_lookup_table(g, op, block): - """Operator converter for lookup_table_v2.""" +def convert_log1p(g, op, block): + """Operator converter for log1p.""" - indices = g.get_node(op.input("Ids")[0]) - padding_idx = op.attr("padding_idx") - if padding_idx != -1: - g.get_params[op.input("W")[0]][padding_idx] = 0.0 - g.add_node(op.input("W")[0], _expr.const(g.params[op.input("W")[0]])) - weights = g.get_node(op.input("W")[0]) - out = _op.take(weights, indices.astype("int32"), axis=0) + x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype + one = _expr.const(1, dtype=dtype) + out = _op.log(x + one) + g.add_node(op.output("Out")[0], out) + + +def convert_binary_logical_op(g, op, block): + """Operator converter for logical op.""" + + ipt0 = g.get_node(op.input("X")[0]) + ipt1 = g.get_node(op.input("Y")[0]) + op_func = get_relay_op(op.type) + out = op_func(ipt0, ipt1) + g.add_node(op.output("Out")[0], out) + + +def convert_logical_not(g, op, block): + """Operator converter for logical_not op.""" + + ipt0 = g.get_node(op.input("X")[0]) + op_func = get_relay_op(op.type) + out = op_func(ipt0) + g.add_node(op.output("Out")[0], out) + + +def convert_logsigmoid(g, op, block): + """Operator converter for logsigmoid.""" + + x = g.get_node(op.input("X")[0]) + out = _op.log(_op.tensor.sigmoid(x)) + g.add_node(op.output("Out")[0], out) + + +def convert_logsoftmax(g, op, block): + """Operator converter for logsoftmax.""" + + x = g.get_node(op.input("X")[0]) + axis = op.attr("axis") + ndim = len(infer_shape(x)) + if axis < 0: + axis += ndim + m = _op.max(x, [axis], keepdims=True) + e = _op.exp(x - m) + s = _op.sum(e, [axis], keepdims=True) + out = x - m - _op.log(s) g.add_node(op.output("Out")[0], out) @@ -498,6 +853,16 @@ def flatten_to_nd(x, x_shape, nd=3): g.add_node(op.output("Out")[0], out) +def convert_meshgrid(g, op, block): + """Operator converter for meshgrid.""" + + inputs = op.input("X") + x = [g.get_node(i) for i in inputs] + outs = _op.meshgrid(x, indexing="ij") + for i, out in enumerate(outs): + g.add_node(op.output("Out")[i], out) + + def convert_mul(g, op, block): """Operator converter for mul.""" @@ -505,8 +870,8 @@ def convert_mul(g, op, block): y = g.get_node(op.input("Y")[0]) x_num_col_dims = op.attr("x_num_col_dims") y_num_col_dims = op.attr("y_num_col_dims") - x_shape = shape_of(x) - y_shape = shape_of(y) + x_shape = _op.shape_of(x) + y_shape = _op.shape_of(y) x_dim = infer_shape(x_shape)[0] y_dim = infer_shape(y_shape)[0] if x_num_col_dims < 0: @@ -543,6 +908,37 @@ def convert_mul(g, op, block): g.add_node(op.output("Out")[0], out) +def convert_mv(g, op, block): + """Operator converter for mv.""" + + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Vec")[0]) + y = _op.expand_dims(y, axis=-1) + y = _op.transpose(y) + out = _op.nn.dense(x, y) + out = _op.squeeze(out, axis=[-1]) + g.add_node(op.output("Out")[0], out) + + +def convert_numel(g, op, block): + """Operator converter for numel.""" + + input_x = g.get_node(op.input("Input")[0]) + out = _op.ndarray_size(input_x, dtype="int64") + out = _op.expand_dims(out, axis=0) + g.add_node(op.output("Out")[0], out) + + +def convert_nonzero(g, op, block): + """Operator converter for nonzero.""" + + input_x = g.get_node(op.input("Condition")[0]) + out = _op.transform.argwhere(input_x) + # Paddle NonZero always outputs int64 + out = _op.cast(out, "int64") + g.add_node(op.output("Out")[0], out) + + def convert_pool2d(g, op, block): """Operator converter for pool2d.""" @@ -558,7 +954,7 @@ def convert_pool2d(g, op, block): ksize = [1, 1] input_x = g.get_node(op.input("X")[0]) - in_h, in_w = infer_shape(input_x)[2:] + _, _, in_h, in_w = infer_shape(input_x) op_map = { "avg": "avg_pool2d", @@ -575,8 +971,19 @@ def convert_pool2d(g, op, block): if padding_algorithm == "VALID": paddings = [0, 0] elif padding_algorithm == "SAME": - pad_h = _get_pad_size(in_h, ksize[0], strides[0]) - pad_w = _get_pad_size(in_w, ksize[1], strides[1]) + if strides[0] == 1 and strides[1] == 1: + pad_h = _get_pad_size(0, ksize[0], strides[0]) + pad_w = _get_pad_size(0, ksize[1], strides[1]) + else: + input_shape = shape_of(input_x) + h_w = _op.strided_slice(input_shape, [2], [4]) + try: + in_h, in_w = infer_value(h_w, g.get_params()).numpy().tolist() + except Exception as e: + msg = "The SAME padding algorithm of Conv not support dynamic shape" + raise tvm.error.OpAttributeInvalid(msg) from e + pad_h = _get_pad_size(in_h, ksize[0], strides[0]) + pad_w = _get_pad_size(in_w, ksize[1], strides[1]) paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: @@ -587,6 +994,11 @@ def convert_pool2d(g, op, block): msg = 'Value {} in attribute "padding" of operator Pool2d is not "valid."' raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) + if not isinstance(in_h, _op.Expr) and in_h < ksize[0]: + ksize[0] = in_h + if not isinstance(in_w, _op.Expr) and in_w < ksize[1]: + ksize[1] = in_w + if not adaptive: out = getattr(_op.nn, op_map[pooling_type])( input_x, pool_size=ksize, strides=strides, padding=paddings, ceil_mode=ceil_mode @@ -605,18 +1017,14 @@ def convert_reshape(g, op, block): if input_shape: new_shape = g.get_node(input_shape[0]) elif input_shape_tensor: - tmp_shape = [] + new_shape = [] for shape_name in input_shape_tensor: shape = g.get_node(shape_name) if len(infer_shape(shape)) == 0: shape = _op.reshape(shape, [-1]) - if isinstance(shape, _expr.Constant): - tmp_shape.append(shape) - elif isinstance(shape, _expr.Expr): - tmp_shape.append(shape) - else: - tmp_shape.append(_expr.const(np.array(shape).astype("int64"))) - new_shape = _op.concatenate(tmp_shape, axis=0) + new_shape.append(shape.astype("int64")) + new_shape = _op.concatenate(new_shape, axis=0) + new_shape = _infer_value(new_shape, g.get_params()) else: new_shape = op.attr("shape") out = _op.reshape(data, new_shape) @@ -631,8 +1039,11 @@ def convert_scale(g, op, block): bias_after_scale = op.attr("bias_after_scale") x = g.get_node(op.input("X")[0]) if np.isclose(scale, 1.0) and np.isclose(bias, 0.0): - out = _op.copy(x) + out = x else: + x_dtype = infer_type(x).checked_type.dtype + if x_dtype != "float32": + x = x.astype("float32") if np.isclose(bias, 0.0): out = x * _expr.const(np.array(scale).astype("float32")) elif np.isclose(scale, 1.0): @@ -646,6 +1057,58 @@ def convert_scale(g, op, block): out = (x + _expr.const(np.array(bias).astype("float32"))) * _expr.const( np.array(scale).astype("float32") ) + if x_dtype != "float32": + out = out.astype(x_dtype) + g.add_node(op.output("Out")[0], out) + + +def convert_scatter(g, op, block): + """Operator converter for scatter.""" + + x = g.get_node(op.input("X")[0]) + index = g.get_node(op.input("Ids")[0]) + updates = g.get_node(op.input("Updates")[0]) + overwrite = op.attr("overwrite") + + shape = infer_shape(updates) + ndims = len(shape) + index = _op.expand_dims(index, axis=-1, num_newaxis=ndims - 1) + index = _op.transform.broadcast_to(index, shape) + + if overwrite: + out = _op.scatter(x, index, updates, axis=0) + else: + out = _op.scatter_add(_op.zeros_like(x), index, updates, axis=0) + out += _op.scatter(x, index, _op.zeros_like(updates), axis=0) + g.add_node(op.output("Out")[0], out) + + +def convert_scatter_nd_add(g, op, block): + """Operator converter for scatter_nd_add.""" + + x = g.get_node(op.input("X")[0]) + index = g.get_node(op.input("Index")[0]) + updates = g.get_node(op.input("Updates")[0]) + indices_dim = len(infer_shape(index)) + axes = list(range(indices_dim)) + index = _op.transpose(index, axes[-1:] + axes[:-1]) + out = _op.scatter_nd(x, index, updates, mode="add") + g.add_node(op.output("Out")[0], out) + + +def convert_selu(g, op, block): + """Operator converter for selu.""" + + x = g.get_node(op.input("X")[0]) + dtype = infer_type(x).checked_type.dtype + alpha = _op.const(op.attr("alpha"), dtype) + scale = _op.const(op.attr("scale"), dtype) + out = ( + _expr.const(-1.0, dtype=dtype) + * alpha + * _op.nn.relu(_expr.const(1.0, dtype=dtype) - _op.exp(x)) + ) + out = scale * (out + _op.nn.relu(x)) g.add_node(op.output("Out")[0], out) @@ -660,38 +1123,107 @@ def convert_shape(g, op, block): def convert_slice(g, op, block): """Operator converter for slice.""" - def parameter_process(starts, ends, axes, dshape): - new_axes = [] - new_starts = [] - new_ends = [] - pop_index = 0 - for i in range(max(axes) + 1): - new_axes.append(i) - if i in axes: - new_starts.append(starts[pop_index]) - new_ends.append(ends[pop_index]) - pop_index += 1 - else: - new_starts.append(0) - new_ends.append(dshape[i]) - return new_starts, new_ends, new_axes - data = g.get_node(op.input("Input")[0]) - dshape = infer_shape(data) - starts = op.attr("starts") - ends = op.attr("ends") + dims = len(infer_shape(data)) + axes = op.attr("axes") + indices = _expr.const(axes, dtype="int64") + decrease_axis = op.attr("decrease_axis") - if isinstance(starts, int): - starts = [starts] - if isinstance(ends, int): - ends = [ends] - if isinstance(axes, int): - axes = [axes] if isinstance(decrease_axis, int): decrease_axis = [decrease_axis] - starts, ends, axes = parameter_process(starts, ends, axes, dshape) - out = _op.strided_slice(data, begin=starts, end=ends) + + if op.input("StartsTensor"): + starts = g.get_node(op.input("StartsTensor")[0]) + starts = _infer_value(starts, g.get_params()) + elif op.input("StartsTensorList"): + starts = [] + for start_index in op.input("StartsTensorList"): + start_index = g.get_node(start_index).astype("int64") + starts.append(start_index) + starts = _op.concatenate(starts, axis=0) + starts = _infer_value(starts, g.get_params()) + else: + starts = op.attr("starts") + + if len(axes) < dims: + if isinstance(starts, _expr.Expr): + starts = _op.scatter( + _op.const([0] * dims, dtype=infer_type(starts).checked_type.dtype), + indices, + starts, + axis=0, + ) + else: + base = [0] * dims + for i, axis in enumerate(axes): + base[axis] = starts[i] + starts = base + + if op.input("EndsTensor"): + ends = g.get_node(op.input("EndsTensor")[0]) + ends = _infer_value(ends, g.get_params()) + elif op.input("EndsTensorList"): + ends = [] + for end_index in op.input("EndsTensorList"): + end_index = g.get_node(end_index).astype("int64") + ends.append(end_index) + ends = _op.concatenate(ends, axis=0) + ends = _infer_value(ends, g.get_params()) + else: + ends = op.attr("ends") + + if len(axes) < dims: + if isinstance(ends, _expr.Expr): + ends = _op.scatter( + _expr.const( + np.array([np.iinfo(np.int32).max] * dims), + dtype=infer_type(ends).checked_type.dtype, + ), + indices, + ends, + axis=0, + ) + else: + base = [np.iinfo(np.int32).max] * dims + for i, axis in enumerate(axes): + base[axis] = ends[i] + ends = base + + strides = None + if "StridesTensor" in op.input_names and op.input("StridesTensor"): + strides = g.get_node(op.input("StridesTensor")[0]) + strides = _infer_value(strides, g.get_params()) + elif "StridesTensorList" in op.input_names and op.input("StridesTensorList"): + strides = [] + for strides_index in op.input("StridesTensorList"): + strides_index = g.get_node(strides_index).astype("int64") + strides.append(strides_index) + strides = _op.concatenate(strides, axis=0) + strides = _infer_value(strides, g.get_params()) + elif op.has_attr("strides"): + strides = op.attr("strides") + + if len(axes) < dims: + if isinstance(strides, _expr.Expr): + strides = _op.scatter( + _expr.const( + np.array([1] * dims), + dtype=infer_type(strides).checked_type.dtype, + ), + indices, + strides, + axis=0, + ) + elif strides: + base = [1] * dims + for i, axis in enumerate(axes): + base[axis] = strides[i] + strides = base + if not strides: + strides = _op.const([1] * dims, dtype="int64") + + out = _op.strided_slice(data, begin=starts, end=ends, strides=strides) if decrease_axis: out = _op.squeeze(out, axis=decrease_axis) g.add_node(op.output("Out")[0], out) @@ -722,41 +1254,105 @@ def convert_unsqueeze(g, op, block): _convert_map = { + "abs": convert_unary_op, + "acos": convert_unary_op, + "addmm": convert_addmm, "arg_max": convert_arg_max, + "arg_min": convert_arg_min, + "argsort": convert_argsort, + "asin": convert_unary_op, "assign": convert_assign, + "assign_value": convert_assign_value, + "atan": convert_unary_op, "batch_norm": convert_batch_norm, + "bmm": convert_bmm, + "brelu": convert_hard_tanh, "cast": convert_cast, + "ceil": convert_unary_op, + "clip": convert_clip, "concat": convert_concat, "conv2d": convert_conv2d, + "cos": convert_unary_op, + "cosh": convert_unary_op, + "crop_tensor": convert_crop, "cumsum": convert_cumsum, "depthwise_conv2d": convert_conv2d, + "dist": convert_dist, + "dot": convert_dot, "dropout": convert_dropout, "elementwise_add": convert_elementwise_op, "elementwise_div": convert_elementwise_op, "elementwise_mul": convert_elementwise_op, "elementwise_sub": convert_elementwise_op, - "equal": convert_equal, - "exp": convert_activation, + "elementwise_mod": convert_elementwise_op, + "elementwise_max": convert_elementwise_op, + "elementwise_min": convert_elementwise_op, + "elementwise_pow": convert_elementwise_op, + "elementwise_floordiv": convert_elementwise_op, + "elu": convert_elu, + "equal": convert_elementwise_op, + "erf": convert_unary_op, + "exp": convert_unary_op, + "expand_v2": convert_expand, + "expand_as_v2": convert_expand_as, "feed": convert_feed, "fill_any_like": convert_fill_any_like, "fill_constant": convert_fill_constant, + "floor": convert_unary_op, + "floor_mod": convert_elementwise_op, "gelu": convert_gelu, + "greater_equal": convert_elementwise_op, + "greater_than": convert_elementwise_op, + "hard_shrink": convert_hard_shrink, "hard_sigmoid": convert_hard_sigmoid, "hard_swish": convert_hard_swish, + "isfinite": convert_unary_op, + "isfinite_v2": convert_unary_op, + "isinf": convert_unary_op, + "isinf_v2": convert_unary_op, + "isnan": convert_unary_op, + "isnan_v2": convert_unary_op, "layer_norm": convert_layer_norm, "leaky_relu": convert_leaky_relu, - "lookup_table_v2": convert_lookup_table, + "less_equal": convert_elementwise_op, + "less_than": convert_elementwise_op, + "log": convert_unary_op, + "log2": convert_unary_op, + "log10": convert_unary_op, + "log1p": convert_log1p, + "logical_and": convert_binary_logical_op, + "logical_not": convert_logical_not, + "logical_or": convert_binary_logical_op, + "logical_xor": convert_binary_logical_op, + "logsigmoid": convert_logsigmoid, + "log_softmax": convert_logsoftmax, "matmul": convert_matmul, "matmul_v2": convert_matmul, + "meshgrid": convert_meshgrid, + "mv": convert_mv, "mul": convert_mul, + "not_equal": convert_elementwise_op, "pool2d": convert_pool2d, - "relu": convert_activation, + "relu": convert_unary_op, "reshape2": convert_reshape, + "round": convert_unary_op, + "rsqrt": convert_unary_op, "scale": convert_scale, + "scatter": convert_scatter, + "scatter_nd_add": convert_scatter_nd_add, + "selu": convert_selu, "shape": convert_shape, + "sigmoid": convert_unary_op, + "sign": convert_unary_op, + "sin": convert_unary_op, + "sinh": convert_unary_op, + "size": convert_numel, "slice": convert_slice, "softmax": convert_softmax, - "tanh": convert_activation, + "sqrt": convert_unary_op, + "strided_slice": convert_slice, + "tan": convert_unary_op, + "tanh": convert_unary_op, "unsqueeze2": convert_unsqueeze, } @@ -764,21 +1360,24 @@ def convert_unsqueeze(g, op, block): class GraphProto: """A helper class for handling relay functions from PaddlePaddle model.""" - def __init__(self): + def __init__(self, freeze_params=False): self.nodes = {} self.params = {} self.shape_dict = None + self.freeze_params = freeze_params def get_node(self, name): """get node from graph""" - assert name in self.nodes + assert name in self.nodes, "Node: {} not found".format(name) return self.nodes[name] def add_node(self, name, node): """add a node to graph""" - - self.nodes[name] = fold_constant(node) + if self.shape_dict: + self.nodes[name] = fold_constant(node) + else: + self.nodes[name] = node def get_params(self, name=None): """get params from graph""" @@ -788,6 +1387,11 @@ def get_params(self, name=None): assert name in self.params return self.params[name] + def set_params(self, params): + """set params for graph""" + + self.params = params + def extract_parameters(self, program, scope=None): """Extract all the weights from PaddlePaddle program.""" @@ -803,20 +1407,12 @@ def extract_parameters(self, program, scope=None): self.params[name] = scope[name] else: self.params[name] = np.array(scope.var(name).get_tensor()) - self.nodes[name] = _expr.const(self.params[name]) - - def check_input_shape(self, op, block): - """Check the shape information of model's inputs, fixed shape is recommended.""" - - ipt_name = op.input(op.input_names[0]) - ipt_shape = block.var(ipt_name).shape - for i in ipt_shape: - if i < 0: - warning_msg = "Input {}(shape={}) has unkown dimension shapes. \ - Specifying static values may improve performance".format( - ipt_name, ipt_shape + if self.freeze_params: + self.nodes[name] = _expr.const(self.params[name]) + else: + self.nodes[name] = _expr.var( + name, shape=self.params[name].shape, dtype=str(self.params[name].dtype) ) - warnings.warn(warning_msg) def check_unsupported_ops(self, program): """Check whether all the operators are supported.""" @@ -839,12 +1435,12 @@ def ops_to_relay(self, program, input_specs=None): if input_specs is not None: for input_spec in input_specs: convert_feed(self, input_spec, None) - for block in program.blocks: - for op in block.ops: - if op.type == "fetch": - continue - convert_func = _convert_map[op.type] - convert_func(self, op, block) + global_block = program.blocks[0] + for op in global_block.ops: + if op.type == "fetch": + continue + convert_func = _convert_map[op.type] + convert_func(self, op, global_block) def from_program(self, program, shape_dict, scope): """Construct the TVM relay expression from PaddlePaddle program.""" @@ -864,12 +1460,14 @@ def from_program(self, program, shape_dict, scope): if op.type == "fetch": output_names.append(op.input("X")[0]) - outputs = [self.nodes[name] for name in output_names] + outputs = [self.get_node(name) for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) free_vars = analysis.free_vars(outputs) func = _function.Function(free_vars, outputs) mod = IRModule.from_expr(func) + if self.freeze_params: + self.params = {} return mod, self.params def from_translated_layer(self, layer, shape_dict): @@ -888,25 +1486,27 @@ def from_translated_layer(self, layer, shape_dict): output_names = [x.name for x in layer._output_spec()] - outputs = [self.nodes[name] for name in output_names] + outputs = [self.get_node(name) for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) free_vars = analysis.free_vars(outputs) func = _function.Function(free_vars, outputs) mod = IRModule.from_expr(func) + if self.freeze_params: + self.params = {} return mod, self.params -def from_paddle(program_or_layer, shape_dict=None, scope=None): +def from_paddle(program_or_layer, shape_dict=None, scope=None, freeze_params=False): """Convert a PaddlePaddle model into an equivalent Relay Function. - - PaddlePaddle Program/TranslatedLayer represent the computation graph of PaddlePaddle model, - and PaddlePaddle scope stores all the weights of PaddlePaddle model. + PaddlePaddle Program/TranslatedLayer represent the computation + graph of PaddlePaddle model, and PaddlePaddle scope stores all the + weights of PaddlePaddle model. """ import paddle - g = GraphProto() + g = GraphProto(freeze_params) if isinstance(program_or_layer, paddle.jit.TranslatedLayer): # model is loaded by `paddle.jit.load` mod, params = g.from_translated_layer(program_or_layer, shape_dict) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index db07e07f9d83..b4fe5259e63e 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -650,7 +650,6 @@ def tanh(inputs): test_forward_hard_swish() test_forward_layer_norm() test_forward_leaky_relu() - test_forward_look_up() test_forward_multiply() test_forward_matmul() test_forward_pool2d() From 39b96fca5f7d6ec6b242737008c3c32ffe303c66 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Sun, 26 Sep 2021 11:54:24 +0000 Subject: [PATCH 02/24] remove part of operators --- python/tvm/relay/frontend/paddlepaddle.py | 172 +--------------------- 1 file changed, 3 insertions(+), 169 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index b404bab1dbd9..ca8975a154c9 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -118,8 +118,9 @@ def _convert_dtype_value(val): def convert_unary_op(g, op, block): - """Operator converter for all the activation.""" + """Operator converter for all the unary operators.""" + # op_map stores mapping relationship between tvm and relay op_map = { "isinf_v2": _op.isinf, "isfinite_v2": _op.isfinite, @@ -128,6 +129,7 @@ def convert_unary_op(g, op, block): if op.type in op_map: unary_func = op_map[op.type] else: + # while paddle operator's name is same with relay unary_func = get_relay_op(op.type) out = unary_func(g.get_node(op.input("X")[0])) g.add_node(op.output("Out")[0], out) @@ -275,45 +277,6 @@ def convert_cast(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_clip(g, op, block): - """Operator converter for clip.""" - - x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - is_dynamic = False - if op.input("Min"): - min_value = g.get_node(op.input("Min")[0]) - min_value = _infer_value(min_value, g.get_params()) - if isinstance(min_value, _expr.Expr): - is_dynamic = True - else: - min_value = min_value[0] - else: - min_value = op.attr("min") - if op.input("Max"): - max_value = g.get_node(op.input("Max")[0]) - max_value = _infer_value(max_value, g.get_params()) - if isinstance(max_value, _expr.Expr): - if not is_dynamic: - is_dynamic = True - min_value = _op.const(min_value, dtype) - else: - max_value = max_value[0] - if is_dynamic: - max_value = _op.const(max_value, dtype) - else: - max_value = op.attr("max") - if is_dynamic: - max_value = _op.const(max_value, dtype) - - if not is_dynamic: - out = _op.clip(x, min_value, max_value) - else: - out = _op.maximum(x, min_value) - out = _op.minimum(out, max_value) - g.add_node(op.output("Out")[0], out) - - def convert_concat(g, op, block): """Operator converter for concat.""" @@ -375,37 +338,6 @@ def convert_conv2d(g, op, block): g.add_node(op.output("Output")[0], out) -def convert_crop(g, op, block): - """Operator converter for crop.""" - - x = g.get_node(op.input("X")[0]) - dims = len(infer_shape(x)) - input_shape = op.input("Shape") - input_offsets = op.input("Offsets") - if input_shape: - shape = g.get_node(input_shape[0]) - shape = _infer_value(shape, g.get_params()) - else: - shape = op.attr("shape") - - if input_offsets: - offsets = g.get_node(input_offsets[0]) - offsets = _infer_value(offsets, g.get_params()) - else: - offsets = op.attr("offsets") - - if not isinstance(shape, _expr.Expr): - shape = _op.const(shape, "int32") - if not isinstance(offsets, _expr.Expr): - offsets = _op.const(offsets, "int32") - slice_start = offsets - slice_end = _op.add(shape, offsets) - strides = _op.const([1] * dims, dtype="int32") - - out = _op.strided_slice(x, slice_start, slice_end, strides) - g.add_node(op.output("Out")[0], out) - - def convert_cumsum(g, op, block): """Operator converter for cumsum.""" @@ -433,41 +365,6 @@ def convert_dropout(g, op, block): g.add_node(op.output("Out")[0], x) -def convert_elu(g, op, block): - """Operator converter for elu.""" - - x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - alpha = op.attr("alpha") - alpha = _expr.const(-1.0 * alpha, dtype=dtype) - out = alpha * _op.nn.relu(_expr.const(1, dtype=dtype) - _op.exp(x)) + _op.nn.relu(x) - g.add_node(op.output("Out")[0], out) - - -def convert_dist(g, op, block): - """Operator converter for dist.""" - - x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Y")[0]) - dtype = infer_type(x).checked_type.dtype - p = op.attr("p") - - x -= y - if p == np.inf: - out = _op.reduce.max(_op.abs(x)) - elif p == np.NINF: - out = _op.reduce.min(_op.abs(x)) - else: - reci_order = _expr.const(1.0 / p, dtype=dtype) - p = _expr.const(p) - out = _op.power( - _op.reduce.sum(_op.power(_op.abs(x), p)), - reci_order, - ) - out = _op.expand_dims(out, axis=0) - g.add_node(op.output("Out")[0], out) - - def convert_dot(g, op, block): """Operator converter for dot.""" @@ -908,18 +805,6 @@ def convert_mul(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_mv(g, op, block): - """Operator converter for mv.""" - - x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Vec")[0]) - y = _op.expand_dims(y, axis=-1) - y = _op.transpose(y) - out = _op.nn.dense(x, y) - out = _op.squeeze(out, axis=[-1]) - g.add_node(op.output("Out")[0], out) - - def convert_numel(g, op, block): """Operator converter for numel.""" @@ -929,16 +814,6 @@ def convert_numel(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_nonzero(g, op, block): - """Operator converter for nonzero.""" - - input_x = g.get_node(op.input("Condition")[0]) - out = _op.transform.argwhere(input_x) - # Paddle NonZero always outputs int64 - out = _op.cast(out, "int64") - g.add_node(op.output("Out")[0], out) - - def convert_pool2d(g, op, block): """Operator converter for pool2d.""" @@ -1062,40 +937,6 @@ def convert_scale(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_scatter(g, op, block): - """Operator converter for scatter.""" - - x = g.get_node(op.input("X")[0]) - index = g.get_node(op.input("Ids")[0]) - updates = g.get_node(op.input("Updates")[0]) - overwrite = op.attr("overwrite") - - shape = infer_shape(updates) - ndims = len(shape) - index = _op.expand_dims(index, axis=-1, num_newaxis=ndims - 1) - index = _op.transform.broadcast_to(index, shape) - - if overwrite: - out = _op.scatter(x, index, updates, axis=0) - else: - out = _op.scatter_add(_op.zeros_like(x), index, updates, axis=0) - out += _op.scatter(x, index, _op.zeros_like(updates), axis=0) - g.add_node(op.output("Out")[0], out) - - -def convert_scatter_nd_add(g, op, block): - """Operator converter for scatter_nd_add.""" - - x = g.get_node(op.input("X")[0]) - index = g.get_node(op.input("Index")[0]) - updates = g.get_node(op.input("Updates")[0]) - indices_dim = len(infer_shape(index)) - axes = list(range(indices_dim)) - index = _op.transpose(index, axes[-1:] + axes[:-1]) - out = _op.scatter_nd(x, index, updates, mode="add") - g.add_node(op.output("Out")[0], out) - - def convert_selu(g, op, block): """Operator converter for selu.""" @@ -1269,15 +1110,12 @@ def convert_unsqueeze(g, op, block): "brelu": convert_hard_tanh, "cast": convert_cast, "ceil": convert_unary_op, - "clip": convert_clip, "concat": convert_concat, "conv2d": convert_conv2d, "cos": convert_unary_op, "cosh": convert_unary_op, - "crop_tensor": convert_crop, "cumsum": convert_cumsum, "depthwise_conv2d": convert_conv2d, - "dist": convert_dist, "dot": convert_dot, "dropout": convert_dropout, "elementwise_add": convert_elementwise_op, @@ -1289,7 +1127,6 @@ def convert_unsqueeze(g, op, block): "elementwise_min": convert_elementwise_op, "elementwise_pow": convert_elementwise_op, "elementwise_floordiv": convert_elementwise_op, - "elu": convert_elu, "equal": convert_elementwise_op, "erf": convert_unary_op, "exp": convert_unary_op, @@ -1329,7 +1166,6 @@ def convert_unsqueeze(g, op, block): "matmul": convert_matmul, "matmul_v2": convert_matmul, "meshgrid": convert_meshgrid, - "mv": convert_mv, "mul": convert_mul, "not_equal": convert_elementwise_op, "pool2d": convert_pool2d, @@ -1338,8 +1174,6 @@ def convert_unsqueeze(g, op, block): "round": convert_unary_op, "rsqrt": convert_unary_op, "scale": convert_scale, - "scatter": convert_scatter, - "scatter_nd_add": convert_scatter_nd_add, "selu": convert_selu, "shape": convert_shape, "sigmoid": convert_unary_op, From 50e3c41f380c3c6e6e7f101c244df67cd1d89603 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Sun, 26 Sep 2021 12:05:49 +0000 Subject: [PATCH 03/24] add lookup --- python/tvm/relay/frontend/paddlepaddle.py | 109 ++++++---------------- 1 file changed, 28 insertions(+), 81 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index ca8975a154c9..a1b928ef6b57 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -135,29 +135,13 @@ def convert_unary_op(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_addmm(g, op, block): - """Operator converter for addmm.""" - - input_x = g.get_node(op.input("Input")[0]) - x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Y")[0]) - - alpha = op.attr("Alpha") - beta = op.attr("Beta") - dtype = block.var(op.output("Out")[0]).dtype - dtype = str(dtype).strip().split(".")[1] - - if not isinstance(alpha, _expr.Expr) and alpha != 1: - alpha = _expr.const(alpha, dtype) - x *= alpha - - if not isinstance(beta, _expr.Expr) and beta != 1: - beta = _expr.const(beta, dtype) - input_x *= beta +def convert_binary_logical_op(g, op, block): + """Operator converter for logical op.""" - transposed_y = _op.transpose(y, axes=[1, 0]) - dense_out = _op.nn.dense(x, transposed_y) - out = dense_out + input_x + ipt0 = g.get_node(op.input("X")[0]) + ipt1 = g.get_node(op.input("Y")[0]) + op_func = get_relay_op(op.type) + out = op_func(ipt0, ipt1) g.add_node(op.output("Out")[0], out) @@ -257,16 +241,6 @@ def convert_batch_norm(g, op, block): g.add_node(op.output("Y")[0], out[0]) -def convert_bmm(g, op, block): - """Operator converter for bmm.""" - - x = g.get_node(op.input("X")[0]) - y = g.get_node(op.input("Y")[0]) - y = _op.transpose(y, [0, 2, 1]) - out = _op.nn.batch_matmul(x, y) - g.add_node(op.output("Out")[0], out) - - def convert_cast(g, op, block): """Operator converter for cast.""" @@ -499,18 +473,6 @@ def convert_gelu(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_hard_shrink(g, op, block): - """Operator converter for hard_shrink.""" - - x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - threshold = op.attr("threshold") - threshold = _op.const(threshold, dtype) - out = _op.logical_or(x < _op.const(-1.0, dtype) * threshold, x > threshold) - out = _op.cast(out, dtype) * x - g.add_node(op.output("Out")[0], out) - - def convert_hard_sigmoid(g, op, block): """Operator converter for hard_sigmoid.""" @@ -539,16 +501,6 @@ def convert_hard_swish(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_hard_tanh(g, op, block): - """Operator converter for hard_tanh.""" - - x = g.get_node(op.input("X")[0]) - t_max = op.attr("t_max") - t_min = op.attr("t_min") - out = _op.tensor.clip(x, t_min, t_max) - g.add_node(op.output("Out")[0], out) - - def convert_layer_norm(g, op, block): """Operator converter for layer_norm.""" @@ -599,13 +551,27 @@ def convert_log1p(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_binary_logical_op(g, op, block): - """Operator converter for logical op.""" +def convert_lookup_table(g, op, block): + """Operator converter for lookup_table_v2.""" - ipt0 = g.get_node(op.input("X")[0]) - ipt1 = g.get_node(op.input("Y")[0]) - op_func = get_relay_op(op.type) - out = op_func(ipt0, ipt1) + indices = g.get_node(op.input("Ids")[0]) + padding_idx = op.attr("padding_idx") + weights = g.get_node(op.input("W")[0]) + if padding_idx != -1: + if op.input("W")[0] in g.get_params(): + weights = g.get_params(op.input("W")[0]) + weights[padding_idx] = 0.0 + weights = _expr.const(weights) + else: + shape = _infer_value(shape_of(weights), g.get_params()) + assert not isinstance( + shape, _expr.Expr + ), "Shape of weight has to be fixed for PaddlePaddle's lookup_table" + filters = np.ones(shape).astype(infer_type(weights).checked_type.dtype) + filters[padding_idx] = 0.0 + filters = _expr.const(filters) + weights = weights * filters + out = _op.take(weights, indices.astype("int32"), axis=0) g.add_node(op.output("Out")[0], out) @@ -937,22 +903,6 @@ def convert_scale(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_selu(g, op, block): - """Operator converter for selu.""" - - x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - alpha = _op.const(op.attr("alpha"), dtype) - scale = _op.const(op.attr("scale"), dtype) - out = ( - _expr.const(-1.0, dtype=dtype) - * alpha - * _op.nn.relu(_expr.const(1.0, dtype=dtype) - _op.exp(x)) - ) - out = scale * (out + _op.nn.relu(x)) - g.add_node(op.output("Out")[0], out) - - def convert_shape(g, op, block): """Operator converter for shape.""" @@ -1097,7 +1047,6 @@ def convert_unsqueeze(g, op, block): _convert_map = { "abs": convert_unary_op, "acos": convert_unary_op, - "addmm": convert_addmm, "arg_max": convert_arg_max, "arg_min": convert_arg_min, "argsort": convert_argsort, @@ -1106,8 +1055,6 @@ def convert_unsqueeze(g, op, block): "assign_value": convert_assign_value, "atan": convert_unary_op, "batch_norm": convert_batch_norm, - "bmm": convert_bmm, - "brelu": convert_hard_tanh, "cast": convert_cast, "ceil": convert_unary_op, "concat": convert_concat, @@ -1140,7 +1087,6 @@ def convert_unsqueeze(g, op, block): "gelu": convert_gelu, "greater_equal": convert_elementwise_op, "greater_than": convert_elementwise_op, - "hard_shrink": convert_hard_shrink, "hard_sigmoid": convert_hard_sigmoid, "hard_swish": convert_hard_swish, "isfinite": convert_unary_op, @@ -1163,6 +1109,8 @@ def convert_unsqueeze(g, op, block): "logical_xor": convert_binary_logical_op, "logsigmoid": convert_logsigmoid, "log_softmax": convert_logsoftmax, + "lookup_table": convert_lookup_table, + "lookup_table_v2": convert_lookup_table, "matmul": convert_matmul, "matmul_v2": convert_matmul, "meshgrid": convert_meshgrid, @@ -1174,7 +1122,6 @@ def convert_unsqueeze(g, op, block): "round": convert_unary_op, "rsqrt": convert_unary_op, "scale": convert_scale, - "selu": convert_selu, "shape": convert_shape, "sigmoid": convert_unary_op, "sign": convert_unary_op, From 555406ea3d9058d7fc4317c20dc0e29b6a1f67f6 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Sun, 26 Sep 2021 13:02:07 +0000 Subject: [PATCH 04/24] add test --- python/tvm/relay/frontend/paddlepaddle.py | 26 +- .../frontend/paddlepaddle/test_forward.py | 622 ++++++++++++++---- 2 files changed, 506 insertions(+), 142 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index a1b928ef6b57..41f4bf5a5b17 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -432,17 +432,6 @@ def convert_feed(g, op, block): g.add_node(ipt_name, out) -def convert_fill_any_like(g, op, block): - """Operator converter for fill_any_like.""" - - dtype = op.attr("dtype") - dtype = _convert_dtype_value(dtype) - x = g.get_node(op.input("X")[0]) - value = _expr.const(op.attr("value"), dtype=dtype) - out = _op.transform.full_like(x, value) - g.add_node(op.output("Out")[0], out) - - def convert_fill_constant(g, op, block): """Operator converter for fill_constant.""" @@ -462,6 +451,17 @@ def convert_fill_constant(g, op, block): g.add_node(op.output("Out")[0], out) +def convert_fill_any_like(g, op, block): + """Operator converter for fill_any_like.""" + + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + x = g.get_node(op.input("X")[0]) + value = _expr.const(op.attr("value"), dtype=dtype) + out = _op.transform.full_like(x, value).astype(dtype) + g.add_node(op.output("Out")[0], out) + + def convert_gelu(g, op, block): """Operator converter for gelu.""" @@ -1109,8 +1109,8 @@ def convert_unsqueeze(g, op, block): "logical_xor": convert_binary_logical_op, "logsigmoid": convert_logsigmoid, "log_softmax": convert_logsoftmax, - "lookup_table": convert_lookup_table, - "lookup_table_v2": convert_lookup_table, + "lookup_table": convert_lookup_table, + "lookup_table_v2": convert_lookup_table, "matmul": convert_matmul, "matmul_v2": convert_matmul, "meshgrid": convert_meshgrid, diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index b4fe5259e63e..a384f2acb53a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -14,19 +14,21 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -import os from pathlib import Path import shutil import numpy as np + +import paddle +from paddle.framework import dtype +import paddle.nn as nn + import tvm import tvm.testing import tvm.topi.testing from tvm import relay from tvm.contrib import graph_executor -import paddle -import paddle.nn as nn PADDLE_TEST_DATA_ROOT_PATH = Path(Path("~").expanduser(), ".tvm_test_data", "paddle") PADDLE_TEST_DATA_ROOT_PATH.mkdir(parents=True, exist_ok=True) @@ -34,14 +36,16 @@ def assert_shapes_match(tru, est): if tru.shape != est.shape: - msg = "Output shapes {} and {} don't match" + msg = "Paddle Output shapes {} and TVM shapes {} don't match" raise AssertionError(msg.format(tru.shape, est.shape)) + if tru.dtype != est.dtype: + msg = "Paddle Output dtype {} and TVM dtype {} don't match" + raise AssertionError(msg.format(tru.dtype, est.dtype)) def get_paddle_model(func, input_spec): global PADDLE_TEST_DATA_ROOT_PATH model_path = Path(PADDLE_TEST_DATA_ROOT_PATH, "model") - paddle.jit.save(func, str(model_path), input_spec=input_spec) baseline_model = paddle.jit.load(str(model_path)) @@ -49,7 +53,34 @@ def get_paddle_model(func, input_spec): return baseline_model -def verify_model(func, input_data, rtol=1e-5, atol=1e-5): +def get_tvm_output_with_vm(mod, params, target, device, input_data): + """Generic function to execute and get tvm output with vm executor""" + + ex = relay.create_executor("vm", mod=mod, device=device, target=target) + params.update(input_data) + result = ex.evaluate()(**params) + if isinstance(result, tvm.runtime.NDArray): + return [ + result.numpy(), + ] + return [r.numpy() for r in result] + + +def get_tvm_output(mod, params, target, device, input_data, compiled_names, num): + """Generic function to execute and get tvm output""" + + lib = relay.build(mod, target=target, params=params) + gmod = graph_executor.GraphModule(lib["default"](device)) + for name in compiled_names: + gmod.set_input(name, input_data[name]) + gmod.run() + outputs = [] + for i in range(num): + outputs.append(gmod.get_output(i).numpy()) + return outputs + + +def verify_model(func, input_data, rtol=1e-5, atol=1e-5, input_shape=None): if not (isinstance(input_data, (tuple, list))): input_data = [input_data] @@ -59,11 +90,13 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): compiled_input = {} for idx, data in enumerate(input_data): input_name = "input{}".format(idx) - input_spec.append( - paddle.static.InputSpec(dtype=data.dtype, shape=data.shape, name=input_name) - ) + if input_shape: + shape = input_shape[idx] + else: + shape = data.shape + input_shape_dict[input_name] = shape + input_spec.append(paddle.static.InputSpec(dtype=data.dtype, shape=shape, name=input_name)) input_names.append(input_name) - input_shape_dict[input_name] = data.shape if isinstance(data, np.ndarray): compiled_input[input_name] = data else: @@ -81,25 +114,72 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): mod, params = relay.frontend.from_paddle(baseline_model, input_shape_dict) parms_num = min(len(input_names), len(mod["main"].params)) compiled_names = [] - for arg in mod["main"].params[:parms_num]: - assert arg.name_hint in input_names - compiled_names.append(arg.name_hint) + for arg in mod["main"].params: + assert arg.name_hint in input_names or arg.name_hint in params + if arg.name_hint in input_names: + compiled_names.append(arg.name_hint) with tvm.transform.PassContext(opt_level=3): for target, dev in tvm.testing.enabled_targets(): - lib = relay.build(mod, target=target, params=params) - gmod = graph_executor.GraphModule(lib["default"](dev)) - for name in compiled_names: - gmod.set_input(name, compiled_input[name]) - gmod.run() - - for i, baseline_output in enumerate(baseline_outputs): - compiled_output = gmod.get_output(i).numpy() - + if input_shape: + tvm_output = get_tvm_output_with_vm(mod, params, target, dev, compiled_input) + else: + tvm_output = get_tvm_output( + mod, params, target, dev, compiled_input, compiled_names, len(baseline_outputs) + ) + + for baseline_output, compiled_output in zip(baseline_outputs, tvm_output): assert_shapes_match(baseline_output, compiled_output) tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) +@tvm.testing.uses_gpu +def test_forward_math(): + class MathOp(nn.Layer): + def __init__(self, op_name): + super(MathOp, self).__init__() + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, op_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, inputs): + return self.func(inputs) + + input_data = paddle.rand([1, 2, 5, 5], dtype="float32") + op_list = [ + "abs", + "acos", + "asin", + "atan", + "ceil", + "cos", + "cosh", + "erf", + "exp", + "floor", + "log", + "log2", + "log10", + "log1p", + "numel", + "relu", + "round", + "rsqrt", + "sigmoid", + "sign", + "rsqrt", + "sin", + "sinh", + "sqrt", + "tan", + "tanh", + ] + for op_name in op_list: + verify_model(MathOp(op_name), input_data) + + @tvm.testing.uses_gpu def test_forward_add_subtract(): input_shape = [10] @@ -156,26 +236,65 @@ def forward(self, inputs): @tvm.testing.uses_gpu -def test_forward_assign(): +def test_forward_argmin(): + input_shape = [1, 3, 10, 10] + + class ArgMin(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argmin(inputs) + + class ArgMin1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=1) + + class ArgMin2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=1, keepdim=False) + + class ArgMin3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=2, keepdim=True) + + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMin(), input_data=input_data) + verify_model(ArgMin1(), input_data=input_data) + verify_model(ArgMin2(), input_data=input_data) + verify_model(ArgMin3(), input_data=input_data) + + +@tvm.testing.uses_gpu +def test_forward_argsort(): + @paddle.jit.to_static + def argsort(inputs): + return paddle.argsort(inputs) + @paddle.jit.to_static - def assign(inputs): - return paddle.assign(inputs) + def argsort2(inputs): + return paddle.argsort(inputs, axis=0, descending=True) + + input_shape = [2, 3, 5] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(argsort, input_data) + input_data2 = np.random.randint(100, size=input_shape) + verify_model(argsort2, input_data2) + + +@tvm.testing.uses_gpu +def test_forward_assign(): + class Assign(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.assign(inputs) input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") - verify_model( - assign, - [ - input_data, - ], - ) + verify_model(Assign(), [input_data]) input_data2 = np.random.randint(100, size=input_shape) - verify_model( - assign, - [ - input_data2, - ], - ) + verify_model(Assign(), [input_data2], input_shape=[[-1, -1]]) @tvm.testing.uses_gpu @@ -227,18 +346,8 @@ def cast2(inputs, dtype="int64"): input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") * 100 - verify_model( - cast1, - [ - input_data, - ], - ) - verify_model( - cast2, - [ - input_data, - ], - ) + verify_model(cast1, [input_data]) + verify_model(cast2, [input_data]) @tvm.testing.uses_gpu @@ -262,39 +371,30 @@ def concat_unsqueeze2(inputs): @tvm.testing.uses_gpu def test_forward_cumsum(): - @paddle.jit.to_static - def cusum1(inputs): - return paddle.cumsum(inputs) + class Cumsum1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cumsum(inputs) - @paddle.jit.to_static - def cusum2(inputs): - return paddle.cumsum(inputs, axis=0) + class Cumsum2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cumsum(inputs, axis=0) - @paddle.jit.to_static - def cusum3(inputs): - return paddle.cumsum(inputs, axis=1) + class Cumsum3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cumsum(inputs, axis=1) input_data = paddle.randint(0, 100, (10, 10), dtype=paddle.int32) - verify_model(cusum1, [input_data]) - verify_model(cusum1, [input_data.astype(paddle.int64)]) - verify_model( - cusum2, - [ - input_data, - ], - ) - verify_model( - cusum3, - [ - input_data, - ], - ) + verify_model(Cumsum1(), input_data) + verify_model(Cumsum1(), [input_data.astype(paddle.int64)]) + verify_model(Cumsum2(), input_data) + verify_model(Cumsum3(), input_data) @tvm.testing.uses_gpu def test_forward_conv(): - conv2d_input_shape = [1, 3, 10, 10] - class Conv2D1(nn.Layer): def __init__(self): super(Conv2D1, self).__init__() @@ -315,9 +415,46 @@ def __init__(self): def forward(self, inputs): return self.softmax(self.conv(inputs)) + class Conv2D3(nn.Layer): + def __init__(self): + super(Conv2D3, self).__init__() + self.conv = nn.Conv2D(3, 6, 7, groups=3, bias_attr=False, padding="SAME") + + @paddle.jit.to_static + def forward(self, inputs): + return self.conv(inputs) + + class Conv2D4(nn.Layer): + def __init__(self): + super(Conv2D4, self).__init__() + self.conv = nn.Conv2D( + 3, 6, 7, groups=3, bias_attr=False, padding=[1, 2, 0, 1], stride=2, dilation=2 + ) + + @paddle.jit.to_static + def forward(self, inputs): + return self.conv(inputs) + + conv2d_input_shape = [1, 3, 112, 112] conv2d_input_data = paddle.rand(conv2d_input_shape, dtype="float32") verify_model(Conv2D1(), input_data=conv2d_input_data) verify_model(Conv2D2(), input_data=conv2d_input_data) + verify_model(Conv2D3(), input_data=conv2d_input_data) + verify_model(Conv2D4(), input_data=conv2d_input_data) + verify_model(Conv2D1(), conv2d_input_data, input_shape=[[-1, 3, 112, 112]]) + + +@tvm.testing.uses_gpu +def test_forward_dot(): + @paddle.jit.to_static + def dot(x, y): + return paddle.dot(x, y) + + x_shape = [10, 3] + y_shape = [10, 3] + x_data = paddle.rand(x_shape, dtype="float32") + y_data = paddle.rand(y_shape, dtype="float32") + verify_model(dot, input_data=[x_data, y_data]) @tvm.testing.uses_gpu @@ -332,19 +469,55 @@ def dropout(inputs): verify_model(dropout, input_data=input_data) +@tvm.testing.uses_gpu +def test_forward_expand(): + @paddle.jit.to_static + def expand1(inputs): + return paddle.expand(inputs, shape=[2, 3]) + + @paddle.jit.to_static + def expand2(inputs, shape): + return paddle.expand(inputs, shape=shape) + + x_shape = [3] + x_data = paddle.rand(x_shape, dtype="float32") + verify_model(expand1, input_data=[x_data]) + shape = paddle.to_tensor(np.array([2, 3]).astype("int32")) + verify_model(expand2, [x_data, shape], input_shape=[[3], [2]]) + + +@tvm.testing.uses_gpu +def test_forward_expand_as(): + @paddle.jit.to_static + def expand_as(x, y): + z = paddle.expand_as(x, y) + z += y + return z + + data_x = paddle.to_tensor([1, 2, 3], dtype="int32") + data_y = paddle.to_tensor([[1, 2, 3], [4, 5, 6]], dtype="float32") + verify_model(expand_as, [data_x, data_y]) + + @tvm.testing.uses_gpu def test_forward_shape_full(): @paddle.jit.to_static def full1(inputs): - return paddle.full(paddle.shape(inputs), 3.14) + return paddle.full(inputs, 3.14) @paddle.jit.to_static def full2(inputs): return paddle.full(paddle.shape(inputs), 1.0, dtype=inputs.dtype) + @paddle.jit.to_static + def shape1(inputs): + return paddle.shape(inputs) + input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(full1, input_data=[input_data]) + verify_model(shape1, input_data=[input_data]) + shape = paddle.to_tensor(np.array(input_shape, "int32")) + verify_model(full1, input_data=[shape], input_shape=[[4]]) verify_model(full2, input_data=[input_data]) @@ -361,7 +534,71 @@ def ones_like2(inputs): input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") verify_model(ones_like1, input_data=input_data) - verify_model(ones_like2, input_data=input_data) + verify_model(ones_like2, input_data, input_shape=[[-1, -1, -1, -1]]) + + +@tvm.testing.uses_gpu +def test_forward_ones(): + @paddle.jit.to_static + def ones1(inputs): + ones = paddle.ones([1, 3, 10, 10]) + out = inputs + ones + return out + + @paddle.jit.to_static + def ones2(inputs): + shape = paddle.to_tensor([1, 3, 10, 10], dtype="int32") + ones = paddle.ones(shape) + out = inputs + ones + return out + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ones1, input_data=input_data) + verify_model(ones2, input_data=input_data) + + +def test_forward_elemwise(): + class ElemwiseOp(nn.Layer): + def __init__(self, op_name): + super(ElemwiseOp, self).__init__() + self.op_name_ = op_name + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, op_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, input1, input2): + y = self.func(input1, input2) + if "equal" in self.op_name_ or "than" in self.op_name_: + y = paddle.cast(y, "int32") + return y + + op_list = [ + "floor_divide", + "floor_mod", + "maximum", + "minimum", + "equal", + "greater_equal", + "greater_than", + "less_equal", + "less_than", + "not_equal", + ] + input_shape = [10, 10] + input_shape_2 = [ + 10, + ] + x_data = paddle.rand(input_shape, dtype="float32") + y_data = paddle.rand(input_shape_2, dtype="float32") + x_data_2 = paddle.randint(1, 100, input_shape_2, dtype="int32") + y_data_2 = paddle.randint(1, 100, input_shape, dtype="int32") + for op_name in op_list: + if op_name not in ["floor_divide"]: + verify_model(ElemwiseOp(op_name), [x_data, y_data]) + verify_model(ElemwiseOp(op_name), [x_data_2, y_data_2]) @tvm.testing.uses_gpu @@ -376,25 +613,58 @@ def gelu(inputs): @tvm.testing.uses_gpu -def test_forward_hard_sigmoid(): - @paddle.jit.to_static - def hard_sigmoid(inputs): - return nn.functional.hardsigmoid(inputs) +def test_forward_activation(): + class Activation(nn.Layer): + def __init__(self, op_name): + super(Activation, self).__init__() + self.op_name_ = op_name + for candidate in (paddle.nn.functional, paddle): + self.func = getattr(candidate, op_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, inputs): + return self.func(inputs) input_shape = [1, 3, 10, 10] + input_data = paddle.normal(shape=input_shape) * 10.0 + input_data_2 = paddle.normal(shape=input_shape).astype("float64") * 10.0 + op_list = [ + "hardsigmoid", + "hardswish", + "log_sigmoid", + "log_softmax", + "sigmoid", + ] + for op_name in op_list: + try: + verify_model(Activation(op_name), input_data=input_data) + # verify_model(Activation(op_name), input_data=input_data_2) + except Exception as e: + print("{}".format(e)) + + +@tvm.testing.uses_gpu +def test_forward_isinf(): + @paddle.jit.to_static + def isinf(inputs): + return paddle.cast(paddle.isinf(inputs), "int32") + + input_shape = [5, 5] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(hard_sigmoid, input_data=input_data) + verify_model(isinf, input_data=input_data) @tvm.testing.uses_gpu -def test_forward_hard_swish(): +def test_forward_isnan(): @paddle.jit.to_static - def hard_swish(inputs): - return nn.functional.hardswish(inputs) + def isnan(inputs): + return paddle.cast(paddle.isnan(inputs), "int32") - input_shape = [1, 3, 10, 10] + input_shape = [5, 5] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(hard_swish, input_data=input_data) + verify_model(isnan, input_data=input_data) @tvm.testing.uses_gpu @@ -432,6 +702,50 @@ def leaky_relu(inputs): verify_model(leaky_relu, input_data=input_data) +@tvm.testing.uses_gpu +def test_forward_logical_op(): + class LogicalOp(nn.Layer): + def __init__(self, op_name, out=False): + super(LogicalOp, self).__init__() + self.out = out + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, op_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, x, y): + if self.out: + out = paddle.to_tensor([True, True, True]) + z = self.func(x, y, out=out) + else: + z = self.func(x, y) + return paddle.cast(z, "int32") + + class LogicalOp_not(LogicalOp): + @paddle.jit.to_static + def forward(self, x): + if self.out: + out = paddle.to_tensor([True, True, True]) + z = self.func(x, out=out) + else: + z = self.func(x) + return paddle.cast(z, "int32") + + op_list = [ + "logical_or", + "logical_xor", + "logical_and", + ] + x = paddle.to_tensor([True]) + y = paddle.to_tensor([True, False, True, False]) + for op_name in op_list: + verify_model(LogicalOp(op_name, False), [x, y]) + verify_model(LogicalOp(op_name, True), [x, y]) + verify_model(LogicalOp_not("logical_not", False), [y]) + verify_model(LogicalOp_not("logical_not", True), [y]) + + @tvm.testing.uses_gpu def test_forward_look_up(): @paddle.jit.to_static @@ -451,7 +765,7 @@ def forward(self, inputs): input_data = paddle.randint(0, 10, input_shape, dtype="int32") weight = paddle.rand([10, 4], dtype="float32") verify_model(look_up, input_data=[input_data, weight]) - verify_model(LookUp(), input_data=input_data) + verify_model(LookUp(), input_data, input_shape=[[-1, -1, -1, -1]]) @tvm.testing.uses_gpu @@ -504,6 +818,44 @@ def forward(self, input1, input2): verify_model(MatMul1(), input_data=[input_data1, input_data2]) +@tvm.testing.uses_gpu +def test_forward_meshgrid(): + @paddle.jit.to_static + def t(x, y, z): + return paddle.meshgrid(x, y, z) + + x = paddle.randint(low=0, high=100, shape=[2]) + y = paddle.randint(low=0, high=100, shape=[3]) + z = paddle.randint(low=0, high=100, shape=[5]) + verify_model(t, [x, y, z]) + + +def test_forward_mm(): + class Mm(nn.Layer): + def forward(self, input1, input2): + return paddle.mm(input1, input2) + + # matrix x vector + input_data1 = paddle.randn((3, 4), dtype="float32") + input_data2 = paddle.randn((4,), dtype="float32") + verify_model(Mm(), input_data=[input_data1, input_data2]) + + # matrix x matrix + input_data1 = paddle.randn((5, 4), dtype="float32") + input_data2 = paddle.randn((4, 5), dtype="float32") + verify_model(Mm(), input_data=[input_data1, input_data2]) + + # batched matrix x batched matrix + input_data1 = paddle.randn((10, 3, 4), dtype="float32") + input_data2 = paddle.randn((10, 4, 5), dtype="float32") + verify_model(Mm(), input_data=[input_data1, input_data2]) + + # batched matrix x broadcasted matrix + input_data1 = paddle.randn((10, 3, 4), dtype="float32") + input_data2 = paddle.randn((4, 5), dtype="float32") + verify_model(Mm(), input_data=[input_data1, input_data2]) + + @tvm.testing.uses_gpu def test_forward_pool2d(): @paddle.jit.to_static @@ -516,32 +868,42 @@ def pool2d2(inputs): @paddle.jit.to_static def pool2d3(inputs): - return nn.functional.max_pool2d( + output = nn.functional.max_pool2d(inputs, kernel_size=2, stride=2, padding=0) + return output + + @paddle.jit.to_static + def pool2d4(inputs): + output, max_indices = nn.functional.max_pool2d( inputs, kernel_size=2, stride=2, padding=0, return_mask=True ) + return output input_data = paddle.uniform(shape=[1, 2, 32, 32], dtype="float32", min=-1, max=1) - verify_model(pool2d1, input_data=input_data) + verify_model(pool2d1, input_data, input_shape=[[-1, 2, 32, 32]]) verify_model(pool2d2, input_data=input_data) - # verify_model(pool2d3, input_data=input_data) + input_data1 = paddle.uniform(shape=[1, 2, 1, 50], dtype="float32", min=-1, max=1) + verify_model(pool2d3, input_data=input_data1) @tvm.testing.uses_gpu -def test_forward_relu(): - @paddle.jit.to_static - def relu(inputs): - return nn.functional.relu(inputs) +def test_forward_rank(): + class Rank(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + rank = paddle.rank(inputs) + rank = paddle.unsqueeze(rank, axis=0) + output = inputs + rank + return output - input_shape = [10, 10] + input_shape = [1, 2, 1, 3, 1] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(relu, input_data=input_data) + verify_model(Rank(), input_data=input_data) @tvm.testing.uses_gpu def test_forward_reshape(): @paddle.jit.to_static - def reshape1(inputs, x): - new_shape = paddle.shape(x) + def reshape1(inputs, new_shape): return paddle.reshape(inputs, new_shape) @paddle.jit.to_static @@ -551,7 +913,7 @@ def reshape2(inputs): @paddle.jit.to_static def reshape3(inputs): data_shape = inputs.shape - return inputs.reshape([data_shape[0] * data_shape[1], data_shape[2]]) + return inputs.reshape([data_shape[1], data_shape[2], data_shape[0]]) @paddle.jit.to_static def reshape4(inputs, x): @@ -561,7 +923,8 @@ def reshape4(inputs, x): input_shape = [2, 1, 10, 1, 10] input_data = paddle.rand(input_shape, dtype="float32") input_data2 = paddle.randn([2, 1, 10, 10]) - verify_model(reshape1, input_data=[input_data, input_data2]) + new_shape = paddle.shape(input_data2) + verify_model(reshape1, [input_data, new_shape], input_shape=[[2, 1, 10, 1, 10], [4]]) verify_model(reshape2, input_data=input_data) verify_model(reshape3, input_data=paddle.randn((2, 3, 4))) verify_model(reshape4, input_data=[input_data, input_data2]) @@ -590,8 +953,8 @@ def scale2(inputs): @tvm.testing.uses_gpu def test_forward_slice(): @paddle.jit.to_static - def slice1(inputs): - return inputs[:, :, :, :3] + def slice1(inputs, end): + return inputs[:, :, :, :end] @paddle.jit.to_static def slice2(inputs): @@ -607,54 +970,55 @@ def slice4(inputs): x1 = paddle.to_tensor([3]) + paddle.to_tensor([1]) return inputs[:, x0:, 1:x1, :] - input_shape = [1, 3, 10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model( - slice1, - input_data=[ - input_data, - ], - ) - verify_model(slice2, input_data=input_data) - # need op "strided_slice" - # verify_model(slice3, input_data=paddle.randn((4, 4))) - # need op "assign_value" - # verify_model(slice4, input_data=input_data) - - -@tvm.testing.uses_gpu -def test_forward_tanh(): @paddle.jit.to_static - def tanh(inputs): - return paddle.tanh(inputs) + def slice5(inputs): + x0 = paddle.to_tensor([3]) + return inputs[:, 1::1, 2::x0, 4:10] input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(tanh, input_data=input_data) + end = paddle.to_tensor(np.array([3])) + verify_model(slice1, [input_data, end], input_shape=[[1, 3, 10, 10], [1]]) + verify_model(slice2, input_data=input_data) + verify_model(slice3, input_data=paddle.randn((4, 4))) + verify_model(slice4, input_data=input_data) + verify_model(slice5, input_data=input_data) if __name__ == "__main__": test_forward_add_subtract() test_forward_argmax() + test_forward_argmin() + test_forward_argsort() test_forward_assign() test_forward_batch_norm() test_forward_cast() test_forward_concat_unsqueeze() - test_forward_cumsum() test_forward_conv() + test_forward_cumsum() + test_forward_dot() test_forward_dropout() + test_forward_elemwise() + test_forward_expand() + test_forward_expand_as() test_forward_shape_full() + test_forward_ones() test_forward_ones_like() test_forward_gelu() - test_forward_hard_sigmoid() - test_forward_hard_swish() + test_forward_math() + test_forward_activation() + test_forward_isinf() + test_forward_isnan() test_forward_layer_norm() test_forward_leaky_relu() - test_forward_multiply() + test_forward_logical_op() + test_forward_look_up() test_forward_matmul() + test_forward_meshgrid() + test_forward_mm() + test_forward_multiply() test_forward_pool2d() - test_forward_relu() + test_forward_rank() test_forward_reshape() test_forward_scale() test_forward_slice() - test_forward_tanh() From 75956dbfefc687b7d9607dd8d00e8ccfeedb3f68 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Mon, 27 Sep 2021 10:15:31 +0800 Subject: [PATCH 05/24] Update paddlepaddle.py --- python/tvm/relay/frontend/paddlepaddle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 41f4bf5a5b17..938c8b58fbce 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -120,7 +120,7 @@ def _convert_dtype_value(val): def convert_unary_op(g, op, block): """Operator converter for all the unary operators.""" - # op_map stores mapping relationship between tvm and relay + # op_map stores mapping relationship between paddlepaddle and relay op_map = { "isinf_v2": _op.isinf, "isfinite_v2": _op.isfinite, From a3aa170f3713ba5e4554fc6c57db1f4d019d4d9b Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Mon, 27 Sep 2021 06:45:35 +0000 Subject: [PATCH 06/24] modify error message for SAME padding --- python/tvm/relay/frontend/paddlepaddle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 938c8b58fbce..b8a663d83dcf 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -821,7 +821,7 @@ def convert_pool2d(g, op, block): try: in_h, in_w = infer_value(h_w, g.get_params()).numpy().tolist() except Exception as e: - msg = "The SAME padding algorithm of Conv not support dynamic shape" + msg = "Dynamic shape is not supported in SAME padding algorithm while stride!=1" raise tvm.error.OpAttributeInvalid(msg) from e pad_h = _get_pad_size(in_h, ksize[0], strides[0]) pad_w = _get_pad_size(in_w, ksize[1], strides[1]) From 326383a99f3fca6cd3675dfe374fc72b082b77fb Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 08:05:43 +0000 Subject: [PATCH 07/24] Remove some function and old version operator --- python/tvm/relay/frontend/paddlepaddle.py | 151 +++++++++--------- .../frontend/paddlepaddle/test_forward.py | 73 ++++----- 2 files changed, 106 insertions(+), 118 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index b8a663d83dcf..8583b29eb272 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -35,6 +35,7 @@ infer_shape, infer_type, infer_value, + try_infer_value, new_var, ) @@ -42,7 +43,7 @@ def _get_pad_size(in_size, dilated_kernel_size, stride_size): - """calculate the paddings size""" + """Calculate the paddings size.""" if stride_size == 1 or in_size % stride_size == 0: pad = max(dilated_kernel_size - stride_size, 0) @@ -56,7 +57,7 @@ def _get_pad_size(in_size, dilated_kernel_size, stride_size): def _dtype_shape_promotion(inputs): - """promote data type and shape for list of tensors.""" + """Promote data type and shape for list of tensors.""" dtype_order = ["bool", "int8", "int16", "int32", "int64", "float32", "float64"] @@ -77,7 +78,7 @@ def _dtype_shape_promotion(inputs): def shape_of(x, dtype="int32"): - """Get shape of a tensor""" + """Get shape of a tensor.""" ttype = infer_type(x).checked_type if not _ty.is_dynamic(ttype): @@ -86,19 +87,8 @@ def shape_of(x, dtype="int32"): return _op.shape_of(x, dtype) -def _infer_value(x, params): - """Try running infer_value, and if successful, return the inferred value. - Otherwise, return input""" - - try: - value = infer_value(x, params) - return value.numpy().tolist() - except Exception: # pylint: disable=broad-except - return x - - def _convert_dtype_value(val): - """converts a Paddle type id to a string.""" + """Converts a Paddle type id to a string.""" convert_dtype_map = { 21: "int8", @@ -145,28 +135,8 @@ def convert_binary_logical_op(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_arg_max(g, op, block): - """Operator converter for arg_max.""" - - axis = op.attr("axis") - keepdims = op.attr("keepdims") - flatten = op.attr("flatten") - dtype = op.attr("dtype") - dtype = _convert_dtype_value(dtype) - - x = g.get_node(op.input("X")[0]) - if axis is None or flatten: - x = _op.reshape(x, [-1]) - out = _op.argmax(x, axis=None, keepdims=True) - else: - out = _op.argmax(x, axis=axis, keepdims=keepdims) - if dtype != infer_type(out).checked_type.dtype: - out = _op.cast(out, dtype) - g.add_node(op.output("Out")[0], out) - - -def convert_arg_min(g, op, block): - """Operator converter for arg_min.""" +def convert_arg_max_min(g, op, block): + """Operator converter for arg_max and arg_min.""" axis = op.attr("axis") keepdims = op.attr("keepdims") @@ -174,12 +144,13 @@ def convert_arg_min(g, op, block): dtype = op.attr("dtype") dtype = _convert_dtype_value(dtype) + func = _op.argmax if op.type == "arg_max" else _op.argmin x = g.get_node(op.input("X")[0]) if axis is None or flatten: x = _op.reshape(x, [-1]) - out = _op.argmin(x, axis=None, keepdims=True) + out = func(x, axis=None, keepdims=True) else: - out = _op.argmin(x, axis=axis, keepdims=keepdims) + out = func(x, axis=axis, keepdims=keepdims) if dtype != infer_type(out).checked_type.dtype: out = _op.cast(out, dtype) g.add_node(op.output("Out")[0], out) @@ -192,10 +163,10 @@ def convert_argsort(g, op, block): axis = op.attr("axis") descending = op.attr("descending") - out = _op.sort(x, axis, not descending) - out_indice = _op.argsort(x, axis, not descending, dtype="int64") + out_indices = _op.argsort(x, axis, not descending, dtype="int64") + out = _op.gather(x, axis, out_indices) g.add_node(op.output("Out")[0], out) - g.add_node(op.output("Indices")[0], out_indice) + g.add_node(op.output("Indices")[0], out_indices) def convert_assign(g, op, block): @@ -342,6 +313,8 @@ def convert_dropout(g, op, block): def convert_dot(g, op, block): """Operator converter for dot.""" + # x, y should be 1D or 2D tensor + # when it's 2D tensor, the first dimension means batch dimension x = g.get_node(op.input("X")[0]) y = g.get_node(op.input("Y")[0]) @@ -362,7 +335,6 @@ def convert_elementwise_op(g, op, block): "elementwise_min": "minimum", "elementwise_pow": "power", "elementwise_floordiv": "floor_divide", - "floor_mod": "floor_mod", "equal": "equal", "greater_equal": "greater_equal", "greater_than": "greater", @@ -392,10 +364,13 @@ def convert_expand(g, op, block): x = g.get_node(op.input("X")[0]) if op.input("Shape"): sizes = g.get_node(op.input("Shape")[0]) - sizes = _infer_value(sizes, g.get_params()) + sizes = try_infer_value(sizes, g.get_params())[0] else: sizes = op.attr("shape") + if isinstance(sizes, np.ndarray): + sizes = size.tolist() + out = _op.broadcast_to(x, sizes) g.add_node(op.output("Out")[0], out) @@ -432,6 +407,17 @@ def convert_feed(g, op, block): g.add_node(ipt_name, out) +def convert_fill_any_like(g, op, block): + """Operator converter for fill_any_like.""" + + dtype = op.attr("dtype") + dtype = _convert_dtype_value(dtype) + x = g.get_node(op.input("X")[0]) + value = _expr.const(op.attr("value"), dtype=dtype) + out = _op.transform.full_like(x, value).astype(dtype) + g.add_node(op.output("Out")[0], out) + + def convert_fill_constant(g, op, block): """Operator converter for fill_constant.""" @@ -442,23 +428,15 @@ def convert_fill_constant(g, op, block): value = _expr.const(value).astype(dtype) if "ValueTensor" in op.input_names and op.input("ValueTensor"): shape = g.get_node(op.input("ValueTensor")[0]) - shape = _infer_value(shape, g.get_params()) + shape = try_infer_value(shape, g.get_params())[0] if "ShapeTensor" in op.input_names and op.input("ShapeTensor"): shape = g.get_node(op.input("ShapeTensor")[0]) - shape = _infer_value(shape, g.get_params()) - - out = _op.full(value, shape=shape, dtype=dtype) - g.add_node(op.output("Out")[0], out) - + shape = try_infer_value(shape, g.get_params())[0] -def convert_fill_any_like(g, op, block): - """Operator converter for fill_any_like.""" + if isinstance(shape, np.ndarray): + shape = shape.tolist() - dtype = op.attr("dtype") - dtype = _convert_dtype_value(dtype) - x = g.get_node(op.input("X")[0]) - value = _expr.const(op.attr("value"), dtype=dtype) - out = _op.transform.full_like(x, value).astype(dtype) + out = _op.full(value, shape=shape, dtype=dtype) g.add_node(op.output("Out")[0], out) @@ -559,15 +537,17 @@ def convert_lookup_table(g, op, block): weights = g.get_node(op.input("W")[0]) if padding_idx != -1: if op.input("W")[0] in g.get_params(): + # while `w` is a parameter weights = g.get_params(op.input("W")[0]) weights[padding_idx] = 0.0 weights = _expr.const(weights) else: - shape = _infer_value(shape_of(weights), g.get_params()) + # while `w` is a tensor + shape = try_infer_value(shape_of(weights), g.get_params())[0] assert not isinstance( shape, _expr.Expr ), "Shape of weight has to be fixed for PaddlePaddle's lookup_table" - filters = np.ones(shape).astype(infer_type(weights).checked_type.dtype) + filters = np.ones(shape.tolist()).astype(infer_type(weights).checked_type.dtype) filters[padding_idx] = 0.0 filters = _expr.const(filters) weights = weights * filters @@ -856,18 +836,26 @@ def convert_reshape(g, op, block): input_shape_tensor = op.input("ShapeTensor") data = g.get_node(op.input("X")[0]) if input_shape: + # if the target shape is a 1D tensor new_shape = g.get_node(input_shape[0]) elif input_shape_tensor: + # if the target shape is a list of tensors new_shape = [] for shape_name in input_shape_tensor: shape = g.get_node(shape_name) if len(infer_shape(shape)) == 0: + # sometimes the element maybe a scalar tensor shape = _op.reshape(shape, [-1]) new_shape.append(shape.astype("int64")) new_shape = _op.concatenate(new_shape, axis=0) - new_shape = _infer_value(new_shape, g.get_params()) + new_shape = try_infer_value(new_shape, g.get_params())[0] else: + # if the target shape is a list of constant value new_shape = op.attr("shape") + + if isinstance(new_shape, np.ndarray): + new_shape = new_shape.tolist() + out = _op.reshape(data, new_shape) g.add_node(op.output("Out")[0], out) @@ -925,18 +913,24 @@ def convert_slice(g, op, block): decrease_axis = [decrease_axis] if op.input("StartsTensor"): + # if `starts` is a 1D tensor starts = g.get_node(op.input("StartsTensor")[0]) - starts = _infer_value(starts, g.get_params()) + starts = try_infer_value(starts, g.get_params())[0] elif op.input("StartsTensorList"): + # if `starts` is a list of tensors starts = [] for start_index in op.input("StartsTensorList"): start_index = g.get_node(start_index).astype("int64") starts.append(start_index) starts = _op.concatenate(starts, axis=0) - starts = _infer_value(starts, g.get_params()) + starts = try_infer_value(starts, g.get_params())[0] else: + # if `starts` is a list of constant values starts = op.attr("starts") + if isinstance(starts, np.ndarray): + starts = starts.tolist() + if len(axes) < dims: if isinstance(starts, _expr.Expr): starts = _op.scatter( @@ -952,18 +946,24 @@ def convert_slice(g, op, block): starts = base if op.input("EndsTensor"): + # if `ends` is a 1D tensor ends = g.get_node(op.input("EndsTensor")[0]) - ends = _infer_value(ends, g.get_params()) + ends = try_infer_value(ends, g.get_params())[0] elif op.input("EndsTensorList"): + # if `ends` is a list of tensors ends = [] for end_index in op.input("EndsTensorList"): end_index = g.get_node(end_index).astype("int64") ends.append(end_index) ends = _op.concatenate(ends, axis=0) - ends = _infer_value(ends, g.get_params()) + ends = try_infer_value(ends, g.get_params())[0] else: + # if `ends` is a list of constant values ends = op.attr("ends") + if isinstance(ends, np.ndarray): + ends = ends.tolist() + if len(axes) < dims: if isinstance(ends, _expr.Expr): ends = _op.scatter( @@ -983,18 +983,24 @@ def convert_slice(g, op, block): strides = None if "StridesTensor" in op.input_names and op.input("StridesTensor"): + # if `strides` is a 1D tensor strides = g.get_node(op.input("StridesTensor")[0]) - strides = _infer_value(strides, g.get_params()) + strides = try_infer_value(strides, g.get_params())[0] elif "StridesTensorList" in op.input_names and op.input("StridesTensorList"): + # if `strides` is a list of tensors strides = [] for strides_index in op.input("StridesTensorList"): strides_index = g.get_node(strides_index).astype("int64") strides.append(strides_index) strides = _op.concatenate(strides, axis=0) - strides = _infer_value(strides, g.get_params()) + strides = try_infer_value(strides, g.get_params())[0] elif op.has_attr("strides"): + # if `strides` is a list of constant values strides = op.attr("strides") + if isinstance(strides, np.ndarray): + strides = strides.tolist() + if len(axes) < dims: if isinstance(strides, _expr.Expr): strides = _op.scatter( @@ -1016,6 +1022,8 @@ def convert_slice(g, op, block): out = _op.strided_slice(data, begin=starts, end=ends, strides=strides) if decrease_axis: + # `decrease_axis` is False while using paddle.slice() + # `decrease_axis` is True while using tensor[1:2] out = _op.squeeze(out, axis=decrease_axis) g.add_node(op.output("Out")[0], out) @@ -1047,8 +1055,8 @@ def convert_unsqueeze(g, op, block): _convert_map = { "abs": convert_unary_op, "acos": convert_unary_op, - "arg_max": convert_arg_max, - "arg_min": convert_arg_min, + "arg_max": convert_arg_max_min, + "arg_min": convert_arg_max_min, "argsort": convert_argsort, "asin": convert_unary_op, "assign": convert_assign, @@ -1083,17 +1091,13 @@ def convert_unsqueeze(g, op, block): "fill_any_like": convert_fill_any_like, "fill_constant": convert_fill_constant, "floor": convert_unary_op, - "floor_mod": convert_elementwise_op, "gelu": convert_gelu, "greater_equal": convert_elementwise_op, "greater_than": convert_elementwise_op, "hard_sigmoid": convert_hard_sigmoid, "hard_swish": convert_hard_swish, - "isfinite": convert_unary_op, "isfinite_v2": convert_unary_op, - "isinf": convert_unary_op, "isinf_v2": convert_unary_op, - "isnan": convert_unary_op, "isnan_v2": convert_unary_op, "layer_norm": convert_layer_norm, "leaky_relu": convert_leaky_relu, @@ -1109,7 +1113,6 @@ def convert_unsqueeze(g, op, block): "logical_xor": convert_binary_logical_op, "logsigmoid": convert_logsigmoid, "log_softmax": convert_logsoftmax, - "lookup_table": convert_lookup_table, "lookup_table_v2": convert_lookup_table, "matmul": convert_matmul, "matmul_v2": convert_matmul, @@ -1202,6 +1205,8 @@ def check_unsupported_ops(self, program): for block in program.blocks: for op in block.ops: if op.type == "fetch": + # `fetch` is a flag of output tensors + # we do not need handle this continue if op.type not in _convert_map: unsupported_ops.add(op.type) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index a384f2acb53a..0d224ad9535a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -135,11 +135,11 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5, input_shape=None): @tvm.testing.uses_gpu def test_forward_math(): - class MathOp(nn.Layer): - def __init__(self, op_name): - super(MathOp, self).__init__() + class MathAPI(nn.Layer): + def __init__(self, api_name): + super(MathAPI, self).__init__() for candidate in (paddle, paddle.nn.functional): - self.func = getattr(candidate, op_name, None) + self.func = getattr(candidate, api_name, None) if self.func: break @@ -148,7 +148,7 @@ def forward(self, inputs): return self.func(inputs) input_data = paddle.rand([1, 2, 5, 5], dtype="float32") - op_list = [ + api_list = [ "abs", "acos", "asin", @@ -176,8 +176,8 @@ def forward(self, inputs): "tan", "tanh", ] - for op_name in op_list: - verify_model(MathOp(op_name), input_data) + for api_name in api_list: + verify_model(MathAPI(api_name), input_data) @tvm.testing.uses_gpu @@ -559,23 +559,23 @@ def ones2(inputs): def test_forward_elemwise(): - class ElemwiseOp(nn.Layer): - def __init__(self, op_name): - super(ElemwiseOp, self).__init__() - self.op_name_ = op_name + class ElemwiseAPI(nn.Layer): + def __init__(self, api_name): + super(ElemwiseAPI, self).__init__() + self.api_name_ = api_name for candidate in (paddle, paddle.nn.functional): - self.func = getattr(candidate, op_name, None) + self.func = getattr(candidate, api_name, None) if self.func: break @paddle.jit.to_static def forward(self, input1, input2): y = self.func(input1, input2) - if "equal" in self.op_name_ or "than" in self.op_name_: + if "equal" in self.api_name_ or "than" in self.api_name_: y = paddle.cast(y, "int32") return y - op_list = [ + api_list = [ "floor_divide", "floor_mod", "maximum", @@ -595,10 +595,10 @@ def forward(self, input1, input2): y_data = paddle.rand(input_shape_2, dtype="float32") x_data_2 = paddle.randint(1, 100, input_shape_2, dtype="int32") y_data_2 = paddle.randint(1, 100, input_shape, dtype="int32") - for op_name in op_list: - if op_name not in ["floor_divide"]: - verify_model(ElemwiseOp(op_name), [x_data, y_data]) - verify_model(ElemwiseOp(op_name), [x_data_2, y_data_2]) + for api_name in api_list: + if api_name not in ["floor_divide"]: + verify_model(ElemwiseAPI(api_name), [x_data, y_data]) + verify_model(ElemwiseAPI(api_name), [x_data_2, y_data_2]) @tvm.testing.uses_gpu @@ -633,37 +633,33 @@ def forward(self, inputs): op_list = [ "hardsigmoid", "hardswish", + "leaky_relu", "log_sigmoid", "log_softmax", "sigmoid", ] for op_name in op_list: - try: - verify_model(Activation(op_name), input_data=input_data) - # verify_model(Activation(op_name), input_data=input_data_2) - except Exception as e: - print("{}".format(e)) + verify_model(Activation(op_name), input_data=input_data) @tvm.testing.uses_gpu -def test_forward_isinf(): +def test_forward_check_tensor(): + @paddle.jit.to_static + def isfinite(inputs): + return paddle.cast(paddle.isfinite(inputs), "int32") + @paddle.jit.to_static def isinf(inputs): return paddle.cast(paddle.isinf(inputs), "int32") - input_shape = [5, 5] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(isinf, input_data=input_data) - - -@tvm.testing.uses_gpu -def test_forward_isnan(): @paddle.jit.to_static def isnan(inputs): return paddle.cast(paddle.isnan(inputs), "int32") input_shape = [5, 5] input_data = paddle.rand(input_shape, dtype="float32") + verify_model(isfinite, input_data=input_data) + verify_model(isinf, input_data=input_data) verify_model(isnan, input_data=input_data) @@ -691,17 +687,6 @@ def forward(self, inputs): verify_model(LayerNorm(), input_data=input_data) -@tvm.testing.uses_gpu -def test_forward_leaky_relu(): - @paddle.jit.to_static - def leaky_relu(inputs): - return nn.functional.leaky_relu(inputs) - - input_shape = [1, 3, 10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(leaky_relu, input_data=input_data) - - @tvm.testing.uses_gpu def test_forward_logical_op(): class LogicalOp(nn.Layer): @@ -1007,10 +992,8 @@ def slice5(inputs): test_forward_gelu() test_forward_math() test_forward_activation() - test_forward_isinf() - test_forward_isnan() + test_forward_check_tensor() test_forward_layer_norm() - test_forward_leaky_relu() test_forward_logical_op() test_forward_look_up() test_forward_matmul() From 6e275c27387c81dd05f61a36806fb83946d8283d Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 08:08:07 +0000 Subject: [PATCH 08/24] Remove some function and old version operator --- python/tvm/relay/frontend/paddlepaddle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 8583b29eb272..a846f26c06a3 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -1206,7 +1206,7 @@ def check_unsupported_ops(self, program): for op in block.ops: if op.type == "fetch": # `fetch` is a flag of output tensors - # we do not need handle this + # there's no need to handle this continue if op.type not in _convert_map: unsupported_ops.add(op.type) From f8e93cb5459abe5c5b18c5c84265f7c17b5dc67a Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 08:41:48 +0000 Subject: [PATCH 09/24] Remove some function and old version operator --- python/tvm/relay/frontend/paddlepaddle.py | 29 ++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index a846f26c06a3..fe9f8686933d 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -25,6 +25,7 @@ from .. import analysis from .. import ty as _ty +from ... import nd as _nd from .. import expr as _expr from .. import function as _function from .. import ty as _ty @@ -1190,7 +1191,7 @@ def extract_parameters(self, program, scope=None): if isinstance(scope, dict): self.params[name] = scope[name] else: - self.params[name] = np.array(scope.var(name).get_tensor()) + self.params[name] = _nd.array(np.array(scope.var(name).get_tensor())) if self.freeze_params: self.nodes[name] = _expr.const(self.params[name]) else: @@ -1288,6 +1289,32 @@ def from_paddle(program_or_layer, shape_dict=None, scope=None, freeze_params=Fal PaddlePaddle Program/TranslatedLayer represent the computation graph of PaddlePaddle model, and PaddlePaddle scope stores all the weights of PaddlePaddle model. + + Parameters + ---------- + program_or_layer : Program/TranslatedLayer object + loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` + + shape_dict : dict of str to tuple, optional + The input shape to the model + + scope : Scope object, optional + All the weights saved in scope, by default, use `paddle.fluid.global_scope` + + freeze_params: bool + If this parameter is true, the importer will take any provided + weights and embed them into the relay model as Constants instead of variables. + This allows more aggressive optimizations at compile time and helps in making + models static if certain inputs represent attributes relay would traditionally + consider compile-time constants. + + Returns + ------- + mod : tvm.IRModule + The relay module for compilation + + params : dict of str to tvm.nd.NDArray + The parameter dict to be used by relay """ import paddle From cd4ef5972d9f598844f26159dc7020a0ef490b60 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 08:43:30 +0000 Subject: [PATCH 10/24] Remove some function and old version operator --- python/tvm/relay/frontend/paddlepaddle.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index fe9f8686933d..2b2811e1d386 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -1293,20 +1293,20 @@ def from_paddle(program_or_layer, shape_dict=None, scope=None, freeze_params=Fal Parameters ---------- program_or_layer : Program/TranslatedLayer object - loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` + loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` - shape_dict : dict of str to tuple, optional + shape_dict : dict of str to tuple, optional The input shape to the model - scope : Scope object, optional - All the weights saved in scope, by default, use `paddle.fluid.global_scope` + scope : Scope object, optional + All the weights saved in scope, by default, use `paddle.fluid.global_scope` freeze_params: bool If this parameter is true, the importer will take any provided weights and embed them into the relay model as Constants instead of variables. - This allows more aggressive optimizations at compile time and helps in making - models static if certain inputs represent attributes relay would traditionally - consider compile-time constants. + This allows more aggressive optimizations at compile time and helps in making + models static if certain inputs represent attributes relay would traditionally + consider compile-time constants. Returns ------- From 67e9816172dbd3aefd97ef02c0753e1430ad9dec Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 09:02:08 +0000 Subject: [PATCH 11/24] add dot test --- .../frontend/paddlepaddle/test_forward.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 0d224ad9535a..418e22fd4409 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -447,14 +447,19 @@ def forward(self, inputs): @tvm.testing.uses_gpu def test_forward_dot(): @paddle.jit.to_static - def dot(x, y): + def dot1(x, y): return paddle.dot(x, y) - x_shape = [10, 3] - y_shape = [10, 3] - x_data = paddle.rand(x_shape, dtype="float32") - y_data = paddle.rand(y_shape, dtype="float32") - verify_model(dot, input_data=[x_data, y_data]) + @paddle.jit.to_static + def dot2(x): + y = paddle.to_tensor(np.random.rand(10).astype("float32")) + return paddle.dot(x, y) + + x_data = paddle.rand([10, 3], dtype="float32") + y_data = paddle.rand([10, 3], dtype="float32") + verify_model(dot1, input_data=[x_data, y_data]) + x_data = paddle.rand([10], dtype="float32") + verify_model(dot2, input_data=[x_data]) @tvm.testing.uses_gpu From 98fb38a48d4c567e05e660883b141be95929ab34 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 28 Sep 2021 14:13:33 +0000 Subject: [PATCH 12/24] modify doc --- python/tvm/relay/frontend/paddlepaddle.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 2b2811e1d386..62cfbc6c3b94 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -1293,20 +1293,20 @@ def from_paddle(program_or_layer, shape_dict=None, scope=None, freeze_params=Fal Parameters ---------- program_or_layer : Program/TranslatedLayer object - loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` + Loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` - shape_dict : dict of str to tuple, optional + shape_dict : dict of str to tuple, optional The input shape to the model - scope : Scope object, optional - All the weights saved in scope, by default, use `paddle.fluid.global_scope` + scope : Scope object, optional + All the weights saved in scope, by default, use `paddle.fluid.global_scope` - freeze_params: bool - If this parameter is true, the importer will take any provided - weights and embed them into the relay model as Constants instead of variables. - This allows more aggressive optimizations at compile time and helps in making - models static if certain inputs represent attributes relay would traditionally - consider compile-time constants. + freeze_params : bool + If this parameter is true, the importer will take any provided weights and + embed them into the relay model as Constants instead of variables. This + allows more aggressive optimizations at compile time and helps in making + models static if certain inputs represent attributes relay would traditionally + consider compile-time constants. Returns ------- From 201be4506282ee40604454904a106c7bc28a00b0 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Wed, 29 Sep 2021 06:41:39 +0000 Subject: [PATCH 13/24] remove unreviewed code --- python/tvm/relay/frontend/paddlepaddle.py | 392 ++--------- .../frontend/paddlepaddle/test_forward.py | 647 ++++-------------- 2 files changed, 222 insertions(+), 817 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 62cfbc6c3b94..846dd428ec35 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -520,74 +520,19 @@ def convert_leaky_relu(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_log1p(g, op, block): - """Operator converter for log1p.""" - - x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - one = _expr.const(1, dtype=dtype) - out = _op.log(x + one) - g.add_node(op.output("Out")[0], out) - - def convert_lookup_table(g, op, block): """Operator converter for lookup_table_v2.""" indices = g.get_node(op.input("Ids")[0]) padding_idx = op.attr("padding_idx") - weights = g.get_node(op.input("W")[0]) if padding_idx != -1: - if op.input("W")[0] in g.get_params(): - # while `w` is a parameter - weights = g.get_params(op.input("W")[0]) - weights[padding_idx] = 0.0 - weights = _expr.const(weights) - else: - # while `w` is a tensor - shape = try_infer_value(shape_of(weights), g.get_params())[0] - assert not isinstance( - shape, _expr.Expr - ), "Shape of weight has to be fixed for PaddlePaddle's lookup_table" - filters = np.ones(shape.tolist()).astype(infer_type(weights).checked_type.dtype) - filters[padding_idx] = 0.0 - filters = _expr.const(filters) - weights = weights * filters + g.get_params[op.input("W")[0]][padding_idx] = 0.0 + g.add_node(op.input("W")[0], _expr.const(g.params[op.input("W")[0]])) + weights = g.get_node(op.input("W")[0]) out = _op.take(weights, indices.astype("int32"), axis=0) g.add_node(op.output("Out")[0], out) -def convert_logical_not(g, op, block): - """Operator converter for logical_not op.""" - - ipt0 = g.get_node(op.input("X")[0]) - op_func = get_relay_op(op.type) - out = op_func(ipt0) - g.add_node(op.output("Out")[0], out) - - -def convert_logsigmoid(g, op, block): - """Operator converter for logsigmoid.""" - - x = g.get_node(op.input("X")[0]) - out = _op.log(_op.tensor.sigmoid(x)) - g.add_node(op.output("Out")[0], out) - - -def convert_logsoftmax(g, op, block): - """Operator converter for logsoftmax.""" - - x = g.get_node(op.input("X")[0]) - axis = op.attr("axis") - ndim = len(infer_shape(x)) - if axis < 0: - axis += ndim - m = _op.max(x, [axis], keepdims=True) - e = _op.exp(x - m) - s = _op.sum(e, [axis], keepdims=True) - out = x - m - _op.log(s) - g.add_node(op.output("Out")[0], out) - - def convert_matmul(g, op, block): """Operator converter for matmul.""" @@ -697,16 +642,6 @@ def flatten_to_nd(x, x_shape, nd=3): g.add_node(op.output("Out")[0], out) -def convert_meshgrid(g, op, block): - """Operator converter for meshgrid.""" - - inputs = op.input("X") - x = [g.get_node(i) for i in inputs] - outs = _op.meshgrid(x, indexing="ij") - for i, out in enumerate(outs): - g.add_node(op.output("Out")[i], out) - - def convert_mul(g, op, block): """Operator converter for mul.""" @@ -714,8 +649,8 @@ def convert_mul(g, op, block): y = g.get_node(op.input("Y")[0]) x_num_col_dims = op.attr("x_num_col_dims") y_num_col_dims = op.attr("y_num_col_dims") - x_shape = _op.shape_of(x) - y_shape = _op.shape_of(y) + x_shape = shape_of(x) + y_shape = shape_of(y) x_dim = infer_shape(x_shape)[0] y_dim = infer_shape(y_shape)[0] if x_num_col_dims < 0: @@ -752,15 +687,6 @@ def convert_mul(g, op, block): g.add_node(op.output("Out")[0], out) -def convert_numel(g, op, block): - """Operator converter for numel.""" - - input_x = g.get_node(op.input("Input")[0]) - out = _op.ndarray_size(input_x, dtype="int64") - out = _op.expand_dims(out, axis=0) - g.add_node(op.output("Out")[0], out) - - def convert_pool2d(g, op, block): """Operator converter for pool2d.""" @@ -793,19 +719,8 @@ def convert_pool2d(g, op, block): if padding_algorithm == "VALID": paddings = [0, 0] elif padding_algorithm == "SAME": - if strides[0] == 1 and strides[1] == 1: - pad_h = _get_pad_size(0, ksize[0], strides[0]) - pad_w = _get_pad_size(0, ksize[1], strides[1]) - else: - input_shape = shape_of(input_x) - h_w = _op.strided_slice(input_shape, [2], [4]) - try: - in_h, in_w = infer_value(h_w, g.get_params()).numpy().tolist() - except Exception as e: - msg = "Dynamic shape is not supported in SAME padding algorithm while stride!=1" - raise tvm.error.OpAttributeInvalid(msg) from e - pad_h = _get_pad_size(in_h, ksize[0], strides[0]) - pad_w = _get_pad_size(in_w, ksize[1], strides[1]) + pad_h = _get_pad_size(in_h, ksize[0], strides[0]) + pad_w = _get_pad_size(in_w, ksize[1], strides[1]) paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: @@ -816,11 +731,6 @@ def convert_pool2d(g, op, block): msg = 'Value {} in attribute "padding" of operator Pool2d is not "valid."' raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) - if not isinstance(in_h, _op.Expr) and in_h < ksize[0]: - ksize[0] = in_h - if not isinstance(in_w, _op.Expr) and in_w < ksize[1]: - ksize[1] = in_w - if not adaptive: out = getattr(_op.nn, op_map[pooling_type])( input_x, pool_size=ksize, strides=strides, padding=paddings, ceil_mode=ceil_mode @@ -837,26 +747,22 @@ def convert_reshape(g, op, block): input_shape_tensor = op.input("ShapeTensor") data = g.get_node(op.input("X")[0]) if input_shape: - # if the target shape is a 1D tensor new_shape = g.get_node(input_shape[0]) elif input_shape_tensor: - # if the target shape is a list of tensors - new_shape = [] + tmp_shape = [] for shape_name in input_shape_tensor: shape = g.get_node(shape_name) if len(infer_shape(shape)) == 0: - # sometimes the element maybe a scalar tensor shape = _op.reshape(shape, [-1]) - new_shape.append(shape.astype("int64")) - new_shape = _op.concatenate(new_shape, axis=0) - new_shape = try_infer_value(new_shape, g.get_params())[0] + if isinstance(shape, _expr.Constant): + tmp_shape.append(shape) + elif isinstance(shape, _expr.Expr): + tmp_shape.append(shape) + else: + tmp_shape.append(_expr.const(np.array(shape).astype("int64"))) + new_shape = _op.concatenate(tmp_shape, axis=0) else: - # if the target shape is a list of constant value new_shape = op.attr("shape") - - if isinstance(new_shape, np.ndarray): - new_shape = new_shape.tolist() - out = _op.reshape(data, new_shape) g.add_node(op.output("Out")[0], out) @@ -869,11 +775,8 @@ def convert_scale(g, op, block): bias_after_scale = op.attr("bias_after_scale") x = g.get_node(op.input("X")[0]) if np.isclose(scale, 1.0) and np.isclose(bias, 0.0): - out = x + out = _op.copy(x) else: - x_dtype = infer_type(x).checked_type.dtype - if x_dtype != "float32": - x = x.astype("float32") if np.isclose(bias, 0.0): out = x * _expr.const(np.array(scale).astype("float32")) elif np.isclose(scale, 1.0): @@ -887,8 +790,6 @@ def convert_scale(g, op, block): out = (x + _expr.const(np.array(bias).astype("float32"))) * _expr.const( np.array(scale).astype("float32") ) - if x_dtype != "float32": - out = out.astype(x_dtype) g.add_node(op.output("Out")[0], out) @@ -903,128 +804,39 @@ def convert_shape(g, op, block): def convert_slice(g, op, block): """Operator converter for slice.""" - data = g.get_node(op.input("Input")[0]) - dims = len(infer_shape(data)) + def parameter_process(starts, ends, axes, dshape): + new_axes = [] + new_starts = [] + new_ends = [] + pop_index = 0 + for i in range(max(axes) + 1): + new_axes.append(i) + if i in axes: + new_starts.append(starts[pop_index]) + new_ends.append(ends[pop_index]) + pop_index += 1 + else: + new_starts.append(0) + new_ends.append(dshape[i]) + return new_starts, new_ends, new_axes + data = g.get_node(op.input("Input")[0]) + dshape = infer_shape(data) + starts = op.attr("starts") + ends = op.attr("ends") axes = op.attr("axes") - indices = _expr.const(axes, dtype="int64") - decrease_axis = op.attr("decrease_axis") + if isinstance(starts, int): + starts = [starts] + if isinstance(ends, int): + ends = [ends] + if isinstance(axes, int): + axes = [axes] if isinstance(decrease_axis, int): decrease_axis = [decrease_axis] - - if op.input("StartsTensor"): - # if `starts` is a 1D tensor - starts = g.get_node(op.input("StartsTensor")[0]) - starts = try_infer_value(starts, g.get_params())[0] - elif op.input("StartsTensorList"): - # if `starts` is a list of tensors - starts = [] - for start_index in op.input("StartsTensorList"): - start_index = g.get_node(start_index).astype("int64") - starts.append(start_index) - starts = _op.concatenate(starts, axis=0) - starts = try_infer_value(starts, g.get_params())[0] - else: - # if `starts` is a list of constant values - starts = op.attr("starts") - - if isinstance(starts, np.ndarray): - starts = starts.tolist() - - if len(axes) < dims: - if isinstance(starts, _expr.Expr): - starts = _op.scatter( - _op.const([0] * dims, dtype=infer_type(starts).checked_type.dtype), - indices, - starts, - axis=0, - ) - else: - base = [0] * dims - for i, axis in enumerate(axes): - base[axis] = starts[i] - starts = base - - if op.input("EndsTensor"): - # if `ends` is a 1D tensor - ends = g.get_node(op.input("EndsTensor")[0]) - ends = try_infer_value(ends, g.get_params())[0] - elif op.input("EndsTensorList"): - # if `ends` is a list of tensors - ends = [] - for end_index in op.input("EndsTensorList"): - end_index = g.get_node(end_index).astype("int64") - ends.append(end_index) - ends = _op.concatenate(ends, axis=0) - ends = try_infer_value(ends, g.get_params())[0] - else: - # if `ends` is a list of constant values - ends = op.attr("ends") - - if isinstance(ends, np.ndarray): - ends = ends.tolist() - - if len(axes) < dims: - if isinstance(ends, _expr.Expr): - ends = _op.scatter( - _expr.const( - np.array([np.iinfo(np.int32).max] * dims), - dtype=infer_type(ends).checked_type.dtype, - ), - indices, - ends, - axis=0, - ) - else: - base = [np.iinfo(np.int32).max] * dims - for i, axis in enumerate(axes): - base[axis] = ends[i] - ends = base - - strides = None - if "StridesTensor" in op.input_names and op.input("StridesTensor"): - # if `strides` is a 1D tensor - strides = g.get_node(op.input("StridesTensor")[0]) - strides = try_infer_value(strides, g.get_params())[0] - elif "StridesTensorList" in op.input_names and op.input("StridesTensorList"): - # if `strides` is a list of tensors - strides = [] - for strides_index in op.input("StridesTensorList"): - strides_index = g.get_node(strides_index).astype("int64") - strides.append(strides_index) - strides = _op.concatenate(strides, axis=0) - strides = try_infer_value(strides, g.get_params())[0] - elif op.has_attr("strides"): - # if `strides` is a list of constant values - strides = op.attr("strides") - - if isinstance(strides, np.ndarray): - strides = strides.tolist() - - if len(axes) < dims: - if isinstance(strides, _expr.Expr): - strides = _op.scatter( - _expr.const( - np.array([1] * dims), - dtype=infer_type(strides).checked_type.dtype, - ), - indices, - strides, - axis=0, - ) - elif strides: - base = [1] * dims - for i, axis in enumerate(axes): - base[axis] = strides[i] - strides = base - if not strides: - strides = _op.const([1] * dims, dtype="int64") - - out = _op.strided_slice(data, begin=starts, end=ends, strides=strides) + starts, ends, axes = parameter_process(starts, ends, axes, dshape) + out = _op.strided_slice(data, begin=starts, end=ends) if decrease_axis: - # `decrease_axis` is False while using paddle.slice() - # `decrease_axis` is True while using tensor[1:2] out = _op.squeeze(out, axis=decrease_axis) g.add_node(op.output("Out")[0], out) @@ -1054,22 +866,15 @@ def convert_unsqueeze(g, op, block): _convert_map = { - "abs": convert_unary_op, - "acos": convert_unary_op, "arg_max": convert_arg_max_min, "arg_min": convert_arg_max_min, "argsort": convert_argsort, - "asin": convert_unary_op, "assign": convert_assign, "assign_value": convert_assign_value, - "atan": convert_unary_op, "batch_norm": convert_batch_norm, "cast": convert_cast, - "ceil": convert_unary_op, "concat": convert_concat, "conv2d": convert_conv2d, - "cos": convert_unary_op, - "cosh": convert_unary_op, "cumsum": convert_cumsum, "depthwise_conv2d": convert_conv2d, "dot": convert_dot, @@ -1084,14 +889,12 @@ def convert_unsqueeze(g, op, block): "elementwise_pow": convert_elementwise_op, "elementwise_floordiv": convert_elementwise_op, "equal": convert_elementwise_op, - "erf": convert_unary_op, "exp": convert_unary_op, "expand_v2": convert_expand, "expand_as_v2": convert_expand_as, "feed": convert_feed, "fill_any_like": convert_fill_any_like, "fill_constant": convert_fill_constant, - "floor": convert_unary_op, "gelu": convert_gelu, "greater_equal": convert_elementwise_op, "greater_than": convert_elementwise_op, @@ -1104,39 +907,21 @@ def convert_unsqueeze(g, op, block): "leaky_relu": convert_leaky_relu, "less_equal": convert_elementwise_op, "less_than": convert_elementwise_op, - "log": convert_unary_op, - "log2": convert_unary_op, - "log10": convert_unary_op, - "log1p": convert_log1p, "logical_and": convert_binary_logical_op, - "logical_not": convert_logical_not, "logical_or": convert_binary_logical_op, "logical_xor": convert_binary_logical_op, - "logsigmoid": convert_logsigmoid, - "log_softmax": convert_logsoftmax, "lookup_table_v2": convert_lookup_table, "matmul": convert_matmul, "matmul_v2": convert_matmul, - "meshgrid": convert_meshgrid, "mul": convert_mul, "not_equal": convert_elementwise_op, "pool2d": convert_pool2d, "relu": convert_unary_op, "reshape2": convert_reshape, - "round": convert_unary_op, - "rsqrt": convert_unary_op, "scale": convert_scale, "shape": convert_shape, - "sigmoid": convert_unary_op, - "sign": convert_unary_op, - "sin": convert_unary_op, - "sinh": convert_unary_op, - "size": convert_numel, "slice": convert_slice, "softmax": convert_softmax, - "sqrt": convert_unary_op, - "strided_slice": convert_slice, - "tan": convert_unary_op, "tanh": convert_unary_op, "unsqueeze2": convert_unsqueeze, } @@ -1145,38 +930,30 @@ def convert_unsqueeze(g, op, block): class GraphProto: """A helper class for handling relay functions from PaddlePaddle model.""" - def __init__(self, freeze_params=False): + def __init__(self): self.nodes = {} self.params = {} self.shape_dict = None - self.freeze_params = freeze_params def get_node(self, name): """get node from graph""" - assert name in self.nodes, "Node: {} not found".format(name) + assert name in self.nodes return self.nodes[name] def add_node(self, name, node): """add a node to graph""" - if self.shape_dict: - self.nodes[name] = fold_constant(node) - else: - self.nodes[name] = node + + self.nodes[name] = fold_constant(node) def get_params(self, name=None): - """get params from graph""" + """Get params from graph.""" if name is None: return self.params assert name in self.params return self.params[name] - def set_params(self, params): - """set params for graph""" - - self.params = params - def extract_parameters(self, program, scope=None): """Extract all the weights from PaddlePaddle program.""" @@ -1192,12 +969,20 @@ def extract_parameters(self, program, scope=None): self.params[name] = scope[name] else: self.params[name] = _nd.array(np.array(scope.var(name).get_tensor())) - if self.freeze_params: - self.nodes[name] = _expr.const(self.params[name]) - else: - self.nodes[name] = _expr.var( - name, shape=self.params[name].shape, dtype=str(self.params[name].dtype) + self.nodes[name] = _expr.const(self.params[name]) + + def check_input_shape(self, op, block): + """Check the shape information of model's inputs, fixed shape is recommended.""" + + ipt_name = op.input(op.input_names[0]) + ipt_shape = block.var(ipt_name).shape + for i in ipt_shape: + if i < 0: + warning_msg = "Input {}(shape={}) has unkown dimension shapes. \ + Specifying static values may improve performance".format( + ipt_name, ipt_shape ) + warnings.warn(warning_msg) def check_unsupported_ops(self, program): """Check whether all the operators are supported.""" @@ -1222,12 +1007,12 @@ def ops_to_relay(self, program, input_specs=None): if input_specs is not None: for input_spec in input_specs: convert_feed(self, input_spec, None) - global_block = program.blocks[0] - for op in global_block.ops: - if op.type == "fetch": - continue - convert_func = _convert_map[op.type] - convert_func(self, op, global_block) + for block in program.blocks: + for op in block.ops: + if op.type == "fetch": + continue + convert_func = _convert_map[op.type] + convert_func(self, op, block) def from_program(self, program, shape_dict, scope): """Construct the TVM relay expression from PaddlePaddle program.""" @@ -1247,14 +1032,12 @@ def from_program(self, program, shape_dict, scope): if op.type == "fetch": output_names.append(op.input("X")[0]) - outputs = [self.get_node(name) for name in output_names] + outputs = [self.nodes[name] for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) free_vars = analysis.free_vars(outputs) func = _function.Function(free_vars, outputs) mod = IRModule.from_expr(func) - if self.freeze_params: - self.params = {} return mod, self.params def from_translated_layer(self, layer, shape_dict): @@ -1273,53 +1056,24 @@ def from_translated_layer(self, layer, shape_dict): output_names = [x.name for x in layer._output_spec()] - outputs = [self.get_node(name) for name in output_names] + outputs = [self.nodes[name] for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) free_vars = analysis.free_vars(outputs) func = _function.Function(free_vars, outputs) mod = IRModule.from_expr(func) - if self.freeze_params: - self.params = {} return mod, self.params -def from_paddle(program_or_layer, shape_dict=None, scope=None, freeze_params=False): +def from_paddle(program_or_layer, shape_dict=None, scope=None): """Convert a PaddlePaddle model into an equivalent Relay Function. - PaddlePaddle Program/TranslatedLayer represent the computation - graph of PaddlePaddle model, and PaddlePaddle scope stores all the - weights of PaddlePaddle model. - - Parameters - ---------- - program_or_layer : Program/TranslatedLayer object - Loaded model by `paddle.static.load_inference_model` or `paddle.jit.load` - - shape_dict : dict of str to tuple, optional - The input shape to the model - - scope : Scope object, optional - All the weights saved in scope, by default, use `paddle.fluid.global_scope` - - freeze_params : bool - If this parameter is true, the importer will take any provided weights and - embed them into the relay model as Constants instead of variables. This - allows more aggressive optimizations at compile time and helps in making - models static if certain inputs represent attributes relay would traditionally - consider compile-time constants. - - Returns - ------- - mod : tvm.IRModule - The relay module for compilation - - params : dict of str to tvm.nd.NDArray - The parameter dict to be used by relay + PaddlePaddle Program/TranslatedLayer represent the computation graph of PaddlePaddle model, + and PaddlePaddle scope stores all the weights of PaddlePaddle model. """ import paddle - g = GraphProto(freeze_params) + g = GraphProto() if isinstance(program_or_layer, paddle.jit.TranslatedLayer): # model is loaded by `paddle.jit.load` mod, params = g.from_translated_layer(program_or_layer, shape_dict) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 418e22fd4409..048ecff8218e 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -14,21 +14,19 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +import os from pathlib import Path import shutil import numpy as np - -import paddle -from paddle.framework import dtype -import paddle.nn as nn - import tvm import tvm.testing import tvm.topi.testing from tvm import relay from tvm.contrib import graph_executor +import paddle +import paddle.nn as nn PADDLE_TEST_DATA_ROOT_PATH = Path(Path("~").expanduser(), ".tvm_test_data", "paddle") PADDLE_TEST_DATA_ROOT_PATH.mkdir(parents=True, exist_ok=True) @@ -36,16 +34,14 @@ def assert_shapes_match(tru, est): if tru.shape != est.shape: - msg = "Paddle Output shapes {} and TVM shapes {} don't match" + msg = "Output shapes {} and {} don't match" raise AssertionError(msg.format(tru.shape, est.shape)) - if tru.dtype != est.dtype: - msg = "Paddle Output dtype {} and TVM dtype {} don't match" - raise AssertionError(msg.format(tru.dtype, est.dtype)) def get_paddle_model(func, input_spec): global PADDLE_TEST_DATA_ROOT_PATH model_path = Path(PADDLE_TEST_DATA_ROOT_PATH, "model") + paddle.jit.save(func, str(model_path), input_spec=input_spec) baseline_model = paddle.jit.load(str(model_path)) @@ -53,34 +49,7 @@ def get_paddle_model(func, input_spec): return baseline_model -def get_tvm_output_with_vm(mod, params, target, device, input_data): - """Generic function to execute and get tvm output with vm executor""" - - ex = relay.create_executor("vm", mod=mod, device=device, target=target) - params.update(input_data) - result = ex.evaluate()(**params) - if isinstance(result, tvm.runtime.NDArray): - return [ - result.numpy(), - ] - return [r.numpy() for r in result] - - -def get_tvm_output(mod, params, target, device, input_data, compiled_names, num): - """Generic function to execute and get tvm output""" - - lib = relay.build(mod, target=target, params=params) - gmod = graph_executor.GraphModule(lib["default"](device)) - for name in compiled_names: - gmod.set_input(name, input_data[name]) - gmod.run() - outputs = [] - for i in range(num): - outputs.append(gmod.get_output(i).numpy()) - return outputs - - -def verify_model(func, input_data, rtol=1e-5, atol=1e-5, input_shape=None): +def verify_model(func, input_data, rtol=1e-5, atol=1e-5): if not (isinstance(input_data, (tuple, list))): input_data = [input_data] @@ -90,13 +59,11 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5, input_shape=None): compiled_input = {} for idx, data in enumerate(input_data): input_name = "input{}".format(idx) - if input_shape: - shape = input_shape[idx] - else: - shape = data.shape - input_shape_dict[input_name] = shape - input_spec.append(paddle.static.InputSpec(dtype=data.dtype, shape=shape, name=input_name)) + input_spec.append( + paddle.static.InputSpec(dtype=data.dtype, shape=data.shape, name=input_name) + ) input_names.append(input_name) + input_shape_dict[input_name] = data.shape if isinstance(data, np.ndarray): compiled_input[input_name] = data else: @@ -114,70 +81,24 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5, input_shape=None): mod, params = relay.frontend.from_paddle(baseline_model, input_shape_dict) parms_num = min(len(input_names), len(mod["main"].params)) compiled_names = [] - for arg in mod["main"].params: + for arg in mod["main"].params[:parms_num]: assert arg.name_hint in input_names or arg.name_hint in params if arg.name_hint in input_names: compiled_names.append(arg.name_hint) with tvm.transform.PassContext(opt_level=3): for target, dev in tvm.testing.enabled_targets(): - if input_shape: - tvm_output = get_tvm_output_with_vm(mod, params, target, dev, compiled_input) - else: - tvm_output = get_tvm_output( - mod, params, target, dev, compiled_input, compiled_names, len(baseline_outputs) - ) - - for baseline_output, compiled_output in zip(baseline_outputs, tvm_output): - assert_shapes_match(baseline_output, compiled_output) - tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) - + lib = relay.build(mod, target=target, params=params) + gmod = graph_executor.GraphModule(lib["default"](dev)) + for name in compiled_names: + gmod.set_input(name, compiled_input[name]) + gmod.run() -@tvm.testing.uses_gpu -def test_forward_math(): - class MathAPI(nn.Layer): - def __init__(self, api_name): - super(MathAPI, self).__init__() - for candidate in (paddle, paddle.nn.functional): - self.func = getattr(candidate, api_name, None) - if self.func: - break + for i, baseline_output in enumerate(baseline_outputs): + compiled_output = gmod.get_output(i).numpy() - @paddle.jit.to_static - def forward(self, inputs): - return self.func(inputs) - - input_data = paddle.rand([1, 2, 5, 5], dtype="float32") - api_list = [ - "abs", - "acos", - "asin", - "atan", - "ceil", - "cos", - "cosh", - "erf", - "exp", - "floor", - "log", - "log2", - "log10", - "log1p", - "numel", - "relu", - "round", - "rsqrt", - "sigmoid", - "sign", - "rsqrt", - "sin", - "sinh", - "sqrt", - "tan", - "tanh", - ] - for api_name in api_list: - verify_model(MathAPI(api_name), input_data) + assert_shapes_match(baseline_output, compiled_output) + tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) @tvm.testing.uses_gpu @@ -235,66 +156,27 @@ def forward(self, inputs): verify_model(ArgMax3(), input_data=input_data) -@tvm.testing.uses_gpu -def test_forward_argmin(): - input_shape = [1, 3, 10, 10] - - class ArgMin(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return paddle.argmin(inputs) - - class ArgMin1(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return inputs.argmin(axis=1) - - class ArgMin2(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return inputs.argmin(axis=1, keepdim=False) - - class ArgMin3(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return inputs.argmin(axis=2, keepdim=True) - - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(ArgMin(), input_data=input_data) - verify_model(ArgMin1(), input_data=input_data) - verify_model(ArgMin2(), input_data=input_data) - verify_model(ArgMin3(), input_data=input_data) - - -@tvm.testing.uses_gpu -def test_forward_argsort(): - @paddle.jit.to_static - def argsort(inputs): - return paddle.argsort(inputs) - - @paddle.jit.to_static - def argsort2(inputs): - return paddle.argsort(inputs, axis=0, descending=True) - - input_shape = [2, 3, 5] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(argsort, input_data) - input_data2 = np.random.randint(100, size=input_shape) - verify_model(argsort2, input_data2) - - @tvm.testing.uses_gpu def test_forward_assign(): - class Assign(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return paddle.assign(inputs) + @paddle.jit.to_static + def assign(inputs): + return paddle.assign(inputs) input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(Assign(), [input_data]) + verify_model( + assign, + [ + input_data, + ], + ) input_data2 = np.random.randint(100, size=input_shape) - verify_model(Assign(), [input_data2], input_shape=[[-1, -1]]) + verify_model( + assign, + [ + input_data2, + ], + ) @tvm.testing.uses_gpu @@ -346,8 +228,18 @@ def cast2(inputs, dtype="int64"): input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") * 100 - verify_model(cast1, [input_data]) - verify_model(cast2, [input_data]) + verify_model( + cast1, + [ + input_data, + ], + ) + verify_model( + cast2, + [ + input_data, + ], + ) @tvm.testing.uses_gpu @@ -371,30 +263,39 @@ def concat_unsqueeze2(inputs): @tvm.testing.uses_gpu def test_forward_cumsum(): - class Cumsum1(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return paddle.cumsum(inputs) + @paddle.jit.to_static + def cusum1(inputs): + return paddle.cumsum(inputs) - class Cumsum2(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return paddle.cumsum(inputs, axis=0) + @paddle.jit.to_static + def cusum2(inputs): + return paddle.cumsum(inputs, axis=0) - class Cumsum3(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - return paddle.cumsum(inputs, axis=1) + @paddle.jit.to_static + def cusum3(inputs): + return paddle.cumsum(inputs, axis=1) input_data = paddle.randint(0, 100, (10, 10), dtype=paddle.int32) - verify_model(Cumsum1(), input_data) - verify_model(Cumsum1(), [input_data.astype(paddle.int64)]) - verify_model(Cumsum2(), input_data) - verify_model(Cumsum3(), input_data) + verify_model(cusum1, [input_data]) + verify_model(cusum1, [input_data.astype(paddle.int64)]) + verify_model( + cusum2, + [ + input_data, + ], + ) + verify_model( + cusum3, + [ + input_data, + ], + ) @tvm.testing.uses_gpu def test_forward_conv(): + conv2d_input_shape = [1, 3, 10, 10] + class Conv2D1(nn.Layer): def __init__(self): super(Conv2D1, self).__init__() @@ -415,51 +316,9 @@ def __init__(self): def forward(self, inputs): return self.softmax(self.conv(inputs)) - class Conv2D3(nn.Layer): - def __init__(self): - super(Conv2D3, self).__init__() - self.conv = nn.Conv2D(3, 6, 7, groups=3, bias_attr=False, padding="SAME") - - @paddle.jit.to_static - def forward(self, inputs): - return self.conv(inputs) - - class Conv2D4(nn.Layer): - def __init__(self): - super(Conv2D4, self).__init__() - self.conv = nn.Conv2D( - 3, 6, 7, groups=3, bias_attr=False, padding=[1, 2, 0, 1], stride=2, dilation=2 - ) - - @paddle.jit.to_static - def forward(self, inputs): - return self.conv(inputs) - - conv2d_input_shape = [1, 3, 112, 112] conv2d_input_data = paddle.rand(conv2d_input_shape, dtype="float32") verify_model(Conv2D1(), input_data=conv2d_input_data) verify_model(Conv2D2(), input_data=conv2d_input_data) - verify_model(Conv2D3(), input_data=conv2d_input_data) - verify_model(Conv2D4(), input_data=conv2d_input_data) - verify_model(Conv2D1(), conv2d_input_data, input_shape=[[-1, 3, 112, 112]]) - - -@tvm.testing.uses_gpu -def test_forward_dot(): - @paddle.jit.to_static - def dot1(x, y): - return paddle.dot(x, y) - - @paddle.jit.to_static - def dot2(x): - y = paddle.to_tensor(np.random.rand(10).astype("float32")) - return paddle.dot(x, y) - - x_data = paddle.rand([10, 3], dtype="float32") - y_data = paddle.rand([10, 3], dtype="float32") - verify_model(dot1, input_data=[x_data, y_data]) - x_data = paddle.rand([10], dtype="float32") - verify_model(dot2, input_data=[x_data]) @tvm.testing.uses_gpu @@ -474,55 +333,19 @@ def dropout(inputs): verify_model(dropout, input_data=input_data) -@tvm.testing.uses_gpu -def test_forward_expand(): - @paddle.jit.to_static - def expand1(inputs): - return paddle.expand(inputs, shape=[2, 3]) - - @paddle.jit.to_static - def expand2(inputs, shape): - return paddle.expand(inputs, shape=shape) - - x_shape = [3] - x_data = paddle.rand(x_shape, dtype="float32") - verify_model(expand1, input_data=[x_data]) - shape = paddle.to_tensor(np.array([2, 3]).astype("int32")) - verify_model(expand2, [x_data, shape], input_shape=[[3], [2]]) - - -@tvm.testing.uses_gpu -def test_forward_expand_as(): - @paddle.jit.to_static - def expand_as(x, y): - z = paddle.expand_as(x, y) - z += y - return z - - data_x = paddle.to_tensor([1, 2, 3], dtype="int32") - data_y = paddle.to_tensor([[1, 2, 3], [4, 5, 6]], dtype="float32") - verify_model(expand_as, [data_x, data_y]) - - @tvm.testing.uses_gpu def test_forward_shape_full(): @paddle.jit.to_static def full1(inputs): - return paddle.full(inputs, 3.14) + return paddle.full(paddle.shape(inputs), 3.14) @paddle.jit.to_static def full2(inputs): return paddle.full(paddle.shape(inputs), 1.0, dtype=inputs.dtype) - @paddle.jit.to_static - def shape1(inputs): - return paddle.shape(inputs) - input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(shape1, input_data=[input_data]) - shape = paddle.to_tensor(np.array(input_shape, "int32")) - verify_model(full1, input_data=[shape], input_shape=[[4]]) + verify_model(full1, input_data=[input_data]) verify_model(full2, input_data=[input_data]) @@ -539,71 +362,7 @@ def ones_like2(inputs): input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") verify_model(ones_like1, input_data=input_data) - verify_model(ones_like2, input_data, input_shape=[[-1, -1, -1, -1]]) - - -@tvm.testing.uses_gpu -def test_forward_ones(): - @paddle.jit.to_static - def ones1(inputs): - ones = paddle.ones([1, 3, 10, 10]) - out = inputs + ones - return out - - @paddle.jit.to_static - def ones2(inputs): - shape = paddle.to_tensor([1, 3, 10, 10], dtype="int32") - ones = paddle.ones(shape) - out = inputs + ones - return out - - input_shape = [1, 3, 10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(ones1, input_data=input_data) - verify_model(ones2, input_data=input_data) - - -def test_forward_elemwise(): - class ElemwiseAPI(nn.Layer): - def __init__(self, api_name): - super(ElemwiseAPI, self).__init__() - self.api_name_ = api_name - for candidate in (paddle, paddle.nn.functional): - self.func = getattr(candidate, api_name, None) - if self.func: - break - - @paddle.jit.to_static - def forward(self, input1, input2): - y = self.func(input1, input2) - if "equal" in self.api_name_ or "than" in self.api_name_: - y = paddle.cast(y, "int32") - return y - - api_list = [ - "floor_divide", - "floor_mod", - "maximum", - "minimum", - "equal", - "greater_equal", - "greater_than", - "less_equal", - "less_than", - "not_equal", - ] - input_shape = [10, 10] - input_shape_2 = [ - 10, - ] - x_data = paddle.rand(input_shape, dtype="float32") - y_data = paddle.rand(input_shape_2, dtype="float32") - x_data_2 = paddle.randint(1, 100, input_shape_2, dtype="int32") - y_data_2 = paddle.randint(1, 100, input_shape, dtype="int32") - for api_name in api_list: - if api_name not in ["floor_divide"]: - verify_model(ElemwiseAPI(api_name), [x_data, y_data]) - verify_model(ElemwiseAPI(api_name), [x_data_2, y_data_2]) + verify_model(ones_like2, input_data=input_data) @tvm.testing.uses_gpu @@ -618,54 +377,25 @@ def gelu(inputs): @tvm.testing.uses_gpu -def test_forward_activation(): - class Activation(nn.Layer): - def __init__(self, op_name): - super(Activation, self).__init__() - self.op_name_ = op_name - for candidate in (paddle.nn.functional, paddle): - self.func = getattr(candidate, op_name, None) - if self.func: - break - - @paddle.jit.to_static - def forward(self, inputs): - return self.func(inputs) +def test_forward_hard_sigmoid(): + @paddle.jit.to_static + def hard_sigmoid(inputs): + return nn.functional.hardsigmoid(inputs) input_shape = [1, 3, 10, 10] - input_data = paddle.normal(shape=input_shape) * 10.0 - input_data_2 = paddle.normal(shape=input_shape).astype("float64") * 10.0 - op_list = [ - "hardsigmoid", - "hardswish", - "leaky_relu", - "log_sigmoid", - "log_softmax", - "sigmoid", - ] - for op_name in op_list: - verify_model(Activation(op_name), input_data=input_data) + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(hard_sigmoid, input_data=input_data) @tvm.testing.uses_gpu -def test_forward_check_tensor(): - @paddle.jit.to_static - def isfinite(inputs): - return paddle.cast(paddle.isfinite(inputs), "int32") - +def test_forward_hard_swish(): @paddle.jit.to_static - def isinf(inputs): - return paddle.cast(paddle.isinf(inputs), "int32") + def hard_swish(inputs): + return nn.functional.hardswish(inputs) - @paddle.jit.to_static - def isnan(inputs): - return paddle.cast(paddle.isnan(inputs), "int32") - - input_shape = [5, 5] + input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(isfinite, input_data=input_data) - verify_model(isinf, input_data=input_data) - verify_model(isnan, input_data=input_data) + verify_model(hard_swish, input_data=input_data) @tvm.testing.uses_gpu @@ -693,47 +423,14 @@ def forward(self, inputs): @tvm.testing.uses_gpu -def test_forward_logical_op(): - class LogicalOp(nn.Layer): - def __init__(self, op_name, out=False): - super(LogicalOp, self).__init__() - self.out = out - for candidate in (paddle, paddle.nn.functional): - self.func = getattr(candidate, op_name, None) - if self.func: - break +def test_forward_leaky_relu(): + @paddle.jit.to_static + def leaky_relu(inputs): + return nn.functional.leaky_relu(inputs) - @paddle.jit.to_static - def forward(self, x, y): - if self.out: - out = paddle.to_tensor([True, True, True]) - z = self.func(x, y, out=out) - else: - z = self.func(x, y) - return paddle.cast(z, "int32") - - class LogicalOp_not(LogicalOp): - @paddle.jit.to_static - def forward(self, x): - if self.out: - out = paddle.to_tensor([True, True, True]) - z = self.func(x, out=out) - else: - z = self.func(x) - return paddle.cast(z, "int32") - - op_list = [ - "logical_or", - "logical_xor", - "logical_and", - ] - x = paddle.to_tensor([True]) - y = paddle.to_tensor([True, False, True, False]) - for op_name in op_list: - verify_model(LogicalOp(op_name, False), [x, y]) - verify_model(LogicalOp(op_name, True), [x, y]) - verify_model(LogicalOp_not("logical_not", False), [y]) - verify_model(LogicalOp_not("logical_not", True), [y]) + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(leaky_relu, input_data=input_data) @tvm.testing.uses_gpu @@ -755,7 +452,7 @@ def forward(self, inputs): input_data = paddle.randint(0, 10, input_shape, dtype="int32") weight = paddle.rand([10, 4], dtype="float32") verify_model(look_up, input_data=[input_data, weight]) - verify_model(LookUp(), input_data, input_shape=[[-1, -1, -1, -1]]) + verify_model(LookUp(), input_data=input_data) @tvm.testing.uses_gpu @@ -808,44 +505,6 @@ def forward(self, input1, input2): verify_model(MatMul1(), input_data=[input_data1, input_data2]) -@tvm.testing.uses_gpu -def test_forward_meshgrid(): - @paddle.jit.to_static - def t(x, y, z): - return paddle.meshgrid(x, y, z) - - x = paddle.randint(low=0, high=100, shape=[2]) - y = paddle.randint(low=0, high=100, shape=[3]) - z = paddle.randint(low=0, high=100, shape=[5]) - verify_model(t, [x, y, z]) - - -def test_forward_mm(): - class Mm(nn.Layer): - def forward(self, input1, input2): - return paddle.mm(input1, input2) - - # matrix x vector - input_data1 = paddle.randn((3, 4), dtype="float32") - input_data2 = paddle.randn((4,), dtype="float32") - verify_model(Mm(), input_data=[input_data1, input_data2]) - - # matrix x matrix - input_data1 = paddle.randn((5, 4), dtype="float32") - input_data2 = paddle.randn((4, 5), dtype="float32") - verify_model(Mm(), input_data=[input_data1, input_data2]) - - # batched matrix x batched matrix - input_data1 = paddle.randn((10, 3, 4), dtype="float32") - input_data2 = paddle.randn((10, 4, 5), dtype="float32") - verify_model(Mm(), input_data=[input_data1, input_data2]) - - # batched matrix x broadcasted matrix - input_data1 = paddle.randn((10, 3, 4), dtype="float32") - input_data2 = paddle.randn((4, 5), dtype="float32") - verify_model(Mm(), input_data=[input_data1, input_data2]) - - @tvm.testing.uses_gpu def test_forward_pool2d(): @paddle.jit.to_static @@ -858,42 +517,32 @@ def pool2d2(inputs): @paddle.jit.to_static def pool2d3(inputs): - output = nn.functional.max_pool2d(inputs, kernel_size=2, stride=2, padding=0) - return output - - @paddle.jit.to_static - def pool2d4(inputs): - output, max_indices = nn.functional.max_pool2d( + return nn.functional.max_pool2d( inputs, kernel_size=2, stride=2, padding=0, return_mask=True ) - return output input_data = paddle.uniform(shape=[1, 2, 32, 32], dtype="float32", min=-1, max=1) - verify_model(pool2d1, input_data, input_shape=[[-1, 2, 32, 32]]) + verify_model(pool2d1, input_data=input_data) verify_model(pool2d2, input_data=input_data) - input_data1 = paddle.uniform(shape=[1, 2, 1, 50], dtype="float32", min=-1, max=1) - verify_model(pool2d3, input_data=input_data1) + # verify_model(pool2d3, input_data=input_data) @tvm.testing.uses_gpu -def test_forward_rank(): - class Rank(nn.Layer): - @paddle.jit.to_static - def forward(self, inputs): - rank = paddle.rank(inputs) - rank = paddle.unsqueeze(rank, axis=0) - output = inputs + rank - return output +def test_forward_relu(): + @paddle.jit.to_static + def relu(inputs): + return nn.functional.relu(inputs) - input_shape = [1, 2, 1, 3, 1] + input_shape = [10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(Rank(), input_data=input_data) + verify_model(relu, input_data=input_data) @tvm.testing.uses_gpu def test_forward_reshape(): @paddle.jit.to_static - def reshape1(inputs, new_shape): + def reshape1(inputs, x): + new_shape = paddle.shape(x) return paddle.reshape(inputs, new_shape) @paddle.jit.to_static @@ -903,7 +552,7 @@ def reshape2(inputs): @paddle.jit.to_static def reshape3(inputs): data_shape = inputs.shape - return inputs.reshape([data_shape[1], data_shape[2], data_shape[0]]) + return inputs.reshape([data_shape[0] * data_shape[1], data_shape[2]]) @paddle.jit.to_static def reshape4(inputs, x): @@ -913,8 +562,7 @@ def reshape4(inputs, x): input_shape = [2, 1, 10, 1, 10] input_data = paddle.rand(input_shape, dtype="float32") input_data2 = paddle.randn([2, 1, 10, 10]) - new_shape = paddle.shape(input_data2) - verify_model(reshape1, [input_data, new_shape], input_shape=[[2, 1, 10, 1, 10], [4]]) + verify_model(reshape1, input_data=[input_data, input_data2]) verify_model(reshape2, input_data=input_data) verify_model(reshape3, input_data=paddle.randn((2, 3, 4))) verify_model(reshape4, input_data=[input_data, input_data2]) @@ -943,8 +591,8 @@ def scale2(inputs): @tvm.testing.uses_gpu def test_forward_slice(): @paddle.jit.to_static - def slice1(inputs, end): - return inputs[:, :, :, :end] + def slice1(inputs): + return inputs[:, :, :, :3] @paddle.jit.to_static def slice2(inputs): @@ -960,53 +608,56 @@ def slice4(inputs): x1 = paddle.to_tensor([3]) + paddle.to_tensor([1]) return inputs[:, x0:, 1:x1, :] + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model( + slice1, + input_data=[ + input_data, + ], + ) + verify_model(slice2, input_data=input_data) + # need op "strided_slice" + # verify_model(slice3, input_data=paddle.randn((4, 4))) + # need op "assign_value" + # verify_model(slice4, input_data=input_data) + + +@tvm.testing.uses_gpu +def test_forward_tanh(): @paddle.jit.to_static - def slice5(inputs): - x0 = paddle.to_tensor([3]) - return inputs[:, 1::1, 2::x0, 4:10] + def tanh(inputs): + return paddle.tanh(inputs) input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - end = paddle.to_tensor(np.array([3])) - verify_model(slice1, [input_data, end], input_shape=[[1, 3, 10, 10], [1]]) - verify_model(slice2, input_data=input_data) - verify_model(slice3, input_data=paddle.randn((4, 4))) - verify_model(slice4, input_data=input_data) - verify_model(slice5, input_data=input_data) + verify_model(tanh, input_data=input_data) if __name__ == "__main__": - test_forward_add_subtract() - test_forward_argmax() - test_forward_argmin() - test_forward_argsort() - test_forward_assign() - test_forward_batch_norm() - test_forward_cast() - test_forward_concat_unsqueeze() - test_forward_conv() - test_forward_cumsum() - test_forward_dot() - test_forward_dropout() - test_forward_elemwise() - test_forward_expand() - test_forward_expand_as() - test_forward_shape_full() - test_forward_ones() - test_forward_ones_like() - test_forward_gelu() - test_forward_math() - test_forward_activation() - test_forward_check_tensor() - test_forward_layer_norm() - test_forward_logical_op() +# test_forward_add_subtract() +# test_forward_argmax() +# test_forward_assign() +# test_forward_batch_norm() +# test_forward_cast() +# test_forward_concat_unsqueeze() +# test_forward_cumsum() +# test_forward_conv() +# test_forward_dropout() +# test_forward_shape_full() +# test_forward_ones_like() +# test_forward_gelu() +# test_forward_hard_sigmoid() +# test_forward_hard_swish() +# test_forward_layer_norm() +# test_forward_leaky_relu() test_forward_look_up() - test_forward_matmul() - test_forward_meshgrid() - test_forward_mm() test_forward_multiply() + test_forward_matmul() test_forward_pool2d() - test_forward_rank() + test_forward_relu() test_forward_reshape() test_forward_scale() test_forward_slice() + test_forward_tanh() + From 8d865b7b1eab9057413e2b213a465f227f172cd2 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 19:30:34 +0800 Subject: [PATCH 14/24] Update paddlepaddle.py --- python/tvm/relay/frontend/paddlepaddle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 846dd428ec35..95d6e6380d5e 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -370,7 +370,7 @@ def convert_expand(g, op, block): sizes = op.attr("shape") if isinstance(sizes, np.ndarray): - sizes = size.tolist() + sizes = sizes.tolist() out = _op.broadcast_to(x, sizes) g.add_node(op.output("Out")[0], out) From 96afd3d86e8e59e94578b7feeeade8389929af2b Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 19:36:01 +0800 Subject: [PATCH 15/24] Update test_forward.py --- .../frontend/paddlepaddle/test_forward.py | 261 +++++++++++++++--- 1 file changed, 225 insertions(+), 36 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 048ecff8218e..0ad7b5b4dcce 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -126,7 +126,7 @@ def add_subtract3(inputs1, inputs2): @tvm.testing.uses_gpu -def test_forward_argmax(): +def test_forward_arg_max_min(): input_shape = [1, 3, 10, 10] class ArgMax(nn.Layer): @@ -149,11 +149,52 @@ class ArgMax3(nn.Layer): def forward(self, inputs): return inputs.argmax(axis=2, keepdim=True) + class ArgMin(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argmin(inputs) + + class ArgMin1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=1) + + class ArgMin2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmax(axis=1, keepdim=False) + + class ArgMin3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmin(axis=2, keepdim=True) + input_data = paddle.rand(input_shape, dtype="float32") verify_model(ArgMax(), input_data=input_data) verify_model(ArgMax1(), input_data=input_data) verify_model(ArgMax2(), input_data=input_data) verify_model(ArgMax3(), input_data=input_data) + verify_model(ArgMin(), input_data=input_data) + verify_model(ArgMin1(), input_data=input_data) + verify_model(ArgMin2(), input_data=input_data) + verify_model(ArgMin3(), input_data=input_data) + + +@tvm.testing.uses_gpu +def test_forward_argsort(): + @paddle.jit.to_static + def argsort(inputs): + return paddle.argsort(inputs) + + @paddle.jit.to_static + def argsort2(inputs): + return paddle.argsort(inputs, axis=0, descending=True) + + input_shape = [2, 3, 5] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(argsort, input_data) + input_data2 = np.random.randint(100, size=input_shape) + verify_model(argsort2, input_data2) @tvm.testing.uses_gpu @@ -162,6 +203,11 @@ def test_forward_assign(): def assign(inputs): return paddle.assign(inputs) + @paddle.jit.to_static + def assign_value(inputs): + x = paddle.to_tensor(np.array([3]).astype("float32")) + return inputs + x + input_shape = [2, 3] input_data = paddle.rand(input_shape, dtype="float32") verify_model( @@ -177,6 +223,7 @@ def assign(inputs): input_data2, ], ) + verify_model(assign_value, [input_data]) @tvm.testing.uses_gpu @@ -242,6 +289,27 @@ def cast2(inputs, dtype="int64"): ) +@tvm.testing.uses_gpu +def test_forward_check_tensor(): + @paddle.jit.to_static + def isfinite(inputs): + return paddle.cast(paddle.isfinite(inputs), "int32") + + @paddle.jit.to_static + def isnan(inputs): + return paddle.cast(paddle.isnan(inputs), "int32") + + @paddle.jit.to_static + def isinf(inputs): + return paddle.cast(paddle.isinf(inputs), "int32") + + input_shape = [5, 5] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(isfinite, input_data=input_data) + verify_model(isnan, input_data=input_data) + verify_model(isinf, input_data=input_data) + + @tvm.testing.uses_gpu def test_forward_concat_unsqueeze(): @paddle.jit.to_static @@ -321,6 +389,19 @@ def forward(self, inputs): verify_model(Conv2D2(), input_data=conv2d_input_data) +@tvm.testing.uses_gpu +def test_forward_dot(): + @paddle.jit.to_static + def dot(x, y): + return paddle.dot(x, y) + + x_shape = [10, 3] + y_shape = [10, 3] + x_data = paddle.rand(x_shape, dtype="float32") + y_data = paddle.rand(y_shape, dtype="float32") + verify_model(dot, input_data=[x_data, y_data]) + + @tvm.testing.uses_gpu def test_forward_dropout(): @paddle.jit.to_static @@ -333,6 +414,83 @@ def dropout(inputs): verify_model(dropout, input_data=input_data) +def test_forward_elemwise(): + class ElemwiseAPI(nn.Layer): + def __init__(self, api_name): + super(ElemwiseAPI, self).__init__() + self.api_name_ = api_name + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, input1, input2): + if self.api_name_ == "pow": + # for pow, only support float32 + input1 = paddle.cast(input1, dtype="float32") + input2 = paddle.cast(input2, dtype="float32") + + y = self.func(input1, input2) + + if "equal" in self.api_name_ or "than" in self.api_name_: + # for compare operation, cast boolean result to int32 + y = paddle.cast(y, "int32") + return y + + api_list = [ + "floor_divide", + "maximum", + "minimum", + "mod", + "equal", + "greater_equal", + "greater_than", + "less_equal", + "less_than", + "not_equal", + "pow", + ] + input_shape = [10, 10] + input_shape_2 = [ + 10, + ] + x_data = paddle.randint(1, 10, input_shape, dtype="int32") + y_data = paddle.randint(1, 10, input_shape_2, dtype="int32") + for api_name in api_list: + verify_model(ElemwiseAPI(api_name), [x_data, y_data]) + + +@tvm.testing.uses_gpu +def test_forward_expand(): + @paddle.jit.to_static + def expand1(inputs): + return paddle.expand(inputs, shape=[2, 3]) + + @paddle.jit.to_static + def expand2(inputs): + shape = paddle.to_tensor(np.array([2, 3]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + x_shape = [3] + x_data = paddle.rand(x_shape, dtype="float32") + verify_model(expand1, input_data=[x_data]) + verify_model(expand2, input_data=[x_data]) + + +@tvm.testing.uses_gpu +def test_forward_expand_as(): + @paddle.jit.to_static + def expand_as(x, y): + z = paddle.expand_as(x, y) + z += y + return z + + data_x = paddle.to_tensor([1, 2, 3], dtype="int32") + data_y = paddle.to_tensor([[1, 2, 3], [4, 5, 6]], dtype="float32") + verify_model(expand_as, [data_x, data_y]) + + @tvm.testing.uses_gpu def test_forward_shape_full(): @paddle.jit.to_static @@ -433,6 +591,29 @@ def leaky_relu(inputs): verify_model(leaky_relu, input_data=input_data) +@tvm.testing.uses_gpu +def test_forward_logical_api(): + class LogicalAPI(nn.Layer): + def __init__(self, api_name): + super(LogicalAPI, self).__init__() + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break + + @paddle.jit.to_static + def forward(self, x, y): + out = paddle.to_tensor([True, True, True]) + z = self.func(x, y, out=out) + return paddle.cast(z, "int32") + + x = paddle.to_tensor([True]) + y = paddle.to_tensor([True, False, True, False]) + verify_model(LogicalAPI("logical_and"), [x, y]) + verify_model(LogicalAPI("logical_or"), [x, y]) + verify_model(LogicalAPI("logical_xor"), [x, y]) + + @tvm.testing.uses_gpu def test_forward_look_up(): @paddle.jit.to_static @@ -527,17 +708,6 @@ def pool2d3(inputs): # verify_model(pool2d3, input_data=input_data) -@tvm.testing.uses_gpu -def test_forward_relu(): - @paddle.jit.to_static - def relu(inputs): - return nn.functional.relu(inputs) - - input_shape = [10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(relu, input_data=input_data) - - @tvm.testing.uses_gpu def test_forward_reshape(): @paddle.jit.to_static @@ -624,40 +794,59 @@ def slice4(inputs): @tvm.testing.uses_gpu -def test_forward_tanh(): - @paddle.jit.to_static - def tanh(inputs): - return paddle.tanh(inputs) +def test_forward_math_api(): + class MathAPI(nn.Layer): + def __init__(self, api_name): + super(MathAPI, self).__init__() + for candidate in (paddle, paddle.nn.functional): + self.func = getattr(candidate, api_name, None) + if self.func: + break + @paddle.jit.to_static + def forward(self, inputs): + return self.func(inputs) + + api_list = [ + "exp", + "relu", + "tanh", + ] input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(tanh, input_data=input_data) + for api_name in api_list: + verify_model(MathAPI(api_name), input_data=input_data) if __name__ == "__main__": -# test_forward_add_subtract() -# test_forward_argmax() -# test_forward_assign() -# test_forward_batch_norm() -# test_forward_cast() -# test_forward_concat_unsqueeze() -# test_forward_cumsum() -# test_forward_conv() -# test_forward_dropout() -# test_forward_shape_full() -# test_forward_ones_like() -# test_forward_gelu() -# test_forward_hard_sigmoid() -# test_forward_hard_swish() -# test_forward_layer_norm() -# test_forward_leaky_relu() + test_forward_add_subtract() + test_forward_arg_max_min() + test_forward_argsort() + test_forward_assign() + test_forward_batch_norm() + test_forward_cast() + test_forward_check_tensor() + test_forward_concat_unsqueeze() + test_forward_cumsum() + test_forward_conv() + test_forward_dot() + test_forward_dropout() + test_forward_elemwise() + test_forward_expand() + test_forward_expand_as() + test_forward_math_api() + test_forward_logical_api() + test_forward_shape_full() + test_forward_ones_like() + test_forward_gelu() + test_forward_hard_sigmoid() + test_forward_hard_swish() + test_forward_layer_norm() + test_forward_leaky_relu() test_forward_look_up() test_forward_multiply() test_forward_matmul() test_forward_pool2d() - test_forward_relu() test_forward_reshape() test_forward_scale() test_forward_slice() - test_forward_tanh() - From ef7a00311455bb9c76a236f68fe0615b077a7b40 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 19:46:09 +0800 Subject: [PATCH 16/24] Update paddlepaddle.py --- python/tvm/relay/frontend/paddlepaddle.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 95d6e6380d5e..96ccc7c26002 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -25,7 +25,6 @@ from .. import analysis from .. import ty as _ty -from ... import nd as _nd from .. import expr as _expr from .. import function as _function from .. import ty as _ty @@ -883,11 +882,6 @@ def convert_unsqueeze(g, op, block): "elementwise_div": convert_elementwise_op, "elementwise_mul": convert_elementwise_op, "elementwise_sub": convert_elementwise_op, - "elementwise_mod": convert_elementwise_op, - "elementwise_max": convert_elementwise_op, - "elementwise_min": convert_elementwise_op, - "elementwise_pow": convert_elementwise_op, - "elementwise_floordiv": convert_elementwise_op, "equal": convert_elementwise_op, "exp": convert_unary_op, "expand_v2": convert_expand, @@ -896,8 +890,6 @@ def convert_unsqueeze(g, op, block): "fill_any_like": convert_fill_any_like, "fill_constant": convert_fill_constant, "gelu": convert_gelu, - "greater_equal": convert_elementwise_op, - "greater_than": convert_elementwise_op, "hard_sigmoid": convert_hard_sigmoid, "hard_swish": convert_hard_swish, "isfinite_v2": convert_unary_op, @@ -905,8 +897,6 @@ def convert_unsqueeze(g, op, block): "isnan_v2": convert_unary_op, "layer_norm": convert_layer_norm, "leaky_relu": convert_leaky_relu, - "less_equal": convert_elementwise_op, - "less_than": convert_elementwise_op, "logical_and": convert_binary_logical_op, "logical_or": convert_binary_logical_op, "logical_xor": convert_binary_logical_op, @@ -914,7 +904,6 @@ def convert_unsqueeze(g, op, block): "matmul": convert_matmul, "matmul_v2": convert_matmul, "mul": convert_mul, - "not_equal": convert_elementwise_op, "pool2d": convert_pool2d, "relu": convert_unary_op, "reshape2": convert_reshape, @@ -968,7 +957,7 @@ def extract_parameters(self, program, scope=None): if isinstance(scope, dict): self.params[name] = scope[name] else: - self.params[name] = _nd.array(np.array(scope.var(name).get_tensor())) + self.params[name] = np.array(scope.var(name).get_tensor()) self.nodes[name] = _expr.const(self.params[name]) def check_input_shape(self, op, block): @@ -991,8 +980,6 @@ def check_unsupported_ops(self, program): for block in program.blocks: for op in block.ops: if op.type == "fetch": - # `fetch` is a flag of output tensors - # there's no need to handle this continue if op.type not in _convert_map: unsupported_ops.add(op.type) From 43ae5ab482c903aba36ef06f1705ddb4e552fb21 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 19:49:20 +0800 Subject: [PATCH 17/24] Update paddlepaddle.py --- python/tvm/relay/frontend/paddlepaddle.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 96ccc7c26002..668c5d7b8261 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -456,8 +456,7 @@ def convert_hard_sigmoid(g, op, block): slope = op.attr("slope") x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype - out = x * _expr.const(slope, dtype) + _expr.const(0.5, dtype) + out = x * _expr.const(slope) + _expr.const(0.5) out = _op.clip(out, 0, 1) g.add_node(op.output("Out")[0], out) @@ -472,9 +471,8 @@ def convert_hard_swish(g, op, block): assert np.isclose(scale, 6.0), "Only support scale==6.0 for PaddlePaddle's hard_swish" assert np.isclose(threshold, 6.0), "Only support threshold==6.0 for PaddlePaddle's hard_swish" x = g.get_node(op.input("X")[0]) - dtype = infer_type(x).checked_type.dtype out = _op.clip(x, -1 * offset, offset) - out = out / _expr.const(threshold, dtype) + _expr.const(0.5, dtype) + out = out / _expr.const(threshold) + _expr.const(0.5) out = x * out g.add_node(op.output("Out")[0], out) @@ -701,7 +699,7 @@ def convert_pool2d(g, op, block): ksize = [1, 1] input_x = g.get_node(op.input("X")[0]) - _, _, in_h, in_w = infer_shape(input_x) + in_h, in_w = infer_shape(input_x)[2:] op_map = { "avg": "avg_pool2d", @@ -1054,6 +1052,7 @@ def from_translated_layer(self, layer, shape_dict): def from_paddle(program_or_layer, shape_dict=None, scope=None): """Convert a PaddlePaddle model into an equivalent Relay Function. + PaddlePaddle Program/TranslatedLayer represent the computation graph of PaddlePaddle model, and PaddlePaddle scope stores all the weights of PaddlePaddle model. """ From 8d0af492523f4716121a7a389385ae6e63e74d89 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 19:57:36 +0800 Subject: [PATCH 18/24] Update test_forward.py --- .../python/frontend/paddlepaddle/test_forward.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 0ad7b5b4dcce..8f74d7cb50d1 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -426,30 +426,14 @@ def __init__(self, api_name): @paddle.jit.to_static def forward(self, input1, input2): - if self.api_name_ == "pow": - # for pow, only support float32 - input1 = paddle.cast(input1, dtype="float32") - input2 = paddle.cast(input2, dtype="float32") - y = self.func(input1, input2) - if "equal" in self.api_name_ or "than" in self.api_name_: # for compare operation, cast boolean result to int32 y = paddle.cast(y, "int32") return y api_list = [ - "floor_divide", - "maximum", - "minimum", - "mod", "equal", - "greater_equal", - "greater_than", - "less_equal", - "less_than", - "not_equal", - "pow", ] input_shape = [10, 10] input_shape_2 = [ From 509e0236fd5388eb64ad49996146d122b8e631f2 Mon Sep 17 00:00:00 2001 From: Jason <928090362@qq.com> Date: Wed, 29 Sep 2021 20:02:32 +0800 Subject: [PATCH 19/24] Update test_forward.py --- .../frontend/paddlepaddle/test_forward.py | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 8f74d7cb50d1..ac6cd0ed94d9 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -803,34 +803,4 @@ def forward(self, inputs): if __name__ == "__main__": - test_forward_add_subtract() - test_forward_arg_max_min() - test_forward_argsort() - test_forward_assign() - test_forward_batch_norm() - test_forward_cast() - test_forward_check_tensor() - test_forward_concat_unsqueeze() - test_forward_cumsum() - test_forward_conv() - test_forward_dot() - test_forward_dropout() - test_forward_elemwise() - test_forward_expand() - test_forward_expand_as() - test_forward_math_api() - test_forward_logical_api() - test_forward_shape_full() - test_forward_ones_like() - test_forward_gelu() - test_forward_hard_sigmoid() - test_forward_hard_swish() - test_forward_layer_norm() - test_forward_leaky_relu() - test_forward_look_up() - test_forward_multiply() - test_forward_matmul() - test_forward_pool2d() - test_forward_reshape() - test_forward_scale() - test_forward_slice() + pytest.main([__file__]) From 3c34b5aa9a18f266df659986676bc9253463eed1 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Mon, 4 Oct 2021 08:28:50 +0000 Subject: [PATCH 20/24] add more cases for tests --- python/tvm/relay/frontend/paddlepaddle.py | 2 +- .../frontend/paddlepaddle/test_forward.py | 206 +++++++++++------- 2 files changed, 131 insertions(+), 77 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 668c5d7b8261..755c9f70f4ab 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -43,7 +43,7 @@ def _get_pad_size(in_size, dilated_kernel_size, stride_size): - """Calculate the paddings size.""" + """Calculate the paddings size for Conv/Pool in SAME padding mode.""" if stride_size == 1 or in_size % stride_size == 0: pad = max(dilated_kernel_size - stride_size, 0) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index ac6cd0ed94d9..4aa150169e42 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -24,6 +24,7 @@ import tvm.topi.testing from tvm import relay from tvm.contrib import graph_executor +import pytest import paddle import paddle.nn as nn @@ -99,6 +100,7 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): assert_shapes_match(baseline_output, compiled_output) tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) + break @tvm.testing.uses_gpu @@ -127,8 +129,6 @@ def add_subtract3(inputs1, inputs2): @tvm.testing.uses_gpu def test_forward_arg_max_min(): - input_shape = [1, 3, 10, 10] - class ArgMax(nn.Layer): @paddle.jit.to_static def forward(self, inputs): @@ -169,32 +169,50 @@ class ArgMin3(nn.Layer): def forward(self, inputs): return inputs.argmin(axis=2, keepdim=True) - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(ArgMax(), input_data=input_data) - verify_model(ArgMax1(), input_data=input_data) - verify_model(ArgMax2(), input_data=input_data) - verify_model(ArgMax3(), input_data=input_data) - verify_model(ArgMin(), input_data=input_data) - verify_model(ArgMin1(), input_data=input_data) - verify_model(ArgMin2(), input_data=input_data) - verify_model(ArgMin3(), input_data=input_data) + input_shapes = [[256], [10, 128], [100, 500, 200], [1, 3, 224, 224]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax(), input_data=input_data) + verify_model(ArgMin(), input_data=input_data) + for input_shape in input_shapes[1:]: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax1(), input_data=input_data) + verify_model(ArgMax2(), input_data=input_data) + verify_model(ArgMin1(), input_data=input_data) + verify_model(ArgMin2(), input_data=input_data) + for input_shape in input_shapes[2:]: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(ArgMax3(), input_data=input_data) + verify_model(ArgMin3(), input_data=input_data) @tvm.testing.uses_gpu def test_forward_argsort(): - @paddle.jit.to_static - def argsort(inputs): - return paddle.argsort(inputs) + class ArgSort1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs) - @paddle.jit.to_static - def argsort2(inputs): - return paddle.argsort(inputs, axis=0, descending=True) + class ArgSort2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs, axis=0, descending=True) - input_shape = [2, 3, 5] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(argsort, input_data) - input_data2 = np.random.randint(100, size=input_shape) - verify_model(argsort2, input_data2) + class ArgSort3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argsort(inputs, axis=-1, descending=True) + + input_shapes = [[256], [10, 20], [10, 10, 3], [1, 3, 5, 5]] + for input_shape in input_shapes: + # Avoid duplicate elements in the array which will bring + # different results with different sort algorithms + np.random.seed(13) + np_data = np.random.choice(range(-5000, 5000), np.prod(input_shape), replace=False) + input_data = paddle.to_tensor(np_data.reshape(input_shape).astype("int64")) + verify_model(ArgSort1(), [input_data]) + verify_model(ArgSort2(), [input_data]) + verify_model(ArgSort3(), [input_data]) @tvm.testing.uses_gpu @@ -291,23 +309,27 @@ def cast2(inputs, dtype="int64"): @tvm.testing.uses_gpu def test_forward_check_tensor(): - @paddle.jit.to_static - def isfinite(inputs): - return paddle.cast(paddle.isfinite(inputs), "int32") + class IsFinite(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isfinite(inputs), "int32") - @paddle.jit.to_static - def isnan(inputs): - return paddle.cast(paddle.isnan(inputs), "int32") + class IsNan(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isnan(inputs), "int32") - @paddle.jit.to_static - def isinf(inputs): - return paddle.cast(paddle.isinf(inputs), "int32") + class IsInf(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.cast(paddle.isinf(inputs), "int32") - input_shape = [5, 5] - input_data = paddle.rand(input_shape, dtype="float32") - verify_model(isfinite, input_data=input_data) - verify_model(isnan, input_data=input_data) - verify_model(isinf, input_data=input_data) + input_shapes = [[32], [8, 128], [2, 128, 256], [2, 3, 224, 224], [2, 2, 3, 229, 229]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(IsFinite(), input_data=input_data) + verify_model(IsNan(), input_data=input_data) + verify_model(IsInf(), input_data=input_data) @tvm.testing.uses_gpu @@ -391,15 +413,16 @@ def forward(self, inputs): @tvm.testing.uses_gpu def test_forward_dot(): - @paddle.jit.to_static - def dot(x, y): - return paddle.dot(x, y) + class Dot(nn.Layer): + @paddle.jit.to_static + def forward(self, x, y): + return paddle.dot(x, y) - x_shape = [10, 3] - y_shape = [10, 3] - x_data = paddle.rand(x_shape, dtype="float32") - y_data = paddle.rand(y_shape, dtype="float32") - verify_model(dot, input_data=[x_data, y_data]) + input_shapes = [[128], [8, 128]] + for input_shape in input_shapes: + x_data = paddle.rand(input_shape, dtype="float32") + y_data = paddle.rand(input_shape, dtype="float32") + verify_model(Dot(), input_data=[x_data, y_data]) @tvm.testing.uses_gpu @@ -435,44 +458,70 @@ def forward(self, input1, input2): api_list = [ "equal", ] - input_shape = [10, 10] - input_shape_2 = [ - 10, - ] - x_data = paddle.randint(1, 10, input_shape, dtype="int32") - y_data = paddle.randint(1, 10, input_shape_2, dtype="int32") - for api_name in api_list: - verify_model(ElemwiseAPI(api_name), [x_data, y_data]) + x_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] + y_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.randint(1, 1000, x_shape, dtype="int32") + y_data = paddle.randint(1, 1000, y_shape, dtype="int32") + for api_name in api_list: + verify_model(ElemwiseAPI(api_name), [x_data, y_data]) @tvm.testing.uses_gpu def test_forward_expand(): @paddle.jit.to_static def expand1(inputs): - return paddle.expand(inputs, shape=[2, 3]) + return paddle.expand(inputs, shape=[2, 128]) @paddle.jit.to_static def expand2(inputs): - shape = paddle.to_tensor(np.array([2, 3]).astype("int32")) + return paddle.expand(inputs, shape=[3, 1, 8, 256]) + + @paddle.jit.to_static + def expand3(inputs): + return paddle.expand(inputs, shape=[5, 1, 3, 224, 224]) + + @paddle.jit.to_static + def expand4(inputs): + shape = paddle.to_tensor(np.array([2, 128]).astype("int32")) return paddle.expand(inputs, shape=shape) - x_shape = [3] - x_data = paddle.rand(x_shape, dtype="float32") - verify_model(expand1, input_data=[x_data]) - verify_model(expand2, input_data=[x_data]) + @paddle.jit.to_static + def expand5(inputs): + shape = paddle.to_tensor(np.array([3, 1, 8, 256]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + @paddle.jit.to_static + def expand6(inputs): + shape = paddle.to_tensor(np.array([5, 1, 3, 224, 224]).astype("int32")) + return paddle.expand(inputs, shape=shape) + + data = paddle.rand([128], dtype="float32") + verify_model(expand1, input_data=[data]) + verify_model(expand4, input_data=[data]) + data = paddle.rand([8, 256], dtype="float32") + verify_model(expand2, input_data=[data]) + verify_model(expand5, input_data=[data]) + data = paddle.rand([1, 3, 224, 224], dtype="float32") + verify_model(expand3, input_data=[data]) + verify_model(expand6, input_data=[data]) @tvm.testing.uses_gpu def test_forward_expand_as(): - @paddle.jit.to_static - def expand_as(x, y): - z = paddle.expand_as(x, y) - z += y - return z + class ExpandAs(nn.Layer): + @paddle.jit.to_static + def forward(self, x, y): + z = paddle.expand_as(x, y) + z += y + return z - data_x = paddle.to_tensor([1, 2, 3], dtype="int32") - data_y = paddle.to_tensor([[1, 2, 3], [4, 5, 6]], dtype="float32") - verify_model(expand_as, [data_x, data_y]) + x_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + y_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.rand(x_shape, dtype="float32") + y_data = paddle.rand(y_shape, dtype="float32") + verify_model(ExpandAs(), [x_data, y_data]) @tvm.testing.uses_gpu @@ -591,11 +640,14 @@ def forward(self, x, y): z = self.func(x, y, out=out) return paddle.cast(z, "int32") - x = paddle.to_tensor([True]) - y = paddle.to_tensor([True, False, True, False]) - verify_model(LogicalAPI("logical_and"), [x, y]) - verify_model(LogicalAPI("logical_or"), [x, y]) - verify_model(LogicalAPI("logical_xor"), [x, y]) + x_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] + y_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + for x_shape, y_shape in zip(x_shapes, y_shapes): + x_data = paddle.randint(0, 2, x_shape).astype("bool") + y_data = paddle.randint(0, 2, y_shape).astype("bool") + verify_model(LogicalAPI("logical_and"), [x_data, y_data]) + verify_model(LogicalAPI("logical_or"), [x_data, y_data]) + verify_model(LogicalAPI("logical_xor"), [x_data, y_data]) @tvm.testing.uses_gpu @@ -796,11 +848,13 @@ def forward(self, inputs): "relu", "tanh", ] - input_shape = [1, 3, 10, 10] - input_data = paddle.rand(input_shape, dtype="float32") - for api_name in api_list: - verify_model(MathAPI(api_name), input_data=input_data) + input_shapes = [[128], [2, 256], [1000, 128, 32], [7, 3, 256, 256]] + for input_shape in input_shapes: + input_data = paddle.rand(input_shape, dtype="float32") + for api_name in api_list: + verify_model(MathAPI(api_name), input_data=input_data) if __name__ == "__main__": - pytest.main([__file__]) + # pytest.main([__file__]) + test_forward_math_api() From e600036f08c49ea704ee596118d6a76fb5b52614 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Mon, 4 Oct 2021 08:31:23 +0000 Subject: [PATCH 21/24] add more cases for tests --- tests/python/frontend/paddlepaddle/test_forward.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 4aa150169e42..ce17b64ba1d7 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -100,7 +100,6 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): assert_shapes_match(baseline_output, compiled_output) tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) - break @tvm.testing.uses_gpu From ef4c84bbf71b8313374c6c095656c6b6d9463967 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Mon, 4 Oct 2021 08:46:57 +0000 Subject: [PATCH 22/24] remove annotation --- tests/python/frontend/paddlepaddle/test_forward.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index ce17b64ba1d7..b6f477840290 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -855,5 +855,4 @@ def forward(self, inputs): if __name__ == "__main__": - # pytest.main([__file__]) - test_forward_math_api() + pytest.main([__file__]) From 5d1aa7c1b8958fcc6f6e4ff11e8483f13dadbd20 Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Tue, 5 Oct 2021 05:45:45 +0000 Subject: [PATCH 23/24] reduce test case sizes --- .../frontend/paddlepaddle/test_forward.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index b6f477840290..1d64f947e68a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -168,7 +168,7 @@ class ArgMin3(nn.Layer): def forward(self, inputs): return inputs.argmin(axis=2, keepdim=True) - input_shapes = [[256], [10, 128], [100, 500, 200], [1, 3, 224, 224]] + input_shapes = [[256], [5, 28], [10, 5, 4], [1, 3, 8, 8]] for input_shape in input_shapes: input_data = paddle.rand(input_shape, dtype="float32") verify_model(ArgMax(), input_data=input_data) @@ -202,7 +202,7 @@ class ArgSort3(nn.Layer): def forward(self, inputs): return paddle.argsort(inputs, axis=-1, descending=True) - input_shapes = [[256], [10, 20], [10, 10, 3], [1, 3, 5, 5]] + input_shapes = [[256], [10, 20], [10, 5, 3], [1, 3, 5, 5]] for input_shape in input_shapes: # Avoid duplicate elements in the array which will bring # different results with different sort algorithms @@ -323,7 +323,7 @@ class IsInf(nn.Layer): def forward(self, inputs): return paddle.cast(paddle.isinf(inputs), "int32") - input_shapes = [[32], [8, 128], [2, 128, 256], [2, 3, 224, 224], [2, 2, 3, 229, 229]] + input_shapes = [[32], [8, 32], [2, 5, 20], [2, 3, 8, 8], [2, 2, 3, 6, 6]] for input_shape in input_shapes: input_data = paddle.rand(input_shape, dtype="float32") verify_model(IsFinite(), input_data=input_data) @@ -417,7 +417,7 @@ class Dot(nn.Layer): def forward(self, x, y): return paddle.dot(x, y) - input_shapes = [[128], [8, 128]] + input_shapes = [[128], [8, 24]] for input_shape in input_shapes: x_data = paddle.rand(input_shape, dtype="float32") y_data = paddle.rand(input_shape, dtype="float32") @@ -457,8 +457,8 @@ def forward(self, input1, input2): api_list = [ "equal", ] - x_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] - y_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + x_shapes = [[128], [8, 20], [4, 20, 3], [2, 3, 8, 8], [2, 3, 3, 9, 9]] + y_shapes = [[1], [8, 20], [4, 1, 1], [2, 3, 8, 8], [2, 3, 3, 9, 1]] for x_shape, y_shape in zip(x_shapes, y_shapes): x_data = paddle.randint(1, 1000, x_shape, dtype="int32") y_data = paddle.randint(1, 1000, y_shape, dtype="int32") @@ -474,11 +474,11 @@ def expand1(inputs): @paddle.jit.to_static def expand2(inputs): - return paddle.expand(inputs, shape=[3, 1, 8, 256]) + return paddle.expand(inputs, shape=[2, 1, 4, 16]) @paddle.jit.to_static def expand3(inputs): - return paddle.expand(inputs, shape=[5, 1, 3, 224, 224]) + return paddle.expand(inputs, shape=[2, 1, 3, 7, 7]) @paddle.jit.to_static def expand4(inputs): @@ -487,21 +487,21 @@ def expand4(inputs): @paddle.jit.to_static def expand5(inputs): - shape = paddle.to_tensor(np.array([3, 1, 8, 256]).astype("int32")) + shape = paddle.to_tensor(np.array([2, 1, 4, 16]).astype("int32")) return paddle.expand(inputs, shape=shape) @paddle.jit.to_static def expand6(inputs): - shape = paddle.to_tensor(np.array([5, 1, 3, 224, 224]).astype("int32")) + shape = paddle.to_tensor(np.array([2, 1, 3, 7, 7]).astype("int32")) return paddle.expand(inputs, shape=shape) data = paddle.rand([128], dtype="float32") verify_model(expand1, input_data=[data]) verify_model(expand4, input_data=[data]) - data = paddle.rand([8, 256], dtype="float32") + data = paddle.rand([4, 16], dtype="float32") verify_model(expand2, input_data=[data]) verify_model(expand5, input_data=[data]) - data = paddle.rand([1, 3, 224, 224], dtype="float32") + data = paddle.rand([1, 3, 7, 7], dtype="float32") verify_model(expand3, input_data=[data]) verify_model(expand6, input_data=[data]) @@ -639,8 +639,8 @@ def forward(self, x, y): z = self.func(x, y, out=out) return paddle.cast(z, "int32") - x_shapes = [[128], [8, 128], [8, 200, 300], [2, 3, 229, 229], [2, 3, 3, 224, 224]] - y_shapes = [[1], [8, 128], [8, 1, 1], [2, 3, 229, 229], [2, 3, 3, 224, 1]] + x_shapes = [[128], [8, 20], [4, 20, 3], [2, 3, 8, 8], [2, 3, 3, 9, 9]] + y_shapes = [[1], [8, 20], [4, 1, 1], [2, 3, 8, 8], [2, 3, 3, 9, 1]] for x_shape, y_shape in zip(x_shapes, y_shapes): x_data = paddle.randint(0, 2, x_shape).astype("bool") y_data = paddle.randint(0, 2, y_shape).astype("bool") @@ -847,7 +847,7 @@ def forward(self, inputs): "relu", "tanh", ] - input_shapes = [[128], [2, 256], [1000, 128, 32], [7, 3, 256, 256]] + input_shapes = [[128], [2, 100], [10, 2, 5], [7, 3, 4, 1]] for input_shape in input_shapes: input_data = paddle.rand(input_shape, dtype="float32") for api_name in api_list: From 224e3cdaf4e200204f84e92bc78b82a7cb5a473f Mon Sep 17 00:00:00 2001 From: jiangjiajun Date: Sat, 9 Oct 2021 03:03:36 +0000 Subject: [PATCH 24/24] fix bug for paddlepaddle frontend --- python/tvm/relay/frontend/paddlepaddle.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 755c9f70f4ab..378002a74416 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -389,8 +389,9 @@ def convert_feed(g, op, block): if block is not None: ipt_name = op.output("Out")[0] - dtype = op.attr("dtype") - dtype = _convert_dtype_value(dtype) + ipt_shape = block.var(ipt_name).shape + ipt_dtype = block.var(ipt_name).dtype + ipt_dtype = str(ipt_dtype).strip().split(".")[1] else: ipt_shape = op.shape ipt_dtype = str(op.dtype).strip().split(".")[1]