tensorflow2.0(3)-Resnet模型

??tensorflow2不再需要靜態(tài)建圖啟動(dòng)session()琅摩,拋棄很多繁雜的功能設(shè)計(jì)率触,代碼上更加簡(jiǎn)潔清晰豹储,而在工程上也更加靈活。
但是一些基礎(chǔ)的用法坏匪,單靠api接口去訓(xùn)練模型是遠(yuǎn)遠(yuǎn)無(wú)法滿足實(shí)際的應(yīng)用拟逮,基于這種框架,更多還需要自己在其上自定義開(kāi)發(fā)适滓。

例如:model.fit() 雖然能一句代碼把訓(xùn)練跑起來(lái)敦迄,但你根本無(wú)法知道整個(gè)模型內(nèi)部數(shù)據(jù)的變化,也難以去查看某些變量凭迹。我們不可能永遠(yuǎn)停留在MNIST之類的數(shù)據(jù)集上颅崩。

Resnet

??個(gè)人更傾向在實(shí)戰(zhàn)中學(xué)習(xí)深化基礎(chǔ),而不是把基礎(chǔ)理論學(xué)好了再去實(shí)踐蕊苗。本篇基于tf2.0是搭建Resnet網(wǎng)絡(luò)沿后,Resnet有很多變種,也作為很多模型的骨干網(wǎng)絡(luò)朽砰,這次實(shí)戰(zhàn)項(xiàng)目就從它開(kāi)始
需要對(duì)Resnet有一定的認(rèn)知了解尖滚,本文只是代碼實(shí)現(xiàn)

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

?? 官方給出的Resnet網(wǎng)絡(luò)結(jié)構(gòu),分別為18瞧柔,34漆弄,50,101造锅,152層撼唾,可以看出,不同層數(shù)之間總體的結(jié)構(gòu)是一樣的哥蔚,這樣就很方便用類去實(shí)例化每一個(gè)模塊了


image

基礎(chǔ)模塊

??從conv2_x到conv5_x倒谷,18和34layer的結(jié)構(gòu)是一樣的蛛蒙,50,101和152是一樣的渤愁,具體分別為:

image

先定義18or34layer的模塊

# for 18 or 34 layers
class Basic_Block(keras.Model):

    def __init__(self, filters, downsample=False, stride=1):
        self.expasion = 1
        super(Basic_Block, self).__init__()

        self.downsample = downsample

        self.conv2a = keras.layers.Conv2D(filters=filters,
                                          kernel_size=3,
                                          strides=stride,
                                          kernel_initializer='he_normal',
                                          )
        self.bn2a = keras.layers.BatchNormalization(axis=-1)

        self.conv2b = keras.layers.Conv2D(filters=filters,
                                          kernel_size=3,
                                          padding='same',
                                          kernel_initializer='he_normal'
                                          )
        self.bn2b = keras.layers.BatchNormalization(axis=-1)

        self.relu = keras.layers.ReLU()

        if self.downsample:
            self.conv_shortcut = keras.layers.Conv2D(filters=filters,
                                                     kernel_size=1,
                                                     strides=stride,
                                                     kernel_initializer='he_normal',
                                                     )
            self.bn_shortcut = keras.layers.BatchNormalization(axis=-1)

    def call(self, inputs, **kwargs):
        x = self.conv2a(inputs)
        x = self.bn2a(x)
        x = self.relu(x)

        x = self.conv2b(x)
        x = self.bn2b(x)
        x = self.relu(x)

        if self.downsample:
            shortcut = self.conv_shortcut(inputs)
            shortcut = self.bn_shortcut(shortcut)
        else:
            shortcut = inputs

        x = keras.layers.add([x, shortcut])
        x = self.relu(x)

代碼雖然長(zhǎng)了點(diǎn)牵祟,但看一下call() 里面就很清晰了,就是2個(gè) conv+bn+relu抖格,最后與input做點(diǎn)加操作
同理應(yīng)用在50诺苹,101or152layer:

# for 50, 101 or 152 layers
class Block(keras.Model):

    def __init__(self, filters, block_name,
                 downsample=False, stride=1, **kwargs):
        self.expasion = 4
        super(Block, self).__init__(**kwargs)

        conv_name = 'res' + block_name + '_branch'
        bn_name = 'bn' + block_name + '_branch'
        self.downsample = downsample

        self.conv2a = keras.layers.Conv2D(filters=filters,
                                          kernel_size=1,
                                          strides=stride,
                                          kernel_initializer='he_normal',
                                          name=conv_name + '2a')
        self.bn2a = keras.layers.BatchNormalization(axis=3, name=bn_name + '2a')

        self.conv2b = keras.layers.Conv2D(filters=filters,
                                          kernel_size=3,
                                          padding='same',
                                          kernel_initializer='he_normal',
                                          name=conv_name + '2b')
        self.bn2b = keras.layers.BatchNormalization(axis=3, name=bn_name + '2b')

        self.conv2c = keras.layers.Conv2D(filters=4 * filters,
                                          kernel_size=1,
                                          kernel_initializer='he_normal',
                                          name=conv_name + '2c')
        self.bn2c = keras.layers.BatchNormalization(axis=3, name=bn_name + '2c')

        if self.downsample:
            self.conv_shortcut = keras.layers.Conv2D(filters=4 * filters,
                                                     kernel_size=1,
                                                     strides=stride,
                                                     kernel_initializer='he_normal',
                                                     name=conv_name + '1')
            self.bn_shortcut = keras.layers.BatchNormalization(axis=3, name=bn_name + '1')

    def call(self, inputs, **kwargs):
        x = self.conv2a(inputs)
        x = self.bn2a(x)
        x = tf.nn.relu(x)

        x = self.conv2b(x)
        x = self.bn2b(x)
        x = tf.nn.relu(x)

        x = self.conv2c(x)
        x = self.bn2c(x)

        if self.downsample:
            shortcut = self.conv_shortcut(inputs)
            shortcut = self.bn_shortcut(shortcut)
        else:
            shortcut = inputs

        x = keras.layers.add([x, shortcut])
        x = tf.nn.relu(x)

        return x

對(duì)于downsample的操作,如果input和最后一層輸出的chanels不一樣就需要downsample來(lái)保持chanel一致雹拄,這樣才能相加收奔,一般解析resnet的文章都會(huì)提到。
用類封裝了模塊的功能滓玖,接下來(lái)只需要在主體網(wǎng)路結(jié)構(gòu)里添加這個(gè)模塊就好了

主體結(jié)構(gòu)

用subclassing的方式去搭建model坪哄,就像砌墻一樣,一個(gè)模塊一個(gè)模塊拼上去就好了呢撞,先在init()里面定義好需要用到的方法损姜,再在call()把他們調(diào)用起來(lái)。
對(duì)于resnet的主體結(jié)構(gòu)殊霞,先看一下call()里是該如何寫的:

def call(self, inputs, **kwargs):
    x = self.padding(inputs)
    x = self.conv1(x)
    x = self.bn_conv1(x)
    x = tf.nn.relu(x)
    x = self.max_pool(x)

    # layer2
    x = self.res2(x)
    # layer3
    x = self.res3(x)
    # layer4
    x = self.res4(x)
    # layer5
    x = self.res5(x)

    x = self.avgpool(x)
    x = self.fc(x)
    return x

一目了然摧阅,跟文章開(kāi)頭的結(jié)構(gòu)圖一摸一樣,
最重要的是中間conv2-5 的操作绷蹲,這個(gè)需要對(duì)resnet結(jié)構(gòu)熟悉

在Resnet的init()里面棒卷,這樣去定義中間的4個(gè)層

# layer2
self.res2 = self.mid_layer(block, 64, layers[0], stride=1, layer_number=2)

# layer3
self.res3 = self.mid_layer(block, 128, layers[1], stride=2, layer_number=3)

# layer4
self.res4 = self.mid_layer(block, 256, layers[2], stride=2, layer_number=4)

# layer5
self.res5 = self.mid_layer(block, 512, layers[3], stride=2, layer_number=5)

函數(shù)self.mid_layer() 就是把block模塊串起來(lái)

def mid_layer(self, block, filter, block_layers, stride=1, layer_number=1):
    layer = keras.Sequential()
    if stride != 1 or filter * 4 != 64:
        layer.add(block(filters=filter,
                        downsample=True, stride=stride,
                        block_name='{}a'.format(layer_number)))

    for i in range(1, block_layers):
        p = chr(i + ord('a'))
        layer.add(block(filters=filter,
                        block_name='{}'.format(layer_number) + p))

    return layer

到此主體的結(jié)構(gòu)就定義好了,官方源碼Resnet祝钢,是直接從上到下直接編寫的比规,就是一邊構(gòu)建網(wǎng)絡(luò)一邊計(jì)算,類似于這樣

x = input()
x = keras.layers.Conv2D()(x)
x = keras.layers.MaxPooling2D()(X)
x = keras.layers.Dense(num_classes)(x)

??相對(duì)來(lái)說(shuō)更喜歡用subclassing的方式去搭建model,雖然代碼量多了點(diǎn)拦英,但是結(jié)構(gòu)清晰蜒什,自己要中間修改的時(shí)候也很簡(jiǎn)單,也方便別的地方直接調(diào)用疤估,但有一點(diǎn)不好就是灾常,當(dāng)想打印模型model.summary() 的時(shí)候,看不到圖像在各個(gè)操作后的shape铃拇,直接顯示multiple钞瀑,目前不知道有沒(méi)其他的方法。慷荔。

image

代碼

??上述代碼呈現(xiàn)了Resnet的大部分內(nèi)容雕什,可以隨便實(shí)現(xiàn)18-152layer,全部代碼放在了我的github里:https://github.com/angryhen/learning_tensorflow2.0/blob/master/base_model/ResNet.py

??持續(xù)更新中,tensorflow2.0這一系列的代碼也會(huì)放在上面贷岸,包括VGG壹士,Mobilenet的基礎(chǔ)網(wǎng)絡(luò),以后也會(huì)更新引入senet這種變種網(wǎng)絡(luò)凰盔。

Thanks

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末墓卦,一起剝皮案震驚了整個(gè)濱河市倦春,隨后出現(xiàn)的幾起案子户敬,更是在濱河造成了極大的恐慌,老刑警劉巖睁本,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尿庐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呢堰,警方通過(guò)查閱死者的電腦和手機(jī)抄瑟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枉疼,“玉大人皮假,你說(shuō)我怎么就攤上這事÷钗” “怎么了惹资?”我有些...
    開(kāi)封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)航闺。 經(jīng)常有香客問(wèn)我褪测,道長(zhǎng),這世上最難降的妖魔是什么潦刃? 我笑而不...
    開(kāi)封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任侮措,我火速辦了婚禮,結(jié)果婚禮上乖杠,老公的妹妹穿的比我還像新娘分扎。我一直安慰自己,他們只是感情好胧洒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布畏吓。 她就那樣靜靜地躺著,像睡著了一般略荡。 火紅的嫁衣襯著肌膚如雪庵佣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天汛兜,我揣著相機(jī)與錄音巴粪,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肛根,可吹牛的內(nèi)容都是我干的辫塌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼派哲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼臼氨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起芭届,我...
    開(kāi)封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤储矩,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后褂乍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體持隧,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年逃片,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屡拨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡褥实,死狀恐怖呀狼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情损离,我是刑警寧澤哥艇,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站草冈,受9級(jí)特大地震影響她奥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怎棱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一哩俭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拳恋,春花似錦凡资、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至梆暖,卻和暖如春伞访,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轰驳。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工厚掷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弟灼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓冒黑,卻偏偏與公主長(zhǎng)得像田绑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抡爹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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

  • Training spaCy’s Statistical Models訓(xùn)練spaCy模型 This guide d...
    Joe_Gao_89f1閱讀 6,504評(píng)論 1 5
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,092評(píng)論 1 32
  • 朋友問(wèn)我掩驱,你會(huì)一個(gè)人過(guò)日子嗎?我想反問(wèn)你冬竟,你聽(tīng)說(shuō)過(guò)誰(shuí)欧穴,在這世界上,不是孤獨(dú)的生诱咏,不是孤獨(dú)的死苔可?有誰(shuí)缴挖?請(qǐng)你告...
    顧城的詩(shī)閱讀 241評(píng)論 0 0
  • “我曾經(jīng)喜歡過(guò)一個(gè)人袋狞。”這面有天藍(lán)色邊框的四四方方的小鏡子和我說(shuō)映屋」堆欤“那個(gè)人是主人的男朋友,他第一次來(lái)主人的家里的時(shí)...
    洛可可_閱讀 332評(píng)論 0 0
  • 到底要怎樣才能做到既簡(jiǎn)略又全面呢棚点?為了寫出精練而深刻的讀書筆記早处,你應(yīng)該在讀完一本書以后認(rèn)真回想需要摘抄哪一頁(yè)哪一行...
    naughty心閱讀 76評(píng)論 0 0