机器学习笔记:ResNet 及残差连接
1 為什么會(huì)有ResNet?
? ? ? ?自從深度神經(jīng)網(wǎng)絡(luò)在ImageNet大放異彩之后,后來(lái)問世的深度神經(jīng)網(wǎng)絡(luò)就朝著網(wǎng)絡(luò)層數(shù)越來(lái)越深的方向發(fā)展。直覺上我們不難得出結(jié)論:增加網(wǎng)絡(luò)深度后,網(wǎng)絡(luò)可以進(jìn)行更加復(fù)雜的特征提取,因此更深的模型可以取得更好的結(jié)果。
????????但事實(shí)并非如此,人們發(fā)現(xiàn)隨著網(wǎng)絡(luò)深度的增加,模型精度并不總是提升,并且這個(gè)問題顯然不是由過擬合(overfitting)造成的,因?yàn)榫W(wǎng)絡(luò)加深后不僅測(cè)試誤差變高了,它的訓(xùn)練誤差竟然也變高了。【注:過擬合應(yīng)該是訓(xùn)練誤差很小,但是測(cè)試誤差很大;這里訓(xùn)練誤差都很大,說明不是過擬合的問題】
????????這可能是因?yàn)?span style="color:#38d8f0;">更深的網(wǎng)絡(luò)會(huì)伴隨梯度消失/爆炸問題,從而阻礙網(wǎng)絡(luò)的收斂。這種加深網(wǎng)絡(luò)深度但網(wǎng)絡(luò)性能卻下降的現(xiàn)象被稱為退化問題(degradation problem)。
? ? ? ? 從上圖我們可以看出,當(dāng)傳統(tǒng)神經(jīng)網(wǎng)絡(luò)的層數(shù)從20增加為56時(shí),網(wǎng)絡(luò)的訓(xùn)練誤差和測(cè)試誤差均出現(xiàn)了明顯的增長(zhǎng),也就是說,網(wǎng)絡(luò)的性能隨著深度的增加出現(xiàn)了明顯的退化。
????????ResNet就是為了解決這種退化問題而誕生的。
2 ResNet原理
????????隨著網(wǎng)絡(luò)層數(shù)的增加,梯度爆炸和梯度消失問題嚴(yán)重制約了神經(jīng)網(wǎng)絡(luò)的性能,研究人員通過提出包括Batch normalization在內(nèi)的方法,已經(jīng)在一定程度上緩解了這個(gè)問題,但依然不足以滿足需求。
2.1?恒等映射(Identity mapping)
? ? ? ? ResNet提出了使用恒等映射(Identity mapping)來(lái)解決這個(gè)問題。
????????問題解決的標(biāo)志是:增加網(wǎng)絡(luò)層數(shù),但訓(xùn)練誤差不增加。
????????那怎么構(gòu)建恒等映射呢?
????????簡(jiǎn)單地說,原先的網(wǎng)絡(luò)輸入x,希望輸出H(x)。
????????現(xiàn)在我們改一改,我們令H(x)=F(x)+x,那么我們的網(wǎng)絡(luò)就只需要學(xué)習(xí)輸出一個(gè)殘差F(x)=H(x)-x。
? ? ? ? ResNet作者提出,學(xué)習(xí)殘差F(x)=H(x)-x會(huì)比直接學(xué)習(xí)原始特征H(x)簡(jiǎn)單的多。
2.2 skip connection?
?
?3 ResNet(各變體)網(wǎng)絡(luò)結(jié)構(gòu)
????????ResNet有很多變體,比較著名的有五種主要形式:Res18,Res34,Res50,Res101,Res152。
????????
? ? ? ? 如上圖所示,ResNet及其變體主要包括三個(gè)主要部分:輸入部分(input)+中間卷積(stage1~stage4)+輸出部分(output)
3.1 網(wǎng)絡(luò)整體結(jié)構(gòu)
? ? ? ? 以ResNet18為例:?
class ResNet(nn.Module):def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.maxpool(x)#輸入部分x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)#卷積部分#ResNet18和其他的ResNet網(wǎng)絡(luò)的區(qū)別主要就在這四層里面x = self.avgpool(x)x = x.view(x.size(0), -1)x = self.fc(x)#輸出部分return x# 生成一個(gè)res18網(wǎng)絡(luò)(看是否有預(yù)訓(xùn)練的參數(shù)) def resnet18(pretrained=False, **kwargs):model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))return model?3.2 網(wǎng)絡(luò)輸入部分
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) ''' size=7*7,stride=2的卷積核 '''self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1 ''' size=3x3, stride=2的最大池化 ''')在pytorch 筆記:torch.nn.Conv2d_UQI-LIUWJ的博客-CSDN博客、pytorch筆記:torch.nn.MaxPool2d_UQI-LIUWJ的博客-CSDN博客中,我們說到,經(jīng)過Conv2d和MaxPool2d之后的channel數(shù)為:
????????在ResNet中,輸入部分是一個(gè)224x224的圖像,經(jīng)過輸入部分之后,變成了56x56大小的特征圖,極大減少了存儲(chǔ)所需大小。
?
3.3 中間卷積部分
????????中間卷積部分主要是下圖中的藍(lán)框部分,通過3*3卷積的堆疊來(lái)實(shí)現(xiàn)信息的提取。紅框中的[2, 2, 2, 2]和[3, 4, 6, 3]等則代表了bolck的重復(fù)堆疊次數(shù)。?
——>每一層卷積層:減一半feature map的長(zhǎng)和寬,增加輸出channel一倍
——>第一個(gè)卷積層(conv1——,又被稱為”stem“)
? ? ? ? ?紅框前面的是卷積核的大小,以及輸出的channel數(shù)
????????剛剛我們調(diào)用的resnet18( )函數(shù)中有一句
model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)????????這里的[2, 2, 2, 2]與圖中紅框是一致的,如果你將這行代碼改為 ResNet(BasicBlock, [3, 4, 6, 3], **kwargs), 那你就會(huì)得到一個(gè)res34網(wǎng)絡(luò)。
3.4? 殘差塊
? ? ? ??下面我們來(lái)具體看一下一個(gè)殘差塊是怎么實(shí)現(xiàn)的。
????????如下圖所示的basic-block,輸入數(shù)據(jù)分成兩條路,一條路經(jīng)過兩個(gè)3*3卷積,另一條路直接短接,二者相加經(jīng)過relu輸出,十分簡(jiǎn)單。
class BasicBlock(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 = stridedef forward(self, x):identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return out?3.5 輸出部分
?????????網(wǎng)絡(luò)輸出部分很簡(jiǎn)單,通過全局自適應(yīng)平滑池化,把所有的特征圖拉成1*1。
????????對(duì)于res18來(lái)說,就是1x512x7x7 的輸入數(shù)據(jù)拉成 1x512x1x1,然后接全連接層輸出,輸出節(jié)點(diǎn)個(gè)數(shù)與預(yù)測(cè)類別個(gè)數(shù)一致。
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * block.expansion, num_classes)4 bottleneck 結(jié)構(gòu)?
????????ResNet50起,就采用Bottleneck結(jié)構(gòu),主要是引入1x1卷積。
????????我們來(lái)看一下這里的1x1卷積有什么作用:
- 對(duì)通道數(shù)進(jìn)行升維和降維(跨通道信息整合),實(shí)現(xiàn)了多個(gè)特征圖的線性組合,同時(shí)保持了原有的特征圖大小;
- 相比于其他尺寸的卷積核,可以極大地降低運(yùn)算復(fù)雜度;
- 如果使用兩個(gè)3x3卷積堆疊,只有一個(gè)relu,但使用1x1卷積就會(huì)有兩個(gè)relu,引入了更多的非線性映射;
?
????????我們來(lái)計(jì)算一下1*1卷積的計(jì)算量?jī)?yōu)勢(shì):首先看上圖右邊的bottleneck結(jié)構(gòu),對(duì)于256維的輸入特征,參數(shù)數(shù)目:1x1x256x64+3x3x64x64+1x1x64x256=69632.
????????如果同樣的輸入輸出維度但不使用1x1卷積,而使用兩個(gè)3x3卷積的話,參數(shù)數(shù)目為(3x3x256x256)x2=1179648。
????????簡(jiǎn)單計(jì)算下就知道了,使用了1x1卷積的bottleneck將計(jì)算量簡(jiǎn)化為原有的5.9%,收益超高。
?參考文獻(xiàn):ResNet及其變種的結(jié)構(gòu)梳理、有效性分析與代碼解讀 - 知乎 (zhihu.com)
5 Batch-normalization和Relu,孰先孰后?
這個(gè)不確定,這里說一下ResNet中先BN再Relu的優(yōu)點(diǎn)吧
先BN的話,會(huì)有一半左右的輸出小于0,那么經(jīng)過Relu之后的結(jié)果就是0,網(wǎng)絡(luò)可以剪掉很多枝
總結(jié)
以上是生活随笔為你收集整理的机器学习笔记:ResNet 及残差连接的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch笔记:torch.nn.M
- 下一篇: 文巾解题 17. 电话号码的字母组合