來(lái)親自手搭一個(gè)ResNet18網(wǎng)絡(luò)

在何大佬的文章中承耿,提出了下面兩種殘差塊:

殘差塊

左邊的稱為building block墓捻,右邊的稱為bottleneck building block李请。

  • 左邊的輸入和輸出都是64個(gè)channel的藤抡,四四方方的,像個(gè)建筑物硝枉;
  • 右邊的就好像通過(guò)了一個(gè)瓶頸一樣廉丽,輸入殘差塊的網(wǎng)絡(luò)通道數(shù)會(huì)先從256變成64,然后最終再升到256妻味,其中降維和升維使用的是1x1的卷積正压,可以減少參數(shù)量;

代碼

Building Block弧可,就是之前寫(xiě)的(換了激活函數(shù)):

class ResidualBlock(nn.Module):
    def __init__(self, in_channels):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(in_channels)
        self.relu = nn.ReLU()
        self.conv2 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(in_channels)

    def forward(self, x):
        residual = self.conv1(x)
        residual = self.bn1(residual)
        residual = self.relu(residual)
        residual = self.conv2(residual)
        residual = self.bn2(residual)
        
        out = self.relu(x + residual)
        return out

BottleNeck的代碼稍微改下就行:

class BottleNeck(nn.Module):
    def __init__(self,in_channels):
        super(BottleNeck, self).__init__()

        self.main = nn.Sequential(
            nn.Conv2d(in_channels,64,kernel_size=1,stride=1,padding=0),
            nn.BatchNorm2d(64),
            nn.ReLU(),

            nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),

            nn.Conv2d(63,in_channels,kernel_size=1,stride=1,padding=0),
            nn.BatchNorm2d(in_channels),
        )
        self.shortcut = nn.Sequential()

    def forward(self,x):
        shortcut = self.shortcut(x) 
        residual = self.main(x) 

        out = nn.ReLU(shortcut + residual )

        return out 

幾點(diǎn)經(jīng)驗(yàn)

  1. 在網(wǎng)絡(luò)中蔑匣,如果層數(shù)比較多的時(shí)候盡可能使用容器來(lái)寫(xiě)(比如上面的Sequential)劣欢,這樣子看起來(lái)更加的清晰;
  2. 在殘差塊的最后加上輸入之后記得要加上激活函數(shù)裁良;
  3. 要有積木思想凿将,就是盡可能的把網(wǎng)絡(luò)中的結(jié)構(gòu)搭建為可復(fù)用的,就比如上面的殘差塊价脾;
  4. BottleNeck用在較深的網(wǎng)絡(luò)層中可以減少參數(shù)量牧抵;

ResNet18

在何大佬的文章中提出了幾種不同的殘差網(wǎng)絡(luò),主要是網(wǎng)絡(luò)層的不同侨把,最少的為18層:

幾種殘差網(wǎng)絡(luò)

我們根據(jù)上面的信息復(fù)現(xiàn)一下ResNet18犀变。先分析其結(jié)構(gòu):

  1. 原文中用的圖像輸入是3*224*224,先通過(guò)一個(gè)7*7*64的卷積秋柄,但是步長(zhǎng)設(shè)置為2获枝,使得圖像的大小縮小了一半;
  2. 在con2_x的剛開(kāi)始骇笔,通過(guò)一個(gè)最大值池化省店,步長(zhǎng)設(shè)置為2,使得圖像又縮小了一半笨触;
  3. 然后是con2_x懦傍、con3_x、con4_x芦劣、con5_x一共8個(gè)殘差塊粗俱;
  4. 按照作者說(shuō)的,在con3_1虚吟、con4_1寸认、con5_1都進(jìn)行了2倍的下采樣;
  5. 最后一層先經(jīng)過(guò)一個(gè)自適應(yīng)平均池化層稍味,然后一個(gè)全連接層映射到輸出废麻;

那么根據(jù)上面的過(guò)程寫(xiě)代碼即可,但是寫(xiě)之前有幾點(diǎn)需要注意:

  • 原文章中說(shuō)了每個(gè)卷積層后面跟上一個(gè)批歸一化層(BN層)模庐;
  • 特征圖的尺寸減半的時(shí)候,特征圖的數(shù)量要增加一倍油宜;
  • 原文說(shuō)的是直接用的步長(zhǎng)為2的卷積層進(jìn)行下采樣掂碱;

整體代碼:

class ResidualBlock(nn.Module):
    def __init__(self, in_channels):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(in_channels)
        self.relu = nn.ReLU()
        self.conv2 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(in_channels)

    def forward(self, x):
        residual = self.conv1(x)
        residual = self.bn1(residual)
        residual = self.relu(residual)
        residual = self.conv2(residual)
        residual = self.bn2(residual)
        
        out = self.relu(x + residual)
        return out

class ResNet18(nn.Module):
    def __init__(self,in_channels,resblock,outputs=1000):
        super(ResNet18, self).__init__()

        self.block1 = nn.Sequential(
            nn.Conv2d(in_channels,64,kernel_size=7,stride=2,padding=3),
            nn.BatchNorm2d(64),
            nn.ReLU(),
        )
        self.block2 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3,stride=2,padding=1),
            resblock(in_channels=64),
            resblock(in_channels=64)
        )
        self.block3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,stride=(2,2),padding=1),
            resblock(in_channels=128),
            resblock(in_channels=128),
        )
        self.block4 = nn.Sequential(
            nn.Conv2d(128,256,kernel_size=3,stride=(2,2),padding=1),
            resblock(in_channels=256),
            resblock(in_channels=256),
        )
        self.block5 = nn.Sequential(
            nn.Conv2d(256,512,kernel_size=3,stride=(2,2),padding=1),
            resblock(in_channels=512),
            resblock(in_channels=512),
        )
        self.block6 = nn.AdaptiveAvgPool2d(output_size=(1,1))
        self.fc = nn.Linear(in_features=512,out_features=outputs)
        
    def forward(self, x):
        x = self.block1(x)
        
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)

        x = self.block6(x)
        x = x.reshape(x.shape[0],-1)
        x = self.fc(x)

        return x 

為什么叫做ResNet18?

如果打印出來(lái)看下(用之前說(shuō)的torchsummary)慎冤,可以發(fā)現(xiàn)其中帶有可學(xué)習(xí)參數(shù)的層數(shù)一共是18層疼燥,所以叫做ResNet18(除去那些BN層、激活函數(shù)層等)蚁堤。

小總結(jié)與注意項(xiàng)

  1. 上面的代碼我是嚴(yán)格按照參考【1】的論文進(jìn)行復(fù)現(xiàn)的醉者,可能跟網(wǎng)上的有些不一樣,比如跟參考【3】的就不太一樣;
  2. 網(wǎng)上各種ResNet18的復(fù)現(xiàn)撬即,但是也有一些跟論文不太一樣的地方立磁,比如參考【4】在最初的卷積層之后就沒(méi)有加激活層;
  3. 還有其他的一些網(wǎng)上的教程也是各不相同剥槐,但是都是大同小異唱歧,所以在使用的時(shí)候要自己仔細(xì)斟酌;
  4. 上面的復(fù)現(xiàn)跟Pytorch官方提供的基本一致粒竖,但是參數(shù)量有些不同颅崩,后面就沒(méi)有細(xì)細(xì)比較了;
  5. 使用別人的代碼的時(shí)候一定要先讀懂了原理蕊苗,不要無(wú)腦直接套用沿后,血的教訓(xùn);
  6. 酌情根據(jù)自己的需求修改其中可以修改的模塊(比如激活函數(shù)朽砰,卷積核的大小等)尖滚。

參考

【1】HE K, ZHANG X, REN S, et al. Deep Residual Learning for Image Recognition[C]//2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR).2016:770-778. 10.1109/CVPR.2016.90.
【2】https://blog.csdn.net/sazass/article/details/116864275
【3】https://towardsdev.com/implement-resnet-with-pytorch-a9fb40a77448
【4】https://blog.csdn.net/weixin_36979214/article/details/108879684?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162374909216780265420718%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162374909216780265420718&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-108879684.first_rank_v2_pc_rank_v29&utm_term=pytorch+resnet18&spm=1018.2226.3001.4187

本文由mdnice多平臺(tái)發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锅移,隨后出現(xiàn)的幾起案子熔掺,更是在濱河造成了極大的恐慌,老刑警劉巖非剃,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件置逻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡备绽,警方通過(guò)查閱死者的電腦和手機(jī)券坞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肺素,“玉大人恨锚,你說(shuō)我怎么就攤上這事”睹遥” “怎么了猴伶?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)塌西。 經(jīng)常有香客問(wèn)我他挎,道長(zhǎng),這世上最難降的妖魔是什么捡需? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任办桨,我火速辦了婚禮,結(jié)果婚禮上站辉,老公的妹妹穿的比我還像新娘呢撞。我一直安慰自己损姜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布殊霞。 她就那樣靜靜地躺著摧阅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脓鹃。 梳的紋絲不亂的頭發(fā)上逸尖,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音瘸右,去河邊找鬼娇跟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛太颤,可吹牛的內(nèi)容都是我干的苞俘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼龄章,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吃谣!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起做裙,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岗憋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后锚贱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仔戈,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伐蒂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年倒脓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了播演。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片困后。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖睦刃,靈堂內(nèi)的尸體忽然破棺而出搬俊,到底是詐尸還是另有隱情掷贾,我是刑警寧澤倦春,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布户敬,位于F島的核電站,受9級(jí)特大地震影響睁本,放射性物質(zhì)發(fā)生泄漏山叮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一添履、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脑又,春花似錦暮胧、人聲如沸锐借。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)钞翔。三九已至,卻和暖如春席舍,著一層夾襖步出監(jiān)牢的瞬間布轿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工来颤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汰扭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓福铅,卻偏偏與公主長(zhǎng)得像萝毛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滑黔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容