Skip to content

Commit eed14f6

Browse files
committed
doc: add the discription doc of theseus layer
1 parent 304bafb commit eed14f6

File tree

1 file changed

+243
-0
lines changed

1 file changed

+243
-0
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# TheseusLayer 使用说明
2+
3+
基于 TheseusLayer 构建的网络模型,支持网络截断、返回网络中间层输出和修改网络中间层的功能。
4+
5+
---
6+
7+
## 目录
8+
9+
- [1. 前言](#1)
10+
- [2. 网络层描述符说明](#2)
11+
- [3. 功能介绍](#3)
12+
- [3.1 网络截断(stop_after)](#3.1)
13+
- [3.2 返回网络中间层输出(update_res)](#3.2)
14+
- [3.3 修改网络中间层(upgrade_sublayer)](#3.3)
15+
16+
<a name="1"></a>
17+
18+
## 1. 前言
19+
20+
`TheseusLayer` 是继承了 `nn.Layer` 的子类,使用 `TheseusLayer` 作为父类构建的网络模型,可以通过 `TheseusLayer``stop_after()``update_res()``upgrade_sublayer()` 实现网络截断、返回中间层输出以及修改网络中间层的功能。目前 PaddleClas 中 `ppcls.arch.backbone.legendary_models` 下的所有模型均支持上述操作。
21+
22+
如需基于 `TheseusLayer` 构建新的网络结构,只需继承 `TheseusLayer` 即可:
23+
24+
```python
25+
from ppcls.arch.backbone.base.theseus_layer import TheseusLayer
26+
27+
class net(TheseusLayer):
28+
def __init__():
29+
super().__init__()
30+
31+
def forward(x):
32+
pass
33+
```
34+
35+
<a name="2"></a>
36+
37+
## 2. 网络层描述符说明
38+
39+
使用 `TheseusLayer` 提供的方法对模型进行操作/修改时,需要通过参数指定网络中间层,因此 `TheseusLayer` 规定了用于描述网络中间层的网络层描述符。网络层描述符为 Python 字符串(str)类型,使用网络层对象的变量名指定子层,以 `.` 作为网络层级的分隔符,对于 `nn.Sequential` 类型的层,使用 `["index"]` 指定其子层。
40+
41+
`MobileNetV1` 网络为例,其模型结构定义在 [MobileNetV1](../../../ppcls/arch/backbone/legendary_models/mobilenet_v1.py)。网络 `MobileNetV1``conv``blocks``avg_pool``flatten``fc` 4 个子层组成,其中 `blocks``nn.Sequential` 类型对象,包括 13 层 `DepthwiseSeparable` 类型的子层,`DepthwiseSeparable` 又由 `depthwise_conv``pointwise_conv` 2 个子层组成,`depthwise_conv``pointwise_conv` 均为 `ConvBNLayer` 类型对象,由 `conv``bn``relu` 3 层子层组成,如下图所示:
42+
43+
```
44+
MobileNetV1 (TheseusLayer)
45+
├── conv (ConvBNLayer)
46+
│   ├── conv (nn.Conv2D)
47+
│   ├── bn (nn.BatchNorm)
48+
│   └── relu (nn.ReLU)
49+
50+
├── blocks (nn.Sequential)
51+
│   ├── blocks0 (DepthwiseSeparable)
52+
│   │   ├── depthwise_conv (ConvBNLayer)
53+
│   │   │   ├── conv (nn.Conv2D)
54+
│   │   │   ├── bn (nn.BatchNorm)
55+
│   │   │   └── relu (nn.ReLU)
56+
│   │   └── pointwise_conv (ConvBNLayer)
57+
│   │   ├── conv (nn.Conv2D)
58+
│   │   ├── bn (nn.BatchNorm)
59+
│   │   └── relu (nn.ReLU)
60+
│   .
61+
│   .
62+
│   .
63+
│   └── blocks12 (DepthwiseSeparable)
64+
│      ├── depthwise_conv (ConvBNLayer)
65+
│      │   ├── conv (nn.Conv2D)
66+
│      │   ├── bn (nn.BatchNorm)
67+
│      │   └── relu (nn.ReLU)
68+
│      └── pointwise_conv (ConvBNLayer)
69+
│      ├── conv (nn.Conv2D)
70+
│      ├── bn (nn.BatchNorm)
71+
│      └── relu (nn.ReLU)
72+
73+
├── avg_pool (nn.AdaptiveAvgPool2D)
74+
75+
├── flatten (nn.Flatten)
76+
77+
└── fc (nn.Linear)
78+
```
79+
80+
因此,对于 `MobileNetV1` 而言:
81+
* 网络层描述符 `blocks[0].depthwise_conv.conv`,其指定了网络 `MobileNetV1``blocks` 层中的第 `1``DepthwiseSeparable` 对象中的 `depthwise_conv` 中的 `conv` 这一层;
82+
* 网络层描述符 `blocks[5]`,其指定了网络 `MobileNetV1``blocks` 层中的第 `6``DepthwiseSeparable` 对象这一层;
83+
* 网络层描述符 `flatten`,其指定了网络 `MobileNetV1``flatten` 这一层。
84+
85+
<a name="3"></a>
86+
87+
## 3. 方法说明
88+
89+
PaddleClas 提供的 backbone 网络均基于图像分类数据集训练得到,因此网络的尾部带有用于分类的全连接层,而在特定任务场景下,需要去掉分类的全连接层。在部分下游任务中,例如目标检测场景,需要获取到网络中间层的输出结果,也可能需要对网络的中间层进行修改,因此 `TheseusLayer` 提供了 3 个接口函数用于实现不同的修改功能。
90+
91+
<a name="3.1"></a>
92+
93+
### 3.1 网络截断(stop_after)
94+
95+
```python
96+
def stop_after(self, stop_layer_name: str) -> bool:
97+
"""stop forward and backward after 'stop_layer_name'.
98+
99+
Args:
100+
stop_layer_name (str): The name of layer that stop forward and backward after this layer.
101+
102+
Returns:
103+
bool: 'True' if successful, 'False' otherwise.
104+
"""
105+
```
106+
107+
该方法可通过参数 `stop_layer_name` 指定网络中的特定子层,并将该层之后的所有层修改为映射层(`Identity`),从而达到网络截断的目的。映射层(`Identity`)的定义如下:
108+
109+
```python
110+
class Identity(nn.Layer):
111+
def __init__(self):
112+
super(Identity, self).__init__()
113+
114+
def forward(self, inputs):
115+
return inputs
116+
```
117+
118+
当该方法成功执行时,其返回值为 `True`,否则为 `False`
119+
120+
`MobileNetV1` 网络为例,当 `stop_layer_name``"blocks[0].depthwise_conv.conv"`,该方法:
121+
* 将网络 `MobileNetV1``avg_pool``flatten``fc` 置为 `Identity`
122+
*`blocks` 层的第 2 至 第 13 个子层置为 `Identity`
123+
*`blocks` 层第 1 个子层的 `pointwise_conv` 置为 `Identity`
124+
*`blocks` 层第 1 个子层的 `depthwise_conv``bn``relu` 置为 `Identity`
125+
126+
具体效果可以参考下方代码案例进行尝试。
127+
128+
```python
129+
import paddleclas
130+
131+
net = paddleclas.MobileNetV1()
132+
print("========== the origin mobilenetv1 net arch ==========")
133+
print(net)
134+
135+
res = net.stop_after(stop_layer_name="blocks[0].depthwise_conv.conv")
136+
print("The result returned by stop_after(): ", res)
137+
# The result returned by stop_after(): True
138+
139+
print("\n\n========== the truncated mobilenetv1 net arch ==========")
140+
print(net)
141+
```
142+
143+
<a name="3.2"></a>
144+
145+
### 3.2 返回网络中间层输出(update_res)
146+
147+
```python
148+
def update_res(
149+
self,
150+
return_patterns: Union[str, List[str]]) -> Dict[str, nn.Layer]:
151+
"""update the result(s) to be returned.
152+
153+
Args:
154+
return_patterns (Union[str, List[str]]): The name of layer to return output.
155+
156+
Returns:
157+
Dict[str, nn.Layer]: The pattern(str) and corresponding layer(nn.Layer) that have been set successfully.
158+
"""
159+
```
160+
161+
该方法可通过参数 `return_patterns` 指定一层或多层网络的中间子层,并在网络前向时,将指定层的输出结果与网络的最终结果一同返回。该方法的返回值为 `dict` 对象,元素为设置成功的层,其中,key 为设置成功的网络层描述符,value 为对应的网络层对象。
162+
163+
`MobileNetV1` 网络为例,当 `return_patterns``["blocks[0]", "blocks[2]", "blocks[4]", "blocks[10]"]`,在网络前向推理时,网络的输出结果将包含以上 4 层的输出,具体效果可以参考下方代码案例进行尝试。
164+
165+
```python
166+
import numpy as np
167+
import paddle
168+
import paddleclas
169+
170+
np_input = np.zeros((1, 3, 224, 224))
171+
pd_input = paddle.to_tensor(np_input, dtype="float32")
172+
173+
net = paddleclas.MobileNetV1(pretrained=True)
174+
175+
output = net(pd_input)
176+
print("The output's type of origin net: ", type(output))
177+
# The output's type of origin net: <class 'paddle.Tensor'>
178+
179+
res = net.update_res(return_patterns=["blocks[0]", "blocks[2]", "blocks[4]", "blocks[10]"])
180+
print("The result returned by update_res(): ", res)
181+
# The result returned by update_res(): {'blocks[0]': ...}
182+
183+
output = net(pd_input)
184+
print("The output's keys of processed net: ", output.keys())
185+
# The output's keys of net: dict_keys(['output', 'blocks[0]', 'blocks[2]', 'blocks[4]', 'blocks[10]'])
186+
```
187+
188+
除了通过调用方法 `update_res()` 的方式之外,也同样可以在实例化网络对象时,通过指定参数 `return_patterns` 实现相同效果:
189+
190+
```python
191+
net = paddleclas.MobileNetV1(pretrained=True, return_patterns=["blocks[0]", "blocks[2]", "blocks[4]", "blocks[10]"])
192+
```
193+
194+
<a name="3.3"></a>
195+
196+
### 3.3 修改网络中间层(upgrade_sublayer)
197+
198+
```python
199+
def upgrade_sublayer(self,
200+
layer_name_pattern: Union[str, List[str]],
201+
handle_func: Callable[[nn.Layer, str], nn.Layer]
202+
) -> Dict[str, nn.Layer]:
203+
"""use 'handle_func' to modify the sub-layer(s) specified by 'layer_name_pattern'.
204+
205+
Args:
206+
layer_name_pattern (Union[str, List[str]]): The name of layer to be modified by 'handle_func'.
207+
handle_func (Callable[[nn.Layer, str], nn.Layer]): The function to modify target layer specified by 'layer_name_pattern'. The formal params are the layer(nn.Layer) and pattern(str) that is (a member of) layer_name_pattern (when layer_name_pattern is List type). And the return is the layer processed.
208+
209+
Returns:
210+
Dict[str, nn.Layer]: The key is the pattern and corresponding value is the result returned by 'handle_func()'.
211+
"""
212+
```
213+
214+
该方法可通过参数 `layer_name_pattern` 指定一层或多层网络子层,并使用参数 `handle_func` 所指定的函数对指定的子层进行修改。该方法的返回值为 `dict`,元素为修改的层,其中,key 为指定的网络层描述符,value 为 `handle_func` 针对该层的返回结果。
215+
216+
`upgrade_sublayer` 方法会根据 `layer_name_pattern` 查找对应的网络子层,并将查找到的子层和其 `pattern` 传入可调用对象 `handle_func`,并使用 `handle_func` 的返回值替换该层。需要注意的是,形参 `handle_func` 须为可调用对象,且该对象应有 2 个形参,第 1 个形参为 `nn.Layer` 类型,第 2 个形参为 `str` 类型,该可调用对象返回值必须为 `nn.Layer` 类型对象。
217+
218+
`MobileNetV1` 网络为例,将网络最后的 2 个 block 中的深度可分离卷积(depthwise_conv)改为 `5*5` 大小的卷积核,同时将 padding 改为 `2`,如下方代码所示:
219+
220+
```python
221+
from paddle import nn
222+
import paddleclas
223+
224+
def rep_func(layer: nn.Layer, pattern: str):
225+
new_layer = nn.Conv2D(
226+
in_channels=layer._in_channels,
227+
out_channels=layer._out_channels,
228+
kernel_size=5,
229+
padding=2
230+
)
231+
return new_layer
232+
233+
net = paddleclas.MobileNetV1(pretrained=True)
234+
print("========== the origin mobilenetv1 net arch ==========")
235+
print(net)
236+
237+
res = net.upgrade_sublayer(layer_name_pattern=["blocks[11].depthwise_conv.conv", "blocks[12].depthwise_conv.conv"], handle_func=rep_func)
238+
print("The result returned by upgrade_sublayer() is", res)
239+
# The result returned by replace_sub() is {'blocks[11].depthwise_conv.conv': Conv2D(512, 512, kernel_size=[5, 5], padding=2, data_format=NCHW), 'blocks[12].depthwise_conv.conv': Conv2D(1024, 1024, kernel_size=[5, 5], padding=2, data_format=NCHW)}
240+
241+
print("\n\n========== the upgraded mobilenetv1 net arch ==========")
242+
print(net)
243+
```

0 commit comments

Comments
 (0)