ResNet在分别在Keras和tensorflow框架下的应用案例
一、殘差神經(jīng)網(wǎng)絡(luò)——ResNet的綜述
深度學(xué)習(xí)網(wǎng)絡(luò)的深度對(duì)最后的分類和識(shí)別的效果有著很大的影響,所以正常想法就是能把網(wǎng)絡(luò)設(shè)計(jì)的越深越好,
但是事實(shí)上卻不是這樣,常規(guī)的網(wǎng)絡(luò)的堆疊(plain network)在網(wǎng)絡(luò)很深的時(shí)候,效果卻越來(lái)越差了。其中原因之一
即是網(wǎng)絡(luò)越深,梯度消失的現(xiàn)象就越來(lái)越明顯,網(wǎng)絡(luò)的訓(xùn)練效果也不會(huì)很好。?但是現(xiàn)在淺層的網(wǎng)絡(luò)(shallower network)
又無(wú)法明顯提升網(wǎng)絡(luò)的識(shí)別效果了,所以現(xiàn)在要解決的問(wèn)題就是怎樣在加深網(wǎng)絡(luò)的情況下又解決梯度消失的問(wèn)題。
(二)殘差模塊——Residual bloack
?
通過(guò)在一個(gè)淺層網(wǎng)絡(luò)基礎(chǔ)上疊加 y=x 的層(稱identity mappings,恒等映射),可以讓網(wǎng)絡(luò)隨深度增加而不退化。
這反映了多層非線性網(wǎng)絡(luò)無(wú)法逼近恒等映射網(wǎng)絡(luò)。但是,不退化不是我們的目的,我們希望有更好性能的網(wǎng)絡(luò)。
?resnet學(xué)習(xí)的是殘差函數(shù)F(x) = H(x) - x, 這里如果F(x) = 0, 那么就是上面提到的恒等映射。事實(shí)上,
resnet是“shortcut connections”的在connections是在恒等映射下的特殊情況,它沒(méi)有引入額外的參數(shù)和計(jì)算復(fù)雜度。
假如優(yōu)化目標(biāo)函數(shù)是逼近一個(gè)恒等映射, 而不是0映射, 那么學(xué)習(xí)找到對(duì)恒等映射的擾動(dòng)會(huì)比重新學(xué)習(xí)一個(gè)映射函數(shù)要容易。
殘差函數(shù)一般會(huì)有較小的響應(yīng)波動(dòng),表明恒等映射是一個(gè)合理的預(yù)處理。
殘差模塊小結(jié):
非常深的網(wǎng)絡(luò)很難訓(xùn)練,存在梯度消失和梯度爆炸問(wèn)題,學(xué)習(xí) skip connection它可以從某一層獲得激活,然后迅速反饋給另外一層甚至更深層,利用?skip connection可以構(gòu)建殘差網(wǎng)絡(luò)ResNet來(lái)訓(xùn)練更深的網(wǎng)絡(luò),ResNet網(wǎng)絡(luò)是由殘差模塊構(gòu)建的。
上圖中,是一個(gè)兩層的神經(jīng)網(wǎng)絡(luò),在l層進(jìn)行激活操作,得到a[l+1],再次進(jìn)行激活得到a[l+2]。由下面公式:
a[l+2] 加上了 a[l]的殘差塊,即:殘差網(wǎng)絡(luò)中,直接將a[l]向后拷貝到神經(jīng)網(wǎng)絡(luò)的更深層,在ReLU非線性激活前面
加上a[l],a[l]的信息直接達(dá)到網(wǎng)絡(luò)深層。使用殘差塊能夠訓(xùn)練更深層的網(wǎng)絡(luò),構(gòu)建一個(gè)ResNet網(wǎng)絡(luò)就是通過(guò)將很多
這樣的殘差塊堆積在一起,形成一個(gè)深度神經(jīng)網(wǎng)絡(luò)。
?
(三)殘差網(wǎng)絡(luò)——ResNet
上圖中是用5個(gè)殘差塊連接在一起構(gòu)成的殘差網(wǎng)絡(luò),用梯度下降算法訓(xùn)練一個(gè)神經(jīng)網(wǎng)絡(luò),若沒(méi)有殘差,會(huì)發(fā)現(xiàn)
隨著網(wǎng)絡(luò)加深,訓(xùn)練誤差先減少后增加,理論上訓(xùn)練誤差越來(lái)越小比較好。而對(duì)于殘差網(wǎng)絡(luò)來(lái)講,隨著層數(shù)增加,
訓(xùn)練誤差越來(lái)越減小,這種方式能夠到達(dá)網(wǎng)絡(luò)更深層,有助于解決梯度消失和梯度爆炸的問(wèn)題,讓我們訓(xùn)練更深網(wǎng)絡(luò)
同時(shí)又能保證良好的性能。
殘差網(wǎng)絡(luò)有很好表現(xiàn)的原因舉例:
?
假設(shè)有一個(gè)很大的神經(jīng)網(wǎng)絡(luò),輸入矩陣為X,輸出激活值為a[l],加入給這個(gè)網(wǎng)絡(luò)額外增加兩層,最終輸出結(jié)果為a[l+2],
可以把這兩層看做一個(gè)殘差模塊,在整個(gè)網(wǎng)絡(luò)中使用ReLU激活函數(shù),所有的激活值都大于等于0。
對(duì)于大型的網(wǎng)絡(luò),無(wú)論把殘差塊添加到神經(jīng)網(wǎng)絡(luò)的中間還是末端,都不會(huì)影響網(wǎng)絡(luò)的表現(xiàn)。
殘差網(wǎng)絡(luò)起作用的主要原因是:It's so easy for these extra layers to learn the itentity function.?
這些殘差塊學(xué)習(xí)恒等函數(shù)非常容易。可以確定網(wǎng)絡(luò)性能不受影響,很多時(shí)候甚至可以提高學(xué)習(xí)效率。
?
模型構(gòu)建好后進(jìn)行實(shí)驗(yàn),在plain上觀測(cè)到明顯的退化現(xiàn)象,而且ResNet上不僅沒(méi)有退化,34層網(wǎng)絡(luò)的效果反而比18層的更好,而且不僅如此,ResNet的收斂速度比plain的要快得多。
實(shí)際中,考慮計(jì)算的成本,對(duì)殘差塊做了計(jì)算優(yōu)化,即將兩個(gè)3x3的卷積層替換為1x1 + 3x3 + 1x1, 如下圖。新結(jié)構(gòu)中的中間3x3的卷積層首先在一個(gè)降維1x1卷積層下減少了計(jì)算,然后在另一個(gè)1x1的卷積層下做了還原,既保持了精度又減少了計(jì)算量。
?
這相當(dāng)于對(duì)于相同數(shù)量的層又減少了參數(shù)量,因此可以拓展成更深的模型。于是作者提出了50、101、152層的ResNet,而且不僅沒(méi)有出現(xiàn)退化問(wèn)題,錯(cuò)誤率也大大降低,同時(shí)計(jì)算復(fù)雜度也保持在很低的程度。
這個(gè)時(shí)候ResNet的錯(cuò)誤率已經(jīng)把其他網(wǎng)絡(luò)落下幾條街了,但是似乎還并不滿足,于是又搭建了更加變態(tài)的1202層的網(wǎng)絡(luò),對(duì)于這么深的網(wǎng)絡(luò),優(yōu)化依然并不困難,但是出現(xiàn)了過(guò)擬合的問(wèn)題,這是很正常的,作者也說(shuō)了以后會(huì)對(duì)這個(gè)1202層的模型進(jìn)行進(jìn)一步的改進(jìn)。
?
二、ResNet在Keras框架下的實(shí)現(xiàn)
(一)概況
?利用殘差神經(jīng)網(wǎng)絡(luò)ResNet構(gòu)建一個(gè)很深度的卷積神經(jīng)網(wǎng)絡(luò)。理論上,很深的神經(jīng)網(wǎng)絡(luò)代表著復(fù)雜的函數(shù),實(shí)際上它們很難訓(xùn)練,而ResNet比其他很深的神經(jīng)網(wǎng)絡(luò)訓(xùn)練起來(lái)更加實(shí)際可行。
1、構(gòu)建基本的ResNet模塊。
2、把這些ResNet模塊集成在一起,實(shí)現(xiàn)和訓(xùn)練一個(gè)用于圖像分類的最先進(jìn)的神經(jīng)網(wǎng)絡(luò)。
3、該ResNet網(wǎng)絡(luò)在深度學(xué)習(xí)框架?Keras下構(gòu)建完成。
?近年來(lái),神經(jīng)網(wǎng)絡(luò)已變得更深,與最先進(jìn)的網(wǎng)絡(luò)從幾層(如AlexNet)到一百層。一個(gè)非常深的網(wǎng)絡(luò)的主要好處是:它可以代表非常復(fù)雜的函數(shù)。它還可以從許多不同層次的抽象中學(xué)習(xí)特征,從邊緣(在較低的層)到非常復(fù)雜的特性(在更深的層次)。然而,使用更深層的網(wǎng)絡(luò)并不總是有用的。訓(xùn)練它們巨大的障礙就是梯度消失:很深的網(wǎng)絡(luò)往往梯度信號(hào)變?yōu)榱愕乃俣瓤?#xff0c;從而使梯度下降法非常地慢。更特別的是,在梯度下降,當(dāng)你從最后一層BackProp回到第一層,每一步乘以權(quán)重矩陣,從而梯度以指數(shù)方式迅速減為為零(或者,在罕見(jiàn)的情況下,成倍的增長(zhǎng),迅速“爆炸”為非常大的值)。
在訓(xùn)練過(guò)程中,可能會(huì)看到前面層的梯度的量級(jí)或標(biāo)準(zhǔn)減小到零的速度很快,如下圖所示:
(二)、構(gòu)建殘差塊
在ResNet中,"shortcut" 或 "skip connection" 使得梯度反向傳播到更前面的層:下圖中左邊的圖片展示了神經(jīng)網(wǎng)絡(luò)的主路徑“main path”,右側(cè)的圖片為“main path”添加了一個(gè)“short cut”,通過(guò)堆疊這些ResNet模塊,可以構(gòu)建很深的神經(jīng)網(wǎng)絡(luò)。
?帶有“shortcut”的ResNet模塊,使得它對(duì)每一個(gè)模塊很容易學(xué)習(xí)到恒等函數(shù)“identy function”,這意味著你可以疊加額外ResNet模塊而對(duì)訓(xùn)練集的性能產(chǎn)生風(fēng)險(xiǎn)小的危害。還有一些證據(jù)表明,學(xué)習(xí)恒等函數(shù)“identy function”?甚至比"skip connection" 對(duì)于梯度消失問(wèn)題更有幫助,且更能說(shuō)明ResNets表現(xiàn)出色。在ResNet中使用兩種主要類型的模塊,主要取決于輸入/輸出尺寸是相同還是不同。
1、恒等殘差塊——The identity block
?The identity block是ResNets中使用的標(biāo)準(zhǔn)塊,對(duì)應(yīng)于輸入激活(例如 a [1])與輸出激活具有相同維度(例如a [l +2])。
(1)、兩層恒等殘差塊
下圖展示了ResNets的兩層的恒等殘差塊?identity block:
上面的路徑是“shortcut path”,下面的路徑是“main path”,在這個(gè)圖中,同樣也進(jìn)行了CONV2D和ReLU操作,為了加速訓(xùn)練的速度也加入了Batch正則化“Batch Norm”,Batch Norm在Keras框架中就一句代碼。
(2)、三層恒等殘差塊
在下面的這個(gè)例子中,將實(shí)際上實(shí)現(xiàn)一個(gè)略微更強(qiáng)大的版本,這個(gè)跳轉(zhuǎn)連接“跳過(guò)”3個(gè)隱藏層而不是2層。
(3)、下面是細(xì)節(jié)的步驟:
a、主路徑的第一部分:
第一個(gè)CONV2D的過(guò)濾器F1大小是(1*1),步長(zhǎng)s大小為(1*1),卷積填充padding="valid"即無(wú)填充卷積。
將這一部分命名為conv_name_base+'2a',初始化時(shí)利用種子為seed=0。
第一個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'2a'.
應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
b、主路徑的第二部分:
第二個(gè)CONV2D的過(guò)濾器F2大小是(f*f),步長(zhǎng)s大小為(1*1),卷積填充padding="same"即相同卷積。
將這一部分命名為conv_name_base'2b',初始化時(shí)利用種子為seed=0。
第二個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'2b'.
應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
c、主路徑的第三部分:
?
第三個(gè)CONV2D的過(guò)濾器F3大小是(1*1),步長(zhǎng)s大小為(1*1),卷積填充padding="valid"即無(wú)填充卷積。
將這一部分命名為conv_name_base+'2c',初始化時(shí)利用種子為seed=0。
第三個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base +'2c'.
這一部分中沒(méi)有應(yīng)用ReLU激活函數(shù)。
d、最后部分:
將shortcut和輸入一起添加到模塊中,然后再應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
#卷積殘差塊——convolutional_block def convolutional_block(X,f,filters,stage,block,s=2):"""param :X -- 輸入的張量,維度為(m, n_H_prev, n_W_prev, n_C_prev)f -- 整數(shù),指定主路徑的中間 CONV 窗口的形狀(過(guò)濾器大小)filters -- python整數(shù)列表,定義主路徑的CONV層中的過(guò)濾器stage -- 整數(shù),用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置block --字符串/字符,用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置s -- 整數(shù),指定使用的步幅return:X -- 卷積殘差塊的輸出,維度為:(n_H, n_W, n_C)"""# 定義基本的名字conv_name_base = "res" + str(stage) + block + "_branch"bn_name_base = "bn" + str(stage) + block + "_branch"# 過(guò)濾器F1, F2, F3 = filters# 保存輸入值,后面將需要添加回主路徑X_shortcut = X# 主路徑第一部分X = Conv2D(filters=F1, kernel_size=(1, 1), strides=(s, s), padding="valid",name=conv_name_base + "2a", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2a")(X)X = Activation("relu")(X)# 主路徑第二部分X = Conv2D(filters=F2, kernel_size=(f, f), strides=(1, 1), padding="same",name=conv_name_base + "2b", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2b")(X)X = Activation("relu")(X)# 主路徑第三部分X = Conv2D(filters=F3, kernel_size=(1, 1), strides=(1, 1), padding="valid",name=conv_name_base + "2c", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2c")(X)#shortcut路徑X_shortcut = Conv2D(filters=F3,kernel_size=(1,1),strides=(s,s),padding="valid",name=conv_name_base+"1",kernel_initializer=glorot_uniform(seed=0))(X_shortcut)X_shortcut = BatchNormalization(axis=3,name=bn_name_base+"1")(X_shortcut)# 主路徑最后部分,為主路徑添加shortcut并通過(guò)relu激活X = layers.add([X, X_shortcut])X = Activation("relu")(X)return Xtf.reset_default_graph()with tf.Session() as test:np.random.seed(1)A_prev = tf.placeholder("float", [3, 4, 4, 6])X = np.random.randn(3, 4, 4, 6)A = convolutional_block(A_prev, f = 2, filters = [2, 4, 6], stage = 1, block = 'a')test.run(tf.global_variables_initializer())out = test.run([A], feed_dict={A_prev: X, K.learning_phase(): 0})print("out = " + str(out[0][1][1][0])) #運(yùn)行結(jié)果: out = [0.09018463 1.2348977 0.46822017 0.0367176 0. 0.65516603]運(yùn)行結(jié)果:
out = [0.19716813 0. 1.3561227 2.1713073 0. 1.3324987 ]
2、卷積殘差塊——The convolutional block
(1)、綜述
ResNet的convolutional_block是另一種類型的殘差塊,當(dāng)輸入和輸出尺寸不匹配時(shí),可以使用這種類型的塊。
與identity block恒等殘差塊不同的地方是:在shortcut路徑中是一個(gè)CONV2D的層。
shortcut路徑中的CONV2D層用于將輸入x調(diào)整為不同的尺寸,以便在添加shortcut殘差塊的值返回到主路徑時(shí)需要最后添加的尺寸相匹配(與矩陣Ws的作用相同)。例如,要將激活值維度的高度和寬度縮小2倍,可以使用步長(zhǎng)為2的1x1卷積。shortcut路徑上的CONV2D層路徑不使用任何非線性激活函數(shù)。 它的主要作用是只應(yīng)用一個(gè)(學(xué)習(xí)的)線性函數(shù)來(lái)減小輸入的尺寸,使得尺寸匹配后面的添加步驟。
(2)、convolutional_block的細(xì)節(jié)步驟
a、主路徑第一部分
第一個(gè)CONV2D的過(guò)濾器F1大小是(1*1),步長(zhǎng)s大小為(s*s),卷積填充padding="valid"即無(wú)填充卷積。
將這一部分命名為conv_name_base+'2a'。
第一個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'2a'.
應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
b、主路徑第二部分
第二個(gè)CONV2D的過(guò)濾器F2大小是(f*f),步長(zhǎng)s大小為(1*1),卷積填充padding="same"即相同卷積。
將這一部分命名為conv_name_base+'2b',初始化時(shí)利用種子為seed=0。
第二個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'2b'.
?應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
c、主路徑第三部分
第三個(gè)CONV2D的過(guò)濾器 F3大小是(1*1),步長(zhǎng)s大小為(1*1),卷積填充padding="valid"即無(wú)填充卷積。
將這一部分命名為 conv_name_base+'2c',初始化時(shí)利用種子為seed=0。
第三個(gè)BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'2c'.
這一部分中沒(méi)有應(yīng)用ReLU激活函數(shù)。
d、shortcut 路徑
該部分CONV2D的過(guò)濾器 F3大小是(1*1),步長(zhǎng)s大小為(s*s),卷積填充 padding="valid"即無(wú)填充卷積。
將這一部分命名為conv_name_base+'1'。
該部分BatchNorm是在通道/厚度的軸上進(jìn)行標(biāo)準(zhǔn)化,并命名為bn_name_base+'1'?
e、最后部分
將shortcut和輸入一起添加到模塊中,然后再應(yīng)用ReLU激活函數(shù),這里沒(méi)有超參數(shù)。
#卷積殘差塊——convolutional_blockdef convolutional_block(X,f,filters,stage,block,s=2):"""param :X -- 輸入的張量,維度為(m, n_H_prev, n_W_prev, n_C_prev)f -- 整數(shù),指定主路徑的中間 CONV 窗口的形狀(過(guò)濾器大小)filters -- python整數(shù)列表,定義主路徑的CONV層中的過(guò)濾器stage -- 整數(shù),用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置block --字符串/字符,用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置s -- 整數(shù),指定使用的步幅return:X -- 卷積殘差塊的輸出,維度為:(n_H, n_W, n_C)"""# 定義基本的名字conv_name_base = "res" + str(stage) + block + "_branch"bn_name_base = "bn" + str(stage) + block + "_branch"# 過(guò)濾器F1, F2, F3 = filters# 保存輸入值,后面將需要添加回主路徑X_shortcut = X# 主路徑第一部分X = Conv2D(filters=F1, kernel_size=(1, 1), strides=(s, s), padding="valid",name=conv_name_base + "2a", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2a")(X)X = Activation("relu")(X)# 主路徑第二部分X = Conv2D(filters=F2, kernel_size=(f, f), strides=(1, 1), padding="same",name=conv_name_base + "2b", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2b")(X)X = Activation("relu")(X)# 主路徑第三部分X = Conv2D(filters=F3, kernel_size=(1, 1), strides=(1, 1), padding="valid",name=conv_name_base + "2c", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2c")(X)#shortcut路徑X_shortcut = Conv2D(filters=F3,kernel_size=(1,1),strides=(s,s),padding="valid",name=conv_name_base+"1",kernel_initializer=glorot_uniform(seed=0))(X_shortcut)X_shortcut = BatchNormalization(axis=3,name=bn_name_base+"1")(X_shortcut)# 主路徑最后部分,為主路徑添加shortcut并通過(guò)relu激活X = layers.add([X, X_shortcut])X = Activation("relu")(X)return Xtf.reset_default_graph()with tf.Session() as test:np.random.seed(1)A_prev = tf.placeholder("float", [3, 4, 4, 6])X = np.random.randn(3, 4, 4, 6)A = convolutional_block(A_prev, f = 2, filters = [2, 4, 6], stage = 1, block = 'a')test.run(tf.global_variables_initializer())out = test.run([A], feed_dict={A_prev: X, K.learning_phase(): 0})print("out = " + str(out[0][1][1][0]))運(yùn)行結(jié)果:
out = [0.09018463 1.2348977 0.46822017 0.0367176 0. 0.65516603]
三、在Keras框架下構(gòu)建50層的殘差卷積神經(jīng)網(wǎng)絡(luò)ResNet
?
50層的殘差卷積神經(jīng)網(wǎng)絡(luò)ResNet的細(xì)節(jié)部分:
0、為輸入224*224*3填充大小為(3*3)的zero_padding。
1、第一階段,輸出大小為:56*56*64
2、第二階段,輸出大小為:56*56*256
3、第三階段,輸出大小為:28*28*512
4、第四階段,輸出大小為:14*14*1024
?5、第五階段,輸出大小為:7*7*2048
6、最后階段,輸出6個(gè)類別
注意:
(1)正如在Keras教程中所看到的,在事先訓(xùn)練模型之前,需要通過(guò)編譯模型來(lái)配置學(xué)習(xí)過(guò)程。
(2)模型訓(xùn)練20次epches,批樣本數(shù)目batch_size = 32,在單個(gè)CPU上運(yùn)行大概每個(gè)epoch需5分鐘。
(3)當(dāng)訓(xùn)練足夠數(shù)量的迭代時(shí),ResNet50是一個(gè)強(qiáng)大的圖像分類模型。
#!/usr/bin/env python # -*- coding:utf-8 -*-#構(gòu)建50層的ResNet神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)圖像的分類 #深度學(xué)習(xí)框架 Keras中實(shí)現(xiàn)import numpy as np import tensorflow as tf from keras import layers from keras.layers import Input,Add,Dense,Activation,ZeroPadding2D,\BatchNormalization,Flatten,Conv2D,AveragePooling2D,MaxPooling2D,GlobalMaxPooling2D from keras.models import Model,load_model from keras.preprocessing import image from keras.utils import layer_utils from keras.utils.data_utils import get_file from keras.applications.imagenet_utils import preprocess_input from keras.utils.vis_utils import model_to_dot from keras.utils import plot_model from keras.initializers import glorot_uniform import pydoc from IPython.display import SVG from resnets_utils import * import scipy.misc from matplotlib.pyplot import imshowimport keras.backend as K K.set_image_data_format("channels_last") K.set_learning_phase(1)#恒等模塊——identity_block def identity_block(X,f,filters,stage,block):"""三層的恒等殘差塊param :X -- 輸入的張量,維度為(m, n_H_prev, n_W_prev, n_C_prev)f -- 整數(shù),指定主路徑的中間 CONV 窗口的形狀filters -- python整數(shù)列表,定義主路徑的CONV層中的過(guò)濾器數(shù)目stage -- 整數(shù),用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置block --字符串/字符,用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置return:X -- 三層的恒等殘差塊的輸出,維度為:(n_H, n_W, n_C)"""#定義基本的名字conv_name_base = "res"+str(stage)+block+"_branch"bn_name_base = "bn"+str(stage)+block+"_branch"#過(guò)濾器F1,F2,F3 = filters#保存輸入值,后面將需要添加回主路徑X_shortcut = X#主路徑第一部分X = Conv2D(filters=F1,kernel_size=(1,1),strides=(1,1),padding="valid",name=conv_name_base+"2a",kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3,name=bn_name_base+"2a")(X)X = Activation("relu")(X)# 主路徑第二部分X = Conv2D(filters=F2,kernel_size=(f,f),strides=(1,1),padding="same",name=conv_name_base+"2b",kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3,name=bn_name_base+"2b")(X)X = Activation("relu")(X)# 主路徑第三部分X = Conv2D(filters=F3,kernel_size=(1,1),strides=(1,1),padding="valid",name=conv_name_base+"2c",kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3,name=bn_name_base+"2c")(X)# 主路徑最后部分,為主路徑添加shortcut并通過(guò)relu激活X = layers.add([X,X_shortcut])X = Activation("relu")(X)return Xtf.reset_default_graph() with tf.Session() as sess:np.random.seed(1)A_prev = tf.placeholder("float",shape=[3,4,4,6])X = np.random.randn(3,4,4,6)A = identity_block(A_prev,f=2,filters=[2,4,6],stage=1,block="a")sess.run(tf.global_variables_initializer())out = sess.run([A],feed_dict={A_prev:X,K.learning_phase():0})print("out = "+str(out[0][1][1][0]))#卷積殘差塊——convolutional_block def convolutional_block(X,f,filters,stage,block,s=2):"""param :X -- 輸入的張量,維度為(m, n_H_prev, n_W_prev, n_C_prev)f -- 整數(shù),指定主路徑的中間 CONV 窗口的形狀(過(guò)濾器大小,ResNet中f=3)filters -- python整數(shù)列表,定義主路徑的CONV層中過(guò)濾器的數(shù)目stage -- 整數(shù),用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置block --字符串/字符,用于命名層,取決于它們?cè)诰W(wǎng)絡(luò)中的位置s -- 整數(shù),指定使用的步幅return:X -- 卷積殘差塊的輸出,維度為:(n_H, n_W, n_C)"""# 定義基本的名字conv_name_base = "res" + str(stage) + block + "_branch"bn_name_base = "bn" + str(stage) + block + "_branch"# 過(guò)濾器F1, F2, F3 = filters# 保存輸入值,后面將需要添加回主路徑X_shortcut = X# 主路徑第一部分X = Conv2D(filters=F1, kernel_size=(1, 1), strides=(s, s), padding="valid",name=conv_name_base + "2a", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2a")(X)X = Activation("relu")(X)# 主路徑第二部分X = Conv2D(filters=F2, kernel_size=(f, f), strides=(1, 1), padding="same",name=conv_name_base + "2b", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2b")(X)X = Activation("relu")(X)# 主路徑第三部分X = Conv2D(filters=F3, kernel_size=(1, 1), strides=(1, 1), padding="valid",name=conv_name_base + "2c", kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3, name=bn_name_base + "2c")(X)#shortcut路徑X_shortcut = Conv2D(filters=F3,kernel_size=(1,1),strides=(s,s),padding="valid",name=conv_name_base+"1",kernel_initializer=glorot_uniform(seed=0))(X_shortcut)X_shortcut = BatchNormalization(axis=3,name=bn_name_base+"1")(X_shortcut)# 主路徑最后部分,為主路徑添加shortcut并通過(guò)relu激活X = layers.add([X, X_shortcut])X = Activation("relu")(X)return Xtf.reset_default_graph()with tf.Session() as test:np.random.seed(1)A_prev = tf.placeholder("float", [3, 4, 4, 6])X = np.random.randn(3, 4, 4, 6)A = convolutional_block(A_prev, f = 2, filters = [2, 4, 6], stage = 1, block = 'a')test.run(tf.global_variables_initializer())out = test.run([A], feed_dict={A_prev: X, K.learning_phase(): 0})print("out = " + str(out[0][1][1][0]))#50層ResNet模型構(gòu)建 def ResNet50(input_shape = (64,64,3),classes = 6):"""構(gòu)建50層的ResNet,結(jié)構(gòu)為:CONV2D -> BATCHNORM -> RELU -> MAXPOOL -> CONVBLOCK -> IDBLOCK*2 -> CONVBLOCK -> IDBLOCK*3-> CONVBLOCK -> IDBLOCK*5 -> CONVBLOCK -> IDBLOCK*2 -> AVGPOOL -> TOPLAYERparam :input_shape -- 數(shù)據(jù)集圖片的維度classes -- 整數(shù),分類的數(shù)目return:model -- Keras中的模型實(shí)例"""#將輸入定義為維度大小為 input_shape的張量X_input = Input(input_shape)# Zero-PaddingX = ZeroPadding2D((3,3))(X_input)# Stage 1X = Conv2D(64,kernel_size=(7,7),strides=(2,2),name="conv1",kernel_initializer=glorot_uniform(seed=0))(X)X = BatchNormalization(axis=3,name="bn_conv1")(X)X = Activation("relu")(X)X = MaxPooling2D(pool_size=(3,3),strides=(2,2))(X)# Stage 2X = convolutional_block(X,f=3,filters=[64,64,256],stage=2,block="a",s=1)X = identity_block(X,f=3,filters=[64,64,256],stage=2,block="b")X = identity_block(X,f=3,filters=[64,64,256],stage=2,block="c")#Stage 3X = convolutional_block(X,f=3,filters=[128,128,512],stage=3,block="a",s=2)X = identity_block(X,f=3,filters=[128,128,512],stage=3,block="b")X = identity_block(X,f=3,filters=[128,128,512],stage=3,block="c")X = identity_block(X,f=3,filters=[128,128,512],stage=3,block="d")# Stage 4X = convolutional_block(X,f=3,filters=[256,256,1024],stage=4,block="a",s=2)X = identity_block(X,f=3,filters=[256,256,1024],stage=4,block="b")X = identity_block(X,f=3,filters=[256,256,1024],stage=4,block="c")X = identity_block(X,f=3,filters=[256,256,1024],stage=4,block="d")X = identity_block(X,f=3,filters=[256,256,1024],stage=4,block="e")X = identity_block(X,f=3,filters=[256,256,1024],stage=4,block="f")#Stage 5X = convolutional_block(X,f=3,filters=[512,512,2048],stage=5,block="a",s=2)X = identity_block(X,f=3,filters=[256,256,2048],stage=5,block="b")X = identity_block(X,f=3,filters=[256,256,2048],stage=5,block="c")#最后階段#平均池化X = AveragePooling2D(pool_size=(2,2))(X)#輸出層X(jué) = Flatten()(X) #展平X = Dense(classes,activation="softmax",name="fc"+str(classes),kernel_initializer=glorot_uniform(seed=0))(X)#創(chuàng)建模型model = Model(inputs=X_input,outputs=X,name="ResNet50")return model#運(yùn)行構(gòu)建的模型圖 model = ResNet50(input_shape=(64,64,3),classes=6)#編譯模型來(lái)配置學(xué)習(xí)過(guò)程 model.compile(optimizer="adam",loss="categorical_crossentropy",metrics=["accuracy"])#加載數(shù)據(jù)集 X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset()# Normalize image vectors X_train = X_train_orig/255. X_test = X_test_orig/255.# Convert training and test labels to one hot matrices Y_train = convert_to_one_hot(Y_train_orig, 6).T Y_test = convert_to_one_hot(Y_test_orig, 6).Tprint ("number of training examples = " + str(X_train.shape[0])) print ("number of test examples = " + str(X_test.shape[0])) print ("X_train shape: " + str(X_train.shape)) print ("Y_train shape: " + str(Y_train.shape)) print ("X_test shape: " + str(X_test.shape)) print ("Y_test shape: " + str(Y_test.shape))#訓(xùn)練模型 model.fit(X_train,Y_train,epochs=20,batch_size=32)#測(cè)試集性能測(cè)試 preds = model.evaluate(X_test,Y_test) print("Loss = "+str(preds[0])) print("Test Accuracy ="+str(preds[1]))四、在tensorflow框架下構(gòu)建殘差卷積神經(jīng)網(wǎng)絡(luò)ResNet V2
?ResNet V2中的殘差學(xué)習(xí)單元為 bottleneck,與ResNet V1的主要區(qū)別在于:
1、在每一層前都使用了Batch Normalization;2、對(duì)輸入進(jìn)行preactivation,而不是在卷積進(jìn)行激活函數(shù)處理。
(一)基礎(chǔ)知識(shí)應(yīng)用
1、Python內(nèi)建的集合模塊——collections
--------------------- 作者:loveliuzz 來(lái)源:CSDN 原文:https://blog.csdn.net/loveliuzz/article/details/79117397?utm_source=copy 版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的ResNet在分别在Keras和tensorflow框架下的应用案例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数据结构基础(8) --单链表的设计与
- 下一篇: 积谷防饥