我平時常用的分類網(wǎng)絡(luò)

姓名:畢曉鵬

學(xué)號:19021210824

【嵌牛導(dǎo)讀】自己平時積累了一些pytorch的訓(xùn)練trick,分享給大家

【嵌牛鼻子】深度學(xué)習(xí),分類網(wǎng)絡(luò)

【嵌牛提問】了解其他的網(wǎng)絡(luò)嗎

【嵌牛正文】


resnet

平時經(jīng)常用到resnet網(wǎng)絡(luò)當(dāng)作backbone,resnet結(jié)構(gòu)清晰,比較方便單獨(dú)拿出一層來單獨(dú)操作。

BasicBlock采用的是兩個3x3的卷積核

Bottleneck先用1x1卷積核降維,然后3x3卷積核,1x1卷積核升維,所以殘差塊輸入通道和輸出通道是不變的。

resnet層數(shù)計(jì)算:【3,4,6,3】(3+4+6+3)*3 + 2 = 50

3,4,6,3為殘差塊數(shù)量,其中每個塊里面含有三層,然后加上分類層和第一層一共50

1x1卷積核作用:

降維:減小參數(shù)量,減小了通道數(shù)量,后面進(jìn)行下一步操作,需要的卷積核數(shù)量就少了。

升維:跨通道信息組合,降維的也會有不同通道的信息組合,另外可以feature maps size不變的(即不損失分辨率)的前提下大幅增加非線性特性

resne網(wǎng)絡(luò)整體結(jié)構(gòu)

# encoding: utf-8"""@author:? liaoxingyu@contact: sherlockliao01@gmail.com"""importmathimporttorchfromtorchimportnndefconv3x3(in_planes,out_planes,stride=1):"""3x3 convolution with padding"""returnnn.Conv2d(in_planes,out_planes,kernel_size=3,stride=stride,padding=1,bias=False)classBasicBlock(nn.Module):expansion=1def__init__(self,inplanes,planes,stride=1,downsample=None):super(BasicBlock,self).__init__()self.conv1=conv3x3(inplanes,planes,stride)self.bn1=nn.BatchNorm2d(planes)self.relu=nn.ReLU(inplace=True)self.conv2=conv3x3(planes,planes)self.bn2=nn.BatchNorm2d(planes)self.downsample=downsampleself.stride=stridedefforward(self,x):residual=xout=self.conv1(x)out=self.bn1(out)out=self.relu(out)out=self.conv2(out)out=self.bn2(out)ifself.downsampleisnotNone:residual=self.downsample(x)out+=residualout=self.relu(out)returnout#輸入 in_planes? #先壓縮通道 然后恢復(fù)到 inplanesclassBottleneck(nn.Module):expansion=4def__init__(self,inplanes,planes,stride=1,downsample=None):super(Bottleneck,self).__init__()self.conv1=nn.Conv2d(inplanes,planes,kernel_size=1,bias=False)self.bn1=nn.BatchNorm2d(planes)self.conv2=nn.Conv2d(planes,planes,kernel_size=3,stride=stride,padding=1,bias=False)self.bn2=nn.BatchNorm2d(planes)self.conv3=nn.Conv2d(planes,planes*4,kernel_size=1,bias=False)self.bn3=nn.BatchNorm2d(planes*4)self.relu=nn.ReLU(inplace=True)self.downsample=downsampleself.stride=stridedefforward(self,x):residual=xout=self.conv1(x)out=self.bn1(out)out=self.relu(out)out=self.conv2(out)out=self.bn2(out)out=self.relu(out)out=self.conv3(out)out=self.bn3(out)ifself.downsampleisnotNone:residual=self.downsample(x)out+=residualout=self.relu(out)returnoutclassResNet(nn.Module):def__init__(self,last_stride=1,block=Bottleneck,layers=[3,4,6,3]):#輸入通道self.inplanes=64super().__init__()self.conv1=nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3,bias=False)self.bn1=nn.BatchNorm2d(64)self.relu=nn.ReLU(inplace=True)self.maxpool=nn.MaxPool2d(kernel_size=3,stride=2,padding=1)self.layer1=self._make_layer(block,64,layers[0])self.layer2=self._make_layer(block,128,layers[1],stride=2)self.layer3=self._make_layer(block,256,layers[2],stride=2)self.layer4=self._make_layer(block,512,layers[3],stride=last_stride)def_make_layer(self,block,planes,blocks,stride=1):downsample=None#把通道數(shù)量調(diào)整為planes*4ifstride!=1orself.inplanes!=planes*block.expansion:downsample=nn.Sequential(nn.Conv2d(self.inplanes,planes*block.expansion,kernel_size=1,stride=stride,bias=False),nn.BatchNorm2d(planes*block.expansion),)layers=[]layers.append(block(self.inplanes,planes,stride,downsample))self.inplanes=planes*block.expansion#blocks 每層含有的block數(shù)量foriinrange(1,blocks):layers.append(block(self.inplanes,planes))returnnn.Sequential(*layers)defforward(self,x):#conv1 7x7 64 stride=2x=self.conv1(x)x=self.bn1(x)x=self.relu(x)#max pool 3x3 stride=2x=self.maxpool(x)x=self.layer1(x)x=self.layer2(x)x=self.layer3(x)x=self.layer4(x)returnxdefload_param(self,model_path):#加載預(yù)訓(xùn)練模型param_dict=torch.load(model_path)# param_dict = torch.load("/home/bi/Downloads/resnet50-19c8e357.pth")print("load pretain success!")foriinparam_dict:if'fc'ini:continueself.state_dict()[i].copy_(param_dict[i])defrandom_init(self):forminself.modules():ifisinstance(m,nn.Conv2d):n=m.kernel_size[0]*m.kernel_size[1]*m.out_channelsm.weight.data.normal_(0,math.sqrt(2./n))elifisinstance(m,nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()defresnet50(last_stride):returnResNet(last_stride=1,block=Bottleneck,layers=[3,4,6,3])

resnext

resnext使用了分組卷積的形式來提高網(wǎng)絡(luò)的性能

分組卷積效果好的原因:

不同group可以學(xué)到不同的信息,可以參考alexnet

代碼上只是殘差塊不同

class Bottleneck(nn.Module):

? ? """

? ? RexNeXt bottleneck type C

? ? """

? ? expansion = 4

? ? def __init__(self, inplanes, planes, with_ibn, baseWidth, cardinality, stride=1, downsample=None):

? ? ? ? """ Constructor

? ? ? ? Args:

? ? ? ? ? ? inplanes: input channel dimensionality

? ? ? ? ? ? planes: output channel dimensionality

? ? ? ? ? ? baseWidth: base width.? default 4

? ? ? ? ? ? cardinality: num of convolution groups.? default 32

? ? ? ? ? ? stride: conv stride. Replaces pooling layer.

? ? ? ? """

? ? ? ? super(Bottleneck, self).__init__()




? ? ? ? D = int(math.floor(planes * (baseWidth / 64)))

? ? ? ? C = cardinality

? ? ? ? self.conv1 = nn.Conv2d(inplanes, D * C, kernel_size=1, stride=1, padding=0, bias=False)

? ? ? ? if with_ibn:

? ? ? ? ? ? self.bn1 = IBN(D * C)

? ? ? ? else:

? ? ? ? ? ? self.bn1 = nn.BatchNorm2d(D * C)

? ? ? ? self.conv2 = nn.Conv2d(D * C, D * C, kernel_size=3, stride=stride, padding=1, groups=C, bias=False)

? ? ? ? self.bn2 = nn.BatchNorm2d(D * C)

? ? ? ? self.conv3 = nn.Conv2d(D * C, planes * 4, kernel_size=1, stride=1, padding=0, bias=False)

? ? ? ? self.bn3 = nn.BatchNorm2d(planes * 4)

? ? ? ? self.relu = nn.ReLU(inplace=True)

? ? ? ? self.downsample = downsample

? ? def forward(self, x):

? ? ? ? residual = x

? ? ? ? out = self.conv1(x)

? ? ? ? out = self.bn1(out)

? ? ? ? out = self.relu(out)

? ? ? ? out = self.conv2(out)

? ? ? ? out = self.bn2(out)

? ? ? ? out = self.relu(out)

? ? ? ? out = self.conv3(out)

? ? ? ? out = self.bn3(out)

? ? ? ? if self.downsample is not None:

? ? ? ? ? ? residual = self.downsample(x)

? ? ? ? out += residual

? ? ? ? out = self.relu(out)

? ? ? ? return out

senet

senet模塊操作

首先是Squeeze操作,我們順著空間維度來進(jìn)行特征壓縮,將每個二維的特征通道變成一個實(shí)數(shù),這個實(shí)數(shù)某種程度上具有全局的感受野,并且輸出的維度和輸入的特征通道數(shù)相匹配。它表征著在特征通道上響應(yīng)的全局分布,而且使得靠近輸入的層也可以獲得全局的感受野,這一點(diǎn)在很多任務(wù)中都是非常有用的。

其次是Excitation操作,它是一個類似于循環(huán)神經(jīng)網(wǎng)絡(luò)中門的機(jī)制。通過參數(shù) 來為每個特征通道生成權(quán)重,其中參數(shù) 被學(xué)習(xí)用來顯式地建模特征通道間的相關(guān)性。

最后是一個Reweight的操作,我們將Excitation的輸出的權(quán)重看做是進(jìn)過特征選擇后的每個特征通道的重要性,然后通過乘法逐通道加權(quán)到先前的特征上,完成在通道維度上的對原始特征的重標(biāo)定。

實(shí)現(xiàn)

from torch import nn

class SELayer(nn.Module):

? ? def __init__(self, channel, reduction=16):

? ? ? ? super(SELayer, self).__init__()

? ? ? ? self.avg_pool = nn.AdaptiveAvgPool2d(1)

? ? ? ? self.fc = nn.Sequential(

? ? ? ? ? ? nn.Linear(channel, int(channel / reduction), bias=False),

? ? ? ? ? ? nn.ReLU(inplace=True),

? ? ? ? ? ? nn.Linear(int(channel / reduction), channel, bias=False),

? ? ? ? ? ? nn.Sigmoid()

? ? ? ? )

? ? def forward(self, x):

? ? ? ? b, c, _, _ = x.size()

? ? ? ? y = self.avg_pool(x).view(b, c)

? ? ? ? y = self.fc(y).view(b, c, 1, 1)

? ? ? ? return x * y.expand_as(x)

densenet

densenet的特點(diǎn)

特征重用:使用密集鏈接,使用了不同層次的特征,一般分類網(wǎng)絡(luò)只使用最高層次的特征,densenet保留了低維度的特征,對特征的利用率比較高。

參數(shù)少,因?yàn)槊總€通道數(shù)很少

占用顯存高,因?yàn)樽銮跋騻鞑r,不但需要當(dāng)前層特征,還需要之前的特征。

res2net

網(wǎng)絡(luò)將原來串行的方式,通過在通道上進(jìn)行拆分,不同尺度特征的提取,在融合特征,提高網(wǎng)絡(luò)的復(fù)雜度和表達(dá)能力。上圖表示在一個block中有4個尺度。

圖中的橫向箭頭是加操作,融合不同感受野的特征圖。

殘差塊實(shí)現(xiàn)代碼為:

class Bottle2neck(nn.Module):

? ? expansion = 4

? ? def __init__(self, inplanes, planes, stride=1, downsample=None, baseWidth=26, scale=4, stype='normal'):

? ? ? ? """ Constructor

? ? ? ? Args:

? ? ? ? ? ? inplanes: input channel dimensionality

? ? ? ? ? ? planes: output channel dimensionality

? ? ? ? ? ? stride: conv stride. Replaces pooling layer.

? ? ? ? ? ? downsample: None when stride = 1

? ? ? ? ? ? baseWidth: basic width of conv3x3

? ? ? ? ? ? scale: number of scale.

? ? ? ? ? ? type: 'normal': normal set. 'stage': first block of a new stage.

? ? ? ? """

? ? ? ? super(Bottle2neck, self).__init__()

? ? ? ? width = int(math.floor(planes * (baseWidth / 64.0)))

? ? ? ? self.conv1 = nn.Conv2d(inplanes, width * scale, kernel_size=1, bias=False)

? ? ? ? self.bn1 = nn.BatchNorm2d(width * scale)

? ? ? ? if scale == 1:

? ? ? ? ? ? self.nums = 1

? ? ? ? else:

? ? ? ? ? ? self.nums = scale - 1

? ? ? ? if stype == 'stage':

? ? ? ? ? ? self.pool = nn.AvgPool2d(kernel_size=3, stride=stride, padding=1)

? ? ? ? convs = []

? ? ? ? bns = []

? ? ? ? for i in range(self.nums):

? ? ? ? ? ? convs.append(nn.Conv2d(width, width, kernel_size=3, stride=stride, padding=1, bias=False))

? ? ? ? ? ? bns.append(nn.BatchNorm2d(width))

? ? ? ? self.convs = nn.ModuleList(convs)

? ? ? ? self.bns = nn.ModuleList(bns)

? ? ? ? self.conv3 = nn.Conv2d(width * scale, planes * self.expansion, kernel_size=1, bias=False)

? ? ? ? self.bn3 = nn.BatchNorm2d(planes * self.expansion)

? ? ? ? self.relu = nn.ReLU(inplace=True)

? ? ? ? self.downsample = downsample

? ? ? ? self.stype = stype

? ? ? ? self.scale = scale

? ? ? ? self.width = width

? ? def forward(self, x):

? ? ? ? residual = x

? ? ? ? out = self.conv1(x)

? ? ? ? out = self.bn1(out)

? ? ? ? out = self.relu(out)

? ? ? ? spx = torch.split(out, self.width, 1)

? ? ? ? for i in range(self.nums):

? ? ? ? ? ? if i == 0 or self.stype == 'stage':

? ? ? ? ? ? ? ? sp = spx[i]

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? sp = sp + spx[i]

? ? ? ? ? ? sp = self.convs[i](sp)

? ? ? ? ? ? sp = self.relu(self.bns[i](sp))

? ? ? ? ? ? if i == 0:

? ? ? ? ? ? ? ? out = sp

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? out = torch.cat((out, sp), 1)

? ? ? ? if self.scale != 1 and self.stype == 'normal':

? ? ? ? ? ? out = torch.cat((out, spx[self.nums]), 1)

? ? ? ? elif self.scale != 1 and self.stype == 'stage':

? ? ? ? ? ? out = torch.cat((out, self.pool(spx[self.nums])), 1)

? ? ? ? out = self.conv3(out)

? ? ? ? out = self.bn3(out)

? ? ? ? if self.downsample is not None:

? ? ? ? ? ? residual = self.downsample(x)

? ? ? ? out += residual

? ? ? ? out = self.relu(out)

? ? ? ? return out

resnest

ResNeSt 的全稱是:Split-Attention?Networks,也就是特別引入了Split-Attention模塊。

論文借鑒了

GoogleNet?采用了Multi-path機(jī)制,其中每個網(wǎng)絡(luò)塊均由不同的卷積kernels組成。

ResNeXt在ResNet bottle模塊中采用組卷積,將multi-path結(jié)構(gòu)轉(zhuǎn)換為統(tǒng)一操作。

SE-Net?通過自適應(yīng)地重新校準(zhǔn)通道特征響應(yīng)來引入通道注意力(channel-attention)機(jī)制

SK-Net 通過兩個網(wǎng)絡(luò)分支引入特征圖注意力(feature-map attention)。

ResNeSt 和 SE-Net、SK-Net 的對應(yīng)圖示如下:

其中上圖中都包含的 Split Attention模塊如下圖所示:

參考:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容