Files
FastDeploy/paddle2onnx/legacy/op_mapper/nn.py
Jason 6343b0db47 [Build] Support build with source code of Paddle2ONNX (#1559)
* Add notes for tensors

* Optimize some apis

* move some warnings

* Support build with Paddle2ONNX

* Add protobuf support

* Fix compile on mac

* add clearn package script

* Add paddle2onnx code

* remove submodule

* Add onnx ocde

* remove softlink

* add onnx code

* fix error

* Add cmake file

* fix patchelf

* update paddle2onnx

* Delete .gitmodules

---------

Co-authored-by: PaddleCI <paddle_ci@example.com>
Co-authored-by: pangyoki <pangyoki@126.com>
Co-authored-by: jiangjiajun <jiangjiajun@baidu.lcom>
2023-03-17 10:03:22 +08:00

953 lines
38 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2020 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 __future__ import absolute_import
import numpy as np
import math
import collections
from paddle2onnx.legacy.constant import dtypes
from paddle2onnx.legacy.op_mapper import OpMapper as op_mapper
from paddle2onnx.legacy.op_mapper import mapper_helper
from paddle2onnx import utils
import paddle
@op_mapper(['conv2d', 'depthwise_conv2d', 'conv3d'])
class Conv():
support_opset_version_range = (1, 12)
@classmethod
def opset_1(cls, graph, node, **kw):
kernel_shape = node.input_shape('Filter', 0)
dilations = node.attr('dilations')
kernel_shape = kernel_shape[2:]
strides = node.attr('strides')
group = node.attr('groups')
pads = node.attr('paddings')
assert node.attrs['data_format'] == 'NCHW' or node.attrs['data_format'] == 'NCDHW' or node.attrs['data_format'] == "AnyLayout", \
"The conv data format should be 'NCHW' or 'NCDHW', but received data format " \
"is %s." % node.attrs['data_format']
# onnx padding is [x1_begin, x2_begin...x1_end, x2_end, ...]
if len(pads) == 2 or len(pads) == 3:
pads = pads + pads
elif len(pads) == 4:
pads = [pads[i] for i in [0, 2, 1, 3]]
elif len(pads) == 6:
pads = [pads[i] for i in [0, 2, 4, 1, 3, 5]]
attrs = {
'dilations': dilations,
'kernel_shape': kernel_shape,
'strides': strides,
'group': group
}
auto_pad = node.attr('padding_algorithm')
if auto_pad == 'SAME':
attrs['auto_pad'] = 'SAME_UPPER'
elif auto_pad == 'VALID':
attrs['auto_pad'] = 'VALID'
else:
attrs['pads'] = pads
graph.make_node(
'Conv',
inputs=node.input('Input') + node.input('Filter'),
outputs=node.output('Output'),
attrs=attrs)
@op_mapper(
['conv2d_transpose', 'depthwise_conv2d_transpose', 'conv3d_transpose'])
class ConvTranspose():
support_opset_version_range = (1, 12)
@classmethod
def opset_1(cls, graph, node, **kw):
output_padding = node.attr('output_padding')
kernel_shape = node.input_shape('Filter', 0)
dilations = node.attr('dilations')
kernel_shape = kernel_shape[2:]
strides = node.attr('strides')
group = node.attr('groups')
pads = node.attr('paddings')
assert node.attrs['data_format'] == 'NCHW' or node.attrs['data_format'] == 'NCDHW', \
"The conv data format should be 'NCHW' or 'NCDHW', but received data format " \
"is %s." % node.attrs['data_format']
if len(pads) == 2 or len(pads) == 3:
pads = pads + pads
elif len(pads) == 4:
pads = [pads[i] for i in [0, 2, 1, 3]]
elif len(pads) == 6:
pads = [pads[i] for i in [0, 2, 4, 1, 3, 5]]
attrs = {
'dilations': dilations,
'kernel_shape': kernel_shape,
'strides': strides,
'group': group
}
auto_pad = node.attr('padding_algorithm')
if auto_pad == 'SAME':
attrs['auto_pad'] = 'SAME_UPPER'
elif auto_pad == 'VALID':
attrs['auto_pad'] = 'VALID'
else:
attrs['pads'] = pads
if output_padding and len(output_padding) > 0:
attrs['output_padding'] = output_padding
graph.make_node(
'ConvTranspose',
inputs=node.input('Input') + node.input('Filter'),
outputs=node.output('Output'),
attrs=attrs)
@op_mapper('pool2d')
class Pool():
support_opset_version_range = (1, 12)
pool_type = {
'max': ('MaxPool', 'GlobalMaxPool'),
'avg': ('AveragePool', 'GlobalAveragePool')
}
@classmethod
def is_same_span(cls, in_size, out_size):
spans = []
for i in range(out_size):
start = math.floor(i * (in_size / out_size))
end = math.ceil((i + 1) * (in_size / out_size))
spans.append(end - start)
if len(set(spans)) == 1:
return True
return False
@classmethod
def opset_1(cls, graph, node, **kw):
assert node.attrs['data_format'] == 'NCHW' or node.attrs['data_format'] == "AnyLayout", \
"The conv data format should be 'NCHW', but received data format " \
"is %s." % node.attrs['data_format']
x_dtype = node.input_dtype('X', 0)
need_dtype_convert = False
input_name = node.input('X', 0)
if x_dtype != paddle.float32:
need_dtype_convert = True
input_name = graph.make_node(
'Cast', inputs=node.input('X'), to=dtypes.ONNX.FLOAT)
if node.attr('global_pooling') or (node.attr('adaptive') and
node.attr('ksize') == [1, 1]):
if need_dtype_convert:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][1],
inputs=[input_name])
graph.make_node(
'Cast',
inputs=[onnx_node],
outputs=node.output('Out'),
to=dtypes.ONNX.DOUBLE)
else:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][1],
inputs=[input_name],
outputs=node.output('Out'))
elif node.attr('adaptive'):
# if pool is adaptive, check if input shape of pool is fixed.
if node.input_shape('X', 0)[2:].count(-1) > 0:
raise Exception(
"Converting this model to ONNX need with static input shape," \
" please fix input shape of this model, see doc Q2 in" \
" https://github.com/PaddlePaddle/paddle2onnx/blob/develop/docs/en/FAQ.md."
)
input_h, input_w = node.input_shape('X', 0)[2:]
output_h, output_w = node.output_shape('Out', 0)[2:]
stride_h = int(input_h / output_h)
stride_w = int(input_w / output_w)
kernel_h = input_h - (output_h - 1) * stride_h
kernel_w = input_w - (output_w - 1) * stride_w
#check if kernel_size is fixed.
if not cls.is_same_span(input_h, output_h) or not cls.is_same_span(
input_w, output_w):
raise Exception(
"Cannot convert adaptive pool with input_size: {}, output_size: {}"
.format(
node.input_shape('X', 0), node.output_shape('Out', 0)))
else:
attrs = {
'kernel_shape': (kernel_h, kernel_w),
'strides': (stride_h, stride_w),
}
if node.attr('ceil_mode') and graph.opset_version < 10:
raise Exception(
"Cannot convert pool with ceil_model == True to ONNX Opset version < 10."
)
elif graph.opset_version > 10:
attrs['ceil_mode'] = node.attr('ceil_mode')
auto_pad = node.attr('padding_algorithm')
if auto_pad == 'SAME':
attrs['auto_pad'] = 'SAME_UPPER'
elif auto_pad == 'VALID':
attrs['auto_pad'] = 'VALID'
if node.attr('pooling_type') == 'avg':
attrs['count_include_pad'] = not node.attr('exclusive')
if need_dtype_convert:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=[input_name],
attrs=attrs)
graph.make_node(
'Cast',
inputs=[onnx_node],
outputs=node.output('Out'),
to=dtypes.ONNX.DOUBLE)
else:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=[input_name],
outputs=node.output('Out'),
attrs=attrs)
else:
input_shape = node.input_shape('X', 0)
k_size = node.attr('ksize')
pads = node.attr('paddings')
strides = node.attr('strides')
if len(pads) == 2:
pads = pads + pads
elif len(pads) == 4:
pads = [pads[i] for i in [0, 2, 1, 3]]
if input_shape[2] > 0 and input_shape[2] + pads[0] < k_size[0]:
k_size[0] = input_shape[2] + pads[0]
if input_shape[3] > 0 and input_shape[3] + pads[1] < k_size[1]:
k_size[1] = input_shape[3] + pads[1]
input_x = [input_name]
if max(k_size) <= max(pads):
onnx_paddings = [0, 0, pads[0], pads[1], 0, 0, pads[2], pads[3]]
attrs_pad = {'mode': 'constant', }
if graph.opset_version >= 11:
pads_node = graph.make_node(
'Constant',
attrs={
'dtype': dtypes.ONNX.INT64,
'value': onnx_paddings
})
value_node = graph.make_node(
'Constant',
attrs={'dtype': dtypes.ONNX.FLOAT,
'value': 0.0})
input_x = input_x + [pads_node, value_node]
else:
attrs_pad['pads'] = onnx_paddings
attrs_pad['value'] = 0.0
input_x = graph.make_node(
'Pad', inputs=input_x, attrs=attrs_pad)
pads = [0, 0, 0, 0]
attrs = {
'kernel_shape': k_size,
'strides': strides,
}
auto_pad = node.attr('padding_algorithm')
if auto_pad == 'SAME':
attrs['auto_pad'] = 'SAME_UPPER'
elif auto_pad == 'VALID':
attrs['auto_pad'] = 'VALID'
else:
attrs['pads'] = pads
if node.attr('ceil_mode') and graph.opset_version < 10:
raise Exception(
"Cannot convert pool with ceil_model == True to ONNX Opset version < 10"
)
elif graph.opset_version >= 10:
attrs['ceil_mode'] = node.attr('ceil_mode')
if node.attr('pooling_type') == 'avg':
attrs['count_include_pad'] = not node.attr('exclusive')
if need_dtype_convert:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=input_x,
attrs=attrs)
graph.make_node(
'Cast',
inputs=[onnx_node],
outputs=node.output('Out'),
to=dtypes.ONNX.DOUBLE)
else:
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=input_x,
outputs=node.output('Out'),
attrs=attrs)
@op_mapper('pool3d')
class Pool3D():
support_opset_version_range = (1, 12)
pool_type = {
'max': ('MaxPool', 'GlobalMaxPool'),
'avg': ('AveragePool', 'GlobalAveragePool')
}
@classmethod
def is_same_span(cls, in_size, out_size):
spans = []
for i in range(out_size):
start = math.floor(i * (in_size / out_size))
end = math.ceil((i + 1) * (in_size / out_size))
spans.append(end - start)
if len(set(spans)) == 1:
return True
return False
@classmethod
def opset_1(cls, graph, node, **kw):
assert node.attrs['data_format'] == 'NCDHW' or node.attrs['data_format'] == "AnyLayout", \
"The conv data format should be 'NCDHW', but received data format " \
"is %s." % node.attrs['data_format']
if node.attr('global_pooling') or (node.attr('adaptive') and
node.attr('ksize') == [1, 1, 1]):
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][1],
inputs=node.input('X'),
outputs=node.output('Out'))
elif node.attr('adaptive'):
# if pool is adaptive, check if input shape of pool is fixed.
if node.input_shape('X', 0)[2:].count(-1) > 0:
raise Exception(
"Converting this model to ONNX need with static input shape," \
" please fix input shape of this model, see doc Q2 in" \
" https://github.com/PaddlePaddle/paddle2onnx/blob/develop/docs/en/FAQ.md."
)
input_d, input_h, input_w = node.input_shape('X', 0)[2:]
output_d, output_h, output_w = node.output_shape('Out', 0)[2:]
stride_d = int(input_d / output_d)
stride_h = int(input_h / output_h)
stride_w = int(input_w / output_w)
kernel_d = input_d - (output_d - 1) * stride_d
kernel_h = input_h - (output_h - 1) * stride_h
kernel_w = input_w - (output_w - 1) * stride_w
#check if kernel_size is fixed.
if not cls.is_same_span(input_h, output_h) or not cls.is_same_span(
input_w, output_w) or not cls.is_same_span(input_d,
output_d):
raise Exception(
"Cannot convert adaptive pool with input_size: {}, output_size: {}"
.format(
node.input_shape('X', 0), node.output_shape('Out', 0)))
else:
attrs = {
'kernel_shape': (kernel_d, kernel_h, kernel_w),
'strides': (stride_d, stride_h, stride_w),
}
if node.attr('ceil_mode') and graph.opset_version < 10:
raise Exception(
"Cannot convert pool with ceil_model == True to ONNX Opset version < 10."
)
elif graph.opset_version > 10:
attrs['ceil_mode'] = node.attr('ceil_mode')
auto_pad = node.attr('padding_algorithm')
if auto_pad == 'SAME':
attrs['auto_pad'] = 'SAME_UPPER'
elif auto_pad == 'VALID':
attrs['auto_pad'] = 'VALID'
if node.attr('pooling_type') == 'avg':
attrs['count_include_pad'] = not node.attr('exclusive')
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=node.input('X'),
outputs=node.output('Out'),
attrs=attrs)
else:
input_shape = node.input_shape('X', 0)
k_size = node.attr('ksize')
paddings = node.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]
if input_shape[4] > 0 and input_shape[4] + paddings[2] < k_size[2]:
k_size[2] = input_shape[4] + paddings[2]
attrs = {
'kernel_shape': k_size,
'strides': node.attr('strides'),
'pads': node.attr('paddings') + node.attr('paddings'),
}
if node.attr('ceil_mode') and graph.opset_version < 10:
raise Exception(
"Cannot convert pool with ceil_model == True to ONNX Opset version < 10"
)
elif graph.opset_version >= 10:
attrs['ceil_mode'] = node.attr('ceil_mode')
if node.attr('pooling_type') == 'avg':
attrs['count_include_pad'] = not node.attr('exclusive')
onnx_node = graph.make_node(
cls.pool_type[node.attr('pooling_type')][0],
inputs=node.input('X'),
outputs=node.output('Out'),
attrs=attrs)
@op_mapper('elu')
class ELU():
support_opset_version_range = (7, 15)
@classmethod
def opset_1(cls, graph, node, **kw):
node = graph.make_node(
'Elu',
inputs=node.input('X'),
outputs=node.output('Out'),
alpha=node.attr('alpha'))
@op_mapper('softsign')
class SoftSign():
support_opset_version_range = (7, 15)
@classmethod
def opset_1(cls, graph, node, **kw):
graph.make_node(
'Softsign', inputs=node.input('X'), outputs=node.output('Out'))
@op_mapper('hard_shrink')
class Hardshrink():
support_opset_version_range = (9, 15)
@classmethod
def opset_9(cls, graph, node, **kw):
node = graph.make_node(
'Shrink',
inputs=node.input('X'),
outputs=node.output('Out'),
lambd=node.attr('threshold'))
@op_mapper('logsigmoid')
class LogSigmoid():
support_opset_version_range = (1, 12)
@classmethod
def opset_1(cls, graph, node, **kw):
sigmoid_node = graph.make_node('Sigmoid', inputs=node.input('X'))
graph.make_node('Log', inputs=sigmoid_node, outputs=node.output('Out'))
@op_mapper('norm')
class Norm():
support_opset_version_range = (1, 12)
@classmethod
def opset_1(cls, graph, node, **kw):
node = graph.make_node(
'LpNormalization',
inputs=node.input('X'),
outputs=node.output('Out'),
axis=node.attr('axis'))
@op_mapper('softshrink')
class SoftShrink():
support_opset_version_range = (9, 15)
@classmethod
def opset_9(cls, graph, node, **kw):
graph.make_node(
'Shrink',
inputs=node.input('X'),
bias=node.attr('lambda'),
lambd=node.attr('lambda'),
outputs=node.output('Out'))
@op_mapper('tanh_shrink')
class TanhShrink():
support_opset_version_range = (7, 15)
@classmethod
def opset_7(cls, graph, node, **kw):
tanh_node = graph.make_node(
'Tanh',
inputs=node.input('X', 0), )
graph.make_node(
'Sub',
inputs=[node.input('X', 0), tanh_node],
outputs=node.output('Out'))
@op_mapper('log_softmax')
class LogSoftmax():
support_opset_version_range = (7, 15)
@classmethod
def opset_7(cls, graph, node, **kw):
axis = node.attr('axis')
shape = node.output_shape('Out', 0)
if axis is None:
axis = -1
if axis < 0:
axis += len(shape)
if axis == len(shape) - 1:
node = graph.make_node(
'LogSoftmax',
inputs=node.input('X'),
outputs=node.output('Out'),
attrs={'axis': axis})
else:
perm = [i for i in range(len(shape))]
perm[-1] = axis
perm[axis] = len(shape) - 1
transpose_node = graph.make_node(
'Transpose', inputs=node.input('X'), attrs={'perm': perm})
softmax_node = graph.make_node(
'LogSoftmax', inputs=[transpose_node], axis=-1)
transpose_node1 = graph.make_node(
'Transpose',
inputs=[softmax_node],
outputs=node.output('Out'),
attrs={'perm': perm})
@classmethod
def opset_13(cls, graph, node, **kw):
graph.make_node(
'LogSoftmax',
inputs=node.input('X'),
axis=node.attr('axis'),
outputs=node.output('Out'))
@op_mapper('layer_norm')
class LayerNorm():
support_opset_version_range = (7, 15)
@classmethod
def opset_7(cls, graph, node, **kw):
ipt = node.input('X', 0)
ipt_dims = len(node.input_shape('X', 0))
normalized_shape = node.attr('begin_norm_axis')
axes = None
if isinstance(normalized_shape, collections.Iterable):
axes = [-i for i in range(len(normalized_shape), 0, -1)]
else:
axes = [i for i in range(normalized_shape, ipt_dims)]
dtype = node.block.vars[node.input('X', 0)].dtype
dtype = dtypes.DTYPE_PADDLE_ONNX_MAP[dtype]
epsilon = graph.make_node(
'Constant', dtype=dtype, value=node.attr('epsilon'))
two = graph.make_node('Constant', dtype=dtype, value=2.0)
mean = graph.make_node("ReduceMean", inputs=[ipt], axes=axes)
numerator = graph.make_node("Sub", inputs=[ipt, mean])
pow_num = graph.make_node("Pow", inputs=[numerator, two])
variance = graph.make_node("ReduceMean", inputs=[pow_num], axes=axes)
add_eps = graph.make_node("Add", inputs=[variance, epsilon])
denominator = graph.make_node("Sqrt", inputs=[add_eps])
ipt_shape = graph.make_node("Shape", inputs=[ipt])
weight_shape = mapper_helper.slice_helper(
graph, ipt_shape, [0], [ipt_dims - len(axes)], [ipt_dims])
if 'Bias' in node.inputs and 'Scale' in node.inputs and len(
node.input('Scale')) > 0 and len(node.input('Bias')) > 0:
if normalized_shape == ipt_dims - 1:
shape_const = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[-1])
scale = graph.make_node(
"Reshape", inputs=[node.input('Scale', 0), shape_const])
bias = graph.make_node(
"Reshape", inputs=[node.input('Bias', 0), shape_const])
else:
scale = graph.make_node(
"Reshape", inputs=[node.input('Scale', 0), weight_shape])
bias = graph.make_node(
"Reshape", inputs=[node.input('Bias', 0), weight_shape])
layer_norm = graph.make_node("Div", inputs=[numerator, denominator])
layer_norm = graph.make_node("Mul", inputs=[layer_norm, scale])
graph.make_node(
"Add", inputs=[layer_norm, bias], outputs=node.output('Y'))
elif 'Bias' in node.inputs and len(node.input('Bias')) > 0:
if normalized_shape == ipt_dims - 1:
shape_const = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[-1])
bias = graph.make_node(
"Reshape", inputs=[node.input('Bias', 0), shape_const])
else:
bias = graph.make_node(
"Reshape", inputs=[node.input('Bias', 0), weight_shape])
layer_norm = graph.make_node("Div", inputs=[numerator, denominator])
graph.make_node(
"Add", inputs=[layer_norm, bias], outputs=node.output('Y'))
elif 'Scale' in node.inputs and len(node.input('Scale')) > 0:
if normalized_shape == ipt_dims - 1:
shape_const = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[-1])
scale = graph.make_node(
"Reshape", inputs=[node.input('Scale', 0), shape_const])
else:
scale = graph.make_node(
"Reshape", inputs=[node.input('Scale', 0), weight_shape])
layer_norm = graph.make_node("Div", inputs=[numerator, denominator])
graph.make_node(
"Mul", inputs=[layer_norm, scale], outputs=node.output('Y'))
else:
layer_norm = graph.make_node(
"Div",
inputs=[numerator, denominator],
outputs=node.output('Y'))
@op_mapper('batch_norm')
class BatchNorm():
support_opset_version_range = (7, 15)
@classmethod
def make_attrs_and_inputs(cls, graph, node, **kw):
onnx_attr = {
'epsilon': node.attr('epsilon'),
'momentum': node.attr('momentum')
}
inputs = node.input('X') + node.input('Scale') + node.input(
'Bias') + node.input('Mean') + node.input('Variance')
return onnx_attr, inputs
@classmethod
def opset_9(cls, graph, node, **kw):
onnx_attr, inputs = cls.make_attrs_and_inputs(graph, node, **kw)
onnx_node = graph.make_node(
'BatchNormalization',
inputs=inputs,
outputs=node.output('Y'),
**onnx_attr)
@classmethod
def opset_7(cls, graph, node, **kw):
onnx_attr, inputs = cls.make_attrs_and_inputs(graph, node, **kw)
onnx_attr['spatial'] = 1
onnx_node = graph.make_node(
'BatchNormalization',
inputs=inputs,
outputs=node.output('Y'),
**onnx_attr)
@op_mapper('group_norm')
class GroupNorm():
support_opset_version_range = (6, 15)
@classmethod
def opset_6(cls, graph, node, **kw):
num_groups = node.attr('groups')
epsilon = node.attr('epsilon')
ipt = node.input('X')[0]
ipt_shape = node.input_shape('X', 0)
assert len(
ipt_shape) == 4, "Only support 4D-Tensor as input for GroupNorm"
dtype = node.block.vars[node.input('X', 0)].dtype
dtype = dtypes.DTYPE_PADDLE_ONNX_MAP[dtype]
shape = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[0, num_groups, -1])
reshape_input = graph.make_node('Reshape', inputs=[ipt, shape])
scale_ = graph.make_node(
'Constant', dtype=dtype, value=[1.0] * num_groups)
bias_ = graph.make_node(
'Constant', dtype=dtype, value=[0.0] * num_groups)
reshaped_output = graph.make_node(
'InstanceNormalization',
inputs=[reshape_input, scale_, bias_],
epsilon=epsilon)
origin_shape = graph.make_node('Shape', inputs=[ipt])
if len(node.input('Scale')) > 0 and len(node.input('Bias')) > 0:
output = graph.make_node(
'Reshape', inputs=[reshaped_output, origin_shape])
unsqueezed_scale = mapper_helper.unsqueeze_helper(
graph, node.input('Scale', 0), [1, 2])
unsqueezed_bias = mapper_helper.unsqueeze_helper(
graph, node.input('Bias', 0), [1, 2])
part0 = graph.make_node('Mul', inputs=[output, unsqueezed_scale])
graph.make_node(
'Add',
inputs=[part0, unsqueezed_bias],
outputs=node.output('Y'))
else:
output = graph.make_node(
'Reshape',
inputs=[reshaped_output, origin_shape],
outputs=node.output('Y'))
@op_mapper('instance_norm')
class InstanceNorm():
support_opset_version_range = (6, 15)
@classmethod
def opset_6(cls, graph, node, **kw):
onnx_attr = {'epsilon': node.attr('epsilon'), }
num_groups = node.block.vars[node.input('X')[0]].shape[1]
dtype = node.block.vars[node.input('X', 0)].dtype
dtype = dtypes.DTYPE_PADDLE_ONNX_MAP[dtype]
if len(node.input('Scale')) == 0:
scale_ = graph.make_node(
'Constant', dtype=dtype, value=[1.0] * num_groups)
else:
scale_ = node.input('Scale')[0]
if len(node.input('Bias')) == 0:
bias_ = graph.make_node(
'Constant', dtype=dtype, value=[0.0] * num_groups)
else:
bias_ = node.input('Bias')[0]
inputs = node.input('X') + [scale_] + [bias_]
onnx_node = graph.make_node(
'InstanceNormalization',
inputs=inputs,
outputs=node.output('Y'),
**onnx_attr)
@op_mapper('dropout')
class Dropout():
support_opset_version_range = (7, 15)
@classmethod
def opset_7(cls, graph, node, **kw):
dropout_mode = node.attr('dropout_implementation')
dropout_prob = node.attr('dropout_prob')
if dropout_mode == 'upscale_in_train':
onnx_node = graph.make_node(
'Identity', inputs=node.input('X'), outputs=node.output('Out'))
elif dropout_mode == 'downgrade_in_infer':
scale_node = graph.make_node(
'Constant',
attrs={'dtype': dtypes.ONNX.FLOAT,
'value': 1 - dropout_prob})
graph.make_node(
"Mul",
inputs=[node.input('X')[0], scale_node],
outputs=node.output('Out'))
else:
raise Exception("Unexpected situation happend")
@op_mapper('roi_align')
class RoiAlign():
support_opset_version_range = (10, 16)
@classmethod
def opset_10(cls, graph, node, **kw):
if node.attr('aligned') and graph.opset_version < 16:
raise Exception(
'when aligned is true, onnx opset should be (onnx_opset>= 16)')
rois_shape = graph.make_node('Shape', inputs=[node.input('ROIs', 0)])
starts = graph.make_node(
'Constant', attrs={'dtype': dtypes.ONNX.INT64,
'value': [0]})
ends = graph.make_node(
'Constant', attrs={'dtype': dtypes.ONNX.INT64,
'value': [1]})
num_rois = graph.make_node('Slice', inputs=[rois_shape, starts, ends])
zero = graph.make_node(
'Constant', dims=[1], dtype=dtypes.ONNX.INT64, value=[0])
batch_indices = graph.make_node('Expand', inputs=[zero, num_rois])
node = graph.make_node(
'RoiAlign',
inputs=[node.input('X', 0), node.input('ROIs', 0), batch_indices],
outputs=node.output('Out'),
mode='avg',
output_height=node.attr('pooled_height'),
output_width=node.attr('pooled_width'),
sampling_ratio=node.attr('sampling_ratio'),
spatial_scale=node.attr('spatial_scale'))
@op_mapper('rnn')
class RNN():
support_opset_version_range = (7, 15)
@classmethod
def make_param_inputs(cls, graph, node, layer, hidden_size, num_layers):
# weight assign order:
# (F_whi F_whh B_whi B_whh* layer_num + (F_bias_hi F_bias_hh B_bias_hi B_bias_hi)* layer_num
def reform_weights(g, w, n, intervals):
slices = [
mapper_helper.slice_helper(
g, w, axes=[1], starts=[x * n], ends=[y * n])
for x, y in intervals
]
return g.make_node('Concat', slices, axis=1)
def transform_weight_with_bias(g, weights, n, intervals):
return [reform_weights(g, w, n, intervals) for w in weights]
if node.attr('mode') == 'LSTM':
reform_permutation = [(0, 1), (3, 4), (1, 3)]
elif node.attr('mode') == 'GRU':
reform_permutation = [(1, 2), (0, 1), (2, 3)]
bidirect_len = 4 if node.attr('is_bidirec') else 2
all_layer_param_len = len(node.input('WeightList'))
weight_list = node.input('WeightList')[:all_layer_param_len // 2]
bias_list = node.input('WeightList')[all_layer_param_len // 2:]
single_layer_param_len = all_layer_param_len // num_layers
unsqueeze_weights = []
layer_weight_list = weight_list[layer * bidirect_len:layer *
bidirect_len + bidirect_len]
layer_bias_list = bias_list[layer * bidirect_len:layer * bidirect_len +
bidirect_len]
param_list = layer_weight_list + layer_bias_list
param_list_len = len(param_list)
for i in range(param_list_len):
weight = mapper_helper.unsqueeze_helper(graph, param_list[i], [0])
unsqueeze_weights.append(weight)
input_weights = unsqueeze_weights[0:param_list_len // 2:2]
hidden_weights = unsqueeze_weights[1:param_list_len // 2:2]
input_weight = graph.make_node('Concat', inputs=input_weights, axis=0)
hidden_weight = graph.make_node('Concat', inputs=hidden_weights, axis=0)
input_bias = unsqueeze_weights[param_list_len // 2:param_list_len:2]
hidden_bias = unsqueeze_weights[param_list_len // 2 + 1:param_list_len:
2]
input_bias = graph.make_node('Concat', inputs=input_bias, axis=0)
hidden_bias = graph.make_node('Concat', inputs=hidden_bias, axis=0)
input_weight, hidden_weight, input_bias, hidden_bias = transform_weight_with_bias(
graph, [input_weight, hidden_weight, input_bias, hidden_bias],
hidden_size, reform_permutation)
bias = graph.make_node(
'Concat', inputs=[input_bias, hidden_bias], axis=1)
return [input_weight, hidden_weight, bias, '']
@classmethod
def make_init_param_inputs(cls, graph, node, layer):
if node.attr('mode') == 'LSTM':
all_init_h, all_init_c = node.input('PreState')
bidirect_len = 2 if node.attr('is_bidirec') else 1
init_h = mapper_helper.slice_helper(
graph, all_init_h, [0], [layer * bidirect_len],
[layer * bidirect_len + bidirect_len])
init_c = mapper_helper.slice_helper(
graph, all_init_c, [0], [layer * bidirect_len],
[layer * bidirect_len + bidirect_len])
return [init_h, init_c]
elif node.attr('mode') == 'GRU':
all_init_h = node.input('PreState', 0)
bidirect_len = 2 if node.attr('is_bidirec') else 1
init_h = mapper_helper.slice_helper(
graph, all_init_h, [0], [layer * bidirect_len],
[layer * bidirect_len + bidirect_len])
return [init_h]
@classmethod
def opset_7(cls, graph, node, **kw):
mode = node.attr('mode')
hidden_size = node.attr('hidden_size')
num_layers = node.attr('num_layers')
prev_output = node.input('Input', 0)
if node.attr('mode') == 'LSTM':
for layer in range(num_layers):
param_inputs = cls.make_param_inputs(graph, node, layer,
hidden_size, num_layers)
init_param_inputs = cls.make_init_param_inputs(graph, node,
layer)
if layer + 1 < num_layers:
rnn_outputs = 3
output_y = None
else:
rnn_outputs = [1] + node.output('State')
output_y = node.output('Out')
prev_output, h_out, c_out = graph.make_node(
node.attr('mode'),
inputs=[prev_output] + param_inputs + init_param_inputs,
outputs=rnn_outputs,
direction='bidirectional'
if node.attr('is_bidirec') else 'forward',
hidden_size=node.attr('hidden_size'))
prev_output = graph.make_node(
'Transpose', inputs=[prev_output], perm=[0, 2, 1, 3])
prev_shape = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[0, 0, -1])
prev_output = graph.make_node(
'Reshape',
inputs=[prev_output, prev_shape],
outputs=output_y)
elif node.attr('mode') == 'GRU':
for layer in range(num_layers):
param_inputs = cls.make_param_inputs(graph, node, layer,
hidden_size, num_layers)
init_param_inputs = cls.make_init_param_inputs(graph, node,
layer)
if layer + 1 < num_layers:
rnn_outputs = 2
output_y = None
else:
rnn_outputs = [1] + node.output('State')
output_y = node.output('Out')
attrs = {
'direction': 'bidirectional'
if node.attr('is_bidirec') else 'forward',
'hidden_size': node.attr('hidden_size'),
'linear_before_reset': 1,
}
prev_output, h_out = graph.make_node(
node.attr('mode'),
inputs=[prev_output] + param_inputs + init_param_inputs,
outputs=rnn_outputs,
attrs=attrs)
prev_output = graph.make_node(
'Transpose', inputs=[prev_output], perm=[0, 2, 1, 3])
prev_shape = graph.make_node(
'Constant', dtype=dtypes.ONNX.INT64, value=[0, 0, -1])
prev_output = graph.make_node(
'Reshape',
inputs=[prev_output, prev_shape],
outputs=output_y)
@op_mapper('thresholded_relu')
class ThresholdedRelu():
support_opset_version_range = (10, 15)
@classmethod
def opset_10(cls, graph, node, **kw):
x_dtype = node.input_dtype('X', 0)
if x_dtype != paddle.float32:
x = graph.make_node(
'Cast', inputs=node.input('X'), to=dtypes.ONNX.FLOAT)
threshholdedrelu_node = graph.make_node(
'ThresholdedRelu', inputs=[x], alpha=node.attr('threshold'))
graph.make_node(
'Cast',
inputs=[threshholdedrelu_node],
outputs=node.output('Out'),
to=dtypes.DTYPE_PADDLE_ONNX_MAP[x_dtype])
else:
graph.make_node(
'ThresholdedRelu',
inputs=node.input('X'),
alpha=node.attr('threshold'),
outputs=node.output('Out'))