Skip to content

Latest commit

 

History

History
162 lines (144 loc) · 7.86 KB

add_caffe_custom_layer.md

File metadata and controls

162 lines (144 loc) · 7.86 KB

如何转换Caffe自定义Layer

本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。 目前,代码中已经提供了10个非官方op(不在官网上的op)的转换,这些op对应的Caffe实现源码如下:

op 该版本实现源码
PriorBox code
DetectionOutput code
ConvolutionDepthwise code
ShuffleChannel code
Permute code
Normalize code
ROIPooling code
Axpy code
ReLU6 code
Upsample code

添加代码实现自定义层的步骤如下:

步骤一 下载代码
此处涉及修改源码,应先卸载x2paddle,并且下载源码,主要有以下两步完成:

pip uninstall x2paddle
pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop

步骤二 编译caffe.proto
该步骤依赖protobuf编译器,其安装过程有以下两种方式:

选择一:pip install protobuf (protobuf >= 3.6.0) 选择二:使用官方源码进行编译

使用脚本./tools/compile.sh将caffe.proto(包含所需的自定义Layer信息)编译成我们所需的目标语言(Python)
使用方式:

bash ./toos/compile.sh /home/root/caffe/src/caffe/proto
# /home/root/caffe/src/caffe/proto为caffe.proto的存放路径,生成的caffe_pb2.py也将保存在该路径下

将生成的caffe_pb2.py替换x2paddle/decoder下的caffe_pb2.py。

步骤三 添加自定义Layer的实现代码

【注意】若Caffe自定义layer与Paddle的op一一对应,使用方式一,否则使用方式二。

  • 方式一:
  1. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py中的CaffeOpMapper类中的映射方法(输入为self和node),实现类似的映射方法,以下述的映射方法为例:
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(
        "paddle.transpose",
        inputs={"x": input.name},
        outputs=[node.layer_name],
        perm=order)

需完成的步骤: a. 获取Caffe Layer的属性,并对应转换为Paddle的属性。 b. 获取当前Layer的输入。 c. 使用self.paddle_graph.add_layer为PaddleGraph添加layer。其中,第一个参数代表Paddle的kernel;inputs是一个字典,用于存储paddle中的输入的key与其输入名字;outputs是一个列表,用于存储输出的名字;其余参数为属性对应关系。

  1. 仿照./x2paddle/decoder/caffe_shape_inference.py中的shape_xx方法,实现获取当前Layer输出大小的函数,以下述方法为例:
def shape_permute(layer, input_shape):
    order = layer.permute_param.order
    inshape = input_shape[0]
    output_shape = []
    order = list(order)
    for ii in order:
        assert ii < len(inshape), "invalid order for permute[%s]" % (name)
        output_shape.append(inshape[ii])
    return [output_shape]

参数: layer (caffe_pb2.LayerParameter): caffe的Layer,可用于获取当前Layer的属性。 input_shape (list): 其中每个元素代表该层每个输入数据的大小。

  • 方式二:
  1. 进入./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_custom_layer,创建.py文件,例如mylayer.py
  2. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_custom_layer中的其他文件,在mylayer.py中主要需要实现1个类,下面以roipooling.py为例分析代码:
class ROIPooling(object):
    def __init__(self, pooled_height, pooled_width, spatial_scale):
        self.roipooling_layer_attrs = {
            "pooled_height": pooled_height,
            "pooled_width": pooled_width,
            "spatial_scale": spatial_scale}

    def __call__(self, x0, x1):
        slice_x1 = paddle.slice(input=x1, axes=[1], 
                                starts=[1], ends=[5])
        out = fluid.layers.roi_pool(input=x0, 
                                    rois=slice_x1, 
                                    **self.roipooling_layer_attrs)
        return out

__init__函数:用于初始化各个属性 __call__函数:用于组合实现当前Layer的前向,输入为当前Layer所需要的输入

  1. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py中的CaffeOpMapper类中的映射方法(输入为self和node),实现类似的映射方法,以下述的映射方法为例:
def ROIPooling(self, node):
      roipooling_name = name_generator("roipooling", self.nn_name2id)
      output_name = node.layer_name
      layer_outputs = [roipooling_name, output_name]
      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=layer_outputs,
          **layer_attrs)

需完成的步骤: a. 获取Caffe Layer的属性,并对应转换为Paddle的属性。 b. 获取当前Layer的输入。 c. 使用self.paddle_graph.add_layer为PaddleGraph添加layer。其中,第一个参数代表Paddle的kernel(此处kernel必须以“custom_layer:“开头);inputs是一个字典,用于存储paddle中的输入的key与其输入名字;outputs是一个列表,用于存储输出的名字;其余参数为属性对应关系。

  1. 仿照./x2paddle/decoder/caffe_shape_inference.py中的shape_xx方法,实现获取当前Layer输出大小的函数,以下述方法为例:
def shape_roipooling(layer, input_shape):
  pooled_w = layer.roi_pooling_param.pooled_w
  pooled_h = layer.roi_pooling_param.pooled_h
  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]

参数: layer (caffe_pb2.LayerParameter): caffe的Layer,可用于获取当前Layer的属性。 input_shape (list): 其中每个元素代表该层每个输入数据的大小。

步骤四 运行转换代码

# 在X2Paddle目录下安装x2paddle
python setup.py install
# 运行转换代码
x2paddle --framework=caffe
         --prototxt=deploy.proto
         --weight=deploy.caffemodel
         --save_dir=pd_model
         --caffe_proto=/home/root/caffe/src/caffe/proto/caffe_pb2.py