From 22a20be3c373d578a39e50e7d4f77ac8c258b0bc Mon Sep 17 00:00:00 2001 From: root Date: Tue, 3 Aug 2021 13:49:34 +0000 Subject: [PATCH 01/23] fix some problems for matmul --- python/tvm/relay/frontend/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/tvm/relay/frontend/__init__.py b/python/tvm/relay/frontend/__init__.py index aa8ac4fc7434..aa49b63203f2 100644 --- a/python/tvm/relay/frontend/__init__.py +++ b/python/tvm/relay/frontend/__init__.py @@ -31,4 +31,5 @@ from .darknet import from_darknet from .pytorch import from_pytorch from .caffe import from_caffe +from .paddlepaddle import from_paddle from .change_datatype import ChangeDatatype From 1a7f180944949db25ac28eff3f48f2fe4738637b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 3 Aug 2021 13:49:50 +0000 Subject: [PATCH 02/23] fix some problems for matmul --- python/tvm/relay/frontend/paddlepaddle.py | 768 ++++++++++++++++++++++ 1 file changed, 768 insertions(+) create mode 100644 python/tvm/relay/frontend/paddlepaddle.py diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py new file mode 100644 index 000000000000..9326ba8a184e --- /dev/null +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -0,0 +1,768 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# 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 copy +import warnings +import six + +import numpy as np + +import tvm +from tvm.ir import IRModule + +from .. import analysis +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, + infer_shape, + infer_type, + infer_value, + new_var, +) + +__all__ = ["from_paddle"] + + +def shape_of(x, dtype='int32'): + ttype = infer_type(x).checked_type + if not _ty.is_dynamic(ttype): + shape = list(ttype.shape) + return _expr.const(shape, dtype) + return _op.shape_of(x, dtype) + + +def convert_arg_max(g, op, block): + """Operator converter for arg_max.""" + + axis = op.attr('axis') + keepdims = op.attr('keepdims') + flatten = op.attr('flatten') + assert not flatten, "Only flatten==True is supported for PaddlePaddle's arg_max" + + x = g.get_node(x.input('X')[0]) + out = _op.argmax(x, axis=axis, keepdims=keepdims) + g.add_node(op.output('Out')[0], out) + + +def convert_assign(g, op, block): + """Operator converter for assign.""" + + out = _op.copy(g.get_node(op.input('X')[0])) + g.add_node(op.output('Out')[0], out) + + +def convert_batch_norm(g, op, block): + """Operator converter for batch_norm.""" + + ipt_name = op.input('X')[0] + scale_name = op.input('Scale')[0] + bias_name = op.input('Bias')[0] + mean_name = op.input('Mean')[0] + variance_name = op.input('Variance')[0] + epsilon = op.attr('epsilon') + momentum = op.attr('momentum') + out = _op.nn.batch_norm(g.get_node(ipt_name), + g.get_node(scale_name), + g.get_node(bias_name), + g.get_node(mean_name), + g.get_node(variance_name), + epsilon=epsilon) + g.add_node(op.output('Y')[0], out[0]) + + +def convert_cast(g, op, block): + """Operator converter for cast.""" + + dtype = block.var(op.output('Out')[0]).dtype + dtype = str(dtype).strip().split('.')[1] + x = g.get_node(op.input('X')[0]) + out = _op.cast(x, dtype=dtype) + 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') + out = _op.concatenate(inputs, axis=axis) + g.add_node(op.output('Out')[0], out) + + +def convert_conv2d(g, op, block): + """Operator converter for conv2d.""" + def get_pad_size(in_size, dilated_kernel_size, stride_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) + return [pad // 2, pad - pad // 2] + + assert op.attr( + 'data_format' + ) == 'NCHW', "Only NCHW format is support for PaddlePaddle's conv2d" + dilations = op.attr('dilations') + groups = op.attr('groups') + paddings = op.attr('paddings') + padding_algorithm = op.attr('padding_algorithm') + strides = op.attr('strides') + + kernel = g.get_node(op.input('Filter')[0]) + input = g.get_node(op.input('Input')[0]) + out_channels, _, k_h, k_w = infer_shape(kernel) + in_h, in_w = infer_shape(input)[2:] + assert len( + paddings + ) == 2, "Only support len(paddings)==2 for PaddlePaddle's conv2d" + assert len( + dilations + ) == 2, "Only support len(dilations)==2 for PaddlePaddle's conv2d" + if 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]) + paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] + out = _op.nn.conv2d(input, + kernel, + strides=strides, + padding=paddings, + dilation=dilations, + groups=groups, + channels=out_channels, + kernel_size=[k_h, k_w]) + g.add_node(op.output('Output')[0], out) + + +def convert_cumsum(g, op, block): + """Operator converter for cumsum.""" + + axis = op.attr('axis') + exclusive = op.attr('exclusive') + flatten = op.attr('flatten') + reverse = op.attr('reverse') + + assert not flatten, "Only flatten==False is supported for PaddlePaddle's cumsum" + + x = g.get_node(op.input('X')[0]) + if reverse: + x = _op.reverse(x, axis=axis) + out = _op.cumsum(x, axis=axis, exclusive=exclusive) + out = _op.reverse(out, axis=axis) + else: + out = _op.cumsum(x, axis=axis, exclusive=exclusive) + g.add_node(op.output('Out')[0], out) + + +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], out) + + +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, + } + 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 + 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)) + out = op_func(ipt0, ipt1) + g.add_node(op.output('Out')[0], out) + + +def convert_equal(g, op, block): + """Operator converter for equal.""" + + x = g.get_node(op.input('X')[0]) + y = g.get_node(op.input('Y')[0]) + out = _op.equal(x, y) + g.add_node(op.output('Out')[0], out) + + +def convert_activation(g, op, block): + """Operator converter for all the activation.""" + + 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])) + g.add_node(op.output('Out')[0], out) + + +def convert_feed(g, op, block): + """Converter for model input node.""" + + 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] + if g.shape_dict is not None: + ipt_shape = g.shape_dict[ipt_name] + out = new_var(ipt_name, shape=ipt_shape, dtype=ipt_dtype) + g.add_node(ipt_name, out) + + +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] + x = g.get_node(op.input('X')[0]) + ipt_shape = infer_shape(x) + if not is_fixed_shape(ipt_shape): + msg = "Only support fixed input shape of PaddlePaddle's fill_any_like" + raise tvm.error.OpNotImplemented(msg) + value = op.attr('value') + const = np.ones(ipt_shape) * value + out = _expr.const(const.astype(out_dtype)) + g.add_node(op.output('Out')[0], out) + + +def convert_fill_constant(g, op, block): + """Operator converter for fill_constant.""" + + 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] + value = np.full(shape, value, dtype) + out = _expr.const(value.astype(dtype)).astype(dtype) + g.add_node(op.output('Out')[0], out) + + +def convert_gelu(g, op, block): + """Operator converter for gelu.""" + + x = g.get_node(op.input('X')[0]) + out = x * (_expr.const(0.5, dtype='float32') + + _op.erf(x * _expr.const(0.5**0.5, dtype='float32')) * + _expr.const(0.5, dtype='float32')) + g.add_node(op.output('Out')[0], out) + + +def convert_hard_sigmoid(g, op, block): + """Operator converter for hard_sigmoid.""" + + slope = op.attr('slope') + offset = op.attr('offset') + x = g.get_node(op.input('X')[0]) + out = x * _expr.const(slope) + _expr.const(0.5) + out = _op.clip(out, 0, 1) + g.add_node(op.output('Out')[0], out) + + +def convert_hard_swish(g, op, block): + """Operator converter for hard_swish.""" + + offset = op.attr('offset') + scale = op.attr('scale') + threshold = op.attr('threshold') + assert np.isclose( + offset, 3.0), "Only support offset==3.0 for PaddlePaddle's hard_swish" + 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]) + out = _op.clip(x, -1 * offset, offset) + out = out / _expr.const(threshold) + _expr.const(0.5) + out = x * out + g.add_node(op.output('Out')[0], out) + + +def convert_layer_norm(g, op, block): + """Operator converter for layer_norm.""" + + begin_norm_axis = op.attr('begin_norm_axis') + epsilon = op.attr('epsilon') + x = g.get_node(op.input('X')[0]) + bias = g.get_node(op.input('Bias')[0]) + scale = g.get_node(op.input('Scale')[0]) + out = _op.nn.layer_norm(x, + gamma=scale, + beta=bias, + axis=begin_norm_axis, + epsilon=epsilon, + center=True, + scale=True) + g.add_node(op.output('Y')[0], out) + + +def convert_leaky_relu(g, op, block): + """Operator converter for leaky_relu.""" + + alpha = op.attr('alpha') + x = g.get_node(op.input('X')[0]) + out = _op.nn.leaky_relu(x, alpha=alpha) + g.add_node(op.output('Out')[0]) + + +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') + is_sparse = op.attr('is_sparse') + height_sections = op.attr('height_sections') + 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) + g.add_node(op.output('Out')[0], out) + + +def convert_matmul(g, op, block): + """Operator converter for matmul.""" + + inputs = [g.get_node(op.input('X')[0]), g.get_node(op.input('Y')[0])] + a_shape = infer_shape(inputs[0]) + b_shape = infer_shape(inputs[1]) + try: + # for matmul_v2 + trans_x = op.attr('trans_x') + trans_y = op.attr('trans_y') + except: + # for matmul + trans_x = op.attr('transpose_X') + trans_y = op.attr('transpose_Y') + if trans_x: + perm = list(range(len(a_shape))) + perm[-2] = len(a_shape) - 1 + perm[-1] = len(a_shape) - 2 + inputs[0] = _op.transpose(inputs[0], axes=perm) + if trans_y: + perm = list(range(len(b_shape))) + perm[-2] = len(b_shape) - 1 + perm[-1] = len(b_shape) - 2 + inputs[1] = _op.transpose(inputs[1], axes=perm) + + # This implemention almost keeps same with ONNX + # Need to check input shape as batch matmul must be supported. + a_shape = shape_of(inputs[0]) + a_rank = infer_shape(a_shape)[0] + b_shape = shape_of(inputs[1]) + b_rank = infer_shape(b_shape)[0] + # When performing a batch matmul, we need to properly handle N-dim shapes. + if a_rank > 2 or b_rank > 2: + + def flatten_to_nd(x, x_shape, nd=3): + ndims = infer_shape(x_shape)[0] + if ndims == nd: + return x + newshape = _op.concatenate( + [ + _expr.const([-1], + dtype=infer_type(x_shape).checked_type.dtype), + _op.strided_slice(x_shape, [ndims - nd + 1], [ndims]), + ], + 0, + ) + out = _op.reshape(x, fold_constant(newshape)) + return out + + b_type = infer_type(inputs[1]) + # Convert to dense if the second matrix is 2d and non-dynamic + if b_rank == 2 and not _ty.is_dynamic(b_type.checked_type): + a = flatten_to_nd(inputs[0], a_shape, 2) + b = _op.transpose(inputs[1]) + output = _op.nn.dense(a, b) + else: + # Convert a and b into 3 dimensional tensors. + a = flatten_to_nd(inputs[0], a_shape, 3) + b = flatten_to_nd(inputs[1], b_shape, 3) + # Transpose matrix dimensions of b. + b = _op.transpose(b, [0, 2, 1]) + # Perform a batch matmul. + output = _op.nn.batch_matmul(a, b) + # Determine the output batch dimension. + if a_rank > b_rank: + out_batch = _op.strided_slice(a_shape, [0], [a_rank - 2]) + elif a_rank < b_rank: + out_batch = _op.strided_slice(b_shape, [0], [b_rank - 2]) + # If its unclear how broadcasting should be applied, the output + # shape is determined by choosing the maximum value from each input. + else: + out_batch = _op.concatenate( + [ + _op.maximum( + _op.strided_slice(a_shape, [i], [i + 1]), + _op.strided_slice(b_shape, [i], [i + 1]), + ) for i in range(a_rank - 2) + ], + 0, + ) + # Reshape output to original dimensions. + final_shape = _op.concatenate( + [ + out_batch, + _op.strided_slice(a_shape, [infer_shape(a_shape)[0] - 2], + [infer_shape(a_shape)[0] - 1]), + _op.strided_slice(b_shape, [infer_shape(b_shape)[0] - 1], + [infer_shape(b_shape)[0]]), + ], + 0, + ) + out = _op.reshape(output, fold_constant(final_shape)) + g.add_node(op.output('Out')[0], out) + return + # Otherwise a simple dense op will get the job done. + input_1_t = _op.transpose(inputs[1], axes=(1, 0)) + out = _op.nn.dense(inputs[0], input_1_t) + g.add_node(op.output('Out')[0], out) + + +def convert_pool2d(g, op, block): + """Operator converter for pool2d.""" + + layout = op.attr('data_format') + assert layout == 'NCHW', "Only support NCHW format for PaddlePaddle's pool2d." + adaptive = op.attr('adaptive') + ceil_mode = op.attr('ceil_mode') + exclusive = op.attr('exclusive') + global_pooling = op.attr('global_pooling') + ksize = op.attr('ksize') + paddings = op.attr('paddings') + padding_algorithm = op.attr('padding_algorithm') + pooling_type = op.attr('pooling_type') + + op_map = { + 'avg': 'avg_pool2d', + 'max': 'max_pool2d', + } + strides = op.attr('strides') + assert exclusive, "Only support exclusive==True for PaddlePaddle's pool2d" + assert padding_algorithm == "EXPLICIT", "Only support padding_algorithm==EXPLICIT for PaddlePaddle's pool2d" + if isinstance(strides, int): + strides = [strides, strides] + if isinstance(ksize, int): + ksize = [ksize, ksize] + if isinstance(paddings, six.string_types): + msg = "Setting paddings to `SAME` or `VALID` is not support for PaddlePaddle's pool2d" + raise tvm.error.OpNotImplemented(msg) + elif isinstance(paddings, int): + paddings = [paddings] * 2 + elif len(paddings) == 2: + pass + elif len(paddings) == 4: + msg = "Only support length of paddings equals to 2 for PaddlePaddle's pool2d" + raise tvm.error.OpNotImplemented(msg) + + x = g.get_node(op.input('X')[0]) + if not adaptive: + out = getattr(_op.nn, op_map[pooling_type])(x, + pool_size=ksize, + strides=strides, + padding=paddings, + ceil_mode=ceil_mode) + else: + out = getattr(_op.nn, + "adaptive_" + op_map[pooling_type])(x, output_size=ksize) + g.add_node(op.output('Out')[0], out) + + +def convert_reshape(g, op, block): + """Operator converter for reshape.""" + + shape = op.attr('shape') + out = _op.reshape(g.get_node(op.input('X')[0]), shape) + g.add_node(op.output('Out')[0], out) + + +def convert_scale(g, op, block): + """Operator converter for scale.""" + + scale = op.attr('scale') + bias = op.attr('bias') + 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) + else: + if np.isclose(bias, 0.0): + out = x * _expr.const(np.array(scale).astype('float32')) + elif np.isclose(scale, 1.0): + out = x + _expr.const(np.array(bias).astype('float32')) + else: + if bias_after_scale: + out = x * _expr.const( + np.array(scale).astype('float32')) + _expr.const( + np.array(bias).astype('float32')) + else: + out = (x + _expr.const(np.array(bias).astype('float32')) + ) * _expr.const(np.array(scale).astype('float32')) + g.add_node(op.output('Out')[0], out) + + +def convert_shape(g, op, block): + """Operator converter for shape.""" + + x = g.get_node(op.input('Input')[0]) + out = shape_of(x) + g.add_node(op.output('Out')[0], out) + + +def convert_slice(g, op, block): + """Operator converter for slice.""" + def parameter_process(starts, ends, axes): + 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(np.iinfo(np.int32).max) + return new_starts, new_ends, new_axes + + starts = op.attr('starts') + ends = op.attr('ends') + axes = op.attr('axes') + if isinstance(starts, int): + starts = [starts] + if isinstance(ends, int): + ends = [ends] + if isinstance(axes, int): + axes = [axes] + starts, ends, axes = parameter_process(starts, ends, axes) + out = _op.strided_slice(g.get_node(op.input('Input')[0]), + begin=starts, + end=ends) + g.add_node(op.output('Out')[0], out) + + +def convert_softmax(g, op, block): + """Operator converter for softmax.""" + + axis = op.attr('axis') + input_shape = block.var(op.input('X')[0]).shape + if axis < 0: + axis = len(input_shape) + axis + x = g.get_node(op.input('X')[0]) + m = _op.max(x, axis, keepdims=True) + e = _op.exp(x - m) + out = e / _op.sum(e, axis, keepdims=True) + g.add_node(op.output('Out')[0], out) + + +def convert_transpose(g, op, block): + """Operator converter for transpose.""" + + perm = op.attr('axis') + out = _op.transpose(g.get_node(op.input('X')[0]), axes=perm) + g.add_node(op.output('Out')[0], out) + + +def convert_unsqueeze(g, op, block): + """Operator converter for unsqueeze.""" + + x = g.get_node(op.input('X')[0]) + axes = sorted(op.attr('axes')) + for axis in axes: + x = _op.expand_dims(x, axis=axis, num_newaxis=1) + g.add_node(op.output('Out')[0], x) + + +_convert_map = { + 'arg_max': convert_arg_max, + 'assign': convert_assign, + 'batch_norm': convert_batch_norm, + 'cast': convert_cast, + 'concat': convert_concat, + 'conv2d': convert_conv2d, + 'cumsum': convert_cumsum, + 'depthwise_conv2d': convert_conv2d, + '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, + 'feed': convert_feed, + 'fill_any_like': convert_fill_any_like, + 'fill_constant': convert_fill_constant, + 'gelu': convert_gelu, + 'hard_sigmoid': convert_hard_sigmoid, + 'hard_swish': convert_hard_swish, + 'layer_norm': convert_layer_norm, + 'leaky_relu': convert_leaky_relu, + 'lookup_table_v2': convert_lookup_table, + 'matmul': convert_matmul, + 'matmul_v2': convert_matmul, + 'pool2d': convert_pool2d, + 'relu': convert_activation, + 'reshape2': convert_reshape, + 'scale': convert_scale, + 'shape': convert_shape, + 'slice': convert_slice, + 'softmax': convert_softmax, + 'tanh': convert_activation, + 'transpose2': convert_transpose, + 'unsqueeze2': convert_unsqueeze, +} + + +class GraphProto(object): + """ A helper class for handling relay functions from PaddlePaddle model.""" + def __init__(self): + self.nodes = {} + self.params = {} + self.shape_dict = None + + def get_node(self, name): + assert name in self.nodes + return self.nodes[name] + + def add_node(self, name, node): + self.nodes[name] = fold_constant(node) + + def get_params(self, name): + assert name in self.params + return self.params[name] + + def extract_parameters(self, program, scope): + """ Extract all the weights from PaddlePaddle program.""" + + self.params = {} + variables = program.global_block().vars + for name in variables: + var = program.global_block().var(name) + if name.endswith('feed') or name.endswith('fetch'): + continue + if not var.persistable: + continue + if isinstance(scope, dict): + 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)) + warings.warn(warning_msg) + + def check_unsupported_ops(self, program): + """ Check whether all the operators are supported.""" + + unsupported_ops = set() + for block in program.blocks: + for i, op in enumerate(block.ops): + if op.type == 'fetch': + continue + if op.type not in _convert_map: + unsupported_ops.add(op.type) + if len(unsupported_ops) > 0: + msg = "The following operators are not supported for frontend Paddle: " + msg += ", ".join(unsupported_ops) + raise tvm.error.OpNotImplemented(msg) + + def ops_to_relay(self, program, scope): + """ Convert PaddlePaddle operators to TVM relay functions.""" + + for block in program.blocks: + for i, op in enumerate(block.ops): + if op.type == 'fetch': + continue + convert_func = _convert_map[op.type] + convert_func(self, op, block) + + def get_outputs(self, program): + """ Get outputs of PaddlePaddle model.""" + + outputs = list() + for block in program.blocks: + for i, op in enumerate(block.ops): + if op.type == "fetch": + outputs.append(op.input('X')[0]) + return outputs + + def from_paddle(self, program, shape_dict, scope): + """ Construct the TVM relay expression from PaddlePaddle program.""" + + self.shape_dict = shape_dict + if scope is None: + import paddle + scope = paddle.fluid.global_scope() + self.check_unsupported_ops(program) + self.extract_parameters(program, scope) + self.ops_to_relay(program, scope) + output_names = self.get_outputs(program) + + 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) + return mod, self.params + + +def from_paddle(program, shape_dict=None, scope=None): + """ Convert a PaddlePaddle model into an equivalent Relay Function. + + PaddlePaddle Program represent the computation graph of PaddlePaddle model, + and PaddlePaddle scope stores all the weights of PaddlePaddle model. + """ + + import paddle + g = GraphProto() + mod, params = g.from_paddle(program, shape_dict, scope) + return mod, params From 3f604e925f04415ebc1bfae3e6a13b4014113bc2 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Aug 2021 03:01:45 +0000 Subject: [PATCH 03/23] add alpha parameter for matmul --- python/tvm/relay/frontend/paddlepaddle.py | 28 ++++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 9326ba8a184e..87a8679adcc2 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -251,13 +251,14 @@ def convert_fill_any_like(g, op, block): out_dtype = block.var(out_name).dtype out_dtype = str(out_dtype).strip().split('.')[1] x = g.get_node(op.input('X')[0]) - ipt_shape = infer_shape(x) - if not is_fixed_shape(ipt_shape): - msg = "Only support fixed input shape of PaddlePaddle's fill_any_like" - raise tvm.error.OpNotImplemented(msg) + ipt_type = infer_type(x).checked_type value = op.attr('value') - const = np.ones(ipt_shape) * value - out = _expr.const(const.astype(out_dtype)) + 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) g.add_node(op.output('Out')[0], out) @@ -448,11 +449,16 @@ def flatten_to_nd(x, x_shape, nd=3): 0, ) out = _op.reshape(output, fold_constant(final_shape)) - g.add_node(op.output('Out')[0], out) - return - # Otherwise a simple dense op will get the job done. - input_1_t = _op.transpose(inputs[1], axes=(1, 0)) - out = _op.nn.dense(inputs[0], input_1_t) + else: + # Otherwise a simple dense op will get the job done. + input_1_t = _op.transpose(inputs[1], axes=(1, 0)) + out = _op.nn.dense(inputs[0], input_1_t) + try: + alpha = op.attr('alpha') + if not np.isclose(alpha, 1.0): + out = out * _expr.const(alpha).astype('float32') + except: + pass g.add_node(op.output('Out')[0], out) From fe6de07603bebbb7adb05df1f8dc89b5453562f3 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Aug 2021 06:11:48 +0000 Subject: [PATCH 04/23] remove unnecessary condition --- python/tvm/relay/frontend/paddlepaddle.py | 28 +---------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 87a8679adcc2..e2beb3a15b0b 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -117,9 +117,6 @@ def get_pad_size(in_size, dilated_kernel_size, stride_size): pad = max(dilated_kernel_size - (in_size % stride_size), 0) return [pad // 2, pad - pad // 2] - assert op.attr( - 'data_format' - ) == 'NCHW', "Only NCHW format is support for PaddlePaddle's conv2d" dilations = op.attr('dilations') groups = op.attr('groups') paddings = op.attr('paddings') @@ -130,16 +127,6 @@ def get_pad_size(in_size, dilated_kernel_size, stride_size): input = g.get_node(op.input('Input')[0]) out_channels, _, k_h, k_w = infer_shape(kernel) in_h, in_w = infer_shape(input)[2:] - assert len( - paddings - ) == 2, "Only support len(paddings)==2 for PaddlePaddle's conv2d" - assert len( - dilations - ) == 2, "Only support len(dilations)==2 for PaddlePaddle's conv2d" - if 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]) - paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] out = _op.nn.conv2d(input, kernel, strides=strides, @@ -465,11 +452,8 @@ def flatten_to_nd(x, x_shape, nd=3): def convert_pool2d(g, op, block): """Operator converter for pool2d.""" - layout = op.attr('data_format') - assert layout == 'NCHW', "Only support NCHW format for PaddlePaddle's pool2d." adaptive = op.attr('adaptive') ceil_mode = op.attr('ceil_mode') - exclusive = op.attr('exclusive') global_pooling = op.attr('global_pooling') ksize = op.attr('ksize') paddings = op.attr('paddings') @@ -481,22 +465,12 @@ def convert_pool2d(g, op, block): 'max': 'max_pool2d', } strides = op.attr('strides') - assert exclusive, "Only support exclusive==True for PaddlePaddle's pool2d" - assert padding_algorithm == "EXPLICIT", "Only support padding_algorithm==EXPLICIT for PaddlePaddle's pool2d" if isinstance(strides, int): strides = [strides, strides] if isinstance(ksize, int): ksize = [ksize, ksize] - if isinstance(paddings, six.string_types): - msg = "Setting paddings to `SAME` or `VALID` is not support for PaddlePaddle's pool2d" - raise tvm.error.OpNotImplemented(msg) - elif isinstance(paddings, int): + if isinstance(paddings, int): paddings = [paddings] * 2 - elif len(paddings) == 2: - pass - elif len(paddings) == 4: - msg = "Only support length of paddings equals to 2 for PaddlePaddle's pool2d" - raise tvm.error.OpNotImplemented(msg) x = g.get_node(op.input('X')[0]) if not adaptive: From dfb9cb1e12f267fecea546002ef038bb84c3431d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Aug 2021 12:55:04 +0000 Subject: [PATCH 05/23] add TranslatedLayer which support model loaded by jit.load --- python/tvm/relay/frontend/paddlepaddle.py | 79 ++++++++++++++++------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index e2beb3a15b0b..b517ab1969a3 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -221,10 +221,15 @@ def convert_activation(g, op, block): def convert_feed(g, op, block): """Converter for model input node.""" - 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] + if block is not None: + ipt_shape = block.var(ipt_name).shape + ipt_dtype = block.var(ipt_name).dtype + ipt_dtype = str(ipt_dtype).strip().split('.')[1] + ipt_name = op.output('Out')[0] + 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] out = new_var(ipt_name, shape=ipt_shape, dtype=ipt_dtype) @@ -650,7 +655,7 @@ def get_params(self, name): assert name in self.params return self.params[name] - def extract_parameters(self, program, scope): + def extract_parameters(self, program, scope=None): """ Extract all the weights from PaddlePaddle program.""" self.params = {} @@ -694,9 +699,12 @@ def check_unsupported_ops(self, program): msg += ", ".join(unsupported_ops) raise tvm.error.OpNotImplemented(msg) - def ops_to_relay(self, program, scope): + def ops_to_relay(self, program, input_specs=None): """ Convert PaddlePaddle operators to TVM relay functions.""" + if input_specs is not None: + for input_spec in input_specs: + convert_feed(self, input_spec, None) for block in program.blocks: for i, op in enumerate(block.ops): if op.type == 'fetch': @@ -704,17 +712,7 @@ def ops_to_relay(self, program, scope): convert_func = _convert_map[op.type] convert_func(self, op, block) - def get_outputs(self, program): - """ Get outputs of PaddlePaddle model.""" - - outputs = list() - for block in program.blocks: - for i, op in enumerate(block.ops): - if op.type == "fetch": - outputs.append(op.input('X')[0]) - return outputs - - def from_paddle(self, program, shape_dict, scope): + def from_program(self, program, shape_dict, scope): """ Construct the TVM relay expression from PaddlePaddle program.""" self.shape_dict = shape_dict @@ -723,8 +721,37 @@ def from_paddle(self, program, shape_dict, scope): scope = paddle.fluid.global_scope() self.check_unsupported_ops(program) self.extract_parameters(program, scope) - self.ops_to_relay(program, scope) - output_names = self.get_outputs(program) + self.ops_to_relay(program) + + output_names = list() + for block in program.blocks: + for i, op in enumerate(block.ops): + if op.type == "fetch": + output_names.append(op.input('X')[0]) + + 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) + return mod, self.params + + def from_translated_layer(self, layer, shape_dict): + """ Construct the TVM relay expression from PaddlePaddle TranslatedLayer.""" + + self.shape_dict = shape_dict + program = layer.program() + parameters = dict() + for param in layer.parameters(): + parameters[param.name] = np.array(param.value().get_tensor()) + self.check_unsupported_ops(program) + self.extract_parameters(program, parameters) + + input_specs = layer._input_spec() + self.ops_to_relay(program, input_specs) + + output_names = [x.name for x in layer._output_spec()] outputs = [self.nodes[name] for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) @@ -735,14 +762,22 @@ def from_paddle(self, program, shape_dict, scope): return mod, self.params -def from_paddle(program, shape_dict=None, scope=None): +def from_paddle(program_or_layer, shape_dict=None, scope=None): """ Convert a PaddlePaddle model into an equivalent Relay Function. - PaddlePaddle Program represent the computation graph 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() - mod, params = g.from_paddle(program, shape_dict, scope) + if isinstance(program_or_layer, paddle.fluid.dygraph.TranslatedLayer): + # model is loaded by `paddle.jit.load` + mod, params = g.from_translated_layer(program_or_layer, shape_dict) + elif isinstance(program_or_layer, paddle.fluid.framework.Program): + # model is loaded by `paddle.static.load_inference_model` + mod, params = g.from_program(program_or_layer, shape_dict, scope) + else: + raise Exception( + "Only PaddlePaddle's Program and TranslatedLayer are supported.") return mod, params From 36ab16e8a58ffabb918bbe5d0f7debd9eb5c9743 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 5 Aug 2021 02:56:19 +0000 Subject: [PATCH 06/23] add mul operator support --- python/tvm/relay/frontend/paddlepaddle.py | 55 ++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index b517ab1969a3..54c72ff9d628 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -222,10 +222,10 @@ def convert_feed(g, op, block): """Converter for model input node.""" 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] - ipt_name = op.output('Out')[0] else: ipt_shape = op.shape ipt_dtype = str(op.dtype).strip().split('.')[1] @@ -454,6 +454,55 @@ def flatten_to_nd(x, x_shape, nd=3): g.add_node(op.output('Out')[0], out) +def convert_mul(g, op, block): + """Operator converter for mul.""" + + x = g.get_node(op.input('X')[0]) + 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_dim = infer_shape(x_shape)[0] + y_dim = infer_shape(y_shape)[0] + if x_num_col_dims < 0: + x_num_col_dims += x_dim + if y_num_col_dims < 0: + y_num_col_dims += y_dim + if x_num_col_dims == 1: + x = _op.nn.batch_flatten(x) + else: + pre_shape = _op.prod(_op.strided_slice(x_shape, [0], [x_num_col_dims], + [1]), + keepdims=True) + post_shape = _op.prod(_op.strided_slice(x_shape, [x_num_col_dims], + [x_dim], [1]), + keepdims=True) + new_shape = _op.concatenate([pre_shape, post_shape], axis=0) + new_shape = fold_constant(new_shape) + x = _op.reshape(x, new_shape) + if y_num_col_dims == 1: + y = _op.nn.batch_flatten(y) + else: + pre_shape = _op.prod(_op.strided_slice(y_shape, [0], [y_num_col_dims], + [1]), + keepdims=True) + post_shape = _op.prod(_op.strided_slice(y_shape, [y_num_col_dims], + [y_dim], [1]), + keepdims=True) + new_shape = _op.concatenate([pre_shape, post_shape], axis=0) + new_shape = fold_constant(new_shape) + y = _op.reshape(y, new_shape) + y = _op.transpose(y) + out = _op.nn.dense(x, y) + out_pre_shape = _op.strided_slice(x_shape, [0], [x_num_col_dims], [1]) + out_post_shape = _op.strided_slice(y_shape, [y_num_col_dims], [y_dim], [1]) + out_shape = _op.concatenate([out_pre_shape, out_post_shape], axis=0) + out_shape = fold_constant(out_shape) + out = _op.reshape(out, out_shape) + g.add_node(op.output('Out')[0], out) + + def convert_pool2d(g, op, block): """Operator converter for pool2d.""" @@ -464,6 +513,9 @@ def convert_pool2d(g, op, block): paddings = op.attr('paddings') padding_algorithm = op.attr('padding_algorithm') pooling_type = op.attr('pooling_type') + if global_pooling: + adaptive = True + ksize = [1, 1] op_map = { 'avg': 'avg_pool2d', @@ -624,6 +676,7 @@ def convert_unsqueeze(g, op, block): 'lookup_table_v2': convert_lookup_table, 'matmul': convert_matmul, 'matmul_v2': convert_matmul, + 'mul': convert_mul, 'pool2d': convert_pool2d, 'relu': convert_activation, 'reshape2': convert_reshape, From 63b0138e6f431fa6f5634594308b724e8f71d6cd Mon Sep 17 00:00:00 2001 From: wjj19950828 Date: Thu, 5 Aug 2021 11:37:52 +0800 Subject: [PATCH 07/23] Add padding mode support for conv/pool2d --- python/tvm/relay/frontend/paddlepaddle.py | 46 +++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index e2beb3a15b0b..397b7dfb39f4 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -50,6 +50,18 @@ def shape_of(x, dtype='int32'): return _op.shape_of(x, dtype) +def _get_pad_size(in_size, dilated_kernel_size, stride_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 convert_arg_max(g, op, block): """Operator converter for arg_max.""" @@ -110,13 +122,6 @@ def convert_concat(g, op, block): def convert_conv2d(g, op, block): """Operator converter for conv2d.""" - def get_pad_size(in_size, dilated_kernel_size, stride_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) - return [pad // 2, pad - pad // 2] - dilations = op.attr('dilations') groups = op.attr('groups') paddings = op.attr('paddings') @@ -127,6 +132,16 @@ def get_pad_size(in_size, dilated_kernel_size, stride_size): input = g.get_node(op.input('Input')[0]) out_channels, _, k_h, k_w = infer_shape(kernel) in_h, in_w = infer_shape(input)[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]) + paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] + else: + msg = 'Value {} in attribute "padding" of operator Conv is not ' "valid." + raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) + out = _op.nn.conv2d(input, kernel, strides=strides, @@ -459,6 +474,12 @@ def convert_pool2d(g, op, block): paddings = op.attr('paddings') padding_algorithm = op.attr('padding_algorithm') pooling_type = op.attr('pooling_type') + if global_pooling: + adaptive = True + ksize = [1, 1] + + input = g.get_node(op.input('X')[0]) + in_h, in_w = infer_shape(input)[2:] op_map = { 'avg': 'avg_pool2d', @@ -472,6 +493,16 @@ def convert_pool2d(g, op, block): if isinstance(paddings, int): paddings = [paddings] * 2 + 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]) + paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] + else: + msg = 'Value {} in attribute "padding" of operator Pool2d is not ' "valid." + raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) + x = g.get_node(op.input('X')[0]) if not adaptive: out = getattr(_op.nn, op_map[pooling_type])(x, @@ -634,6 +665,7 @@ def convert_unsqueeze(g, op, block): class GraphProto(object): """ A helper class for handling relay functions from PaddlePaddle model.""" + def __init__(self): self.nodes = {} self.params = {} From 1967d461ddf82d7b12854cf33e3fb1d075ab532b Mon Sep 17 00:00:00 2001 From: wjj19950828 Date: Thu, 5 Aug 2021 15:22:34 +0800 Subject: [PATCH 08/23] support 4 two-tuples --- python/tvm/relay/frontend/paddlepaddle.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 397b7dfb39f4..2beca3a491db 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -138,6 +138,11 @@ def convert_conv2d(g, op, block): 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: + paddings = [paddings[0],paddings[1],paddings[0],paddings[1]] + if len(paddings) == 4: + paddings = [paddings[0],paddings[2],paddings[1],paddings[3]] else: msg = 'Value {} in attribute "padding" of operator Conv is not ' "valid." raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) @@ -499,6 +504,11 @@ def convert_pool2d(g, op, block): 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: + paddings = [paddings[0],paddings[1],paddings[0],paddings[1]] + if len(paddings) == 4: + paddings = [paddings[0],paddings[2],paddings[1],paddings[3]] else: msg = 'Value {} in attribute "padding" of operator Pool2d is not ' "valid." raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) From f4932bf5649e816650ef7381b9f75a85084fd1a9 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Thu, 5 Aug 2021 20:00:48 +0800 Subject: [PATCH 09/23] add paddle test case --- .../frontend/paddlepaddle/test_forward.py | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 tests/python/frontend/paddlepaddle/test_forward.py diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py new file mode 100644 index 000000000000..1b4c960824dd --- /dev/null +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -0,0 +1,124 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import os +from pathlib import Path +import re + +import numpy as np +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) + +def assert_shapes_match(tru, est): + if tru.shape != est.shape: + msg = "Output shapes {} and {} don't match" + raise AssertionError(msg.format(tru.shape, est.shape)) + +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)) + + os.remove(str(model_path) + '.pdmodel') + return baseline_model + +def verify_model(func, input_data, rtol=1e-5, atol=1e-5): + if not (isinstance(input_data, list) or isinstance(input_data, tuple)): + input_data = [input_data] + + input_spec = [] + input_names = [] + input_shape_dict = {} + 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)) + input_names.append(input_name) + input_shape_dict[input_name] = data.shape + + baseline_model = get_paddle_model(func, input_spec) + with paddle.no_grad(): + baseline_outputs = baseline_model(*[input.clone() for input in input_data]) + + # get paddle outputs + if isinstance(baseline_outputs, tuple): + baseline_outputs = tuple(out.numpy() for out in baseline_outputs) + else: + baseline_outputs = (baseline_outputs.numpy(),) + + mod, params = relay.frontend.from_paddle(baseline_model, input_shape_dict) + for arg in mod["main"].params[: len(input_names)]: + assert arg.name_hint in input_names + compiled_input = dict(zip(input_names, [inp.clone().numpy() for inp in input_data])) + + 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, inp in compiled_input.items(): + gmod.set_input(name, inp) + gmod.run() + + for i, baseline_output in enumerate(baseline_outputs): + compiled_output = gmod.get_output(i).numpy() + + 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_add(): + paddle.set_grad_enabled(False) + input_shape = [10] + + @paddle.jit.to_static + def add(inputs): + return paddle.add(inputs, inputs) + + @paddle.jit.to_static + def add2(inputs): + return inputs + 1 + + @paddle.jit.to_static + def add3(inputs): + ones = paddle.ones([10], dtype="float32") + return inputs + ones + + class add4(nn.Layer): + @paddle.jit.to_static + def forward(self, input1, input2): + return input1 + input2 + + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(add, input_data) + verify_model(add2, input_data) + verify_model(add3, input_data) + input_data = paddle.rand([2, 3], dtype="float32") + input_data2 = paddle.rand([2, 3], dtype="float32") + model = add4() + verify_model(model, [input_data, input_data2]) + +if __name__ == "__main__": + test_forward_add() From 0c92955cb195376abe1781910601c1c6fba212b4 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Thu, 5 Aug 2021 21:30:24 +0800 Subject: [PATCH 10/23] add paddle conv2d case --- .../frontend/paddlepaddle/test_forward.py | 69 +++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 1b4c960824dd..786c0769c0d8 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -16,9 +16,10 @@ # under the License. import os from pathlib import Path -import re +import shutil import numpy as np +from paddle.fluid.layers.nn import pad import tvm import tvm.testing import tvm.topi.testing @@ -43,7 +44,7 @@ def get_paddle_model(func, input_spec): paddle.jit.save(func, str(model_path), input_spec=input_spec) baseline_model = paddle.jit.load(str(model_path)) - os.remove(str(model_path) + '.pdmodel') + shutil.rmtree(str(PADDLE_TEST_DATA_ROOT_PATH)) return baseline_model def verify_model(func, input_data, rtol=1e-5, atol=1e-5): @@ -60,8 +61,7 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): input_shape_dict[input_name] = data.shape baseline_model = get_paddle_model(func, input_spec) - with paddle.no_grad(): - baseline_outputs = baseline_model(*[input.clone() for input in input_data]) + baseline_outputs = baseline_model(*[input.clone() for input in input_data]) # get paddle outputs if isinstance(baseline_outputs, tuple): @@ -106,19 +106,62 @@ def add3(inputs): ones = paddle.ones([10], dtype="float32") return inputs + ones - class add4(nn.Layer): - @paddle.jit.to_static - def forward(self, input1, input2): - return input1 + input2 - input_data = paddle.rand(input_shape, dtype="float32") verify_model(add, input_data) verify_model(add2, input_data) verify_model(add3, input_data) - input_data = paddle.rand([2, 3], dtype="float32") - input_data2 = paddle.rand([2, 3], dtype="float32") - model = add4() - verify_model(model, [input_data, input_data2]) + +@tvm.testing.uses_gpu +def test_forward_conv(): + paddle.set_grad_enabled(False) + conv1d_input_shape = [1, 3, 10] + conv2d_input_shape = [1, 3, 10, 10] + + class Conv2D1(nn.Layer): + def __init__(self): + super(Conv2D1, self).__init__() + self.conv = nn.Conv2D(3, 6, 7, bias_attr=True) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + class Conv2D2(nn.Layer): + def __init__(self): + super(Conv2D2, self).__init__() + self.conv = nn.Conv2D(3, 6, 7, groups=3, bias_attr=False) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + class Conv1D1(nn.Layer): + def __init__(self): + super(Conv1D1, self).__init__() + self.conv = nn.Conv1D(3, 6, 7, bias_attr=True) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + class Conv1D2(nn.Layer): + def __init__(self): + super(Conv1D2, self).__init__() + self.conv = nn.Conv1d(3, 6, 7, groups=3, bias_attr=False) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + 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) + if __name__ == "__main__": test_forward_add() + test_forward_conv() From b0b80946ca1dda023c6c62e4dd1df092225a2a00 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 6 Aug 2021 03:28:53 +0000 Subject: [PATCH 11/23] update test_forward.py --- .../frontend/paddlepaddle/test_forward.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 786c0769c0d8..1006cd799a35 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -90,7 +90,6 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): @tvm.testing.uses_gpu def test_forward_add(): - paddle.set_grad_enabled(False) input_shape = [10] @paddle.jit.to_static @@ -113,8 +112,6 @@ def add3(inputs): @tvm.testing.uses_gpu def test_forward_conv(): - paddle.set_grad_enabled(False) - conv1d_input_shape = [1, 3, 10] conv2d_input_shape = [1, 3, 10, 10] class Conv2D1(nn.Layer): @@ -137,26 +134,6 @@ def __init__(self): def forward(self, inputs): return self.softmax(self.conv(inputs)) - class Conv1D1(nn.Layer): - def __init__(self): - super(Conv1D1, self).__init__() - self.conv = nn.Conv1D(3, 6, 7, bias_attr=True) - self.softmax = nn.Softmax() - - @paddle.jit.to_static - def forward(self, inputs): - return self.softmax(self.conv(inputs)) - - class Conv1D2(nn.Layer): - def __init__(self): - super(Conv1D2, self).__init__() - self.conv = nn.Conv1d(3, 6, 7, groups=3, bias_attr=False) - self.softmax = nn.Softmax() - - @paddle.jit.to_static - def forward(self, inputs): - return self.softmax(self.conv(inputs)) - 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) From 35fccb2b16d9f583553f6586ee950333b4c8c00a Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 6 Aug 2021 16:44:41 +0800 Subject: [PATCH 12/23] fix paddle convert_matmul --- python/tvm/relay/frontend/paddlepaddle.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 54c72ff9d628..ec2cd945e456 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -442,9 +442,13 @@ def flatten_to_nd(x, x_shape, nd=3): ) out = _op.reshape(output, fold_constant(final_shape)) else: + if b_rank == 1: + inputs[1] = _op.expand_dims(inputs[1], 1, 1) # Otherwise a simple dense op will get the job done. input_1_t = _op.transpose(inputs[1], axes=(1, 0)) out = _op.nn.dense(inputs[0], input_1_t) + if b_rank == 1: + out = _op.squeeze(out, axis=[-1]) try: alpha = op.attr('alpha') if not np.isclose(alpha, 1.0): From 094f44ed8121e8145405699bd5f952aa17e21561 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 6 Aug 2021 16:47:31 +0800 Subject: [PATCH 13/23] add paddle multiply and matmul op test case --- .../frontend/paddlepaddle/test_forward.py | 74 ++++++++++++++++--- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 1006cd799a35..94a2c468b21a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -89,26 +89,76 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) @tvm.testing.uses_gpu -def test_forward_add(): +def test_forward_add_subtract(): input_shape = [10] @paddle.jit.to_static - def add(inputs): - return paddle.add(inputs, inputs) + def add_subtract(inputs): + return paddle.subtract(paddle.add(inputs, inputs), inputs) @paddle.jit.to_static - def add2(inputs): - return inputs + 1 + def add_subtract2(inputs): + return inputs + 1 - 2 @paddle.jit.to_static - def add3(inputs): + def add_subtract3(inputs1, inputs2): ones = paddle.ones([10], dtype="float32") - return inputs + ones + return inputs1 + ones - inputs2 input_data = paddle.rand(input_shape, dtype="float32") - verify_model(add, input_data) - verify_model(add2, input_data) - verify_model(add3, input_data) + verify_model(add_subtract, input_data) + verify_model(add_subtract2, input_data) + input_data2 = paddle.rand(input_shape, dtype="float32") + verify_model(add_subtract3, [input_data, input_data2]) + +@tvm.testing.uses_gpu +def test_forward_multiply(): + input_shape = [10] + + @paddle.jit.to_static + def multiply1(inputs): + return inputs * inputs + + @paddle.jit.to_static + def multiply2(inputs): + return inputs * 1.0 + + @paddle.jit.to_static + def multiply3(inputs): + ones = paddle.ones([10], dtype="float32") + return inputs * ones + + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(multiply1, input_data=input_data) + verify_model(multiply2, input_data=input_data) + verify_model(multiply3, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_matmul(): + + class MatMul1(nn.Layer): + def forward(self, input1, input2): + return paddle.matmul(input1, input2) + + # matrix x vector + input_data1 = paddle.randn((3, 4), dtype="float32") + input_data2 = paddle.randn((4,), dtype="float32") + verify_model(MatMul1(), 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(MatMul1(), 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(MatMul1(), 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(MatMul1(), input_data=[input_data1, input_data2]) @tvm.testing.uses_gpu def test_forward_conv(): @@ -140,5 +190,7 @@ def forward(self, inputs): if __name__ == "__main__": - test_forward_add() + test_forward_add_subtract() + test_forward_multiply() + test_forward_matmul() test_forward_conv() From 4520515996dba25662644a3160ce6ca661d9e6d4 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Wed, 11 Aug 2021 20:12:10 +0800 Subject: [PATCH 14/23] add test case and fix bug --- python/tvm/relay/frontend/paddlepaddle.py | 94 +++- .../frontend/paddlepaddle/test_forward.py | 466 ++++++++++++++++-- 2 files changed, 497 insertions(+), 63 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 3d63826b6ea2..02f256ab3435 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -19,6 +19,7 @@ """Paddle: PArallel Distributed Deep LEarning.""" import copy import warnings +from pandas.core.dtypes.inference import is_scalar import six import numpy as np @@ -68,10 +69,13 @@ def convert_arg_max(g, op, block): axis = op.attr('axis') keepdims = op.attr('keepdims') flatten = op.attr('flatten') - assert not flatten, "Only flatten==True is supported for PaddlePaddle's arg_max" - x = g.get_node(x.input('X')[0]) - out = _op.argmax(x, axis=axis, keepdims=keepdims) + 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) g.add_node(op.output('Out')[0], out) @@ -166,9 +170,9 @@ def convert_cumsum(g, op, block): flatten = op.attr('flatten') reverse = op.attr('reverse') - assert not flatten, "Only flatten==False is supported for PaddlePaddle's cumsum" - x = g.get_node(op.input('X')[0]) + if axis is None or flatten: + x = _op.reshape(x, [-1]) if reverse: x = _op.reverse(x, axis=axis) out = _op.cumsum(x, axis=axis, exclusive=exclusive) @@ -281,6 +285,12 @@ def convert_fill_constant(g, op, block): 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'): + shape = g.get_node(op.input('ValueTensor')[0]) + shape = infer_value(shape, g.get_params()).numpy() + if 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) g.add_node(op.output('Out')[0], out) @@ -333,8 +343,22 @@ def convert_layer_norm(g, op, block): begin_norm_axis = op.attr('begin_norm_axis') epsilon = op.attr('epsilon') x = g.get_node(op.input('X')[0]) - bias = g.get_node(op.input('Bias')[0]) - scale = g.get_node(op.input('Scale')[0]) + bias_input = op.input('Bias') + scale_input = op.input('Scale') + + x_shape = infer_shape(x) + assert begin_norm_axis == -1 or begin_norm_axis == len(x_shape) - 1, "Support only normalization over last one dimension." + + if bias_input: + bias = g.get_node(bias_input[0]) + else: + bias = _expr.const(np.zeros(x_shape[begin_norm_axis])) + + if scale_input: + scale = g.get_node(scale_input[0]) + else: + scale = _expr.const(np.ones(x_shape[begin_norm_axis])) + out = _op.nn.layer_norm(x, gamma=scale, beta=bias, @@ -351,7 +375,7 @@ def convert_leaky_relu(g, op, block): alpha = op.attr('alpha') x = g.get_node(op.input('X')[0]) out = _op.nn.leaky_relu(x, alpha=alpha) - g.add_node(op.output('Out')[0]) + g.add_node(op.output('Out')[0], out) def convert_lookup_table(g, op, block): @@ -540,7 +564,7 @@ def convert_pool2d(g, op, block): if global_pooling: adaptive = True ksize = [1, 1] - + input = g.get_node(op.input('X')[0]) in_h, in_w = infer_shape(input)[2:] @@ -587,8 +611,27 @@ def convert_pool2d(g, op, block): def convert_reshape(g, op, block): """Operator converter for reshape.""" - shape = op.attr('shape') - out = _op.reshape(g.get_node(op.input('X')[0]), shape) + shape_attr = op.input('Shape') + tensor_attr = op.input('ShapeTensor') + data = g.get_node(op.input('X')[0]) + if shape_attr: + new_shape = g.get_node(shape_attr[0]) + elif tensor_attr: + tmp_shape = [] + for shape_name in tensor_attr: + 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) + else: + new_shape = op.attr('shape') + out = _op.reshape(data, new_shape) g.add_node(op.output('Out')[0], out) @@ -627,7 +670,7 @@ def convert_shape(g, op, block): def convert_slice(g, op, block): """Operator converter for slice.""" - def parameter_process(starts, ends, axes): + def parameter_process(starts, ends, axes, dshape): new_axes = [] new_starts = [] new_ends = [] @@ -640,22 +683,29 @@ def parameter_process(starts, ends, axes): pop_index += 1 else: new_starts.append(0) - new_ends.append(np.iinfo(np.int32).max) + 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') + decrease_axis = op.attr('decrease_axis') if isinstance(starts, int): starts = [starts] if isinstance(ends, int): ends = [ends] if isinstance(axes, int): axes = [axes] - starts, ends, axes = parameter_process(starts, ends, axes) - out = _op.strided_slice(g.get_node(op.input('Input')[0]), + 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 decrease_axis: + out = _op.squeeze(out, axis=decrease_axis) g.add_node(op.output('Out')[0], out) @@ -672,15 +722,6 @@ def convert_softmax(g, op, block): out = e / _op.sum(e, axis, keepdims=True) g.add_node(op.output('Out')[0], out) - -def convert_transpose(g, op, block): - """Operator converter for transpose.""" - - perm = op.attr('axis') - out = _op.transpose(g.get_node(op.input('X')[0]), axes=perm) - g.add_node(op.output('Out')[0], out) - - def convert_unsqueeze(g, op, block): """Operator converter for unsqueeze.""" @@ -727,7 +768,6 @@ def convert_unsqueeze(g, op, block): 'slice': convert_slice, 'softmax': convert_softmax, 'tanh': convert_activation, - 'transpose2': convert_transpose, 'unsqueeze2': convert_unsqueeze, } @@ -747,7 +787,9 @@ def get_node(self, name): def add_node(self, name, node): self.nodes[name] = fold_constant(node) - def get_params(self, name): + def get_params(self, name=None): + if name is None: + return self.params assert name in self.params return self.params[name] diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 94a2c468b21a..d4dea6cbe59a 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -19,7 +19,6 @@ import shutil import numpy as np -from paddle.fluid.layers.nn import pad import tvm import tvm.testing import tvm.topi.testing @@ -48,38 +47,45 @@ def get_paddle_model(func, input_spec): return baseline_model def verify_model(func, input_data, rtol=1e-5, atol=1e-5): - if not (isinstance(input_data, list) or isinstance(input_data, tuple)): + if not (isinstance(input_data, (tuple, list))): input_data = [input_data] input_spec = [] input_names = [] input_shape_dict = {} + 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)) input_names.append(input_name) input_shape_dict[input_name] = data.shape + if isinstance(data, np.ndarray): + compiled_input[input_name] = data + else: + compiled_input[input_name] = data.numpy() baseline_model = get_paddle_model(func, input_spec) - baseline_outputs = baseline_model(*[input.clone() for input in input_data]) + baseline_outputs = baseline_model(*[input[:] for input in input_data]) # get paddle outputs - if isinstance(baseline_outputs, tuple): + if isinstance(baseline_outputs, (tuple, list)): baseline_outputs = tuple(out.numpy() for out in baseline_outputs) else: baseline_outputs = (baseline_outputs.numpy(),) mod, params = relay.frontend.from_paddle(baseline_model, input_shape_dict) - for arg in mod["main"].params[: len(input_names)]: + 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_input = dict(zip(input_names, [inp.clone().numpy() for inp in input_data])) + 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, inp in compiled_input.items(): - gmod.set_input(name, inp) + for name in compiled_names: + gmod.set_input(name, compiled_input[name]) gmod.run() for i, baseline_output in enumerate(baseline_outputs): @@ -112,26 +118,310 @@ def add_subtract3(inputs1, inputs2): verify_model(add_subtract3, [input_data, input_data2]) @tvm.testing.uses_gpu -def test_forward_multiply(): - input_shape = [10] +def test_forward_argmax(): + input_shape = [1, 3, 10, 10] + + class ArgMax(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return paddle.argmax(inputs) + + class ArgMax1(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmax(axis=1) + + class ArgMax2(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmax(axis=1, keepdim=False) + + class ArgMax3(nn.Layer): + @paddle.jit.to_static + def forward(self, inputs): + return inputs.argmax(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) + +@tvm.testing.uses_gpu +def test_forward_assign(): + @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,]) + input_data2 = np.random.randint(100, size=input_shape) + verify_model(assign, [input_data2,]) + +@tvm.testing.uses_gpu +def test_forward_batch_norm(): + class BatchNorm1D(nn.Layer): + def __init__(self): + super(BatchNorm1D, self).__init__() + self.batch_norm = nn.BatchNorm1D(2) + + @paddle.jit.to_static + def forward(self, input_data): + return self.batch_norm(input_data) + + class BatchNorm2D(nn.Layer): + def __init__(self): + super(BatchNorm2D, self).__init__() + self.batch_norm = nn.BatchNorm2D(2) + + @paddle.jit.to_static + def forward(self, input_data): + return self.batch_norm(input_data) + + class BatchNorm3D(nn.Layer): + def __init__(self): + super(BatchNorm3D, self).__init__() + self.batch_norm = nn.BatchNorm3D(2) + + @paddle.jit.to_static + def forward(self, input_data): + return self.batch_norm(input_data) + + input_data = paddle.rand((2, 2, 3), dtype="float32") + verify_model(BatchNorm1D(), input_data=input_data) + input_data = paddle.rand((2, 2, 2, 3), dtype="float32") + verify_model(BatchNorm2D(), input_data=input_data) + input_data = paddle.rand((2, 2, 2, 2, 3), dtype="float32") + verify_model(BatchNorm3D(), input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_cast(): + @paddle.jit.to_static + def cast1(inputs, dtype="uint8"): + return paddle.cast(inputs, dtype) + + @paddle.jit.to_static + def cast2(inputs, dtype="int64"): + return inputs.cast(dtype) + + input_shape = [2, 3] + input_data = paddle.rand(input_shape, dtype="float32") * 100 + verify_model(cast1, [input_data,]) + verify_model(cast2, [input_data,]) + +@tvm.testing.uses_gpu +def test_forward_concat_unsqueeze(): + @paddle.jit.to_static + def concat_unsqueeze1(inputs): + return paddle.concat([inputs[:, 0].unsqueeze(1), inputs[:, 1].unsqueeze(1)], axis=1) + + @paddle.jit.to_static + def concat_unsqueeze2(inputs): + a = (inputs[:, :, 0] + 2) * 7 + b = (inputs[:, :, 1] + 3) * 11 + c = (inputs[:, :, 2] + 5) * 13 + return paddle.concat([paddle.unsqueeze(t, axis=2) for t in [a, b, c]], axis=2) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(concat_unsqueeze1, input_data=input_data) + verify_model(concat_unsqueeze2, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_cumsum(): + @paddle.jit.to_static + def cusum1(inputs): + return paddle.cumsum(inputs) + + @paddle.jit.to_static + def cusum2(inputs): + return paddle.cumsum(inputs, axis=0) + + @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(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__() + self.conv = nn.Conv2D(3, 6, 7, bias_attr=True) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + class Conv2D2(nn.Layer): + def __init__(self): + super(Conv2D2, self).__init__() + self.conv = nn.Conv2D(3, 6, 7, groups=3, bias_attr=False) + self.softmax = nn.Softmax() + + @paddle.jit.to_static + def forward(self, inputs): + return self.softmax(self.conv(inputs)) + + 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) + +@tvm.testing.uses_gpu +def test_forward_dropout(): + @paddle.jit.to_static + def dropout(inputs): + return nn.functional.dropout(inputs) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(dropout, input_data=input_data[0, 0]) + verify_model(dropout, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_shape_full(): + @paddle.jit.to_static + def full1(inputs): + 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) + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(full1, input_data=[input_data]) + verify_model(full2, input_data=[input_data]) + +@tvm.testing.uses_gpu +def test_forward_ones_like(): + @paddle.jit.to_static + def ones_like1(inputs): + return paddle.ones_like(inputs) + + @paddle.jit.to_static + def ones_like2(inputs): + return paddle.ones_like(inputs, dtype="int32") + + 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) + +@tvm.testing.uses_gpu +def test_forward_gelu(): + @paddle.jit.to_static + def gelu(inputs): + return nn.functional.gelu(inputs) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(gelu, input_data=input_data) + +@tvm.testing.uses_gpu +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.rand(input_shape, dtype="float32") + verify_model(hard_sigmoid, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_hard_swish(): + @paddle.jit.to_static + def hard_swish(inputs): + return nn.functional.hardswish(inputs) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(hard_swish, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_layer_norm(): + @paddle.jit.to_static + def layer_norm(inputs, weight, bias): + return nn.functional.layer_norm(inputs, inputs.shape[-1], weight=weight, bias=bias) + + class LayerNorm(nn.Layer): + def __init__(self): + super(LayerNorm, self).__init__() + data_shape = [10] + self.layer_norm = nn.LayerNorm(data_shape) + + @paddle.jit.to_static + def forward(self, inputs): + return self.layer_norm(inputs) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + weight = paddle.rand([10], dtype="float32") + bias = paddle.rand([10], dtype="float32") + verify_model(layer_norm, input_data=[input_data, weight, bias]) + 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_look_up(): + @paddle.jit.to_static + def look_up(inputs, weight): + return nn.functional.embedding(inputs, weight) + + class LookUp(nn.Layer): + def __init__(self): + super(LookUp, self).__init__() + self.embedding = paddle.nn.Embedding(10, 4, sparse=True) + + @paddle.jit.to_static + def forward(self, inputs): + return self.embedding(inputs) + + input_shape = [1, 3, 10, 10] + 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) + +@tvm.testing.uses_gpu +def test_forward_multiply(): @paddle.jit.to_static def multiply1(inputs): return inputs * inputs @paddle.jit.to_static def multiply2(inputs): - return inputs * 1.0 + return inputs * 1.0 / 2.0 @paddle.jit.to_static - def multiply3(inputs): + def multiply3(inputs, inputs2): ones = paddle.ones([10], dtype="float32") - return inputs * ones + return inputs * ones / inputs2 + input_shape = [10] input_data = paddle.rand(input_shape, dtype="float32") verify_model(multiply1, input_data=input_data) verify_model(multiply2, input_data=input_data) - verify_model(multiply3, input_data=input_data) + input_data2 = paddle.rand(input_shape, dtype="float32") + verify_model(multiply3, input_data=[input_data, input_data2]) @tvm.testing.uses_gpu def test_forward_matmul(): @@ -161,36 +451,138 @@ def forward(self, input1, input2): verify_model(MatMul1(), input_data=[input_data1, input_data2]) @tvm.testing.uses_gpu -def test_forward_conv(): - conv2d_input_shape = [1, 3, 10, 10] +def test_forward_pool2d(): + @paddle.jit.to_static + def pool2d1(inputs): + return nn.functional.avg_pool2d(inputs, kernel_size=2, stride=2, padding=0) - class Conv2D1(nn.Layer): - def __init__(self): - super(Conv2D1, self).__init__() - self.conv = nn.Conv2D(3, 6, 7, bias_attr=True) - self.softmax = nn.Softmax() - - @paddle.jit.to_static - def forward(self, inputs): - return self.softmax(self.conv(inputs)) + @paddle.jit.to_static + def pool2d2(inputs): + return nn.functional.adaptive_avg_pool2d(inputs, output_size=[3, 3]) - class Conv2D2(nn.Layer): - def __init__(self): - super(Conv2D2, self).__init__() - self.conv = nn.Conv2D(3, 6, 7, groups=3, bias_attr=False) - self.softmax = nn.Softmax() + @paddle.jit.to_static + def pool2d3(inputs): + return nn.functional.max_pool2d(inputs, kernel_size=2, stride=2, padding=0, return_mask=True) - @paddle.jit.to_static - def forward(self, inputs): - return self.softmax(self.conv(inputs)) + input_data = paddle.uniform(shape=[1, 2, 32, 32], dtype='float32', min=-1, max=1) + verify_model(pool2d1, input_data=input_data) + verify_model(pool2d2, input_data=input_data) + # verify_model(pool2d3, input_data=input_data) - 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) +@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 + def reshape1(inputs, x): + new_shape = paddle.shape(x) + return paddle.reshape(inputs, new_shape) + + @paddle.jit.to_static + def reshape2(inputs): + return inputs.reshape([-1]) + + @paddle.jit.to_static + def reshape3(inputs): + data_shape = inputs.shape + return inputs.reshape([data_shape[0] * data_shape[1], data_shape[2]]) + + @paddle.jit.to_static + def reshape4(inputs, x): + new_shape = paddle.shape(x) + return paddle.reshape(inputs, [new_shape[2], 2, -1]) + + 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]) + 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]) + +@tvm.testing.uses_gpu +def test_forward_scale(): + @paddle.jit.to_static + def scale1(inputs): + return paddle.scale(inputs, scale=2.0, bias=1.0) + + @paddle.jit.to_static + def scale2(inputs): + return paddle.scale(inputs, scale=3, bias=2.1, act="gelu") + + input_data = paddle.randn(shape=[2,3], dtype='float32') + verify_model(scale1, input_data=[input_data,]) + verify_model(scale2, input_data=input_data) + +@tvm.testing.uses_gpu +def test_forward_slice(): + @paddle.jit.to_static + def slice1(inputs): + return inputs[:, :, :, :3] + + @paddle.jit.to_static + def slice2(inputs): + return inputs[0, :, :-3, :] + + @paddle.jit.to_static + def slice3(inputs): + return inputs[0::2, 0::2] + inputs[1::2, 1::2] + + @paddle.jit.to_static + def slice4(inputs): + x0 = paddle.to_tensor([2]) - paddle.to_tensor([1]) + 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) + + input_shape = [1, 3, 10, 10] + input_data = paddle.rand(input_shape, dtype="float32") + verify_model(tanh, 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_look_up() test_forward_multiply() test_forward_matmul() - test_forward_conv() + test_forward_pool2d() + test_forward_relu() + test_forward_reshape() + test_forward_scale() + test_forward_slice() + test_forward_tanh() From 1d599c1d2faf6284da38fdbc38fb2b29c419cb2a Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Wed, 11 Aug 2021 20:18:04 +0800 Subject: [PATCH 15/23] delete import pandas --- python/tvm/relay/frontend/paddlepaddle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 02f256ab3435..5308c960b838 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -19,7 +19,6 @@ """Paddle: PArallel Distributed Deep LEarning.""" import copy import warnings -from pandas.core.dtypes.inference import is_scalar import six import numpy as np From 75ef19b0f143e3986fcf13d7fae5286d94eec5da Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 13 Aug 2021 14:45:34 +0800 Subject: [PATCH 16/23] add paddlepaddle tests --- tests/scripts/task_python_frontend.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/scripts/task_python_frontend.sh b/tests/scripts/task_python_frontend.sh index fb388a6b7edd..d25d52438daa 100755 --- a/tests/scripts/task_python_frontend.sh +++ b/tests/scripts/task_python_frontend.sh @@ -54,3 +54,6 @@ run_pytest cython python-frontend-darknet tests/python/frontend/darknet echo "Running relay PyTorch frontend test..." run_pytest cython python-frontend-pytorch tests/python/frontend/pytorch + +echo "Running relay PaddlePaddle frontend test..." +run_pytest cython python-frontend-paddlepaddle tests/python/frontend/paddlepaddle From 67ca20da4b4c921f7a1986733107b429c2e869eb Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 13 Aug 2021 15:15:22 +0800 Subject: [PATCH 17/23] modify the variable name of convert_reshape --- python/tvm/relay/frontend/paddlepaddle.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index 5308c960b838..dc2e94c896b8 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -610,14 +610,14 @@ def convert_pool2d(g, op, block): def convert_reshape(g, op, block): """Operator converter for reshape.""" - shape_attr = op.input('Shape') - tensor_attr = op.input('ShapeTensor') + input_shape = op.input('Shape') + input_shape_tensor = op.input('ShapeTensor') data = g.get_node(op.input('X')[0]) - if shape_attr: - new_shape = g.get_node(shape_attr[0]) - elif tensor_attr: + if input_shape: + new_shape = g.get_node(input_shape[0]) + elif input_shape_tensor: tmp_shape = [] - for shape_name in tensor_attr: + for shape_name in input_shape_tensor: shape = g.get_node(shape_name) if len(infer_shape(shape)) == 0: shape = _op.reshape(shape, [-1]) From 6f91fc5150b773c542f4dd1d9cd2fb12c280df4c Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 13 Aug 2021 15:29:04 +0800 Subject: [PATCH 18/23] formatting --- python/tvm/relay/frontend/paddlepaddle.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index dc2e94c896b8..c95ed0108b49 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -143,9 +143,9 @@ def convert_conv2d(g, op, block): paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: - paddings = [paddings[0],paddings[1],paddings[0],paddings[1]] + paddings = [paddings[0], paddings[1], paddings[0], paddings[1]] if len(paddings) == 4: - paddings = [paddings[0],paddings[2],paddings[1],paddings[3]] + paddings = [paddings[0], paddings[2], paddings[1], paddings[3]] else: msg = 'Value {} in attribute "padding" of operator Conv is not ' "valid." raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) @@ -346,7 +346,8 @@ def convert_layer_norm(g, op, block): scale_input = op.input('Scale') x_shape = infer_shape(x) - assert begin_norm_axis == -1 or begin_norm_axis == len(x_shape) - 1, "Support only normalization over last one dimension." + assert begin_norm_axis == -1 or begin_norm_axis == len( + x_shape) - 1, "Support only normalization over last one dimension." if bias_input: bias = g.get_node(bias_input[0]) @@ -356,7 +357,7 @@ def convert_layer_norm(g, op, block): if scale_input: scale = g.get_node(scale_input[0]) else: - scale = _expr.const(np.ones(x_shape[begin_norm_axis])) + scale = _expr.const(np.ones(x_shape[begin_norm_axis])) out = _op.nn.layer_norm(x, gamma=scale, @@ -587,9 +588,9 @@ def convert_pool2d(g, op, block): paddings = [pad_h[0], pad_w[0], pad_h[1], pad_w[1]] elif padding_algorithm == "EXPLICIT": if len(paddings) == 2: - paddings = [paddings[0],paddings[1],paddings[0],paddings[1]] + paddings = [paddings[0], paddings[1], paddings[0], paddings[1]] if len(paddings) == 4: - paddings = [paddings[0],paddings[2],paddings[1],paddings[3]] + paddings = [paddings[0], paddings[2], paddings[1], paddings[3]] else: msg = 'Value {} in attribute "padding" of operator Pool2d is not ' "valid." raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) @@ -700,9 +701,7 @@ def parameter_process(starts, ends, axes, dshape): 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) + out = _op.strided_slice(data, begin=starts, end=ends) if decrease_axis: out = _op.squeeze(out, axis=decrease_axis) g.add_node(op.output('Out')[0], out) @@ -721,6 +720,7 @@ def convert_softmax(g, op, block): out = e / _op.sum(e, axis, keepdims=True) g.add_node(op.output('Out')[0], out) + def convert_unsqueeze(g, op, block): """Operator converter for unsqueeze.""" @@ -773,7 +773,6 @@ def convert_unsqueeze(g, op, block): class GraphProto(object): """ A helper class for handling relay functions from PaddlePaddle model.""" - def __init__(self): self.nodes = {} self.params = {} From a0aee15ccd26131d75ec45fd3f69a25c89816fc7 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Fri, 13 Aug 2021 15:32:09 +0800 Subject: [PATCH 19/23] formatting --- .../frontend/paddlepaddle/test_forward.py | 117 ++++++++++++++---- 1 file changed, 92 insertions(+), 25 deletions(-) diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index d4dea6cbe59a..20a110f7d657 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -28,14 +28,17 @@ import paddle import paddle.nn as nn -PADDLE_TEST_DATA_ROOT_PATH = Path(Path("~").expanduser(), ".tvm_test_data", "paddle") +PADDLE_TEST_DATA_ROOT_PATH = Path( + Path("~").expanduser(), ".tvm_test_data", "paddle") PADDLE_TEST_DATA_ROOT_PATH.mkdir(parents=True, exist_ok=True) + def assert_shapes_match(tru, est): if tru.shape != est.shape: msg = "Output shapes {} and {} don't match" raise AssertionError(msg.format(tru.shape, est.shape)) + def get_paddle_model(func, input_spec): global PADDLE_TEST_DATA_ROOT_PATH model_path = Path(PADDLE_TEST_DATA_ROOT_PATH, "model") @@ -46,6 +49,7 @@ def get_paddle_model(func, input_spec): shutil.rmtree(str(PADDLE_TEST_DATA_ROOT_PATH)) return baseline_model + def verify_model(func, input_data, rtol=1e-5, atol=1e-5): if not (isinstance(input_data, (tuple, list))): input_data = [input_data] @@ -56,7 +60,10 @@ 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)) + 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): @@ -71,15 +78,15 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): if isinstance(baseline_outputs, (tuple, list)): baseline_outputs = tuple(out.numpy() for out in baseline_outputs) else: - baseline_outputs = (baseline_outputs.numpy(),) + baseline_outputs = (baseline_outputs.numpy(), ) 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]: + for arg in mod["main"].params[:parms_num]: assert 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) @@ -92,7 +99,11 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): compiled_output = gmod.get_output(i).numpy() assert_shapes_match(baseline_output, compiled_output) - tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) + tvm.testing.assert_allclose(baseline_output, + compiled_output, + rtol=rtol, + atol=atol) + @tvm.testing.uses_gpu def test_forward_add_subtract(): @@ -101,7 +112,7 @@ def test_forward_add_subtract(): @paddle.jit.to_static def add_subtract(inputs): return paddle.subtract(paddle.add(inputs, inputs), inputs) - + @paddle.jit.to_static def add_subtract2(inputs): return inputs + 1 - 2 @@ -117,6 +128,7 @@ def add_subtract3(inputs1, inputs2): input_data2 = paddle.rand(input_shape, dtype="float32") verify_model(add_subtract3, [input_data, input_data2]) + @tvm.testing.uses_gpu def test_forward_argmax(): input_shape = [1, 3, 10, 10] @@ -147,6 +159,7 @@ def forward(self, inputs): verify_model(ArgMax2(), input_data=input_data) verify_model(ArgMax3(), input_data=input_data) + @tvm.testing.uses_gpu def test_forward_assign(): @paddle.jit.to_static @@ -155,9 +168,14 @@ def 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, + ]) + @tvm.testing.uses_gpu def test_forward_batch_norm(): @@ -195,6 +213,7 @@ def forward(self, input_data): input_data = paddle.rand((2, 2, 2, 2, 3), dtype="float32") verify_model(BatchNorm3D(), input_data=input_data) + @tvm.testing.uses_gpu def test_forward_cast(): @paddle.jit.to_static @@ -207,27 +226,35 @@ 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 def test_forward_concat_unsqueeze(): @paddle.jit.to_static def concat_unsqueeze1(inputs): - return paddle.concat([inputs[:, 0].unsqueeze(1), inputs[:, 1].unsqueeze(1)], axis=1) + return paddle.concat( + [inputs[:, 0].unsqueeze(1), inputs[:, 1].unsqueeze(1)], axis=1) @paddle.jit.to_static def concat_unsqueeze2(inputs): a = (inputs[:, :, 0] + 2) * 7 b = (inputs[:, :, 1] + 3) * 11 c = (inputs[:, :, 2] + 5) * 13 - return paddle.concat([paddle.unsqueeze(t, axis=2) for t in [a, b, c]], axis=2) + return paddle.concat([paddle.unsqueeze(t, axis=2) for t in [a, b, c]], + axis=2) input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") verify_model(concat_unsqueeze1, input_data=input_data) verify_model(concat_unsqueeze2, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_cumsum(): @paddle.jit.to_static @@ -245,8 +272,13 @@ def cusum3(inputs): 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(cusum2, [ + input_data, + ]) + verify_model(cusum3, [ + input_data, + ]) + @tvm.testing.uses_gpu def test_forward_conv(): @@ -276,6 +308,7 @@ def forward(self, inputs): verify_model(Conv2D1(), input_data=conv2d_input_data) verify_model(Conv2D2(), input_data=conv2d_input_data) + @tvm.testing.uses_gpu def test_forward_dropout(): @paddle.jit.to_static @@ -287,6 +320,7 @@ def dropout(inputs): verify_model(dropout, input_data=input_data[0, 0]) verify_model(dropout, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_shape_full(): @paddle.jit.to_static @@ -302,6 +336,7 @@ def full2(inputs): verify_model(full1, input_data=[input_data]) verify_model(full2, input_data=[input_data]) + @tvm.testing.uses_gpu def test_forward_ones_like(): @paddle.jit.to_static @@ -317,6 +352,7 @@ def ones_like2(inputs): verify_model(ones_like1, input_data=input_data) verify_model(ones_like2, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_gelu(): @paddle.jit.to_static @@ -327,6 +363,7 @@ def gelu(inputs): input_data = paddle.rand(input_shape, dtype="float32") verify_model(gelu, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_hard_sigmoid(): @paddle.jit.to_static @@ -337,6 +374,7 @@ def hard_sigmoid(inputs): input_data = paddle.rand(input_shape, dtype="float32") verify_model(hard_sigmoid, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_hard_swish(): @paddle.jit.to_static @@ -347,11 +385,15 @@ def hard_swish(inputs): input_data = paddle.rand(input_shape, dtype="float32") verify_model(hard_swish, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_layer_norm(): @paddle.jit.to_static def layer_norm(inputs, weight, bias): - return nn.functional.layer_norm(inputs, inputs.shape[-1], weight=weight, bias=bias) + return nn.functional.layer_norm(inputs, + inputs.shape[-1], + weight=weight, + bias=bias) class LayerNorm(nn.Layer): def __init__(self): @@ -370,6 +412,7 @@ def forward(self, inputs): verify_model(layer_norm, input_data=[input_data, weight, bias]) verify_model(LayerNorm(), input_data=input_data) + @tvm.testing.uses_gpu def test_forward_leaky_relu(): @paddle.jit.to_static @@ -380,6 +423,7 @@ def leaky_relu(inputs): input_data = paddle.rand(input_shape, dtype="float32") verify_model(leaky_relu, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_look_up(): @paddle.jit.to_static @@ -401,6 +445,7 @@ def forward(self, inputs): verify_model(look_up, input_data=[input_data, weight]) verify_model(LookUp(), input_data=input_data) + @tvm.testing.uses_gpu def test_forward_multiply(): @paddle.jit.to_static @@ -423,16 +468,16 @@ def multiply3(inputs, inputs2): input_data2 = paddle.rand(input_shape, dtype="float32") verify_model(multiply3, input_data=[input_data, input_data2]) + @tvm.testing.uses_gpu def test_forward_matmul(): - class MatMul1(nn.Layer): def forward(self, input1, input2): return paddle.matmul(input1, input2) # matrix x vector input_data1 = paddle.randn((3, 4), dtype="float32") - input_data2 = paddle.randn((4,), dtype="float32") + input_data2 = paddle.randn((4, ), dtype="float32") verify_model(MatMul1(), input_data=[input_data1, input_data2]) # matrix x matrix @@ -450,11 +495,15 @@ def forward(self, input1, input2): input_data2 = paddle.randn((4, 5), dtype="float32") verify_model(MatMul1(), input_data=[input_data1, input_data2]) + @tvm.testing.uses_gpu def test_forward_pool2d(): @paddle.jit.to_static def pool2d1(inputs): - return nn.functional.avg_pool2d(inputs, kernel_size=2, stride=2, padding=0) + return nn.functional.avg_pool2d(inputs, + kernel_size=2, + stride=2, + padding=0) @paddle.jit.to_static def pool2d2(inputs): @@ -462,13 +511,21 @@ def pool2d2(inputs): @paddle.jit.to_static def pool2d3(inputs): - return nn.functional.max_pool2d(inputs, kernel_size=2, stride=2, padding=0, return_mask=True) - - input_data = paddle.uniform(shape=[1, 2, 32, 32], dtype='float32', min=-1, max=1) + return nn.functional.max_pool2d(inputs, + kernel_size=2, + stride=2, + padding=0, + return_mask=True) + + input_data = paddle.uniform(shape=[1, 2, 32, 32], + dtype='float32', + min=-1, + max=1) verify_model(pool2d1, input_data=input_data) verify_model(pool2d2, input_data=input_data) # verify_model(pool2d3, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_relu(): @paddle.jit.to_static @@ -479,6 +536,7 @@ def relu(inputs): 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 @@ -508,6 +566,7 @@ def reshape4(inputs, x): verify_model(reshape3, input_data=paddle.randn((2, 3, 4))) verify_model(reshape4, input_data=[input_data, input_data2]) + @tvm.testing.uses_gpu def test_forward_scale(): @paddle.jit.to_static @@ -518,10 +577,13 @@ def scale1(inputs): def scale2(inputs): return paddle.scale(inputs, scale=3, bias=2.1, act="gelu") - input_data = paddle.randn(shape=[2,3], dtype='float32') - verify_model(scale1, input_data=[input_data,]) + input_data = paddle.randn(shape=[2, 3], dtype='float32') + verify_model(scale1, input_data=[ + input_data, + ]) verify_model(scale2, input_data=input_data) + @tvm.testing.uses_gpu def test_forward_slice(): @paddle.jit.to_static @@ -541,15 +603,19 @@ def slice4(inputs): x0 = paddle.to_tensor([2]) - paddle.to_tensor([1]) 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(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 @@ -560,6 +626,7 @@ def tanh(inputs): input_data = paddle.rand(input_shape, dtype="float32") verify_model(tanh, input_data=input_data) + if __name__ == "__main__": test_forward_add_subtract() test_forward_argmax() From 80d308a8e096e0ac1cd4781c8e69aa5267c25e0d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 13 Aug 2021 07:55:21 +0000 Subject: [PATCH 20/23] use black to format python code --- python/tvm/relay/frontend/paddlepaddle.py | 563 +++++++++--------- .../frontend/paddlepaddle/test_forward.py | 122 ++-- 2 files changed, 343 insertions(+), 342 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index c95ed0108b49..c67c8b9c39e0 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -42,7 +42,7 @@ __all__ = ["from_paddle"] -def shape_of(x, dtype='int32'): +def shape_of(x, dtype="int32"): ttype = infer_type(x).checked_type if not _ty.is_dynamic(ttype): shape = list(ttype.shape) @@ -65,74 +65,76 @@ def _get_pad_size(in_size, dilated_kernel_size, stride_size): def convert_arg_max(g, op, block): """Operator converter for arg_max.""" - axis = op.attr('axis') - keepdims = op.attr('keepdims') - flatten = op.attr('flatten') + axis = op.attr("axis") + keepdims = op.attr("keepdims") + flatten = op.attr("flatten") - x = g.get_node(op.input('X')[0]) + 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) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_assign(g, op, block): """Operator converter for assign.""" - out = _op.copy(g.get_node(op.input('X')[0])) - g.add_node(op.output('Out')[0], out) + out = _op.copy(g.get_node(op.input("X")[0])) + g.add_node(op.output("Out")[0], out) def convert_batch_norm(g, op, block): """Operator converter for batch_norm.""" - ipt_name = op.input('X')[0] - scale_name = op.input('Scale')[0] - bias_name = op.input('Bias')[0] - mean_name = op.input('Mean')[0] - variance_name = op.input('Variance')[0] - epsilon = op.attr('epsilon') - momentum = op.attr('momentum') - out = _op.nn.batch_norm(g.get_node(ipt_name), - g.get_node(scale_name), - g.get_node(bias_name), - g.get_node(mean_name), - g.get_node(variance_name), - epsilon=epsilon) - g.add_node(op.output('Y')[0], out[0]) + ipt_name = op.input("X")[0] + scale_name = op.input("Scale")[0] + bias_name = op.input("Bias")[0] + mean_name = op.input("Mean")[0] + variance_name = op.input("Variance")[0] + epsilon = op.attr("epsilon") + momentum = op.attr("momentum") + out = _op.nn.batch_norm( + g.get_node(ipt_name), + g.get_node(scale_name), + g.get_node(bias_name), + g.get_node(mean_name), + g.get_node(variance_name), + epsilon=epsilon, + ) + g.add_node(op.output("Y")[0], out[0]) def convert_cast(g, op, block): """Operator converter for cast.""" - dtype = block.var(op.output('Out')[0]).dtype - dtype = str(dtype).strip().split('.')[1] - x = g.get_node(op.input('X')[0]) + dtype = block.var(op.output("Out")[0]).dtype + dtype = str(dtype).strip().split(".")[1] + x = g.get_node(op.input("X")[0]) out = _op.cast(x, dtype=dtype) - g.add_node(op.output('Out')[0], out) + 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 = [g.get_node(op.input("X")[i]) for i in range(len(op.input("X")))] + axis = op.attr("axis") out = _op.concatenate(inputs, axis=axis) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_conv2d(g, op, block): """Operator converter for conv2d.""" - dilations = op.attr('dilations') - groups = op.attr('groups') - paddings = op.attr('paddings') - padding_algorithm = op.attr('padding_algorithm') - strides = op.attr('strides') - - kernel = g.get_node(op.input('Filter')[0]) - input = g.get_node(op.input('Input')[0]) + dilations = op.attr("dilations") + groups = op.attr("groups") + paddings = op.attr("paddings") + padding_algorithm = op.attr("padding_algorithm") + strides = op.attr("strides") + + kernel = g.get_node(op.input("Filter")[0]) + input = g.get_node(op.input("Input")[0]) out_channels, _, k_h, k_w = infer_shape(kernel) in_h, in_w = infer_shape(input)[2:] if padding_algorithm == "VALID": @@ -150,26 +152,28 @@ def convert_conv2d(g, op, block): msg = 'Value {} in attribute "padding" of operator Conv is not ' "valid." raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) - out = _op.nn.conv2d(input, - kernel, - strides=strides, - padding=paddings, - dilation=dilations, - groups=groups, - channels=out_channels, - kernel_size=[k_h, k_w]) - g.add_node(op.output('Output')[0], out) + out = _op.nn.conv2d( + input, + kernel, + strides=strides, + padding=paddings, + dilation=dilations, + groups=groups, + channels=out_channels, + kernel_size=[k_h, k_w], + ) + g.add_node(op.output("Output")[0], out) def convert_cumsum(g, op, block): """Operator converter for cumsum.""" - axis = op.attr('axis') - exclusive = op.attr('exclusive') - flatten = op.attr('flatten') - reverse = op.attr('reverse') + axis = op.attr("axis") + exclusive = op.attr("exclusive") + flatten = op.attr("flatten") + reverse = op.attr("reverse") - x = g.get_node(op.input('X')[0]) + x = g.get_node(op.input("X")[0]) if axis is None or flatten: x = _op.reshape(x, [-1]) if reverse: @@ -178,80 +182,78 @@ def convert_cumsum(g, op, block): out = _op.reverse(out, axis=axis) else: out = _op.cumsum(x, axis=axis, exclusive=exclusive) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_dropout(g, op, block): """Operator converter for dropout.""" - x = g.get_node(op.input('X')[0]) + x = g.get_node(op.input("X")[0]) out = _op.copy(x) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) 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": 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, } 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 - axis = op.attr('axis') + 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 + 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)) + ipt1 = _op.expand_dims(ipt1, axis=axis, num_newaxis=(len(ipt0_shape) - axis - 1)) out = op_func(ipt0, ipt1) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_equal(g, op, block): """Operator converter for equal.""" - x = g.get_node(op.input('X')[0]) - y = g.get_node(op.input('Y')[0]) + x = g.get_node(op.input("X")[0]) + y = g.get_node(op.input("Y")[0]) out = _op.equal(x, y) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_activation(g, op, block): """Operator converter for all the activation.""" op_map = { - 'exp': _op.exp, - 'relu': _op.nn.relu, - 'tanh': _op.tanh, - 'sqrt': _op.sqrt, - 'erf': _op.erf, - 'abs': _op.abs, + "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])) - g.add_node(op.output('Out')[0], out) + out = act_func(g.get_node(op.input("X")[0])) + g.add_node(op.output("Out")[0], out) def convert_feed(g, op, block): """Converter for model input node.""" if block is not None: - ipt_name = op.output('Out')[0] + 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] + ipt_dtype = str(ipt_dtype).strip().split(".")[1] else: ipt_shape = op.shape - ipt_dtype = str(op.dtype).strip().split('.')[1] + 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] @@ -262,92 +264,90 @@ 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_name = op.output("Out")[0] out_dtype = block.var(out_name).dtype - out_dtype = str(out_dtype).strip().split('.')[1] - x = g.get_node(op.input('X')[0]) + out_dtype = str(out_dtype).strip().split(".")[1] + x = g.get_node(op.input("X")[0]) ipt_type = infer_type(x).checked_type - value = op.attr('value') + 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) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_fill_constant(g, op, block): """Operator converter for fill_constant.""" - 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'): - shape = g.get_node(op.input('ValueTensor')[0]) + 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"): + shape = g.get_node(op.input("ValueTensor")[0]) shape = infer_value(shape, g.get_params()).numpy() - if op.input('ShapeTensor'): - shape = g.get_node(op.input('ShapeTensor')[0]) + if 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) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_gelu(g, op, block): """Operator converter for gelu.""" - x = g.get_node(op.input('X')[0]) - out = x * (_expr.const(0.5, dtype='float32') + - _op.erf(x * _expr.const(0.5**0.5, dtype='float32')) * - _expr.const(0.5, dtype='float32')) - g.add_node(op.output('Out')[0], out) + x = g.get_node(op.input("X")[0]) + out = x * ( + _expr.const(0.5, dtype="float32") + + _op.erf(x * _expr.const(0.5 ** 0.5, dtype="float32")) * _expr.const(0.5, dtype="float32") + ) + g.add_node(op.output("Out")[0], out) def convert_hard_sigmoid(g, op, block): """Operator converter for hard_sigmoid.""" - slope = op.attr('slope') - offset = op.attr('offset') - x = g.get_node(op.input('X')[0]) + slope = op.attr("slope") + offset = op.attr("offset") + x = g.get_node(op.input("X")[0]) out = x * _expr.const(slope) + _expr.const(0.5) out = _op.clip(out, 0, 1) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_hard_swish(g, op, block): """Operator converter for hard_swish.""" - offset = op.attr('offset') - scale = op.attr('scale') - threshold = op.attr('threshold') - assert np.isclose( - offset, 3.0), "Only support offset==3.0 for PaddlePaddle's hard_swish" - 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]) + offset = op.attr("offset") + scale = op.attr("scale") + threshold = op.attr("threshold") + assert np.isclose(offset, 3.0), "Only support offset==3.0 for PaddlePaddle's hard_swish" + 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]) out = _op.clip(x, -1 * offset, offset) out = out / _expr.const(threshold) + _expr.const(0.5) out = x * out - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_layer_norm(g, op, block): """Operator converter for layer_norm.""" - begin_norm_axis = op.attr('begin_norm_axis') - epsilon = op.attr('epsilon') - x = g.get_node(op.input('X')[0]) - bias_input = op.input('Bias') - scale_input = op.input('Scale') + begin_norm_axis = op.attr("begin_norm_axis") + epsilon = op.attr("epsilon") + x = g.get_node(op.input("X")[0]) + bias_input = op.input("Bias") + scale_input = op.input("Scale") x_shape = infer_shape(x) - assert begin_norm_axis == -1 or begin_norm_axis == len( - x_shape) - 1, "Support only normalization over last one dimension." + assert ( + begin_norm_axis == -1 or begin_norm_axis == len(x_shape) - 1 + ), "Support only normalization over last one dimension." if bias_input: bias = g.get_node(bias_input[0]) @@ -359,54 +359,50 @@ def convert_layer_norm(g, op, block): else: scale = _expr.const(np.ones(x_shape[begin_norm_axis])) - out = _op.nn.layer_norm(x, - gamma=scale, - beta=bias, - axis=begin_norm_axis, - epsilon=epsilon, - center=True, - scale=True) - g.add_node(op.output('Y')[0], out) + out = _op.nn.layer_norm( + x, gamma=scale, beta=bias, axis=begin_norm_axis, epsilon=epsilon, center=True, scale=True + ) + g.add_node(op.output("Y")[0], out) def convert_leaky_relu(g, op, block): """Operator converter for leaky_relu.""" - alpha = op.attr('alpha') - x = g.get_node(op.input('X')[0]) + alpha = op.attr("alpha") + x = g.get_node(op.input("X")[0]) out = _op.nn.leaky_relu(x, alpha=alpha) - g.add_node(op.output('Out')[0], out) + 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') - is_sparse = op.attr('is_sparse') - height_sections = op.attr('height_sections') + indices = g.get_node(op.input("Ids")[0]) + padding_idx = op.attr("padding_idx") + is_sparse = op.attr("is_sparse") + height_sections = op.attr("height_sections") 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) - g.add_node(op.output('Out')[0], out) + 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_matmul(g, op, block): """Operator converter for matmul.""" - inputs = [g.get_node(op.input('X')[0]), g.get_node(op.input('Y')[0])] + inputs = [g.get_node(op.input("X")[0]), g.get_node(op.input("Y")[0])] a_shape = infer_shape(inputs[0]) b_shape = infer_shape(inputs[1]) try: # for matmul_v2 - trans_x = op.attr('trans_x') - trans_y = op.attr('trans_y') + trans_x = op.attr("trans_x") + trans_y = op.attr("trans_y") except: # for matmul - trans_x = op.attr('transpose_X') - trans_y = op.attr('transpose_Y') + trans_x = op.attr("transpose_X") + trans_y = op.attr("transpose_Y") if trans_x: perm = list(range(len(a_shape))) perm[-2] = len(a_shape) - 1 @@ -433,8 +429,7 @@ def flatten_to_nd(x, x_shape, nd=3): return x newshape = _op.concatenate( [ - _expr.const([-1], - dtype=infer_type(x_shape).checked_type.dtype), + _expr.const([-1], dtype=infer_type(x_shape).checked_type.dtype), _op.strided_slice(x_shape, [ndims - nd + 1], [ndims]), ], 0, @@ -469,7 +464,8 @@ def flatten_to_nd(x, x_shape, nd=3): _op.maximum( _op.strided_slice(a_shape, [i], [i + 1]), _op.strided_slice(b_shape, [i], [i + 1]), - ) for i in range(a_rank - 2) + ) + for i in range(a_rank - 2) ], 0, ) @@ -477,10 +473,12 @@ def flatten_to_nd(x, x_shape, nd=3): final_shape = _op.concatenate( [ out_batch, - _op.strided_slice(a_shape, [infer_shape(a_shape)[0] - 2], - [infer_shape(a_shape)[0] - 1]), - _op.strided_slice(b_shape, [infer_shape(b_shape)[0] - 1], - [infer_shape(b_shape)[0]]), + _op.strided_slice( + a_shape, [infer_shape(a_shape)[0] - 2], [infer_shape(a_shape)[0] - 1] + ), + _op.strided_slice( + b_shape, [infer_shape(b_shape)[0] - 1], [infer_shape(b_shape)[0]] + ), ], 0, ) @@ -494,21 +492,21 @@ def flatten_to_nd(x, x_shape, nd=3): if b_rank == 1: out = _op.squeeze(out, axis=[-1]) try: - alpha = op.attr('alpha') + alpha = op.attr("alpha") if not np.isclose(alpha, 1.0): - out = out * _expr.const(alpha).astype('float32') + out = out * _expr.const(alpha).astype("float32") except: pass - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_mul(g, op, block): """Operator converter for mul.""" - x = g.get_node(op.input('X')[0]) - 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 = g.get_node(op.input("X")[0]) + 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_dim = infer_shape(x_shape)[0] @@ -520,24 +518,20 @@ def convert_mul(g, op, block): if x_num_col_dims == 1: x = _op.nn.batch_flatten(x) else: - pre_shape = _op.prod(_op.strided_slice(x_shape, [0], [x_num_col_dims], - [1]), - keepdims=True) - post_shape = _op.prod(_op.strided_slice(x_shape, [x_num_col_dims], - [x_dim], [1]), - keepdims=True) + pre_shape = _op.prod(_op.strided_slice(x_shape, [0], [x_num_col_dims], [1]), keepdims=True) + post_shape = _op.prod( + _op.strided_slice(x_shape, [x_num_col_dims], [x_dim], [1]), keepdims=True + ) new_shape = _op.concatenate([pre_shape, post_shape], axis=0) new_shape = fold_constant(new_shape) x = _op.reshape(x, new_shape) if y_num_col_dims == 1: y = _op.nn.batch_flatten(y) else: - pre_shape = _op.prod(_op.strided_slice(y_shape, [0], [y_num_col_dims], - [1]), - keepdims=True) - post_shape = _op.prod(_op.strided_slice(y_shape, [y_num_col_dims], - [y_dim], [1]), - keepdims=True) + pre_shape = _op.prod(_op.strided_slice(y_shape, [0], [y_num_col_dims], [1]), keepdims=True) + post_shape = _op.prod( + _op.strided_slice(y_shape, [y_num_col_dims], [y_dim], [1]), keepdims=True + ) new_shape = _op.concatenate([pre_shape, post_shape], axis=0) new_shape = fold_constant(new_shape) y = _op.reshape(y, new_shape) @@ -548,31 +542,31 @@ def convert_mul(g, op, block): out_shape = _op.concatenate([out_pre_shape, out_post_shape], axis=0) out_shape = fold_constant(out_shape) out = _op.reshape(out, out_shape) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_pool2d(g, op, block): """Operator converter for pool2d.""" - adaptive = op.attr('adaptive') - ceil_mode = op.attr('ceil_mode') - global_pooling = op.attr('global_pooling') - ksize = op.attr('ksize') - paddings = op.attr('paddings') - padding_algorithm = op.attr('padding_algorithm') - pooling_type = op.attr('pooling_type') + adaptive = op.attr("adaptive") + ceil_mode = op.attr("ceil_mode") + global_pooling = op.attr("global_pooling") + ksize = op.attr("ksize") + paddings = op.attr("paddings") + padding_algorithm = op.attr("padding_algorithm") + pooling_type = op.attr("pooling_type") if global_pooling: adaptive = True ksize = [1, 1] - input = g.get_node(op.input('X')[0]) + input = g.get_node(op.input("X")[0]) in_h, in_w = infer_shape(input)[2:] op_map = { - 'avg': 'avg_pool2d', - 'max': 'max_pool2d', + "avg": "avg_pool2d", + "max": "max_pool2d", } - strides = op.attr('strides') + strides = op.attr("strides") if isinstance(strides, int): strides = [strides, strides] if isinstance(ksize, int): @@ -595,25 +589,22 @@ 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)) - x = g.get_node(op.input('X')[0]) + x = g.get_node(op.input("X")[0]) if not adaptive: - out = getattr(_op.nn, op_map[pooling_type])(x, - pool_size=ksize, - strides=strides, - padding=paddings, - ceil_mode=ceil_mode) + out = getattr(_op.nn, op_map[pooling_type])( + x, pool_size=ksize, strides=strides, padding=paddings, ceil_mode=ceil_mode + ) else: - out = getattr(_op.nn, - "adaptive_" + op_map[pooling_type])(x, output_size=ksize) - g.add_node(op.output('Out')[0], out) + out = getattr(_op.nn, "adaptive_" + op_map[pooling_type])(x, output_size=ksize) + g.add_node(op.output("Out")[0], out) def convert_reshape(g, op, block): """Operator converter for reshape.""" - input_shape = op.input('Shape') - input_shape_tensor = op.input('ShapeTensor') - data = g.get_node(op.input('X')[0]) + input_shape = op.input("Shape") + input_shape_tensor = op.input("ShapeTensor") + data = g.get_node(op.input("X")[0]) if input_shape: new_shape = g.get_node(input_shape[0]) elif input_shape_tensor: @@ -627,49 +618,51 @@ def convert_reshape(g, op, block): elif isinstance(shape, _expr.Expr): tmp_shape.append(shape) else: - tmp_shape.append(_expr.const(np.array(shape).astype('int64'))) + tmp_shape.append(_expr.const(np.array(shape).astype("int64"))) new_shape = _op.concatenate(tmp_shape, axis=0) else: - new_shape = op.attr('shape') + new_shape = op.attr("shape") out = _op.reshape(data, new_shape) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_scale(g, op, block): """Operator converter for scale.""" - scale = op.attr('scale') - bias = op.attr('bias') - bias_after_scale = op.attr('bias_after_scale') - x = g.get_node(op.input('X')[0]) + scale = op.attr("scale") + bias = op.attr("bias") + 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) else: if np.isclose(bias, 0.0): - out = x * _expr.const(np.array(scale).astype('float32')) + out = x * _expr.const(np.array(scale).astype("float32")) elif np.isclose(scale, 1.0): - out = x + _expr.const(np.array(bias).astype('float32')) + out = x + _expr.const(np.array(bias).astype("float32")) else: if bias_after_scale: - out = x * _expr.const( - np.array(scale).astype('float32')) + _expr.const( - np.array(bias).astype('float32')) + out = x * _expr.const(np.array(scale).astype("float32")) + _expr.const( + np.array(bias).astype("float32") + ) else: - out = (x + _expr.const(np.array(bias).astype('float32')) - ) * _expr.const(np.array(scale).astype('float32')) - g.add_node(op.output('Out')[0], out) + out = (x + _expr.const(np.array(bias).astype("float32"))) * _expr.const( + np.array(scale).astype("float32") + ) + g.add_node(op.output("Out")[0], out) def convert_shape(g, op, block): """Operator converter for shape.""" - x = g.get_node(op.input('Input')[0]) + x = g.get_node(op.input("Input")[0]) out = shape_of(x) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_slice(g, op, block): """Operator converter for slice.""" + def parameter_process(starts, ends, axes, dshape): new_axes = [] new_starts = [] @@ -686,12 +679,12 @@ def parameter_process(starts, ends, axes, dshape): new_ends.append(dshape[i]) return new_starts, new_ends, new_axes - data = g.get_node(op.input('Input')[0]) + data = g.get_node(op.input("Input")[0]) dshape = infer_shape(data) - starts = op.attr('starts') - ends = op.attr('ends') - axes = op.attr('axes') - decrease_axis = op.attr('decrease_axis') + starts = op.attr("starts") + ends = op.attr("ends") + axes = op.attr("axes") + decrease_axis = op.attr("decrease_axis") if isinstance(starts, int): starts = [starts] if isinstance(ends, int): @@ -704,75 +697,76 @@ def parameter_process(starts, ends, axes, dshape): out = _op.strided_slice(data, begin=starts, end=ends) if decrease_axis: out = _op.squeeze(out, axis=decrease_axis) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_softmax(g, op, block): """Operator converter for softmax.""" - axis = op.attr('axis') - input_shape = block.var(op.input('X')[0]).shape + axis = op.attr("axis") + input_shape = block.var(op.input("X")[0]).shape if axis < 0: axis = len(input_shape) + axis - x = g.get_node(op.input('X')[0]) + x = g.get_node(op.input("X")[0]) m = _op.max(x, axis, keepdims=True) e = _op.exp(x - m) out = e / _op.sum(e, axis, keepdims=True) - g.add_node(op.output('Out')[0], out) + g.add_node(op.output("Out")[0], out) def convert_unsqueeze(g, op, block): """Operator converter for unsqueeze.""" - x = g.get_node(op.input('X')[0]) - axes = sorted(op.attr('axes')) + x = g.get_node(op.input("X")[0]) + axes = sorted(op.attr("axes")) for axis in axes: x = _op.expand_dims(x, axis=axis, num_newaxis=1) - g.add_node(op.output('Out')[0], x) + g.add_node(op.output("Out")[0], x) _convert_map = { - 'arg_max': convert_arg_max, - 'assign': convert_assign, - 'batch_norm': convert_batch_norm, - 'cast': convert_cast, - 'concat': convert_concat, - 'conv2d': convert_conv2d, - 'cumsum': convert_cumsum, - 'depthwise_conv2d': convert_conv2d, - '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, - 'feed': convert_feed, - 'fill_any_like': convert_fill_any_like, - 'fill_constant': convert_fill_constant, - 'gelu': convert_gelu, - 'hard_sigmoid': convert_hard_sigmoid, - 'hard_swish': convert_hard_swish, - 'layer_norm': convert_layer_norm, - 'leaky_relu': convert_leaky_relu, - 'lookup_table_v2': convert_lookup_table, - 'matmul': convert_matmul, - 'matmul_v2': convert_matmul, - 'mul': convert_mul, - 'pool2d': convert_pool2d, - 'relu': convert_activation, - 'reshape2': convert_reshape, - 'scale': convert_scale, - 'shape': convert_shape, - 'slice': convert_slice, - 'softmax': convert_softmax, - 'tanh': convert_activation, - 'unsqueeze2': convert_unsqueeze, + "arg_max": convert_arg_max, + "assign": convert_assign, + "batch_norm": convert_batch_norm, + "cast": convert_cast, + "concat": convert_concat, + "conv2d": convert_conv2d, + "cumsum": convert_cumsum, + "depthwise_conv2d": convert_conv2d, + "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, + "feed": convert_feed, + "fill_any_like": convert_fill_any_like, + "fill_constant": convert_fill_constant, + "gelu": convert_gelu, + "hard_sigmoid": convert_hard_sigmoid, + "hard_swish": convert_hard_swish, + "layer_norm": convert_layer_norm, + "leaky_relu": convert_leaky_relu, + "lookup_table_v2": convert_lookup_table, + "matmul": convert_matmul, + "matmul_v2": convert_matmul, + "mul": convert_mul, + "pool2d": convert_pool2d, + "relu": convert_activation, + "reshape2": convert_reshape, + "scale": convert_scale, + "shape": convert_shape, + "slice": convert_slice, + "softmax": convert_softmax, + "tanh": convert_activation, + "unsqueeze2": convert_unsqueeze, } class GraphProto(object): - """ A helper class for handling relay functions from PaddlePaddle model.""" + """A helper class for handling relay functions from PaddlePaddle model.""" + def __init__(self): self.nodes = {} self.params = {} @@ -792,13 +786,13 @@ def get_params(self, name=None): return self.params[name] def extract_parameters(self, program, scope=None): - """ Extract all the weights from PaddlePaddle program.""" + """Extract all the weights from PaddlePaddle program.""" self.params = {} variables = program.global_block().vars for name in variables: var = program.global_block().var(name) - if name.endswith('feed') or name.endswith('fetch'): + if name.endswith("feed") or name.endswith("fetch"): continue if not var.persistable: continue @@ -809,24 +803,24 @@ def extract_parameters(self, program, scope=None): 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.""" + """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)) + warning_msg = "Input {}(shape={}) has unkown dimension shapes. Specifying static values may improve performance".format( + ipt_name, ipt_shape + ) warings.warn(warning_msg) def check_unsupported_ops(self, program): - """ Check whether all the operators are supported.""" + """Check whether all the operators are supported.""" unsupported_ops = set() for block in program.blocks: for i, op in enumerate(block.ops): - if op.type == 'fetch': + if op.type == "fetch": continue if op.type not in _convert_map: unsupported_ops.add(op.type) @@ -836,24 +830,25 @@ def check_unsupported_ops(self, program): raise tvm.error.OpNotImplemented(msg) def ops_to_relay(self, program, input_specs=None): - """ Convert PaddlePaddle operators to TVM relay functions.""" + """Convert PaddlePaddle operators to TVM relay functions.""" if input_specs is not None: for input_spec in input_specs: convert_feed(self, input_spec, None) for block in program.blocks: for i, op in enumerate(block.ops): - if op.type == 'fetch': + 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.""" + """Construct the TVM relay expression from PaddlePaddle program.""" self.shape_dict = shape_dict if scope is None: import paddle + scope = paddle.fluid.global_scope() self.check_unsupported_ops(program) self.extract_parameters(program, scope) @@ -863,7 +858,7 @@ def from_program(self, program, shape_dict, scope): for block in program.blocks: for i, op in enumerate(block.ops): if op.type == "fetch": - output_names.append(op.input('X')[0]) + output_names.append(op.input("X")[0]) outputs = [self.nodes[name] for name in output_names] outputs = outputs[0] if len(outputs) == 1 else _expr.Tuple(outputs) @@ -874,7 +869,7 @@ def from_program(self, program, shape_dict, scope): return mod, self.params def from_translated_layer(self, layer, shape_dict): - """ Construct the TVM relay expression from PaddlePaddle TranslatedLayer.""" + """Construct the TVM relay expression from PaddlePaddle TranslatedLayer.""" self.shape_dict = shape_dict program = layer.program() @@ -899,13 +894,14 @@ 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. + """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() if isinstance(program_or_layer, paddle.fluid.dygraph.TranslatedLayer): # model is loaded by `paddle.jit.load` @@ -914,6 +910,5 @@ def from_paddle(program_or_layer, shape_dict=None, scope=None): # model is loaded by `paddle.static.load_inference_model` mod, params = g.from_program(program_or_layer, shape_dict, scope) else: - raise Exception( - "Only PaddlePaddle's Program and TranslatedLayer are supported.") + raise Exception("Only PaddlePaddle's Program and TranslatedLayer are supported.") return mod, params diff --git a/tests/python/frontend/paddlepaddle/test_forward.py b/tests/python/frontend/paddlepaddle/test_forward.py index 20a110f7d657..db07e07f9d83 100644 --- a/tests/python/frontend/paddlepaddle/test_forward.py +++ b/tests/python/frontend/paddlepaddle/test_forward.py @@ -28,8 +28,7 @@ import paddle import paddle.nn as nn -PADDLE_TEST_DATA_ROOT_PATH = Path( - Path("~").expanduser(), ".tvm_test_data", "paddle") +PADDLE_TEST_DATA_ROOT_PATH = Path(Path("~").expanduser(), ".tvm_test_data", "paddle") PADDLE_TEST_DATA_ROOT_PATH.mkdir(parents=True, exist_ok=True) @@ -61,9 +60,8 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): 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)) + 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): @@ -78,7 +76,7 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): if isinstance(baseline_outputs, (tuple, list)): baseline_outputs = tuple(out.numpy() for out in baseline_outputs) else: - baseline_outputs = (baseline_outputs.numpy(), ) + baseline_outputs = (baseline_outputs.numpy(),) mod, params = relay.frontend.from_paddle(baseline_model, input_shape_dict) parms_num = min(len(input_names), len(mod["main"].params)) @@ -99,10 +97,7 @@ def verify_model(func, input_data, rtol=1e-5, atol=1e-5): compiled_output = gmod.get_output(i).numpy() assert_shapes_match(baseline_output, compiled_output) - tvm.testing.assert_allclose(baseline_output, - compiled_output, - rtol=rtol, - atol=atol) + tvm.testing.assert_allclose(baseline_output, compiled_output, rtol=rtol, atol=atol) @tvm.testing.uses_gpu @@ -168,13 +163,19 @@ def 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, + ], + ) @tvm.testing.uses_gpu @@ -226,28 +227,32 @@ 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 def test_forward_concat_unsqueeze(): @paddle.jit.to_static def concat_unsqueeze1(inputs): - return paddle.concat( - [inputs[:, 0].unsqueeze(1), inputs[:, 1].unsqueeze(1)], axis=1) + return paddle.concat([inputs[:, 0].unsqueeze(1), inputs[:, 1].unsqueeze(1)], axis=1) @paddle.jit.to_static def concat_unsqueeze2(inputs): a = (inputs[:, :, 0] + 2) * 7 b = (inputs[:, :, 1] + 3) * 11 c = (inputs[:, :, 2] + 5) * 13 - return paddle.concat([paddle.unsqueeze(t, axis=2) for t in [a, b, c]], - axis=2) + return paddle.concat([paddle.unsqueeze(t, axis=2) for t in [a, b, c]], axis=2) input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") @@ -272,12 +277,18 @@ def cusum3(inputs): 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( + cusum2, + [ + input_data, + ], + ) + verify_model( + cusum3, + [ + input_data, + ], + ) @tvm.testing.uses_gpu @@ -390,10 +401,7 @@ def hard_swish(inputs): def test_forward_layer_norm(): @paddle.jit.to_static def layer_norm(inputs, weight, bias): - return nn.functional.layer_norm(inputs, - inputs.shape[-1], - weight=weight, - bias=bias) + return nn.functional.layer_norm(inputs, inputs.shape[-1], weight=weight, bias=bias) class LayerNorm(nn.Layer): def __init__(self): @@ -477,7 +485,7 @@ def forward(self, input1, input2): # matrix x vector input_data1 = paddle.randn((3, 4), dtype="float32") - input_data2 = paddle.randn((4, ), dtype="float32") + input_data2 = paddle.randn((4,), dtype="float32") verify_model(MatMul1(), input_data=[input_data1, input_data2]) # matrix x matrix @@ -500,10 +508,7 @@ def forward(self, input1, input2): def test_forward_pool2d(): @paddle.jit.to_static def pool2d1(inputs): - return nn.functional.avg_pool2d(inputs, - kernel_size=2, - stride=2, - padding=0) + return nn.functional.avg_pool2d(inputs, kernel_size=2, stride=2, padding=0) @paddle.jit.to_static def pool2d2(inputs): @@ -511,16 +516,11 @@ def pool2d2(inputs): @paddle.jit.to_static def pool2d3(inputs): - return nn.functional.max_pool2d(inputs, - kernel_size=2, - stride=2, - padding=0, - return_mask=True) - - input_data = paddle.uniform(shape=[1, 2, 32, 32], - dtype='float32', - min=-1, - max=1) + return nn.functional.max_pool2d( + inputs, kernel_size=2, stride=2, padding=0, return_mask=True + ) + + input_data = paddle.uniform(shape=[1, 2, 32, 32], dtype="float32", min=-1, max=1) verify_model(pool2d1, input_data=input_data) verify_model(pool2d2, input_data=input_data) # verify_model(pool2d3, input_data=input_data) @@ -577,10 +577,13 @@ def scale1(inputs): def scale2(inputs): return paddle.scale(inputs, scale=3, bias=2.1, act="gelu") - input_data = paddle.randn(shape=[2, 3], dtype='float32') - verify_model(scale1, input_data=[ - input_data, - ]) + input_data = paddle.randn(shape=[2, 3], dtype="float32") + verify_model( + scale1, + input_data=[ + input_data, + ], + ) verify_model(scale2, input_data=input_data) @@ -606,9 +609,12 @@ def slice4(inputs): input_shape = [1, 3, 10, 10] input_data = paddle.rand(input_shape, dtype="float32") - verify_model(slice1, input_data=[ - input_data, - ]) + 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))) From 202893c453d612fcdf268ca1210d186d34f8ee4c Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Tue, 17 Aug 2021 19:55:30 +0800 Subject: [PATCH 21/23] pylint check --- python/tvm/relay/frontend/paddlepaddle.py | 59 ++++++++++++----------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index c67c8b9c39e0..ab17c3b6daa4 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -17,9 +17,7 @@ # 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 copy import warnings -import six import numpy as np @@ -43,6 +41,8 @@ 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) @@ -51,6 +51,8 @@ def shape_of(x, dtype="int32"): 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: @@ -94,7 +96,6 @@ def convert_batch_norm(g, op, block): mean_name = op.input("Mean")[0] variance_name = op.input("Variance")[0] epsilon = op.attr("epsilon") - momentum = op.attr("momentum") out = _op.nn.batch_norm( g.get_node(ipt_name), g.get_node(scale_name), @@ -127,6 +128,7 @@ def convert_concat(g, op, block): def convert_conv2d(g, op, block): """Operator converter for conv2d.""" + dilations = op.attr("dilations") groups = op.attr("groups") paddings = op.attr("paddings") @@ -134,9 +136,9 @@ def convert_conv2d(g, op, block): strides = op.attr("strides") kernel = g.get_node(op.input("Filter")[0]) - input = g.get_node(op.input("Input")[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)[2:] + in_h, in_w = infer_shape(input_x)[2:] if padding_algorithm == "VALID": paddings = [0, 0] elif padding_algorithm == "SAME": @@ -149,11 +151,11 @@ def convert_conv2d(g, op, block): if len(paddings) == 4: paddings = [paddings[0], paddings[2], paddings[1], paddings[3]] else: - msg = 'Value {} in attribute "padding" of operator Conv is not ' "valid." + msg = 'Value {} in attribute "padding" of operator Conv is not "valid."' raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) out = _op.nn.conv2d( - input, + input_x, kernel, strides=strides, padding=paddings, @@ -312,7 +314,6 @@ def convert_hard_sigmoid(g, op, block): """Operator converter for hard_sigmoid.""" slope = op.attr("slope") - offset = op.attr("offset") x = g.get_node(op.input("X")[0]) out = x * _expr.const(slope) + _expr.const(0.5) out = _op.clip(out, 0, 1) @@ -346,7 +347,7 @@ def convert_layer_norm(g, op, block): x_shape = infer_shape(x) assert ( - begin_norm_axis == -1 or begin_norm_axis == len(x_shape) - 1 + begin_norm_axis in (len(x_shape) - 1, -1) ), "Support only normalization over last one dimension." if bias_input: @@ -379,8 +380,6 @@ def convert_lookup_table(g, op, block): indices = g.get_node(op.input("Ids")[0]) padding_idx = op.attr("padding_idx") - is_sparse = op.attr("is_sparse") - height_sections = op.attr("height_sections") 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]])) @@ -395,11 +394,11 @@ def convert_matmul(g, op, block): inputs = [g.get_node(op.input("X")[0]), g.get_node(op.input("Y")[0])] a_shape = infer_shape(inputs[0]) b_shape = infer_shape(inputs[1]) - try: + if op.has_attr("trans_x"): # for matmul_v2 trans_x = op.attr("trans_x") trans_y = op.attr("trans_y") - except: + else: # for matmul trans_x = op.attr("transpose_X") trans_y = op.attr("transpose_Y") @@ -491,12 +490,10 @@ def flatten_to_nd(x, x_shape, nd=3): out = _op.nn.dense(inputs[0], input_1_t) if b_rank == 1: out = _op.squeeze(out, axis=[-1]) - try: + if op.has_attr("alpha"): alpha = op.attr("alpha") if not np.isclose(alpha, 1.0): out = out * _expr.const(alpha).astype("float32") - except: - pass g.add_node(op.output("Out")[0], out) @@ -559,8 +556,8 @@ def convert_pool2d(g, op, block): adaptive = True ksize = [1, 1] - input = g.get_node(op.input("X")[0]) - in_h, in_w = infer_shape(input)[2:] + input_x = g.get_node(op.input("X")[0]) + in_h, in_w = infer_shape(input_x)[2:] op_map = { "avg": "avg_pool2d", @@ -586,16 +583,15 @@ def convert_pool2d(g, op, block): if len(paddings) == 4: paddings = [paddings[0], paddings[2], paddings[1], paddings[3]] else: - msg = 'Value {} in attribute "padding" of operator Pool2d is not ' "valid." + msg = 'Value {} in attribute "padding" of operator Pool2d is not "valid."' raise tvm.error.OpAttributeInvalid(msg.format(padding_algorithm)) - x = g.get_node(op.input("X")[0]) if not adaptive: out = getattr(_op.nn, op_map[pooling_type])( - x, pool_size=ksize, strides=strides, padding=paddings, ceil_mode=ceil_mode + input_x, pool_size=ksize, strides=strides, padding=paddings, ceil_mode=ceil_mode ) else: - out = getattr(_op.nn, "adaptive_" + op_map[pooling_type])(x, output_size=ksize) + out = getattr(_op.nn, "adaptive_" + op_map[pooling_type])(input_x, output_size=ksize) g.add_node(op.output("Out")[0], out) @@ -764,7 +760,7 @@ def convert_unsqueeze(g, op, block): } -class GraphProto(object): +class GraphProto: """A helper class for handling relay functions from PaddlePaddle model.""" def __init__(self): @@ -773,13 +769,19 @@ def __init__(self): self.shape_dict = None def get_node(self, name): + """get node from graph""" + assert name in self.nodes return self.nodes[name] def add_node(self, name, node): + """add a node to graph""" + self.nodes[name] = fold_constant(node) def get_params(self, name=None): + """get params from graph""" + if name is None: return self.params assert name in self.params @@ -809,17 +811,18 @@ def check_input_shape(self, op, block): 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( + warning_msg = "Input {}(shape={}) has unkown dimension shapes. \ + Specifying static values may improve performance".format( ipt_name, ipt_shape ) - warings.warn(warning_msg) + warnings.warn(warning_msg) def check_unsupported_ops(self, program): """Check whether all the operators are supported.""" unsupported_ops = set() for block in program.blocks: - for i, op in enumerate(block.ops): + for op in block.ops: if op.type == "fetch": continue if op.type not in _convert_map: @@ -836,7 +839,7 @@ def ops_to_relay(self, program, input_specs=None): for input_spec in input_specs: convert_feed(self, input_spec, None) for block in program.blocks: - for i, op in enumerate(block.ops): + for op in block.ops: if op.type == "fetch": continue convert_func = _convert_map[op.type] @@ -856,7 +859,7 @@ def from_program(self, program, shape_dict, scope): output_names = list() for block in program.blocks: - for i, op in enumerate(block.ops): + for op in block.ops: if op.type == "fetch": output_names.append(op.input("X")[0]) From a281d602eca0a8a307e2788bcc388d14c9f94af8 Mon Sep 17 00:00:00 2001 From: jiangjiajun <928090362@qq.com> Date: Wed, 18 Aug 2021 02:27:59 +0000 Subject: [PATCH 22/23] Remove fluid api --- python/tvm/relay/frontend/paddlepaddle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/tvm/relay/frontend/paddlepaddle.py b/python/tvm/relay/frontend/paddlepaddle.py index ab17c3b6daa4..0b59f1d8ea58 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -906,10 +906,10 @@ def from_paddle(program_or_layer, shape_dict=None, scope=None): import paddle g = GraphProto() - if isinstance(program_or_layer, paddle.fluid.dygraph.TranslatedLayer): + 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) - elif isinstance(program_or_layer, paddle.fluid.framework.Program): + elif isinstance(program_or_layer, paddle.static.Program): # model is loaded by `paddle.static.load_inference_model` mod, params = g.from_program(program_or_layer, shape_dict, scope) else: From be0e04ece30672c86b44009b27d1d253ee79e8a5 Mon Sep 17 00:00:00 2001 From: heliqi <1101791222@qq.com> Date: Wed, 18 Aug 2021 10:40:24 +0800 Subject: [PATCH 23/23] black format --- 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 0b59f1d8ea58..76a12691d2bf 100644 --- a/python/tvm/relay/frontend/paddlepaddle.py +++ b/python/tvm/relay/frontend/paddlepaddle.py @@ -346,8 +346,9 @@ def convert_layer_norm(g, op, block): scale_input = op.input("Scale") x_shape = infer_shape(x) - assert ( - begin_norm_axis in (len(x_shape) - 1, -1) + assert begin_norm_axis in ( + len(x_shape) - 1, + -1, ), "Support only normalization over last one dimension." if bias_input: