🌜|从零搭建网络| EfficientNet系列网络详解及搭建🌛
文章目录
🌜 前言 🌛
轻量级网络整理的也差不多了,应该还剩下这一个EfficientNet没出,这篇博客就用来对EfficientNet网络进行一个网络详解和搭建讲解,同时本篇博客也会从pytorch的框架下复现EfficientNet网络。
🌜 EfficientNet详解 🌛
首先附上原论文地址:https://arxiv.org/pdf/1905.11946.pdf。
本篇博客除了给出一个新的轻量级网络的搭建结构之外,还主要介绍了网络的各个属性对于网络准确率,计算速度等的影响以及怎样改进的方法。事实证明,EfficientNet网络在ImageNet的准确率和参数量方面对于当时期的各大模型都遥遥领先。
可以看出在当时期的网络中,EfficientNet在ImageNet top-1的准确率达到了一个新高。并且正如本篇论文摘要中叙述的,他是当时时期唯一一个同时探索分辨率、网络深度以及宽度的影响。
🌜 EfficientNet创新点 🌛
本篇论文除了做了一个新的网络结构之外,极大篇幅都用来叙述 分辨率、网络深度以及宽度对于网络评估结果的影响 ,这篇博客简单对其做一个叙述,感兴趣的话可以到原论文中查看,主要对于网络搭建部分详细解释。
如上图所示,a图表示一个基准网络、b图表示增加卷积核个数,增大输出特征的通道数、c图表示对网络结构进行堆叠,加深网络深度、d图表示对图像的分辨率进行增加,e图指的是同时增加输出特征通道数、网络深度以及输入图像的分辨率。
上图是增加各部分指标对于准确率的影响。首先我们根据以往的经验,增加网络的深度能够得到更加丰富的特征并且能够很好地应用到其他的任务中,但是过深的网络会面临梯度消失,训练困难等问题;增加网络宽度能够获得更高细粒度的特征并且也更容易训练,但对于宽度很大而且深度较浅的网络往往很难学习到较为复杂的特征;增加图像的分辨率能够潜在的获得更高细粒度的特征模板,但对于非常高的输入分辨率,准确率的增益也会减小,并且较大的分辨率也会增大训练的难度。
🌜 一维EfficientNet搭建详解 🌛
EfficientNet的搭建也并没有很难得点,首先看一下整体结构。
这里的MBConv
指的是MobileNetV3
的block
结构,那么首先就来简单回顾一下这个block
结构。(这里就不过多赘述,想要详细了解可以移步:|从零搭建网络| MobileNet系列网络详解及搭建)
这里和MobileNetV3中唯一不同的点就是在捷径分支前面加了个Dropout层,中间深度可分离卷积卷积核由EfficientNet中的结构图定义。下面直接看相关block的搭建:
import torch
class SE_block(torch.nn.Module):
def __init__(self,inchannel,ratio = 4):
super().__init__()
self.pool = torch.nn.AdaptiveAvgPool1d(1)
self.conv = torch.nn.Sequential(
torch.nn.Linear(inchannel,inchannel // ratio),
torch.nn.ReLU(),
torch.nn.Linear(inchannel // ratio,inchannel),
torch.nn.Hardsigmoid(inplace=True)
)
def forward(self,input):
b,c,_ = input.size()
x = self.pool(input)
x = x.view([b,c])
x = self.conv(x)
x = x.view([b,c,1])
return x*input
class MB_block(torch.nn.Module):
def __init__(self,input_channels,outchannel,kernal,stride,expand_ratio):
super().__init__()
self.input_channels = input_channels
self.outchannel = outchannel
self.stride = stride
self.conv = torch.nn.Sequential(
torch.nn.Conv1d(input_channels,input_channels*expand_ratio,1),
torch.nn.BatchNorm1d(input_channels*expand_ratio),
torch.nn.Hardswish(),
torch.nn.Conv1d(input_channels*expand_ratio,input_channels*expand_ratio,kernal,stride,padding=kernal // 2,groups=input_channels*expand_ratio),
torch.nn.BatchNorm1d(input_channels*expand_ratio),
torch.nn.Hardswish(),
SE_block(input_channels*expand_ratio),
torch.nn.Conv1d(input_channels*expand_ratio,outchannel,1,1),
torch.nn.BatchNorm1d(outchannel)
)
def forward(self,x):
out = self.conv(x)
if self.stride == 1 and self.input_channels == self.outchannel:
out += x
return out
代码中SE_block
为定义的注意力机制。
下面只需要根据结构图一步步堆叠MBConv
即可,下面是搭建网络的代码:
class EfficientNet(torch.nn.Module):
def __init__(self,input_channels,classes):
super().__init__()
self.input_channels = input_channels
self.classes = classes
self.stage_1 = torch.nn.Sequential(
torch.nn.Conv1d(input_channels,32,3,2,1),
torch.nn.BatchNorm1d(32),
torch.nn.Hardswish()
)
self.stage_2 = torch.nn.Sequential(
MB_block(32,16,3,1,1)
)
self.stage_3 = torch.nn.Sequential(
MB_block(16,24,3,2,6),
MB_block(24,24,3,1,6)
)
self.stage_4 = torch.nn.Sequential(
MB_block(24,40,5,2,6),
MB_block(40,40,5,1,6)
)
self.stage_5 = torch.nn.Sequential(
MB_block(40,80,3,2,6),
MB_block(80,80,3,1,6),
MB_block(80,80,3,1,6),
)
self.stage_6 = torch.nn.Sequential(
MB_block(80,112,5,1,6),
MB_block(112,112,5,1,6),
MB_block(112,112,5,1,6),
)
self.stage_7 = torch.nn.Sequential(
MB_block(112,192,5,2,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
)
self.stage_8 = torch.nn.Sequential(
MB_block(192,320,3,1,6)
)
self.stage_9 = torch.nn.Sequential(
torch.nn.Conv1d(320,1280,1),
torch.nn.AdaptiveAvgPool1d(1),
torch.nn.Flatten(),
torch.nn.Linear(1280,classes)
)
def forward(self,x):
x = self.stage_1(x)
x = self.stage_2(x)
x = self.stage_3(x)
x = self.stage_4(x)
x = self.stage_5(x)
x = self.stage_6(x)
x = self.stage_7(x)
x = self.stage_8(x)
x = self.stage_9(x)
return x
下面是完整代码:
import torch
class SE_block(torch.nn.Module):
def __init__(self,inchannel,ratio = 4):
super().__init__()
self.pool = torch.nn.AdaptiveAvgPool1d(1)
self.conv = torch.nn.Sequential(
torch.nn.Linear(inchannel,inchannel // ratio),
torch.nn.ReLU(),
torch.nn.Linear(inchannel // ratio,inchannel),
torch.nn.Hardsigmoid(inplace=True)
)
def forward(self,input):
b,c,_ = input.size()
x = self.pool(input)
x = x.view([b,c])
x = self.conv(x)
x = x.view([b,c,1])
return x*input
class MB_block(torch.nn.Module):
def __init__(self,input_channels,outchannel,kernal,stride,expand_ratio):
super().__init__()
self.input_channels = input_channels
self.outchannel = outchannel
self.stride = stride
self.conv = torch.nn.Sequential(
torch.nn.Conv1d(input_channels,input_channels*expand_ratio,1),
torch.nn.BatchNorm1d(input_channels*expand_ratio),
torch.nn.Hardswish(),
torch.nn.Conv1d(input_channels*expand_ratio,input_channels*expand_ratio,kernal,stride,padding=kernal // 2,groups=input_channels*expand_ratio),
torch.nn.BatchNorm1d(input_channels*expand_ratio),
torch.nn.Hardswish(),
SE_block(input_channels*expand_ratio),
torch.nn.Conv1d(input_channels*expand_ratio,outchannel,1,1),
torch.nn.BatchNorm1d(outchannel)
)
def forward(self,x):
out = self.conv(x)
if self.stride == 1 and self.input_channels == self.outchannel:
out += x
return out
class EfficientNet(torch.nn.Module):
def __init__(self,input_channels,classes):
super().__init__()
self.input_channels = input_channels
self.classes = classes
self.stage_1 = torch.nn.Sequential(
torch.nn.Conv1d(input_channels,32,3,2,1),
torch.nn.BatchNorm1d(32),
torch.nn.Hardswish()
)
self.stage_2 = torch.nn.Sequential(
MB_block(32,16,3,1,1)
)
self.stage_3 = torch.nn.Sequential(
MB_block(16,24,3,2,6),
MB_block(24,24,3,1,6)
)
self.stage_4 = torch.nn.Sequential(
MB_block(24,40,5,2,6),
MB_block(40,40,5,1,6)
)
self.stage_5 = torch.nn.Sequential(
MB_block(40,80,3,2,6),
MB_block(80,80,3,1,6),
MB_block(80,80,3,1,6),
)
self.stage_6 = torch.nn.Sequential(
MB_block(80,112,5,1,6),
MB_block(112,112,5,1,6),
MB_block(112,112,5,1,6),
)
self.stage_7 = torch.nn.Sequential(
MB_block(112,192,5,2,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
)
self.stage_8 = torch.nn.Sequential(
MB_block(192,320,3,1,6)
)
self.stage_9 = torch.nn.Sequential(
torch.nn.Conv1d(320,1280,1),
torch.nn.AdaptiveAvgPool1d(1),
torch.nn.Flatten(),
torch.nn.Linear(1280,classes)
)
def forward(self,x):
x = self.stage_1(x)
x = self.stage_2(x)
x = self.stage_3(x)
x = self.stage_4(x)
x = self.stage_5(x)
x = self.stage_6(x)
x = self.stage_7(x)
x = self.stage_8(x)
x = self.stage_9(x)
return x
if __name__ == '__main__':
x = torch.randn(1,1,224)
model = EfficientNet(1,10)
y = model(x)
print(y.shape)
🌜 二维EfficientNet搭建 🌛
二维网络的搭建除了把各个一维网络层改为二维之外,还需要主义注意力机制的更改,下面是注意力机制更改的代码:
import torch
class SE_block(torch.nn.Module):
def __init__(self,inchannel,ratio = 4):
super().__init__()
self.pool = torch.nn.AdaptiveAvgPool2d(1)
self.conv = torch.nn.Sequential(
torch.nn.Linear(inchannel,inchannel // ratio),
torch.nn.ReLU(),
torch.nn.Linear(inchannel // ratio,inchannel),
torch.nn.Hardsigmoid(inplace=True)
)
def forward(self,input):
b,c,_,_ = input.size()
x = self.pool(input)
x = x.view([b,c])
x = self.conv(x)
x = x.view([b,c,1,1])
return x*input
这里主要就是在前项传播模块中加入处理二维数组的因素。
下面是完整代码:
import torch
class SE_block(torch.nn.Module):
def __init__(self,inchannel,ratio = 4):
super().__init__()
self.pool = torch.nn.AdaptiveAvgPool2d(1)
self.conv = torch.nn.Sequential(
torch.nn.Linear(inchannel,inchannel // ratio),
torch.nn.ReLU(),
torch.nn.Linear(inchannel // ratio,inchannel),
torch.nn.Hardsigmoid(inplace=True)
)
def forward(self,input):
b,c,_,_ = input.size()
x = self.pool(input)
x = x.view([b,c])
x = self.conv(x)
x = x.view([b,c,1,1])
return x*input
class MB_block(torch.nn.Module):
def __init__(self,input_channels,outchannel,kernal,stride,expand_ratio):
super().__init__()
self.input_channels = input_channels
self.outchannel = outchannel
self.stride = stride
self.conv = torch.nn.Sequential(
torch.nn.Conv2d(input_channels,input_channels*expand_ratio,1),
torch.nn.BatchNorm2d(input_channels*expand_ratio),
torch.nn.Hardswish(),
torch.nn.Conv2d(input_channels*expand_ratio,input_channels*expand_ratio,kernal,stride,padding=kernal // 2,groups=input_channels*expand_ratio),
torch.nn.BatchNorm2d(input_channels*expand_ratio),
torch.nn.Hardswish(),
SE_block(input_channels*expand_ratio),
torch.nn.Conv2d(input_channels*expand_ratio,outchannel,1,1),
torch.nn.BatchNorm2d(outchannel)
)
def forward(self,x):
out = self.conv(x)
if self.stride == 1 and self.input_channels == self.outchannel:
out += x
return out
class EfficientNet(torch.nn.Module):
def __init__(self,input_channels,classes):
super().__init__()
self.input_channels = input_channels
self.classes = classes
self.stage_1 = torch.nn.Sequential(
torch.nn.Conv2d(input_channels,32,3,2,1),
torch.nn.BatchNorm2d(32),
torch.nn.Hardswish()
)
self.stage_2 = torch.nn.Sequential(
MB_block(32,16,3,1,1)
)
self.stage_3 = torch.nn.Sequential(
MB_block(16,24,3,2,6),
MB_block(24,24,3,1,6)
)
self.stage_4 = torch.nn.Sequential(
MB_block(24,40,5,2,6),
MB_block(40,40,5,1,6)
)
self.stage_5 = torch.nn.Sequential(
MB_block(40,80,3,2,6),
MB_block(80,80,3,1,6),
MB_block(80,80,3,1,6),
)
self.stage_6 = torch.nn.Sequential(
MB_block(80,112,5,1,6),
MB_block(112,112,5,1,6),
MB_block(112,112,5,1,6),
)
self.stage_7 = torch.nn.Sequential(
MB_block(112,192,5,2,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
MB_block(192,192,5,1,6),
)
self.stage_8 = torch.nn.Sequential(
MB_block(192,320,3,1,6)
)
self.stage_9 = torch.nn.Sequential(
torch.nn.Conv2d(320,1280,1),
torch.nn.AdaptiveAvgPool2d(1),
torch.nn.Flatten(),
torch.nn.Linear(1280,classes)
)
def forward(self,x):
x = self.stage_1(x)
x = self.stage_2(x)
x = self.stage_3(x)
x = self.stage_4(x)
x = self.stage_5(x)
x = self.stage_6(x)
x = self.stage_7(x)
x = self.stage_8(x)
x = self.stage_9(x)
return x
if __name__ == '__main__':
x = torch.randn(1,1,224,224)
model = EfficientNet(1,10)
y = model(x)
print(y.shape)
🌜 总结 🌛
总体写完感觉这一篇略有点水,但确实这个网络并没有提出很新颖的Block结构或者是Element-wise的操作,所以也没叙述过多。
模型方面到这里也就结束了, 后续还会有生成数据、数据集打包以及训练等模板, 详情可以看 @浩浩的科研笔记 Pytroch 自写训练模板适合入门版 包含十五种经典的自己复现的一维模型 1D CNN