From c1f65a10945788e1d039fbf1b05ccb3e9735324a Mon Sep 17 00:00:00 2001 From: Channingss Date: Wed, 15 Jul 2020 07:50:30 +0000 Subject: [PATCH] paddle2onnx support opset:9,10,11 --- x2paddle/op_mapper/onnx2paddle/__init__.py | 0 .../op_mapper/onnx2paddle/onnx_op_mapper.py | 92 + .../op_mapper/onnx2paddle/opsets/__init__.py | 0 .../onnx2paddle/opsets/_shape_inference.py | 599 +++++++ .../custom_layer/InstanceNormalization.py | 56 + .../opsets/custom_layer/__init__.py | 115 ++ .../opsets/custom_layer/register.py | 55 + .../op_mapper/onnx2paddle/opsets/opset10.py | 37 + .../op_mapper/onnx2paddle/opsets/opset11.py | 37 + .../op_mapper/onnx2paddle/opsets/opset9.py | 1523 +++++++++++++++++ x2paddle/op_mapper/paddle2onnx/__init__.py | 0 .../op_mapper/paddle2onnx/opset10/__init__.py | 0 .../op_mapper/paddle2onnx/opset10/opset.py | 61 + .../opset10/paddle_custom_layer/__init__.py | 0 .../paddle_custom_layer/im2sequence.py | 80 + .../paddle_custom_layer/multiclass_nms.py | 416 +++++ .../opset10/paddle_custom_layer/yolo_box.py | 822 +++++++++ .../op_mapper/paddle2onnx/opset11/__init__.py | 0 .../op_mapper/paddle2onnx/opset11/opset.py | 249 +++ .../opset11/paddle_custom_layer/__init__.py | 0 .../paddle_custom_layer/im2sequence.py | 80 + .../paddle_custom_layer/multiclass_nms.py | 416 +++++ .../opset11/paddle_custom_layer/yolo_box.py | 841 +++++++++ .../op_mapper/paddle2onnx/opset9/__init__.py | 0 .../op_mapper/paddle2onnx/opset9/opset.py | 746 ++++++++ .../opset9/paddle_custom_layer/__init__.py | 0 .../opset9/paddle_custom_layer/im2sequence.py | 80 + .../paddle_custom_layer/multiclass_nms.py | 416 +++++ .../opset9/paddle_custom_layer/yolo_box.py | 822 +++++++++ .../op_mapper/paddle2onnx/paddle_op_mapper.py | 108 ++ 30 files changed, 7651 insertions(+) create mode 100644 x2paddle/op_mapper/onnx2paddle/__init__.py create mode 100644 x2paddle/op_mapper/onnx2paddle/onnx_op_mapper.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/__init__.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/_shape_inference.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/InstanceNormalization.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/__init__.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/register.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/opset10.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/opset11.py create mode 100644 x2paddle/op_mapper/onnx2paddle/opsets/opset9.py create mode 100644 x2paddle/op_mapper/paddle2onnx/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/opset.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/im2sequence.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/multiclass_nms.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/yolo_box.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/opset.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/im2sequence.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/multiclass_nms.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/yolo_box.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/opset.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/__init__.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/im2sequence.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/multiclass_nms.py create mode 100644 x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/yolo_box.py create mode 100644 x2paddle/op_mapper/paddle2onnx/paddle_op_mapper.py diff --git a/x2paddle/op_mapper/onnx2paddle/__init__.py b/x2paddle/op_mapper/onnx2paddle/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/onnx2paddle/onnx_op_mapper.py b/x2paddle/op_mapper/onnx2paddle/onnx_op_mapper.py new file mode 100644 index 0000000..292ab9d --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/onnx_op_mapper.py @@ -0,0 +1,92 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from x2paddle.op_mapper.onnx2paddle.opsets.opset9 import OpSet9 +from x2paddle.core.op_mapper import OpMapper +from x2paddle.op_mapper.onnx_opsets.custom_layer import * +from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode + + +class ONNXOpMapper(OpMapper): + def __init__(self, decoder): + super(ONNXOpMapper, self).__init__() + self.support_op_sets = [9, ] + self.default_op_set = 9 + self.graph = decoder.graph + self.opset = self.create_opset(decoder) + if not self.op_checker(): + raise Exception("Model are not supported yet.") + #mapping op + print("Total nodes: {}".format( + sum([ + isinstance(node, ONNXGraphNode) + for name, node in self.graph.node_map.items() + ]))) + + print("Nodes converting ...") + for node_name in self.graph.topo_sort: + node = self.graph.get_node(node_name) + op = node.layer_type + if hasattr(self.opset, op): + func = getattr(self.opset, op) + func(node) + elif op in self.opset.default_op_mapping: + self.opset.directly_map(node) + elif op in custom_layers: + self.opset.deal_custom_layer(node) + elif op in self.opset.elementwise_ops: + self.opset.elementwise_map(node) + print("Nodes converted.") + self.weights = self.opset.weights + self.omit_nodes = self.opset.omit_nodes + self.used_custom_layers = self.opset.used_custom_layers + + def op_checker(self): + unsupported_ops = set() + for node_name in self.graph.topo_sort: + node = self.graph.get_node(node_name) + op = node.layer_type + if not hasattr(self.opset, op) and \ + op not in self.opset.default_op_mapping and \ + op not in custom_layers and \ + op not in self.opset.elementwise_ops: + unsupported_ops.add(op) + if len(unsupported_ops) == 0: + return True + else: + print("There are {} ops not supported yet, list as below".format( + len(unsupported_ops))) + for op in unsupported_ops: + print(op) + return False + + def create_opset(self, decoder): + run_op_set = self.default_op_set + opset = '' + if decoder.op_set in self.support_op_sets: + opset = 'OpSet' + str(decoder.op_set) + elif decoder.op_set < self.default_op_set: + opset = 'OpSet' + str(self.default_op_set) + else: + for op_set in self.support_op_sets: + if decoder.op_set > op_set: + run_op_set = op_set + else: + break + opset = 'OpSet' + str(run_op_set) + print( + 'Now, onnx2paddle support convert onnx model opset_verison {},' + 'opset_verison of your onnx model is {}, automatically treated as op_set: {}.' + .format(self.support_op_sets, decoder.op_set, run_op_set)) + return eval(opset)(decoder) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/__init__.py b/x2paddle/op_mapper/onnx2paddle/opsets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/_shape_inference.py b/x2paddle/op_mapper/onnx2paddle/opsets/_shape_inference.py new file mode 100644 index 0000000..db5aca3 --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/_shape_inference.py @@ -0,0 +1,599 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode +import numpy as np +import sympy + + +def handle_negative_axis(axis, rank): + return axis if axis >= 0 else axis + rank + + +class ShapeInference(): + def __init__(self, decoder, auto_merge=False): + self.decoder = decoder + self.fluid_data = {} + self.suggested_merge_ = {} + self.symbolic_dims_ = {} + self.auto_merge_ = auto_merge + self.dispatcher = { + # activation ops + 'Relu': self.activation_ops, + 'LeakyRelu': self.activation_ops, + 'Elu': self.activation_ops, + 'ThresholdRelu': self.activation_ops, + 'Prelu': self.activation_ops, + 'Tanh': self.activation_ops, + 'Sigmoid': self.activation_ops, + 'Softplus': self.activation_ops, + 'Softsign': self.activation_ops, + 'HardSigmoid': self.activation_ops, + 'Shrink': self.activation_ops, + 'Exp': self.activation_ops, + 'Clip': self.activation_ops, + + # elementwise ops + 'Add': self.elementwise_ops, + 'Div': self.elementwise_ops, + 'Sub': self.elementwise_ops, + 'Mul': self.elementwise_ops, + 'Pow': self.elementwise_ops, + 'Sqrt': self.elementwise_ops, + 'Softmax': self.elementwise_ops, + 'Constant': self.constant, + 'AveragePool': self.pool, + 'MaxPool': self.pool, + 'Cast': self.cast, + 'Conv': self.conv, + 'BatchNormalization': self.batch_norm, + 'Pad': self.pad, + 'Gather': self.gather, + 'Split': self.split, + 'Transpose': self.transpose, + 'Reshape': self.reshape, + 'MatMul': self.matmul, + 'Squeeze': self.squeeze, + 'Unsqueeze': self.unsqueeze, + 'Concat': self.concat, + } + self.run_ = True + self.suggested_merge_ = {} + self.symbolic_dims_ = {} + self.input_symbols_ = {} + + def __call__(self): + """ + run shape inference + """ + nodes = self.decoder.model.graph.node + node_map = self.decoder.onnx_graph.node_map + value_infos = self.decoder.onnx_graph.value_infos + onnx_model = self.decoder.model + #self._apply_suggested_merge(graph_input_only=True) + for layer in nodes: + node = node_map[layer.name] + for opt in layer.output: + if opt in value_infos: + value_info = value_infos[opt] + #if len(value_info['shape']) == 0 or value_info[ + # 'dtype'] is None or 0 in value_info['shape']: + # #TODO add node shape inference + # if self.is_support_inference(node): + # op_infer = self.dispatcher[node.layer_type] + # #shapes = op_infer(node) + # print(node.layer_name + ': ') + # print(node.layer_type + ': ') + #else: + # print(node.layer_name) + node.dtype = value_info['dtype'] + node.out_shapes.append(value_info['shape']) + else: + #TODO add node shape inference + if self.is_support_inference(node): + op_infer = self.dispatcher[node.layer_type] + #shapes = op_infer(node) + #print(node.layer_name + ': ') + #print(node.layer_type + ': ') + + def get_input_node(self, node, idx, copy=False): + return self.decoder.onnx_graph.get_input_node(node, idx=idx, copy=copy) + + def get_fluid_data(self, node, return_ndarray=False): + data = None + if node.layer_name in self.fluid_data: + data = self.fluid_data[node.layer_name] + elif isinstance(node, ONNXGraphDataNode): + data = node.weight + elif isinstance(node, ONNXGraphNode): + data = node.value + if return_ndarray: + return data + else: + return data.tolist() + + def is_support_inference(self, node): + if node.layer_type not in self.dispatcher: + print( + "[WARNNING] Shape inference not support Node[{}](op type: {}) ". + format(node.layer_name, node.layer_type)) + return False + return True + + def _try_get_value(self, node, idx): + if idx >= len(node.inputs): + return None + return self.get_input_node(node, idx=idx, return_ndarray=True) + + def _get_int_values(self, node, broadcast=False): + values = [self._try_get_value(node, i) for i in range(len(node.input))] + if all([v is not None for v in values]): + # some shape compute is in floating point, cast to int for sympy + for i, v in enumerate(values): + if type(v) != np.ndarray: + continue + if len(v.shape) > 1: + new_v = None # ignore value for rank > 1 + elif len(v.shape) == 0: + new_v = int(np.asscalar(v)) + else: + assert len(v.shape) == 1 + new_v = [int(vv) for vv in v] + values[i] = new_v + values_len = [len(v) if type(v) == list else 0 for v in values] + max_len = max(values_len) + if max_len >= 1 and broadcast: + # broadcast + for i, v in enumerate(values): + if v is None: + continue # don't broadcast if value is unknown + if type(v) == list: + if len(v) < max_len: + values[i] = v * max_len + else: + assert len(v) == max_len + else: + values[i] = [v] * max_len + return values + + def _compute_on_sympy_data(self, node, op_func): + assert len(node.outputs) == 1 + values = self._get_int_values(node, broadcast=True) + if all([v is not None for v in values]): + is_list = [type(v) == list for v in values] + as_list = any(is_list) + if as_list: + data = [op_func(vs) for vs in zip(*values)] + self.fluid_data[node.layer_name] = data + node.out_shapes.append(data.shape) + print('*' * 10, data) + else: + data = op_func(values) + self.fluid_data[node.layer_name] = data + print('*' * 10, data) + node.out_shapes.append(data.shape) + + def _pass_on_sympy_data(self, node): + assert len(node.inputs) == 1 or node.layer_type == 'Reshape' + self._compute_on_sympy_data(node, lambda x: x[0]) + + def _get_sympy_shape(self, node, idx): + sympy_shape = [] + for d in self._get_shape(node, idx): + if type(d) == str: + sympy_shape.append(self.symbolic_dims_[d] if d in + self.symbolic_dims_ else sympy.Symbol( + d, integer=True)) + else: + assert None != d + sympy_shape.append(d) + return sympy_shape + + def _check_merged_dims(self, dims, allow_broadcast=True): + if allow_broadcast: + dims = [d for d in dims if not (is_literal(d) and int(d) <= 1)] + if not all([d == dims[0] for d in dims]): + self._add_suggested_merge(dims, apply=True) + + def check_specific_shape(self, input_node, output_node, shape): + if -1 in input_node.out_shapes[0]: + assert "Shape inference failed, when calculate output_node[{}]'s \ + shape need specific shape, but got input_node[{}]'s shape: {}".format( + output_node.layer_name, input_node.layer_name, + input_node.out_shapes[0]) + + def _add_suggested_merge(self, symbols, apply=False): + assert all([(type(s) == str and s in self.symbolic_dims_) or + is_literal(s) for s in symbols]) + symbols = set(symbols) + for k, v in self.suggested_merge_.items(): + if k in symbols: + symbols.remove(k) + symbols.add(v) + map_to = None + # if there is literal, map to it first + for s in symbols: + if is_literal(s): + map_to = s + break + # when no literals, map to input symbolic dims, then existing symbolic dims + if map_to is None: + for s in symbols: + if s in self.input_symbols_: + map_to = s + break + if map_to is None: + for s in symbols: + if type(self.symbolic_dims_[s]) == sympy.Symbol: + map_to = s + break + # when nothing to map to, use the shorter one + if map_to is None: + if self.verbose_ > 0: + print( + 'Potential unsafe merge between symbolic expressions: ({})'. + format(','.join(symbols))) + symbols_list = list(symbols) + lens = [len(s) for s in symbols_list] + map_to = symbols_list[lens.index(min(lens))] + symbols.remove(map_to) + + def _merge_symbols(self, dims): + if not all([type(d) == str for d in dims]): + if self.auto_merge_: + assert len( + dims + ) == 2 # only allow symbol->int merge in binary ops for now + is_int = [is_literal(d) for d in dims] + if sum(is_int) == 1: + int_dim = is_int.index(1) + if self.verbose_ > 0: + print('dim {} has been merged with value {}'.format( + dims[1 - int_dim], dims[int_dim])) + self._check_merged_dims(dims, allow_broadcast=False) + return dims[int_dim] + else: + if self.verbose_ > 0: + print('dim {} has been mergd with dim {}'.format(dims[ + 0], dims[1])) + return dims[0] + else: + return None + if all([d == dims[0] for d in dims]): + return dims[0] + merged = [ + self.suggested_merge_[d] if d in self.suggested_merge_ else d + for d in dims + ] + if all([d == merged[0] for d in merged]): + assert merged[0] in self.symbolic_dims_ + return merged[0] + else: + return None + + # broadcast from right to left, and merge symbolic dims if needed + def _broadcast_shapes(self, shape1, shape2): + new_shape = [] + rank1 = len(shape1) + rank2 = len(shape2) + new_rank = max(rank1, rank2) + for i in range(new_rank): + dim1 = shape1[rank1 - 1 - i] if i < rank1 else 1 + dim2 = shape2[rank2 - 1 - i] if i < rank2 else 1 + if dim1 == 1 or dim1 == dim2: + new_dim = dim2 + elif dim2 == 1: + new_dim = dim1 + else: + new_dim = self._merge_symbols([dim1, dim2]) + if not new_dim: + # warning about unsupported broadcast when not auto merge + # note that auto merge has the risk of incorrectly merge symbols while one of them being 1 + # for example, 'a' = 1, 'b' = 5 at runtime is valid broadcasting, but with auto merge 'a' == 'b' + if self.auto_merge_: + self._add_suggested_merge([dim1, dim2], apply=True) + else: + print('unsupported broadcast between ' + str(dim1) + ' ' + + str(dim2)) + new_shape = [new_dim] + new_shape + return new_shape + + def _apply_suggested_merge(self, graph_input_only=False): + if not self.suggested_merge_: + return + for i in list(self.decoder.model.graph.input) + ( + [] if graph_input_only else + list(self.decoder.model.graph.value_info)): + for d in i.type.tensor_type.shape.dim: + if d.dim_param in self.suggested_merge_: + v = self.suggested_merge_[d.dim_param] + if is_literal(v): + d.dim_value = int(v) + else: + d.dim_param = v + + def _add_suggested_merge(self, symbols, apply=False): + assert all([(type(s) == str and s in self.symbolic_dims_) or + is_literal(s) for s in symbols]) + symbols = set(symbols) + for k, v in self.suggested_merge_.items(): + if k in symbols: + symbols.remove(k) + symbols.add(v) + map_to = None + # if there is literal, map to it first + for s in symbols: + if is_literal(s): + map_to = s + break + # when no literals, map to input symbolic dims, then existing symbolic dims + if map_to is None: + for s in symbols: + if s in self.input_symbols_: + map_to = s + break + if map_to is None: + for s in symbols: + if type(self.symbolic_dims_[s]) == sympy.Symbol: + map_to = s + break + # when nothing to map to, use the shorter one + if map_to is None: + if self.verbose_ > 0: + print( + 'Potential unsafe merge between symbolic expressions: ({})'. + format(','.join(symbols))) + symbols_list = list(symbols) + lens = [len(s) for s in symbols_list] + map_to = symbols_list[lens.index(min(lens))] + symbols.remove(map_to) + + for s in symbols: + if s == map_to: + continue + if is_literal(map_to) and is_literal(s): + assert int(map_to) == int(s) + self.suggested_merge_[s] = int(map_to) if is_literal( + map_to) else map_to + for k, v in self.suggested_merge_.items(): + if v == s: + self.suggested_merge_[k] = map_to + if apply and self.auto_merge_: + self._apply_suggested_merge() + + def pool_conv_ops(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + if len(node.inputs) > 1: + W_shape = self.get_input_node(node, idx=1).out_shapes[0] + rank = len(W_shape) - 2 # number of spatial axes + kernel_shape = W_shape[-rank:] + sympy_shape[1] = W_shape[0] + else: + W_shape = None + kernel_shape = node.get_attr('kernel_shape') + rank = len(kernel_shape) + dilations = node.get_attr('dilations', [1] * rank) + strides = node.get_attr('strides', [1] * rank) + pads = node.get_attr('pads') + effective_kernel_shape = [(k - 1) * d + 1 + for k, d in zip(kernel_shape, dilations)] + if pads is None: + pads = [0] * (2 * rank) + auto_pad = node.get_attr('auto_pad', b'NOTSET').decode('utf-8') + if auto_pad != 'VALID' and auto_pad != 'NOTSET': + try: + residual = [ + sympy.Mod(d, s) + for d, s in zip(fluid_shape[-rank:], strides) + ] + total_pads = [ + max(0, (k - s) if r == 0 else (k - r)) + for k, s, r in zip(effective_kernel_shape, strides, + residual) + ] + except TypeError: # sympy may throw TypeError: cannot determine truth value of Relational + total_pads = [ + max(0, (k - s)) + for k, s in zip(effective_kernel_shape, strides) + ] # assuming no residual if sympy throws error + elif auto_pad == 'VALID': + total_pads = [] + else: + total_pads = [0] * rank + else: + assert len(pads) == 2 * rank + total_pads = [p1 + p2 for p1, p2 in zip(pads[:rank], pads[rank:])] + ceil_mode = node.get_attr('ceil_mode', 0) + for i in range(rank): + effective_input_size = fluid_shape[-rank + i] + if len(total_pads) > 0: + effective_input_size = effective_input_size + total_pads[i] + if ceil_mode: + strided_kernel_positions = sympy.ceiling( + (effective_input_size - effective_kernel_shape[i]) / + strides[i]) + else: + strided_kernel_positions = ( + effective_input_size - effective_kernel_shape[i] + ) // strides[i] + fluid_shape[-rank + i] = strided_kernel_positions + 1 + node.out_shapes.append(fluid_shape) + return fluid_shape + + def cast(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shape[0] + node.out_shapes.append(fluid_shape) + return fluid_shape + + def pool(self, node): + return self.conv_pool_ops(node) + + def conv(self, node): + return self.conv_pool_ops(node) + + def batch_norm(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + node.out_shapes.append(fluid_shape) + return fluid_shape + + def activation_ops(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + node.out_shapes.append(fluid_shape) + return fluid_shape + + def elementwise_ops(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + node.out_shapes.append(fluid_shape) + return fluid_shape + + def pad(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + # op_set <= 10 + pads = node.get_attr('pads') + + rank = len(fluid_shape) + fluid_shape = [ + d + pad_up + pad_down + for d, pad_up, pad_down in zip(fluid_shape, pads[:rank], pads[ + rank:]) + ] + node.out_shapes.append(fluid_shape) + return fluid_shape + + def gather(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + axis = handle_negative_axis(node.get_attr('axis', 0), len(fluid_shape)) + indices_shape = self.get_input_node(node, idx=1).out_shapes[0] + fluid_shape = fluid_shape[:axis] + list(indices_shape) + fluid_shape[ + axis + 1:] + input = self.get_input_node(node, 0) + if input.layer_name in self.fluid_data: + assert 0 == axis # only handle 1D sympy compute + idx = self.get_fluid_date(indices_shape) + data = self.fluid_data[input.layer_name] + if type(data) == list: + if type(idx) == np.ndarray and len(idx.shape) == 1: + self.fluid_data[ + node.layer_name] = [data[int(i)] for i in idx] + else: + self.fluid_data[node.layer_name] = data[int(idx)] + else: + assert idx == 0 + self.fluid_data[node.layer_name] = data + + node.out_shapes.append(fluid_shape) + return fluid_shape + + def constant(self, node): + if isinstance(node, ONNXGraphNode): + fluid_shape = node.value.shape + else: + fluid_shape = node.weight.shape + + node.out_shapes.append(fluid_shape) + return fluid_shape + + def split(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + axis = handle_negative_axis(node.get_attr('axis', 0), len(fluid_shape)) + split = node.get_attr('split') + + if not split: + num_outputs = len(node.outputs) + split = [fluid_shape[axis] / + sympy.Integer(num_outputs)] * num_outputs + else: + split = [sympy.Integer(s) for s in split] + shapes = [] + for i_o in range(len(split)): + shape = fluid_shape[:axis] + [split[i_o]] + fluid_shape[axis + 1:] + shapes.append(shape) + node.out_shapes += shapes + + return shapes + + def shape(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + fluid_shape = [len(fluid_shape), ] + node.out_shapes.append(fluid_shape) + self.fluid_data[node.layer_name] = np.array(fluid_shape) + return fluid_shape + + def transpose(self, node): + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + perm = node.get_attr('perm') + fulid_shape = np.array(fluid_shape)[perm].tolist() + node.out_shapes.append(fluid_shape) + return fluid_shape + + def reshape(self, node): + shape = self.get_input_node(node, idx=1) + shape_data = self.get_fluid_data(shape) + if shape_data is not None: + if -1 in shape_data: + fluid_shape = self.get_input_node(node, idx=0).out_shapes[0] + print(fluid_shape) + index = shape_data.index(-1) + total_elements = 1 + for dim in fluid_shape: + total_elements *= dim + part_elements = 1 + for dim in shape_data: + if dim != -1: + part_elements *= dim + shape_data[index] = total_elements // part_elements + node.out_shapes.append(shape_data) + else: + pass + return shape_data + + def matmul(self, node): + x_shape = self.get_input_node(node, idx=0).out_shapes[0] + y_shape = self.get_input_node(node, idx=1).out_shapes[0] + x_rank = len(x_shape) + y_rank = len(y_shape) + if x_rank == 1 and y_rank == 1: + new_shape = [] + elif x_rank == 1: + y_reduce_dim = -2 + new_shape = x_shape[:y_reduce_dim] + [x_shape[-1]] + elif y_rank == 1: + x_reduce_dim = -1 + new_shape = x_shape[:x_reduce_dim] + else: + x_reduce_dim = -1 + y_reduce_dim = -2 + new_shape = self._broadcast_shapes( + x_shape[:-2], y_shape[:-2]) + [x_shape[-2]] + [y_shape[-1]] + node.out_shapes.append(new_shape) + return new_shape + + def squeeze(self, node): + self._pass_on_sympy_data(node) + + def unsqueeze(self, node): + self._pass_on_sympy_data(node) + + def concat(self, node): + if any([i in self.fluid_data for i in node.inputs]): + values = self._get_int_values(node) + if all([v is not None for v in values]): + assert 0 == get_attribute(node, 'axis') + self.fluid_data[node.layer_name] = [] + for i in range(len(node.input)): + value = values[i] + if type(value) == list: + self.fluid_data[node.layer_name].extend(value) + else: + self.fluid_data[node.layer_name].append(value) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/InstanceNormalization.py b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/InstanceNormalization.py new file mode 100644 index 0000000..f472732 --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/InstanceNormalization.py @@ -0,0 +1,56 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from .register import register + + +def InstanceNormalization_shape(input_shape): + return input_shape + + +def InstanceNormalization_layer(inputs, name=None): + # TODO(lvmengsi@baidu.com): Check the accuracy when using fluid.layers.layer_norm. + epsilon = 1e-5 + input_ = inputs[0] + mean = fluid.layers.reduce_mean(input_, dim=[2, 3], keep_dim=True) + var = fluid.layers.reduce_mean( + fluid.layers.square(input_ - mean), dim=[2, 3], keep_dim=True) + if name is not None: + scale_name = name + "_scale" + offset_name = name + "_offset" + + scale_param = inputs[1] + offset_param = inputs[2] + scale = fluid.layers.create_parameter( + name=scale_param.name, shape=input_.shape[1:2], dtype="float32") + offset = fluid.layers.create_parameter( + name=offset_param.name, shape=input_.shape[1:2], dtype="float32") + + tmp = fluid.layers.elementwise_mul(x=(input_ - mean), y=scale, axis=1) + tmp = tmp / fluid.layers.sqrt(var + epsilon) + tmp = fluid.layers.elementwise_add(tmp, offset, axis=1) + return tmp + + +def InstanceNormalization_weights(name, data=None): + weights_name = [name + '_scale'] + return weights_name + + +register( + kind='InstanceNormalization', + shape=InstanceNormalization_shape, + layer=InstanceNormalization_layer, + child_func=None, + weights=InstanceNormalization_weights) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/__init__.py b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/__init__.py new file mode 100644 index 0000000..3c07435 --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/__init__.py @@ -0,0 +1,115 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from .register import get_registered_layers +#custom layer import begins + +from . import InstanceNormalization +#custom layer import ends + +custom_layers = get_registered_layers() + + +def set_args(f, params): + """ set args for function 'f' using the parameters in node.layer.param + Args: + f (function): a python function object + params (object): a object contains attributes needed by f's arguments + Returns: + arg_names (list): a list of argument names + kwargs (dict): a dict contains needed arguments + """ + argc = f.__code__.co_argcount + arg_list = f.__code__.co_varnames[0:argc] + kwargs = {} + for arg_name in arg_list: + if hasattr(params, arg_name) and params is not None: + kwargs[arg_name] = getattr(params, arg_name) + return arg_list, kwargs + + +def has_layer(layer_type): + """ test whether this layer exists in custom layer + """ + return layer_type in custom_layers + + +def get_params(layer, layer_type): + import re + if layer_type.lower() == "deconvolution" or layer_type.lower( + ) == "convolutiondepthwise": + param_name = '_'.join(('convolution', 'param')) + elif layer_type.lower() == "normalize": + param_name = '_'.join(('norm', 'param')) + elif len(layer_type) - len(re.sub("[A-Z]", "", layer_type)) >= 2: + s = '' + tmp_name = '' + for i, ch in enumerate(layer_type): + if i == 0: + s += ch.lower() + continue + elif ch.isupper() and layer_type[i - 1].islower(): + tmp_name += (s + '_') + s = '' + s += ch.lower() + tmp_name += s + param_name = '_'.join((tmp_name, 'param')) + else: + param_name = '_'.join((layer_type.lower(), 'param')) + return getattr(layer, param_name, None) + + +def compute_output_shape(node): + """ compute the output shape of custom layer + """ + layer_type = node.layer_type + assert layer_type in custom_layers, "layer[%s] not exist in custom layers" % ( + layer_type) + shape_func = custom_layers[layer_type]['shape'] + layer = node.layer + params = get_params(layer, layer_type) + arg_names, kwargs = set_args(shape_func, params) + input_shape = node.input_shape + return shape_func(input_shape, **kwargs) + + +def make_custom_layer(node): + """ get the code which implement the custom layer function + """ + layer_type = node.layer_type + assert layer_type in custom_layers, "layer[%s] not exist in custom layers" % ( + layer_type) + layer_func = custom_layers[layer_type]['layer'] + import inspect + return inspect.getsource(layer_func), layer_func + + +def make_custom_child_func(node): + """ get the code which implement the custom layer function + """ + layer_type = node.layer_type + child_func = custom_layers[layer_type]['child_func'] + if child_func is None: + return None, child_func + import inspect + return inspect.getsource(child_func), child_func + + +def deal_weights(node, data=None): + """ deal the weights of the custom layer + """ + layer_type = node.layer_type + weights_func = custom_layers[layer_type]['weights'] + name = node.layer_name + return weights_func(name, data) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/register.py b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/register.py new file mode 100644 index 0000000..37a5936 --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/custom_layer/register.py @@ -0,0 +1,55 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. +""" this module provides 'register' for registering customized layers +""" + +g_custom_layers = {} + + +def register(kind, shape, layer, child_func, weights): + """ register a custom layer or a list of custom layers + + Args: + @kind (str or list): type name of the layer + @shape (function): a function to generate the shape of layer's output + @layer (function): a function to generate the paddle code of layer + @weights (function): a function to deal with weights data + + Returns: + None + """ + assert type(shape).__name__ == 'function', 'shape should be a function' + assert type(layer).__name__ == 'function', 'layer should be a function' + + if type(kind) is str: + kind = [kind] + else: + assert type( + kind) is list, 'invalid param "kind" for register, not a list or str' + + for k in kind: + assert type( + k) is str, 'invalid param "kind" for register, not a list of str' + assert k not in g_custom_layers, 'this type[%s] has already been registered' % ( + k) + g_custom_layers[k] = { + 'shape': shape, + 'layer': layer, + 'child_func': child_func, + 'weights': weights + } + + +def get_registered_layers(): + return g_custom_layers diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/opset10.py b/x2paddle/op_mapper/onnx2paddle/opsets/opset10.py new file mode 100644 index 0000000..9455aad --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/opset10.py @@ -0,0 +1,37 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from x2paddle.core.graph import GraphNode +from x2paddle.core.op_mapper import OpMapper +from x2paddle.core.fluid_code import Layer +from x2paddle.core.fluid_code import FluidCode +from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode +from x2paddle.op_mapper.onnx.custom_layer import * +from x2paddle.op_mapper.onnx.opset9 import ONNXOpMapperOpSet9 +from x2paddle.core.util import string +import numpy as np +import onnx +import onnx.numpy_helper as numpy_helper +from onnx.mapping import TENSOR_TYPE_TO_NP_TYPE +import logging as _logging +from collections import OrderedDict +import math +import os +import shutil +from functools import reduce + + +class ONNXOpMapperOpSet10(ONNXOpMapperOpSet9): + def __init__(self, decoder): + super(ONNXOpMapperOpSet10, self).__init__(decoder) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/opset11.py b/x2paddle/op_mapper/onnx2paddle/opsets/opset11.py new file mode 100644 index 0000000..3db1bad --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/opset11.py @@ -0,0 +1,37 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from x2paddle.core.graph import GraphNode +from x2paddle.core.op_mapper import OpMapper +from x2paddle.core.fluid_code import Layer +from x2paddle.core.fluid_code import FluidCode +from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode +from x2paddle.op_mapper.onnx.custom_layer import * +from x2paddle.op_mapper.onnx.opset10 import ONNXOpMapperOpSet10 +from x2paddle.core.util import string +import numpy as np +import onnx +import onnx.numpy_helper as numpy_helper +from onnx.mapping import TENSOR_TYPE_TO_NP_TYPE +import logging as _logging +from collections import OrderedDict +import math +import os +import shutil +from functools import reduce + + +class ONNXOpMapperOpSet11(ONNXOpMapperOpSet10): + def __init__(self, decoder): + super(ONNXOpMapperOpSet11, self).__init__(decoder) diff --git a/x2paddle/op_mapper/onnx2paddle/opsets/opset9.py b/x2paddle/op_mapper/onnx2paddle/opsets/opset9.py new file mode 100644 index 0000000..5cf058f --- /dev/null +++ b/x2paddle/op_mapper/onnx2paddle/opsets/opset9.py @@ -0,0 +1,1523 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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. + +from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode +from x2paddle.core.graph import GraphNode +from x2paddle.core.fluid_code import Layer +from x2paddle.core.fluid_code import FluidCode +from x2paddle.core.util import string +from functools import reduce +import numpy as np +import onnx +import onnx.numpy_helper as numpy_helper +from onnx.mapping import TENSOR_TYPE_TO_NP_TYPE +import logging as _logging +from collections import OrderedDict +import math +import os +import shutil + +_logger = _logging.getLogger(__name__) + + +def _const_weight_or_none(node): + if 'Constant' in node.layer_type: + return node.value + if isinstance(node, ONNXGraphDataNode): + return node.weight + return None + + +def get_same_padding(in_size, kernel_size, stride): + new_size = int(math.ceil(in_size * 1.0 / stride)) + pad_size = (new_size - 1) * stride + kernel_size - in_size + pad0 = int(pad_size / 2) + pad1 = pad_size - pad0 + return [pad0, pad1] + + +def print_mapping_info(func): + def run_mapping(*args, **kwargs): + node = args[1] + try: + res = func(*args, **kwargs) + except: + print("convert failed node:{}, op_type is {}".format( + node.layer_name[9:], node.layer_type)) + raise + else: + #print("convert successfully node:{}, op_type is {}".format( + # node.layer_name[9:], node.layer_type)) + return res + + return run_mapping + + +class OpSet9(): + elementwise_ops = { + 'Add': 'elementwise_add', + 'Div': 'elementwise_div', + 'Sub': 'elementwise_sub', + 'Mul': 'elementwise_mul', + 'Pow': 'elementwise_pow', + } + + default_op_mapping_field_values = OrderedDict() + default_op_mapping_field_values['FLUID_OP'] = '' + default_op_mapping_field_values['FLUID_INPUT_ARGS'] = None + default_op_mapping_field_values['FLUID_OUTPUT_ARGS'] = None + default_op_mapping_field_values['ATTR_MAPPING'] = dict() + default_op_mapping_field_values['DEFAULTS'] = dict() + default_op_mapping_field_values['INPUT_PERM'] = None + default_op_mapping_field_values['OUTPUT_PERM'] = None + default_op_mapping_field_values['FILL_NAME_FIELD'] = True + + default_op_mapping = { + 'Shape': ['shape', ['X'], ['Out']], + 'Clip': [ + 'clip', ['X'], ['Out'], dict(), dict( + min=(np.asarray( + [255, 255, 127, 255], dtype=np.uint8).view(np.float32)[0]), + max=(np.asarray( + [255, 255, 127, 127], dtype=np.uint8).view(np.float32)[0]), + ) + ], + 'Erf': ['erf', ['X'], ['Out']], + 'Ceil': ['ceil', ['X'], ['Out']], + 'ReduceMean': [ + 'reduce_mean', ['X'], ['Out'], dict( + axes='dim', keepdims='keep_dim'), dict(keep_dim=1) + ], + 'ReduceSum': [ + 'reduce_sum', ['X'], ['Out'], dict( + axes='dim', keepdims='keep_dim'), dict(keep_dim=1) + ], + 'ReduceMin': [ + 'reduce_min', ['X'], ['Out'], dict( + axes='dim', keepdims='keep_dim'), dict(keep_dim=1) + ], + #active function + 'Relu': ['relu', ['X'], ['Out']], + 'LeakyRelu': ['leaky_relu', ['X'], ['Out'], dict(), dict(alpha=.01)], + 'Elu': ['elu', ['X'], ['Out'], dict(), dict(alpha=1.)], + 'ThresholdedRelu': [ + 'thresholded_relu', ['X'], ['Out'], dict(alpha='threshold'), + dict(alpha=1.) + ], + 'Tanh': ['tanh', ['X'], ['Out']], + 'Sigmoid': ['sigmoid', ['X'], ['Out']], + 'HardSigmoid': [ + 'hard_sigmoid', ['X'], ['Out'], dict( + alpha='slope', beta='offset'), dict( + slope=.2, offset=.5) + ], + 'Softsign': ['softsign', ['X'], ['Out']], + 'Softplus': ['softplus', ['X'], ['Out']], + 'Exp': ['exp', ['X'], ['Out']], + 'Softmax': ['softmax', ['X'], ['Out'], dict(), dict(axis=1)], + 'Sqrt': ['sqrt', ['X'], ['Out']], + 'Floor': ['floor', ['X'], ['Out']], + 'Abs': ['abs', ['X'], ['Out']], + } + + default_ioa_constraint = { + 'Gather': + [(lambda i, o, a: a.get('axis', 0) == 0, 'only axis = 0 is supported')], + } + + def __init__(self, decoder): + super(OpSet9, self).__init__() + self.graph = decoder.graph + self.input_shapes = [] + self.weights = dict() + self.omit_nodes = list() + self.used_custom_layers = dict() + + @print_mapping_info + def directly_map(self, node, name='', *args, **kwargs): + inputs = node.layer.input + outputs = node.layer.output + op_type = node.layer_type + attrs = node.attr_map + info = self.default_op_mapping[op_type] + info.extend( + list(self.default_op_mapping_field_values.values())[len(info):]) + ( + fluid_op, + fluid_input_args, + fluid_output_args, + attr_mapping, + default_attrs, + input_perm, + output_perm, + fill_name_field, ) = info + + if fluid_op in self.default_ioa_constraint: + for predicate, message in self.default_ioa_constraint[fluid_op]: + assert predicate(inputs, outputs, attrs), message + + mapped_attrs = { + attr_mapping.get(key, key): value + for key, value in attrs.items() + } + if '' in mapped_attrs: + mapped_attrs.pop('') + if '_' in mapped_attrs: + mapped_attrs.pop('_') + fluid_attrs = default_attrs.copy() + fluid_attrs.update(mapped_attrs) + inputs = inputs if input_perm is None else list( + map(lambda i: inputs[i], input_perm)) + val_inps = [] + for idx, ipt in enumerate(inputs): + val_inps.append(self.graph.get_input_node(node, idx=idx, copy=True)) + + val_outs = outputs if output_perm is None else list( + map(lambda i: outputs[i], output_perm)) + attr = fluid_attrs + assert len(val_inps) == 1, 'directly_map error with multi inputs' + if fluid_op not in ['shape', 'erf']: + attr['name'] = string(node.layer_name) + node.fluid_code.add_layer( + fluid_op, inputs=val_inps[0], output=val_outs[0], param_attr=attr) + if fluid_op in ['shape']: + node.fluid_code.add_layer( + 'cast', + inputs=val_outs[0], + output=val_outs[0], + param_attr={'dtype': string('int64')}) + + @print_mapping_info + def deal_custom_layer(self, node): + op = node.layer_type + custom_code, func = make_custom_layer(node) + child_func_code, child_func = make_custom_child_func(node) + params = get_params(node.layer, node.layer_type) + arg_names, kwargs = set_args(func, params) + kwargs['name'] = string(node.layer_name) + node.fluid_code.add_layer( + func.__code__.co_name, + inputs=node.inputs, + output=node, + param_attr=kwargs, + is_custom_layer=True) + if op not in self.used_custom_layers: + self.used_custom_layers[op] = custom_code + if op + '_child_func' not in self.used_custom_layers: + if child_func_code is not None: + self.used_custom_layers[op + + '_child_func'] = child_func_code + + @print_mapping_info + def elementwise_map(self, node): + assert node.layer_type in self.elementwise_ops + op_type = self.elementwise_ops[node.layer_type] + + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_input_node(node, idx=1, copy=True) + val_y_shape = val_y.out_shapes[0] + val_x_shape = val_x.out_shapes[0] + + if len(val_x_shape) < len(val_y_shape): + val_x, val_y = val_y, val_x + val_y_shape, val_x_shape = val_x_shape, val_y_shape + + str_y_shape = ','.join(str(e) for e in val_y_shape) + str_x_shape = ','.join(str(e) for e in val_x_shape) + slice_idx = 0 + if str_y_shape not in str_x_shape: + for dim in val_y_shape: + if dim == 1: + slice_idx += 1 + else: + break + attr = {"name": string(node.layer_name)} + if slice_idx < len(val_y_shape) and slice_idx > 0: + val_y_reshaped = val_y_shape[slice_idx:] + var_y_reshaped = val_y.layer_name + '_reshaped' + attr_reshaped = { + 'shape': val_y_reshaped, + 'name': string(var_y_reshaped) + } + node.fluid_code.add_layer( + 'reshape', + inputs=val_y, + output=var_y_reshaped, + param_attr=attr_reshaped) + inputs = {'x': val_x, 'y': var_y_reshaped} + node.fluid_code.add_layer( + op_type, inputs=inputs, output=node, param_attr=attr) + else: + inputs = {'x': val_x, 'y': val_y} + node.fluid_code.add_layer( + op_type, inputs=inputs, output=node, param_attr=attr) + + @print_mapping_info + def place_holder(self, node): + self.input_shapes.append(node.out_shapes[0]) + + shape = node.out_shapes[0] + for i, dim_shape in enumerate(shape): + if dim_shape == 0 and i == 0: + shape[i] = 1 + if dim_shape == 0 and i != 0: + assert 'shape of input is not assigned' + attr = { + "dtype": string(node.dtype), + "shape": shape, + "name": string(node.layer_name), + "append_batch_size": 'False' + } + + node.fluid_code.add_layer( + "data", inputs=None, output=node, param_attr=attr) + + @print_mapping_info + def create_parameter(self, node, parameter=None): + if parameter is not None: + node = parameter + dtype = node.dtype + shape = node.out_shapes[0] + if len(node.weight.shape) == 0: + shape = [1] + self.weights[node.layer_name] = node.weight + attr = { + 'dtype': string(dtype), + 'shape': shape, + 'name': string(node.layer_name), + 'default_initializer': 'Constant(0.0)' + } + if dtype == 'bool': + attr['dtype'] = string('int64') + node.fluid_code.add_layer( + "create_parameter", inputs=None, output=node, param_attr=attr) + node.fluid_code.add_layer( + "cast", + inputs=node, + output=node, + param_attr={'dtype': string('bool')}) + elif dtype == 'uint8': + attr['dtype'] = string('float32') + node.fluid_code.add_layer( + "create_parameter", inputs=None, output=node, param_attr=attr) + else: + node.fluid_code.add_layer( + "create_parameter", inputs=None, output=node, param_attr=attr) + + def _pad_if_asymmetric(self, node, pads, val_name): # pads: SSEE + assert len(pads) & 1 == 0 + symmetric = True + ndims = len(pads) // 2 + for idx_dim in range(ndims): + if pads[idx_dim] != pads[ndims + idx_dim]: + symmetric = False + break + if symmetric: + return pads[:ndims], val_name + val_padded = self.Pad(node, op_independent=False) + return [0] * ndims, val_padded + + def _interpolate(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + if node.layer_type == 'Resize': + val_scales = self.graph.get_input_node(node, idx=2, copy=True) + elif node.layer_type == 'Upsample': + val_scales = self.graph.get_input_node(node, idx=1, copy=True) + + attr = {'name': string(node.layer_name)} + mode = node.get_attr('mode', 'nearest') + fluid_op = 'resize_{}'.format(mode) + if 'linear' in mode: + print( + 'Warnning: paddle not support op:resize wiht mode: linear, we use bilinear replace linear' + ) + fluid_op = 'resize_bilinear' + + node.fluid_code.add_layer( + fluid_op, + inputs={'input': val_x, + 'scale': val_scales}, + output=node, + param_attr=attr) + + @print_mapping_info + def RoiAlign(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_rois = self.graph.get_input_node(node, idx=1, copy=True) + + pooled_height = node.get_attr('output_height') + pooled_width = node.get_attr('output_width') + spatial_scale = node.get_attr('spatial_scale') + sampling_ratio = node.get_attr('sampling_ratio') + attr = { + 'pooled_height': pooled_height, + 'pooled_width': pooled_width, + 'spatial_scale': spatial_scale, + 'sampling_ratio': sampling_ratio, + } + node.fluid_code.add_layer( + 'roi_align', + inputs={'input': val_x, + 'rois': val_rois}, + output=node, + param_attr=attr) + + @print_mapping_info + def MaxRoiPool(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_rois = self.graph.get_input_node(node, idx=1, copy=True) + + spatial_scale = node.get_attr('spatial_scale') + pooled_height, pooled_width = node.get_attr('pooled_shape') + attr = { + 'pooled_height': pooled_height, + 'pooled_width': pooled_width, + 'spatial_scale': spatial_scale, + } + node.fluid_code.add_layer( + 'roi_pool', + inputs={'input': val_x, + 'rois': val_rois}, + output=node, + param_attr=attr) + + @print_mapping_info + def Pad(self, node, op_independent=True): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + pads = node.get_attr('pads') + mode = node.get_attr('mode', 'constant') + value = node.get_attr('value', 0.) + data_shape = val_x.out_shapes[0] + output_shape = node.out_shapes[0] + assume_pad2d = False + attr = {} + if len(pads) == 4: + assume_pad2d |= mode != 'constant' + if data_shape: + assume_pad2d |= data_shape and len(data_shape) == 4 # NCHW + if output_shape: + assume_pad2d |= output_shape and len(output_shape) == 4 # NCHW + if assume_pad2d: + fluid_op = 'pad2d' + attr['data_format'] = string('NCHW') + attr['mode'] = string(mode) + else: + attr = {'pad_value': value} + fluid_op = 'pad' + if len(pads) == 4: + paddings = np.array(pads).reshape( + (-1, 2)).transpose().flatten().tolist() # SSEE -> SESE + elif len(pads) == 8: + paddings = np.array(pads).reshape( + (-1, 4)).transpose().flatten().tolist() # SSEE -> SESE + if sum(paddings[:4]) == 0: + fluid_op = 'pad2d' + paddings = paddings[4:] + attr['mode'] = string(mode) + attr['paddings'] = paddings + if op_independent: + attr['name'] = string(node.layer_name) + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + else: + attr['name'] = string(node.layer_name + '_paded') + node.fluid_code.add_layer( + fluid_op, + inputs=val_x, + output=node.layer_name + '_paded', + param_attr=attr) + return node.layer_name + '_paded' + + @print_mapping_info + def Unsqueeze(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + axes = node.get_attr('axes') + attr = {'axes': axes, 'name': string(node.layer_name)} + if len(val_x.out_shapes[0]) == 0: + if node.layer_name: + node.fluid_code.add_layer( + 'reshape', + inputs=val_x, + output=node, + param_attr={'shape': [1]}) + else: + node.fluid_code.add_layer( + 'unsqueeze', inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Shrink(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + bias = node.get_attr('bias') + lambd = node.get_attr('lambd') + assert bias == 0.0, 'not support bias!=0' + attr = {'threshold': lambd, 'name': node.layer_name} + node.fluid_code.add_layer( + 'hard_shrink', inputs=val_x, output=node, param_attr=attr) + + def Greater(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_input_node(node, idx=1, copy=True) + node.fluid_code.add_layer( + 'greater_than', + inputs={'x': val_x, + 'y': val_y}, + output=node, + param_attr=None) + + @print_mapping_info + def Constant(self, node): + val_output = self.graph.get_node(node.layer.output[0], copy=True) + + value = node.get_attr('value') + dtype = np.dtype(value.dtype) + output_dtype = val_output.dtype + if output_dtype: + assert dtype == output_dtype, 'tensor dtype unmatches storage dtype' + + shape = node.get_attr('shape', None) + + if shape is None: + shape = val_output.out_shapes[0] + if shape is None: + shape = list(value.shape) + _logger.warning('in (Constant -> %s): ' + 'attribute "shape" of %s not inferred, ' + 'using value as 1-D tensor may lead to fails', + val_output.layer_name, val_output.layer_name) + + if len(value) == 1: + value = value.tolist() + shape = [1] + value = value[0] + if dtype.name == 'int64': + dtype = 'int32' + attr = {'shape': shape, 'dtype': string(dtype), 'value': value} + node.fluid_code.add_layer( + 'fill_constant', inputs=None, output=node, param_attr=attr) + else: + if dtype.name == 'uint8': + dtype = 'int64' + value = np.reshape(value, shape) + self.weights[node.layer_name] = value + attr = { + 'dtype': string(dtype), + 'shape': shape, + 'name': string(node.layer_name), + 'default_initializer': 'Constant(0.0)' + } + node.fluid_code.add_layer( + "create_parameter", inputs=None, output=node, param_attr=attr) + + @print_mapping_info + def Resize(self, node): + self._interpolate(node) + + @print_mapping_info + def Upsample(self, node): + self._interpolate(node) + + @print_mapping_info + def InstanceNormalization(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_scale = self.graph.get_input_node(node, idx=1, copy=True) + val_b = self.graph.get_input_node(node, idx=2, copy=True) + epsilon = node.get_attr('epsilon', 1e-5) + attr = { + 'epsilon': epsilon, + 'param_attr': string(val_scale.layer_name), + 'bias_attr': string(val_b.layer_name) + } + node.fluid_code.add_layer( + "instance_norm", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Expand(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_shape = self.graph.get_input_node(node, idx=1, copy=True) + + if len(val_shape.outputs) == 1: + self.omit_nodes.append(val_shape.layer_name) + + val_y = self.graph.get_node(node.layer.output[0], copy=True) + out_shape = node.out_shapes[0] + val_x_dtype = val_x.dtype + + name_ones = node.layer_name + '_ones' + attr_ones = {'shape': out_shape, 'dtype': string(val_x_dtype)} + node.fluid_code.add_layer( + 'ones', inputs=None, output=name_ones, param_attr=attr_ones) + inputs = {'x': name_ones, 'y': val_x} + attr = {'name': string(node.layer_name)} + node.fluid_code.add_layer( + 'elementwise_mul', + inputs=inputs, + output=node.layer_name, + param_attr=attr) + + @print_mapping_info + def Gather(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + indices = self.graph.get_input_node(node, idx=1, copy=True) + indices_shape = indices.out_shapes[0] + axis = node.get_attr('axis', 0) + #assert len( + # indices_shape) <= 2, "Gather op don't support dim of indice >2 " + if axis == 0 and len(indices_shape) <= 1: + node.fluid_code.add_layer( + 'gather', + inputs={'input': val_x, + 'index': indices}, + output=node, + param_attr=None) + elif axis > 0 and len(indices_shape) <= 1: + perm = list(range(len(val_x.out_shapes[0]))) + perm = [axis] + perm[:axis] + perm[axis + 1:] + attr_trans = {'perm': perm} + name_trans = val_x.layer_name + '_trans' + node.fluid_code.add_layer( + 'transpose', + inputs=val_x, + output=name_trans, + param_attr=attr_trans) + node.fluid_code.add_layer( + 'gather', + inputs={'input': name_trans, + 'index': indices}, + output=node, + param_attr=None) + node.fluid_code.add_layer( + 'transpose', inputs=node, output=node, param_attr=attr_trans) + elif axis == 0 and len(indices_shape) > 1: + if val_x.out_shapes[0] is not None and isinstance( + val_x, ONNXGraphDataNode): + node.fluid_code.add_layer( + 'embedding', + inputs=indices, + output=node, + use_fluid=True, + param_attr={ + 'param_attr': string(val_x.layer_name), + 'size': val_x.out_shapes[0] + }) + else: + from functools import reduce + #indices_shape = [1,7] + reshape_shape = reduce(lambda x, y: x * y, indices_shape) + indices_reshape = indices.layer_name + '_shape' + node.fluid_code.add_layer( + 'reshape', + inputs=indices, + output=indices_reshape, + param_attr={'shape': [reshape_shape, ]}) + + perm = list(range(len(val_x.out_shapes[0]))) + node.fluid_code.add_layer( + 'gather', + inputs={'input': val_x, + 'index': indices_reshape}, + output=node, + param_attr=None) + val_x_shape = val_x.out_shapes[0] + reshaped_shape = [] + for i in perm: + reshaped_shape.append(indices_shape[i]) + for i in val_x_shape[:axis] + val_x_shape[axis + 1:]: + reshaped_shape.append(i) + node.fluid_code.add_layer( + 'reshape', + inputs=node, + output=node, + param_attr={'shape': reshaped_shape}) + elif axis > 0 and len(indices_shape) > 1: + from functools import reduce + reshape_shape = reduce(lambda x, y: x * y, indices_shape) + indices_reshape = indices.layer_name + '_shape' + node.fluid_code.add_layer( + 'reshape', + inputs=indices, + output=indices_reshape, + param_attr={'shape': [reshape_shape, ]}) + + perm = list(range(len(val_x.out_shapes[0]))) + perm = [axis] + perm[:axis] + perm[axis + 1:] + attr_trans = {'perm': perm} + name_trans = val_x.layer_name + '_trans' + node.fluid_code.add_layer( + 'transpose', + inputs=val_x, + output=name_trans, + param_attr=attr_trans) + node.fluid_code.add_layer( + 'gather', + inputs={'input': name_trans, + 'index': indices_reshape}, + output=node, + param_attr=None) + node.fluid_code.add_layer( + 'transpose', inputs=node, output=node, param_attr=attr_trans) + val_x_shape = val_x.out_shapes[0] + reshaped_shape = [] + for i in perm: + reshaped_shape.append(indices_shape[i]) + for i in val_x_shape[:axis] + val_x_shape[axis + 1:]: + reshaped_shape.append(i) + node.fluid_code.add_layer( + 'reshape', + inputs=node, + output=node, + param_attr={'shape': reshaped_shape}) + + @print_mapping_info + def Range(self, node): + val_start = self.graph.get_input_node(node, idx=0, copy=True) + val_limit = self.graph.get_input_node(node, idx=1, copy=True) + val_delta = self.graph.get_input_node(node, idx=2, copy=True) + dtype = val_start.dtype + inputs = {'start': val_start, 'end': val_limit, 'step': val_delta} + node.fluid_code.add_layer( + 'range', + inputs=inputs, + output=node, + param_attr={'dtype': string(dtype)}) + + @print_mapping_info + def Slice(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + starts, ends, axes, steps = None, None, None, None + attr = {} + if len(node.inputs) > 1: + starts = self.graph.get_input_node(node, idx=1, copy=True) + ends = self.graph.get_input_node(node, idx=2, copy=True) + if len(node.inputs) > 3: + axes = self.graph.get_input_node(node, idx=3, copy=True) + axes = _const_weight_or_none(axes) + if len(node.inputs) > 4: + steps = self.graph.get_input_node(node, idx=4, copy=True) + steps = _const_weight_or_none(steps) + if steps is not None: + assert steps == 1, "Only support convert op:Slice, which attribute:steps == 1" + attr = { + "axes": axes, + "starts": starts.layer_name, + "ends": ends.layer_name + } + starts_value = _const_weight_or_none(starts) + ends_value = _const_weight_or_none(ends) + if starts_value is not None and ends_value is not None: + self.omit_nodes.append(starts.layer_name) + self.omit_nodes.append(ends.layer_name) + ends_value = ends_value.copy() + for idx in range(len(ends_value)): + if ends_value[idx] > 2**31 - 1: + ends_value[idx] = 2**31 - 1 + attr = { + "axes": axes, + "starts": starts_value, + "ends": ends_value + } + else: + if starts.dtype != 'int32': + node.fluid_code.add_layer( + 'cast', + inputs=starts, + output=starts, + param_attr={'dtype': string('int32')}) + if ends.dtype != 'int32': + node.fluid_code.add_layer( + 'cast', + inputs=ends, + output=ends, + param_attr={'dtype': string('int32')}) + else: + starts = node.get_attr('starts') + ends = node.get_attr('ends') + axes = node.get_attr('axes') + for idx in range(len(ends)): + if ends[idx] > 2**31 - 1: + ends[idx] = 2**31 - 1 + attr = {"axes": axes, "starts": starts, "ends": ends} + + node.fluid_code.add_layer( + 'slice', inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def ConstantOfShape(self, node): + val_shape = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_node(node.layer.output[0], copy=True) + + value = node.get_attr('value') + dtype = value.dtype + value = value.tolist() + assert len(value) == 1, ('given value not Scalar, shape of value > 1, ' + 'this is not supported') + if len(value) == 1: + value = value[0] + if dtype.name == 'int64': + dtype = 'int32' + attr = { + 'shape': val_shape.layer_name, + 'dtype': string(dtype), + 'value': value + } + node.fluid_code.add_layer( + 'fill_constant', inputs=None, output=node, param_attr=attr) + + @print_mapping_info + def Split(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_node(node.layer.output[0], copy=True) + + fluid_op = 'split' + split = node.get_attr('split') + axis = node.get_attr('axis', 0) + attr = { + 'num_or_sections': split, + 'dim': axis, + 'name': string(node.layer_name) + } + + node.fluid_code.add_layer( + 'split', inputs=val_x, output=val_y, param_attr=attr) + + @print_mapping_info + def Reshape(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_shape = self.graph.get_input_node(node, idx=1, copy=True) + val_reshaped = self.graph.get_node(node.layer.output[0], copy=True) + attr = {} + shape_value = _const_weight_or_none(val_shape) + shape_dims = len(val_shape.out_shapes[0]) + + if shape_value is not None: + node.fluid_code.add_layer( + 'reshape', + inputs={'x': val_x}, + output=node, + param_attr={'shape': shape_value.tolist()}) + elif val_shape.dtype == 'int64': + val_shape_cast = val_shape.layer_name + '_cast' + node.fluid_code.add_layer( + 'cast', + inputs=val_shape, + output=val_shape_cast, + param_attr={'dtype': string('int32')}) + node.fluid_code.add_layer( + 'reshape', + inputs=val_shape_cast, + output=val_shape_cast, + param_attr={'shape': val_shape.out_shapes[0]}) + node.fluid_code.add_layer( + 'reshape', + inputs={'x': val_x, + 'shape': val_shape_cast}, + output=node, + param_attr=attr) + else: + node.fluid_code.add_layer( + 'reshape', + inputs=val_shape, + output=val_shape, + param_attr={'shape': val_shape.out_shapes[0]}) + node.fluid_code.add_layer( + 'reshape', + inputs={'x': val_x, + 'shape': val_shape}, + output=node, + param_attr=attr) + + @print_mapping_info + def Cast(self, node): + val_input = self.graph.get_input_node(node, idx=0, copy=True) + val_output = self.graph.get_node(node.layer.output[0], copy=True) + + dtype = node.get_attr('to') + if not isinstance(dtype, np.dtype): + dtype = TENSOR_TYPE_TO_NP_TYPE[dtype] + + output_dtype = val_output.dtype + if output_dtype: + assert dtype == output_dtype, 'dtype of to unmatches output' + attr = {'dtype': string(dtype)} + node.fluid_code.add_layer( + 'cast', inputs=val_input, output=node, param_attr=attr) + + @print_mapping_info + def AveragePool(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + + auto_pad = node.get_attr('auto_pad', 'NOTSET') + kernel_shape = node.get_attr("kernel_shape") + poolnd = len(kernel_shape) + strides = node.get_attr("strides") + pad_mode = node.get_attr("pads") + ceil_mode = bool(node.get_attr('ceil_mode', 0)) + pads = node.get_attr('pads', [0] * (poolnd * 2)) + fluid_op = 'pool{}d'.format(poolnd) + assert 2 <= poolnd <= 3, 'only pool2d and pool3d is supported' + + paddings, val_x = self._pad_if_asymmetric(node, pads, val_x) + + if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + input_shape = val_x.out_shapes[0] + pad_h = get_same_padding(input_shape[2], kernel_shape[0], + strides[0]) + pad_w = get_same_padding(input_shape[3], kernel_shape[1], + strides[1]) + attr = {"paddings": pad_h + pad_w, "pad_value": 0.0} + + attr = { + "pool_size": kernel_shape, + "pool_type": string('avg'), + "pool_stride": strides, + "pool_padding": paddings, + "ceil_mode": ceil_mode, + "exclusive": 'True', + "name": string(node.layer_name) + } + + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Concat(self, node): + inputs = [] + for i in range(len(node.layer.input)): + ipt = self.graph.get_input_node(node, idx=i, copy=True) + if isinstance(ipt, str): + inputs.append(ipt) + else: + inputs.append(ipt.layer_name) + axis = node.get_attr('axis') + attr = {'axis': axis} + node.fluid_code.add_layer( + 'concat', inputs=inputs, output=node, param_attr=attr) + + @print_mapping_info + def Flatten(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + axis = node.get_attr('axis', 1) + attr = {"axis": str(axis), "name": string(node.layer_name)} + node.fluid_code.add_layer( + 'flatten', inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Gemm(self, node): + val_a = self.graph.get_input_node(node, idx=0, copy=True) + val_b = self.graph.get_input_node(node, idx=1, copy=True) + val_c = self.graph.get_input_node(node, idx=2, copy=True) + + alpha = node.get_attr('alpha', 1.) # optional + beta = node.get_attr('beta', 1.) # optional + trans_a = bool(node.get_attr('transA', 0)) # optional + trans_b = bool(node.get_attr('transB', 0)) # optional + val_mm = node.layer_name + '_mm' + matmul_inputs = {"x": val_a, "y": val_b} + attr_matmul = { + "transpose_x": trans_a, + "transpose_y": trans_b, + "alpha": alpha, + "name": string(val_mm) + } + node.fluid_code.add_layer( + 'matmul', + inputs=matmul_inputs, + output=val_mm, + param_attr=attr_matmul) + + if beta != 0: + if beta == 1.: + add_inputs = {"x": val_mm, "y": val_c} + attr = {"name": string(node.layer_name)} + node.fluid_code.add_layer( + "elementwise_add", + inputs=add_inputs, + output=node, + param_attr=attr) + else: + var_beta = node.layer_name + '_beta' + matmul_beta_inputs = {"x": val_c, "y": var_beta} + node.fluid_code.add_layer( + "Constant", + inputs=matmul_beta_inputs, + output=var_beta, + param_attr={'value': beta}) + + add_inputs = {"x": val_mm, "y": var_beta} + attr = {"name": string(node.layer_name)} + node.fluid_code.add_layer( + "elementwise_add", + inputs=add_inputs, + output=node, + param_attr=attr) + + @print_mapping_info + def Sum(self, node): + val_inps = node.layer.input + inputs = { + "x": self.graph.get_input_node( + node, idx=0, copy=True), + "y": self.graph.get_input_node( + node, idx=1, copy=True), + } + node.fluid_code.add_layer("elementwise_add", inputs=inputs, output=node) + + for idx, ipt in enumerate(val_inps[2:]): + y = self.graph.get_input_node(node, idx=idx, copy=True) + inputs = { + "x": node.layer_name, + "y": y, + } + node.fluid_code.add_layer( + "elementwise_add", inputs=inputs, output=node) + + @print_mapping_info + def MatMul(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_input_node(node, idx=1, copy=True) + inputs = {"x": val_x, "y": val_y} + attr = {"name": string(node.layer_name)} + node.fluid_code.add_layer( + "matmul", inputs=inputs, output=node, param_attr=attr) + + @print_mapping_info + def BatchNormalization(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_scale = self.graph.get_input_node(node, idx=1, copy=True) + val_b = self.graph.get_input_node(node, idx=2, copy=True) + val_mean = self.graph.get_input_node(node, idx=3, copy=True) + val_var = self.graph.get_input_node(node, idx=4, copy=True) + + self.omit_nodes.append(val_scale.layer_name) + self.omit_nodes.append(val_b.layer_name) + self.omit_nodes.append(val_mean.layer_name) + self.omit_nodes.append(val_var.layer_name) + + momentum = node.get_attr('momentum', .9) + epsilon = node.get_attr('epsilon', 1e-5) + + # Attribute: spatial is used in BatchNormalization-1,6,7 + spatial = bool(node.get_attr('spatial')) + attr = { + "momentum": momentum, + "epsilon": epsilon, + "data_layout": string('NCHW'), + "is_test": True, + "param_attr": string(val_scale.layer_name), + "bias_attr": string(val_b.layer_name), + "moving_mean_name": string(val_mean.layer_name), + "moving_variance_name": string(val_var.layer_name), + "use_global_stats": spatial, + "name": string(node.layer_name) + } + node.fluid_code.add_layer( + "batch_norm", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Transpose(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + perm = node.get_attr('perm') + attr = {'perm': perm, "name": string(node.layer_name)} + node.fluid_code.add_layer( + "transpose", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Relu(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + attr = {"name": string(node.layer_name)} + node.fluid_code.add_layer( + "relu", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def PRelu(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_slope = self.graph.get_input_node(node, idx=1, copy=True) + + mode = 'channel' + shape_slope = val_slope.out_shapes[0] + if len(shape_slope) == 1: + mode = 'all' + elif len(shape_slope) > 2: + mode = 'element' + attr = { + "param_attr": string(val_slope.layer_name), + 'mode': string(mode) + } + node.fluid_code.add_layer( + "prelu", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Squeeze(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + axes = node.get_attr('axes') + attr = {'axes': axes, "name": string(node.layer_name)} + if len(val_x.out_shapes[0]) == 1: + node.fluid_code.add_layer( + "cast", + inputs=val_x, + output=node, + param_attr={'dtype': string(val_x.dtype)}) + else: + node.fluid_code.add_layer( + "squeeze", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def Equal(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_input_node(node, idx=1, copy=True) + node.fluid_code.add_layer( + "equal", + inputs={'x': val_x, + 'y': val_y}, + output=node, + param_attr=None) + + @print_mapping_info + def Where(self, node): + condition = self.graph.get_input_node(node, idx=0, copy=True) + val_x = self.graph.get_input_node(node, idx=1, copy=True) + val_y = self.graph.get_input_node(node, idx=2, copy=True) + + not_condition = condition.layer_name + '_not' + node.fluid_code.add_layer( + "logical_not", + inputs=condition, + output=not_condition, + param_attr=None) + cast_not_condition = not_condition + '_cast' + node.fluid_code.add_layer( + "cast", + inputs=not_condition, + output=cast_not_condition, + param_attr={'dtype': string(val_x.dtype)}) + cast_condition = condition.layer_name + '_cast' + node.fluid_code.add_layer( + "cast", + inputs=condition, + output=cast_condition, + param_attr={'dtype': string(val_x.dtype)}) + mul_val_x = val_x.layer_name + '_mul' + node.fluid_code.add_layer( + "elementwise_mul", + inputs={'x': val_x, + 'y': cast_condition}, + output=mul_val_x, + param_attr=None) + + mul_val_y = val_y.layer_name + '_mul' + node.fluid_code.add_layer( + "elementwise_mul", + inputs={'x': val_y, + 'y': cast_not_condition}, + output=mul_val_y, + param_attr=None) + + node.fluid_code.add_layer( + "elementwise_add", + inputs={'x': mul_val_x, + 'y': mul_val_y}, + output=node, + param_attr=None) + + @print_mapping_info + def NonZero(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_x_dim = len(val_x.out_shapes[0]) + print(val_x.layer_name, val_x.out_shapes[0]) + if val_x_dim == 1: + node.fluid_code.add_layer("nonzero", inputs=val_x, output=val_x) + node.fluid_code.add_layer( + "transpose", + inputs=val_x, + output=node, + param_attr={'perm': [1, 0]}) + if val_x_dim > 1: + node.fluid_code.add_layer("nonzero", inputs=val_x, output=val_x) + node.fluid_code.add_layer( + "split", + inputs=val_x, + output=val_x, + param_attr={'num_or_sections': 1, + 'dim': val_x_dim}) + node.fluid_code.add_layer("concat", inputs=val_x, output=node) + + @print_mapping_info + def Identity(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + node.fluid_code.add_layer("assign", inputs=val_x, output=node) + + @print_mapping_info + def Tile(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_repeats = self.graph.get_input_node(node, idx=1, copy=True) + repeats = _const_weight_or_none(val_repeats) + + if repeats is None: + repeats = val_repeats.layer_name + elif isinstance(repeats, int): + repeats = [repeats] + + attr = { + 'expand_times': repeats, + "name": string(node.layer_name), + } + node.fluid_code.add_layer( + "expand", inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def MaxPool(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + auto_pad = node.get_attr('auto_pad', 'NOTSET') + assert node.get_attr( + "dilations") is None, 'only dilations = 0 is supported' # optional + + kernel_shape = node.get_attr("kernel_shape") + poolnd = len(kernel_shape) + strides = node.get_attr("strides") + pad_mode = node.get_attr("pads") + ceil_mode = bool(node.get_attr('ceil_mode', 0)) # optional + pads = node.get_attr('pads', [0] * (poolnd * 2)) # optional + fluid_op = 'pool{}d'.format(poolnd) + assert 2 <= poolnd <= 3, 'only pool2d and pool3d is supported' + + paddings, val_x = self._pad_if_asymmetric(node, pads, val_x) + + if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + input_shape = val_x.out_shapes[0] + pad_h = get_same_padding(input_shape[2], kernel_shape[0], + strides[0]) + pad_w = get_same_padding(input_shape[3], kernel_shape[1], + strides[1]) + attr = {"paddings": pad_h + pad_w, "pad_value": 0.0} + + attr = { + "pool_size": kernel_shape, + "pool_type": string("max"), + "pool_stride": strides, + "pool_padding": paddings, + "ceil_mode": ceil_mode, + "name": string(node.layer_name), + "exclusive": False + } + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + + def _global_pool(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_y = self.graph.get_node(node.layer.output[0], copy=True) + fluid_op = 'pool2d' + pool_type = None + if node.layer.op_type == 'GlobalMaxPool': + pool_type = 'max' + elif node.layer.op_type == 'GlobalAveragePool': + pool_type = 'avg' + + attr = { + "pool_type": string(pool_type), + "global_pooling": True, + "name": string(node.layer_name) + } + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def GlobalMaxPool(self, node): + self._global_pool(node) + + @print_mapping_info + def GlobalAveragePool(self, node): + self._global_pool(node) + + @print_mapping_info + def Conv(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_w = self.graph.get_input_node(node, idx=1, copy=True) + val_y = self.graph.get_node(node.layer.output[0], copy=True) + + self.omit_nodes.append(val_w.layer_name) + + has_bias = len(node.layer.input) == 3 + if has_bias: + val_b = self.graph.get_input_node(node, idx=2, copy=True) + self.omit_nodes.append(val_b.layer_name) + auto_pad = node.get_attr('auto_pad', 'NOTSET') + + kernel_shape = node.get_attr('kernel_shape') + convnd = len(kernel_shape) + assert 2 <= convnd <= 3, 'only conv2d and conv3d is supported' + num_out_channels = val_w.out_shapes[0][0] # OI... + fluid_op = 'conv{}d'.format(convnd) + + num_groups = node.get_attr('group', 1) + strides = node.get_attr('strides', [1] * convnd) # optional + dilations = node.get_attr('dilations', [1] * convnd) # optional + pads = node.get_attr('pads', [0] * (convnd * 2)) # optional + + input_shape = val_x.out_shapes[0] + paddings, val_x = self._pad_if_asymmetric(node, pads, val_x) + + if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + pad_h = get_same_padding(input_shape[2], kernel_shape[0], + strides[0]) + pad_w = get_same_padding(input_shape[3], kernel_shape[1], + strides[1]) + attr = {"paddings": pad_h + pad_w, "pad_value": 0.0} + + attr = { + "num_filters": num_out_channels, + "filter_size": kernel_shape, + "stride": strides, + "padding": paddings, + "dilation": dilations, + "groups": num_groups, + 'param_attr': string(val_w.layer_name), + "name": string(node.layer_name) + } + if has_bias: + attr["bias_attr"] = string(val_b.layer_name) + else: + attr["bias_attr"] = False + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def ConvTranspose(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_w = self.graph.get_input_node(node, idx=1, copy=True) + val_b = None + if len(node.layer.input) > 2: + val_b = self.graph.get_input_node(node, idx=2, copy=True) + self.omit_nodes.append(val_b.layer_name) + self.omit_nodes.append(val_w.layer_name) + + val_y = self.graph.get_node(node.layer.output[0], copy=True) + + auto_pad = node.get_attr('auto_pad', 'NOTSET') + out_padding = node.get_attr('output_padding', [0, 0]) + kernel_shape = node.get_attr('kernel_shape') + assert kernel_shape, 'kernel_shape not inferred' + convnd = len(kernel_shape) + assert 2 <= convnd <= 3, 'only conv2d_transpose and conv3d_transpose supported' + num_out_channels = val_w.out_shapes[0][1] + fluid_op = 'conv{}d_transpose'.format(convnd) + + num_groups = node.get_attr('group', 1) + strides = node.get_attr('strides', [1] * convnd) + dilations = node.get_attr('dilations', [1] * convnd) + output_size = node.get_attr('output_shape', []) + pads = node.get_attr('pads', [0] * (convnd * 2)) + + paddings, var_x = self._pad_if_asymmetric(node, pads, val_x) + + output_size = [0, 0] + + output_size[0] = (val_x.out_shapes[0][2] - 1 + ) * strides[0] - 2 * paddings[0] + dilations[0] * ( + kernel_shape[0] - 1) + 1 + out_padding[0] + output_size[1] = (val_x.out_shapes[0][3] - 1 + ) * strides[1] - 2 * paddings[1] + dilations[1] * ( + kernel_shape[1] - 1) + 1 + out_padding[1] + attr = { + 'num_filters': num_out_channels, + 'output_size': output_size or None, + 'filter_size': kernel_shape, + 'padding': paddings, + 'stride': strides, + 'dilation': dilations, + 'groups': num_groups, + 'param_attr': string(val_w.layer_name), + 'bias_attr': None if val_b is None else string(val_b.layer_name), + 'name': string(node.layer_name), + } + node.fluid_code.add_layer( + fluid_op, inputs=val_x, output=node, param_attr=attr) + + @print_mapping_info + def GRU(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + val_w = self.graph.get_input_node(node, idx=1, copy=True) + val_r = self.graph.get_input_node(node, idx=2, copy=True) + + val_b = None + val_len = None + val_xh = None + miss_arg_num = 0 + num_ipt = len(node.layer.input) + if num_ipt > 3 and node.layer.input[3] != '': + val_b = self.graph.get_input_node(node, idx=3, copy=True) + else: + miss_arg_num += 1 + if num_ipt > 4 and node.layer.input[4] != '': + val_len = self.graph.get_input_node( + node, idx=4 - miss_arg_num, copy=True) + else: + miss_arg_num += 1 + if num_ipt > 5 and node.layer.input[5] != '': + val_xh = self.graph.get_input_node( + node, idx=5 - miss_arg_num, copy=True) + + x_shape = val_x.out_shapes[0] + + assert x_shape[1] == 1, 'only X with batch_size = 1 supported' + assert node.get_attr('clip', None) is None, 'clipping not supported' + + hidden_size = node.get_attr('hidden_size', None) + if hidden_size is None: + r_shape = val_r.out_shapes[0] + if r_shape: + hidden_size = r_shape[-1] + if hidden_size is None: + w_shape = var_w.out_shapes[0] + if w_shape: + hidden_size = w_shape[-2] // 3 + if hidden_size is None and val_b: + b_shape = val_b.out_shapes[0] + if b_shape: + hidden_size = b_shape[-1] // 6 + if hidden_size is None and val_xh: + xh_shape = val_xh.out_shapes[0] + if xh_shape: + hidden_size = xh_shape[-1] + + direction = node.get_attr('direction', 'forward') + assert direction != 'bidirectional', 'direction = bidirectional not supported' + + activations = node.get_attr('activations', ['Sigmoid', 'Tanh']) + assert len(activations) == 2, 'bidirectional operation not supported' + + assert node.get_attr('linear_before_reset', + 0) == 0, 'only linear_before_reset = 0 supported' + + activations = [s.lower() for s in activations] + gate_activation, candidate_activation = activations + is_reverse = direction == 'reverse' + + var_x0 = node.layer_name + '_x0' + node.fluid_code.add_layer( + 'squeeze', + inputs=val_x, + output=var_x0, + param_attr={'axes': [1], + 'name': string(var_x0)}) + + var_w0 = node.layer_name + '_w0' + node.fluid_code.add_layer( + 'squeeze', + inputs=val_w, + output=var_w0, + param_attr={'axes': [0], + 'name': string(var_w0)}) + + var_fc = node.layer_name + '_fc' + var_mm = (node.layer_name + '_mm') if val_b else var_fc + node.fluid_code.add_layer( + 'matmul', + inputs={'x': var_x0, + 'y': var_w0}, + output=var_mm, + param_attr={ + 'transpose_x': 0, + 'transpose_y': 1, + 'name': string(var_mm) + }) + + var_r0 = node.layer_name + '_r0' + node.fluid_code.add_layer( + 'squeeze', + inputs=val_r, + output=var_r0, + param_attr={'axes': [0], + 'name': string(var_r0)}) + + var_r0t = node.layer_name + '_r0t' + + node.fluid_code.add_layer( + 'transpose', + inputs=var_r0, + output=var_r0t, + param_attr={'perm': [1, 0], + 'name': string(var_r0t)}) + if val_b: + var_bi = node.layer_name + '_bi' + var_bh = node.layer_name + '_bh' + node.fluid_code.add_layer( + 'split', + inputs=val_b, + output=var_bi + ',' + var_bh, + param_attr={ + 'axis': 1, + 'split': [hidden_size * 3, hidden_size * 3], + 'name': string(node.layer_name + '.b/split') + }) + var_bi0 = node.layer_name + '_bi0' + node.fluid_code.add_layer( + 'squeeze', + inputs=var_bi, + output=var_bi0, + param_attr={'axes': [0], + 'name': string(var_bi0)}) + + node.fluid_code.add_layer( + 'elmentwise_add', + inputs=[var_mm, var_bi0], + output=var_fc, + param_attr={ + 'axes': 1, + 'name': string(node.layer_name + '.i/bias') + }) + + if val_xh: + var_xh0 = node.layer_name + '_xh0' + node.fluid_code.add_layer( + 'squeeze', + inputs=val_xh, + output=var_xh0, + param_attr={'axes': [1], + 'name': string(var_xh0)}) + var_y00 = node.layer_name + '_y00' + + attr = { + 'origin_mode': True, + 'h_0': var_xh0 if val_xh else None, + 'is_reverse': is_reverse, + 'gate_activation': string(gate_activation), + 'candidate_activation': string(candidate_activation), + 'param_attr': string(var_r0t), + 'bias_attr': string(var_bh) if val_b else False, + } + node.fluid_code.add_layer( + 'dynamic_gru', + inputs=var_fc + ',' + str(hidden_size), + output=var_y00, + param_attr=attr) + + num_opt = len(node.layer.output) + + if num_opt > 0 and node.layer.output[0] != '': + node.fluid_code.add_layer( + 'unsqueeze', + inputs=var_y00, + output=node.layer.output[0], + param_attr={ + 'axes': [1, 1], + 'name': string(node.layer.output[0]) + }) + if num_opt > 1 and node.layer.output[1] != '': + node.fluid_code.add_layer( + 'unsqueeze', + inputs=var_y00, + output=node.layer.output[1], + param_attr={ + 'axes': [1, 1], + 'name': string(node.layer.output[1]) + }) diff --git a/x2paddle/op_mapper/paddle2onnx/__init__.py b/x2paddle/op_mapper/paddle2onnx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset10/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/opset.py b/x2paddle/op_mapper/paddle2onnx/opset10/opset.py new file mode 100644 index 0000000..72925dd --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset10/opset.py @@ -0,0 +1,61 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import x2paddle +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +from onnx import helper, onnx_pb +from x2paddle.op_mapper.paddle2onnx.opset9.opset import OpSet9 + + +class OpSet10(OpSet9): + def __init__(self): + super(OpSet10, self).__init__() + + def slice(self, op, block): + axes = op.attr('axes') + starts = op.attr('starts') + ends = op.attr('ends') + axes_name = self.get_name(op.type, 'axes') + starts_name = self.get_name(op.type, 'starts') + ends_name = self.get_name(op.type, 'ends') + + axes_node = self.make_constant_node(axes_name, + onnx_pb.TensorProto.INT64, axes) + starts_node = self.make_constant_node(starts_name, + onnx_pb.TensorProto.INT64, starts) + ends_node = self.make_constant_node(ends_name, + onnx_pb.TensorProto.INT64, ends) + node = helper.make_node( + "Slice", + inputs=[op.input('Input')[0], starts_name, ends_name, axes_name], + outputs=op.output('Out'), ) + return [starts_node, ends_node, axes_node, node] + + def im2sequence(self, op, block): + from .paddle_custom_layer.im2sequence import im2sequence + return im2sequence(op, block) + + def yolo_box(self, op, block): + from .paddle_custom_layer.yolo_box import yolo_box + return yolo_box(op, block) + + def multiclass_nms(self, op, block): + from .paddle_custom_layer.multiclass_nms import multiclass_nms + return multiclass_nms(op, block) diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/im2sequence.py b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/im2sequence.py new file mode 100644 index 0000000..aeb4a9c --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/im2sequence.py @@ -0,0 +1,80 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + +im2seq_counter = 0 + + +def im2sequence(op, block): + global im2sequence_counter + n, c, h, w = block.var(op.input('X')[0]).shape + assert h > 0 and w > 0, "Only supported fixed input shape for im2sequence operator." + stride_h, stride_w = op.attr('strides') + paddings = op.attr('paddings') + assert op.attr( + 'out_stride' + ) != 1, "Only out_stride==1 is supported for im2sequence operator." + h = h + paddings[0] + paddings[1] + w = w + paddings[1] + paddings[2] + kernel_h, kernel_w = op.attr('kernels') + out_h = 1 + (h - kernel_h + stride_h - 1) // stride_h + out_w = 1 + (w - kernel_w + stride_w - 1) // stride_w + h_steps = list() + for i in range(out_h): + h_steps.append([i * stride_h, i * stride_h + kernel_h]) + w_steps = list() + for i in range(out_w): + w_steps.append([i * stride_w, i * stride_w + kernel_w]) + + nodes = list() + slice_blocks = list() + for i in range(out_h): + for j in range(out_w): + starts_name = "im2sequence.starts.{}.{}.{}".format(im2seq_counter, + i, j) + starts_tensor = helper.make_tensor( + name=starts_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[0, 0, h_steps[i][0], w_steps[j][0]]) + ends_name = "im2sequence.ends.{}.{}.{}".format(im2seq_counter, i, j) + ends_tensor = helper.make_tensor( + name=ends_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[999999, 999999, h_steps[i][1], w_steps[j][1]]) + starts_node = helper.make_node( + 'Constant', + inputs=[], + outputs=[starts_name], + value=starts_tensor) + ends_node = helper.make_node( + 'Constant', inputs=[], outputs=[ends_name], value=ends_tensor) + nodes.extend([starts_node, ends_node]) + + slice_block_name = "im2sequence.slice.{}.{}.{}".format( + im2seq_counter, i, j) + slice_block_node = helper.make_node( + 'Slice', + inputs=[op.input('X')[0], starts_name, ends_name], + outputs=[slice_block_name]) + flatten_block_name = "im2sequence.flatten.{}.{}.{}".format( + im2seq_counter, i, j) + flatten_block_node = helper.make_node( + "Flatten", + inputs=[slice_block_name], + outputs=[flatten_block_name], + axis=0) + nodes.extend([slice_block_node, flatten_block_node]) + slice_blocks.append(flatten_block_name) + concat_block_name = "im2sequence.concat_block.{}".format(im2seq_counter) + # concat_block_node = helper.make_node("Concat", inputs=slice_blocks, outputs=[concat_block_name], axis=0) + concat_block_node = helper.make_node( + "Concat", inputs=slice_blocks, outputs=op.output('Out'), axis=0) + nodes.append(concat_block_node) + print("\n\n==========Importance Notice===========") + print( + "Since im2sequence operator is used in your paddlepaddle model, the translated onnx model only support input data with batch_size=1." + ) + print("======================================\n") + return nodes diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/multiclass_nms.py b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/multiclass_nms.py new file mode 100644 index 0000000..5d30f65 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/multiclass_nms.py @@ -0,0 +1,416 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +import warnings +from onnx import helper, onnx_pb + + +def multiclass_nms(op, block): + """ + Convert the paddle multiclass_nms to onnx op. + This op is get the select boxes from origin boxes. + """ + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + + result_name = outputs['Out'][0] + background = attrs['background_label'] + normalized = attrs['normalized'] + if normalized == False: + warnings.warn( + 'The parameter normalized of multiclass_nms OP of Paddle is False, which has diff with ONNX. \ + Please set normalized=True in multiclass_nms of Paddle') + + #convert the paddle attribute to onnx tensor + name_score_threshold = [outputs['Out'][0] + "@score_threshold"] + name_iou_threshold = [outputs['Out'][0] + "@iou_threshold"] + name_keep_top_k = [outputs['Out'][0] + '@keep_top_k'] + name_keep_top_k_2D = [outputs['Out'][0] + '@keep_top_k_1D'] + + node_score_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_score_threshold, + value=onnx.helper.make_tensor( + name=name_score_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['score_threshold'])])) + + node_iou_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_iou_threshold, + value=onnx.helper.make_tensor( + name=name_iou_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['nms_threshold'])])) + + node_keep_top_k = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k, + value=onnx.helper.make_tensor( + name=name_keep_top_k[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=(), + vals=[np.int64(attrs['keep_top_k'])])) + + node_keep_top_k_2D = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k_2D, + value=onnx.helper.make_tensor( + name=name_keep_top_k_2D[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1, 1], + vals=[np.int64(attrs['keep_top_k'])])) + + # the paddle data format is x1,y1,x2,y2 + kwargs = {'center_point_box': 0} + + name_select_nms = [outputs['Out'][0] + "@select_index"] + node_select_nms= onnx.helper.make_node( + 'NonMaxSuppression', + inputs=inputs['BBoxes'] + inputs['Scores'] + name_keep_top_k +\ + name_iou_threshold + name_score_threshold, + outputs=name_select_nms) + # step 1 nodes select the nms class + node_list = [ + node_score_threshold, node_iou_threshold, node_keep_top_k, + node_keep_top_k_2D, node_select_nms + ] + + # create some const value to use + name_const_value = [result_name+"@const_0", + result_name+"@const_1",\ + result_name+"@const_2",\ + result_name+"@const_-1"] + value_const_value = [0, 1, 2, -1] + for name, value in zip(name_const_value, value_const_value): + node = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=[name], + value=onnx.helper.make_tensor( + name=name + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1], + vals=[value])) + node_list.append(node) + + # Ine this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M + # and the same time, decode the select indices to 1 * D, gather the select_indices + outputs_gather_1 = [result_name + "@gather_1"] + node_gather_1 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_1"], + outputs=outputs_gather_1, + axis=1) + node_list.append(node_gather_1) + + outputs_squeeze_gather_1 = [result_name + "@sequeeze_gather_1"] + node_squeeze_gather_1 = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_1, + outputs=outputs_squeeze_gather_1, + axes=[1]) + node_list.append(node_squeeze_gather_1) + + outputs_gather_2 = [result_name + "@gather_2"] + node_gather_2 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_2"], + outputs=outputs_gather_2, + axis=1) + node_list.append(node_gather_2) + + #slice the class is not 0 + if background == 0: + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_squeeze_gather_1, outputs=outputs_nonzero) + node_list.append(node_nonzero) + else: + name_thresh = [result_name + "@thresh"] + node_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_thresh, + value=onnx.helper.make_tensor( + name=name_thresh[0] + "@const", + data_type=onnx.TensorProto.INT32, + dims=[1], + vals=[-1])) + node_list.append(node_thresh) + + outputs_cast = [result_name + "@cast"] + node_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_squeeze_gather_1, outputs=outputs_cast, to=6) + node_list.append(node_cast) + + outputs_greater = [result_name + "@greater"] + node_greater = onnx.helper.make_node( + 'Greater', + inputs=outputs_cast + name_thresh, + outputs=outputs_greater) + node_list.append(node_greater) + + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_greater, outputs=outputs_nonzero) + node_list.append(node_nonzero) + + outputs_gather_1_nonzero = [result_name + "@gather_1_nonzero"] + node_gather_1_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1 + outputs_nonzero, + outputs=outputs_gather_1_nonzero, + axis=0) + node_list.append(node_gather_1_nonzero) + + outputs_gather_2_nonzero = [result_name + "@gather_2_nonzero"] + node_gather_2_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2 + outputs_nonzero, + outputs=outputs_gather_2_nonzero, + axis=0) + node_list.append(node_gather_2_nonzero) + + # reshape scores N * C * M to (N*C*M) * 1 + outputs_reshape_scores_rank1 = [result_name + "@reshape_scores_rank1"] + node_reshape_scores_rank1 = onnx.helper.make_node( + "Reshape", + inputs=inputs['Scores'] + [result_name + "@const_-1"], + outputs=outputs_reshape_scores_rank1) + node_list.append(node_reshape_scores_rank1) + + # get the shape of scores + outputs_shape_scores = [result_name + "@shape_scores"] + node_shape_scores = onnx.helper.make_node( + 'Shape', inputs=inputs['Scores'], outputs=outputs_shape_scores) + node_list.append(node_shape_scores) + + # gather the index: 2 shape of scores + outputs_gather_scores_dim1 = [result_name + "@gather_scores_dim1"] + node_gather_scores_dim1 = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_scores + [result_name + "@const_2"], + outputs=outputs_gather_scores_dim1, + axis=0) + node_list.append(node_gather_scores_dim1) + + # mul class * M + outputs_mul_classnum_boxnum = [result_name + "@mul_classnum_boxnum"] + node_mul_classnum_boxnum = onnx.helper.make_node( + 'Mul', + inputs=outputs_gather_1_nonzero + outputs_gather_scores_dim1, + outputs=outputs_mul_classnum_boxnum) + node_list.append(node_mul_classnum_boxnum) + + # add class * M * index + outputs_add_class_M_index = [result_name + "@add_class_M_index"] + node_add_class_M_index = onnx.helper.make_node( + 'Add', + inputs=outputs_mul_classnum_boxnum + outputs_gather_2_nonzero, + outputs=outputs_add_class_M_index) + node_list.append(node_add_class_M_index) + + # Squeeze the indices to 1 dim + outputs_squeeze_select_index = [result_name + "@squeeze_select_index"] + node_squeeze_select_index = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_add_class_M_index, + outputs=outputs_squeeze_select_index, + axes=[0, 2]) + node_list.append(node_squeeze_select_index) + + # gather the data from flatten scores + outputs_gather_select_scores = [result_name + "@gather_select_scores"] + node_gather_select_scores = onnx.helper.make_node('Gather', + inputs=outputs_reshape_scores_rank1 + \ + outputs_squeeze_select_index, + outputs=outputs_gather_select_scores, + axis=0) + node_list.append(node_gather_select_scores) + + # get nums to input TopK + outputs_shape_select_num = [result_name + "@shape_select_num"] + node_shape_select_num = onnx.helper.make_node( + 'Shape', + inputs=outputs_gather_select_scores, + outputs=outputs_shape_select_num) + node_list.append(node_shape_select_num) + + outputs_gather_select_num = [result_name + "@gather_select_num"] + node_gather_select_num = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_select_num + [result_name + "@const_0"], + outputs=outputs_gather_select_num, + axis=0) + node_list.append(node_gather_select_num) + + outputs_unsqueeze_select_num = [result_name + "@unsqueeze_select_num"] + node_unsqueeze_select_num = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_select_num, + outputs=outputs_unsqueeze_select_num, + axes=[0]) + node_list.append(node_unsqueeze_select_num) + + outputs_concat_topK_select_num = [result_name + "@conat_topK_select_num"] + node_conat_topK_select_num = onnx.helper.make_node( + 'Concat', + inputs=outputs_unsqueeze_select_num + name_keep_top_k_2D, + outputs=outputs_concat_topK_select_num, + axis=0) + node_list.append(node_conat_topK_select_num) + + outputs_cast_concat_topK_select_num = [ + result_name + "@concat_topK_select_num" + ] + node_outputs_cast_concat_topK_select_num = onnx.helper.make_node( + 'Cast', + inputs=outputs_concat_topK_select_num, + outputs=outputs_cast_concat_topK_select_num, + to=6) + node_list.append(node_outputs_cast_concat_topK_select_num) + # get min(topK, num_select) + outputs_compare_topk_num_select = [result_name + "@compare_topk_num_select"] + node_compare_topk_num_select = onnx.helper.make_node( + 'ReduceMin', + inputs=outputs_cast_concat_topK_select_num, + outputs=outputs_compare_topk_num_select, + keepdims=0) + node_list.append(node_compare_topk_num_select) + + # unsqueeze the indices to 1D tensor + outputs_unsqueeze_topk_select_indices = [ + result_name + "@unsqueeze_topk_select_indices" + ] + node_unsqueeze_topk_select_indices = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_compare_topk_num_select, + outputs=outputs_unsqueeze_topk_select_indices, + axes=[0]) + node_list.append(node_unsqueeze_topk_select_indices) + + # cast the indices to INT64 + outputs_cast_topk_indices = [result_name + "@cast_topk_indices"] + node_cast_topk_indices = onnx.helper.make_node( + 'Cast', + inputs=outputs_unsqueeze_topk_select_indices, + outputs=outputs_cast_topk_indices, + to=7) + node_list.append(node_cast_topk_indices) + + # select topk scores indices + outputs_topk_select_topk_indices = [result_name + "@topk_select_topk_values",\ + result_name + "@topk_select_topk_indices"] + node_topk_select_topk_indices = onnx.helper.make_node( + 'TopK', + inputs=outputs_gather_select_scores + outputs_cast_topk_indices, + outputs=outputs_topk_select_topk_indices) + node_list.append(node_topk_select_topk_indices) + + # gather topk label, scores, boxes + outputs_gather_topk_scores = [result_name + "@gather_topk_scores"] + node_gather_topk_scores = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_select_scores + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_scores, + axis=0) + node_list.append(node_gather_topk_scores) + + outputs_gather_topk_class = [result_name + "@gather_topk_class"] + node_gather_topk_class = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_class, + axis=1) + node_list.append(node_gather_topk_class) + + # gather the boxes need to gather the boxes id, then get boxes + outputs_gather_topk_boxes_id = [result_name + "@gather_topk_boxes_id"] + node_gather_topk_boxes_id = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_boxes_id, + axis=1) + node_list.append(node_gather_topk_boxes_id) + + # squeeze the gather_topk_boxes_id to 1 dim + outputs_squeeze_topk_boxes_id = [result_name + "@squeeze_topk_boxes_id"] + node_squeeze_topk_boxes_id = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_topk_boxes_id, + outputs=outputs_squeeze_topk_boxes_id, + axes=[0, 2]) + node_list.append(node_squeeze_topk_boxes_id) + + outputs_gather_select_boxes = [result_name + "@gather_select_boxes"] + node_gather_select_boxes = onnx.helper.make_node( + 'Gather', + inputs=inputs['BBoxes'] + outputs_squeeze_topk_boxes_id, + outputs=outputs_gather_select_boxes, + axis=1) + node_list.append(node_gather_select_boxes) + + # concat the final result + # before concat need to cast the class to float + outputs_cast_topk_class = [result_name + "@cast_topk_class"] + node_cast_topk_class = onnx.helper.make_node( + 'Cast', + inputs=outputs_gather_topk_class, + outputs=outputs_cast_topk_class, + to=1) + node_list.append(node_cast_topk_class) + + outputs_unsqueeze_topk_scores = [result_name + "@unsqueeze_topk_scores"] + node_unsqueeze_topk_scores = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_topk_scores, + outputs=outputs_unsqueeze_topk_scores, + axes=[0, 2]) + node_list.append(node_unsqueeze_topk_scores) + + inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\ + outputs_gather_select_boxes + outputs_concat_final_results = outputs['Out'] + node_concat_final_results = onnx.helper.make_node( + 'Concat', + inputs=inputs_concat_final_results, + outputs=outputs_concat_final_results, + axis=2) + node_list.append(node_concat_final_results) + + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/yolo_box.py b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/yolo_box.py new file mode 100644 index 0000000..a1e49e7 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset10/paddle_custom_layer/yolo_box.py @@ -0,0 +1,822 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + + +def get_old_name(arg, name_prefix=''): + prefix_index = arg.find(name_prefix) + + if prefix_index != -1: + last_prefix = arg[len(name_prefix):] + else: + last_prefix = arg + idx = last_prefix.find('@') + if idx != -1: + last_prefix = last_prefix[:idx] + return name_prefix + last_prefix + + +def yolo_box(op, block): + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + model_name = outputs['Boxes'][0] + input_shape = block.vars[get_old_name(inputs['X'][0])].shape + image_size = inputs['ImgSize'] + input_height = input_shape[2] + input_width = input_shape[3] + + class_num = attrs['class_num'] + anchors = attrs['anchors'] + num_anchors = int(len(anchors)) // 2 + downsample_ratio = attrs['downsample_ratio'] + input_size = input_height * downsample_ratio + conf_thresh = attrs['conf_thresh'] + conf_thresh_mat = np.ones([num_anchors * input_height * + input_width]) * conf_thresh + + node_list = [] + im_outputs = [] + + x_shape = [1, num_anchors, 5 + class_num, input_height, input_width] + name_x_shape = [model_name + "@x_shape"] + node_x_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_x_shape, + value=onnx.helper.make_tensor( + name=name_x_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[5], + vals=x_shape)) + node_list.append(node_x_shape) + + outputs_x_reshape = [model_name + "@reshape"] + node_x_reshape = onnx.helper.make_node( + 'Reshape', inputs=inputs['X'] + name_x_shape, outputs=outputs_x_reshape) + node_list.append(node_x_reshape) + + outputs_x_transpose = [model_name + "@x_transpose"] + node_x_transpose = onnx.helper.make_node( + 'Transpose', + inputs=outputs_x_reshape, + outputs=outputs_x_transpose, + perm=[0, 1, 3, 4, 2]) + node_list.append(node_x_transpose) + + range_x = [] + range_y = [] + for i in range(0, input_width): + range_x.append(i) + for j in range(0, input_height): + range_y.append(j) + + name_range_x = [model_name + "@range_x"] + node_range_x = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x, + value=onnx.helper.make_tensor( + name=name_range_x[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_width], + vals=range_x)) + node_list.append(node_range_x) + + name_range_y = [model_name + "@range_y"] + node_range_y = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y, + value=onnx.helper.make_tensor( + name=name_range_y[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_height], + vals=range_y)) + node_list.append(node_range_y) + + range_x_new_shape = [1, input_width] + range_y_new_shape = [input_height, 1] + + name_range_x_new_shape = [model_name + "@range_x_new_shape"] + node_range_x_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x_new_shape, + value=onnx.helper.make_tensor( + name=name_range_x_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_x_new_shape)], + vals=range_x_new_shape)) + node_list.append(node_range_x_new_shape) + + name_range_y_new_shape = [model_name + "@range_y_new_shape"] + node_range_y_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y_new_shape, + value=onnx.helper.make_tensor( + name=name_range_y_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_y_new_shape)], + vals=range_y_new_shape)) + node_list.append(node_range_y_new_shape) + + outputs_range_x_reshape = [model_name + "@range_x_reshape"] + node_range_x_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_x + name_range_x_new_shape, + outputs=outputs_range_x_reshape) + node_list.append(node_range_x_reshape) + + outputs_range_y_reshape = [model_name + "@range_y_reshape"] + node_range_y_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_y + name_range_y_new_shape, + outputs=outputs_range_y_reshape) + node_list.append(node_range_y_reshape) + + outputs_grid_x = [model_name + "@grid_x"] + node_grid_x = onnx.helper.make_node( + "Tile", + inputs=outputs_range_x_reshape + name_range_y_new_shape, + outputs=outputs_grid_x) + node_list.append(node_grid_x) + + outputs_grid_y = [model_name + "@grid_y"] + node_grid_y = onnx.helper.make_node( + "Tile", + inputs=outputs_range_y_reshape + name_range_x_new_shape, + outputs=outputs_grid_y) + node_list.append(node_grid_y) + + outputs_box_x = [model_name + "@box_x"] + outputs_box_y = [model_name + "@box_y"] + outputs_box_w = [model_name + "@box_w"] + outputs_box_h = [model_name + "@box_h"] + outputs_conf = [model_name + "@conf"] + outputs_prob = [model_name + "@prob"] + + node_split_input = onnx.helper.make_node( + "Split", + inputs=outputs_x_transpose, + outputs=outputs_box_x + outputs_box_y + outputs_box_w\ + + outputs_box_h + outputs_conf + outputs_prob, + axis=-1, + split=[1, 1, 1, 1, 1, class_num]) + node_list.append(node_split_input) + + outputs_box_x_sigmoid = [model_name + "@box_x_sigmoid"] + outputs_box_y_sigmoid = [model_name + "@box_y_sigmoid"] + + node_box_x_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_x, outputs=outputs_box_x_sigmoid) + node_list.append(node_box_x_sigmoid) + + node_box_y_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_y, outputs=outputs_box_y_sigmoid) + node_list.append(node_box_y_sigmoid) + + outputs_box_x_squeeze = [model_name + "@box_x_squeeze"] + outputs_box_y_squeeze = [model_name + "@box_y_squeeze"] + + node_box_x_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_x_sigmoid, + outputs=outputs_box_x_squeeze, + axes=[4]) + node_list.append(node_box_x_squeeze) + + node_box_y_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_y_sigmoid, + outputs=outputs_box_y_squeeze, + axes=[4]) + node_list.append(node_box_y_squeeze) + + outputs_box_x_add_grid = [model_name + "@box_x_add_grid"] + outputs_box_y_add_grid = [model_name + "@box_y_add_grid"] + + node_box_x_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_x + outputs_box_x_squeeze, + outputs=outputs_box_x_add_grid) + node_list.append(node_box_x_add_grid) + + node_box_y_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_y + outputs_box_y_squeeze, + outputs=outputs_box_y_add_grid) + node_list.append(node_box_y_add_grid) + + name_input_h = [model_name + "@input_h"] + name_input_w = [model_name + "@input_w"] + + node_input_h = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_h, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_height])) + node_list.append(node_input_h) + + node_input_w = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_w, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_width])) + node_list.append(node_input_w) + + outputs_box_x_encode = [model_name + "@box_x_encode"] + outputs_box_y_encode = [model_name + "@box_y_encode"] + + node_box_x_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_x_add_grid + name_input_w, + outputs=outputs_box_x_encode) + node_list.append(node_box_x_encode) + + node_box_y_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_y_add_grid + name_input_h, + outputs=outputs_box_y_encode) + node_list.append(node_box_y_encode) + + name_anchor_tensor = [model_name + "@anchor_tensor"] + node_anchor_tensor = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_tensor, + value=onnx.helper.make_tensor( + name=name_anchor_tensor[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[len(anchors)], + vals=anchors)) + node_list.append(node_anchor_tensor) + + anchor_shape = [int(num_anchors), 2] + name_anchor_shape = [model_name + "@anchor_shape"] + node_anchor_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_shape, + value=onnx.helper.make_tensor( + name=name_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[2], + vals=anchor_shape)) + node_list.append(node_anchor_shape) + + outputs_anchor_tensor_reshape = [model_name + "@anchor_tensor_reshape"] + node_anchor_tensor_reshape = onnx.helper.make_node( + "Reshape", + inputs=name_anchor_tensor + name_anchor_shape, + outputs=outputs_anchor_tensor_reshape) + node_list.append(node_anchor_tensor_reshape) + + name_input_size = [model_name + "@input_size"] + node_input_size = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_input_size, + value=onnx.helper.make_tensor( + name=name_input_size[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_size])) + node_list.append(node_input_size) + + outputs_anchors_div_input_size = [model_name + "@anchors_div_input_size"] + node_anchors_div_input_size = onnx.helper.make_node( + "Div", + inputs=outputs_anchor_tensor_reshape + name_input_size, + outputs=outputs_anchors_div_input_size) + node_list.append(node_anchors_div_input_size) + + outputs_anchor_w = [model_name + "@anchor_w"] + outputs_anchor_h = [model_name + "@anchor_h"] + + node_anchor_split = onnx.helper.make_node( + 'Split', + inputs=outputs_anchors_div_input_size, + outputs=outputs_anchor_w + outputs_anchor_h, + axis=1, + split=[1, 1]) + node_list.append(node_anchor_split) + + new_anchor_shape = [1, int(num_anchors), 1, 1] + name_new_anchor_shape = [model_name + "@new_anchor_shape"] + node_new_anchor_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_anchor_shape, + value=onnx.helper.make_tensor( + name=name_new_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_anchor_shape)], + vals=new_anchor_shape)) + node_list.append(node_new_anchor_shape) + + outputs_anchor_w_reshape = [model_name + "@anchor_w_reshape"] + outputs_anchor_h_reshape = [model_name + "@anchor_h_reshape"] + + node_anchor_w_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_w + name_new_anchor_shape, + outputs=outputs_anchor_w_reshape) + node_list.append(node_anchor_w_reshape) + + node_anchor_h_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_h + name_new_anchor_shape, + outputs=outputs_anchor_h_reshape) + node_list.append(node_anchor_h_reshape) + + outputs_box_w_squeeze = [model_name + "@box_w_squeeze"] + node_box_w_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_w, + outputs=outputs_box_w_squeeze, + axes=[4]) + node_list.append(node_box_w_squeeze) + + outputs_box_h_squeeze = [model_name + "@box_h_squeeze"] + node_box_h_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_h, + outputs=outputs_box_h_squeeze, + axes=[4]) + node_list.append(node_box_h_squeeze) + + outputs_box_w_exp = [model_name + "@box_w_exp"] + node_box_w_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_w_squeeze, outputs=outputs_box_w_exp) + node_list.append(node_box_w_exp) + + outputs_box_h_exp = [model_name + "@box_h_exp"] + node_box_h_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_h_squeeze, outputs=outputs_box_h_exp) + node_list.append(node_box_h_exp) + + outputs_box_w_encode = [model_name + "box_w_encode"] + outputs_box_h_encode = [model_name + "box_h_encode"] + + node_box_w_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_w_exp + outputs_anchor_w_reshape, + outputs=outputs_box_w_encode) + node_list.append(node_box_w_encode) + + node_box_h_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_h_exp + outputs_anchor_h_reshape, + outputs=outputs_box_h_encode) + node_list.append(node_box_h_encode) + + outputs_conf_sigmoid = [model_name + "@conf_sigmoid"] + node_conf_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_conf, outputs=outputs_conf_sigmoid) + node_list.append(node_conf_sigmoid) + + name_conf_thresh = [model_name + "@conf_thresh"] + node_conf_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_thresh, + value=onnx.helper.make_tensor( + name=name_conf_thresh[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[num_anchors * input_height * input_width], + vals=conf_thresh_mat)) + node_list.append(node_conf_thresh) + + conf_shape = [1, int(num_anchors), input_height, input_width, 1] + name_conf_shape = [model_name + "@conf_shape"] + node_conf_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_shape, + value=onnx.helper.make_tensor( + name=name_conf_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(conf_shape)], + vals=conf_shape)) + node_list.append(node_conf_shape) + + outputs_conf_thresh_reshape = [model_name + "@conf_thresh_reshape"] + node_conf_thresh_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_conf_thresh + name_conf_shape, + outputs=outputs_conf_thresh_reshape) + node_list.append(node_conf_thresh_reshape) + + outputs_conf_sub = [model_name + "@conf_sub"] + node_conf_sub = onnx.helper.make_node( + 'Sub', + inputs=outputs_conf_sigmoid + outputs_conf_thresh_reshape, + outputs=outputs_conf_sub) + node_list.append(node_conf_sub) + + outputs_conf_clip = [model_name + "@conf_clip"] + node_conf_clip = onnx.helper.make_node( + 'Clip', inputs=outputs_conf_sub, outputs=outputs_conf_clip) + node_list.append(node_conf_clip) + + zeros = [0] + name_zeros = [model_name + "@zeros"] + node_zeros = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_zeros, + value=onnx.helper.make_tensor( + name=name_zeros[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=zeros)) + node_list.append(node_zeros) + + outputs_conf_clip_bool = [model_name + "@conf_clip_bool"] + node_conf_clip_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_clip + name_zeros, + outputs=outputs_conf_clip_bool) + node_list.append(node_conf_clip_bool) + + outputs_conf_clip_cast = [model_name + "@conf_clip_cast"] + node_conf_clip_cast = onnx.helper.make_node( + 'Cast', + inputs=outputs_conf_clip_bool, + outputs=outputs_conf_clip_cast, + to=1) + node_list.append(node_conf_clip_cast) + + outputs_conf_set_zero = [model_name + "@conf_set_zero"] + node_conf_set_zero = onnx.helper.make_node( + 'Mul', + inputs=outputs_conf_sigmoid + outputs_conf_clip_cast, + outputs=outputs_conf_set_zero) + node_list.append(node_conf_set_zero) + + outputs_prob_sigmoid = [model_name + "@prob_sigmoid"] + node_prob_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_prob, outputs=outputs_prob_sigmoid) + node_list.append(node_prob_sigmoid) + + new_shape = [1, int(num_anchors), input_height, input_width, 1] + name_new_shape = [model_name + "@new_shape"] + node_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_shape, + value=onnx.helper.make_tensor( + name=name_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_shape)], + vals=new_shape)) + node_list.append(node_new_shape) + + outputs_conf_new_shape = [model_name + "@_conf_new_shape"] + node_conf_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_conf_set_zero + name_new_shape, + outputs=outputs_conf_new_shape) + node_list.append(node_conf_new_shape) + + outputs_score = [model_name + "@score"] + node_score = onnx.helper.make_node( + 'Mul', + inputs=outputs_prob_sigmoid + outputs_conf_new_shape, + outputs=outputs_score) + node_list.append(node_score) + + outputs_conf_bool = [model_name + "@conf_bool"] + node_conf_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_new_shape + name_zeros, + outputs=outputs_conf_bool) + node_list.append(node_conf_bool) + + outputs_box_x_new_shape = [model_name + "@box_x_new_shape"] + node_box_x_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_x_encode + name_new_shape, + outputs=outputs_box_x_new_shape) + node_list.append(node_box_x_new_shape) + + outputs_box_y_new_shape = [model_name + "@box_y_new_shape"] + node_box_y_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_y_encode + name_new_shape, + outputs=outputs_box_y_new_shape) + node_list.append(node_box_y_new_shape) + + outputs_box_w_new_shape = [model_name + "@box_w_new_shape"] + node_box_w_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_w_encode + name_new_shape, + outputs=outputs_box_w_new_shape) + node_list.append(node_box_w_new_shape) + + outputs_box_h_new_shape = [model_name + "@box_h_new_shape"] + node_box_h_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_h_encode + name_new_shape, + outputs=outputs_box_h_new_shape) + node_list.append(node_box_h_new_shape) + + outputs_pred_box = [model_name + "@pred_box"] + node_pred_box = onnx.helper.make_node( + 'Concat', + inputs=outputs_box_x_new_shape + outputs_box_y_new_shape + \ + outputs_box_w_new_shape + outputs_box_h_new_shape, + outputs=outputs_pred_box, + axis=4) + node_list.append(node_pred_box) + + outputs_conf_cast = [model_name + "conf_cast"] + node_conf_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_conf_bool, outputs=outputs_conf_cast, to=1) + node_list.append(node_conf_cast) + + outputs_pred_box_mul_conf = [model_name + "@pred_box_mul_conf"] + node_pred_box_mul_conf = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box + outputs_conf_cast, + outputs=outputs_pred_box_mul_conf) + node_list.append(node_pred_box_mul_conf) + + box_shape = [1, int(num_anchors) * input_height * input_width, 4] + name_box_shape = [model_name + "@box_shape"] + node_box_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_box_shape, + value=onnx.helper.make_tensor( + name=name_box_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(box_shape)], + vals=box_shape)) + node_list.append(node_box_shape) + + outputs_pred_box_new_shape = [model_name + "@pred_box_new_shape"] + node_pred_box_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_pred_box_mul_conf + name_box_shape, + outputs=outputs_pred_box_new_shape) + node_list.append(node_pred_box_new_shape) + + outputs_pred_box_x = [model_name + "@_pred_box_x"] + outputs_pred_box_y = [model_name + "@_pred_box_y"] + outputs_pred_box_w = [model_name + "@_pred_box_w"] + outputs_pred_box_h = [model_name + "@_pred_box_h"] + + node_pred_box_split = onnx.helper.make_node( + 'Split', + inputs=outputs_pred_box_new_shape, + outputs=outputs_pred_box_x + outputs_pred_box_y + outputs_pred_box_w + + outputs_pred_box_h, + axis=2) + node_list.append(node_pred_box_split) + + name_number_two = [model_name + "@number_two"] + node_number_two = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_number_two, + value=onnx.helper.make_tensor( + name=name_number_two[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[2])) + node_list.append(node_number_two) + + outputs_half_w = [model_name + "@half_w"] + node_half_w = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_w + name_number_two, + outputs=outputs_half_w) + node_list.append(node_half_w) + + outputs_half_h = [model_name + "@half_h"] + node_half_h = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_h + name_number_two, + outputs=outputs_half_h) + node_list.append(node_half_h) + + outputs_pred_box_x1 = [model_name + "@pred_box_x1"] + node_pred_box_x1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x1) + node_list.append(node_pred_box_x1) + + outputs_pred_box_y1 = [model_name + "@pred_box_y1"] + node_pred_box_y1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y1) + node_list.append(node_pred_box_y1) + + outputs_pred_box_x2 = [model_name + "@pred_box_x2"] + node_pred_box_x2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x2) + node_list.append(node_pred_box_x2) + + outputs_pred_box_y2 = [model_name + "@pred_box_y2"] + node_pred_box_y2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y2) + node_list.append(node_pred_box_y2) + + outputs_sqeeze_image_size = [model_name + "@sqeeze_image_size"] + node_sqeeze_image_size = onnx.helper.make_node( + "Squeeze", + axes=[0], + inputs=image_size, + outputs=outputs_sqeeze_image_size) + node_list.append(node_sqeeze_image_size) + + output_img_height = [model_name + "@img_height"] + output_img_width = [model_name + "@img_width"] + node_image_size_split = onnx.helper.make_node( + "Split", + inputs=outputs_sqeeze_image_size, + outputs=output_img_height + output_img_width, + axis=-1, + split=[1, 1]) + node_list.append(node_image_size_split) + + output_img_width_cast = [model_name + "@img_width_cast"] + node_img_width_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_width, outputs=output_img_width_cast, to=1) + node_list.append(node_img_width_cast) + + output_img_height_cast = [model_name + "@img_height_cast"] + node_img_height_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_height, outputs=output_img_height_cast, to=1) + node_list.append(node_img_height_cast) + + outputs_pred_box_x1_decode = [model_name + "@pred_box_x1_decode"] + outputs_pred_box_y1_decode = [model_name + "@pred_box_y1_decode"] + outputs_pred_box_x2_decode = [model_name + "@pred_box_x2_decode"] + outputs_pred_box_y2_decode = [model_name + "@pred_box_y2_decode"] + + node_pred_box_x1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x1 + output_img_width_cast, + outputs=outputs_pred_box_x1_decode) + node_list.append(node_pred_box_x1_decode) + + node_pred_box_y1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y1 + output_img_height_cast, + outputs=outputs_pred_box_y1_decode) + node_list.append(node_pred_box_y1_decode) + + node_pred_box_x2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x2 + output_img_width_cast, + outputs=outputs_pred_box_x2_decode) + node_list.append(node_pred_box_x2_decode) + + node_pred_box_y2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y2 + output_img_height_cast, + outputs=outputs_pred_box_y2_decode) + node_list.append(node_pred_box_y2_decode) + + name_number_one = [model_name + "@one"] + node_number_one = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_number_one, + value=onnx.helper.make_tensor( + name=name_number_one[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[1])) + node_list.append(node_number_one) + + output_new_img_height = [model_name + "@new_img_height"] + node_new_img_height = onnx.helper.make_node( + 'Sub', + inputs=output_img_height_cast + name_number_one, + outputs=output_new_img_height) + node_list.append(node_new_img_height) + + output_new_img_width = [model_name + "@new_img_width"] + node_new_img_width = onnx.helper.make_node( + 'Sub', + inputs=output_img_width_cast + name_number_one, + outputs=output_new_img_width) + node_list.append(node_new_img_width) + + outputs_pred_box_x2_sub_w = [model_name + "@pred_box_x2_sub_w"] + node_pred_box_x2_sub_w = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + output_new_img_width, + outputs=outputs_pred_box_x2_sub_w) + node_list.append(node_pred_box_x2_sub_w) + + outputs_pred_box_y2_sub_h = [model_name + "@pred_box_y2_sub_h"] + node_pred_box_y2_sub_h = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + output_new_img_height, + outputs=outputs_pred_box_y2_sub_h) + node_list.append(node_pred_box_y2_sub_h) + + outputs_pred_box_x1_clip = [model_name + "@pred_box_x1_clip"] + outputs_pred_box_y1_clip = [model_name + "@pred_box_y1_clip"] + outputs_pred_box_x2_clip = [model_name + "@pred_box_x2_clip"] + outputs_pred_box_y2_clip = [model_name + "@pred_box_y2_clip"] + + node_pred_box_x1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x1_decode, + outputs=outputs_pred_box_x1_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_x1_clip) + + node_pred_box_y1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y1_decode, + outputs=outputs_pred_box_y1_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_y1_clip) + + node_pred_box_x2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x2_sub_w, + outputs=outputs_pred_box_x2_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_x2_clip) + + node_pred_box_y2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y2_sub_h, + outputs=outputs_pred_box_y2_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_y2_clip) + + outputs_pred_box_x2_res = [model_name + "@box_x2_res"] + node_pred_box_x2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + outputs_pred_box_x2_clip, + outputs=outputs_pred_box_x2_res) + node_list.append(node_pred_box_x2_res) + + outputs_pred_box_y2_res = [model_name + "@box_y2_res"] + node_pred_box_y2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + outputs_pred_box_y2_clip, + outputs=outputs_pred_box_y2_res) + node_list.append(node_pred_box_y2_res) + + node_pred_box_result = onnx.helper.make_node( + 'Concat', + inputs=outputs_pred_box_x1_clip + outputs_pred_box_y1_clip + + outputs_pred_box_x2_res + outputs_pred_box_y2_res, + outputs=outputs['Boxes'], + axis=-1) + node_list.append(node_pred_box_result) + + score_shape = [1, input_height * input_width * int(num_anchors), class_num] + name_score_shape = [model_name + "@score_shape"] + node_score_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_score_shape, + value=onnx.helper.make_tensor( + name=name_score_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(score_shape)], + vals=score_shape)) + node_list.append(node_score_shape) + + node_score_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_score + name_score_shape, + outputs=outputs['Scores']) + node_list.append(node_score_new_shape) + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset11/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/opset.py b/x2paddle/op_mapper/paddle2onnx/opset11/opset.py new file mode 100644 index 0000000..7bca0c0 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset11/opset.py @@ -0,0 +1,249 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import x2paddle +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +from onnx import helper, onnx_pb +from x2paddle.op_mapper.paddle2onnx.opset10.opset import OpSet10 + + +class OpSet11(OpSet10): + def __init__(self): + super(OpSet11, self).__init__() + + def relu6(self, op, block): + min_name = self.get_name(op.type, 'min') + max_name = self.get_name(op.type, 'max') + min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT, + 0) + max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT, + op.attr('threshold')) + node = helper.make_node( + 'Clip', + inputs=[op.input('X')[0], min_name, max_name], + outputs=op.output('Out'), ) + return [min_node, max_node, node] + + def bilinear_interp(self, op, block): + input_names = op.input_names + coordinate_transformation_mode = '' + align_corners = op.attr('align_corners') + align_mode = op.attr('align_mode') + if align_corners: + coordinate_transformation_mode = 'align_corners' + elif align_mode == 1: + coordinate_transformation_mode = 'asymmetric' + else: + coordinate_transformation_mode = 'half_pixel' + + if ('OutSize' in input_names and len(op.input('OutSize')) > 0) or ( + 'SizeTensor' in input_names and + len(op.input('SizeTensor')) > 0): + node_list = list() + roi_node = self.make_constant_node( + self.get_name(op.type, 'roi'), onnx_pb.TensorProto.FLOAT, + [1, 1, 1, 1, 1, 1, 1, 1]) + roi_name = self.get_name(op.type, 'roi') + roi_node = self.make_constant_node( + roi_name, onnx_pb.TensorProto.FLOAT, [1, 1, 1, 1, 1, 1, 1, 1]) + empty_name = self.get_name(op.type, 'empty') + empty_tensor = helper.make_tensor( + empty_name, + onnx_pb.TensorProto.FLOAT, (0, ), + np.array([]).astype('float32'), + raw=False) + empty_node = helper.make_node( + 'Constant', [], outputs=[empty_name], value=empty_tensor) + shape_name0 = self.get_name(op.type, 'shape') + shape_node0 = helper.make_node( + 'Shape', inputs=op.input('X'), outputs=[shape_name0]) + starts_name = self.get_name(op.type, 'slice.starts') + starts_node = self.make_constant_node( + starts_name, onnx_pb.TensorProto.INT64, [0]) + ends_name = self.get_name(op.type, 'slice.ends') + ends_node = self.make_constant_node(ends_name, + onnx_pb.TensorProto.INT64, [2]) + shape_name1 = self.get_name(op.type, 'shape') + shape_node1 = helper.make_node( + 'Slice', + inputs=[shape_name0, starts_name, ends_name], + outputs=[shape_name1]) + node_list.extend([ + roi_node, empty_node, shape_node0, starts_node, ends_node, + shape_node1 + ]) + if 'OutSize' in input_names and len(op.input('OutSize')) > 0: + cast_shape_name = self.get_name(op.type, "shape.cast") + cast_shape_node = helper.make_node( + 'Cast', + inputs=op.input('OutSize'), + outputs=[cast_shape_name], + to=onnx_pb.TensorProto.INT64) + node_list.append(cast_shape_node) + else: + concat_shape_name = self.get_name(op.type, "shape.concat") + concat_shape_node = helper.make_node( + "Concat", + inputs=op.input('SizeTensor'), + outputs=[concat_shape_name], + axis=0) + cast_shape_name = self.get_name(op.type, "shape.cast") + cast_shape_node = helper.make_node( + 'Cast', + inputs=[concat_shape_name], + outputs=[cast_shape_name], + to=onnx_pb.TensorProto.INT64) + node_list.extend([concat_shape_node, cast_shape_node]) + shape_name3 = self.get_name(op.type, "shape.concat") + shape_node3 = helper.make_node( + 'Concat', + inputs=[shape_name1, cast_shape_name], + outputs=[shape_name3], + axis=0) + result_node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], roi_name, empty_name, shape_name3], + outputs=op.output('Out'), + mode='linear', + coordinate_transformation_mode=coordinate_transformation_mode) + node_list.extend([shape_node3, result_node]) + return node_list + elif 'Scale' in input_names and len(op.input('Scale')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], op.input('Scale')[0]], + outputs=op.output('Out'), + mode='linear', + coordinate_transformation_mode=coordinate_transformation_mode) + else: + out_shape = [op.attr('out_h'), op.attr('out_w')] + scale = op.attr('scale') + if out_shape.count(-1) > 0: + scale_name = self.get_name(op.type, 'scale') + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, scale, scale]) + roi_name = self.get_name(op.type, 'roi') + roi_node = self.make_constant_node(roi_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, 1, 1, 1, 1, 1, 1]) + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], roi_name, scale_name], + outputs=op.output('Out'), + mode='nearest', + coordinate_transformation_mode=coordinate_transformation_mode + ) + return [scale_node, roi_node, node] + else: + raise Exception("Unexpected situation happend") + return node + + def nearest_interp(self, op, block): + input_names = op.input_names + coordinate_transformation_mode = '' + align_corners = op.attr('align_corners') + if align_corners: + coordinate_transformation_mode = 'align_corners' + else: + coordinate_transformation_mode = 'asymmetric' + if 'OutSize' in input_names and len(op.input('OutSize')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], '', op.input('OutSize')[0]], + outputs=op.output('Out'), + mode='nearest', + coordinate_transformation_mode=coordinate_transformation_mode) + elif 'Scale' in input_names and len(op.input('Scale')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], op.input('Scale')[0]], + outputs=op.output('Out'), + mode='nearest', + coordinate_transformation_mode=coordinate_transformation_mode) + else: + out_shape = [op.attr('out_h'), op.attr('out_w')] + scale = op.attr('scale') + if out_shape.count(-1) > 0: + scale_name = self.get_name(op.type, 'scale') + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, scale, scale]) + roi_name = self.get_name(op.type, 'roi') + roi_node = self.make_constant_node(roi_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, 1, 1, 1, 1, 1, 1]) + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], roi_name, scale_name], + outputs=op.output('Out'), + mode='nearest', + coordinate_transformation_mode=coordinate_transformation_mode + ) + return [scale_node, roi_node, node] + else: + raise Exception("Unexpected situation happend") + return node + + def hard_swish(self, op, block): + min_name = self.get_name(op.type, 'min') + max_name = self.get_name(op.type, 'max') + scale_name = self.get_name(op.type, 'scale') + offset_name = self.get_name(op.type, 'offset') + min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT, + 0) + max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT, + op.attr('threshold')) + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + op.attr('scale')) + offset_node = self.make_constant_node(offset_name, + onnx_pb.TensorProto.FLOAT, + op.attr('offset')) + + name0 = self.get_name(op.type, 'add') + node0 = helper.make_node( + 'Add', inputs=[op.input('X')[0], offset_name], outputs=[name0]) + name1 = self.get_name(op.type, 'relu') + node1 = helper.make_node( + 'Clip', + inputs=[name0, min_name, max_name], + outputs=[name1], ) + name2 = self.get_name(op.type, 'mul') + node2 = helper.make_node( + 'Mul', inputs=[op.input('X')[0], name1], outputs=[name2]) + node3 = helper.make_node( + 'Div', inputs=[name2, scale_name], outputs=op.output('Out')) + return [ + min_node, max_node, scale_node, offset_node, node0, node1, node2, + node3 + ] + + def im2sequence(self, op, block): + from .paddle_custom_layer.im2sequence import im2sequence + return im2sequence(op, block) + + def yolo_box(self, op, block): + from .paddle_custom_layer.yolo_box import yolo_box + return yolo_box(op, block) + + def multiclass_nms(self, op, block): + from .paddle_custom_layer.multiclass_nms import multiclass_nms + return multiclass_nms(op, block) diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/im2sequence.py b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/im2sequence.py new file mode 100644 index 0000000..aeb4a9c --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/im2sequence.py @@ -0,0 +1,80 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + +im2seq_counter = 0 + + +def im2sequence(op, block): + global im2sequence_counter + n, c, h, w = block.var(op.input('X')[0]).shape + assert h > 0 and w > 0, "Only supported fixed input shape for im2sequence operator." + stride_h, stride_w = op.attr('strides') + paddings = op.attr('paddings') + assert op.attr( + 'out_stride' + ) != 1, "Only out_stride==1 is supported for im2sequence operator." + h = h + paddings[0] + paddings[1] + w = w + paddings[1] + paddings[2] + kernel_h, kernel_w = op.attr('kernels') + out_h = 1 + (h - kernel_h + stride_h - 1) // stride_h + out_w = 1 + (w - kernel_w + stride_w - 1) // stride_w + h_steps = list() + for i in range(out_h): + h_steps.append([i * stride_h, i * stride_h + kernel_h]) + w_steps = list() + for i in range(out_w): + w_steps.append([i * stride_w, i * stride_w + kernel_w]) + + nodes = list() + slice_blocks = list() + for i in range(out_h): + for j in range(out_w): + starts_name = "im2sequence.starts.{}.{}.{}".format(im2seq_counter, + i, j) + starts_tensor = helper.make_tensor( + name=starts_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[0, 0, h_steps[i][0], w_steps[j][0]]) + ends_name = "im2sequence.ends.{}.{}.{}".format(im2seq_counter, i, j) + ends_tensor = helper.make_tensor( + name=ends_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[999999, 999999, h_steps[i][1], w_steps[j][1]]) + starts_node = helper.make_node( + 'Constant', + inputs=[], + outputs=[starts_name], + value=starts_tensor) + ends_node = helper.make_node( + 'Constant', inputs=[], outputs=[ends_name], value=ends_tensor) + nodes.extend([starts_node, ends_node]) + + slice_block_name = "im2sequence.slice.{}.{}.{}".format( + im2seq_counter, i, j) + slice_block_node = helper.make_node( + 'Slice', + inputs=[op.input('X')[0], starts_name, ends_name], + outputs=[slice_block_name]) + flatten_block_name = "im2sequence.flatten.{}.{}.{}".format( + im2seq_counter, i, j) + flatten_block_node = helper.make_node( + "Flatten", + inputs=[slice_block_name], + outputs=[flatten_block_name], + axis=0) + nodes.extend([slice_block_node, flatten_block_node]) + slice_blocks.append(flatten_block_name) + concat_block_name = "im2sequence.concat_block.{}".format(im2seq_counter) + # concat_block_node = helper.make_node("Concat", inputs=slice_blocks, outputs=[concat_block_name], axis=0) + concat_block_node = helper.make_node( + "Concat", inputs=slice_blocks, outputs=op.output('Out'), axis=0) + nodes.append(concat_block_node) + print("\n\n==========Importance Notice===========") + print( + "Since im2sequence operator is used in your paddlepaddle model, the translated onnx model only support input data with batch_size=1." + ) + print("======================================\n") + return nodes diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/multiclass_nms.py b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/multiclass_nms.py new file mode 100644 index 0000000..5d30f65 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/multiclass_nms.py @@ -0,0 +1,416 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +import warnings +from onnx import helper, onnx_pb + + +def multiclass_nms(op, block): + """ + Convert the paddle multiclass_nms to onnx op. + This op is get the select boxes from origin boxes. + """ + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + + result_name = outputs['Out'][0] + background = attrs['background_label'] + normalized = attrs['normalized'] + if normalized == False: + warnings.warn( + 'The parameter normalized of multiclass_nms OP of Paddle is False, which has diff with ONNX. \ + Please set normalized=True in multiclass_nms of Paddle') + + #convert the paddle attribute to onnx tensor + name_score_threshold = [outputs['Out'][0] + "@score_threshold"] + name_iou_threshold = [outputs['Out'][0] + "@iou_threshold"] + name_keep_top_k = [outputs['Out'][0] + '@keep_top_k'] + name_keep_top_k_2D = [outputs['Out'][0] + '@keep_top_k_1D'] + + node_score_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_score_threshold, + value=onnx.helper.make_tensor( + name=name_score_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['score_threshold'])])) + + node_iou_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_iou_threshold, + value=onnx.helper.make_tensor( + name=name_iou_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['nms_threshold'])])) + + node_keep_top_k = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k, + value=onnx.helper.make_tensor( + name=name_keep_top_k[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=(), + vals=[np.int64(attrs['keep_top_k'])])) + + node_keep_top_k_2D = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k_2D, + value=onnx.helper.make_tensor( + name=name_keep_top_k_2D[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1, 1], + vals=[np.int64(attrs['keep_top_k'])])) + + # the paddle data format is x1,y1,x2,y2 + kwargs = {'center_point_box': 0} + + name_select_nms = [outputs['Out'][0] + "@select_index"] + node_select_nms= onnx.helper.make_node( + 'NonMaxSuppression', + inputs=inputs['BBoxes'] + inputs['Scores'] + name_keep_top_k +\ + name_iou_threshold + name_score_threshold, + outputs=name_select_nms) + # step 1 nodes select the nms class + node_list = [ + node_score_threshold, node_iou_threshold, node_keep_top_k, + node_keep_top_k_2D, node_select_nms + ] + + # create some const value to use + name_const_value = [result_name+"@const_0", + result_name+"@const_1",\ + result_name+"@const_2",\ + result_name+"@const_-1"] + value_const_value = [0, 1, 2, -1] + for name, value in zip(name_const_value, value_const_value): + node = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=[name], + value=onnx.helper.make_tensor( + name=name + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1], + vals=[value])) + node_list.append(node) + + # Ine this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M + # and the same time, decode the select indices to 1 * D, gather the select_indices + outputs_gather_1 = [result_name + "@gather_1"] + node_gather_1 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_1"], + outputs=outputs_gather_1, + axis=1) + node_list.append(node_gather_1) + + outputs_squeeze_gather_1 = [result_name + "@sequeeze_gather_1"] + node_squeeze_gather_1 = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_1, + outputs=outputs_squeeze_gather_1, + axes=[1]) + node_list.append(node_squeeze_gather_1) + + outputs_gather_2 = [result_name + "@gather_2"] + node_gather_2 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_2"], + outputs=outputs_gather_2, + axis=1) + node_list.append(node_gather_2) + + #slice the class is not 0 + if background == 0: + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_squeeze_gather_1, outputs=outputs_nonzero) + node_list.append(node_nonzero) + else: + name_thresh = [result_name + "@thresh"] + node_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_thresh, + value=onnx.helper.make_tensor( + name=name_thresh[0] + "@const", + data_type=onnx.TensorProto.INT32, + dims=[1], + vals=[-1])) + node_list.append(node_thresh) + + outputs_cast = [result_name + "@cast"] + node_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_squeeze_gather_1, outputs=outputs_cast, to=6) + node_list.append(node_cast) + + outputs_greater = [result_name + "@greater"] + node_greater = onnx.helper.make_node( + 'Greater', + inputs=outputs_cast + name_thresh, + outputs=outputs_greater) + node_list.append(node_greater) + + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_greater, outputs=outputs_nonzero) + node_list.append(node_nonzero) + + outputs_gather_1_nonzero = [result_name + "@gather_1_nonzero"] + node_gather_1_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1 + outputs_nonzero, + outputs=outputs_gather_1_nonzero, + axis=0) + node_list.append(node_gather_1_nonzero) + + outputs_gather_2_nonzero = [result_name + "@gather_2_nonzero"] + node_gather_2_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2 + outputs_nonzero, + outputs=outputs_gather_2_nonzero, + axis=0) + node_list.append(node_gather_2_nonzero) + + # reshape scores N * C * M to (N*C*M) * 1 + outputs_reshape_scores_rank1 = [result_name + "@reshape_scores_rank1"] + node_reshape_scores_rank1 = onnx.helper.make_node( + "Reshape", + inputs=inputs['Scores'] + [result_name + "@const_-1"], + outputs=outputs_reshape_scores_rank1) + node_list.append(node_reshape_scores_rank1) + + # get the shape of scores + outputs_shape_scores = [result_name + "@shape_scores"] + node_shape_scores = onnx.helper.make_node( + 'Shape', inputs=inputs['Scores'], outputs=outputs_shape_scores) + node_list.append(node_shape_scores) + + # gather the index: 2 shape of scores + outputs_gather_scores_dim1 = [result_name + "@gather_scores_dim1"] + node_gather_scores_dim1 = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_scores + [result_name + "@const_2"], + outputs=outputs_gather_scores_dim1, + axis=0) + node_list.append(node_gather_scores_dim1) + + # mul class * M + outputs_mul_classnum_boxnum = [result_name + "@mul_classnum_boxnum"] + node_mul_classnum_boxnum = onnx.helper.make_node( + 'Mul', + inputs=outputs_gather_1_nonzero + outputs_gather_scores_dim1, + outputs=outputs_mul_classnum_boxnum) + node_list.append(node_mul_classnum_boxnum) + + # add class * M * index + outputs_add_class_M_index = [result_name + "@add_class_M_index"] + node_add_class_M_index = onnx.helper.make_node( + 'Add', + inputs=outputs_mul_classnum_boxnum + outputs_gather_2_nonzero, + outputs=outputs_add_class_M_index) + node_list.append(node_add_class_M_index) + + # Squeeze the indices to 1 dim + outputs_squeeze_select_index = [result_name + "@squeeze_select_index"] + node_squeeze_select_index = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_add_class_M_index, + outputs=outputs_squeeze_select_index, + axes=[0, 2]) + node_list.append(node_squeeze_select_index) + + # gather the data from flatten scores + outputs_gather_select_scores = [result_name + "@gather_select_scores"] + node_gather_select_scores = onnx.helper.make_node('Gather', + inputs=outputs_reshape_scores_rank1 + \ + outputs_squeeze_select_index, + outputs=outputs_gather_select_scores, + axis=0) + node_list.append(node_gather_select_scores) + + # get nums to input TopK + outputs_shape_select_num = [result_name + "@shape_select_num"] + node_shape_select_num = onnx.helper.make_node( + 'Shape', + inputs=outputs_gather_select_scores, + outputs=outputs_shape_select_num) + node_list.append(node_shape_select_num) + + outputs_gather_select_num = [result_name + "@gather_select_num"] + node_gather_select_num = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_select_num + [result_name + "@const_0"], + outputs=outputs_gather_select_num, + axis=0) + node_list.append(node_gather_select_num) + + outputs_unsqueeze_select_num = [result_name + "@unsqueeze_select_num"] + node_unsqueeze_select_num = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_select_num, + outputs=outputs_unsqueeze_select_num, + axes=[0]) + node_list.append(node_unsqueeze_select_num) + + outputs_concat_topK_select_num = [result_name + "@conat_topK_select_num"] + node_conat_topK_select_num = onnx.helper.make_node( + 'Concat', + inputs=outputs_unsqueeze_select_num + name_keep_top_k_2D, + outputs=outputs_concat_topK_select_num, + axis=0) + node_list.append(node_conat_topK_select_num) + + outputs_cast_concat_topK_select_num = [ + result_name + "@concat_topK_select_num" + ] + node_outputs_cast_concat_topK_select_num = onnx.helper.make_node( + 'Cast', + inputs=outputs_concat_topK_select_num, + outputs=outputs_cast_concat_topK_select_num, + to=6) + node_list.append(node_outputs_cast_concat_topK_select_num) + # get min(topK, num_select) + outputs_compare_topk_num_select = [result_name + "@compare_topk_num_select"] + node_compare_topk_num_select = onnx.helper.make_node( + 'ReduceMin', + inputs=outputs_cast_concat_topK_select_num, + outputs=outputs_compare_topk_num_select, + keepdims=0) + node_list.append(node_compare_topk_num_select) + + # unsqueeze the indices to 1D tensor + outputs_unsqueeze_topk_select_indices = [ + result_name + "@unsqueeze_topk_select_indices" + ] + node_unsqueeze_topk_select_indices = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_compare_topk_num_select, + outputs=outputs_unsqueeze_topk_select_indices, + axes=[0]) + node_list.append(node_unsqueeze_topk_select_indices) + + # cast the indices to INT64 + outputs_cast_topk_indices = [result_name + "@cast_topk_indices"] + node_cast_topk_indices = onnx.helper.make_node( + 'Cast', + inputs=outputs_unsqueeze_topk_select_indices, + outputs=outputs_cast_topk_indices, + to=7) + node_list.append(node_cast_topk_indices) + + # select topk scores indices + outputs_topk_select_topk_indices = [result_name + "@topk_select_topk_values",\ + result_name + "@topk_select_topk_indices"] + node_topk_select_topk_indices = onnx.helper.make_node( + 'TopK', + inputs=outputs_gather_select_scores + outputs_cast_topk_indices, + outputs=outputs_topk_select_topk_indices) + node_list.append(node_topk_select_topk_indices) + + # gather topk label, scores, boxes + outputs_gather_topk_scores = [result_name + "@gather_topk_scores"] + node_gather_topk_scores = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_select_scores + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_scores, + axis=0) + node_list.append(node_gather_topk_scores) + + outputs_gather_topk_class = [result_name + "@gather_topk_class"] + node_gather_topk_class = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_class, + axis=1) + node_list.append(node_gather_topk_class) + + # gather the boxes need to gather the boxes id, then get boxes + outputs_gather_topk_boxes_id = [result_name + "@gather_topk_boxes_id"] + node_gather_topk_boxes_id = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_boxes_id, + axis=1) + node_list.append(node_gather_topk_boxes_id) + + # squeeze the gather_topk_boxes_id to 1 dim + outputs_squeeze_topk_boxes_id = [result_name + "@squeeze_topk_boxes_id"] + node_squeeze_topk_boxes_id = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_topk_boxes_id, + outputs=outputs_squeeze_topk_boxes_id, + axes=[0, 2]) + node_list.append(node_squeeze_topk_boxes_id) + + outputs_gather_select_boxes = [result_name + "@gather_select_boxes"] + node_gather_select_boxes = onnx.helper.make_node( + 'Gather', + inputs=inputs['BBoxes'] + outputs_squeeze_topk_boxes_id, + outputs=outputs_gather_select_boxes, + axis=1) + node_list.append(node_gather_select_boxes) + + # concat the final result + # before concat need to cast the class to float + outputs_cast_topk_class = [result_name + "@cast_topk_class"] + node_cast_topk_class = onnx.helper.make_node( + 'Cast', + inputs=outputs_gather_topk_class, + outputs=outputs_cast_topk_class, + to=1) + node_list.append(node_cast_topk_class) + + outputs_unsqueeze_topk_scores = [result_name + "@unsqueeze_topk_scores"] + node_unsqueeze_topk_scores = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_topk_scores, + outputs=outputs_unsqueeze_topk_scores, + axes=[0, 2]) + node_list.append(node_unsqueeze_topk_scores) + + inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\ + outputs_gather_select_boxes + outputs_concat_final_results = outputs['Out'] + node_concat_final_results = onnx.helper.make_node( + 'Concat', + inputs=inputs_concat_final_results, + outputs=outputs_concat_final_results, + axis=2) + node_list.append(node_concat_final_results) + + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/yolo_box.py b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/yolo_box.py new file mode 100644 index 0000000..b9375c2 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset11/paddle_custom_layer/yolo_box.py @@ -0,0 +1,841 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + +MAX_FLOAT = np.asarray([255, 255, 127, 127], dtype=np.uint8).view(np.float32)[0] + + +def get_old_name(arg, name_prefix=''): + prefix_index = arg.find(name_prefix) + + if prefix_index != -1: + last_prefix = arg[len(name_prefix):] + else: + last_prefix = arg + idx = last_prefix.find('@') + if idx != -1: + last_prefix = last_prefix[:idx] + return name_prefix + last_prefix + + +def yolo_box(op, block): + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + model_name = outputs['Boxes'][0] + input_shape = block.vars[get_old_name(inputs['X'][0])].shape + image_size = inputs['ImgSize'] + input_height = input_shape[2] + input_width = input_shape[3] + + class_num = attrs['class_num'] + anchors = attrs['anchors'] + num_anchors = int(len(anchors)) // 2 + downsample_ratio = attrs['downsample_ratio'] + input_size = input_height * downsample_ratio + conf_thresh = attrs['conf_thresh'] + conf_thresh_mat = np.ones([num_anchors * input_height * + input_width]) * conf_thresh + + node_list = [] + im_outputs = [] + + x_shape = [1, num_anchors, 5 + class_num, input_height, input_width] + name_x_shape = [model_name + "@x_shape"] + node_x_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_x_shape, + value=onnx.helper.make_tensor( + name=name_x_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[5], + vals=x_shape)) + node_list.append(node_x_shape) + + outputs_x_reshape = [model_name + "@reshape"] + node_x_reshape = onnx.helper.make_node( + 'Reshape', inputs=inputs['X'] + name_x_shape, outputs=outputs_x_reshape) + node_list.append(node_x_reshape) + + outputs_x_transpose = [model_name + "@x_transpose"] + node_x_transpose = onnx.helper.make_node( + 'Transpose', + inputs=outputs_x_reshape, + outputs=outputs_x_transpose, + perm=[0, 1, 3, 4, 2]) + node_list.append(node_x_transpose) + + range_x = [] + range_y = [] + for i in range(0, input_width): + range_x.append(i) + for j in range(0, input_height): + range_y.append(j) + + name_range_x = [model_name + "@range_x"] + node_range_x = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x, + value=onnx.helper.make_tensor( + name=name_range_x[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_width], + vals=range_x)) + node_list.append(node_range_x) + + name_range_y = [model_name + "@range_y"] + node_range_y = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y, + value=onnx.helper.make_tensor( + name=name_range_y[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_height], + vals=range_y)) + node_list.append(node_range_y) + + range_x_new_shape = [1, input_width] + range_y_new_shape = [input_height, 1] + + name_range_x_new_shape = [model_name + "@range_x_new_shape"] + node_range_x_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x_new_shape, + value=onnx.helper.make_tensor( + name=name_range_x_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_x_new_shape)], + vals=range_x_new_shape)) + node_list.append(node_range_x_new_shape) + + name_range_y_new_shape = [model_name + "@range_y_new_shape"] + node_range_y_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y_new_shape, + value=onnx.helper.make_tensor( + name=name_range_y_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_y_new_shape)], + vals=range_y_new_shape)) + node_list.append(node_range_y_new_shape) + + outputs_range_x_reshape = [model_name + "@range_x_reshape"] + node_range_x_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_x + name_range_x_new_shape, + outputs=outputs_range_x_reshape) + node_list.append(node_range_x_reshape) + + outputs_range_y_reshape = [model_name + "@range_y_reshape"] + node_range_y_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_y + name_range_y_new_shape, + outputs=outputs_range_y_reshape) + node_list.append(node_range_y_reshape) + + outputs_grid_x = [model_name + "@grid_x"] + node_grid_x = onnx.helper.make_node( + "Tile", + inputs=outputs_range_x_reshape + name_range_y_new_shape, + outputs=outputs_grid_x) + node_list.append(node_grid_x) + + outputs_grid_y = [model_name + "@grid_y"] + node_grid_y = onnx.helper.make_node( + "Tile", + inputs=outputs_range_y_reshape + name_range_x_new_shape, + outputs=outputs_grid_y) + node_list.append(node_grid_y) + + outputs_box_x = [model_name + "@box_x"] + outputs_box_y = [model_name + "@box_y"] + outputs_box_w = [model_name + "@box_w"] + outputs_box_h = [model_name + "@box_h"] + outputs_conf = [model_name + "@conf"] + outputs_prob = [model_name + "@prob"] + + node_split_input = onnx.helper.make_node( + "Split", + inputs=outputs_x_transpose, + outputs=outputs_box_x + outputs_box_y + outputs_box_w\ + + outputs_box_h + outputs_conf + outputs_prob, + axis=-1, + split=[1, 1, 1, 1, 1, class_num]) + node_list.append(node_split_input) + + outputs_box_x_sigmoid = [model_name + "@box_x_sigmoid"] + outputs_box_y_sigmoid = [model_name + "@box_y_sigmoid"] + + node_box_x_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_x, outputs=outputs_box_x_sigmoid) + node_list.append(node_box_x_sigmoid) + + node_box_y_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_y, outputs=outputs_box_y_sigmoid) + node_list.append(node_box_y_sigmoid) + + outputs_box_x_squeeze = [model_name + "@box_x_squeeze"] + outputs_box_y_squeeze = [model_name + "@box_y_squeeze"] + + node_box_x_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_x_sigmoid, + outputs=outputs_box_x_squeeze, + axes=[4]) + node_list.append(node_box_x_squeeze) + + node_box_y_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_y_sigmoid, + outputs=outputs_box_y_squeeze, + axes=[4]) + node_list.append(node_box_y_squeeze) + + outputs_box_x_add_grid = [model_name + "@box_x_add_grid"] + outputs_box_y_add_grid = [model_name + "@box_y_add_grid"] + + node_box_x_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_x + outputs_box_x_squeeze, + outputs=outputs_box_x_add_grid) + node_list.append(node_box_x_add_grid) + + node_box_y_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_y + outputs_box_y_squeeze, + outputs=outputs_box_y_add_grid) + node_list.append(node_box_y_add_grid) + + name_input_h = [model_name + "@input_h"] + name_input_w = [model_name + "@input_w"] + + node_input_h = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_h, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_height])) + node_list.append(node_input_h) + + node_input_w = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_w, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_width])) + node_list.append(node_input_w) + + outputs_box_x_encode = [model_name + "@box_x_encode"] + outputs_box_y_encode = [model_name + "@box_y_encode"] + + node_box_x_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_x_add_grid + name_input_w, + outputs=outputs_box_x_encode) + node_list.append(node_box_x_encode) + + node_box_y_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_y_add_grid + name_input_h, + outputs=outputs_box_y_encode) + node_list.append(node_box_y_encode) + + name_anchor_tensor = [model_name + "@anchor_tensor"] + node_anchor_tensor = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_tensor, + value=onnx.helper.make_tensor( + name=name_anchor_tensor[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[len(anchors)], + vals=anchors)) + node_list.append(node_anchor_tensor) + + anchor_shape = [int(num_anchors), 2] + name_anchor_shape = [model_name + "@anchor_shape"] + node_anchor_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_shape, + value=onnx.helper.make_tensor( + name=name_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[2], + vals=anchor_shape)) + node_list.append(node_anchor_shape) + + outputs_anchor_tensor_reshape = [model_name + "@anchor_tensor_reshape"] + node_anchor_tensor_reshape = onnx.helper.make_node( + "Reshape", + inputs=name_anchor_tensor + name_anchor_shape, + outputs=outputs_anchor_tensor_reshape) + node_list.append(node_anchor_tensor_reshape) + + name_input_size = [model_name + "@input_size"] + node_input_size = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_input_size, + value=onnx.helper.make_tensor( + name=name_input_size[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_size])) + node_list.append(node_input_size) + + outputs_anchors_div_input_size = [model_name + "@anchors_div_input_size"] + node_anchors_div_input_size = onnx.helper.make_node( + "Div", + inputs=outputs_anchor_tensor_reshape + name_input_size, + outputs=outputs_anchors_div_input_size) + node_list.append(node_anchors_div_input_size) + + outputs_anchor_w = [model_name + "@anchor_w"] + outputs_anchor_h = [model_name + "@anchor_h"] + + node_anchor_split = onnx.helper.make_node( + 'Split', + inputs=outputs_anchors_div_input_size, + outputs=outputs_anchor_w + outputs_anchor_h, + axis=1, + split=[1, 1]) + node_list.append(node_anchor_split) + + new_anchor_shape = [1, int(num_anchors), 1, 1] + name_new_anchor_shape = [model_name + "@new_anchor_shape"] + node_new_anchor_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_anchor_shape, + value=onnx.helper.make_tensor( + name=name_new_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_anchor_shape)], + vals=new_anchor_shape)) + node_list.append(node_new_anchor_shape) + + outputs_anchor_w_reshape = [model_name + "@anchor_w_reshape"] + outputs_anchor_h_reshape = [model_name + "@anchor_h_reshape"] + + node_anchor_w_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_w + name_new_anchor_shape, + outputs=outputs_anchor_w_reshape) + node_list.append(node_anchor_w_reshape) + + node_anchor_h_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_h + name_new_anchor_shape, + outputs=outputs_anchor_h_reshape) + node_list.append(node_anchor_h_reshape) + + outputs_box_w_squeeze = [model_name + "@box_w_squeeze"] + node_box_w_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_w, + outputs=outputs_box_w_squeeze, + axes=[4]) + node_list.append(node_box_w_squeeze) + + outputs_box_h_squeeze = [model_name + "@box_h_squeeze"] + node_box_h_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_h, + outputs=outputs_box_h_squeeze, + axes=[4]) + node_list.append(node_box_h_squeeze) + + outputs_box_w_exp = [model_name + "@box_w_exp"] + node_box_w_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_w_squeeze, outputs=outputs_box_w_exp) + node_list.append(node_box_w_exp) + + outputs_box_h_exp = [model_name + "@box_h_exp"] + node_box_h_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_h_squeeze, outputs=outputs_box_h_exp) + node_list.append(node_box_h_exp) + + outputs_box_w_encode = [model_name + "box_w_encode"] + outputs_box_h_encode = [model_name + "box_h_encode"] + + node_box_w_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_w_exp + outputs_anchor_w_reshape, + outputs=outputs_box_w_encode) + node_list.append(node_box_w_encode) + + node_box_h_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_h_exp + outputs_anchor_h_reshape, + outputs=outputs_box_h_encode) + node_list.append(node_box_h_encode) + + outputs_conf_sigmoid = [model_name + "@conf_sigmoid"] + node_conf_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_conf, outputs=outputs_conf_sigmoid) + node_list.append(node_conf_sigmoid) + + name_conf_thresh = [model_name + "@conf_thresh"] + node_conf_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_thresh, + value=onnx.helper.make_tensor( + name=name_conf_thresh[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[num_anchors * input_height * input_width], + vals=conf_thresh_mat)) + node_list.append(node_conf_thresh) + + conf_shape = [1, int(num_anchors), input_height, input_width, 1] + name_conf_shape = [model_name + "@conf_shape"] + node_conf_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_shape, + value=onnx.helper.make_tensor( + name=name_conf_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(conf_shape)], + vals=conf_shape)) + node_list.append(node_conf_shape) + + outputs_conf_thresh_reshape = [model_name + "@conf_thresh_reshape"] + node_conf_thresh_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_conf_thresh + name_conf_shape, + outputs=outputs_conf_thresh_reshape) + node_list.append(node_conf_thresh_reshape) + + outputs_conf_sub = [model_name + "@conf_sub"] + node_conf_sub = onnx.helper.make_node( + 'Sub', + inputs=outputs_conf_sigmoid + outputs_conf_thresh_reshape, + outputs=outputs_conf_sub) + node_list.append(node_conf_sub) + + outputs_conf_clip = [model_name + "@conf_clip"] + node_conf_clip = onnx.helper.make_node( + 'Clip', inputs=outputs_conf_sub, outputs=outputs_conf_clip) + node_list.append(node_conf_clip) + + zeros = [0] + name_zeros = [model_name + "@zeros"] + node_zeros = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_zeros, + value=onnx.helper.make_tensor( + name=name_zeros[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=zeros)) + node_list.append(node_zeros) + + outputs_conf_clip_bool = [model_name + "@conf_clip_bool"] + node_conf_clip_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_clip + name_zeros, + outputs=outputs_conf_clip_bool) + node_list.append(node_conf_clip_bool) + + outputs_conf_clip_cast = [model_name + "@conf_clip_cast"] + node_conf_clip_cast = onnx.helper.make_node( + 'Cast', + inputs=outputs_conf_clip_bool, + outputs=outputs_conf_clip_cast, + to=1) + node_list.append(node_conf_clip_cast) + + outputs_conf_set_zero = [model_name + "@conf_set_zero"] + node_conf_set_zero = onnx.helper.make_node( + 'Mul', + inputs=outputs_conf_sigmoid + outputs_conf_clip_cast, + outputs=outputs_conf_set_zero) + node_list.append(node_conf_set_zero) + + outputs_prob_sigmoid = [model_name + "@prob_sigmoid"] + node_prob_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_prob, outputs=outputs_prob_sigmoid) + node_list.append(node_prob_sigmoid) + + new_shape = [1, int(num_anchors), input_height, input_width, 1] + name_new_shape = [model_name + "@new_shape"] + node_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_shape, + value=onnx.helper.make_tensor( + name=name_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_shape)], + vals=new_shape)) + node_list.append(node_new_shape) + + outputs_conf_new_shape = [model_name + "@_conf_new_shape"] + node_conf_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_conf_set_zero + name_new_shape, + outputs=outputs_conf_new_shape) + node_list.append(node_conf_new_shape) + + outputs_score = [model_name + "@score"] + node_score = onnx.helper.make_node( + 'Mul', + inputs=outputs_prob_sigmoid + outputs_conf_new_shape, + outputs=outputs_score) + node_list.append(node_score) + + outputs_conf_bool = [model_name + "@conf_bool"] + node_conf_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_new_shape + name_zeros, + outputs=outputs_conf_bool) + node_list.append(node_conf_bool) + + outputs_box_x_new_shape = [model_name + "@box_x_new_shape"] + node_box_x_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_x_encode + name_new_shape, + outputs=outputs_box_x_new_shape) + node_list.append(node_box_x_new_shape) + + outputs_box_y_new_shape = [model_name + "@box_y_new_shape"] + node_box_y_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_y_encode + name_new_shape, + outputs=outputs_box_y_new_shape) + node_list.append(node_box_y_new_shape) + + outputs_box_w_new_shape = [model_name + "@box_w_new_shape"] + node_box_w_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_w_encode + name_new_shape, + outputs=outputs_box_w_new_shape) + node_list.append(node_box_w_new_shape) + + outputs_box_h_new_shape = [model_name + "@box_h_new_shape"] + node_box_h_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_h_encode + name_new_shape, + outputs=outputs_box_h_new_shape) + node_list.append(node_box_h_new_shape) + + outputs_pred_box = [model_name + "@pred_box"] + node_pred_box = onnx.helper.make_node( + 'Concat', + inputs=outputs_box_x_new_shape + outputs_box_y_new_shape + \ + outputs_box_w_new_shape + outputs_box_h_new_shape, + outputs=outputs_pred_box, + axis=4) + node_list.append(node_pred_box) + + outputs_conf_cast = [model_name + "conf_cast"] + node_conf_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_conf_bool, outputs=outputs_conf_cast, to=1) + node_list.append(node_conf_cast) + + outputs_pred_box_mul_conf = [model_name + "@pred_box_mul_conf"] + node_pred_box_mul_conf = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box + outputs_conf_cast, + outputs=outputs_pred_box_mul_conf) + node_list.append(node_pred_box_mul_conf) + + box_shape = [1, int(num_anchors) * input_height * input_width, 4] + name_box_shape = [model_name + "@box_shape"] + node_box_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_box_shape, + value=onnx.helper.make_tensor( + name=name_box_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(box_shape)], + vals=box_shape)) + node_list.append(node_box_shape) + + outputs_pred_box_new_shape = [model_name + "@pred_box_new_shape"] + node_pred_box_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_pred_box_mul_conf + name_box_shape, + outputs=outputs_pred_box_new_shape) + node_list.append(node_pred_box_new_shape) + + outputs_pred_box_x = [model_name + "@_pred_box_x"] + outputs_pred_box_y = [model_name + "@_pred_box_y"] + outputs_pred_box_w = [model_name + "@_pred_box_w"] + outputs_pred_box_h = [model_name + "@_pred_box_h"] + + node_pred_box_split = onnx.helper.make_node( + 'Split', + inputs=outputs_pred_box_new_shape, + outputs=outputs_pred_box_x + outputs_pred_box_y + outputs_pred_box_w + + outputs_pred_box_h, + axis=2) + node_list.append(node_pred_box_split) + + name_number_two = [model_name + "@number_two"] + node_number_two = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_number_two, + value=onnx.helper.make_tensor( + name=name_number_two[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[2])) + node_list.append(node_number_two) + + outputs_half_w = [model_name + "@half_w"] + node_half_w = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_w + name_number_two, + outputs=outputs_half_w) + node_list.append(node_half_w) + + outputs_half_h = [model_name + "@half_h"] + node_half_h = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_h + name_number_two, + outputs=outputs_half_h) + node_list.append(node_half_h) + + outputs_pred_box_x1 = [model_name + "@pred_box_x1"] + node_pred_box_x1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x1) + node_list.append(node_pred_box_x1) + + outputs_pred_box_y1 = [model_name + "@pred_box_y1"] + node_pred_box_y1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y1) + node_list.append(node_pred_box_y1) + + outputs_pred_box_x2 = [model_name + "@pred_box_x2"] + node_pred_box_x2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x2) + node_list.append(node_pred_box_x2) + + outputs_pred_box_y2 = [model_name + "@pred_box_y2"] + node_pred_box_y2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y2) + node_list.append(node_pred_box_y2) + + outputs_sqeeze_image_size = [model_name + "@sqeeze_image_size"] + node_sqeeze_image_size = onnx.helper.make_node( + "Squeeze", + axes=[0], + inputs=image_size, + outputs=outputs_sqeeze_image_size) + node_list.append(node_sqeeze_image_size) + + output_img_height = [model_name + "@img_height"] + output_img_width = [model_name + "@img_width"] + node_image_size_split = onnx.helper.make_node( + "Split", + inputs=outputs_sqeeze_image_size, + outputs=output_img_height + output_img_width, + axis=-1, + split=[1, 1]) + node_list.append(node_image_size_split) + + output_img_width_cast = [model_name + "@img_width_cast"] + node_img_width_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_width, outputs=output_img_width_cast, to=1) + node_list.append(node_img_width_cast) + + output_img_height_cast = [model_name + "@img_height_cast"] + node_img_height_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_height, outputs=output_img_height_cast, to=1) + node_list.append(node_img_height_cast) + + outputs_pred_box_x1_decode = [model_name + "@pred_box_x1_decode"] + outputs_pred_box_y1_decode = [model_name + "@pred_box_y1_decode"] + outputs_pred_box_x2_decode = [model_name + "@pred_box_x2_decode"] + outputs_pred_box_y2_decode = [model_name + "@pred_box_y2_decode"] + + node_pred_box_x1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x1 + output_img_width_cast, + outputs=outputs_pred_box_x1_decode) + node_list.append(node_pred_box_x1_decode) + + node_pred_box_y1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y1 + output_img_height_cast, + outputs=outputs_pred_box_y1_decode) + node_list.append(node_pred_box_y1_decode) + + node_pred_box_x2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x2 + output_img_width_cast, + outputs=outputs_pred_box_x2_decode) + node_list.append(node_pred_box_x2_decode) + + node_pred_box_y2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y2 + output_img_height_cast, + outputs=outputs_pred_box_y2_decode) + node_list.append(node_pred_box_y2_decode) + + name_number_one = [model_name + "@one"] + node_number_one = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_number_one, + value=onnx.helper.make_tensor( + name=name_number_one[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[1])) + node_list.append(node_number_one) + + output_new_img_height = [model_name + "@new_img_height"] + node_new_img_height = onnx.helper.make_node( + 'Sub', + inputs=output_img_height_cast + name_number_one, + outputs=output_new_img_height) + node_list.append(node_new_img_height) + + output_new_img_width = [model_name + "@new_img_width"] + node_new_img_width = onnx.helper.make_node( + 'Sub', + inputs=output_img_width_cast + name_number_one, + outputs=output_new_img_width) + node_list.append(node_new_img_width) + + outputs_pred_box_x2_sub_w = [model_name + "@pred_box_x2_sub_w"] + node_pred_box_x2_sub_w = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + output_new_img_width, + outputs=outputs_pred_box_x2_sub_w) + node_list.append(node_pred_box_x2_sub_w) + + outputs_pred_box_y2_sub_h = [model_name + "@pred_box_y2_sub_h"] + node_pred_box_y2_sub_h = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + output_new_img_height, + outputs=outputs_pred_box_y2_sub_h) + node_list.append(node_pred_box_y2_sub_h) + + outputs_pred_box_x1_clip = [model_name + "@pred_box_x1_clip"] + outputs_pred_box_y1_clip = [model_name + "@pred_box_y1_clip"] + outputs_pred_box_x2_clip = [model_name + "@pred_box_x2_clip"] + outputs_pred_box_y2_clip = [model_name + "@pred_box_y2_clip"] + + min_const_name = model_name + "@pred_box_min_const" + max_const_name = model_name + "@pred_box_max_const" + + min_const = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=[min_const_name], + value=onnx.helper.make_tensor( + name=min_const_name, + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[0.0])) + node_list.append(min_const) + + max_const = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=[max_const_name], + value=onnx.helper.make_tensor( + name=max_const_name, + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[MAX_FLOAT])) + node_list.append(max_const) + + node_pred_box_x1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x1_decode + [min_const_name, max_const_name], + outputs=outputs_pred_box_x1_clip) + node_list.append(node_pred_box_x1_clip) + + node_pred_box_y1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y1_decode + [min_const_name, max_const_name], + outputs=outputs_pred_box_y1_clip) + node_list.append(node_pred_box_y1_clip) + + node_pred_box_x2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x2_sub_w + [min_const_name, max_const_name], + outputs=outputs_pred_box_x2_clip) + node_list.append(node_pred_box_x2_clip) + + node_pred_box_y2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y2_sub_h + [min_const_name, max_const_name], + outputs=outputs_pred_box_y2_clip) + node_list.append(node_pred_box_y2_clip) + + outputs_pred_box_x2_res = [model_name + "@box_x2_res"] + node_pred_box_x2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + outputs_pred_box_x2_clip, + outputs=outputs_pred_box_x2_res) + node_list.append(node_pred_box_x2_res) + + outputs_pred_box_y2_res = [model_name + "@box_y2_res"] + node_pred_box_y2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + outputs_pred_box_y2_clip, + outputs=outputs_pred_box_y2_res) + node_list.append(node_pred_box_y2_res) + + node_pred_box_result = onnx.helper.make_node( + 'Concat', + inputs=outputs_pred_box_x1_clip + outputs_pred_box_y1_clip + + outputs_pred_box_x2_res + outputs_pred_box_y2_res, + outputs=outputs['Boxes'], + axis=-1) + node_list.append(node_pred_box_result) + + score_shape = [1, input_height * input_width * int(num_anchors), class_num] + name_score_shape = [model_name + "@score_shape"] + node_score_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_score_shape, + value=onnx.helper.make_tensor( + name=name_score_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(score_shape)], + vals=score_shape)) + node_list.append(node_score_shape) + + node_score_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_score + name_score_shape, + outputs=outputs['Scores']) + node_list.append(node_score_new_shape) + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset9/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/opset.py b/x2paddle/op_mapper/paddle2onnx/opset9/opset.py new file mode 100644 index 0000000..fbab7db --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset9/opset.py @@ -0,0 +1,746 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import x2paddle +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +from onnx import helper, onnx_pb + + +class OpSet9(object): + def __init__(self): + self.paddle_onnx_dtype_map = { + core.VarDesc.VarType.FP32: onnx_pb.TensorProto.FLOAT, + core.VarDesc.VarType.FP64: onnx_pb.TensorProto.DOUBLE, + core.VarDesc.VarType.INT32: onnx_pb.TensorProto.INT32, + core.VarDesc.VarType.INT16: onnx_pb.TensorProto.INT16, + core.VarDesc.VarType.INT16: onnx_pb.TensorProto.UINT16, + core.VarDesc.VarType.INT64: onnx_pb.TensorProto.INT64, + core.VarDesc.VarType.BOOL: onnx_pb.TensorProto.BOOL + } + self.name_counter = dict() + + def get_name(self, op_name, var_name): + name = 'p2o.{}.{}'.format(op_name, var_name) + if name not in self.name_counter: + self.name_counter[name] = 0 + else: + self.name_counter[name] += 1 + return name + '.{}'.format(self.name_counter[name]) + + def make_constant_node(self, name, dtype, value=None): + if isinstance(value, list): + dims = (len(value), ) + elif value is None: + dims = () + value = [] + else: + dims = () + value = [value] + tensor = helper.make_tensor( + name=name, data_type=dtype, dims=dims, vals=value) + node = helper.make_node( + 'Constant', inputs=[], outputs=[name], value=tensor) + return node + + def convert_weights(self, program): + var_names = program.global_block().vars + nodes = list() + for name in var_names: + var = program.global_block().var(name) + if name.endswith('feed') or name.endswith('fetch'): + continue + if not var.persistable: + continue + weight = np.array(fluid.global_scope().find_var(name).get_tensor()) + tensor = helper.make_tensor( + name=name, + dims=var.shape, + data_type=self.paddle_onnx_dtype_map[var.dtype], + vals=weight.flatten().tolist()) + node = helper.make_node( + 'Constant', inputs=[], outputs=[name], value=tensor) + nodes.append(node) + return nodes + + def conv2d(self, op, block): + kernel_shape = block.var(op.input('Filter')[0]).shape + node = helper.make_node( + 'Conv', + inputs=op.input('Input') + op.input('Filter'), + outputs=op.output('Output'), + dilations=op.attr('dilations'), + kernel_shape=kernel_shape[-2:], + strides=op.attr('strides'), + group=op.attr('groups'), + pads=op.attr('paddings') + op.attr('paddings')) + return node + + def conv2d_transpose(self, op, block): + kernel_shape = block.var(op.input('Filter')[0]).shape + node = helper.make_node( + 'ConvTranspose', + inputs=op.input('Input') + op.input('Filter'), + outputs=op.output('Output'), + dilations=op.attr('dilations'), + kernel_shape=kernel_shape[-2:], + strides=op.attr('strides'), + group=1, + pads=op.attr('paddings') + op.attr('paddings')) + return node + + def relu(self, op, block): + node = helper.make_node( + 'Relu', inputs=op.input('X'), outputs=op.output('Out')) + return node + + def sigmoid(self, op, block): + node = helper.make_node( + 'Sigmoid', inputs=op.input('X'), outputs=op.output('Out')) + return node + + def exp(self, op, block): + node = helper.make_node( + 'Exp', inputs=op.input('X'), outputs=op.output('Out')) + return node + + def leaky_relu(self, op, block): + node = helper.make_node( + 'LeakyRelu', + inputs=op.input('X'), + outputs=op.output('Out'), + alpha=op.attr('alpha')) + return node + + def elementwise_add(self, op, block): + axis = op.attr('axis') + x_shape = block.var(op.input('X')[0]).shape + y_shape = block.var(op.input('Y')[0]).shape + if len(y_shape) == 1 and axis == 1: + shape_name = self.get_name(op.type, 'shape') + shape_value = [1] * len(x_shape) + shape_value[axis] = y_shape[0] + shape_node = self.make_constant_node( + shape_name, onnx_pb.TensorProto.INT64, shape_value) + temp_value = self.get_name(op.type, 'temp') + y_node = helper.make_node( + 'Reshape', + inputs=[op.input('Y')[0], shape_name], + outputs=[temp_value]) + node = helper.make_node( + 'Add', + inputs=[op.input('X')[0], temp_value], + outputs=op.output('Out')) + return [shape_node, y_node, node] + elif len(x_shape) == len(y_shape): + node = helper.make_node( + 'Add', + inputs=[op.input('X')[0], op.input('Y')[0]], + outputs=op.output('Out')) + return node + else: + raise Excpetion("Unexpected situation happend in elementwise_add") + + def elementwise_sub(self, op, block): + axis = op.attr('axis') + x_shape = block.var(op.input('X')[0]).shape + y_shape = block.var(op.input('Y')[0]).shape + if len(y_shape) == 1 and axis == 1: + shape_name = self.get_name(op.type, 'shape') + shape_value = [1] * len(x_shape) + shape_value[axis] = y_shape[0] + shape_node = self.make_constant_node( + shape_name, onnx_pb.TensorProto.INT64, shape_value) + temp_value = self.get_name(op.type, 'temp') + y_node = helper.make_node( + 'Reshape', + inputs=[op.input('Y')[0], shape_name], + outputs=[temp_value]) + node = helper.make_node( + 'Sub', + inputs=[op.input('X')[0], temp_value], + outputs=op.output('Out')) + return [shape_node, y_node, node] + elif len(x_shape) == len(y_shape): + node = helper.make_node( + 'Sub', + inputs=[op.input('X')[0], op.input('Y')[0]], + outputs=op.output('Out')) + return node + else: + raise Excpetion("Unexpected situation happend in elementwise_sub") + + def pool2d(self, op, block): + pool_type = { + 'max': ('MaxPool', 'GlobalMaxPool'), + 'avg': ('AveragePool', 'GlobalAveragePool') + } + if op.attr('global_pooling'): + node = helper.make_node( + pool_type[op.attr('pooling_type')][1], + inputs=op.input('X'), + outputs=op.output('Out'), ) + #elif op.attr('adaptive'): + # k_size = op.attr('ksize') + else: + input_shape = block.var(op.input('X')[0]).shape + k_size = op.attr('ksize') + paddings = op.attr('paddings') + if input_shape[2] > 0 and input_shape[2] + paddings[0] < k_size[0]: + k_size[0] = input_shape[2] + paddings[0] + if input_shape[3] > 0 and input_shape[3] + paddings[1] < k_size[1]: + k_size[1] = input_shape[3] + paddings[1] + node = helper.make_node( + pool_type[op.attr('pooling_type')][0], + inputs=op.input('X'), + outputs=op.output('Out'), + kernel_shape=k_size, + strides=op.attr('strides'), + pads=op.attr('paddings') + op.attr('paddings')) + return node + + def softmax(self, op, block): + axis = op.attr('axis') + shape = block.var(op.output('Out')[0]).shape + if axis < 0: + axis += len(shape) + if axis == len(shape) - 1: + node = helper.make_node( + 'Softmax', + inputs=op.input('X'), + outputs=op.output('Out'), + axis=op.attr('axis')) + return node + else: + perm = [i for i in range(len(shape))] + perm[-1] = axis + perm[axis] = len(shape) - 1 + transpose_name0 = self.get_name(op.type, 'transpose') + transpose_node0 = helper.make_node( + 'Transpose', + inputs=op.input('X'), + outputs=[transpose_name0], + perm=perm) + softmax_name = self.get_name(op.type, 'softmax') + softmax_node = helper.make_node( + 'Softmax', + inputs=[transpose_name0], + outputs=[softmax_name], + axis=-1) + transpose_name1 = self.get_name(op.type, 'transpose') + transpose_node1 = helper.make_node( + 'Transpose', + inputs=[softmax_name], + outputs=op.output('Out'), + perm=perm) + return [transpose_node0, softmax_node, transpose_node1] + + def scale(self, op, block): + scale = op.attr('scale') + bias = op.attr('bias') + if math.fabs(scale - 1.0) < 1e-06 and math.fabs(bias - 0.0) < 1e-06: + node = helper.make_node( + 'Identity', inputs=op.input('X'), outputs=op.output('Out')) + return node + else: + scale_name = self.get_name(op.type, 'scale') + bias_name = self.get_name(op.type, 'bias') + scale_node = self.make_constant_node( + scale_name, onnx_pb.TensorProto.FLOAT, scale) + bias_node = self.make_constant_node(bias_name, + onnx_pb.TensorProto.FLOAT, bias) + temp_tensor_name = self.get_name(op.type, 'temporary') + if op.attr('bias_after_scale'): + node1 = helper.make_node( + 'Mul', + inputs=[scale_name, op.input('X')[0]], + outputs=[temp_tensor_name]) + node2 = helper.make_node( + 'Add', + inputs=[bias_name, temp_tensor_name], + outputs=op.output('Out')) + else: + node1 = helper.make_node( + 'Add', + inputs=[bias_name, op.input('X')[0]], + outputs=temp_tensor_name) + node2 = helper.make_node( + 'Mul', + inputs=[scale_name, temp_tensor_name], + outputs=[op.output('Out')]) + return [scale_node, bias_node, node1, node2] + + def mul(self, op, block): + x_shape = block.var(op.input('X')[0]).shape + y_shape = block.var(op.input('Y')[0]).shape + out_shape = list(block.var(op.output('Out')[0]).shape) + x_num_col_dims = op.attr('x_num_col_dims') + y_num_col_dims = op.attr('y_num_col_dims') + flatten_x_name = 'flatten_{}'.format(op.input('X')[0]) + flatten_y_name = 'flatten_{}'.format(op.input('Y')[0]) + shape_name = 'temp_shape_{}'.format(op.output('Out')[0]) + temp_out_name = 'temp_{}'.format(op.output('Out')[0]) + flatten_x = helper.make_node( + 'Flatten', + inputs=op.input('X'), + outputs=[flatten_x_name], + axis=x_num_col_dims) + flatten_y = helper.make_node( + 'Flatten', + inputs=op.input('Y'), + outputs=[flatten_y_name], + axis=y_num_col_dims) + shape_node = self.make_constant_node( + shape_name, onnx_pb.TensorProto.INT64, out_shape) + node = helper.make_node( + 'MatMul', + inputs=[flatten_x_name, flatten_y_name], + outputs=[temp_out_name]) + reshape_out = helper.make_node( + 'Reshape', + inputs=[temp_out_name, shape_name], + outputs=op.output('Out')) + return [flatten_x, flatten_y, shape_node, node, reshape_out] + + def batch_norm(self, op, block): + kwargs = { + 'epsilon': op.attr('epsilon'), + 'momentum': op.attr('momentum') + } + inputs = op.input('X') + op.input('Scale') + op.input( + 'Bias') + op.input('Mean') + op.input('Variance') + node = helper.make_node( + 'BatchNormalization', + inputs=inputs, + outputs=op.output('Y'), + **kwargs) + return node + + def concat(self, op, block): + node = helper.make_node( + 'Concat', + inputs=op.input('X'), + outputs=op.output('Out'), + axis=op.attr('axis')) + return node + + def depthwise_conv2d(self, op, block): + return self.conv2d(op, block) + + def relu6(self, op, block): + threshold = op.attr('threshold') + node = helper.make_node( + 'Clip', + inputs=[op.input('X')[0]], + outputs=op.output('Out'), + max=threshold, + min=0.0) + return [node] + + def shape(self, op, block): + node = helper.make_node( + 'Shape', inputs=op.input('Input'), outputs=op.output('Out')) + return node + + def split(self, op, block): + sections = op.attr('sections') + if len(sections) > 0: + node = helper.make_node( + 'Split', + inputs=op.input('X'), + outputs=op.output('Out'), + axis=op.attr('axis'), + split=sections) + else: + node = helper.make_node( + 'Split', + inputs=op.input('X'), + outputs=op.output('Out'), + axis=op.attr('axis')) + return node + + def slice(self, op, block): + axes = op.attr('axes') + starts = op.attr('starts') + ends = op.attr('ends') + node = helper.make_node( + "Slice", + inputs=[op.input('Input')[0], starts_name, ends_name, axes_name], + outputs=op.output('Out'), + axes=axes, + starts=starts, + ends=ends) + return [node] + + def fill_constant(self, op, block): + value = op.attr('value') + dtype = op.attr('dtype') + shape = op.attr('shape') + value = np.ones(shape) * value + if dtype == 2: + value = value.astype('int32') + node = helper.make_node( + 'Constant', + inputs=[], + outputs=op.output('Out'), + value=helper.make_tensor( + name=op.output('Out')[0], + data_type=self.paddle_onnx_dtype_map[dtype], + dims=shape, + vals=value.tolist())) + return node + + def transpose2(self, op, block): + node = helper.make_node( + 'Transpose', + inputs=op.input('X'), + outputs=op.output('Out'), + perm=op.attr('axis')) + return node + + def reshape2(self, op, block): + input_names = op.input_names + if len(op.input('ShapeTensor')) > 1: + cast_shape_nodes = list() + cast_shape_names = list() + for i in range(len(op.input('ShapeTensor'))): + dim = op.input('ShapeTensor')[i] + temp_name = self.get_name(op.type, 'shape.cast') + node = helper.make_node( + 'Cast', + inputs=[dim], + outputs=[temp_name], + to=onnx_pb.TensorProto.INT64) + cast_shape_nodes.append(node) + cast_shape_names.append(temp_name) + + temp_name = self.get_name(op.type, 'shape.concat') + shape_node = helper.make_node( + 'Concat', inputs=cast_shape_names, outputs=[temp_name], axis=-1) + node = helper.make_node( + 'Reshape', + inputs=[op.input('X')[0], temp_name], + outputs=op.output('Out')) + return cast_shape_nodes + [shape_node, node] + else: + temp_name = self.get_name(op.type, 'shape.cast') + cast_shape_node = helper.make_node( + 'Cast', + inputs=op.input('ShapeTensor'), + outputs=[temp_name], + to=onnx_pb.TensorProto.INT64) + node = helper.make_node( + 'Reshape', + inputs=[op.input('X')[0], temp_name], + outputs=op.output('Out')) + return [cast_shape_node, node] + + def dropout(self, op, block): + dropout_mode = op.attr('dropout_implementation') + dropout_prob = op.attr('dropout_prob') + if dropout_mode == 'upscale_in_train': + node = helper.make_node( + 'Identity', inputs=op.input('X'), outputs=op.output('Out')) + return node + elif dropout_mode == 'downgrade_in_infer': + scale_name = self.get_name(op.type, 'scale') + scale_node = self.make_constant_node( + scale_name, onnx_pb.TensorProto.FLOAT, 1 - dropout_prob) + node = helper.make_node( + "Mul", + inputs=[op.input('X')[0], scale_name], + outputs=op.output('Out')) + return [scale_node, node] + else: + raise Exception("Unexpected situation happend") + + def reduce_mean(self, op, block): + node = helper.make_node( + 'ReduceMean', + inputs=op.input('X'), + outputs=op.output('Out'), + axes=op.attr('dim'), + keepdims=op.attr('keep_dim')) + return node + + def bilinear_interp(self, op, block): + input_names = op.input_names + input_shape = block.vars[op.input('X')[0]].shape + if op.attr('align_corners') or op.attr('align_mode') == 0: + raise Exception( + "Resize in onnx(opset<=10) only support coordinate_transformation_mode: 'asymmetric'." + ) + if ('OutSize' in input_names and len(op.input('OutSize')) > 0) or ( + 'SizeTensor' in input_names and + len(op.input('SizeTensor')) > 0): + node_list = list() + shape_name0 = self.get_name(op.type, 'shape') + shape_node0 = helper.make_node( + 'Shape', inputs=op.input('X'), outputs=[shape_name0]) + starts_name = self.get_name(op.type, 'slice.starts') + starts_node = self.make_constant_node( + starts_name, onnx_pb.TensorProto.INT64, [0]) + ends_name = self.get_name(op.type, 'slice.ends') + ends_node = self.make_constant_node(ends_name, + onnx_pb.TensorProto.INT64, [2]) + shape_name1 = self.get_name(op.type, 'shape') + shape_node1 = helper.make_node( + 'Slice', + inputs=[shape_name0, starts_name, ends_name], + outputs=[shape_name1]) + node_list.extend([shape_node0, starts_node, ends_node, shape_node1]) + if 'OutSize' in input_names and len(op.input('OutSize')) > 0: + cast_shape_name = self.get_name(op.type, "shape.cast") + cast_shape_node = helper.make_node( + 'Cast', + inputs=op.input('OutSize'), + outputs=[cast_shape_name], + to=onnx_pb.TensorProto.INT64) + node_list.append(cast_shape_node) + else: + concat_shape_name = self.get_name( + op.type, op.output('Out')[0] + "shape.concat") + concat_shape_node = helper.make_node( + "Concat", + inputs=op.input('SizeTensor'), + outputs=[concat_shape_name], + axis=0) + cast_shape_name = self.get_name(op.type, "shape.cast") + cast_shape_node = helper.make_node( + 'Cast', + inputs=[concat_shape_name], + outputs=[cast_shape_name], + to=onnx_pb.TensorProto.INT64) + node_list.extend([concat_shape_node, cast_shape_node]) + shape_name2 = self.get_name(op.type, "shape.concat") + shape_node2 = helper.make_node( + 'Concat', + inputs=[shape_name1, cast_shape_name], + outputs=[shape_name2], + axis=0) + node_list.append(shape_node2) + cast_shape_name2 = self.get_name(op.type, "shape.cast") + cast_shape_node2 = helper.make_node( + 'Cast', + inputs=[shape_name2], + outputs=[cast_shape_name2], + to=onnx_pb.TensorProto.FLOAT) + node_list.append(cast_shape_node2) + cast_shape_name0 = self.get_name(op.type, "shape.cast") + cast_shape_node0 = helper.make_node( + 'Cast', + inputs=[shape_name0], + outputs=[cast_shape_name0], + to=onnx_pb.TensorProto.FLOAT) + node_list.append(cast_shape_node0) + outputs_h_w_scales = op.output('Out')[0] + "@out_hw_scales" + node_h_w_scales = helper.make_node( + 'Div', + inputs=[cast_shape_name2, cast_shape_name0], + outputs=[outputs_h_w_scales]) + node_list.append(node_h_w_scales) + result_node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], outputs_h_w_scales], + outputs=op.output('Out'), + mode='linear') + node_list.extend([result_node]) + return node_list + elif 'Scale' in input_names and len(op.input('Scale')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], op.input('Scale')[0]], + outputs=op.output('Out'), + mode='linear') + else: + out_shape = [op.attr('out_h'), op.attr('out_w')] + scale = op.attr('scale') + if out_shape.count(-1) > 0: + scale_name = self.get_name(op.type, 'scale') + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, scale, scale]) + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], scale_name], + outputs=op.output('Out'), + mode='linear') + return [scale_node, node] + else: + raise Exception("Unexpected situation happend") + return node + + def nearest_interp(self, op, block): + input_names = op.input_names + if op.attr('align_corners'): + raise Exception( + "Resize in onnx(opset<=10) only support coordinate_transformation_mode: 'asymmetric'." + ) + if 'OutSize' in input_names and len(op.input('OutSize')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], op.input('OutSize')[0]], + outputs=op.output('Out'), + mode='nearest') + elif 'Scale' in input_names and len(op.input('Scale')) > 0: + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], op.input('Scale')[0]], + outputs=op.output('Out'), + mode='nearest') + else: + out_shape = [op.attr('out_h'), op.attr('out_w')] + scale = op.attr('scale') + if out_shape.count(-1) > 0: + scale_name = self.get_name(op.type, 'scale') + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + [1, 1, scale, scale]) + node = helper.make_node( + 'Resize', + inputs=[op.input('X')[0], scale_name], + outputs=op.output('Out'), + mode='nearest') + return [scale_node, node] + else: + raise Exception("Unexpected situation happend") + return node + + def hard_sigmoid(self, op, block): + slope = op.attr('slope') + offset = op.attr('offset') + node = helper.make_node( + 'HardSigmoid', + inputs=op.input('X'), + outputs=op.output('Out'), + alpha=slope, + beta=offset) + return node + + def hard_swish(self, op, block): + scale_name = self.get_name(op.type, 'scale') + offset_name = self.get_name(op.type, 'offset') + scale_node = self.make_constant_node(scale_name, + onnx_pb.TensorProto.FLOAT, + op.attr('scale')) + offset_node = self.make_constant_node(offset_name, + onnx_pb.TensorProto.FLOAT, + op.attr('offset')) + + name0 = self.get_name(op.type, 'add') + node0 = helper.make_node( + 'Add', inputs=[op.input('X')[0], offset_name], outputs=[name0]) + name1 = self.get_name(op.type, 'relu') + min_value = op.attr('min') + max_value = op.attr('max') + node1 = helper.make_node( + 'Clip', + inputs=[name0], + outputs=[name1], + max=max_value, + min=min_value) + name2 = self.get_name(op.type, 'mul') + node2 = helper.make_node( + 'Mul', inputs=[op.input('X')[0], name1], outputs=[name2]) + node3 = helper.make_node( + 'Div', inputs=[name2, scale_name], outputs=op.output('Out')) + return [scale_node, offset_node, node0, node1, node2, node3] + + def elementwise_mul(self, op, block): + axis = op.attr('axis') + x_shape = block.var(op.input('X')[0]).shape + y_shape = block.var(op.input('Y')[0]).shape + if len(y_shape) == 1 and axis == 1: + shape_name = self.get_name(op.type, 'shape') + shape_value = [1] * len(x_shape) + shape_value[axis] = y_shape[0] + shape_node = self.make_constant_node( + shape_name, onnx_pb.TensorProto.INT64, shape_value) + temp_value = self.get_name(op.type, 'temp') + y_node = helper.make_node( + 'Reshape', + inputs=[op.input('Y')[0], shape_name], + outputs=[temp_value]) + node = helper.make_node( + 'Mul', + inputs=[op.input('X')[0], temp_value], + outputs=op.output('Out')) + return [shape_node, y_node, node] + elif len(x_shape) == len(y_shape): + node = helper.make_node( + 'Mul', + inputs=[op.input('X')[0], op.input('Y')[0]], + outputs=op.output('Out')) + return node + else: + raise Excpetion("Unexpected situation happend in elementwise_add") + return node + + def feed(self, op, block): + name = op.output('Out')[0] + var = block.var(name) + tensor_info = helper.make_tensor_value_info( + name=name, + shape=var.shape, + elem_type=self.paddle_onnx_dtype_map[var.dtype]) + return tensor_info + + def fetch(self, op, block): + name = op.input('X')[0] + var = block.var(name) + tensor_info = helper.make_tensor_value_info( + name=name, + shape=var.shape, + elem_type=self.paddle_onnx_dtype_map[var.dtype]) + return tensor_info + + def unsqueeze2(self, op, block): + node = helper.make_node( + 'Unsqueeze', + inputs=op.input('X'), + outputs=op.output('Out'), + axes=op.attr('axes')) + return node + + def arg_max(self, op, block): + node = helper.make_node( + 'ArgMax', + inputs=op.input('X'), + outputs=op.output('Out'), + axis=op.attr('axis'), + keepdims=0) + return node + + def reciprocal(self, op, block): + inputs = op.input(op.input_names[0]) + outputs = op.output(op.output_names[0]) + node = helper.make_node('Reciprocal', inputs=inputs, outputs=outputs) + return node + + def im2sequence(self, op, block): + from .paddle_custom_layer.im2sequence import im2sequence + return im2sequence(op, block) + + def yolo_box(self, op, block): + from .paddle_custom_layer.yolo_box import yolo_box + return yolo_box(op, block) + + def multiclass_nms(self, op, block): + from .paddle_custom_layer.multiclass_nms import multiclass_nms + return multiclass_nms(op, block) diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/__init__.py b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/im2sequence.py b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/im2sequence.py new file mode 100644 index 0000000..aeb4a9c --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/im2sequence.py @@ -0,0 +1,80 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + +im2seq_counter = 0 + + +def im2sequence(op, block): + global im2sequence_counter + n, c, h, w = block.var(op.input('X')[0]).shape + assert h > 0 and w > 0, "Only supported fixed input shape for im2sequence operator." + stride_h, stride_w = op.attr('strides') + paddings = op.attr('paddings') + assert op.attr( + 'out_stride' + ) != 1, "Only out_stride==1 is supported for im2sequence operator." + h = h + paddings[0] + paddings[1] + w = w + paddings[1] + paddings[2] + kernel_h, kernel_w = op.attr('kernels') + out_h = 1 + (h - kernel_h + stride_h - 1) // stride_h + out_w = 1 + (w - kernel_w + stride_w - 1) // stride_w + h_steps = list() + for i in range(out_h): + h_steps.append([i * stride_h, i * stride_h + kernel_h]) + w_steps = list() + for i in range(out_w): + w_steps.append([i * stride_w, i * stride_w + kernel_w]) + + nodes = list() + slice_blocks = list() + for i in range(out_h): + for j in range(out_w): + starts_name = "im2sequence.starts.{}.{}.{}".format(im2seq_counter, + i, j) + starts_tensor = helper.make_tensor( + name=starts_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[0, 0, h_steps[i][0], w_steps[j][0]]) + ends_name = "im2sequence.ends.{}.{}.{}".format(im2seq_counter, i, j) + ends_tensor = helper.make_tensor( + name=ends_name, + data_type=onnx_pb.TensorProto.INT64, + dims=[4], + vals=[999999, 999999, h_steps[i][1], w_steps[j][1]]) + starts_node = helper.make_node( + 'Constant', + inputs=[], + outputs=[starts_name], + value=starts_tensor) + ends_node = helper.make_node( + 'Constant', inputs=[], outputs=[ends_name], value=ends_tensor) + nodes.extend([starts_node, ends_node]) + + slice_block_name = "im2sequence.slice.{}.{}.{}".format( + im2seq_counter, i, j) + slice_block_node = helper.make_node( + 'Slice', + inputs=[op.input('X')[0], starts_name, ends_name], + outputs=[slice_block_name]) + flatten_block_name = "im2sequence.flatten.{}.{}.{}".format( + im2seq_counter, i, j) + flatten_block_node = helper.make_node( + "Flatten", + inputs=[slice_block_name], + outputs=[flatten_block_name], + axis=0) + nodes.extend([slice_block_node, flatten_block_node]) + slice_blocks.append(flatten_block_name) + concat_block_name = "im2sequence.concat_block.{}".format(im2seq_counter) + # concat_block_node = helper.make_node("Concat", inputs=slice_blocks, outputs=[concat_block_name], axis=0) + concat_block_node = helper.make_node( + "Concat", inputs=slice_blocks, outputs=op.output('Out'), axis=0) + nodes.append(concat_block_node) + print("\n\n==========Importance Notice===========") + print( + "Since im2sequence operator is used in your paddlepaddle model, the translated onnx model only support input data with batch_size=1." + ) + print("======================================\n") + return nodes diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/multiclass_nms.py b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/multiclass_nms.py new file mode 100644 index 0000000..5d30f65 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/multiclass_nms.py @@ -0,0 +1,416 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +import warnings +from onnx import helper, onnx_pb + + +def multiclass_nms(op, block): + """ + Convert the paddle multiclass_nms to onnx op. + This op is get the select boxes from origin boxes. + """ + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + + result_name = outputs['Out'][0] + background = attrs['background_label'] + normalized = attrs['normalized'] + if normalized == False: + warnings.warn( + 'The parameter normalized of multiclass_nms OP of Paddle is False, which has diff with ONNX. \ + Please set normalized=True in multiclass_nms of Paddle') + + #convert the paddle attribute to onnx tensor + name_score_threshold = [outputs['Out'][0] + "@score_threshold"] + name_iou_threshold = [outputs['Out'][0] + "@iou_threshold"] + name_keep_top_k = [outputs['Out'][0] + '@keep_top_k'] + name_keep_top_k_2D = [outputs['Out'][0] + '@keep_top_k_1D'] + + node_score_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_score_threshold, + value=onnx.helper.make_tensor( + name=name_score_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['score_threshold'])])) + + node_iou_threshold = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_iou_threshold, + value=onnx.helper.make_tensor( + name=name_iou_threshold[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[float(attrs['nms_threshold'])])) + + node_keep_top_k = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k, + value=onnx.helper.make_tensor( + name=name_keep_top_k[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=(), + vals=[np.int64(attrs['keep_top_k'])])) + + node_keep_top_k_2D = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_keep_top_k_2D, + value=onnx.helper.make_tensor( + name=name_keep_top_k_2D[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1, 1], + vals=[np.int64(attrs['keep_top_k'])])) + + # the paddle data format is x1,y1,x2,y2 + kwargs = {'center_point_box': 0} + + name_select_nms = [outputs['Out'][0] + "@select_index"] + node_select_nms= onnx.helper.make_node( + 'NonMaxSuppression', + inputs=inputs['BBoxes'] + inputs['Scores'] + name_keep_top_k +\ + name_iou_threshold + name_score_threshold, + outputs=name_select_nms) + # step 1 nodes select the nms class + node_list = [ + node_score_threshold, node_iou_threshold, node_keep_top_k, + node_keep_top_k_2D, node_select_nms + ] + + # create some const value to use + name_const_value = [result_name+"@const_0", + result_name+"@const_1",\ + result_name+"@const_2",\ + result_name+"@const_-1"] + value_const_value = [0, 1, 2, -1] + for name, value in zip(name_const_value, value_const_value): + node = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=[name], + value=onnx.helper.make_tensor( + name=name + "@const", + data_type=onnx.TensorProto.INT64, + dims=[1], + vals=[value])) + node_list.append(node) + + # Ine this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M + # and the same time, decode the select indices to 1 * D, gather the select_indices + outputs_gather_1 = [result_name + "@gather_1"] + node_gather_1 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_1"], + outputs=outputs_gather_1, + axis=1) + node_list.append(node_gather_1) + + outputs_squeeze_gather_1 = [result_name + "@sequeeze_gather_1"] + node_squeeze_gather_1 = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_1, + outputs=outputs_squeeze_gather_1, + axes=[1]) + node_list.append(node_squeeze_gather_1) + + outputs_gather_2 = [result_name + "@gather_2"] + node_gather_2 = onnx.helper.make_node( + 'Gather', + inputs=name_select_nms + [result_name + "@const_2"], + outputs=outputs_gather_2, + axis=1) + node_list.append(node_gather_2) + + #slice the class is not 0 + if background == 0: + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_squeeze_gather_1, outputs=outputs_nonzero) + node_list.append(node_nonzero) + else: + name_thresh = [result_name + "@thresh"] + node_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_thresh, + value=onnx.helper.make_tensor( + name=name_thresh[0] + "@const", + data_type=onnx.TensorProto.INT32, + dims=[1], + vals=[-1])) + node_list.append(node_thresh) + + outputs_cast = [result_name + "@cast"] + node_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_squeeze_gather_1, outputs=outputs_cast, to=6) + node_list.append(node_cast) + + outputs_greater = [result_name + "@greater"] + node_greater = onnx.helper.make_node( + 'Greater', + inputs=outputs_cast + name_thresh, + outputs=outputs_greater) + node_list.append(node_greater) + + outputs_nonzero = [result_name + "@nonzero"] + node_nonzero = onnx.helper.make_node( + 'NonZero', inputs=outputs_greater, outputs=outputs_nonzero) + node_list.append(node_nonzero) + + outputs_gather_1_nonzero = [result_name + "@gather_1_nonzero"] + node_gather_1_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1 + outputs_nonzero, + outputs=outputs_gather_1_nonzero, + axis=0) + node_list.append(node_gather_1_nonzero) + + outputs_gather_2_nonzero = [result_name + "@gather_2_nonzero"] + node_gather_2_nonzero = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2 + outputs_nonzero, + outputs=outputs_gather_2_nonzero, + axis=0) + node_list.append(node_gather_2_nonzero) + + # reshape scores N * C * M to (N*C*M) * 1 + outputs_reshape_scores_rank1 = [result_name + "@reshape_scores_rank1"] + node_reshape_scores_rank1 = onnx.helper.make_node( + "Reshape", + inputs=inputs['Scores'] + [result_name + "@const_-1"], + outputs=outputs_reshape_scores_rank1) + node_list.append(node_reshape_scores_rank1) + + # get the shape of scores + outputs_shape_scores = [result_name + "@shape_scores"] + node_shape_scores = onnx.helper.make_node( + 'Shape', inputs=inputs['Scores'], outputs=outputs_shape_scores) + node_list.append(node_shape_scores) + + # gather the index: 2 shape of scores + outputs_gather_scores_dim1 = [result_name + "@gather_scores_dim1"] + node_gather_scores_dim1 = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_scores + [result_name + "@const_2"], + outputs=outputs_gather_scores_dim1, + axis=0) + node_list.append(node_gather_scores_dim1) + + # mul class * M + outputs_mul_classnum_boxnum = [result_name + "@mul_classnum_boxnum"] + node_mul_classnum_boxnum = onnx.helper.make_node( + 'Mul', + inputs=outputs_gather_1_nonzero + outputs_gather_scores_dim1, + outputs=outputs_mul_classnum_boxnum) + node_list.append(node_mul_classnum_boxnum) + + # add class * M * index + outputs_add_class_M_index = [result_name + "@add_class_M_index"] + node_add_class_M_index = onnx.helper.make_node( + 'Add', + inputs=outputs_mul_classnum_boxnum + outputs_gather_2_nonzero, + outputs=outputs_add_class_M_index) + node_list.append(node_add_class_M_index) + + # Squeeze the indices to 1 dim + outputs_squeeze_select_index = [result_name + "@squeeze_select_index"] + node_squeeze_select_index = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_add_class_M_index, + outputs=outputs_squeeze_select_index, + axes=[0, 2]) + node_list.append(node_squeeze_select_index) + + # gather the data from flatten scores + outputs_gather_select_scores = [result_name + "@gather_select_scores"] + node_gather_select_scores = onnx.helper.make_node('Gather', + inputs=outputs_reshape_scores_rank1 + \ + outputs_squeeze_select_index, + outputs=outputs_gather_select_scores, + axis=0) + node_list.append(node_gather_select_scores) + + # get nums to input TopK + outputs_shape_select_num = [result_name + "@shape_select_num"] + node_shape_select_num = onnx.helper.make_node( + 'Shape', + inputs=outputs_gather_select_scores, + outputs=outputs_shape_select_num) + node_list.append(node_shape_select_num) + + outputs_gather_select_num = [result_name + "@gather_select_num"] + node_gather_select_num = onnx.helper.make_node( + 'Gather', + inputs=outputs_shape_select_num + [result_name + "@const_0"], + outputs=outputs_gather_select_num, + axis=0) + node_list.append(node_gather_select_num) + + outputs_unsqueeze_select_num = [result_name + "@unsqueeze_select_num"] + node_unsqueeze_select_num = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_select_num, + outputs=outputs_unsqueeze_select_num, + axes=[0]) + node_list.append(node_unsqueeze_select_num) + + outputs_concat_topK_select_num = [result_name + "@conat_topK_select_num"] + node_conat_topK_select_num = onnx.helper.make_node( + 'Concat', + inputs=outputs_unsqueeze_select_num + name_keep_top_k_2D, + outputs=outputs_concat_topK_select_num, + axis=0) + node_list.append(node_conat_topK_select_num) + + outputs_cast_concat_topK_select_num = [ + result_name + "@concat_topK_select_num" + ] + node_outputs_cast_concat_topK_select_num = onnx.helper.make_node( + 'Cast', + inputs=outputs_concat_topK_select_num, + outputs=outputs_cast_concat_topK_select_num, + to=6) + node_list.append(node_outputs_cast_concat_topK_select_num) + # get min(topK, num_select) + outputs_compare_topk_num_select = [result_name + "@compare_topk_num_select"] + node_compare_topk_num_select = onnx.helper.make_node( + 'ReduceMin', + inputs=outputs_cast_concat_topK_select_num, + outputs=outputs_compare_topk_num_select, + keepdims=0) + node_list.append(node_compare_topk_num_select) + + # unsqueeze the indices to 1D tensor + outputs_unsqueeze_topk_select_indices = [ + result_name + "@unsqueeze_topk_select_indices" + ] + node_unsqueeze_topk_select_indices = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_compare_topk_num_select, + outputs=outputs_unsqueeze_topk_select_indices, + axes=[0]) + node_list.append(node_unsqueeze_topk_select_indices) + + # cast the indices to INT64 + outputs_cast_topk_indices = [result_name + "@cast_topk_indices"] + node_cast_topk_indices = onnx.helper.make_node( + 'Cast', + inputs=outputs_unsqueeze_topk_select_indices, + outputs=outputs_cast_topk_indices, + to=7) + node_list.append(node_cast_topk_indices) + + # select topk scores indices + outputs_topk_select_topk_indices = [result_name + "@topk_select_topk_values",\ + result_name + "@topk_select_topk_indices"] + node_topk_select_topk_indices = onnx.helper.make_node( + 'TopK', + inputs=outputs_gather_select_scores + outputs_cast_topk_indices, + outputs=outputs_topk_select_topk_indices) + node_list.append(node_topk_select_topk_indices) + + # gather topk label, scores, boxes + outputs_gather_topk_scores = [result_name + "@gather_topk_scores"] + node_gather_topk_scores = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_select_scores + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_scores, + axis=0) + node_list.append(node_gather_topk_scores) + + outputs_gather_topk_class = [result_name + "@gather_topk_class"] + node_gather_topk_class = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_1_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_class, + axis=1) + node_list.append(node_gather_topk_class) + + # gather the boxes need to gather the boxes id, then get boxes + outputs_gather_topk_boxes_id = [result_name + "@gather_topk_boxes_id"] + node_gather_topk_boxes_id = onnx.helper.make_node( + 'Gather', + inputs=outputs_gather_2_nonzero + + [outputs_topk_select_topk_indices[1]], + outputs=outputs_gather_topk_boxes_id, + axis=1) + node_list.append(node_gather_topk_boxes_id) + + # squeeze the gather_topk_boxes_id to 1 dim + outputs_squeeze_topk_boxes_id = [result_name + "@squeeze_topk_boxes_id"] + node_squeeze_topk_boxes_id = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_gather_topk_boxes_id, + outputs=outputs_squeeze_topk_boxes_id, + axes=[0, 2]) + node_list.append(node_squeeze_topk_boxes_id) + + outputs_gather_select_boxes = [result_name + "@gather_select_boxes"] + node_gather_select_boxes = onnx.helper.make_node( + 'Gather', + inputs=inputs['BBoxes'] + outputs_squeeze_topk_boxes_id, + outputs=outputs_gather_select_boxes, + axis=1) + node_list.append(node_gather_select_boxes) + + # concat the final result + # before concat need to cast the class to float + outputs_cast_topk_class = [result_name + "@cast_topk_class"] + node_cast_topk_class = onnx.helper.make_node( + 'Cast', + inputs=outputs_gather_topk_class, + outputs=outputs_cast_topk_class, + to=1) + node_list.append(node_cast_topk_class) + + outputs_unsqueeze_topk_scores = [result_name + "@unsqueeze_topk_scores"] + node_unsqueeze_topk_scores = onnx.helper.make_node( + 'Unsqueeze', + inputs=outputs_gather_topk_scores, + outputs=outputs_unsqueeze_topk_scores, + axes=[0, 2]) + node_list.append(node_unsqueeze_topk_scores) + + inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\ + outputs_gather_select_boxes + outputs_concat_final_results = outputs['Out'] + node_concat_final_results = onnx.helper.make_node( + 'Concat', + inputs=inputs_concat_final_results, + outputs=outputs_concat_final_results, + axis=2) + node_list.append(node_concat_final_results) + + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/yolo_box.py b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/yolo_box.py new file mode 100644 index 0000000..a1e49e7 --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/opset9/paddle_custom_layer/yolo_box.py @@ -0,0 +1,822 @@ +import onnx +import numpy as np +from onnx import onnx_pb, helper + + +def get_old_name(arg, name_prefix=''): + prefix_index = arg.find(name_prefix) + + if prefix_index != -1: + last_prefix = arg[len(name_prefix):] + else: + last_prefix = arg + idx = last_prefix.find('@') + if idx != -1: + last_prefix = last_prefix[:idx] + return name_prefix + last_prefix + + +def yolo_box(op, block): + inputs = dict() + outputs = dict() + attrs = dict() + for name in op.input_names: + inputs[name] = op.input(name) + for name in op.output_names: + outputs[name] = op.output(name) + for name in op.attr_names: + attrs[name] = op.attr(name) + model_name = outputs['Boxes'][0] + input_shape = block.vars[get_old_name(inputs['X'][0])].shape + image_size = inputs['ImgSize'] + input_height = input_shape[2] + input_width = input_shape[3] + + class_num = attrs['class_num'] + anchors = attrs['anchors'] + num_anchors = int(len(anchors)) // 2 + downsample_ratio = attrs['downsample_ratio'] + input_size = input_height * downsample_ratio + conf_thresh = attrs['conf_thresh'] + conf_thresh_mat = np.ones([num_anchors * input_height * + input_width]) * conf_thresh + + node_list = [] + im_outputs = [] + + x_shape = [1, num_anchors, 5 + class_num, input_height, input_width] + name_x_shape = [model_name + "@x_shape"] + node_x_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_x_shape, + value=onnx.helper.make_tensor( + name=name_x_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[5], + vals=x_shape)) + node_list.append(node_x_shape) + + outputs_x_reshape = [model_name + "@reshape"] + node_x_reshape = onnx.helper.make_node( + 'Reshape', inputs=inputs['X'] + name_x_shape, outputs=outputs_x_reshape) + node_list.append(node_x_reshape) + + outputs_x_transpose = [model_name + "@x_transpose"] + node_x_transpose = onnx.helper.make_node( + 'Transpose', + inputs=outputs_x_reshape, + outputs=outputs_x_transpose, + perm=[0, 1, 3, 4, 2]) + node_list.append(node_x_transpose) + + range_x = [] + range_y = [] + for i in range(0, input_width): + range_x.append(i) + for j in range(0, input_height): + range_y.append(j) + + name_range_x = [model_name + "@range_x"] + node_range_x = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x, + value=onnx.helper.make_tensor( + name=name_range_x[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_width], + vals=range_x)) + node_list.append(node_range_x) + + name_range_y = [model_name + "@range_y"] + node_range_y = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y, + value=onnx.helper.make_tensor( + name=name_range_y[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[input_height], + vals=range_y)) + node_list.append(node_range_y) + + range_x_new_shape = [1, input_width] + range_y_new_shape = [input_height, 1] + + name_range_x_new_shape = [model_name + "@range_x_new_shape"] + node_range_x_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_x_new_shape, + value=onnx.helper.make_tensor( + name=name_range_x_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_x_new_shape)], + vals=range_x_new_shape)) + node_list.append(node_range_x_new_shape) + + name_range_y_new_shape = [model_name + "@range_y_new_shape"] + node_range_y_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_range_y_new_shape, + value=onnx.helper.make_tensor( + name=name_range_y_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(range_y_new_shape)], + vals=range_y_new_shape)) + node_list.append(node_range_y_new_shape) + + outputs_range_x_reshape = [model_name + "@range_x_reshape"] + node_range_x_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_x + name_range_x_new_shape, + outputs=outputs_range_x_reshape) + node_list.append(node_range_x_reshape) + + outputs_range_y_reshape = [model_name + "@range_y_reshape"] + node_range_y_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_range_y + name_range_y_new_shape, + outputs=outputs_range_y_reshape) + node_list.append(node_range_y_reshape) + + outputs_grid_x = [model_name + "@grid_x"] + node_grid_x = onnx.helper.make_node( + "Tile", + inputs=outputs_range_x_reshape + name_range_y_new_shape, + outputs=outputs_grid_x) + node_list.append(node_grid_x) + + outputs_grid_y = [model_name + "@grid_y"] + node_grid_y = onnx.helper.make_node( + "Tile", + inputs=outputs_range_y_reshape + name_range_x_new_shape, + outputs=outputs_grid_y) + node_list.append(node_grid_y) + + outputs_box_x = [model_name + "@box_x"] + outputs_box_y = [model_name + "@box_y"] + outputs_box_w = [model_name + "@box_w"] + outputs_box_h = [model_name + "@box_h"] + outputs_conf = [model_name + "@conf"] + outputs_prob = [model_name + "@prob"] + + node_split_input = onnx.helper.make_node( + "Split", + inputs=outputs_x_transpose, + outputs=outputs_box_x + outputs_box_y + outputs_box_w\ + + outputs_box_h + outputs_conf + outputs_prob, + axis=-1, + split=[1, 1, 1, 1, 1, class_num]) + node_list.append(node_split_input) + + outputs_box_x_sigmoid = [model_name + "@box_x_sigmoid"] + outputs_box_y_sigmoid = [model_name + "@box_y_sigmoid"] + + node_box_x_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_x, outputs=outputs_box_x_sigmoid) + node_list.append(node_box_x_sigmoid) + + node_box_y_sigmoid = onnx.helper.make_node( + "Sigmoid", inputs=outputs_box_y, outputs=outputs_box_y_sigmoid) + node_list.append(node_box_y_sigmoid) + + outputs_box_x_squeeze = [model_name + "@box_x_squeeze"] + outputs_box_y_squeeze = [model_name + "@box_y_squeeze"] + + node_box_x_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_x_sigmoid, + outputs=outputs_box_x_squeeze, + axes=[4]) + node_list.append(node_box_x_squeeze) + + node_box_y_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_y_sigmoid, + outputs=outputs_box_y_squeeze, + axes=[4]) + node_list.append(node_box_y_squeeze) + + outputs_box_x_add_grid = [model_name + "@box_x_add_grid"] + outputs_box_y_add_grid = [model_name + "@box_y_add_grid"] + + node_box_x_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_x + outputs_box_x_squeeze, + outputs=outputs_box_x_add_grid) + node_list.append(node_box_x_add_grid) + + node_box_y_add_grid = onnx.helper.make_node( + "Add", + inputs=outputs_grid_y + outputs_box_y_squeeze, + outputs=outputs_box_y_add_grid) + node_list.append(node_box_y_add_grid) + + name_input_h = [model_name + "@input_h"] + name_input_w = [model_name + "@input_w"] + + node_input_h = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_h, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_height])) + node_list.append(node_input_h) + + node_input_w = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_input_w, + value=onnx.helper.make_tensor( + name=name_input_w[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_width])) + node_list.append(node_input_w) + + outputs_box_x_encode = [model_name + "@box_x_encode"] + outputs_box_y_encode = [model_name + "@box_y_encode"] + + node_box_x_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_x_add_grid + name_input_w, + outputs=outputs_box_x_encode) + node_list.append(node_box_x_encode) + + node_box_y_encode = onnx.helper.make_node( + 'Div', + inputs=outputs_box_y_add_grid + name_input_h, + outputs=outputs_box_y_encode) + node_list.append(node_box_y_encode) + + name_anchor_tensor = [model_name + "@anchor_tensor"] + node_anchor_tensor = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_tensor, + value=onnx.helper.make_tensor( + name=name_anchor_tensor[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[len(anchors)], + vals=anchors)) + node_list.append(node_anchor_tensor) + + anchor_shape = [int(num_anchors), 2] + name_anchor_shape = [model_name + "@anchor_shape"] + node_anchor_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_anchor_shape, + value=onnx.helper.make_tensor( + name=name_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[2], + vals=anchor_shape)) + node_list.append(node_anchor_shape) + + outputs_anchor_tensor_reshape = [model_name + "@anchor_tensor_reshape"] + node_anchor_tensor_reshape = onnx.helper.make_node( + "Reshape", + inputs=name_anchor_tensor + name_anchor_shape, + outputs=outputs_anchor_tensor_reshape) + node_list.append(node_anchor_tensor_reshape) + + name_input_size = [model_name + "@input_size"] + node_input_size = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_input_size, + value=onnx.helper.make_tensor( + name=name_input_size[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[input_size])) + node_list.append(node_input_size) + + outputs_anchors_div_input_size = [model_name + "@anchors_div_input_size"] + node_anchors_div_input_size = onnx.helper.make_node( + "Div", + inputs=outputs_anchor_tensor_reshape + name_input_size, + outputs=outputs_anchors_div_input_size) + node_list.append(node_anchors_div_input_size) + + outputs_anchor_w = [model_name + "@anchor_w"] + outputs_anchor_h = [model_name + "@anchor_h"] + + node_anchor_split = onnx.helper.make_node( + 'Split', + inputs=outputs_anchors_div_input_size, + outputs=outputs_anchor_w + outputs_anchor_h, + axis=1, + split=[1, 1]) + node_list.append(node_anchor_split) + + new_anchor_shape = [1, int(num_anchors), 1, 1] + name_new_anchor_shape = [model_name + "@new_anchor_shape"] + node_new_anchor_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_anchor_shape, + value=onnx.helper.make_tensor( + name=name_new_anchor_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_anchor_shape)], + vals=new_anchor_shape)) + node_list.append(node_new_anchor_shape) + + outputs_anchor_w_reshape = [model_name + "@anchor_w_reshape"] + outputs_anchor_h_reshape = [model_name + "@anchor_h_reshape"] + + node_anchor_w_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_w + name_new_anchor_shape, + outputs=outputs_anchor_w_reshape) + node_list.append(node_anchor_w_reshape) + + node_anchor_h_reshape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_anchor_h + name_new_anchor_shape, + outputs=outputs_anchor_h_reshape) + node_list.append(node_anchor_h_reshape) + + outputs_box_w_squeeze = [model_name + "@box_w_squeeze"] + node_box_w_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_w, + outputs=outputs_box_w_squeeze, + axes=[4]) + node_list.append(node_box_w_squeeze) + + outputs_box_h_squeeze = [model_name + "@box_h_squeeze"] + node_box_h_squeeze = onnx.helper.make_node( + 'Squeeze', + inputs=outputs_box_h, + outputs=outputs_box_h_squeeze, + axes=[4]) + node_list.append(node_box_h_squeeze) + + outputs_box_w_exp = [model_name + "@box_w_exp"] + node_box_w_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_w_squeeze, outputs=outputs_box_w_exp) + node_list.append(node_box_w_exp) + + outputs_box_h_exp = [model_name + "@box_h_exp"] + node_box_h_exp = onnx.helper.make_node( + "Exp", inputs=outputs_box_h_squeeze, outputs=outputs_box_h_exp) + node_list.append(node_box_h_exp) + + outputs_box_w_encode = [model_name + "box_w_encode"] + outputs_box_h_encode = [model_name + "box_h_encode"] + + node_box_w_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_w_exp + outputs_anchor_w_reshape, + outputs=outputs_box_w_encode) + node_list.append(node_box_w_encode) + + node_box_h_encode = onnx.helper.make_node( + 'Mul', + inputs=outputs_box_h_exp + outputs_anchor_h_reshape, + outputs=outputs_box_h_encode) + node_list.append(node_box_h_encode) + + outputs_conf_sigmoid = [model_name + "@conf_sigmoid"] + node_conf_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_conf, outputs=outputs_conf_sigmoid) + node_list.append(node_conf_sigmoid) + + name_conf_thresh = [model_name + "@conf_thresh"] + node_conf_thresh = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_thresh, + value=onnx.helper.make_tensor( + name=name_conf_thresh[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=[num_anchors * input_height * input_width], + vals=conf_thresh_mat)) + node_list.append(node_conf_thresh) + + conf_shape = [1, int(num_anchors), input_height, input_width, 1] + name_conf_shape = [model_name + "@conf_shape"] + node_conf_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_conf_shape, + value=onnx.helper.make_tensor( + name=name_conf_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(conf_shape)], + vals=conf_shape)) + node_list.append(node_conf_shape) + + outputs_conf_thresh_reshape = [model_name + "@conf_thresh_reshape"] + node_conf_thresh_reshape = onnx.helper.make_node( + 'Reshape', + inputs=name_conf_thresh + name_conf_shape, + outputs=outputs_conf_thresh_reshape) + node_list.append(node_conf_thresh_reshape) + + outputs_conf_sub = [model_name + "@conf_sub"] + node_conf_sub = onnx.helper.make_node( + 'Sub', + inputs=outputs_conf_sigmoid + outputs_conf_thresh_reshape, + outputs=outputs_conf_sub) + node_list.append(node_conf_sub) + + outputs_conf_clip = [model_name + "@conf_clip"] + node_conf_clip = onnx.helper.make_node( + 'Clip', inputs=outputs_conf_sub, outputs=outputs_conf_clip) + node_list.append(node_conf_clip) + + zeros = [0] + name_zeros = [model_name + "@zeros"] + node_zeros = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_zeros, + value=onnx.helper.make_tensor( + name=name_zeros[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=zeros)) + node_list.append(node_zeros) + + outputs_conf_clip_bool = [model_name + "@conf_clip_bool"] + node_conf_clip_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_clip + name_zeros, + outputs=outputs_conf_clip_bool) + node_list.append(node_conf_clip_bool) + + outputs_conf_clip_cast = [model_name + "@conf_clip_cast"] + node_conf_clip_cast = onnx.helper.make_node( + 'Cast', + inputs=outputs_conf_clip_bool, + outputs=outputs_conf_clip_cast, + to=1) + node_list.append(node_conf_clip_cast) + + outputs_conf_set_zero = [model_name + "@conf_set_zero"] + node_conf_set_zero = onnx.helper.make_node( + 'Mul', + inputs=outputs_conf_sigmoid + outputs_conf_clip_cast, + outputs=outputs_conf_set_zero) + node_list.append(node_conf_set_zero) + + outputs_prob_sigmoid = [model_name + "@prob_sigmoid"] + node_prob_sigmoid = onnx.helper.make_node( + 'Sigmoid', inputs=outputs_prob, outputs=outputs_prob_sigmoid) + node_list.append(node_prob_sigmoid) + + new_shape = [1, int(num_anchors), input_height, input_width, 1] + name_new_shape = [model_name + "@new_shape"] + node_new_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_new_shape, + value=onnx.helper.make_tensor( + name=name_new_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(new_shape)], + vals=new_shape)) + node_list.append(node_new_shape) + + outputs_conf_new_shape = [model_name + "@_conf_new_shape"] + node_conf_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_conf_set_zero + name_new_shape, + outputs=outputs_conf_new_shape) + node_list.append(node_conf_new_shape) + + outputs_score = [model_name + "@score"] + node_score = onnx.helper.make_node( + 'Mul', + inputs=outputs_prob_sigmoid + outputs_conf_new_shape, + outputs=outputs_score) + node_list.append(node_score) + + outputs_conf_bool = [model_name + "@conf_bool"] + node_conf_bool = onnx.helper.make_node( + 'Greater', + inputs=outputs_conf_new_shape + name_zeros, + outputs=outputs_conf_bool) + node_list.append(node_conf_bool) + + outputs_box_x_new_shape = [model_name + "@box_x_new_shape"] + node_box_x_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_x_encode + name_new_shape, + outputs=outputs_box_x_new_shape) + node_list.append(node_box_x_new_shape) + + outputs_box_y_new_shape = [model_name + "@box_y_new_shape"] + node_box_y_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_y_encode + name_new_shape, + outputs=outputs_box_y_new_shape) + node_list.append(node_box_y_new_shape) + + outputs_box_w_new_shape = [model_name + "@box_w_new_shape"] + node_box_w_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_w_encode + name_new_shape, + outputs=outputs_box_w_new_shape) + node_list.append(node_box_w_new_shape) + + outputs_box_h_new_shape = [model_name + "@box_h_new_shape"] + node_box_h_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_box_h_encode + name_new_shape, + outputs=outputs_box_h_new_shape) + node_list.append(node_box_h_new_shape) + + outputs_pred_box = [model_name + "@pred_box"] + node_pred_box = onnx.helper.make_node( + 'Concat', + inputs=outputs_box_x_new_shape + outputs_box_y_new_shape + \ + outputs_box_w_new_shape + outputs_box_h_new_shape, + outputs=outputs_pred_box, + axis=4) + node_list.append(node_pred_box) + + outputs_conf_cast = [model_name + "conf_cast"] + node_conf_cast = onnx.helper.make_node( + 'Cast', inputs=outputs_conf_bool, outputs=outputs_conf_cast, to=1) + node_list.append(node_conf_cast) + + outputs_pred_box_mul_conf = [model_name + "@pred_box_mul_conf"] + node_pred_box_mul_conf = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box + outputs_conf_cast, + outputs=outputs_pred_box_mul_conf) + node_list.append(node_pred_box_mul_conf) + + box_shape = [1, int(num_anchors) * input_height * input_width, 4] + name_box_shape = [model_name + "@box_shape"] + node_box_shape = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_box_shape, + value=onnx.helper.make_tensor( + name=name_box_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(box_shape)], + vals=box_shape)) + node_list.append(node_box_shape) + + outputs_pred_box_new_shape = [model_name + "@pred_box_new_shape"] + node_pred_box_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_pred_box_mul_conf + name_box_shape, + outputs=outputs_pred_box_new_shape) + node_list.append(node_pred_box_new_shape) + + outputs_pred_box_x = [model_name + "@_pred_box_x"] + outputs_pred_box_y = [model_name + "@_pred_box_y"] + outputs_pred_box_w = [model_name + "@_pred_box_w"] + outputs_pred_box_h = [model_name + "@_pred_box_h"] + + node_pred_box_split = onnx.helper.make_node( + 'Split', + inputs=outputs_pred_box_new_shape, + outputs=outputs_pred_box_x + outputs_pred_box_y + outputs_pred_box_w + + outputs_pred_box_h, + axis=2) + node_list.append(node_pred_box_split) + + name_number_two = [model_name + "@number_two"] + node_number_two = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_number_two, + value=onnx.helper.make_tensor( + name=name_number_two[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[2])) + node_list.append(node_number_two) + + outputs_half_w = [model_name + "@half_w"] + node_half_w = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_w + name_number_two, + outputs=outputs_half_w) + node_list.append(node_half_w) + + outputs_half_h = [model_name + "@half_h"] + node_half_h = onnx.helper.make_node( + "Div", + inputs=outputs_pred_box_h + name_number_two, + outputs=outputs_half_h) + node_list.append(node_half_h) + + outputs_pred_box_x1 = [model_name + "@pred_box_x1"] + node_pred_box_x1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x1) + node_list.append(node_pred_box_x1) + + outputs_pred_box_y1 = [model_name + "@pred_box_y1"] + node_pred_box_y1 = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y1) + node_list.append(node_pred_box_y1) + + outputs_pred_box_x2 = [model_name + "@pred_box_x2"] + node_pred_box_x2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_x + outputs_half_w, + outputs=outputs_pred_box_x2) + node_list.append(node_pred_box_x2) + + outputs_pred_box_y2 = [model_name + "@pred_box_y2"] + node_pred_box_y2 = onnx.helper.make_node( + 'Add', + inputs=outputs_pred_box_y + outputs_half_h, + outputs=outputs_pred_box_y2) + node_list.append(node_pred_box_y2) + + outputs_sqeeze_image_size = [model_name + "@sqeeze_image_size"] + node_sqeeze_image_size = onnx.helper.make_node( + "Squeeze", + axes=[0], + inputs=image_size, + outputs=outputs_sqeeze_image_size) + node_list.append(node_sqeeze_image_size) + + output_img_height = [model_name + "@img_height"] + output_img_width = [model_name + "@img_width"] + node_image_size_split = onnx.helper.make_node( + "Split", + inputs=outputs_sqeeze_image_size, + outputs=output_img_height + output_img_width, + axis=-1, + split=[1, 1]) + node_list.append(node_image_size_split) + + output_img_width_cast = [model_name + "@img_width_cast"] + node_img_width_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_width, outputs=output_img_width_cast, to=1) + node_list.append(node_img_width_cast) + + output_img_height_cast = [model_name + "@img_height_cast"] + node_img_height_cast = onnx.helper.make_node( + 'Cast', inputs=output_img_height, outputs=output_img_height_cast, to=1) + node_list.append(node_img_height_cast) + + outputs_pred_box_x1_decode = [model_name + "@pred_box_x1_decode"] + outputs_pred_box_y1_decode = [model_name + "@pred_box_y1_decode"] + outputs_pred_box_x2_decode = [model_name + "@pred_box_x2_decode"] + outputs_pred_box_y2_decode = [model_name + "@pred_box_y2_decode"] + + node_pred_box_x1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x1 + output_img_width_cast, + outputs=outputs_pred_box_x1_decode) + node_list.append(node_pred_box_x1_decode) + + node_pred_box_y1_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y1 + output_img_height_cast, + outputs=outputs_pred_box_y1_decode) + node_list.append(node_pred_box_y1_decode) + + node_pred_box_x2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_x2 + output_img_width_cast, + outputs=outputs_pred_box_x2_decode) + node_list.append(node_pred_box_x2_decode) + + node_pred_box_y2_decode = onnx.helper.make_node( + 'Mul', + inputs=outputs_pred_box_y2 + output_img_height_cast, + outputs=outputs_pred_box_y2_decode) + node_list.append(node_pred_box_y2_decode) + + name_number_one = [model_name + "@one"] + node_number_one = onnx.helper.make_node( + 'Constant', + inputs=[], + outputs=name_number_one, + value=onnx.helper.make_tensor( + name=name_number_one[0] + "@const", + data_type=onnx.TensorProto.FLOAT, + dims=(), + vals=[1])) + node_list.append(node_number_one) + + output_new_img_height = [model_name + "@new_img_height"] + node_new_img_height = onnx.helper.make_node( + 'Sub', + inputs=output_img_height_cast + name_number_one, + outputs=output_new_img_height) + node_list.append(node_new_img_height) + + output_new_img_width = [model_name + "@new_img_width"] + node_new_img_width = onnx.helper.make_node( + 'Sub', + inputs=output_img_width_cast + name_number_one, + outputs=output_new_img_width) + node_list.append(node_new_img_width) + + outputs_pred_box_x2_sub_w = [model_name + "@pred_box_x2_sub_w"] + node_pred_box_x2_sub_w = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + output_new_img_width, + outputs=outputs_pred_box_x2_sub_w) + node_list.append(node_pred_box_x2_sub_w) + + outputs_pred_box_y2_sub_h = [model_name + "@pred_box_y2_sub_h"] + node_pred_box_y2_sub_h = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + output_new_img_height, + outputs=outputs_pred_box_y2_sub_h) + node_list.append(node_pred_box_y2_sub_h) + + outputs_pred_box_x1_clip = [model_name + "@pred_box_x1_clip"] + outputs_pred_box_y1_clip = [model_name + "@pred_box_y1_clip"] + outputs_pred_box_x2_clip = [model_name + "@pred_box_x2_clip"] + outputs_pred_box_y2_clip = [model_name + "@pred_box_y2_clip"] + + node_pred_box_x1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x1_decode, + outputs=outputs_pred_box_x1_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_x1_clip) + + node_pred_box_y1_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y1_decode, + outputs=outputs_pred_box_y1_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_y1_clip) + + node_pred_box_x2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_x2_sub_w, + outputs=outputs_pred_box_x2_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_x2_clip) + + node_pred_box_y2_clip = onnx.helper.make_node( + 'Clip', + inputs=outputs_pred_box_y2_sub_h, + outputs=outputs_pred_box_y2_clip, + min=0.0, + max=float(np.inf)) + node_list.append(node_pred_box_y2_clip) + + outputs_pred_box_x2_res = [model_name + "@box_x2_res"] + node_pred_box_x2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_x2_decode + outputs_pred_box_x2_clip, + outputs=outputs_pred_box_x2_res) + node_list.append(node_pred_box_x2_res) + + outputs_pred_box_y2_res = [model_name + "@box_y2_res"] + node_pred_box_y2_res = onnx.helper.make_node( + 'Sub', + inputs=outputs_pred_box_y2_decode + outputs_pred_box_y2_clip, + outputs=outputs_pred_box_y2_res) + node_list.append(node_pred_box_y2_res) + + node_pred_box_result = onnx.helper.make_node( + 'Concat', + inputs=outputs_pred_box_x1_clip + outputs_pred_box_y1_clip + + outputs_pred_box_x2_res + outputs_pred_box_y2_res, + outputs=outputs['Boxes'], + axis=-1) + node_list.append(node_pred_box_result) + + score_shape = [1, input_height * input_width * int(num_anchors), class_num] + name_score_shape = [model_name + "@score_shape"] + node_score_shape = onnx.helper.make_node( + "Constant", + inputs=[], + outputs=name_score_shape, + value=onnx.helper.make_tensor( + name=name_score_shape[0] + "@const", + data_type=onnx.TensorProto.INT64, + dims=[len(score_shape)], + vals=score_shape)) + node_list.append(node_score_shape) + + node_score_new_shape = onnx.helper.make_node( + 'Reshape', + inputs=outputs_score + name_score_shape, + outputs=outputs['Scores']) + node_list.append(node_score_new_shape) + return node_list diff --git a/x2paddle/op_mapper/paddle2onnx/paddle_op_mapper.py b/x2paddle/op_mapper/paddle2onnx/paddle_op_mapper.py new file mode 100644 index 0000000..bf9522a --- /dev/null +++ b/x2paddle/op_mapper/paddle2onnx/paddle_op_mapper.py @@ -0,0 +1,108 @@ +# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed 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 math +import sys +import x2paddle +import os +import numpy as np +import paddle.fluid.core as core +import paddle.fluid as fluid +import onnx +from onnx import helper, onnx_pb +from x2paddle.op_mapper.paddle2onnx.opset9.opset import OpSet9 +from x2paddle.op_mapper.paddle2onnx.opset10.opset import OpSet10 +from x2paddle.op_mapper.paddle2onnx.opset11.opset import OpSet11 + + +class PaddleOpMapper(object): + def __init__(self): + self.support_opsets = [9, 10, 11] + self.default_opset = 10 + self.name_counter = dict() + self.op_set = None + + def convert(self, program, save_dir, opset_number=10): + self.op_set = self.create_opset(opset_number) + weight_nodes = self.op_set.convert_weights(program) + op_nodes = list() + input_nodes = list() + output_nodes = list() + unsupported_ops = set() + + print("Translating PaddlePaddle to ONNX...\n") + for block in program.blocks: + for i, op in enumerate(block.ops): + sys.stdout.write("\rTotal:{}, Current:{} : {} ".format( + len(block.ops), i + 1, op.type)) + sys.stdout.flush() + if not hasattr(self.op_set, op.type): + unsupported_ops.add(op.type) + continue + if len(unsupported_ops) > 0: + continue + node = getattr(self.op_set, op.type)(op, block) + if op.type == 'feed': + print(node.name) + input_nodes.append(node) + elif op.type == 'fetch': + output_nodes.append(node) + else: + if isinstance(node, list): + op_nodes = op_nodes + node + else: + op_nodes.append(node) + + if len(unsupported_ops) > 0: + print("\nThere's {} ops are not supported yet".format( + len(unsupported_ops))) + for op in unsupported_ops: + print("=========== {} ===========".format(op)) + return + + graph = helper.make_graph( + nodes=weight_nodes + op_nodes, + name='onnx_model_from_paddle', + initializer=[], + inputs=input_nodes, + outputs=output_nodes) + opset_imports = [helper.make_opsetid("", opset_number)] + model = helper.make_model( + graph, producer_name='X2Paddle', opset_imports=opset_imports) + onnx.checker.check_model(model) + + if not os.path.isdir(save_dir): + os.makedirs(save_dir) + with open(os.path.join(save_dir, 'x2paddle_model.onnx'), 'wb') as f: + f.write(model.SerializeToString()) + print("\nTranslated model saved in {}".format( + os.path.join(save_dir, 'x2paddle_model.onnx'))) + + def create_opset(self, opset_number): + run_opset = self.default_opset + opset = '' + if opset_number in self.support_opsets: + run_opset = opset_number + else: + for support_opset_number in self.support_opsets: + if support_opset_number > opset_number: + run_opset = support_opset_number + else: + break + print( + 'Now, onnx2paddle support convert onnx model opset_verison {},' + 'opset_verison of your onnx model is {}, automatically treated as op_set: {}.' + .format(self.support_opsets, opset_number, run_opset)) + opset = 'OpSet' + str(run_opset) + return eval(opset)() -- GitLab