CNN结构设计|无痛的涨点技巧:ACNet

2020 年 4 月 16 日 极市平台

加入极市专业CV交流群,与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度 等名校名企视觉开发者互动交流!

同时提供每月大咖直播分享、真实项目需求对接、干货资讯汇总,行业技术交流。关注 极市平台 公众号 ,回复 加群,立刻申请入群~

1. 前言

不知道你是否发现了,CNN的结构创新在这两年已经变得相对很少了,同时要做出有影响力并且Solid的工作也变得越来越难,最近CNN结构方面的创新主要包含两个方面:

  • 网络结构搜索,以Google Brain的EfficientNet为代表作。

  • 获取更好的特征表达,主要是将特征复用,特征细化做得更加极致,以HRNet,Res2Net等为代表作。

本文要介绍的是ICCV 2019的一个新CNN架构ACNet(全称为Asymmetric Convolution Net),但是可能很多同学没有实测或者对里面要表达的核心思想并没有捕捉,因此这篇文章的目的是讲清楚ACNet的原理并总结它的核心思想,另外再提供一个即插即用的Pytorch代码段。

2. 介绍

ACNet的切入点为获取更好的特征表达,但和其它方法最大的区别在于它没有带来额外的超参数,而且在推理阶段没有增加计算量,这是十分具有吸引力的。

在正式介绍ACNet之前,首先来明确一下关于卷积计算的一个等式,这个等式表达的意思就是对于输入特征图 ,先进行K(1)和I卷积,K(2)和I卷积后再对结果进行相加,与先进行K(1)和K(2)的逐点相加后再和I进行卷积得到的结果是一致的。这也是ACNet在推理阶段不增加任何计算量的理论基础。

重要等式

3. ACNet原理

下面的Figure1展示了ACNet的思想:

ACNet的整体结构

宏观上来看ACNet分为训练和推理阶段,训练阶段重点在于强化特征提取,实现效果提升。而测试阶段重点在于卷积核融合,不增强任何计算量

  • 训练阶段因为 卷积是大多数网络的基础组件,因此ACNet的实验都是针对 卷积进行的。训练阶段就是将现有网络中的每一个 卷积换成 卷积+ 卷积+ 卷积共三个卷积层,最终将这三个卷积层的计算结果进行融合获得卷积层的输出。因为这个过程中引入的 卷积和 卷积是非对称的,所以将其命名为Asymmetric Convolution。

  • 推理阶如Figure1右图所示,这部分主要是对三个卷积核进行融合。这部分在实现过程中就是使用融合后的卷积核参数来初始化现有的网络,因此在推理阶段,网络结构和原始网络是完全一样的了,只不过网络参数采用了特征提取能力更强的参数即融合后的卷积核参数,因此在推理阶段不会增加计算量。

总结一下就是ACNet在训练阶段强化了原始网络的特征提取能力,在推理阶段融合卷积核达到不增加计算量的目的。虽然训练时间增加了一些时间,但却换来了对推理无痛的精度提升,怎么看都是一笔非常划算的交易。下面的Table3展示出来,对于AlexNet提升了比较多,而对ResNet和DenseNet提升不到一个百分点,不过考虑到这个提升是白赚的也还是非常值得肯定的。

Table3

4. 为什么ACNet能涨点?

为什么ACNet这个看起来十分简单的操作能为各种网络带来涨点?论文中提到,ACNet有一个特点是它提升了模型对图像翻转和旋转的鲁棒性,例如训练好后的 卷积和在图像翻转后仍然能提取正确的特征(如Figure4左图所示,2个红色矩形框就是图像翻转前后的特征提取操作,在输入图像的相同位置处提取出来的特征还是一样的)。那么假设训练阶段只用 卷积核,当图像上下翻转之后,如Figure4右图所示,提取出来的特征显然是不一样的。

因此,引入 这样的水平卷积核可以提升模型对图像上下翻转的鲁棒性,竖直方向的 卷积核同理。

Figure4

下面的Table4则继续从实验角度解释了这种鲁棒性:

从实验上解释这种鲁棒性

5. 推理阶段的卷积核融合

推理阶段的融合操作如Figure3所示,在论文中提到具体的融合操作是和BN层一起的,然后融合操作时发生在BN之后的。但是其实也可以把融合操作放在BN层之前,也就是三个卷积层计算完之后就开始融合。论文对这两种融合方式进行了实验,在上面的Table4中BN in branch这一列有√的话表示融合是在BN之后,可以看到这种方式使得效果确实会更好一些。

推理阶段的卷积层融合

6. Pytorch代码实现

我们来看一下作者的ACNet基础结构Pytorch实现,即将原始的 卷积变成

# 去掉因为3x3卷积的padding多出来的行或者列class CropLayer(nn.Module):
# E.g., (-1, 0) means this layer should crop the first and last rows of the feature map. And (0, -1) crops the first and last columns def __init__(self, crop_set): super(CropLayer, self).__init__() self.rows_to_crop = - crop_set[0] self.cols_to_crop = - crop_set[1] assert self.rows_to_crop >= 0 assert self.cols_to_crop >= 0
def forward(self, input): return input[:, :, self.rows_to_crop:-self.rows_to_crop, self.cols_to_crop:-self.cols_to_crop]
# 论文提出的3x3+1x3+3x1class ACBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, padding_mode='zeros', deploy=False): super(ACBlock, self).__init__() self.deploy = deploy if deploy: self.fused_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(kernel_size,kernel_size), stride=stride, padding=padding, dilation=dilation, groups=groups, bias=True, padding_mode=padding_mode) else: self.square_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(kernel_size, kernel_size), stride=stride, padding=padding, dilation=dilation, groups=groups, bias=False, padding_mode=padding_mode) self.square_bn = nn.BatchNorm2d(num_features=out_channels)
center_offset_from_origin_border = padding - kernel_size // 2 ver_pad_or_crop = (center_offset_from_origin_border + 1, center_offset_from_origin_border) hor_pad_or_crop = (center_offset_from_origin_border, center_offset_from_origin_border + 1) if center_offset_from_origin_border >= 0: self.ver_conv_crop_layer = nn.Identity() ver_conv_padding = ver_pad_or_crop self.hor_conv_crop_layer = nn.Identity() hor_conv_padding = hor_pad_or_crop else: self.ver_conv_crop_layer = CropLayer(crop_set=ver_pad_or_crop) ver_conv_padding = (0, 0) self.hor_conv_crop_layer = CropLayer(crop_set=hor_pad_or_crop) hor_conv_padding = (0, 0) self.ver_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(3, 1), stride=stride, padding=ver_conv_padding, dilation=dilation, groups=groups, bias=False, padding_mode=padding_mode)
self.hor_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(1, 3), stride=stride, padding=hor_conv_padding, dilation=dilation, groups=groups, bias=False, padding_mode=padding_mode) self.ver_bn = nn.BatchNorm2d(num_features=out_channels) self.hor_bn = nn.BatchNorm2d(num_features=out_channels)

# forward函数 def forward(self, input): if self.deploy: return self.fused_conv(input) else: square_outputs = self.square_conv(input) square_outputs = self.square_bn(square_outputs) # print(square_outputs.size()) # return square_outputs vertical_outputs = self.ver_conv_crop_layer(input) vertical_outputs = self.ver_conv(vertical_outputs) vertical_outputs = self.ver_bn(vertical_outputs) # print(vertical_outputs.size()) horizontal_outputs = self.hor_conv_crop_layer(input) horizontal_outputs = self.hor_conv(horizontal_outputs) horizontal_outputs = self.hor_bn(horizontal_outputs) # print(horizontal_outputs.size())            return square_outputs + vertical_outputs + horizontal_outputs

然后在推理阶段进行卷积核融合的代码实现地址为:https://github.com/DingXiaoH/ACNet/blob/master/acnet/acnet_fusion.py,对照Figure3就比较好理解了,介于篇幅这里就不贴这段代码了。

7. 思考

从实验结果中可以看到,在推理阶段即使融合操作放在BN层之前,相比原始网络仍有一定提升(AlexNet的56.18% vs 55.92%,ResNet-18的70.82% vs 70.36%),作者没有讲解这部分的原理,我比较同意魏凯峰大佬的观点,如下:

这部分的原因个人理解是来自梯度差异化,原来只有一个 卷积层,梯度可以看出一份,而添加了 卷积层后,部分位置的梯度变为2份和3份,也是更加细化了。而且理论上可以融合无数个卷积层不断逼近现有网络的效果极限,融合方式不限于相加(训练和推理阶段一致即可),融合的卷积层也不限于 尺寸。

个人将ACNet的结构用在一个业务数据中并取得了2%的精度提升,这一方法确实是非常有用并且也是推理无痛的,建议大家可以在自己的数据上进行尝试。

8. 总结

本文简要讲述了ACNet这个无痛的调参方法,这种方法创新点是非常棒的,我们不一定需要重型BackBone去提取特征,也不一定需要复杂的结构复用特征,像ACNet这样的优雅并且有效的作品确实让人眼前一亮。

论文链接:https://arxiv.org/pdf/1908.03930.pdf


-END -

推荐阅读:


极市独家福利
40万奖金的AI移动应用大赛,参赛就有奖,入围还有额外奖励


添加极市小助手微信 (ID : cv-mart) ,备注: 研究方向-姓名-学校/公司-城市 (如:目标检测-小极-北大-深圳),即可申请加入 目标检测、目标跟踪、人脸、工业检测、医学影像、三维&SLAM、图像分割等极市技术交流群 ,更有 每月大咖直播分享、真实项目需求对接、求职内推、算法竞赛、 干货资讯汇总、行业技术交流 一起来让思想之光照的更远吧~


△长按添加极市小助手


△长按关注极市平台,获取最新CV干货


觉得有用麻烦给个在看啦~  

登录查看更多
0

相关内容

【KDD2020-清华大学】图对比编码的图神经网络预训练
专知会员服务
43+阅读 · 2020年6月18日
专知会员服务
73+阅读 · 2020年5月21日
【CVPR 2020-商汤】8比特数值也能训练卷积神经网络模型
专知会员服务
25+阅读 · 2020年5月7日
CVPR2020 | 商汤-港中文等提出PV-RCNN:3D目标检测新网络
专知会员服务
43+阅读 · 2020年4月17日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
[CVPR 2020-港中文-MIT] 神经架构搜索鲁棒性
专知会员服务
25+阅读 · 2020年4月7日
【论文】结构GANs,Structured GANs,
专知会员服务
14+阅读 · 2020年1月16日
CNN网络结构的发展(最全整理)
极市平台
73+阅读 · 2019年11月2日
语义分割中的Attention和低秩重建
极市平台
37+阅读 · 2019年9月1日
CVPR2019 oral | CPNet : 对应提议网络
极市平台
4+阅读 · 2019年6月17日
模块设计之 SKNet, GCNet, GloRe, Octave
极市平台
16+阅读 · 2019年5月20日
GCNet:当Non-local遇见SENet
极市平台
11+阅读 · 2019年5月9日
神经网络训练tricks
极市平台
6+阅读 · 2019年4月15日
后ResNet时代:SENet与SKNet
PaperWeekly
23+阅读 · 2019年3月25日
Fast-OCNet: 更快更好的OCNet.
极市平台
21+阅读 · 2019年2月10日
从LeNet-5到DenseNet
AI研习社
9+阅读 · 2017年11月18日
深度解析LSTM神经网络的设计原理
AI研习社
5+阅读 · 2017年11月1日
Bivariate Beta LSTM
Arxiv
5+阅读 · 2019年10月7日
Arxiv
15+阅读 · 2019年9月11日
Explanatory Graphs for CNNs
Arxiv
4+阅读 · 2018年12月18日
Arxiv
6+阅读 · 2018年7月29日
VIP会员
相关VIP内容
【KDD2020-清华大学】图对比编码的图神经网络预训练
专知会员服务
43+阅读 · 2020年6月18日
专知会员服务
73+阅读 · 2020年5月21日
【CVPR 2020-商汤】8比特数值也能训练卷积神经网络模型
专知会员服务
25+阅读 · 2020年5月7日
CVPR2020 | 商汤-港中文等提出PV-RCNN:3D目标检测新网络
专知会员服务
43+阅读 · 2020年4月17日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
[CVPR 2020-港中文-MIT] 神经架构搜索鲁棒性
专知会员服务
25+阅读 · 2020年4月7日
【论文】结构GANs,Structured GANs,
专知会员服务
14+阅读 · 2020年1月16日
相关资讯
CNN网络结构的发展(最全整理)
极市平台
73+阅读 · 2019年11月2日
语义分割中的Attention和低秩重建
极市平台
37+阅读 · 2019年9月1日
CVPR2019 oral | CPNet : 对应提议网络
极市平台
4+阅读 · 2019年6月17日
模块设计之 SKNet, GCNet, GloRe, Octave
极市平台
16+阅读 · 2019年5月20日
GCNet:当Non-local遇见SENet
极市平台
11+阅读 · 2019年5月9日
神经网络训练tricks
极市平台
6+阅读 · 2019年4月15日
后ResNet时代:SENet与SKNet
PaperWeekly
23+阅读 · 2019年3月25日
Fast-OCNet: 更快更好的OCNet.
极市平台
21+阅读 · 2019年2月10日
从LeNet-5到DenseNet
AI研习社
9+阅读 · 2017年11月18日
深度解析LSTM神经网络的设计原理
AI研习社
5+阅读 · 2017年11月1日
Top
微信扫码咨询专知VIP会员