TensorFLow搭建ResNet一文詳解

閱前須知:

為了使本文結(jié)構(gòu)精簡敌呈,理解簡單神妹,所以會(huì)盡量少涉及到有關(guān)數(shù)學(xué)公式浆兰,降低學(xué)習(xí)門檻磕仅,帶領(lǐng)讀者快速搭建ResNet-152經(jīng)典模型并投入訓(xùn)練。
本文的最后也會(huì)給出相應(yīng)的inference供大家直接使用
如讀者在閱讀時(shí)發(fā)現(xiàn)有錯(cuò)誤的地方歡在評論的地方指出簸呈,共同進(jìn)步
編譯環(huán)境:Python3.6
? ? ? ? ? ? ? ? ? ? TensorFlow-gpu 1.5.0
? ? ? ? ? ? ? ? ? ? Pycharm

一榕订、結(jié)構(gòu)分析

關(guān)于ResNet的來源我就不進(jìn)行贅述了,相信讀者都對其或多或少有一定的了解蜕便,這個(gè)包攬各大圖像識(shí)別賽事冠軍的模型究竟有什么與眾不同劫恒?


圖片來源Google

說起卷積模型,LeNet、Inception两嘴、Vgg都是我們在學(xué)習(xí)圖像識(shí)別領(lǐng)域神經(jīng)網(wǎng)絡(luò)的經(jīng)典模型丛楚,以上圖片模型就是經(jīng)典的Vgg-19與34層傳統(tǒng)卷積網(wǎng)絡(luò)、ResNet-34的對比憔辫。

從計(jì)算量上來講趣些,Vgg-19的三層全連接神經(jīng)網(wǎng)絡(luò)的計(jì)算量明顯大于傳統(tǒng)卷積網(wǎng)絡(luò)和resnet,傳統(tǒng)卷積網(wǎng)絡(luò)和resnet的參數(shù)數(shù)量相同

plain與resnet

從訓(xùn)練擬合度上講贰您,論文中分別給出了plain-18坏平、plain-34和resnet-18、resnet-34的對比枉圃,我們不難發(fā)現(xiàn)plain隨著層數(shù)的增加功茴,精度并沒有得到明顯的提升,而resnet不僅隨著層數(shù)的增加提高了訓(xùn)練精度孽亲,且相較同深度的plain而言精度更高

在以往的學(xué)習(xí)之中坎穿,我們知道深度網(wǎng)絡(luò)隨著層數(shù)的增加,很容易造成“退化”和“梯度消失”的問題返劲,訓(xùn)練數(shù)據(jù)的過擬合玲昧。但在ResNet中,作者給出了一種解決方案:增加一個(gè)identity mapping(恒等映射篮绿,由于本文面向讀者基礎(chǔ)不同孵延,就不加以詳述,有能力的同學(xué)可以看一下ResNet作者的論文)


殘差模塊

上圖是一個(gè)殘差模塊的結(jié)構(gòu)示意亲配,殘差塊想要有效果需要有兩層或兩層以上的layer尘应,同時(shí),輸入x與輸出F(x)的維度也須相同


residual block

在對于高于50層深度的resnet模型中吼虎,為了進(jìn)一步減少計(jì)算量且保證模型精度犬钢,作者對殘差模塊進(jìn)行了優(yōu)化,將內(nèi)部兩層3*3layer換成1*1 → 3*3 → 1*1,思灰。首先采用1*1卷積進(jìn)行深度降維玷犹,減少殘差模塊在深度上的計(jì)算量,第二層3*3layer和之前的模塊功能一樣洒疚,提取圖像特征歹颓,第三層1*1layer用于維度還原。

那么問題又來了油湖,既然已經(jīng)經(jīng)過了3*3卷積巍扛,那輸出維度怎么會(huì)一樣呢?作者在論文中給出了三種解決方案:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? 1乏德、維度不足部分全0填充? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? 2撤奸、輸入輸出維度一致時(shí)使用恒等映射,不一致時(shí)使用線性投影? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? 3、對于所有的block均使用線性投影寂呛。
在本文中,我們對模型主要采用全0填充瘾晃。

好贷痪,以上就是簡單的理論入門,接下來我們開始著手用TensorFlow對理論進(jìn)行代碼實(shí)現(xiàn)

二蹦误、實(shí)現(xiàn)規(guī)劃(ResNet-50-101-152)

我們來選取最具有代表性的152層ResNet來進(jìn)行搭建劫拢,論文的作者就是用152層模型來獲得Imagenet大賽冠軍的。

不同深度的ResNet結(jié)構(gòu)

結(jié)構(gòu)定義字典

在本文中强胰,我們的模型搭建方式是以字典的形式進(jìn)行循環(huán)堆砌

結(jié)構(gòu)字典

ResNet_demo = {? ? "layer_50":[{"depth": 256,"num_class": 3},
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 512,"num_class": 4},? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 1024,"num_class": 6},? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 2048,"num_class": 3}],? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "layer_101": [{"depth": 256, "num_class": 3},
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 512, "num_class": 4},? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 1024, "num_class": 23},? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 2048, "num_class": 3}],? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "layer_152": [{"depth": 256, "num_class": 3},? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 512, "num_class": 8},? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 1024, "num_class": 36},? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {"depth": 2048, "num_class": 3}]

子類模塊規(guī)劃

在ResNet網(wǎng)絡(luò)傳遞的過程中舱沧,我們來探討一些即將遇到的問題:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 1.降采樣過程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.通道填充


降采樣示意

降采樣過程用于不同類瓶頸模塊之間傳遞的過程,例如上圖中粉色卷積層和藍(lán)色卷積層之間的數(shù)據(jù)交互偶洋,藍(lán)色卷積層中的/2就是降采樣處理

降采樣模塊代碼實(shí)現(xiàn)

def sampling(input_tensor,? ? ? #Tensor入口? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ksize = 1,? ? ? ? ? ? ? #采樣塊大小
? ? ? ? ? ? ? ? ? ? ? ? stride = 2):? ? ? ? ? #采樣步長? ?
? ? data = input_tensor? ?
? ? data = slim.max_pool2d(data,ksize,stride = stride)? ?
? ? return data

通道填充用于輸入數(shù)據(jù)x與結(jié)果數(shù)據(jù)F(x)生成殘差和時(shí)造成的通道不匹配問題

通道填充模塊代碼實(shí)現(xiàn)

def depthFilling(input_tensor, #輸入
? ? ? ? ? ? ? ? ? ? ? ? ? ? Tensor? ? ? ? ? ? ? ? depth):? ? ? ? #輸出深度? ?
? ? data = input_tensor? ? #取出輸入tensor的深度? ?
? ? input_depth = data.get_shape().as_list()[3]
? #tf.pad用與維度填充熟吏,不理解的同學(xué)可以去TensoFLow官網(wǎng)了解一下
? ? data = tf.pad(data,[[0,0],? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [0,0],? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [0,0],? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [abs(depth - input_depth)//2,? ? ? ? ? ? ? ? ? ? ? ? abs(depth - input_depth)//2]])? ?
? ? return data

好的,兩個(gè)子類問題已經(jīng)得到解決玄窝,下面來對殘差模塊進(jìn)行規(guī)劃實(shí)現(xiàn)

殘差模塊

因?yàn)榇罱ǚ较蜻x擇layer大于等于50層牵寺,所以我們采用論文中給出的第二種殘差模塊(1*1+3*3+1*1)

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

def bottleneck(input_tensor,output_depth):
? ? #取出通道? ?
? ? redepth = input_tensor.get_shape().as_list()[3]? ?
? # 當(dāng)通道不相符時(shí),進(jìn)行全零填充并降采樣? ?
? ? if output_depth != redepth:
? ? ? #全零填充
? ? ? ? input_tensor = depthFilling(input_tensor,output_depth)? ? ? ?
? ? ? ? #降采樣? ? ? ?
? ? ? ? input_tensor= sampling(input_tensor)
? ? data = input_tensor? ?
? ? #降通道處理? ?
? ? data = slim.conv2d(inputs = data,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? num_outputs = output_depth//4,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? kernel_size = 1,stride = 1)? ?
? #提取特征? ?
? ? data = slim.conv2d(inputs = data,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? num_outputs = output_depth//4,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? kernel_size = 3,stride = 1)
? ? #通道還原? ?
? ? data = slim.conv2d(inputs = data,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? num_outputs = output_depth,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? kernel_size = 1,stride = 1,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? activation_fn=None,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? normalizer_fn=None)? ?
? ? #生成殘差? ?
? ? data = data + input_tensor? ?
? ? data = tf.nn.relu(data)? ?
? ? return data

有了殘差模塊恩脂,我們就可以對網(wǎng)絡(luò)結(jié)構(gòu)進(jìn)行堆砌了

不過帽氓,為了精簡我們的代碼塊,我選擇把全連接層拿出來單獨(dú)寫成一個(gè)模塊

FC代碼實(shí)現(xiàn)

這一模塊沒有什么技術(shù)含量俩块,和我們?nèi)腴T時(shí)的BP神經(jīng)網(wǎng)絡(luò)差不多

def cnn_to_fc(input_tensor,? ? ? ? #Tensor入口? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? num_output,? ? ? ? ? #輸出接口數(shù)量? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? train = False,? ? ? ? #是否使用dropout? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? regularizer = None):? #正則函數(shù)? ?
? ? data = input_tensor? ? #得到輸出信息的維度黎休,用于全連接層的輸入? ?
? ? data_shape = data.get_shape().as_list()? ?
? ? nodes = data_shape[1] * data_shape[2] * data_shape[3]? ?
? ? reshaped = tf.reshape(data, [data_shape[0], nodes])? ?
? ? #最后全連接層? ?
? ? with tf.variable_scope('layer-fc'):? ? ? ?
? ? ? ? fc_weights = tf.get_variable("weight",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [nodes,num_output],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initializer=tf.truncated_normal_initializer(stddev=0.1))? ? ? ?
? ? ? ? if regularizer != None:
? ? ? ? ? ? tf.add_to_collection('losses', regularizer(fc_weights))
? ? ? ? fc_biases = tf.get_variable("bias", [num_output],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initializer=tf.constant_initializer(0.1))? ? ? ?
? ? fc = tf.nn.relu(tf.matmul(reshaped, fc_weights) + fc_biases)? ? ? ?
? ? if train:
? ? ? ? fc = tf.nn.dropout(fc, 0.5)? ?
? ? return fc

定義傳遞規(guī)則

inference

#堆疊ResNet模塊

def inference(input_tensor, #數(shù)據(jù)入口
????????????????????? demos, #模型資料(list)
????????????????????? num_output, #出口數(shù)量
????????????????????? is_train):
??? data = input_tensor #第一層卷積7*7,stride = 2,深度為64
??? data = conv2d_same(data,64,7,2,is_train,None,normalizer_fn = False)
??? data = slim.max_pool2d(data,3,2,scope="pool_1")
??? with tf.variable_scope("resnet"): #堆疊總類瓶頸模塊
??????? demo_num = 0
??????? for demo in demos:
??????????? demo_num += 1
??????????? print("--------------------------------------------") #堆疊子類瓶頸模塊
??????????? for i in range(demo["num_class"]):
??????????????? print(demo_num)
??????????????? if demo_num is not 4:
??????????????????? if i == demo["num_class"] - 1:
??????????????????????? stride = 2
??????????????????? else:
??????????????????????? stride = 1
??????????????? else:
??????????????????? stride = 1
??????????????? data = bottleneck(data,demo["depth"],stride,is_train)
??????????? print("--------------------------------------------")
??? data = tf.layers.batch_normalization(data,training=is_train)
??? data = tf.nn.relu(data) #平均池化,也可用Avg_pool函數(shù)
??? data = tf.reduce_mean(data, [1, 2], keep_dims=True)
??? print("output : ", data) #最后全連接層
??? data = slim.conv2d(data,num_output,1,activation_fn=None)
??? data_shape = data.get_shape().as_list()
??? nodes = data_shape[1] * data_shape[2] * data_shape[3]
??? data = tf.reshape(data, [-1, nodes])
??? return data

inference調(diào)用方式

inference(input_tensor = 數(shù)據(jù)入口
? ? ? ? ? ? ? ? demos = ResNet_demo["layer_101"],? ? ? #獲取模型詞典
? ? ? ? ? ? ? ? num_output = 出口數(shù)量,
? ? ? ? ? ? ? ? is_train = False)? ? # BN是否被訓(xùn)練

參考文獻(xiàn):https://arxiv.org/pdf/1512.03385.pdf

? ? ? ? ? ? ? ? ? http://blog.csdn.net/xxy0118/article/details/78324256

? ? ? ? ? ? ? ??? http://blog.csdn.net/mao_feng/article/details/52734438

注:本文原創(chuàng)玉凯,已于2018年2月2日售與今日頭條頭條主“昂鈦客AI”

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末势腮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子壮啊,更是在濱河造成了極大的恐慌嫉鲸,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歹啼,死亡現(xiàn)場離奇詭異玄渗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狸眼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門藤树,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拓萌,你說我怎么就攤上這事岁钓。” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵屡限,是天一觀的道長品嚣。 經(jīng)常有香客問我,道長钧大,這世上最難降的妖魔是什么翰撑? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮啊央,結(jié)果婚禮上眶诈,老公的妹妹穿的比我還像新娘。我一直安慰自己瓜饥,他們只是感情好逝撬,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乓土,像睡著了一般宪潮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帐我,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天坎炼,我揣著相機(jī)與錄音,去河邊找鬼拦键。 笑死谣光,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芬为。 我是一名探鬼主播萄金,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼媚朦!你這毒婦竟也來了氧敢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤询张,失蹤者是張志新(化名)和其女友劉穎孙乖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體份氧,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯袄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜗帜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恋拷。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厅缺,靈堂內(nèi)的尸體忽然破棺而出蔬顾,到底是詐尸還是另有隱情宴偿,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布诀豁,位于F島的核電站窄刘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舷胜。R本人自食惡果不足惜都哭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逞带。 院中可真熱鬧,春花似錦纱新、人聲如沸展氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遇汞。三九已至,卻和暖如春簿废,著一層夾襖步出監(jiān)牢的瞬間空入,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工族檬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歪赢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓单料,卻偏偏與公主長得像埋凯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子扫尖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

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