mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2025-10-09 18:40:18 +08:00

* 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>
953 lines
38 KiB
Python
Executable File
953 lines
38 KiB
Python
Executable File
# 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'))
|