一些常见的CNN模型
最近閑著無聊在家敲了一些基本的CNN模型,這里對網(wǎng)上資料做一個簡要的整理總結,供自己學習使用。
一、VGG
? VGG模型是2014年ILSVRC競賽的第二名,第一名是GoogLeNet。但是VGG模型在多個遷移學習任務中的表現(xiàn)要優(yōu)于googLeNet。而且,從圖像中提取CNN特征,VGG模型是首選算法。它的缺點在于,參數(shù)量有140M之多,需要更大的存儲空間。但是這個模型很有研究價值。
VGG有多種網(wǎng)絡結構,如VGG-11,VGG-13,VGG-16,VGG-19等。以吳恩達老師視頻中講到的VGG-16為例,
VGG網(wǎng)絡的特點:(1)采用了小卷積核。作者將卷積核全部替換為3x3。(2)作者采用了小池化核。相比AlexNet的3x3的池化核,VGG全部為2x2的池化核。(3)層數(shù)更深,特征圖更寬。基于前兩點外,由于卷積核專注于擴大通道數(shù)、池化專注于縮小寬和高,使得模型架構上更深更寬的同時,計算量的增加放緩。
? VGG的提出,使人們得到了這樣一個結論:卷積神經(jīng)網(wǎng)絡的深度增加和小卷積核的使用對網(wǎng)絡的最終分類識別效果有很大的作用。
二、GoogLeNet
文章中提出優(yōu)化模型有兩種途徑:提高深度或是寬度。但是這兩種方法都存在局限性:1.參數(shù)太多,容易過擬合,若訓練數(shù)據(jù)集有限;2.網(wǎng)絡越大計算復雜度越大,難以應用;3.網(wǎng)絡越深,梯度越往后穿越容易消失,難以優(yōu)化模型。
GoogLeNet正是圍繞著這兩個思路提出的,其通過構建密集的塊結構——Inception(如下圖)來近似最優(yōu)的稀疏結構,從而達到提高性能而又不大量增加計算量的目的。
GoogLeNet增加了多種核1x1,3x3,5x5,還有直接max pooling的,但是如果簡單的將這些應用到feature map上的話,concat起來的feature map厚度將會很大,所以在googlenet中為了避免這一現(xiàn)象提出的inception具有如下結構,在3x3前,5x5前,max pooling后分別加上了1x1的卷積核起到了降低feature map厚度的作用。5*5的卷積核會帶來巨大的計算量,所以采用1 * 1的卷積核進行降維。(以下是一個Inception的代碼)
class Inception(nn.Module):def __init__(self,in_channels, n1x1, n3x3_reduce, n3x3, n5x5_reduce, n5x5, npool):super().__init__()self.brance1 = nn.Sequential(nn.Conv2d(in_channels,n1x1,kernel_size=1),nn.BatchNorm2d(n1x1),nn.ReLU(inplace=True))self.brance2 = nn.Sequential(nn.Conv2d(in_channels,n3x3_reduce,kernel_size=1),nn.BatchNorm2d(n3x3_reduce),nn.ReLU(inplace=True),nn.Conv2d(n3x3_reduce,n3x3,kernel_size=3,padding=1),nn.BatchNorm2d(n3x3),nn.ReLU(inplace=True))self.brance3 = nn.Sequential(nn.Conv2d(in_channels,n5x5_reduce,kernel_size=1),nn.BatchNorm2d(n5x5_reduce),nn.ReLU(inplace=True),nn.Conv2d(n5x5_reduce,n5x5,kernel_size=5,padding=2),nn.BatchNorm2d(n5x5),nn.ReLU(inplace=True))self.brance4 = nn.Sequential(nn.MaxPool2d(3,stride=1,padding=1),nn.Conv2d(in_channels,npool,kernel_size=1),nn.BatchNorm2d(npool),nn.ReLU(inplace=True))def forward(self,x):out = [self.brance1(x),self.brance2(x),self.brance3(x),self.brance4(x)]out = torch.cat(out,1)return out為了解決深度問題,GoogLeNet巧妙的在不同深度處增加了兩個loss來保證梯度回傳消失的現(xiàn)象。總的網(wǎng)絡結構如下:
三、ResNet
ResNet的提出背景是網(wǎng)絡越深效果越好,因為CNN越深越能提取到更豐富更抽象的特征,這些特征有更高更魯棒的語義信息,網(wǎng)絡越深對輸入圖像的變化越不敏感。但是單純的深模型會遇到問題:模型深,易造成梯度消失,繼續(xù)訓練時,很難再更新參數(shù),很難學到特征。
基于這種背景,何凱明團隊于2015年提出了ResNet(Residual Networks 殘差網(wǎng)絡)。殘差網(wǎng)絡(如下)通過在一個淺層網(wǎng)絡基礎上疊加y=x層,可以讓網(wǎng)絡隨深度增加而不退化。將淺層的輸入值直接連接到端部位置,這樣避免了層層映射過程中,由于權重小于1而導致的梯度消失。它一方面使得模型可以更深,這樣網(wǎng)絡表達的能力更好了,另一方面也使得訓練速度加快。
假設某段神經(jīng)網(wǎng)絡的輸入是x,期望輸出是H(x),如果直接把x傳遞到輸出端,那么我們要學習的目標是F(x)=H(x)-x。此時目標相當于發(fā)生變更,稱之為殘差。(以下是一個殘差模塊的代碼)
class BottleNeck(nn.Module):expansion = 4def __init__(self,in_channels,out_channels,stride=1):super().__init__()self.residual_function = nn.Sequential(nn.Conv2d(in_channels,out_channels,stride=1,bias=False),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels,out_channels,stride=stride,padding=1,bias=False),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels,out_channels*BottleNeck.expansion,\kernel_size=1,bias=False),nn.BatchNorm2d(out_channels*expansion))self.shortcut = nn.Sequential()if stride!=1 or in_channels!=out_channels*BottleNeck.expansion:self.shortcut = nn.Sequential(nn.Conv2d(in_channels,out_channels*BottleNeck.expansion,\kernel_size=1,bias=False),nn.BatchNorm2d(out_channels*BottleNeck.expansion))def forward(self,x):return nn.ReLU(inplace=True)(self.residual_function(x)+self.shortcut(x))殘差網(wǎng)絡可以設計18、34、50、101、152層,用的都是1x1和3x3的小卷積核。
四、RIR
RIR模型的全稱是ResNet in ResNet,這是一種深度dual-stream架構,它對ResNets和標準的CNN進行了推廣,并且很容易實現(xiàn)(沒有額外的計算開銷)。論文中指出當前的ResNet使用identity連接會導致不同級別的特征在每一層積聚,即使在一個深度網(wǎng)絡,前面的一些層學習到的一些特征可能在后面的層不再提供有用的信息。因此RIR網(wǎng)絡被提出,它保留了兩個流:residual stream(殘差流)和transient stream(原始卷積流)。它的結構如下:
從圖中可以看出一個2層的RIR模塊由2個ResNet Init組成。ResNet Init結構如圖b所示,dual stream通過自身卷積和交叉卷積相加得到結果,代碼如下:
class ResnetInit(nn.Module):def __init__(self,in_channels,out_channels,stride):super().__init__()self.residual_stream_conv = nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1,stride=stride)self.transient_stream_conv = nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1,stride=stride)self.residual_stream_conv_across = nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1,stride=stride)self.transient_stream_conv_across = nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1,stride=stride)self.residual_bn_relu = nn.Sequential(nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True))self.transient_bn_relu = nn.Sequential(nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True))self.shortcut = nn.Sequential()if stride!=1 or in_channels!=out_channels:self.shortcut = nn.Sequential(nn.Conv2d(in_channels,out_channels,kernel_size=1,stride=stride))def forward(self,x):x_residual,x_transient = x,xresidual_r_r = self.residual_stream_conv(x_residual)residual_r_t = self.residual_stream_conv_across(x_transient)transient_t_t = self.transient_stream_conv(x_transient)transient_t_r = self.transient_stream_conv_across(x_residual)residual_shortcut = self.shortcut(x_residual)x_residual = self.residual_bn_relu(residual_r_r+residual_r_t+residual_shortcut)x_transient = self.transient_bn_relu(transient_t_r+transient_t_t)return x_residual,x_transient五、InceptionResNet
谷歌于2016年提出了InceptionResNetV1和InceptionResNetV2。二者都是將Inception同ResNet相融合,加上了shortcut的分支。Inception提出的目的是使得模型更寬,ResNet提出的目的是使得模型更深,將二者結合可以使得模型又寬又深。模型結構如下:
在InceptionResNet網(wǎng)絡中,作者采用了三種不同的Inception-ResNet模塊(Inception-resnet-A,Inception-resnet-B,inception-resnet-C)。三種模塊結構如下:
除此之外,作者還提出了Reduction模塊(如下),分別接在Inception-resnet-A后(ReductionA)和Inception-resnet-B后(ReductionB)。Reduction的提出是為了補償因Inception塊引起的降維。k、l、m、n表示filter個數(shù),可以在論文的表中進行查找。
InceptionResNet-v1/v2將Inception和ResNet結合到一起,使得Inception減少了冗余度和復雜度,使它能夠更快的收斂,更加容易訓練。
六、PreActResNet
鑒于ResNet在非常深的網(wǎng)絡結構中也會遇到優(yōu)化問題(盡管我們一般不會使用非常深的網(wǎng)絡),作者對ResNet進行改進,提出了PreAct的殘差結構,如下圖所示
左圖是原始的ResNet殘差結構,右圖是作者提出的PreActResNet殘差結構。從圖中我們可以看出,PreAct殘差結構在進行卷積之前加入了BatchNorm和ReLU。通過實驗我們得到這樣的結論:不管網(wǎng)絡有多深,整個網(wǎng)絡中的梯度流都不會產(chǎn)生彌散問題。
class PreActBasic(nn.Module):expansion = 1def __init__(self, in_channels, out_channels, stride):super().__init__()self.residual = nn.Sequential(nn.BatchNorm2d(in_channels),nn.ReLU(inplace=True),nn.Conv2d(in_channels, out_channels, kernel_size=3, \stride=stride, padding=1),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels, out_channels * PreActBasic.expansion, \kernel_size=3, padding=1))self.shortcut = nn.Sequential()if stride != 1 or in_channels != out_channels * PreActBasic.expansion:self.shortcut = nn.Conv2d(in_channels, out_channels * PreActBasic.expansion,\ kernel_size=1, stride=stride)def forward(self, x):res = self.residual(x)shortcut = self.shortcut(x)return res + shortcut七、ResNeXt
ResNeXt網(wǎng)絡結構于2017年提出,在ResNet的基礎上作出了進一步的改進。與InceptionResNet類似,作者提出這個模型的思想也是基于將模型做的更深及更寬。本文提出的 ResNeXt 結構可以在不增加參數(shù)復雜度的前提下提高準確率,還減少了超參數(shù)的數(shù)量。
在ResNeXt提出之前,有VGG系列和Inception系列網(wǎng)絡模型。VGG系列模型主要是通過堆疊網(wǎng)絡(即深度)來提高網(wǎng)絡性能,Inception系列網(wǎng)絡模型提出了split-transform-merge策略(即提高寬度)來提高網(wǎng)絡性能。而ResNeXt將二者結合起來。下面是一個ResNeXt模塊的結構:
論文中提出了cardinality(the size of the set of transformations),圖示是cardinality=32時,即存在32個并聯(lián)的模塊,在代碼中是通過組卷積實現(xiàn)的,就是卷積中的group size,令group=cardinality。實驗證明,增加 cardinality 比增加深度和寬度更有效。
class ResNeXtBlcok(nn.Module):def __init__(self,in_channels,out_channels,stride):super().__init__()C = CARDINALITYD = int(out_channels*DEPTH)//BASEWIDTHself.split_transform = nn.Sequential(nn.Conv2d(in_channels,C*D,kernel_size=1,groups=C,bias=False),nn.BatchNorm2d(C*D),nn.ReLU(inplace=True),nn.Conv2d(C*D,C*D,kernel_size=3,stride=stride,padding=1,groups=C),nn.BatchNorm2d(C*D),nn.ReLU(inplace=True),nn.Conv2d(C*D,out_channels*expansion,kernel_size=1,bias=False),nn.BatchNorm2d(out_channels*expansion))self.shortcut = nn.Sequential()if stride!=1 or in_channels!=out_channels*4:self.shortcut = nn.Sequential(nn.Conv2d(in_channels,out_channels*expansion,kernel_size=1,\stride=stride,bias=False),nn.BatchNorm2d(out_channels*expansion))self.relu = nn.ReLU(inplace=True)def forward(self,x):shortcut = self.shortcut(x)split_transform = self.split_transform(x)out = shortcut+split_transformout = self.relu(out)return out八、WideResNet
WideResNet的提出是因為ResNet的跳躍連接導致只有少量的殘差塊學習到特征。于是作者嘗試著利用另一種思路:減少深度,增加寬度,就提出了WideResNet。
圖c就是在圖a的基礎上,拓寬了跳躍連接的寬度。圖d在圖c的基礎上增加了dropout,以提高模型的泛化能力。
模型參數(shù)如下:
在搭建好BasicBlock和BottleNeck之后,模型的代碼如下:
class WideResNet(nn.Module):def __init__(self,block,block_num,wfactor,class_num=100):super().__init__()self.in_channel=16self.pre = nn.Sequential(nn.Conv2d(3,16,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(16),nn.ReLU(inplace=True))self.stage1 = self._make_stage(block,block_num[0],16*wfactor,1)self.stage2 = self._make_stage(block,block_num[1],32*wfactor,2)self.stage3 = self._make_stage(block,block_num[2],64*wfactor,2)self.avgpool = nn.AvgPool2d(8,1)self.fc = nn.Linear(64*wfactor*block.expansion,class_num)def _make_stage(self,block,block_num,out_channels,stride):strides = [stride] + [1]*(block_num-1)layers = []for stride in strides:layers.append(block(self.in_channel,out_channels,stride=stride))self.in_channel = out_channelsreturn nn.Sequential(*layers)def forward(self,x):x = self.pre(x)x = self.stage1(x)x = self.stage2(x)x = self.stage3(x)x = self.avgpool(x)x = x.view(x.size(0),-1)x = self.fc(x)return x九、Xception
在Xception模型中,作者提出了“depth-wise separable convolution”的結構。depthwise separable convolution就是先用M個3x3卷積核一對一卷積輸入的M個feature map,不求和,生成M個結果;然后用N個1x1的卷積核正常卷積前面生成的M個結果,求和,最后生成N個結果。因此文章中將depthwise separable convolution分成兩步,一步叫depthwise convolution,另一步是pointwise convolution。
class SeperableConv(nn.Module):def __init__(self,in_channels,out_channels,kernel_size,**kwargs):super().__init__()self.depthwise = nn.Conv2d(in_channels,in_channels,kernel_size,\groups=in_channels,bias=False,**kwargs)self.pointwise = nn.Conv2d(in_channels,out_channels,kernel_size=1,bias=False)def forward(self,x):x = self.depthwise(x)x = self.pointwise(x)return x下圖中為Xception結構的表示。它就是由Inception v3直接演變而來。其中引入了Residual learning的結構。
Xception中引入了Entry/Middle/Exit三個flow,每個flow內(nèi)部使用不同的重復模塊。Entry flow主要是用來不斷下采樣,減小空間維度;中間的Middle flow是最核心的部分,不斷學習關聯(lián)關系,優(yōu)化特征;最終則是通過Exit flow匯總、整理特征,用于交由FC來進行表達。
十、DenseNet
DenseNet于2017年提出。我們知道ResNet的提出,是通過shortcut來避免出現(xiàn)梯度消失/爆炸。因為當梯度消失/爆炸時,輸入的特征不易于學習。DenseNet的核心思想就是在每一層上都加上一個單獨的shortcut,使得任意兩層都能直接連通。DenseNet的模型如下:
在代碼實現(xiàn)中,是通過torch.cat操作實現(xiàn)任意兩層互聯(lián)。各層的參數(shù)如下:
其中,k表示growth_rate,即每個Dense Block中每層輸出的feather map數(shù)量。BottleNeck是由1x1和3x3兩層卷積層組成,目的是降維,減少計算量。Transition Layer層中包含了卷積層和池化層,使得輸出通道比輸入通道減少一半,并減少計算量。
實驗結果證明,DenseNet在與ResNet同等復雜度和參數(shù)的情況下,效果要優(yōu)于ResNet。
十一、SENet
SENet的全稱是Squeeze and Excitation Networks,顧名思義,就是在網(wǎng)絡中加入了Squeeze和Excitation操作。作者提出這個網(wǎng)絡的目的是顯式地建模卷積特征通道之間的相互依賴性來提高網(wǎng)絡的表達能力。網(wǎng)絡結構如下:
其中,Squeeze通過空間維度壓縮特征,在空間上做全局平均池化,每個通道的二維特征變成了一個實數(shù),這個實數(shù)某種程度上具有全局的感受野,并且輸出的維度和輸入的通道數(shù)相匹配。而Excitation的作用是用兩個全連接層和Sigmoid函數(shù)得到各通道的權值,第一個全連接層會壓縮通道數(shù),減少計算量。該權值與Fscale得到的feature map結合得到最后的結果。下圖是將SE模塊嵌入Inception和ResNet中的例子:
與ResNet結合的代碼如下:
class BasicResidualSEBlock(nn.Module):expansion = 1def __init__(self,in_channels,out_channels,stride,r=16):super().__init__()self.residual = nn.Sequential(nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=stride,padding=1),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels,out_channels*self.expansion,kernel_size=3,padding=1),nn.BatchNorm2d(out_channels*self.expansion),nn.ReLU(inplace=True))self.shortcut = nn.Sequential()if stride!=1 or in_channels!=out_channels:self.shortcut = nn.Sequential(nn.Conv2d(in_channels,out_channels*self.expansion,\kernel_size=1,stride=stride),nn.BatchNorm2d(out_channels*self.expansion),nn.ReLU(inplace=True))self.squeeze = nn.AdaptiveAvgPool2d(1)self.excitation = nn.Sequential(nn.Linear(out_channels*self.expansion,out_channels*self.expansion//r),nn.ReLU(inplace=True),nn.Linear(out_channels*self.expansion//r,out_channels*self.expansion),nn.Sigmoid())def forward(self,x):shortcut = self.shortcut(x)residual = self.residual(x)squeeze = self.squeeze(residual)squeeze = squeeze.view(squeeze.size(0), -1)excitation = self.excitation(squeeze)excitation = excitation.view(residual.size(0),residual.size(1),1,1)x = residual*(excitation.expand_as(residual))+shortcutreturn nn.ReLU(inplace=True)(x)十二、SqueezeNet
SqueezeNet 發(fā)表于ICLR-2017,作者分別來自Berkeley和Stanford,SqueezeNet不是模型壓縮技術,而是 “design strategies for CNN architectures with few parameters”。SqueezeNet的思想與Inception非常類似,其提出了squeeze層和expand層。Squeeze層只采用了1x1的卷積核,使得卷積過后的通道盡可能小,以減少計算量。然后進入expand層,expand層的卷積核由1x1和3x3的卷積核構成(如下圖),其思想就和Inception類似。
s1x1 (squeeze convolution layer中1x1filter的個數(shù))、e1x1(expand layer中1?1filter的個數(shù))、e3x3(expand layer中3x3 filter的個數(shù))都可以進行調整。
這里給出一個子模塊的代碼:
class Fire(nn.Module):def __init__(self,in_channel,out_channel,squeeze_channel):super().__init__()self.squeeze = nn.Sequential(nn.Conv2d(in_channel,squeeze_channel,kernel_size=1),nn.BatchNorm2d(squeeze_channel),nn.ReLU(inplace=True)) self.expand1x1 = nn.Sequential(nn.Conv2d(squeeze_channel,int(out_channel//2),kernel_size=1),nn.BatchNorm2d(int(out_channel//2),),nn.ReLU(inplace=True))self.expand3x3 = nn.Sequential(nn.Conv2d(squeeze_channel,int(out_channel//2),kernel_size=3,padding=1),nn.BatchNorm2d(int(out_channel//2)),nn.ReLU(inplace=True))def forward(self,x):x = self.squeeze(x)return torch.cat([self.expand1x1(x),self.expand3x3(x)],1)文中給了三種不同的網(wǎng)絡模型:
左邊為原始的SqueezeNet,中間為包含simple bypass的改進版本,最右側為使用complex bypass的改進版本。在下表中給出了更多的細節(jié)。關于bypass,個人理解就像shortcut,使得模型更容易優(yōu)化。
十三、MobileNet
MobileNet V1,2017年Google人員發(fā)表,針對手機等嵌入式設備提出的一種輕量級的深層神經(jīng)網(wǎng)絡,采用了深度可分離的卷積。核心思想就是卷積核的巧妙分解,可以有效減少網(wǎng)絡參數(shù)。它將標準卷積分解成一個深度卷積和一個點卷積(1 × 1卷積核),即Depthwise和Pointwise。如下圖:
Depthwise將每個卷積核應用到每一個通道,體現(xiàn)在代碼中的group,而Pointwise中1 × 1卷積則用來組合通道卷積的輸出。
class DepthSeperable(nn.Module):def __init__(self,in_channels,out_channels,kernel_size,**kwargs):super().__init__()self.depthwise = nn.Sequential(nn.Conv2d(in_channels,in_channels,kernel_size=kernel_size,\groups=in_channels,**kwargs),nn.BatchNorm2d(in_channels),nn.ReLU(inplace=True))self.pointwise = nn.Sequential(nn.Conv2d(in_channels,out_channels,kernel_size=1),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True))def forward(self,x):x = self.depthwise(x)x = self.pointwise(x)return x而后,通過一個流線型的架構(堆疊式),都是由深度可分離的卷積模塊堆疊的,構成最后的模型。具體的參數(shù)如下:
MobileNetV2則是在V1的基礎上做出了改進,先進行1x1的Pointwise通道擴張,Depthwise+Pointwise通道壓縮,然后Linear激活。
十四、ShuffleNet
ShuffleNetV1,2017年曠視科技的Xiangyu Zhang,Jian Sun發(fā)表,參考MobileNet和ResNet,提出了Channel Shuffle來增強分組卷積的全局信息流通。ShuffleNetV1是專門為計算能力有限的移動平臺設計。采用兩個新操作——逐漸群卷積(pointwise group convolution)和通道混洗(channel shuffle)在保障精確率損失不大的同時大大減少了計算成本。
ShuffleNetV1最大的創(chuàng)新點是在之前模型的基礎上進一步加入了channel shuffle。假設引入group操作,設group為g,那么N個輸入feature map就被分成了g個group,M個filter就被分成g個group。然后在做卷積操作時,第一個group的M/g個filter中的每一個都和第一個group的N/g個輸入feature map做卷積得到結果。第二個group同理,直至最后一個group。這種操作大大減少了計算量。但如果多個group疊加在一起,如有兩個卷積層都有group操作,就會產(chǎn)生邊界效應。即某個輸出channel僅來自輸入channel的一小部分,這樣學出來的特征會非常局限。于是需要channel shuffle來解決。效果如下圖:
在進行GConv之前對其輸入feature map做一個分配,也就是每個group分成幾個subgroup,然后將不同group的subgroup作為GConv2的一個group的輸入,使得GConv2的每一個group都能卷積輸入的所有group的feature map,如(c)中的channel shuffle。其代碼如下:
class ChannelShuffle(nn.Module):def __init__(self,group):self.group = groupdef forward(self,x):batch_size,channels,height,width = x.data.size() channel_per_group = channels/self.groupx = x.view(batch_size,self.group,channel_per_group,height,width)x = x.transpose(1,2).contiguous() #這一步畫一下圖就明白了x = x.view(batch_size,-1,height,width)return xShuffleNetV2根據(jù)提出的4則輕量化標準改進了ShuffleNet V1:通道寬度相同、減少分組卷積、減少分支、減少Element-wise。主要是在網(wǎng)絡結構及參數(shù)上做出了改動。
a和b是ShuffleNet v1的兩種不同block結構,兩者的差別在于后者對特征圖尺寸做了縮小。c和d是ShuffleNet v2的兩種不同block結構。從a和c的對比可以看出c在開始處增加了一個channel split操作,該操作將輸入特征的通道分開(文中是對半切分),這樣操作的目的是使得運行速度最快。然后c中取消了1x1卷積層中的group操作,這和同時前面的channel split其實已經(jīng)算是變相的group操作了。其次,channel shuffle的操作移到了concat后面,同時也是因為第一個1x1卷積層沒有group操作,所以在其后面跟channel shuffle也沒有太大必要。最后是將element-wise add操作替換成concat,這是為了減少element-wise操作。多個c結構連接在一起的話,channel split、concat和channel shuffle是可以合并在一起的。b和d的對比也是同理,只不過因為d的開始處沒有channel split操作,所以最后concat后特征圖通道數(shù)翻倍。
關于ShuffleNetV2的詳細解釋可以參考https://blog.csdn.net/u014380165/article/details/81322175
總結
以上是生活随笔為你收集整理的一些常见的CNN模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: security模仿密码登录实现短信验证
- 下一篇: 回环检测(激光 VS 视觉)