一、前言
由于之前工作中,训练数据集普遍较小 以及 开发板对模型的限制,所以对 SE
模块的使用较少,对它的插入位置不是很清楚,这样不利于日后对它的使用。故最近查了下使用案例,记录总结如下。
二、正文
(一) plain
模型
SE
作者对 SE
模块在 plain
模型插入位置的建议是:在每个卷积的激活函数后面插入。这样一看会误以为在每个卷积层后面加个 SE
模块,一般是在 每个 block
后面插入,下面结合实际的案例来做说明。
1. SE-Inception 模型
2. PP-LCNet 模型
由上面两张图可见,
SE
模块在 plain
模型的插入位置,一般在 上个block的结尾 下一个block之前的位置插入。
; (二) skip connection
模型
skip connection
模型指 ResNet
、 MobileNet v2/v3
这种具有 shortcut
操作的模型。现在的模型基本是这个结构,它与 plain
模型 block
最大的不同就是多了个恒等映射的分支(一些变种可能不是恒等映射分支,意思明白就好)。
1. (类)residual unit
外部, SE
的插入位置
SE
作者做了个实验,验证 SE
模块在 residual unit
外部时,放在哪个位置效果最好。这个实验虽然是用残差网络来做的,但是其他模型如 MobileNet
也可以借鉴,毕竟二者的思路是一致的。
验证结果如下,下图中的
SE
就是上图的 Standard SE block
,其他名词含义与上图一致。由上图可见,
SE-POST block
误差相对最大,所以作者建议: SE
模块要加在两个分支汇合之前。至于
SE-PRE block
、 SE-Identity block
的 top1
误差比 Standard SE block
还小,但 SE
作者最后并没有采用这种形式,而是用了 Standard SE block
(也即上图的 SE
)形式。我猜想可能是 plain
模型的思维惯性,即放在卷积后面。在此还要说明一点,
SE
作者自己说过,这些插入位置什么的,不是 SE
论文的核心,所以他没做很多实验。他建议针对特定网络结构,针对性地插入 SE
模块,可能会得到更好的结果。所以 SE-PRE block
、 SE-Identity block
甚至 SE-POST block
都可以尝试一下。(反正深度学习是拿实验数据说话)
; 2. (类)residual unit
内部, SE
的插入位置
SE
作者还实验了下,把插入位置由下图的 ” SE
模块” 换到 ” SE_3X3
“处(3x3
指的是 block
中间那个 3x3
卷积)。另外说下,下图就是 SE-ResNet50
的模型图,也就是作者最终选定的结构样式。
上面实验结果如下,可以发现二者的性能没什么差别,但因为
ResNet
的 3x3
卷积比下面 1x1
卷积的通道数更低,所以 SE_3X3
的参数量、计算量也更低。此外,看
git
上MobileNet v3
代码,会发现 SE
的插入位置还有个版本,该版本插入位置与 SE-ResNet50
一致,在 unit
最后一个卷积后面。这里估计是想减少点参数量及计算复杂度,毕竟 MobileNet v3
的 3x3
卷积比下面 1x1
卷积的通道数更高。 实际使用时,两个位置都可以试试。
class Block(nn.Module):
'''expand + depthwise + pointwise'''
def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride):
"""
这块代码不重要,就不贴出来了,免得不好看博客
"""
def forward(self, x):
out = self.nolinear1(self.bn1(self.conv1(x)))
out = self.nolinear2(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
if self.se != None:
out = self.se(out)
out = out + self.shortcut(x) if self.stride==1 else out
return out
自 YOLO V5
面世以来,针对其的改进也有添加 SE
模块的方式。纵观网上的博客,发现 V5
添加 SE
模块一般是在两个位置:
① 在 C3-bottleneck
中添加 SE
模块的,这样添加主要为了更好的做实验,参考博客;
另外,目前一般是加在
bottleneck
中第一个卷积 block
后面,参考上面的博客内容,也可以试试放在第二个卷积 block
后面。最后我们可以看到,无论是 YOLO V5
、 MobileNet v3
还是 SE-ResNet50
, 添加 SE
模块都是以block为单位目标来添加的,这点与我们在博文开头处的观点倒是不谋而合。
② 在 V5-backbone
结尾处添加 SE
模块。
这个添加位置比较少见,我也是看这个参考博客才知道,博主表示 backbone结尾添加一个注意力机制 会好点。
backbone:
[
[-1, 1, Focus, [64, 3]],
[-1, 1, Conv, [128, 3, 2]],
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2]],
[-1, 9, C3, [256]],
[-1, 1, Conv, [512, 3, 2]],
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]],
[-1, 1, SPP, [1024, [5, 9, 13]]],
[-1, 3, C3, [1024, False]],
[-1, 1, SELayer, [1024, 4]],
]
想了一下,这点在 SE
论文里的 SE-Inception
也有体现。如下图:
3. 在模型哪几层插入 SE
模块
这对我们使用 SE
模块,确实是个问题,每层都用不合适,选一层的话又不知道应该选哪层。 SE
作者为此做了个实验。
先看看作者做实验的模型,绿色的字体是我打的,让各位更明白模型每个 stage
在模型中的位置,其中每个 feature map
尺寸就是一个 stage
。中间与右边两列中括号里的 fc,[xx, xx]
就是 SE
模块。
作者做了组对比实验,分别只在
SE_stage_2
, SE_stage_3
, SE_stage_4
插入 SE
模块,最后又给出所有 stage
( SE_ALL
)插入 SE
模块的实验结果。可以看见,每层都加的效果是最好的(其参数量与计算量也最高),所以作者最后也是每个 block
都添加了 SE
模块。但要注意的是,在模型的最后三个
block
( SE_stage_4
)添加 SE
模块,会发现 性能-计算复杂度取到一个比较好的平衡,这点在 PP-LCNet
里也得到了呼应。如下图所示, PP-LCNet
在模型最后的两个 block
添加 SE
模块(也是最后一个 stage
),也是取得了 性能-计算复杂度平衡,故以后部署平台算力紧张时,可以考虑这种策略。查看
MobileNet v3
论文,发现其对 SE
的用法也有点类似于前两者。据下图可知, MobileNet v3
在其 large
与 small
版本的最后两个 stage
中都插入了 SE
模块。以后使用时, 在模型最后两个 stage
添加 SE
模块性价比更高点,如果算力限制大,也可以试着 只在最后一个 stage
添加 SE
模块。; 三、后语
抛砖引玉之作,如有遗漏、补充,还请各位看官不吝指出,谢谢。
Original: https://blog.csdn.net/tangshopping/article/details/126133995
Author: tang-shopping
Title: 随笔记录:关于SE模块插入位置的总结
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/718634/
转载文章受原作者版权保护。转载请注明原作者出处!