Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
X2Paddle
提交
ab978c43
X
X2Paddle
项目概览
PaddlePaddle
/
X2Paddle
大约 1 年 前同步成功
通知
328
Star
698
Fork
167
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
26
列表
看板
标记
里程碑
合并请求
4
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
X
X2Paddle
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
26
Issue
26
列表
看板
标记
里程碑
合并请求
4
合并请求
4
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
ab978c43
编写于
8月 09, 2019
作者:
S
SunAhong1993
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add directly map op
上级
e8ebd8da
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
134 addition
and
90 deletion
+134
-90
add_caffe_custom_layer.md
add_caffe_custom_layer.md
+58
-46
tools/compile.sh
tools/compile.sh
+24
-0
x2paddle/op_mapper/caffe_op_mapper.py
x2paddle/op_mapper/caffe_op_mapper.py
+38
-41
x2paddle/op_mapper/caffe_shape.py
x2paddle/op_mapper/caffe_shape.py
+14
-3
未找到文件。
add_caffe_custom_layer.md
浏览文件 @
ab978c43
## 如何转换Caffe自定义Layer
本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。
本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。
***步骤一 下载代码**
*
此处涉及修改源码,应先卸载x2paddle,并且下载源码,主要有以下两步完成:
```
pip uninstall x2paddle
pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop
```
***步骤二 编译caffe.proto**
*
该步骤依赖protobuf编译器,其安装过程有以下两种方式:
> 选择一:pip install protobuf
> 选择二:使用[官方源码](https://github.com/protocolbuffers/protobuf)进行编译
***步骤一 编译caffe.proto**
*
使用脚本./tools/compile.sh将caffe.proto(包含所需的自定义Layer信息)编译成我们所需的目标语言(Python)
使用脚本./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也将保存在该路径下
```
***步骤二 添加自定义Layer的实现代码**
*
-
进入./x2paddle/op_mapper/caffe_custom_layer,创建实现代码的文件,例如mylayer.py
-
仿照./x2paddle/op_mapper/caffe_custom_layer中的其他文件,在mylayer.py中主要实现3个函数:
1.
`def mylayer_shape(input_shape, ...)`
| 参数 | 类型 | 说明 |
| :---------: | :--: | :---------: |
| input_shape | list | 每个元素代表该层每个输入数据的shape |
| 其余 | 默认为None | 命名为Caffe模型的model.prototxt中mylayer_param中每个参数的名字 |
***步骤三 添加自定义Layer的实现代码**
*
-
进入./x2paddle/op_mapper/caffe_custom_layer,创建.py文件,例如mylayer.py
-
仿照./x2paddle/op_mapper/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一致
功能:计算出mylayer的输出shape
返回:一个list,其中每个元素代表每个输出数据的shape
2.
`def mylayer_layer(inputs, input_shape=None, name=None, ...)`
| 参数 | 类型 | 说明 |
| :---------: | :--: | :---------: |
| inputs | list | 每个元素代表该层每个输入数据 |
| input_shape | list(默认为None) | 每个元素代表该层每个输入数据的shape |
| name | str(默认为None) | mylayer的名字 |
| 其余 | 默认为None | 命名为Caffe模型的model.prototxt中mylayer_param中每个参数的名字 |
功能:运用PaddlePaddle完成组网来实现
`mylayer`
的功能
返回:一个Variable或Tensor,为组网后的结果
3.
`def mylayer_weights(name, data=None)`
| 参数 | 类型 | 说明 |
| :---------: | :--: | :---------: |
| name | str | mylayer的名字 |
| data | list(默认为None) | 由Caffe模型的model.caffemodel获得的关于mylayer的参数 |
功能:为每个参数(例如kernel、bias等)命名;同时,若Caffe中该层参数与PaddlePaddle中参数的格式不一致,则变换操作也在该函数中实现。
功能:运用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,包含每个参数的名字。
-
在
mylayer.py中注册
`mylayer
`
,主要运用下述代码实现:
-
在
roipooling.py中注册
`roipooling
`
,主要运用下述代码实现:
```
register(kind='
Mylayer', shape=mylayer_shape, layer=mylayer_layer, weights=mylayer
_weights)
# kind为在model.prototxt中
mylayer
的type
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
mylayer
from . import
roipooling
```
***步骤
三
运行转换代码**
*
***步骤
四
运行转换代码**
*
```
x2paddle --framework=caffe
--prototxt=deploy.proto
--weight=deploy.caffemodel
--save_dir=pd_model
# 在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
```
tools/compile.sh
0 → 100644
浏览文件 @
ab978c43
#!/bin/bash
#function:
# script used to generate caffe_pb2.py from caffe.proto using protoc
#
PROTOC
=
`
which protoc
`
if
[[
-z
$PROTOC
]]
;
then
echo
"not found protoc, you should first install it following this[https://github.com/google/protobuf/releases]"
exit
1
fi
WORK_ROOT
=
$1
PY_NAME
=
"
$WORK_ROOT
/caffe_pb2.py"
$PROTOC
--proto_path
=
$WORK_ROOT
--python_out
=
$WORK_ROOT
$WORK_ROOT
/caffe.proto
ret
=
$?
if
[
-e
"
$PY_NAME
"
]
;
then
echo
"succeed to generate [
$PY_NAME
]"
exit
0
else
echo
"failed to generate [
$PY_NAME
]"
fi
exit
$ret
x2paddle/op_mapper/caffe_op_mapper.py
浏览文件 @
ab978c43
...
...
@@ -22,6 +22,13 @@ from x2paddle.op_mapper.caffe_custom_layer import *
class
CaffeOpMapper
(
OpMapper
):
directly_map_ops
=
{
'ReLU'
:
'relu'
,
'AbsVal'
:
'abs'
,
'Sigmoid'
:
'sigmoid'
,
'TanH'
:
'tanh'
,
}
def
__init__
(
self
,
decoder
):
super
(
CaffeOpMapper
,
self
).
__init__
()
self
.
graph
=
decoder
.
caffe_graph
...
...
@@ -40,8 +47,12 @@ class CaffeOpMapper(OpMapper):
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
(
"Model are not supported yet."
)
raise
Exception
(
"The op {} in model is not supported yet."
.
format
(
op
))
def
op_checker
(
self
):
unsupported_ops
=
set
()
...
...
@@ -180,6 +191,22 @@ class CaffeOpMapper(OpMapper):
output
=
node
,
param_attr
=
attr
)
def
MemoryData
(
self
,
node
):
# TODO(syf): Paddlepaddle can't fully support
shape
=
node
.
output_shape
[
0
][
1
:]
dtype
=
'float32'
attr
=
{
'dtype'
:
string
(
dtype
),
'shape'
:
shape
,
'name'
:
string
(
node
.
layer_name
)
}
node
.
fluid_code
.
add_layer
(
"data"
,
inputs
=
None
,
output
=
node
.
layer_name
+
'0'
,
param_attr
=
attr
)
node
.
fluid_code
.
add_note
(
'{} = [{}]'
.
format
(
node
.
layer_name
,
node
.
layer_name
+
'0'
))
def
Convolution
(
self
,
node
):
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
(
...
...
@@ -290,16 +317,6 @@ class CaffeOpMapper(OpMapper):
output
=
node
,
param_attr
=
attr
)
def
ReLU
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
1
,
'The count of ReLU node
\'
s input is not 1.'
input
=
self
.
graph
.
get_bottom_node
(
node
,
idx
=
0
,
copy
=
True
)
attr
=
{
'name'
:
string
(
node
.
layer_name
)}
node
.
fluid_code
.
add_layer
(
"relu"
,
inputs
=
input
,
output
=
node
,
param_attr
=
attr
)
def
LRN
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
1
,
'The count of LRN node
\'
s input is not 1.'
params
=
node
.
layer
.
lrn_param
...
...
@@ -445,26 +462,6 @@ class CaffeOpMapper(OpMapper):
output
=
node
,
param_attr
=
attr
)
def
Sigmoid
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
1
,
'The count of PReLU node
\'
s input is not 1.'
input
=
self
.
graph
.
get_bottom_node
(
node
,
idx
=
0
,
copy
=
True
)
attr
=
{
'name'
:
string
(
node
.
layer_name
)}
node
.
fluid_code
.
add_layer
(
"sigmoid"
,
inputs
=
input
,
output
=
node
,
param_attr
=
attr
)
def
AbsVal
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
1
,
'The count of PReLU node
\'
s input is not 1.'
input
=
self
.
graph
.
get_bottom_node
(
node
,
idx
=
0
,
copy
=
True
)
attr
=
{
'name'
:
string
(
node
.
layer_name
)}
node
.
fluid_code
.
add_layer
(
"absval"
,
inputs
=
input
,
output
=
node
,
param_attr
=
attr
)
def
Accuracy
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
2
,
'The count of Accuracy node
\'
s input is not 2.'
...
...
@@ -492,16 +489,6 @@ class CaffeOpMapper(OpMapper):
output
=
node
,
param_attr
=
attr
)
def
TanH
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
1
,
'The count of TanH node
\'
s input is not 1.'
input
=
self
.
graph
.
get_bottom_node
(
node
,
idx
=
0
,
copy
=
True
)
attr
=
{
'name'
:
string
(
node
.
layer_name
)}
node
.
fluid_code
.
add_layer
(
"tanh"
,
inputs
=
input
,
output
=
node
,
param_attr
=
attr
)
def
Eltwise
(
self
,
node
):
assert
len
(
node
.
inputs
)
==
2
,
'The count of TanH node
\'
s input is not 2.'
...
...
@@ -892,3 +879,13 @@ class CaffeOpMapper(OpMapper):
is_custom_layer
=
True
)
if
op
not
in
self
.
used_custom_layers
:
self
.
used_custom_layers
[
op
]
=
custom_code
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_bottom_node
(
node
,
idx
=
0
,
copy
=
True
)
attr
=
{
'name'
:
string
(
node
.
layer_name
)}
node
.
fluid_code
.
add_layer
(
op_info
,
inputs
=
input
,
output
=
node
,
param_attr
=
attr
)
x2paddle/op_mapper/caffe_shape.py
浏览文件 @
ab978c43
...
...
@@ -74,11 +74,12 @@ def shape_convolution(layer, input_shape):
def
shape_deconvolution
(
layer
,
input_shape
):
h_i
=
input_shape
[
2
]
w_i
=
input_shape
[
3
]
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_
params_w_h
(
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
...
...
@@ -124,6 +125,16 @@ 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
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录