EffNet于2018年提出,被ICIP18接收。提出的时候MobileNetV1,MobiletNetV2, ShuffleNetV1已经被提出,随后几个月后ShuffleNetV2才被提出。所以EffNet主要对比对象是MobileNet和ShuffleNetV1, 针对其存在的问题进行了调整和改进,是一个非常简单而高效的轻量级模型,整个实现的代码量仅有50多行。
1. 介绍
与MobileNet、ShuffleNet等网络的目的类似,EffNet目标也是让模型能够在嵌入式或者移动端硬件设备上高效地运行。
本文指出了MobileNet和ShuffleNet的不足,由于基本的block和stride等超参数会对信息造成一定的损失,这种损失对于小型的网络尤其突出。EffNet提出就是为了解决以上问题,对shallow和narrow情况下的网络效果更好。
EffNet主要有两个贡献:
- 提出了EffNet Block,将深度可分离3x3卷积改进为1x3和3x1的空间可分离卷积,在两者之间添加一维maxpooling
- ShuffleNet和MobileNet都选择避免处理第一层,并认为第一层的计算代价已经很低。但是EffNet认为每个优化都非常重要,如果优化了除了第一层以外的其它层,那么第一层的计算量相比之下就会比较大。实验证明使用EffNet块替换第一层能够节省30%的计算量。
EffNet和MobileNet、ShuffleNet的基础模块对比如下图所示。
上图中ch代表通道个数,dw代表深度可分离卷积,mp代表maxpool,gc代表组卷积,shuffle代表channel shuffle操作。
(b)中MobileNet所做的工作主要是引入了深度可分离卷积,将普通的3x3卷积替换为深度可分离卷积,可以有效地降低模型的运算量。
GoogLeNet中提到的Inception结构中将普通的3x3卷积替换为连续的1x3和3x1卷积,实际完成的是卷积的空间分离操作。
(c)ShuffleNet的核心是point wise group convolution和channel shuffle两个操作,channel shuffle可以在一定程度上弥补组卷积带来的精度下降。
(a)中EffNet的基础模块中使用了空间可分离卷积、深度可分离卷积、最大化池化等来降低计算量。
2. 代码
具体代码如下,核心就是make_layers函数:
代码语言:javascript复制import torch.nn as nn
class Flatten(nn.Module):
def forward(self, x):
x = x.view(x.size()[0], -1)
return x
class EffNet(nn.Module):
def __init__(self, nb_classes=10, include_top=True, weights=None):
super(EffNet, self).__init__()
self.block1 = self.make_layers(32, 64)
self.block2 = self.make_layers(64, 128)
self.block3 = self.make_layers(128, 256)
self.flatten = Flatten()
self.linear = nn.Linear(4096, nb_classes)
self.include_top = include_top
self.weights = weights
def make_layers(self, ch_in, ch_out):
layers = [
nn.Conv2d(3, ch_in, kernel_size=(1,1), stride=(1,1), bias=False, padding=0, dilation=(1,1)) if ch_in ==32 else nn.Conv2d(ch_in, ch_in, kernel_size=(1,1),stride=(1,1), bias=False, padding=0, dilation=(1,1)) ,
self.make_post(ch_in),
# DepthWiseConvolution2D
nn.Conv2d(ch_in, 1 * ch_in, groups=ch_in, kernel_size=(1, 3),stride=(1,1), padding=(0,1), bias=False, dilation=(1,1)),
self.make_post(ch_in),
nn.MaxPool2d(kernel_size=(2,1), stride=(2,1)),
# DepthWiseConvolution2D
nn.Conv2d(ch_in, 1 * ch_in, groups=ch_in, kernel_size=(3, 1), stride=(1,1), padding=(1,0), bias=False, dilation=(1,1)),
self.make_post(ch_in),
nn.Conv2d(ch_in, ch_out, kernel_size=(1, 2), stride=(1, 2), bias=False, padding=(0,0), dilation=(1,1)),
self.make_post(ch_out),
]
return nn.Sequential(*layers)
def make_post(self, ch_in):
layers = [
nn.LeakyReLU(0.3),
nn.BatchNorm2d(ch_in, momentum=0.99)
]
return nn.Sequential(*layers)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
if self.include_top:
x = self.flatten(x)
x = self.linear(x)
return x
单独看EffNet中的layer:
代码语言:javascript复制nn.Conv2d(3, ch_in, kernel_size=(1,1), stride=(1,1), bias=False, padding=0, dilation=(1,1)) if ch_in ==32 else nn.Conv2d(ch_in, ch_in, kernel_size=(1,1),stride=(1,1), bias=False, padding=0, dilation=(1,1)) ,
self.make_post(ch_in),
# DepthWiseConvolution2D
nn.Conv2d(ch_in, 1 * ch_in, groups=ch_in, kernel_size=(1, 3),stride=(1,1), padding=(0,1), bias=False, dilation=(1,1)),
self.make_post(ch_in),
nn.MaxPool2d(kernel_size=(2,1), stride=(2,1)),
# DepthWiseConvolution2D
nn.Conv2d(ch_in, 1 * ch_in, groups=ch_in, kernel_size=(3, 1), stride=(1,1), padding=(1,0), bias=False, dilation=(1,1)),
self.make_post(ch_in),
nn.Conv2d(ch_in, ch_out, kernel_size=(1, 2), stride=(1, 2), bias=False, padding=(0,0), dilation=(1,1)),
self.make_post(ch_out),
make_post函数主要添加的是leaky relu和Batch Norm,所以只需要关心其中的Conv2d即可。
(1)第一个Conv2d是1x1卷积,或者Pointwise Convolution
(2)接下来是kernel size为(1,3)和(3,1)的空间分离卷积, 这两个卷积之间添加了一个一维的最大化池化层。
(3)最后一个Conv2d是kernel size为(1,2),stride为(1,2)的卷积。
关于以上提到的卷积的综述可以看【综述】神经网络中不同种类的卷积层。
EffNet也尝试过ShuffleNet的分组卷积,虽然分组卷积带来的计算优势,但是结果表明准确度明显下降,因此EffNet也避开使用分组卷积。
3. 实验
EffNet并没有在ImageNet这种大型数据集上训练测试,而是选择了Cifar10, SVHN, GTSRB等较小规模的数据集进行对比。
Cifar10上的对比结果
SVHN上结果对比
GTSRB上的结果对比
此外,由于这篇文章发表和MobileNetV2有一些撞车,所以单独列了一章和MobileNetV2进行对比。
Cifar10上的结果对比
4. 评价
EffNet是在MobileNetV1,V2和ShuffleNetV1之后提出来的改进模型,文章认为MobileNet和ShuffleNet的Block设计对信息造成了一定的损耗,所以提出了一个EffNet Block的模块来弥补,并且在第一层的处理方面和MobileNet和ShuffleNet都不同,选择用EffNet Block替换掉第一层的模块。总体来看,EffNet的参考价值一般,实现的代码非常简单,知名度不够高,github上的实现只有10多个star。不过EffNet算是使用空间分离卷积比较成功的模型,不过提出的时间比较尴尬,前有MobileNetV2后有ShuffleNetV2,所以自然知名度一般了。
5. 参考文献
https://github.com/andrijdavid/EffNet
https://arxiv.org/abs/1801.06434
-END-