diff --git a/docs/user_guides/add_caffe_custom_layer.md b/docs/user_guides/add_caffe_custom_layer.md index 4407f21741f6a1adb2ce186b985789bbc15555a6..f925d16cb6a1a9ae9ec73f5410292c87a79456ac 100644 --- a/docs/user_guides/add_caffe_custom_layer.md +++ b/docs/user_guides/add_caffe_custom_layer.md @@ -38,50 +38,6 @@ bash ./toos/compile.sh /home/root/caffe/src/caffe/proto ``` ***步骤三 添加自定义Layer的实现代码*** -**静态图方式:** -- 进入./x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer,创建.py文件,例如mylayer.py -- 仿照./x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer中的其他文件,在mylayer.py中主要需要实现3个函数,下面以roipooling.py为例分析代码: - 1. `def roipooling_shape(input_shape, pooled_w=None, pooled_h=None)` - 参数: - 1. input_shape(list):其中每个元素代表该层每个输入数据的shape,为必须传入的参数 - 2. pooled_w(int):代表ROI Pooling的kernel的宽,其命名与.prototxt中roi_pooling_param中的key一致 - 3. pooled_h(int):代表ROI Pooling的kernel的高,其命名与.prototxt中roi_pooling_param中的key一致 - - 功能:计算出进行ROI Pooling后的shape - 返回:一个list,其中每个元素代表每个输出数据的shape,由于ROI Pooling的输出数据只有一个,所以其list长度为1 - - 2. `def roipooling_layer(inputs, input_shape=None, name=None, pooled_w=None, pooled_h=None, spatial_scale=None)` - - 参数: - 1. inputs(list):其中每个元素代表该层每个输入数据,为必须传入的参数 - 2. input_shape(list):其中每个元素代表该层每个输入数据的shape,为必须传入的参数 - 3. name(str):ROI Pooling层的名字,为必须传入的参数 - 4. pooled_w(int):代表ROI Pooling的kernel的宽,其命名与.prototxt中roi_pooling_param中的key一致 - 5. pooled_h(int):代表ROI Pooling的kernel的高,其命名与.prototxt中roi_pooling_param中的key一致 - 6. spatial_scale(float):用于将ROI坐标从输入比例转换为池化时使用的比例,其命名与.prototxt中roi_pooling_param中的key一致 - - 功能:运用PaddlePaddle完成组网来实现`roipooling_layer`的功能 - 返回:一个Variable,为组网后的结果 - - 3. `def roipooling_weights(name, data=None)` - - 参数: - 1. name(str):ROI Pooling层的名字,为必须传入的参数 - 2. data(list):由Caffe模型.caffemodel获得的关于roipooling的参数,roipooling的参数为None - - 功能:为每个参数(例如kernel、bias等)命名;同时,若Caffe中该层参数与PaddlePaddle中参数的格式不一致,则变换操作也在该函数中实现。 - 返回:一个list,包含每个参数的名字。 - -- 在roipooling.py中注册`roipooling`,主要运用下述代码实现: - ``` - register(kind='ROIPooling', shape=roipooling_shape, layer=roipooling_layer, weights=roipooling_weights) - # kind为在model.prototxt中roipooling的type - ``` -- 在./x2paddle/op_mapper/caffe_custom_layer/\_\_init\_\_.py中引入该层的使用 - ``` - from . import roipooling - ``` -**动态图方式:** > 【注意】若Caffe自定义layer与Paddle的op一一对应,使用方式一,否则使用方式二。 - 方式一: diff --git a/x2paddle/core/program.py b/x2paddle/core/program.py index d3d3549b80473733cbb20768b668d4cdb3d368f8..6dc2fc96ffa1b0d4bc0ab3a7467df45ba24bb9bf 100644 --- a/x2paddle/core/program.py +++ b/x2paddle/core/program.py @@ -309,12 +309,19 @@ class PaddleGraph(object): if not osp.exists(code_dir): os.makedirs(code_dir) f = open(osp.join(code_dir, 'x2paddle_model.py'), 'w') + + if self.source_type == "caffe": + custom_import = "from x2paddle.op_mapper.static.caffe2paddle " + \ + "import caffe_custom_layer as x2paddle_nn" + else: + custom_import = "" write_code( f, [ "from paddle.fluid.initializer import Constant", "from paddle.fluid.param_attr import ParamAttr", "import paddle.fluid as fluid", + custom_import, "import paddle", "import math", "", ], @@ -347,7 +354,7 @@ class PaddleGraph(object): line += "{}, ".format(output) line = line.strip(", ") if layer.kernel.startswith("custom_layer"): - line += " = {}(".format(layer.kernel.split(":")[-1].lower() + "_layer") + line += "= x2paddle_nn.{}(".format(layer.kernel.split(":")[-1]) else: line += " = {}(".format(layer.kernel) for k, v in layer.inputs.items(): diff --git a/x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py b/x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py index 4b9d135dafd32a644f42d2d56343c4f25483b1d0..93212c68812a1e17a725c78e079f7fe0033f0b01 100644 --- a/x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py +++ b/x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py @@ -1092,11 +1092,7 @@ class CaffeOpMapper(OpMapper): **layer_attrs) def ReLU6(self, node): - if "relu6" in self.nn_name2id: - self.nn_name2id["relu6"] += 1 - else: - self.nn_name2id["relu6"] = 0 - relu6_name = "relu6" + str(self.nn_name2id["relu6"]) + relu6_name = name_generator("relu6", self.nn_name2id) output_name = node.layer_name layer_outputs = [relu6_name, output_name] assert len( diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/__init__.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/__init__.py index 5dc3079cb6dba452d767f20cd2ed29912940c124..6964427ce8b6fd81ab1d936ab720db944e0f90e0 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/__init__.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/__init__.py @@ -1,100 +1,20 @@ -from .register import get_registered_layers - -#custom layer import begins -from . import roipooling -from . import priorbox -from . import permute -from . import detectionoutput -from . import normalize -from . import select -from . import shufflechannel -from . import convolutiondepthwise -from . import axpy -from . import upsample -from . import relu6 -#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 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) +# 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 .detectionoutput import detectionoutput +from .normalize import normalize +from .priorbox import priorbox +from .roipooling import roipooling +from .select import select \ No newline at end of file diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/axpy.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/axpy.py deleted file mode 100644 index 6e0f843398afc877e22b24a454f45c96e5369ceb..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/axpy.py +++ /dev/null @@ -1,29 +0,0 @@ -from .register import register -from x2paddle.core.util import * - - -def axpy_shape(input_shapes): - assert len(input_shapes) == 3, "not valid input shape for axpy layer" - assert len(input_shapes[0]) == len(input_shapes[1]), 'should have same dims' - output_shape = input_shapes[1] - assert (input_shapes[2] == output_shape),\ - "shape not consistent for axpy[%s <--> %s]" \ - % (str(output_shape), str(input_shapes[2])) - return [output_shape] - - -def axpy_layer(inputs, input_shape=None, name=None): - alpha = inputs[0] - x = inputs[1] - y = inputs[2] - out = fluid.layers.elementwise_mul(x, alpha, axis=0) - out = fluid.layers.elementwise_add(out, y, name=name) - return out - - -def axpy_weights(name, data=None): - weights_name = [] - return weights_name - - -register(kind='Axpy', shape=axpy_shape, layer=axpy_layer, weights=axpy_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/convolutiondepthwise.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/convolutiondepthwise.py deleted file mode 100644 index 670d5436af6af54bdc74dd1a79e4ef4e30f42205..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/convolutiondepthwise.py +++ /dev/null @@ -1,150 +0,0 @@ -from .register import register -from x2paddle.core.util import * -import numbers - - -def convolutiondepthwise_shape(input_shape, - num_output=None, - pad=None, - kernel_size=None, - stride=None, - dilation=None, - pad_h=None, - pad_w=None, - kernel_h=None, - kernel_w=None, - stride_h=None, - stride_w=None): - [k_h, k_w] = [1, 1] - if isinstance(kernel_size, numbers.Number): - [k_h, k_w] = [kernel_size] * 2 - elif len(kernel_size) > 0: - k_h = kernel_h if kernel_h > 0 else kernel_size[0] - k_w = kernel_w if kernel_w > 0 else kernel_size[len(kernel_size) - 1] - elif kernel_h > 0 or kernel_w > 0: - k_h = kernel_h - k_w = kernel_w - [s_h, s_w] = [1, 1] - if isinstance(stride, numbers.Number): - [s_h, s_w] = [stride] * 2 - elif len(stride) > 0: - s_h = stride_h if stride_h > 0 else stride[0] - s_w = stride_w if stride_w > 0 else stride[len(stride) - 1] - elif stride_h > 0 or stride_w > 0: - s_h = stride_h - s_w = stride_w - [p_h, p_w] = [0, 0] - if isinstance(pad, numbers.Number): - [p_h, p_w] = [pad] * 2 - elif len(pad) > 0: - p_h = pad_h if pad_h > 0 else pad[0] - p_w = pad_w if pad_w > 0 else pad[len(pad) - 1] - elif pad_h > 0 or pad_w > 0: - p_h = pad_h - p_w = pad_w - dila_len = len(dilation) - dila_h = 1 - dila_w = 1 - if dila_len == 2: - dila_h = dilation[0] - dila_w = dilation[1] - elif dila_len == 1: - dila_h = dila_w = dilation[0] - else: - assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( - dila_len) - i_w = input_shape[0][2] - i_h = input_shape[0][3] - o_h = (i_h + 2 * p_h - (dila_h * (k_h - 1) + 1)) / float(s_h) + 1 - o_w = (i_w + 2 * p_w - (dila_w * (k_w - 1) + 1)) / float(s_w) + 1 - import math - o_h = int(math.floor(o_h)) - o_w = int(math.floor(o_w)) - c = num_output if num_output is not None else input_shape[0][1] - return [[input_shape[0][0], c, o_h, o_w]] - - -def convolutiondepthwise_layer(inputs, - num_output=None, - pad=None, - kernel_size=None, - stride=None, - dilation=None, - pad_h=None, - pad_w=None, - kernel_h=None, - kernel_w=None, - stride_h=None, - stride_w=None, - input_shape=None, - name=None): - import numbers - [k_h, k_w] = [1, 1] - if isinstance(kernel_size, numbers.Number): - [k_h, k_w] = [kernel_size] * 2 - elif len(kernel_size) > 0: - k_h = kernel_h if kernel_h > 0 else kernel_size[0] - k_w = kernel_w if kernel_w > 0 else kernel_size[len(kernel_size) - 1] - elif kernel_h > 0 or kernel_w > 0: - k_h = kernel_h - k_w = kernel_w - [s_h, s_w] = [1, 1] - if isinstance(stride, numbers.Number): - [s_h, s_w] = [stride] * 2 - elif len(stride) > 0: - s_h = stride_h if stride_h > 0 else stride[0] - s_w = stride_w if stride_w > 0 else stride[len(stride) - 1] - elif stride_h > 0 or stride_w > 0: - s_h = stride_h - s_w = stride_w - [p_h, p_w] = [0, 0] - if isinstance(pad, numbers.Number): - [p_h, p_w] = [pad] * 2 - elif len(pad) > 0: - p_h = pad_h if pad_h > 0 else pad[0] - p_w = pad_w if pad_w > 0 else pad[len(pad) - 1] - elif pad_h > 0 or pad_w > 0: - p_h = pad_h - p_w = pad_w - input = inputs[0] - dila_len = len(dilation) - dila_h = 1 - dila_w = 1 - if dila_len == 2: - dila_h = dilation[0] - dila_w = dilation[1] - elif dila_len == 1: - dila_h = dila_w = dilation[0] - else: - assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( - dila_len) - c_in = input_shape[0][1] - c_out = num_output if num_output is not None else input_shape[0][1] - group = int(c_in / (c_in / c_out)) if c_in > c_out else int(c_in / - (c_out / c_in)) - out = fluid.layers.conv2d( - input, - dilation=[dila_h, dila_w], - filter_size=[k_h, k_w], - stride=[s_h, s_w], - padding=[p_h, p_w], - groups=group, - num_filters=c_out, - param_attr=name + '_weights', - bias_attr=name + '_bias', - name=name) - return out - - -def convolutiondepthwise_weights(name, data=None): - weights_name = [] - weights_name.append(name + '_weights') - weights_name.append(name + '_bias') - return weights_name - - -register( - kind='ConvolutionDepthwise', - shape=convolutiondepthwise_shape, - layer=convolutiondepthwise_layer, - weights=convolutiondepthwise_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/detectionoutput.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/detectionoutput.py index 6f70e42b3589af4c3fae3cae6045dd7e112ed53e..acf49a72fffaf2d34c4aece62642c39a2eb816f1 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/detectionoutput.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/detectionoutput.py @@ -1,59 +1,47 @@ -from .register import register -from x2paddle.core.util import * - - -def detectionoutput_shape(input_shape): - return [[-1, 6]] - - -def detectionoutput_layer(inputs, - nms_param=None, - background_label_id=0, - share_location=True, - keep_top_k=100, - confidence_threshold=0.1, - input_shape=None, - name=None): - if nms_param is None: - nms_param = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0} - mbox_conf_flatten = inputs[1] - mbox_priorbox = inputs[2] - mbox_priorbox_list = fluid.layers.split(mbox_priorbox, 2, dim=1) - pb = mbox_priorbox_list[0] - pbv = mbox_priorbox_list[1] - pb = fluid.layers.reshape(x=pb, shape=[-1, 4]) - pbv = fluid.layers.reshape(x=pbv, shape=[-1, 4]) - mbox_loc = inputs[0] - mbox_loc = fluid.layers.reshape(x=mbox_loc, shape=[-1, pb.shape[0], 4]) - mbox_conf_flatten = fluid.layers.reshape( - x=mbox_conf_flatten, shape=[0, pb.shape[0], -1]) - - default = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0} - fields = ['eta', 'top_k', 'nms_threshold'] - for f in default.keys(): - if f not in nms_param: - nms_param[f] = default[f] - out = fluid.layers.detection_output( - scores=mbox_conf_flatten, - loc=mbox_loc, - prior_box=pb, - prior_box_var=pbv, - background_label=background_label_id, - nms_threshold=nms_param["nms_threshold"], - nms_top_k=nms_param["top_k"], - keep_top_k=keep_top_k, - score_threshold=confidence_threshold, - nms_eta=nms_param["eta"]) - return out - - -def detectionoutput_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='DetectionOutput', - shape=detectionoutput_shape, - layer=detectionoutput_layer, - weights=detectionoutput_weights) +# 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. + +import paddle +import paddle.fluid as fluid + +def detectionoutput(x0, + x1, + x2, + nms_threshold, + nms_top_k, + keep_top_k, + nms_eta, + score_threshold, + background_label): + detection_output_layer_attrs = { + "background_label": background_label, + "nms_threshold": nms_threshold, + "nms_top_k": nms_top_k, + "keep_top_k": keep_top_k, + "score_threshold": score_threshold, + "nms_eta": nms_eta} + priorbox_list = paddle.split(x2, num_or_sections=2, axis=1) + pb = priorbox_list[0] + pbv = priorbox_list[1] + pb = paddle.reshape(x=pb, shape=[-1, 4]) + pbv = paddle.reshape(x=pbv, shape=[-1, 4]) + pb_dim = fluid.layers.shape(pb)[0] + loc = paddle.reshape(x0, shape=[-1, pb_dim, 4]) + conf_flatten = paddle.reshape(x1, shape=[0, pb_dim, -1]) + out = fluid.layers.detection_output(loc=loc, + scores=conf_flatten, + prior_box=pb, + prior_box_var=pbv, + **detection_output_layer_attrs) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/normalize.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/normalize.py index 19c583acc7ab73cd63d0bc7f373488e9437aec4c..a3c884c661093b007158053c52cad3e9f1e1fb2d 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/normalize.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/normalize.py @@ -1,37 +1,24 @@ -from .register import register -from x2paddle.core.util import * - - -def normalize_shape(input_shape): - return input_shape - - -def normalize_layer(inputs, - across_spatial=None, - channel_shared=None, - input_shape=None, - name=None): - assert across_spatial == False, "Only support across_spatial == False for Normalize" - input = inputs[0] - l2_norm = fluid.layers.l2_normalize(input, axis=1, name=name + '_l2') - scale_param = fluid.layers.create_parameter( - shape=[1] if channel_shared else [1, 1, 1, input_shape[0][1]], - dtype=input.dtype, - attr=fluid.ParamAttr(name=name + '_scale')) - scale_param = fluid.layers.reshape(x=scale_param, \ - shape=[1] if channel_shared else [input_shape[0][1]]) - out = fluid.layers.elementwise_mul( - x=l2_norm, y=scale_param, axis=-1 if channel_shared else 1) - return out - - -def normalize_weights(name, data=None): - weights_name = [name + '_scale'] - return weights_name - - -register( - kind='Normalize', - shape=normalize_shape, - layer=normalize_layer, - weights=normalize_weights) +# 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. + +import paddle +import paddle.fluid as fluid + +def normalize(x, axis, param_name, param_shape, param_dtype): + l2 = fluid.layers.prior_box(x=x, p=2, axis=1) + param = paddle.static.nn.create_parameter(shape=param_shape, + dtype=string(param_dtype), + name=string(param_name)) + out = paddle.multiply(x=l2, y=param, axis=axis) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/permute.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/permute.py deleted file mode 100644 index 617af1ff7fbc68bda84556deb6a86d96a01a0cf4..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/permute.py +++ /dev/null @@ -1,30 +0,0 @@ -from .register import register -from x2paddle.core.util import * - - -def permute_shape(input_shape, order=None): - inshape = input_shape[0] - output_shape = [] - for ii in order: - assert ii < len(inshape), "invalid order for permute[%s]" % (name) - output_shape.append(inshape[ii]) - return [output_shape] - - -def permute_layer(inputs, order=None, input_shape=None, name=None): - input = inputs[0] - order = list(order) - out = fluid.layers.transpose(input, perm=order, name=name) - return out - - -def permute_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='Permute', - shape=permute_shape, - layer=permute_layer, - weights=permute_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/priorbox.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/priorbox.py index ba41335ecab6293f40f43bb349f80376c71be7ca..319f455ace88804ed1c013bd006c7ae64d4d1315 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/priorbox.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/priorbox.py @@ -1,61 +1,45 @@ -from .register import register -from x2paddle.core.util import * - - -def priorbox_shape(input_shape, max_size=None, aspect_ratio=None): - fc_shape = input_shape[0] - N = 1 - if not max_size == None: - N += 1 - if not aspect_ratio == None: - N += 2 * len(aspect_ratio) - N_bbx = fc_shape[2] * fc_shape[3] * N - output_shape = [1, 2, 4 * N_bbx] - return [output_shape] - - -def priorbox_layer(inputs, - step=0.0, - offset=0.5, - min_size=None, - max_size=[], - aspect_ratio=[1.0], - flip=False, - clip=False, - variance=[0.1, 0.1, 0.2, 0.2], - input_shape=None, - name=None): - input = inputs[0] - image = inputs[1] - steps = tuple(step) if type(step) is list or type(step) is tuple else (step, - step) - - box, variance_ = fluid.layers.prior_box( - input, - image, - min_sizes=min_size, - max_sizes=max_size, - aspect_ratios=aspect_ratio, - variance=variance, - flip=flip, - clip=clip, - steps=steps, - offset=offset, - name=name, - min_max_aspect_ratios_order=True) - box = fluid.layers.reshape(box, [1, 1, -1]) - variance_ = fluid.layers.reshape(variance_, [1, 1, -1]) - out = fluid.layers.concat([box, variance_], axis=1) +# 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. + +import paddle +import paddle.fluid as fluid + +def priorbox(x0, + x1, + min_sizes, + max_sizes, + aspect_ratios, + variance, + flip, + clip, + steps, + offset, + min_max_aspect_ratios_order): + priorbox_layer_attrs = { + "min_sizes": min_sizes, + "max_sizes": max_sizes, + "aspect_ratios": aspect_ratios, + "variance": variance, + "flip": flip, + "clip": clip, + "steps": steps, + "offset": offset, + "min_max_aspect_ratios_order": min_max_aspect_ratios_order} + box, var = fluid.layers.prior_box(input=x0, + image=x1, + **priorbox_layer_attrs) + box = paddle.reshape(x=box, shape=[1, 1, -1]) + var = paddle.reshape(x=var, shape=[1, 1, -1]) + out = paddle.concat(x=[box, var], axis=1) return out - - -def priorbox_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='PriorBox', - shape=priorbox_shape, - layer=priorbox_layer, - weights=priorbox_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/register.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/register.py deleted file mode 100644 index 1d68d7fbb7f058451c4a27304943ebf85d341a21..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/register.py +++ /dev/null @@ -1,41 +0,0 @@ -""" this module provides 'register' for registering customized layers -""" - -g_custom_layers = {} - - -def register(kind, shape, layer, 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, - 'weights': weights - } - - -def get_registered_layers(): - return g_custom_layers diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/relu6.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/relu6.py deleted file mode 100644 index 4e94fcf1a77d730ba8552e30c1c494e966038043..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/relu6.py +++ /dev/null @@ -1,21 +0,0 @@ -from .register import register -from x2paddle.core.util import * - - -def relu6_shape(input_shape): - return input_shape - - -def relu6_layer(inputs, input_shape=None, name=None): - input = inputs[0] - out = fluid.layers.relu6(x=input) - return out - - -def relu6_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='ReLU6', shape=relu6_shape, layer=relu6_layer, weights=relu6_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/roipooling.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/roipooling.py index 5e0d459f1302bb94e99cc922379e739ce087a18e..2a50e98a3fa8c36ed62e6570489acbbd3a5b099d 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/roipooling.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/roipooling.py @@ -1,42 +1,32 @@ -from .register import register -from x2paddle.core.util import * - - -def roipooling_shape(input_shape, pooled_w=None, pooled_h=None): - base_fea_shape = input_shapes[0] - rois_shape = input_shapes[1] - output_shape = base_fea_shape - output_shape[0] = rois_shape[0] - output_shape[2] = pooled_h - output_shape[3] = pooled_w - return [output_shape] - - -def roipooling_layer(inputs, - pooled_w=None, - pooled_h=None, - spatial_scale=None, - input_shape=None, - name=None): - input = inputs[0] - roi = inputs[1] - roi = fluid.layers.slice(roi, axes=[1], starts=[1], ends=[5]) - out = fluid.layers.roi_pool( - input, - roi, - pooled_height=pooled_h, - pooled_width=pooled_w, - spatial_scale=spatial_scale) +# 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. + +import paddle +import paddle.fluid as fluid + +def roipooling(x0, + x1, + pooled_height, + pooled_width, + spatial_scale): + roipooling_layer_attrs = { + "pooled_height": pooled_height, + "pooled_width": pooled_width, + "spatial_scale": spatial_scale} + slice_x1 = paddle.slice(input=x1, axes=[1], + starts=[1], ends=[5]) + out = fluid.layers.roi_pool(input=x0, + rois=slice_x1, + **roipooling_layer_attrs) return out - - -def roipooling_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='ROIPooling', - shape=roipooling_shape, - layer=roipooling_layer, - weights=roipooling_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/select.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/select.py index 497a3c24212b66d6686ff0bc8b2977195c165e28..1e292e22a8658081883d5d34fe5d332b4ceace5a 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/select.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/select.py @@ -1,53 +1,31 @@ -from .register import register -from x2paddle.core.util import * - - -def select_shape(input_shape, axis=None, slice_point=None): - inshape = input_shape[0] - slice_point = slice_point - start = slice_point[0] - if len(slice_point) == 2: - end = slice_point[1] +# 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. + +import paddle +import paddle.fluid as fluid + +def select(x, + input_shape, + point, + axis): + start = point[0] + if len(point) == 2: + end = point[1] else: end = input_shape[axis] - assert end > start, "invalid slice_point with [start:%d, end:%d]" % (start, - end) - output_shape = input_shape - output_shape[axis] = end - start - return [output_shape] - - -def select_layer(inputs, - axis=None, - slice_point=None, - input_shape=None, - name=None): - input = inputs[0] - maxint32 = 2147483647 - slice_point = [0] + slice_point - slice_point.append(maxint32) - i = 0 - out = [] - for i in range(len(slice_point)): - out.append( - fluid.layers.slice( - input, - axes=[axis], - starts=[slice_point[i]], - ends=[slice_point[i + 1]], - name=name + '_' + str(i))) - if i == len(slice_point) - 2: - break - return out - - -def select_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='Select', - shape=select_shape, - layer=select_layer, - weights=select_weights) + out = paddle.slice(x=x, + start=start, + end=end, + axes=[axis]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/shufflechannel.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/shufflechannel.py deleted file mode 100644 index d71404689d032ac4884c9b944999cf5e12e95e40..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/shufflechannel.py +++ /dev/null @@ -1,24 +0,0 @@ -from .register import register -from x2paddle.core.util import * - - -def shufflechannel_shape(input_shape): - return input_shape - - -def shufflechannel_layer(inputs, group=None, input_shape=None, name=None): - input = inputs[0] - out = fluid.layers.shuffle_channel(x=input, group=group) - return out - - -def shufflechannel_weights(name, data=None): - weights_name = [] - return weights_name - - -register( - kind='ShuffleChannel', - shape=shufflechannel_shape, - layer=shufflechannel_layer, - weights=shufflechannel_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/upsample.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/upsample.py deleted file mode 100644 index 44ce7f516bc9049bd10ea41183e676dae5692ef3..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer/upsample.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -################################################################################ -# -# Copyright (c) 2020 Baidu.com, Inc. All Rights Reserved -# -################################################################################ -""" -Author: Drift -Email: wutuobang@baidu.com -Date: 2020/04/22 18:45 -""" - -from .register import register -from x2paddle.core.util import * - - -def upsample_shape(input_shapes, scale): - """ - - :param input_shapes: - :param scale: - :return: - """ - assert len(input_shapes) == 1, "not valid input shape for upsample layer" - assert type(scale) is int - - input_shape = input_shapes[0] - new_h = scale * input_shape[2] - new_w = scale * input_shape[3] - - output_shape = [input_shape[0], input_shape[1], new_h, new_w] - return [output_shape] - - -def upsample_layer(inputs, scale, input_shape=None, name=None): - """ - - :param inputs: - :param scale: - :param input_shape: - :param name: - :return: - """ - x = inputs[0] - out = fluid.layers.resize_nearest( - x, align_corners=False, scale=scale, name=name) - - return out - - -def upsample_weights(name, data=None): - """ - - :param name: - :param data: - :return: - """ - weights_name = [] - return weights_name - - -register( - kind='Upsample', - shape=upsample_shape, - layer=upsample_layer, - weights=upsample_weights) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_op_mapper.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_op_mapper.py index 6c51035de30af4fb8bbc908ce9ef7865818605af..53e7222bb31bd14cd40b2bdc4e384870029bd1e0 100644 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_op_mapper.py +++ b/x2paddle/op_mapper/static/caffe2paddle/caffe_op_mapper.py @@ -15,183 +15,167 @@ import numbers import copy import numpy as np -from x2paddle.decoder.caffe_decoder import CaffeGraph +from x2paddle.decoder.caffe_decoder import CaffeGraph, CaffeGraphNode from x2paddle.core.op_mapper import OpMapper from x2paddle.core.util import * -from x2paddle.op_mapper.static.caffe2paddle import caffe_shape -from x2paddle.op_mapper.static.caffe2paddle.caffe_custom_layer import * from x2paddle.core.program import PaddleGraph +def _adjust_parameters(node): + data = node.data + # When using the protobuf-backend, each parameter initially has four dimensions. + # In certain cases (like FC layers), we want to eliminate the singleton dimensions. + # This implementation takes care of the common cases. However, it does leave the + # potential for future issues. + # The Caffe-backend does not suffer from this problem. + data = list(data) + + squeeze_indices = [1] # Squeeze biases. + if node.layer_type == 'InnerProduct': + squeeze_indices.append(0) # Squeeze FC. + + for idx in squeeze_indices: + if idx >= len(data): + continue + + d = data[idx] + assert len( + d.shape + ) == 4, 'invalid shape[%s] from caffe when adjust_parameters' % ( + str(d.shape)) + + shape_old = d.shape + sq_axis = None + if idx == 0: + sq_axis = (0, 1) + elif idx == 1: + sq_axis = (0, 1, 2) + else: + continue + + data[idx] = np.squeeze(d, axis=sq_axis) + shape_new = data[idx].shape + return data + +def _get_kernel_parameters(kind, params): + assert kind in ["Convolution", "Pooling", "Deconvolution", "ConvolutionDepthwise"] + [k_h, k_w] = [1, 1] + if isinstance(params.kernel_size, numbers.Number): + [k_h, k_w] = [params.kernel_size] * 2 + elif len(params.kernel_size) > 0: + k_h = params.kernel_h if params.kernel_h > 0 else params.kernel_size[ + 0] + k_w = params.kernel_w if params.kernel_w > 0 else params.kernel_size[ + len(params.kernel_size) - 1] + elif params.kernel_h > 0 or params.kernel_w > 0: + k_h = params.kernel_h + k_w = params.kernel_w + [s_h, s_w] = [1, 1] + if isinstance(params.stride, numbers.Number): + [s_h, s_w] = [params.stride] * 2 + elif len(params.stride) > 0: + s_h = params.stride_h if params.stride_h > 0 else params.stride[0] + s_w = params.stride_w if params.stride_w > 0 else params.stride[len( + params.stride) - 1] + elif params.stride_h > 0 or params.stride_w > 0: + s_h = params.stride_h + s_w = params.stride_w + [p_h, p_w] = [0, 0] + if isinstance(params.pad, numbers.Number): + [p_h, p_w] = [params.pad] * 2 + elif len(params.pad) > 0: + p_h = params.pad_h if params.pad_h > 0 else params.pad[0] + p_w = params.pad_w if params.pad_w > 0 else params.pad[len( + params.pad) - 1] + elif params.pad_h > 0 or params.pad_w > 0: + p_h = params.pad_h + p_w = params.pad_w + dila_h = dila_w = 1 + group = 1 + c_o = 1 + if kind in ["Convolution", "Deconvolution", "ConvolutionDepthwise"]: + if kind in ["Convolution", "Deconvolution"]: + c_o = params.num_output + dila_len = len(params.dilation) + if dila_len == 2: + dila_h = params.dilation[0] + dila_w = params.dilation[1] + elif dila_len == 1: + dila_h = dila_w = params.dilation[0] + else: + assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( + dila_len) + if kind in ['Convolution', 'Deconvolution']: + group = params.group + kernel = [k_h, k_w] + stride = [s_h, s_w] + pad = [p_h, p_w] + dilation = [dila_h, dila_w] + return c_o, kernel, stride, pad, dilation, group + + class CaffeOpMapper(OpMapper): directly_map_ops = { 'AbsVal': 'paddle.abs', - 'Sigmoid': 'fluid.layers.sigmoid', + 'Sigmoid': 'paddle.nn.functional.sigmoid', 'TanH': 'paddle.tanh', } def __init__(self, decoder): super(CaffeOpMapper, self).__init__() self.graph = decoder.caffe_graph - self.weights = dict() + self.params = dict() resolver = decoder.resolver self.used_custom_layers = {} self.paddle_graph = PaddleGraph(parent_layer=None, graph_type="static", source_type="caffe") self.paddle_graph.inputs = self.graph.input_nodes self.paddle_graph.outputs = self.graph.output_nodes - print("Total nodes: {}".format(len(self.graph.topo_sort))) + print("Total nodes: {}".format( + sum([ + isinstance(node, CaffeGraphNode) + 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) - if node.layer_type == 'DepthwiseConvolution': - node.layer_type = 'ConvolutionDepthwise' op = node.layer_type if hasattr(self, op): - self.set_node_shape(node) func = getattr(self, op) func(node) - elif op in custom_layers: - self.set_node_shape(node, is_fluid_op=False) - self.deal_custom_layer(node) elif op in self.directly_map_ops: - self.set_node_shape(node) self.directly_map(node) - else: - raise Exception( - "The op {} in model is not supported yet.".format(op)) - self.paddle_graph.set_parameters(self.weights) + print("\nNodes converted.") + self.paddle_graph.set_parameters(self.params) self.paddle_graph.set_custom(self.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, op) and op not in custom_layers: + if not hasattr(self, op) and \ + op not in self.directly_map_ops and \ + op not in self.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))) + if len(unsupported_ops) > 0: + print("\n========= {} OPs are not supported yet ===========".format( + len(unsupported_ops))) for op in unsupported_ops: - print(op) + print("========== {} ============".format(op)) return False - - def set_node_shape(self, node, is_fluid_op=True): - inputs = node.inputs - input_shape = [] - for i, nm in enumerate(inputs): - last_node = self.graph.get_node(nm) - tmp = node.layer.bottom[i] - idx = list(last_node.layer.top).index(tmp) - input_shape.append(last_node.output_shape[idx]) - - node.input_shape = input_shape - func_name = 'shape_' + node.layer_type.lower() - if is_fluid_op: - node.output_shape = getattr(caffe_shape, func_name)(node.layer, - input_shape) - else: - node.output_shape = compute_output_shape(node) - - def adjust_parameters(self, node): - data = node.data - # When using the protobuf-backend, each parameter initially has four dimensions. - # In certain cases (like FC layers), we want to eliminate the singleton dimensions. - # This implementation takes care of the common cases. However, it does leave the - # potential for future issues. - # The Caffe-backend does not suffer from this problem. - data = list(data) - - squeeze_indices = [1] # Squeeze biases. - if node.layer_type == 'InnerProduct': - squeeze_indices.append(0) # Squeeze FC. - - for idx in squeeze_indices: - if idx >= len(data): - continue - - d = data[idx] - assert len( - d.shape - ) == 4, 'invalid shape[%s] from caffe when adjust_parameters' % ( - str(d.shape)) - - shape_old = d.shape - sq_axis = None - if idx == 0: - sq_axis = (0, 1) - elif idx == 1: - sq_axis = (0, 1, 2) - else: - continue - - data[idx] = np.squeeze(d, axis=sq_axis) - shape_new = data[idx].shape - return data - - def get_kernel_parameters(self, kind, params): - assert kind in ['Convolution', 'Pooling', 'Deconvolution'] - [k_h, k_w] = [1, 1] - if isinstance(params.kernel_size, numbers.Number): - [k_h, k_w] = [params.kernel_size] * 2 - elif len(params.kernel_size) > 0: - k_h = params.kernel_h if params.kernel_h > 0 else params.kernel_size[ - 0] - k_w = params.kernel_w if params.kernel_w > 0 else params.kernel_size[ - len(params.kernel_size) - 1] - elif params.kernel_h > 0 or params.kernel_w > 0: - k_h = params.kernel_h - k_w = params.kernel_w - [s_h, s_w] = [1, 1] - if isinstance(params.stride, numbers.Number): - [s_h, s_w] = [params.stride] * 2 - elif len(params.stride) > 0: - s_h = params.stride_h if params.stride_h > 0 else params.stride[0] - s_w = params.stride_w if params.stride_w > 0 else params.stride[len( - params.stride) - 1] - elif params.stride_h > 0 or params.stride_w > 0: - s_h = params.stride_h - s_w = params.stride_w - [p_h, p_w] = [0, 0] - if isinstance(params.pad, numbers.Number): - [p_h, p_w] = [params.pad] * 2 - elif len(params.pad) > 0: - p_h = params.pad_h if params.pad_h > 0 else params.pad[0] - p_w = params.pad_w if params.pad_w > 0 else params.pad[len( - params.pad) - 1] - elif params.pad_h > 0 or params.pad_w > 0: - p_h = params.pad_h - p_w = params.pad_w - dila_h = dila_w = 1 - group = 1 - c_o = 1 - if kind in ['Convolution', 'Deconvolution']: - c_o = params.num_output - dila_len = len(params.dilation) - if dila_len == 2: - dila_h = params.dilation[0] - dila_w = params.dilation[1] - elif dila_len == 1: - dila_h = dila_w = params.dilation[0] - else: - assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( - dila_len) - if kind in ['Convolution', 'Deconvolution']: - group = params.group - kernel = [k_h, k_w] - stride = [s_h, s_w] - pad = [p_h, p_w] - dilation = [dila_h, dila_w] - return c_o, kernel, stride, pad, dilation, group - - def get_input_name(self, node): - if hasattr(node, "index"): - return "{}_{}".format(node.layer_name, node.index) - else: - return node.layer_name + + def directly_map(self, node): + assert node.layer_type in self.directly_map_ops + op_info = self.directly_map_ops[node.layer_type] + input = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + kernel=op_info, + inputs={"x": input.name}, + outputs=[node.name]) def Input(self, node): shape = list(node.layer.input_param.shape[0].dim)[1:] @@ -199,128 +183,236 @@ class CaffeOpMapper(OpMapper): layer_attrs = { "dtype": string(dtype), "shape": [-1] + shape, - "name": string(node.layer_name) + "name": string(node.name) } self.paddle_graph.add_layer( - kernel="fluid.data", + kernel="paddle.static.data", inputs={}, - outputs=[node.layer_name], + outputs=[node.name], **layer_attrs) def Convolution(self, node): data = node.data params = node.layer.convolution_param - channel, kernel, stride, pad, dilation, group = self.get_kernel_parameters( + channel, kernel, stride, pad, dilation, group = _get_kernel_parameters( node.layer_type, params) if data is None: data = [] print( "The parameter of {} (type is {}) is not set. So we set the parameters as 0" - .format(node.layer_name, node.layer_type)) - input_c = node.input_shape[0][1] + .format(node.name, node.layer_type)) + input_c = node.in_shapes[0][1] output_c = channel data.append( np.zeros([output_c, input_c, kernel[0], kernel[1]]).astype( 'float32')) data.append(np.zeros([output_c, ]).astype('float32')) else: - data = self.adjust_parameters(node) - self.weights[node.layer_name + '_weights'] = data[0] + data = _adjust_parameters(node) + kernel_weight_name = node.name + '_weights' + self.params[kernel_weight_name] = data[0] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_weight_name], + shape=self.params[kernel_weight_name].shape, + dtype=string(str(self.params[kernel_weight_name].dtype)), + name=string(kernel_weight_name)) if len(data) == 2: - self.weights[node.layer_name + '_bias'] = data[1] + kernel_bias_name = node.name + '_bias' + self.params[kernel_bias_name] = data[1] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_bias_name], + shape=self.params[kernel_bias_name].shape, + dtype=string(str(self.params[kernel_bias_name].dtype)), + name=string(kernel_bias_name)) assert len(node.inputs ) == 1, 'The count of Convolution node\'s input is not 1.' input = self.graph.get_input_node(node, idx=0, copy=True) - layer_attrs = { - 'filter_size': kernel, - 'num_filters': channel, - 'stride': stride, - 'padding': pad, - 'dilation': dilation, - 'groups': group, - 'name': string(node.layer_name), - 'param_attr': string(node.layer_name + '_weights'), - 'bias_attr': False - if len(data) == 1 else string(node.layer_name + '_bias'), - } + layer_inputs = {"x": input.name, + "weight": kernel_weight_name} + layer_attrs = {'stride': stride, + 'padding': pad, + 'dilation': dilation, + 'groups': group} + if len(data) == 2: + layer_inputs["bias"] = kernel_bias_name + else: + layer_attrs["bias"] = None self.paddle_graph.add_layer( - kernel="fluid.layers.conv2d", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) + kernel="paddle.nn.functional.conv2d", + inputs=layer_inputs, + outputs=[node.name], + **layer_attrs) def Deconvolution(self, node): data = node.data params = node.layer.convolution_param - channel, kernel, stride, pad, dilation, group = self.get_kernel_parameters( + channel, kernel, stride, pad, dilation, group = _get_kernel_parameters( node.layer_type, params) if data is None: data = [] print( 'The parameter of {} (type is {}) is not set. So we set the parameters as 0' - .format(node.layer_name, node.layer_type)) - input_c = node.input_shape[0][1] + .format(node.name, node.layer_type)) + input_c = node.in_shapes[0][1] output_c = channel data.append( np.zeros([output_c, input_c, kernel[0], kernel[1]]).astype( 'float32')) data.append(np.zeros([output_c, ]).astype('float32')) else: - data = self.adjust_parameters(node) - self.weights[node.layer_name + '_weights'] = data[0] + data = _adjust_parameters(node) + kernel_weight_name = node.name + '_weights' + self.params[kernel_weight_name] = data[0] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_weight_name], + shape=self.params[kernel_weight_name].shape, + dtype=string(str(self.params[kernel_weight_name].dtype)), + name=string(kernel_weight_name)) if len(data) == 2: - self.weights[node.layer_name + '_bias'] = data[1] + kernel_bias_name = node.name + '_bias' + self.params[kernel_bias_name] = data[1] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_bias_name], + shape=self.params[kernel_bias_name].shape, + dtype=string(str(self.params[kernel_bias_name].dtype)), + name=string(kernel_bias_name)) assert len(node.inputs ) == 1, 'The count of Deconvolution node\'s input is not 1.' input = self.graph.get_input_node(node, idx=0, copy=True) - layer_attrs = { - 'output_size': None, - 'filter_size': kernel, - 'num_filters': channel, - 'stride': stride, - 'padding': pad, - 'dilation': dilation, - 'groups': group, - 'name': string(node.layer_name), - 'param_attr': string(node.layer_name + '_weights'), - 'bias_attr': False - if len(data) == 1 else string(node.layer_name + '_bias') - } + layer_inputs = {"x": input.name, + "weight": kernel_weight_name} + layer_attrs = {'stride': stride, + 'padding': pad, + 'dilation': dilation, + 'groups': group} + if len(data) == 2: + layer_inputs["bias"] = kernel_bias_name + else: + layer_attrs["bias"] = None self.paddle_graph.add_layer( - kernel="fluid.layers.conv2d_transpose", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], + kernel="paddle.nn.functional.conv2d_transpose", + inputs=layer_inputs, + outputs=[node.name], **layer_attrs) + + def DepthwiseConvolution(self, node): + node.layer_type = "ConvolutionDepthwise" + self.ConvolutionDepthwise(node) + + def ConvolutionDepthwise(self, node): + data = node.data + params = node.layer.convolution_param + out_channel, kernel, stride, pad, dilation, group = _get_kernel_parameters( + node.layer_type, params) + out_channel = params.num_output if params.num_output is not None else node.in_shapes[0][1] + in_channel = node.in_shapes[0][1] + group = int(in_channel / (in_channel / out_channel)) if in_channel > out_channel else int(in_channel / + (out_channel / in_channel)) + if data is None: + data = [] + print( + "The parameter of {} (type is {}) is not set. So we set the parameters as 0" + .format(node.layer_name, node.layer_type)) + data.append( + np.zeros([out_channel, node.in_shapes[0][1], kernel[0], kernel[1]]).astype( + 'float32')) + data.append(np.zeros([out_channel, ]).astype('float32')) + else: + data = _adjust_parameters(node) + kernel_weight_name = node.name + '_weights' + self.params[kernel_weight_name] = data[0] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_weight_name], + shape=self.params[kernel_weight_name].shape, + dtype=string(str(self.params[kernel_weight_name].dtype)), + name=string(kernel_weight_name)) + if len(data) == 2: + kernel_bias_name = node.name + '_bias' + self.params[kernel_bias_name] = data[1] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_bias_name], + shape=self.params[kernel_bias_name].shape, + dtype=string(str(self.params[kernel_bias_name].dtype)), + name=string(kernel_bias_name)) + assert len(node.inputs + ) == 1, "The count of Deconvolution node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + layer_inputs = {"x": input.name, + "weight": kernel_weight_name} + layer_attrs = {'stride': stride, + 'padding': pad, + 'dilation': dilation, + 'groups': group} + if len(data) == 2: + layer_inputs["bias"] = kernel_bias_name + else: + layer_attrs["bias"] = None + self.paddle_graph.add_layer( + kernel="paddle.nn.functional.conv2d", + inputs=layer_inputs, + outputs=[node.name], + **layer_attrs) def Pooling(self, node): params = node.layer.pooling_param ceil_mode = getattr(params, 'ceil_mode', True) global_pool = getattr(params, 'global_pooling', False) kernel_default = [1, 1] - channel, kernel, stride, pad, dilation, group = self.get_kernel_parameters( + channel, kernel, stride, pad, dilation, group = _get_kernel_parameters( node.layer_type, params) - if params.pool == 0: - pool_type = 'max' - else: - pool_type = 'avg' assert len( node.inputs) == 1, 'The count of Pooling node\'s input is not 1.' input = self.graph.get_input_node(node, idx=0, copy=True) - layer_attrs = { - 'pool_size': kernel, - 'pool_stride': stride, - 'pool_padding': pad, - 'ceil_mode': ceil_mode, - 'pool_type': string(pool_type), - 'exclusive': False, - 'global_pooling': global_pool, - 'name': string(node.layer_name) - } - self.paddle_graph.add_layer( - kernel="fluid.layers.pool2d", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) + if global_pool: + if kernel[0] == 0: + kernel = [1, 1] + if params.pool == 0: + self.paddle_graph.add_layer( + "paddle.nn.functional.adaptive_max_pool2d", + inputs={"x": input.name}, + outputs=layer_outputs, + output_size=kernel) + else: + self.paddle_graph.add_layer( + "paddle.nn.functional.adaptive_avg_pool2d", + inputs={"x": input.name}, + outputs=[node.name], + output_size=kernel) + else: + if params.pool == 0: + self.paddle_graph.add_layer( + kernel="paddle.nn.functional.max_pool2d", + inputs={"x": input.name}, + outputs=[node.name], + kernel_size=kernel, + stride=stride, + padding=pad, + ceil_mode=ceil_mode) + else: + # TODO(syf): The op has diff. + self.paddle_graph.add_layer( + kernel="fluid.layers.pool2d", + inputs={"input": input.name}, + outputs=[node.name], + pool_size=kernel, + pool_type=string("avg"), + pool_stride=stride, + pool_padding=pad, + ceil_mode=ceil_mode, + exclusive=False, + global_pooling=False) def LRN(self, node): assert len(node.inputs) == 1, 'The count of LRN node\'s input is not 1.' @@ -338,12 +430,12 @@ class CaffeOpMapper(OpMapper): 'k': params.k, 'alpha': alpha, 'beta': params.beta, - 'name': string(node.layer_name) + 'name': string(node.name) } self.paddle_graph.add_layer( kernel="fluid.layers.lrn", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], + inputs={"input": input.name}, + outputs=[node.name], **layer_attrs) def InnerProduct(self, node): @@ -353,7 +445,7 @@ class CaffeOpMapper(OpMapper): print( 'The parameter of {} (type is {}) is not set. So we set the parameters as 0.' .format(node.layer_name, node.layer_type)) - input_c = node.input_shape[0][1] + input_c = node.in_shapes[0][1] output_c = params.num_output data = [] data.append( @@ -362,7 +454,7 @@ class CaffeOpMapper(OpMapper): data.append( np.zeros([output_c]).astype('float32').astype('float32')) else: - data = self.adjust_parameters(node) + data = _adjust_parameters(node) # Reshape the parameters to Paddle's ordering transpose_order = (1, 0) w = data[0] @@ -372,28 +464,55 @@ class CaffeOpMapper(OpMapper): w = w.transpose(transpose_order) data[0] = w - self.weights[node.layer_name + '_weights'] = data[0] + kernel_weight_name = node.name + '_weights' + self.params[kernel_weight_name] = data[0] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_weight_name], + shape=self.params[kernel_weight_name].shape, + dtype=string(str(self.params[kernel_weight_name].dtype)), + name=string(kernel_weight_name)) if len(data) == 2: - self.weights[node.layer_name + '_bias'] = data[1] + kernel_bias_name = node.name + '_bias' + self.params[kernel_bias_name] = data[1] + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_bias_name], + shape=self.params[kernel_bias_name].shape, + dtype=string(str(self.params[kernel_bias_name].dtype)), + name=string(kernel_bias_name)) assert len(node.inputs ) == 1, 'The count of InnerProduct node\'s input is not 1.' #params = node.layer.inner_product_param assert params.axis == 1 assert params.bias_term == True input = self.graph.get_input_node(node, idx=0, copy=True) - layer_attrs = { - 'size': params.num_output, - 'name': string(node.layer_name), - 'act': None, - 'param_attr': string(node.layer_name + '_weights'), - 'bias_attr': False - if len(data) == 1 else string(node.layer_name + '_bias') - } - self.paddle_graph.add_layer( - kernel="fluid.layers.fc", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) + layer_inputs = {"x": input.name, + "weight": kernel_weight_name} + layer_attrs = dict() + if len(data) == 2: + layer_inputs["bias"] = kernel_bias_name + else: + layer_attrs["bias"] = None + if node.in_shapes[0][-1] != data[0].shape[0]: + self.paddle_graph.add_layer( + "paddle.reshape", + inputs={"x": input.name}, + outputs=[input.name], + shape=[-1, data[0].shape[0]]) + self.paddle_graph.add_layer( + kernel="paddle.nn.functional.linear", + inputs=layer_inputs, + outputs=[node.name], + **layer_attrs) + else: + self.paddle_graph.add_layer( + kernel="paddle.nn.functional.linear", + inputs=layer_inputs, + outputs=[node.name], + **layer_attrs) def Softmax(self, node): assert len( @@ -401,19 +520,19 @@ class CaffeOpMapper(OpMapper): input = self.graph.get_input_node(node, idx=0, copy=True) params = node.layer.softmax_param axis = params.axis - shape = node.input_shape[0] + shape = node.in_shapes[0] dims = len(shape) axis = axis + dims if axis < 0 else axis layer_attrs = {'axis': axis, 'name': string(node.layer_name + '_softmax')} self.paddle_graph.add_layer( kernel="paddle.nn.functional.softmax", - inputs={"x": self.get_input_name(input)}, + inputs={"x": input.name}, outputs=[node.layer_name], **layer_attrs) def Slice(self, node): assert len( - node.inputs) == 1, 'The count of Slice node\'s input is not 1.' + node.inputs) == 1, "The count of Slice node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) top_len = len(node.layer.top) params = node.layer.slice_param @@ -421,20 +540,19 @@ class CaffeOpMapper(OpMapper): slice_dim = params.slice_dim if slice_dim != 1 and axis == 1: axis = slice_dim - output_shape = node.output_shape + output_shape = node.out_shapes sections_list = list() outputs_list = list() for i, s in enumerate(output_shape): sections_list.append(s[axis]) - outputs_list.append("{}_{}".format(node.layer_name, i)) + outputs_list.append("{}_p{}".format(node.layer_name, i)) layer_attrs = { 'num_or_sections': sections_list, - 'dim': axis, - 'name': string(node.layer_name) + 'axis': axis, } self.paddle_graph.add_layer( - kernel="fluid.layers.split", - inputs={"input": self.get_input_name(input)}, + "paddle.split", + inputs={"x": input.name}, outputs=outputs_list, **layer_attrs) @@ -445,18 +563,19 @@ class CaffeOpMapper(OpMapper): inputs_list = [] for i in range(len(node.inputs)): input = self.graph.get_input_node(node, idx=i, copy=True) - inputs_list.append(self.get_input_name(input)) + inputs_list.append(input.name) params = node.layer.concat_param axis = params.axis - layer_attrs = {'axis': axis, 'name': string(node.layer_name)} + layer_attrs = {'axis': axis, 'name': string(node.name)} self.paddle_graph.add_layer( kernel="paddle.concat", inputs={"x": inputs_list}, - outputs=[node.layer_name], + outputs=[node.name], **layer_attrs) def ReLU(self, node): """ + :param node: :return: """ @@ -468,15 +587,15 @@ class CaffeOpMapper(OpMapper): if params.HasField('negative_slope') and params.negative_slope != 0: negative_slope = float(params.negative_slope) self.paddle_graph.add_layer( - kernel="fluid.layers.leaky_relu", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - alpha=negative_slope) + kernel="paddle.nn.functional.leaky_relu", + inputs={"x": input.name}, + outputs=[node.name], + negative_slope=negative_slope) else: self.paddle_graph.add_layer( - kernel="fluid.layers.relu", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name]) + kernel="paddle.nn.functional.relu", + inputs={"x": input.name}, + outputs=[node.name]) def PReLU(self, node): assert len( @@ -484,122 +603,83 @@ class CaffeOpMapper(OpMapper): input = self.graph.get_input_node(node, idx=0, copy=True) params = node.layer.prelu_param mode_bool = params.channel_shared + output_shape = node.out_shapes[0] if mode_bool: - mode = 'all' + num_parameters = 1 else: - mode = 'channel' + num_parameters = output_shape[1] data = node.data assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format( - node.layer_name, node.layer_type) - self.weights[node.layer_name + '_weights'] = data[0] - layer_attrs = { - 'mode': string(mode), - 'param_attr': string(node.layer_name + '_weights'), - 'name': string(node.layer_name) - } + node.name, node.layer_type) + kernel_weight_name = node.name + '_weights' + self.params[kernel_weight_name] = np.squeeze(data[0]) self.paddle_graph.add_layer( - kernel="fluid.layers.prelu", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) - - def Accuracy(self, node): - assert len( - node.inputs) == 2, 'The count of Accuracy node\'s input is not 2.' - inputs_dict = dict() - for i, shape in enumerate(node.input_shape): - if shape[1] == 1: - input = self.graph.get_input_node(node, idx=i, copy=True) - inputs_dict["label"] = self.get_input_name(input) - else: - input = self.graph.get_input_node(node, idx=i, copy=True) - inputs_dict["input"] = self.get_input_name(input) - params = node.layer.accuracy_param - top_k = params.top_k - axis = params.axis - ignore_label = params.ignore_label - assert axis == 1, 'PaddlePaddle can not support the situation when the axis is not 1.' - assert not ignore_label >= 0, 'PaddlePaddle can not support the situation when the model has ignore label.' + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[kernel_weight_name], + shape=[num_parameters], + dtype=string(str(self.params[kernel_weight_name].dtype)), + name=string(kernel_weight_name)) self.paddle_graph.add_layer( - kernel="fluid.layers.accuracy", - inputs=inputs_dict, - outputs=[node.layer_name], - k=top_k) + kernel="paddle.nn.functional.prelu", + inputs={"x": input.name, + "weight": kernel_weight_name}, + outputs=[node.name]) def Eltwise(self, node): assert len( - node.inputs) == 2, 'The count of TanH node\'s input is not 2.' + node.inputs) == 2, "The count of Eltwise node\'s input is not 2." params = node.layer.eltwise_param mode = params.operation inputs = [] input0 = self.graph.get_input_node(node, idx=0, copy=True) - inputs.append(input0) input1 = self.graph.get_input_node(node, idx=1, copy=True) - inputs.append(input1) + input0_name = input0.name + input1_name = input1.name if mode == 0: inputs_dict = {} - inputs_dict['x'] = self.get_input_name(inputs[0]) - inputs_dict['y'] = self.get_input_name(inputs[1]) + inputs_dict['x'] = input0_name + inputs_dict['y'] = input1_name self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_mul", + "paddle.multiply", inputs=inputs_dict, - outputs=[node.layer_name]) + outputs=[node.name]) elif mode == 1: if hasattr(params, 'coeff') and len(params.coeff) == 2: coeff = params.coeff - input1_name = self.get_input_name(inputs[0]) - layer_attrs = { - 'shape': [1], - 'value': coeff[0], - 'dtype': '{}.dtype'.format(input1_name) - } - self.paddle_graph.add_layer( - kernel="fluid.layers.fill_constant", - inputs={}, - outputs=["{}_const1".format(node.layer_name)], - **layer_attrs) - self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_mul", - inputs={"x": input1_name, - "y": "{}_const1".format(node.layer_name)}, - outputs=["{}_mul1".format(node.layer_name)]) - input2_name = self.get_input_name(inputs[1]) - layer_attrs = { - 'shape': [1], - 'value': coeff[1], - 'dtype': '{}.dtype'.format(input2_name) - } self.paddle_graph.add_layer( - kernel="fluid.layers.fill_constant", - inputs={}, - outputs=["{}_const2".format(node.layer_name)], - **layer_attrs) + "paddle.scale", + inputs={"x": input0_name}, + outputs=[node.name + '_mul0'], + scale=coeff[0]) self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_mul", - inputs={"x": input2_name, - "y": "{}_const2".format(node.layer_name)}, - outputs=["{}_mul2".format(node.layer_name)]) + "paddle.scale", + inputs={"x": input1_name}, + outputs=[node.name + '_mul1'], + scale=coeff[2]) + inputs_dict = {} + inputs_dict['x'] = node.name + '_mul0' + inputs_dict['y'] = node.name + '_mul1' self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_add", - inputs={"x": "{}_mul1".format(node.layer_name), - "y": "{}_mul2".format(node.layer_name)}, - outputs=[node.layer_name]) + "paddle.add", + inputs=inputs_dict, + outputs=[node.name]) else: inputs_dict = {} - inputs_dict['x'] = self.get_input_name(inputs[0]) - inputs_dict['y'] = self.get_input_name(inputs[1]) + inputs_dict['x'] = input0_name + inputs_dict['y'] = input1_name self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_add", + "paddle.add", inputs=inputs_dict, - outputs=[node.layer_name]) + outputs=[node.name]) else: inputs_dict = {} - inputs_dict['x'] = self.get_input_name(inputs[0]) - inputs_dict['y'] = self.get_input_name(inputs[1]) + inputs_dict['x'] = input0_name + inputs_dict['y'] = input1_name self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_max", - inputs=inputs_dict, - outputs=[node.layer_name]) + "paddle.max", + inputs=inputs_dict, + outputs=[node.name]) def BatchNorm(self, node): assert len( @@ -610,11 +690,15 @@ class CaffeOpMapper(OpMapper): eps = params.eps else: eps = 1e-5 + if hasattr(params, 'moving_average_fraction'): + momentum = params.moving_average_fraction + else: + momentum = 0.9 if node.data is None or len(node.data) != 3: print( 'The parameter of {} (type is {}) is not set. So we set the parameters as 0' .format(node.layer_name, node.layer_type)) - input_c = node.input_shape[0][1] + input_c = node.in_shapes[0][1] mean = np.zeros([input_c, ]).astype('float32') variance = np.zeros([input_c, ]).astype('float32') scale = 0 @@ -626,235 +710,241 @@ class CaffeOpMapper(OpMapper): scaling_factor = 1.0 / scale if scale != 0 else 0 mean *= scaling_factor variance *= scaling_factor - self.weights[node.layer_name + '_mean'] = mean - self.weights[node.layer_name + '_variance'] = variance + weight_name = node.name + '_weight' + self.paddle_graph.add_layer( + kernel="paddle.ones", + inputs={}, + outputs=[weight_name], + shape=mean.shape, + dtype=string("float32")) + bias_name = node.name + '_bias' + self.paddle_graph.add_layer( + kernel="paddle.zeros", + inputs={}, + outputs=[bias_name], + shape=mean.shape, + dtype=string("float32")) + mean_name = node.name + '_mean' + self.params[mean_name] = mean + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[mean_name], + shape=self.params[mean_name].shape, + dtype=string(str(self.params[mean_name].dtype)), + name=string(mean_name)) + variance_name = node.name + '_variance' + self.params[variance_name] = variance + self.paddle_graph.add_layer( + kernel="paddle.static.create_parameter", + inputs={}, + outputs=[variance_name], + shape=self.params[variance_name].shape, + dtype=string(str(self.params[variance_name].dtype)), + name=string(variance_name)) layer_attrs = { - 'is_test': True, - 'param_attr': None, - 'bias_attr': None, - 'moving_mean_name': string(node.layer_name + '_mean'), - 'moving_variance_name': string(node.layer_name + '_variance'), 'epsilon': eps, - 'name': string(node.layer_name) + 'momentum': momentum } self.paddle_graph.add_layer( - kernel="fluid.layers.batch_norm", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], + kernel="paddle.nn.functional.batch_norm", + inputs={"x": input.name, + "weight": weight_name, + "bias": bias_name, + "running_mean": mean_name, + "running_var": variance_name,}, + outputs=[node.name], **layer_attrs) def Scale(self, node): if node.data is None: print( - 'The parameter of {} (type is {}) is not set. So we set the parameters as 0' - .format(node.layer_name, node.layer_type)) - input_c = node.input_shape[0][1] - self.weights[node.layer_name + '_scale'] = np.zeros([ - input_c, - ]).astype('float32') - self.weights[node.layer_name + '_offset'] = np.zeros([ - input_c, - ]).astype('float32') + "The parameter of {} (type is {}) is not set. So we set the parameters as 0" + .format(node.name, node.layer_type)) + self.params[node.name + "_cparam1"] = np.zeros([ + node.in_shapes[0][1], + ]).astype("float32") + self.params[node.name + "_cparam2"] = np.zeros([ + node.in_shapes[0][1], + ]).astype("float32") else: - self.weights[node.layer_name + '_scale'] = np.squeeze(node.data[ - 0]).astype('float32') - self.weights[node.layer_name + '_offset'] = np.squeeze(node.data[ - 1]).astype('float32') + self.params[node.name + "_cparam1"] = np.squeeze(node.data[ + 0]).astype("float32") + self.params[node.name + "_cparam2"] = np.squeeze(node.data[ + 1]).astype("float32") params = node.layer.scale_param axis = params.axis - num_axes = params.num_axes inputs = [] if len(node.inputs) == 2: - # for two tensor, here resets axis to 1. Maybe there is a bug for unkown case. - axis = 1 - bias_shape = node.input_shape[0][axis:axis + num_axes] input0 = self.graph.get_input_node(node, idx=0, copy=True) input1 = self.graph.get_input_node(node, idx=1, copy=True) + input0_name = input0.name + input1_name = input1.name inputs_dict = {} - inputs_dict['x'] = self.get_input_name(input0) - inputs_dict['y'] = self.get_input_name(input1) + inputs_dict['x'] = input0_name + inputs_dict['y'] = input1_name self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_mul", + "paddle.multiply", inputs=inputs_dict, - outputs=["{}_mul".format(node.layer_name)], - axis=axis) + outputs=[node.name + "_mul"], + axis=1) else: - bias_shape = node.input_shape[0][axis:axis + num_axes] - input0 = self.graph.get_input_node(node, idx=0, copy=True) - input0_name = self.get_input_name(input0) self.paddle_graph.add_layer( - kernel="fluid.ParamAttr", + "paddle.static.create_parameter", inputs={}, - outputs=["{}_scale".format(node.layer_name)], - name = string("{}_scale".format(node.layer_name))) - layer_attrs = { - 'dtype': '{}.dtype'.format(input0_name), - 'shape': bias_shape, - 'name': string(node.layer_name + '_cparam1'), - 'is_bias': True, - 'default_initializer': 'Constant(value=1.0)' - } - self.paddle_graph.add_layer( - kernel="fluid.layers.create_parameter", - inputs={"attr": node.layer_name + '_scale',}, - outputs=["{}_cparam1".format(node.layer_name)], - **layer_attrs) + outputs=[node.name + "_cparam1"], + shape=self.params[node.name + "_cparam1"].shape, + dtype=string(str(self.params[node.name + "_cparam1"].dtype)), + name=string(node.name + "_cparam1")) + input0 = self.graph.get_input_node(node, idx=0, copy=True) + input0_name = input0.name inputs_dict = {} - inputs_dict['x'] = self.get_input_name(input0) - inputs_dict['y'] = "{}_cparam1".format(node.layer_name) + inputs_dict['x'] = input0_name + inputs_dict['y'] = node.name + "_cparam1" self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_mul", + "paddle.multiply", inputs=inputs_dict, - outputs=["{}_mul".format(node.layer_name)], + outputs=[node.name + "_mul"], axis=axis) - scale_shape = bias_shape - input0_name = self.get_input_name(input0) self.paddle_graph.add_layer( - kernel="fluid.ParamAttr", + "paddle.static.create_parameter", inputs={}, - outputs=["{}_offset".format(node.layer_name)], - name = string("{}_offset".format(node.layer_name))) - layer_attrs = { - 'dtype': '{}.dtype'.format(input0_name), - 'shape': scale_shape, - 'name': string(node.layer_name + '_cparam2'), - 'is_bias': True, - 'default_initializer': 'Constant(value=1.0)' - } - self.paddle_graph.add_layer( - kernel="fluid.layers.create_parameter", - inputs={"attr": node.layer_name + '_offset'}, - outputs=["{}_cparam2".format(node.layer_name)], - **layer_attrs) + outputs=[node.name + "_cparam2"], + shape=self.params[node.name + "_cparam2"].shape, + dtype=string(str(self.params[node.name + "_cparam2"].dtype)), + name=string(node.name + "_cparam2")) inputs_dict = {} - inputs_dict['x'] = "{}_mul".format(node.layer_name) - inputs_dict['y'] = "{}_cparam2".format(node.layer_name) - self.paddle_graph.add_layer( - kernel="fluid.layers.elementwise_add", - inputs=inputs_dict, - outputs=[node.layer_name], - axis=axis) + inputs_dict['x'] = node.name + "_mul" + inputs_dict['y'] = node.name + "_cparam2" + output_shape = node.out_shapes[0] + if axis == -1: + self.paddle_graph.add_layer( + "paddle.add", + inputs=inputs_dict, + outputs=[node.name]) + else: + if axis < 0: + axis = axis + len(output_shape) + param2_shape = self.params[node.name + "_cparam2"].shape + param2_shape_len = len(param2_shape) + diff_len = len(output_shape) - axis - param2_shape_len + new_shape = list(param2_shape) + [1] * diff_len + self.paddle_graph.add_layer( + "paddle.reshape", + inputs={"x": node.name + "_cparam2"}, + outputs=[node.name + "_cparam2"], + shape=new_shape) + self.paddle_graph.add_layer( + "paddle.add", + inputs=inputs_dict, + outputs=[node.name]) def Reshape(self, node): input = self.graph.get_input_node(node, idx=0, copy=True) - top_count = len(input.layer.top) - is_inplace = False if top_count == 1 else True - output_shape = node.output_shape[0] - layer_attrs = { - 'shape': output_shape, - 'inplace': is_inplace, - 'act': None, - 'name': string(node.layer_name) - } + output_shape = node.out_shapes[0] self.paddle_graph.add_layer( - kernel="fluid.layers.reshape", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) + "paddle.reshape", + inputs={"x": input.name}, + outputs=[node.name], + shape=output_shape) def ArgMax(self, node): assert len(node.inputs) == 1 and len( node.outputs - ) == 1, 'The count of ArgMax node\'s input and output is not 1.' + ) == 1, "The count of ArgMax node\'s input and output is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) - input_shape = node.input_shape[0] + in_shapes = node.in_shapes[0] params = node.layer.argmax_param out_max_val = params.out_max_val if hasattr(params, out_max_val) else False top_k = params.top_k if hasattr(params, top_k) else 1 axis = parmas.axis if hasattr(params, axis) else -1 if axis < 0: - axis += len(input_shape) + axis += len(in_shapes) if out_max_val is True: self.paddle_graph.add_layer( - kernel="fluid.layers.topk", - inputs={"input": self.get_input_name(input)}, - outputs=["{}_topk_var".format(node.layer_name), - "{}_index_var".format(node.layer_name)], + "paddle.topk", + inputs={"x": input.name}, + outputs=[node.name + "_topk_var", node.name + "_index_var"], k=top_k) self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": "{}_topk_var".format(node.layer_name)}, - outputs=["{}_topk_var".format(node.layer_name)], - dtype="{}_topk_var.dtype".format(node.layer_name)) + "paddle.cast", + inputs={"x": node.name + "_index_var"}, + outputs=[node.name + "_index_var"], + dtype="{}_topk_var.dtype".format(node.name)) self.paddle_graph.add_layer( - kernel="paddle.concat", - inputs={"x": "[{}_topk_var, {}_index_var]".format(node.layer_name, - node.layer_name)}, - outputs=[node.layer_name], + "paddle.concat", + inputs={"x": [node.name + "_topk_var", node.name + "_index_var"]}, + outputs=[node.name], axis=axis) else: self.paddle_graph.add_layer( - kernel="fluid.layers.topk", - inputs={"input": self.get_input_name(input)}, - outputs=["_", node.layer_name], + "paddle.topk", + inputs={"x": input.name}, + outputs=["_", node.name], k=top_k) def Crop(self, node): assert len( - node.inputs) == 2, 'The count of Crop node\'s input is not 2.' + node.inputs) == 2, "The count of Crop node\'s input is not 2." input = self.graph.get_input_node(node, idx=0, copy=True) example = self.graph.get_input_node(node, idx=1, copy=True) params = node.layer.crop_param axis = params.axis - input_shape = node.input_shape[0] + in_shapes = node.in_shapes[0] if axis < 0: - axis += len(input_shape) - offset_real = [0] * len(input_shape) + axis += len(in_shapes) + offset_real = [0] * len(in_shapes) if hasattr(params, "offset") and len(params.offset) > 0: offset = list(params.offset) - assert (len(input_shape) - axis + assert (len(in_shapes) - axis ) == len(offset), "invalid offset[%s] in crop layer" % ( str(offset)) offset_real = [0] * axis + offset - layer_attrs = {"offsets": list(offset_real), - "shape": node.input_shape[1]} self.paddle_graph.add_layer( - kernel="fluid.layers.crop_tensor", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - **layer_attrs) + "paddle.crop", + inputs={"x": input.name}, + outputs=[node.name], + shape=node.in_shapes[1], + offsets=list(offset_real)) + def Flatten(self, node): assert len( node. - inputs) == 1, 'The count of DetectionOutput node\'s input is not 1.' + inputs) == 1, "The count of DetectionOutput node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) self.paddle_graph.add_layer( - kernel="fluid.layers.reshape", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - shape = node.output_shape[0]) - + "paddle.reshape", + inputs={"x": input.name}, + outputs=[node.name], + shape=node.out_shapes[0]) + def Power(self, node): assert len( - node.inputs) == 1, 'The count of Permute node\'s input is not 1.' + node.inputs) == 1, "The count of Permute node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) params = node.layer.power_param - power = params.power - scale = params.scale - shift = params.shift layer_attrs = { - 'scale': scale, - 'bias': shift, - 'bias_after_scale': True, - 'name': string(node.layer_name + '_scale') + 'scale': params.scale, + 'bias': params.shift, + 'bias_after_scale': True } self.paddle_graph.add_layer( - kernel="paddle.scale", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], + "paddle.scale", + inputs={"x": input.name}, + outputs=[node.name], **layer_attrs) self.paddle_graph.add_layer( - kernel="paddle.pow", - inputs={"x": node.layer_name}, - outputs=[node.layer_name], - factor=power) + "paddle.pow", + inputs={"x": node.name}, + outputs=[node.name], + exponent=params.power) def Reduction(self, node): assert len( - node.inputs) == 1, 'The count of Reduction node\'s input is not 1.' + node.inputs) == 1, "The count of Reduction node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) params = node.layer.reduction_param operation = params.operation @@ -862,86 +952,104 @@ class CaffeOpMapper(OpMapper): coeff = params.coeff assert operation >= 1 and operation <= 4, "reduction reduction [%s] error" % ( operation) - input_len = len(node.input_shape[0]) + input_len = len(node.in_shapes[0]) if axis < 0: axis += input_len + 1 dim = list(range(input_len)) - if operation == 1: ## operation = SUM + # operation = SUM + if operation == 1: layer_attrs = { - 'dim': dim[axis:], - 'keep_dim': False, - 'name': string(node.layer_name) + "dim": dim[axis:], + "keep_dim": False, } self.paddle_graph.add_layer( - kernel="fluid.layers.reduce_sum", - inputs={"input": self.get_input_name(input)}, - outputs=[node.layer_name], + "paddle.sum", + inputs={"input": input.name}, + outputs=[node.name], **layer_attrs) - elif operation == 2: ## operation = ASUM + # operation = ASUM + elif operation == 2: self.paddle_graph.add_layer( - kernel="paddle.abs", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name]) + "paddle.abs", + inputs={"x": input.name}, + outputs=[node.name]) layer_attrs = { - 'dim': dim[axis:], - 'keep_dim': False, - 'name': string(node.layer_name) + "dim": dim[axis:], + "keep_dim": False, } self.paddle_graph.add_layer( - kernel="fluid.layers.reduce_sum", - inputs={"input": node.layer_name}, - outputs=[node.layer_name], + "paddle.sum", + inputs={"input": node.name}, + outputs=[node.name], **layer_attrs) - elif operation == 3: ## operation = SUMSQ + # operation = SUMSQ + elif operation == 3: self.paddle_graph.add_layer( - kernel="paddle.pow", - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name], - factor=2.0) + "paddle.pow", + inputs={"x": input.name}, + outputs=[node.name], + exponent=2.0) layer_attrs = { - 'dim': dim[axis:], - 'keep_dim': False, - 'name': string(node.layer_name) + "dim": dim[axis:], + "keep_dim": False, } self.paddle_graph.add_layer( - kernel="fluid.layers.reduce_sum", - inputs={"input": node.layer_name}, - outputs=[node.layer_name], + "paddle.sum", + inputs={"input": node.name}, + outputs=[node.name], **layer_attrs) - else: ## operation = MEAN + # operation = MEAN + else: layer_attrs = { - 'dim': dim[axis:], - 'keep_dim': False, - 'name': string(node.layer_name) + "dim": dim[axis:], + "keep_dim": False, } self.paddle_graph.add_layer( - kernel="fluid.layers.reduce_mean", - inputs={"input": node.layer_name}, - outputs=[node.layer_name], + "paddle.mean", + inputs={"input": input.name}, + outputs=[node.name], **layer_attrs) self.paddle_graph.add_layer( - kernel="paddle.scale", - inputs={"x": node.layer_name}, - outputs=[node.layer_name], + "paddle.scale", + inputs={"x": node.name}, + outputs=[node.name], scale=coeff) - - def deal_custom_layer(self, node): - op = node.layer_type - custom_code, func = make_custom_layer(node) - params = get_params(node.layer, node.layer_type) - arg_names, kwargs = set_args(func, params) - kwargs['name'] = string(node.layer_name) - kwargs['input_shape'] = node.input_shape - data = node.data - if data is not None: - data = self.adjust_parameters(node) - weights_name = deal_weights(node) - for i in range(len(data)): - self.weights[weights_name[i]] = data[i] - inputs_list = [] + + def Axpy(self, node): + assert len(node.inputs) == 1 and len( + node.outputs + ) == 1, "The count of Axpy node\'s input and output is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + params = node.layer.axpy_param + input0 = self.graph.get_input_node(node, idx=0, copy=True) + input1 = self.graph.get_input_node(node, idx=1, copy=True) + input2 = self.graph.get_input_node(node, idx=2, copy=True) + input0_name = input0.name + input1_name = input1.name + input2_name = input2.name + inputs_dict = {} + inputs_dict['x'] = input1_name + inputs_dict['y'] = input0_name + self.paddle_graph.add_layer( + "paddle.multiply", + inputs=inputs_dict, + outputs=[node.name + "_mul"], + axis=0) + inputs_dict = {} + inputs_dict['x'] = node.name + "_mul" + inputs_dict['y'] = input2_name + self.paddle_graph.add_layer( + "paddle.add", + inputs=inputs_dict, + outputs=[node.name + "_mul"]) + + def DetectionOutput(self, node): + assert len( + node.inputs) == 3, "The count of DetectionOutput node\'s input is not 3." + inputs_dict = dict() for i in range(len(node.inputs)): input = self.graph.get_input_node(node, idx=i, copy=True) - if i == 1 and op == 'DetectionOutput': + if i == 1: input = self.graph.get_input_node(node, idx=i, copy=True) while input is not None \ and input.layer_type != 'Softmax' \ @@ -949,27 +1057,165 @@ class CaffeOpMapper(OpMapper): input = self.graph.get_input_node(input, idx=0, copy=True) assert input is not None, 'This kind of DetectionOutput is not supported!' input = self.graph.get_input_node(input, idx=0, copy=True) - inputs_list.append(self.get_input_name(input)) - kwargs_tmp = copy.deepcopy(kwargs) - for k, v in kwargs_tmp.items(): - if str(type(v)) == "": - kwargs[k] = dict() - kwargs[k]["nms_threshold"] = v.nms_threshold - kwargs[k]["top_k"] = v.top_k - kwargs[k]["eta"] = v.eta - self.paddle_graph.add_layer( - kernel="custom_layer:{}".format(op), - inputs={"inputs": inputs_list}, - outputs=[node.layer_name], - **kwargs) - if op not in self.used_custom_layers: - self.used_custom_layers[op] = custom_code + inputs_dict["x{}".format(i)] = input.name + params = node.layer.detection_output_param + nms_param = params.nms_param + nms_param_dict = dict() + nms_param_dict["nms_threshold"] = nms_param.nms_threshold + nms_param_dict["top_k"] = nms_param.top_k + nms_param_dict["eta"] = nms_param.eta + if nms_param is None: + nms_param_dict = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0} + default = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0} + fields = ["eta", "top_k", "nms_threshold"] + for f in default.keys(): + if f not in nms_param_dict: + nms_param_dict[f] = default[f] + layer_attrs = { + "background_label": params.background_label_id, + "nms_threshold": nms_param_dict["nms_threshold"], + "nms_top_k": nms_param_dict["top_k"], + "keep_top_k": params.keep_top_k, + "score_threshold": params.confidence_threshold, + "nms_eta": nms_param_dict["eta"]} + self.paddle_graph.add_layer( + kernel="custom_layer:detectionoutput", + inputs=inputs_dict, + outputs=[node.name], + **layer_attrs) - def directly_map(self, node): - assert node.layer_type in self.directly_map_ops - op_info = self.directly_map_ops[node.layer_type] + def Normalize(self, node): + assert len( + node.inputs) == 1, "The count of Normalize node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) + params = node.layer.norm_param + scale_name = node.name + "_scale" + if node.data is None or len(node.data) != 1: + print( + "The parameter of {} (type is {}) is not set. So we set the parameters as 0" + .format(scale_name, node.layer_type)) + self.parmas[scale_name] = \ + np.zeros([1] if params.channel_shared else [1, 1, 1, node.in_shapes[0][1]]).astype("float32") + else: + self.parmas[scale_name] = _adjust_parameters(node)[0] + + layer_attrs = { + "axis": -1 if params.channel_shared else 1, + "param_name": scale_name, + "param_shape": self.parmas[scale_name].shape, + "param_dtype": str(self.parmas[scale_name].dtype)} + self.pd_pdgraph.add_layer( + "custom_layer:normalize", + inputs={"x": input.name}, + outputs=[node.name], + **layer_attrs) + + def Permute(self, node): + assert len( + node.inputs) == 1, "The count of Permute node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + params = node.layer.permute_param + order = list(params.order) self.paddle_graph.add_layer( - kernel=op_info, - inputs={"x": self.get_input_name(input)}, - outputs=[node.layer_name]) \ No newline at end of file + "paddle.transpose", + inputs={"x": input.name}, + outputs=[node.name], + perm=order) + + def PriorBox(self, node): + assert len( + node.inputs) == 2, "The count of PriorBox node\'s input is not 2." + input0 = self.graph.get_input_node(node, idx=0, copy=True) + input1 = self.graph.get_input_node(node, idx=1, copy=True) + inputs_dict = {} + inputs_dict["x0"] = input0.name + inputs_dict["x1"] = input1.name + params = node.layer.prior_box_param + steps = tuple(params.step) if type(params.step) \ + is list or type(params.step) is tuple \ + else (params.step, params.step) + layer_attrs = { + "min_sizes": params.min_size, + "max_sizes": params.max_size, + "aspect_ratios": params.aspect_ratio, + "variance": params.variance, + "flip": params.flip, + "clip": params.clip, + "steps": steps, + "offset": params.offset, + "min_max_aspect_ratios_order": True} + self.paddle_graph.add_layer( + "custom_layer:priorbox", + inputs=inputs_dict, + outputs=[node.name], + **layer_attrs) + + def ReLU6(self, node): + assert len( + node.inputs) == 1, "The count of RelU6 node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + "paddle.nn.functional.relu6", + inputs={"x": input.name}, + outputs=[node.name]) + + def ROIPooling(self, node): + assert len( + node.inputs) == 2, "The count of ROIPooling node\'s input is not 2." + input0 = self.graph.get_input_node(node, idx=0, copy=True) + input1 = self.graph.get_input_node(node, idx=1, copy=True) + inputs_dict = {} + inputs_dict["x0"] = input0.name + inputs_dict["x1"] = input1.name + params = node.layer.roi_pooling_param + layer_attrs = { + "pooled_height": params.pooled_h, + "pooled_width": params.pooled_w, + "spatial_scale": params.spatial_scale} + self.paddle_graph.add_layer( + "custom_layer:ROIPooling", + inputs=inputs_dict, + outputs=[node.name], + **layer_attrs) + + def ShuffleChannel(self, node): + assert len( + node.inputs) == 1, "The count of ShuffleChannel node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + params = node.layer.shuffle_channel_param + self.paddle_graph.add_layer( + "fluid.layers.shuffle_channel", + inputs={"x": input.name}, + outputs=[node.layer_name], + group=params.group) + + def Upsample(self, node): + assert len( + node.inputs) == 1, "The count of Upsample node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + params = node.layer.upsample_param + layer_attrs = { + "align_corners": False, + "scale_factor": params.scale, + "mode": "nearest"} + self.paddle_graph.add_layer( + "paddle.nn.functioanl.interpolate", + inputs={"input": input.name}, + outputs=[node.layer_name], + **layer_attrs) + + def Select(self, node): + assert len( + node.inputs) == 1, "The count of Select node\'s input is not 1." + input = self.graph.get_input_node(node, idx=0, copy=True) + in_shapes = node.in_shapes[0] + params = node.layer.select_param + layer_attrs = { + "in_shapes": in_shapes, + "point": params.slice_point, + "axis": params.axis} + self.paddle_graph.add_layer( + "custom_layer:select", + inputs={"x": input.name}, + outputs=[node.name], + **layer_attrs) diff --git a/x2paddle/op_mapper/static/caffe2paddle/caffe_shape.py b/x2paddle/op_mapper/static/caffe2paddle/caffe_shape.py deleted file mode 100644 index c574193d45400f351da0058f5d80b152cd6505cb..0000000000000000000000000000000000000000 --- a/x2paddle/op_mapper/static/caffe2paddle/caffe_shape.py +++ /dev/null @@ -1,364 +0,0 @@ -# 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 numbers -from functools import reduce - - -def get_kernel_parameters(params): - [k_h, k_w] = [1, 1] - if isinstance(params.kernel_size, numbers.Number): - [k_h, k_w] = [params.kernel_size] * 2 - elif len(params.kernel_size) > 0: - k_h = params.kernel_h if params.kernel_h > 0 else params.kernel_size[0] - k_w = params.kernel_w if params.kernel_w > 0 else params.kernel_size[ - len(params.kernel_size) - 1] - elif params.kernel_h > 0 or params.kernel_w > 0: - k_h = params.kernel_h - k_w = params.kernel_w - [s_h, s_w] = [1, 1] - if isinstance(params.stride, numbers.Number): - [s_h, s_w] = [params.stride] * 2 - elif len(params.stride) > 0: - s_h = params.stride_h if params.stride_h > 0 else params.stride[0] - s_w = params.stride_w if params.stride_w > 0 else params.stride[len( - params.stride) - 1] - elif params.stride_h > 0 or params.stride_w > 0: - s_h = params.stride_h - s_w = params.stride_w - [p_h, p_w] = [0, 0] - if isinstance(params.pad, numbers.Number): - [p_h, p_w] = [params.pad] * 2 - elif len(params.pad) > 0: - p_h = params.pad_h if params.pad_h > 0 else params.pad[0] - p_w = params.pad_w if params.pad_w > 0 else params.pad[len(params.pad) - - 1] - elif params.pad_h > 0 or params.pad_w > 0: - p_h = params.pad_h - p_w = params.pad_w - dila_h = dila_w = 1 - if hasattr(params, 'dilation'): - dila_len = len(params.dilation) - if dila_len == 2: - dila_h = params.dilation[0] - dila_w = params.dilation[1] - elif dila_len == 1: - dila_h = dila_w = params.dilation[0] - else: - assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( - dila_len) - return dila_h, dila_w, p_h, p_w, k_h, k_w, s_h, s_w - - -def get_strided_kernel_output_shape(params, input_shape, round_func): - i_h = input_shape[2] - i_w = input_shape[3] - dila_h, dila_w, pad_h, pad_w, kernel_h, kernel_w, stride_h, stride_w = get_kernel_parameters( - params) - o_h = (i_h + 2 * pad_h - (dila_h * - (kernel_h - 1) + 1)) / float(stride_h) + 1 - o_w = (i_w + 2 * pad_w - (dila_w * - (kernel_w - 1) + 1)) / float(stride_w) + 1 - o_h = int(round_func(o_h)) - o_w = int(round_func(o_w)) - has_c_o = hasattr(params, 'num_output') - c = params.num_output if has_c_o else input_shape[1] - return [[input_shape[0], c, o_h, o_w]] - - -def shape_convolution(layer, input_shape): - params = layer.convolution_param - return get_strided_kernel_output_shape(params, input_shape[0], math.floor) - - -def shape_deconvolution(layer, input_shape): - - h_i = input_shape[0][2] - w_i = input_shape[0][3] - - params = layer.convolution_param - dila_h, dila_w, pad_h, pad_w, kernel_h, kernel_w, stride_h, stride_w = get_kernel_parameters( - params) - - h_o = (h_i - 1) * stride_h - 2 * pad_h + dila_h * (kernel_h - 1) + 1 - w_o = (w_i - 1) * stride_w - 2 * pad_w + dila_w * (kernel_w - 1) + 1 - - has_c_o = hasattr(params, 'num_output') - c = params.num_output if has_c_o else input_shape.channels - return [[input_shape[0][0], c, h_o, w_o]] - - -def shape_pooling(layer, input_shape): - params = layer.pooling_param - global_pool = getattr(params, 'global_pooling', False) - if global_pool: - return [[input_shape[0][0], input_shape[0][1], 1, 1]] - - ceil_mode = getattr(params, 'ceil_mode', True) - if ceil_mode is True: - method = math.ceil - else: - method = math.floor - return get_strided_kernel_output_shape(params, input_shape[0], method) - - -def shape_innerproduct(layer, input_shape): - params = layer.inner_product_param - return [[input_shape[0][0], params.num_output]] - - -def shape_lrn(layer, input_shape): - return input_shape - - -def shape_relu(layer, input_shape): - return input_shape - - -def shape_softmax(layer, input_shape): - return input_shape - - -def shape_input(layer, input_shape): - return [list(layer.input_param.shape[0].dim)] - - -def shape_memorydata(layer, input_shape): - params = layer.memory_data_param - shape = [] - shape.append(int(params.batch_size)) - shape.append(int(params.channels)) - shape.append(int(params.height)) - shape.append(int(params.width)) - return [shape] - - -def shape_concat(layer, input_shape): - params = layer.concat_param - axis = params.axis - output_shape = None - for shape in input_shape: - if output_shape is None: - output_shape = [] - for i in range(len(shape)): - output_shape.append(shape[i]) - else: - output_shape[axis] += shape[axis] - return [output_shape] - - -def shape_slice(layer, input_shape): - inshape = input_shape[0] - - top_len = len(layer.top) - params = layer.slice_param - axis = params.axis - slice_dim = params.slice_dim - if slice_dim != 1 and axis == 1: - axis = slice_dim - points = list(params.slice_point) - count = inshape[axis] - if len(points) == 0: - assert count % top_len == 0, "the parameter of Slice is wrong" - part = count / top_len - t = part - while t < count: - points.append(int(t)) - t += part - points = [0] + points + [count] - output_shape = [] - for i in range(len(points)): - shape = [] - for ii in range(len(inshape)): - shape.append(inshape[ii]) - size = points[i + 1] - points[i] - shape[axis] = size - output_shape.append(shape) - if i == len(points) - 2: - break - return output_shape - - -def shape_prelu(layer, input_shape): - return input_shape - - -def shape_sigmoid(layer, input_shape): - return input_shape - - -def shape_absval(layer, input_shape): - return input_shape - - -def shape_accuracy(layer, input_shape): - return [[1]] - - -def shape_tanh(layer, input_shape): - return input_shape - - -def shape_eltwise(layer, input_shape): - return [input_shape[0]] - - -def shape_batchnorm(layer, input_shape): - return input_shape - - -def shape_scale(layer, input_shape): - return input_shape - - -def shape_reshape(layer, input_shape): - def count(num_list): - return reduce(lambda a, b: a * b, num_list) - - inshape = input_shape[0] - params = layer.reshape_param - axis = params.axis if hasattr(params, 'axis') else 0 - num_axes = params.num_axes if hasattr(params, 'num_axes') else -1 - if inshape[0] == -1: - inshape[0] = 1 - input_count = count(inshape) - - input_num_axes = len(inshape) - - input_start_axis = axis - start_axis = input_start_axis if input_start_axis >= 0 \ - else input_num_axes + input_start_axis + 1 - - assert start_axis >= 0, "[Reshape]axis %d out of range" % (input_start_axis) - assert start_axis <= input_num_axes, "[Reshape]axis %d out of range for %d-D input data"\ - % (input_start_axis, input_num_axes) - - assert num_axes >= -1, "[Reshape]num_axes must be >= 0, or -1 for all" - - end_axis = input_num_axes if num_axes == -1 else start_axis + num_axes - assert end_axis <= input_num_axes, "end_axis[%d] = axis[%d] + num_axes[%d] is out of range"\ - % (end_axis, start_axis, num_axes) - - num_axes_replaced = end_axis - start_axis - num_axes_retained = input_num_axes - num_axes_replaced - num_new_axes = len(list(params.shape.dim)) - outshape = [] - - for i in range(start_axis): - outshape.append(inshape[i]) - - for i in range(num_new_axes): - outshape.append(params.shape.dim[i]) - - for i in range(end_axis, input_num_axes): - outshape.append(inshape[i]) - - assert len(outshape) == num_axes_retained + num_new_axes,\ - "[Reshape]invalid dims of output shape[%s]" % (str(outshape)) - - inferred_axis = -1 - copy_axes = [] - constant_count = 1 - for i in range(num_new_axes): - top_dim = params.shape.dim[i] - if top_dim == 0: - copy_axes.append(i) - copy_axis_index = start_axis + i - outshape[copy_axis_index] = inshape[copy_axis_index] - elif top_dim == -1: - assert inferred_axis == -1, "[Reshape]new shape contains multiple -1 dims" - inferred_axis = i - else: - constant_count *= top_dim - - if inferred_axis >= 0: - explicit_count = constant_count - l = inshape[0:start_axis] - if len(l) > 0: - explicit_count *= count(l) - l = inshape[end_axis:] - if len(l) > 0: - explicit_count *= count(l) - for i in range(len(copy_axes)): - explicit_count *= outshape[start_axis + copy_axes[i]] - assert input_count % explicit_count == 0, "[Reshape]botom count[%d] "\ - "must be divisible by product of the specified dimensions[%d] "\ - % (input_count, explicit_count) - outshape[start_axis + inferred_axis] = int(input_count / explicit_count) - - output_count = count(outshape) - assert output_count == input_count, "[Reshape]output count[%d] must match input count[%d]" % ( - output_count, input_count) - outshape[0] = -1 - return [outshape] - - -def shape_argmax(layer, input_shape): - inshape = input_shape[0] - params = layer.argmax_param - out_max_val = params.out_max_val if hasattr(params, out_max_val) else False - top_k = params.top_k if hasattr(params, top_k) else 1 - axis = parmas.axis if hasattr(params, axis) else -1 - if axis < 0: - axis += len(inshape) - assert (axis + 1 == len(inshape) - ), 'only can be applied on the last dimension[axis:%d, %s] now,'\ - 'make sure you have set axis param in xxx.prototxt file' \ - % (axis, str(inshape)) - - outshape = inshape - outshape[-1] = top_k - if out_max_val is True: - outshape[-1] *= 2 - return [outshape] - - -def shape_crop(layer, input_shape): - assert len(input_shape) == 2, "the number of crop's inputs must be 2" - return [input_shape[1]] - - -def shape_flatten(layer, input_shape): - assert len(input_shape) == 1, "the number of flatten's inputs must be 1" - inshape = input_shape[0] - params = layer.flatten_param - start_axis = params.axis - end_axis = params.end_axis - if start_axis < 0: - start_axis += len(inshape) - if end_axis < 0: - end_axis += len(inshape) + 1 - assert start_axis <= end_axis, 'invalid axis[%d] or end_axis[%d] params'\ - % (start_axis, end_axis) - output_shape = inshape[0:start_axis] - if len(inshape[start_axis:end_axis]) != 0: - flat_sz = reduce(lambda a, b: a * b, inshape[start_axis:end_axis]) - output_shape += [flat_sz] - output_shape += inshape[end_axis:len(inshape)] - output_shape[0] = -1 - return [output_shape] - - -def shape_power(layer, input_shape): - return input_shape - - -def shape_reduction(layer, input_shape): - params = layer.reduction_param - axis = params.axis - if axis < 0: - axis += len(input_shape[0]) + 1 - assert axis <= len(input_shape[0]), 'invalid axis[%d] error' % (axis) - return [input_shape[0:axis]] diff --git a/x2paddle/optimizer/fusion/static/bn_scale_fuser.py b/x2paddle/optimizer/fusion/static/bn_scale_fuser.py index 05d178026623f509ea3595c298677b79f42454e0..946e7fe804d58a0885a5b1d3e1decc7d16e1d687 100644 --- a/x2paddle/optimizer/fusion/static/bn_scale_fuser.py +++ b/x2paddle/optimizer/fusion/static/bn_scale_fuser.py @@ -20,87 +20,126 @@ from x2paddle.core.util import * class Static_BNScaleFuser(FuseBase): def __init__(self): - super(Static_BNScaleFuser, self).__init__(graph_type="dygraph") + super(Static_BNScaleFuser, self).__init__(graph_type="static") + patterns = list() def build_pattern(self): """ 描述需要替换的batchnorm2d图结构。 batchnorm2d层模式python实现代码示例: - conv5_bn = fluid.layers.batch_norm(input=conv5, is_test=True, param_attr=None, bias_attr=None, moving_mean_name='conv5_bn_mean', moving_variance_name='conv5_bn_variance', epsilon=9.999999747378752e-06, name='conv5_bn') - conv5_scale_scale = fluid.ParamAttr(name='conv5_scale_scale') - conv5_scale_cparam1 = fluid.layers.create_parameter(attr=conv5_scale_scale, dtype=conv5_bn.dtype, shape=[256], name='conv5_scale_cparam1', is_bias=True, default_initializer=Constant(value=1.0)) - conv5_scale_mul = fluid.layers.elementwise_mul(x=conv5_bn, y=conv5_scale_cparam1, axis=1) - conv5_scale_offset = fluid.ParamAttr(name='conv5_scale_offset') - conv5_scale_cparam2 = fluid.layers.create_parameter(attr=conv5_scale_offset, dtype=conv5_bn.dtype, shape=[256], name='conv5_scale_cparam2', is_bias=True, default_initializer=Constant(value=1.0)) - conv5_scale = fluid.layers.elementwise_add(x=conv5_scale_mul, y=conv5_scale_cparam2, axis=1) + 模式一: + conv1_bn = paddle.nn.functional.batch_norm(x=conv1, weight=conv1_bn_weight, bias=conv1_bn_bias, running_mean=conv1_bn_mean, running_var=conv1_bn_variance, epsilon=9.999999747378752e-06, momentum=0.9990000128746033) + conv1_scale_cparam1 = paddle.static.create_parameter(shape=(32,), dtype='float32', name='conv1_scale_cparam1') + conv1_scale_mul = paddle.multiply(x=conv1_bn, y=conv1_scale_cparam1, axis=1) + conv1_scale_cparam2 = paddle.static.create_parameter(shape=(32,), dtype='float32', name='conv1_scale_cparam2') + conv1_scale_cparam2 = paddle.reshape(x=conv1_scale_cparam2, shape=[32, 1, 1]) + conv1_scale = paddle.add(x=conv1_scale_mul, y=conv1_scale_cparam2) + 模式二: + conv1_bn = paddle.nn.functional.batch_norm(x=conv1, weight=conv1_bn_weight, bias=conv1_bn_bias, running_mean=conv1_bn_mean, running_var=conv1_bn_variance, epsilon=9.999999747378752e-06, momentum=0.9990000128746033) + conv1_scale_cparam1 = paddle.static.create_parameter(shape=(32,), dtype='float32', name='conv1_scale_cparam1') + conv1_scale_mul = paddle.multiply(x=conv1_bn, y=conv1_scale_cparam1, axis=1) + conv1_scale_cparam2 = paddle.static.create_parameter(shape=(32,), dtype='float32', name='conv1_scale_cparam2') + conv1_scale = paddle.add(x=conv1_scale_mul, y=conv1_scale_cparam2) """ def gen_name(id): return "x" + str(id) - self.pattern.add_layer( - "fluid.layers.batch_norm", - inputs={"input": "bn-input-0"}, + pattern = PaddleGraph(graph_type="dygraph") + pattern.add_layer( + "paddle.nn.functional.batch_norm", + inputs={"input": "bn-input-0", + "weight": "bn-input-1", + "bias": "bn-input-2", + "running_mean": "bn-input-3", + "running_var": "bn-input-4",}, outputs=[gen_name(0)]) - self.pattern.add_layer( - "fluid.ParamAttr", + pattern.add_layer( + "paddle.static.create_parameter", inputs={}, outputs=[gen_name(1)]) - self.pattern.add_layer( - "fluid.layers.create_parameter", - inputs={"attr": gen_name(1)}, - outputs=[gen_name(2)]) inputs_dict = {} inputs_dict['x'] = gen_name(0) - inputs_dict['y'] = gen_name(2) - self.pattern.add_layer( - "fluid.layers.elementwise_mul", + inputs_dict['y'] = gen_name(1) + pattern.add_layer( + "paddle.multiply", inputs=inputs_dict, - outputs=[gen_name(3)]) - self.pattern.add_layer( - "fluid.ParamAttr", + outputs=[gen_name(2)]) + pattern.add_layer( + "paddle.static.create_parameter", inputs={}, + outputs=[gen_name(3)]) + pattern.add_layer( + "paddle.reshape", + inputs={"x": gen_name(3)}, outputs=[gen_name(4)]) - self.pattern.add_layer( - "fluid.layers.create_parameter", - inputs={"attr": gen_name(4)}, + inputs_dict = {} + inputs_dict['x'] = gen_name(2) + inputs_dict['y'] = gen_name(4) + pattern.add_layer( + "paddle.add", + inputs=inputs_dict, outputs=[gen_name(5)]) + pattern.build(inputs={"input-0": "bn-input-0", + "input-1": "bn-input-1", + "input-2": "bn-input-2", + "input-3": "bn-input-3", + "input-4": "bn-input-4"}) + self.patterns.append(pattern) + + pattern = PaddleGraph(graph_type="dygraph") + pattern.add_layer( + "paddle.nn.functional.batch_norm", + inputs={"input": "bn-input-0", + "weight": "bn-input-1", + "bias": "bn-input-2", + "running_mean": "bn-input-3", + "running_var": "bn-input-4",}, + outputs=[gen_name(0)]) + pattern.add_layer( + "paddle.static.create_parameter", + inputs={}, + outputs=[gen_name(1)]) inputs_dict = {} - inputs_dict['x'] = gen_name(3) - inputs_dict['y'] = gen_name(5) - self.pattern.add_layer( - "fluid.layers.elementwise_add", + inputs_dict['x'] = gen_name(0) + inputs_dict['y'] = gen_name(1) + pattern.add_layer( + "paddle.multiply", inputs=inputs_dict, - outputs=[gen_name(6)]) - self.pattern.build(inputs={"input-0": "bn-input-0"}) + outputs=[gen_name(2)]) + pattern.add_layer( + "paddle.static.create_parameter", + inputs={}, + outputs=[gen_name(3)]) + inputs_dict = {} + inputs_dict['x'] = gen_name(2) + inputs_dict['y'] = gen_name(3) + pattern.add_layer( + "paddle.add", + inputs=inputs_dict, + outputs=[gen_name(4)]) + pattern.build(inputs={"input-0": "bn-input-0", + "input-1": "bn-input-1", + "input-2": "bn-input-2", + "input-3": "bn-input-3", + "input-4": "bn-input-4"}) + self.patterns.append(pattern) def insert_new_layer(self, graph, parameters, matches): new_layer = self.gen_new_layer(parameters, matches) - new_layer_id = list(matches.keys())[0] + new_layer_id = list(matches.keys())[-1] graph.layers[new_layer_id] = new_layer + matches.pop(list(matches.keys())[1]) + matches.pop(list(matches.keys())[2]) matches.pop(new_layer_id) def gen_new_layer(self, parameters, matches): layers_id = list(matches.keys()) - layer = matches[layers_id[0]] - layer_inputs = layer.inputs - layer_name = layer.outputs[0] - layer_attrs = layer.attrs - layer_attrs["param_attr"] = string("{}_scale".format(layer_name)) - layer_attrs["bias_attr"] = string("{}_offset".format(layer_name)) - layer = matches[layers_id[-1]] - layer_outputs = layer.outputs + bn_layer = matches[layers_id[0]] layer = matches[layers_id[1]] - layer_name = layer.outputs[0] - scale_numpy = parameters.pop(layer_name) - parameters[layer_attrs["param_attr"][1: -1]] = scale_numpy - layer = matches[layers_id[4]] - layer_name = layer.outputs[0] - scale_numpy = parameters.pop(layer_name) - parameters[layer_attrs["bias_attr"][1: -1]] = scale_numpy - new_layer = PaddleLayer( - layers_id[0], - "fluid.layers.batch_norm", - inputs=layer_inputs, - outputs=layer_outputs, - **layer_attrs) - return new_layer \ No newline at end of file + bn_layer.inputs["weight"] = layer.outputs[0] + layer = matches[layers_id[3]] + bn_layer.inputs["bias"] = layer.outputs[0] + bn_layer.id = layers_id[-1] + layer = matches[layers_id[-1]] + bn_layer.outputs = layer.outputs + return bn_layer \ No newline at end of file