殘差網(wǎng)絡(luò)ResNet代碼解讀

殘差網(wǎng)絡(luò)效果

卷積神經(jīng)網(wǎng)絡(luò)CNN的發(fā)展歷史如圖所示:

從起初AlexNet的的8層網(wǎng)絡(luò),到ResNet的152層網(wǎng)絡(luò),層數(shù)逐步增加。當(dāng)網(wǎng)絡(luò)層數(shù)增加到一定程度之后,錯(cuò)誤率反而上升,其原因是層數(shù)太多梯度下降變得越發(fā)困難。而ResNet解決了這一問(wèn)題。

目前ResNet是應(yīng)用最廣的圖像相關(guān)深度學(xué)習(xí)網(wǎng)絡(luò),圖像分類(lèi),目標(biāo)檢測(cè),圖片分割都使用該網(wǎng)絡(luò)結(jié)構(gòu)作為基礎(chǔ),另外,一些遷移學(xué)習(xí)也使用ResNet訓(xùn)練好的模型來(lái)提取圖像特征。

殘差網(wǎng)絡(luò)原理

首先,來(lái)看看比較官方的殘差網(wǎng)絡(luò)原理說(shuō)明:

“若將輸入設(shè)為X,將某一有參網(wǎng)絡(luò)層設(shè)為H,那么以X為輸入的此層的輸出將為H(X)。一般的CNN網(wǎng)絡(luò)如Alexnet/VGG等會(huì)直接通過(guò)訓(xùn)練學(xué)習(xí)出參數(shù)函數(shù)H的表達(dá),從而直接學(xué)習(xí)X -> H(X) 。而殘差學(xué)習(xí)則是致力于使用多個(gè)有參網(wǎng)絡(luò)層來(lái)學(xué)習(xí)輸入、輸出之間的殘差即H(X) - X即學(xué)習(xí)X -> (H(X) - X) + X。其中X這一部分為直接的identity mapping,而H(X) - X則為有參網(wǎng)絡(luò)層要學(xué)習(xí)的輸入輸出間殘差?!?/p>

第一次看到上述文字,我似乎明白了,但理解又不一定正確。在沒(méi)看到代碼之前,對(duì)VGG/ResNet的結(jié)構(gòu)原理沒(méi)什么感覺(jué),幾乎就是背下來(lái)哪個(gè)效果比較好,大概用了什么技術(shù)。后來(lái)看到了Pytorch中ResNet的代碼,原來(lái)簡(jiǎn)單到"五分鐘包會(huì)"的程度。用自然語(yǔ)言描述程序果然是把簡(jiǎn)單的問(wèn)題搞復(fù)雜了。

解讀核心程序

直接看代碼,不學(xué)習(xí)TensorFlow的復(fù)雜結(jié)構(gòu),也不使用生澀的公式語(yǔ)言,而用順序結(jié)構(gòu)的Pytorch作為通往深度學(xué)習(xí)的捷徑。下面來(lái)解讀Pytorch官方版的ResNet實(shí)現(xiàn)。完整代碼見(jiàn);

https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py

Torchvision是Torch的圖像工具包,上述代碼包含在Torchvision之中,同一目錄下還有alexnet,googlenet,vgg的實(shí)現(xiàn)。ResNet代碼共300多行,其中核心代碼不到200行,實(shí)現(xiàn)了三個(gè)主要類(lèi):ResNet、BasicBlock、Bottleneck。

1.殘差是什么,如何實(shí)現(xiàn)?

BasicBlock類(lèi)中計(jì)算了殘差,該類(lèi)繼承了nn.Module(Pytorch基本用法請(qǐng)見(jiàn)參考部分),實(shí)現(xiàn)了兩個(gè)函數(shù):用于創(chuàng)建網(wǎng)絡(luò)結(jié)構(gòu)的init和實(shí)現(xiàn)前向算法的forward。如下所示:

image.png

其中x是輸入,out是輸出,從程序代碼可以看出,與基本流程不同的是,它加入了indentity,而indentity就是輸入x本身(也支持下采樣),也就是說(shuō),在經(jīng)過(guò)多層轉(zhuǎn)換得到的out上加輸入數(shù)據(jù)x,即上面所說(shuō)的 H(X)+X。如果設(shè)輸出Y=H(X)+X,則有H(X)=Y-X,構(gòu)建網(wǎng)絡(luò)H(X)用于求取輸出Y與輸入X的差異,即殘差。而之前的網(wǎng)絡(luò)都是直接求從X到Y(jié)的方法。

2.BasicBlock和Bottleneck

BasicBlock類(lèi)用于構(gòu)建網(wǎng)絡(luò)中的子網(wǎng)絡(luò)結(jié)構(gòu)(后稱(chēng)block),子網(wǎng)絡(luò)中包含兩個(gè)卷積層和殘差處理。一個(gè)ResNet包含多個(gè)BasicBlock子網(wǎng)絡(luò)。因此相對(duì)于傳統(tǒng)網(wǎng)絡(luò),ResNet常被描繪成下圖的結(jié)構(gòu),右側(cè)的弧線(xiàn)是“+X”的操作。

Bottleneck是BasicBlock的升級(jí)版,其功能也是構(gòu)造子網(wǎng)絡(luò),resnet18和resnet34中使用了BasicBlock,而resnet50、resnet101、resnet152使用了Bottlenect構(gòu)造網(wǎng)絡(luò)。

Bottleneck和BasicBlock網(wǎng)絡(luò)結(jié)構(gòu)對(duì)比如下圖所示:

左圖中的BasicBlock包含兩個(gè)3x3的卷積層,右圖的Bottleneck包括了三個(gè)卷積層,第一個(gè)1x1的卷積層用于降維,第二個(gè)3x3層用于處理,第三個(gè)1x1層用于升維,這樣減少了計(jì)算量。

3.主控ResNet類(lèi)

ResNet中最常用的是ResNet50,它兼顧了準(zhǔn)確性和運(yùn)算量。下面以RenNet50作為示例,分析構(gòu)建ResNet的具體方法。

在調(diào)用_resnet創(chuàng)建網(wǎng)絡(luò)時(shí),第二個(gè)參數(shù)指定使用Bottleneck類(lèi)構(gòu)建子網(wǎng)絡(luò),第三個(gè)參數(shù)指定了每一層layer由幾個(gè)子網(wǎng)絡(luò)block構(gòu)成。

下圖是ResNet的初始化部分init中,用于構(gòu)建網(wǎng)絡(luò)結(jié)構(gòu)的代碼(建議在github查看完整代碼)。

可以看到程序用函數(shù)_make_layer創(chuàng)建了四個(gè)層,以resnet50為例,各個(gè)層中block的個(gè)數(shù)依次是3,4,6,3個(gè),而每個(gè)block(Bottleneck)中又包含三個(gè)卷積層,(3+4+6+3)*3共48個(gè)卷積層,外加第141行創(chuàng)建的另一卷積層和第154行創(chuàng)建的一個(gè)全連接層,總共50個(gè)主要層,這也是resnet50中50的含義。

除此以外,上述torchvision程序還提供了下載預(yù)測(cè)訓(xùn)練的模型參數(shù),通過(guò)設(shè)置pretrain=True/False選擇是否使用預(yù)訓(xùn)練的模型。

圖片.png

如此這般,一個(gè)ResNet就實(shí)現(xiàn)完成了。

參考

深度學(xué)習(xí)_卷積神經(jīng)網(wǎng)絡(luò)CNN
http://www.itdecent.cn/p/49aa8f35d03e

Pytorch初探
http://www.itdecent.cn/p/cd72618fe126

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

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