雷鋒網(wǎng) AI科技評論按:本文作者陳仲銘,雷鋒網(wǎng) AI科技評論獲其授權(quán)發(fā)布。
為什么我的CNN網(wǎng)絡(luò)模型訓(xùn)練出來的東西總是過度擬合?已經(jīng)改了很多次參數(shù)都不行,到底是樣本有問題還是網(wǎng)絡(luò)模型定義有問題?問題在哪來?
CNN網(wǎng)絡(luò)模型中的每一層學(xué)習(xí)的是些什么特征?為什么有的人說第一層卷積核提取的是邊緣信息特征,有的人卻說第一層卷積核提取的是顏色特征?到底是兩者都有還是什么回事?
CNN網(wǎng)絡(luò)可不可以減掉幾層然后保持相同的精度和損失率呢?減掉幾層可以減少網(wǎng)絡(luò)參數(shù),本來我的GPU顯存不是很大,太大的網(wǎng)絡(luò)塞不下,不想重新買GPU只能減層,有沒有大神可以教教我怎么操作?。?/p>
很多時候我們會遇到上面的問題,然后道聽途說地開始給別人瞎粑粑吹吹牛皮。在這之前,連我自己都不知道VGG16網(wǎng)絡(luò)最后兩層block5 conv2,block5 conv3訓(xùn)練到最后幾乎沒有太多的紋理特征。你知道嗎?不知道的話可以跟我一起學(xué)習(xí)學(xué)習(xí),我也是個初學(xué)者,在國內(nèi)CNN還沒有太流行起來之前就開始跳坑,到現(xiàn)在蹲坑已深。棄坑 ing。。。。
最近為自己所在的公司寫了一個工具來分析訓(xùn)練出來的CNN網(wǎng)絡(luò)模型,如下圖所示:從第一張圖我們可以看到這張圖有一些goldfish金魚的模型,好像有很多條魚尾巴,然后中間有好多鱗片,一張很奇怪的圖像,但是別嫌棄這張圖像,因為假設(shè)你把這張圖像扔到經(jīng)過ImageNet數(shù)據(jù)集訓(xùn)練過的VGGNet模型,出來為goldfish的概率絕對是99%的,你多試100次看是不是都為goldfish。
同理,第2張圖,第3張圖,都是這樣的結(jié)果,連我學(xué)文科的妹紙看到這些圖就yaya叫,這是什么,好惡心,怎么第3張有點像鳥但是有不是鳥的呢。對,我們搞神經(jīng)網(wǎng)絡(luò)的就喜歡這些惡心的圖片。越惡心特征越明顯。
通過梯度上升獲得可視化卷積圖
假設(shè)人工合成的可視化卷積核圖為 x,我們希望這張合成圖 x 能夠使其對應(yīng)的神經(jīng)元(卷積核)具有最高的激活值。所得到的這張合成圖像就是該卷基層的卷積核“想要看到的”或者“正在尋找的紋理特征”。也就是說我們希望找到一張圖像經(jīng)過CNN網(wǎng)絡(luò),傳播到指定的卷積核的時候,這張圖片可以使得該卷積核的得分最高。
為了合成這一張圖片,我們開始從一張帶有隨機(jī)噪聲的圖像開始,每個像素值隨機(jī)選取一種顏色。
接下來,我們使用這張噪聲圖作為CNN網(wǎng)絡(luò)的輸入向前傳播,然后取得其在網(wǎng)絡(luò)中第 i 層 j 個卷積核的激活 a_ij(x),然后做一個反向傳播計算 delta a_i(x)/delta x 的梯度,最后我們把該噪聲圖的卷積核梯度來更新噪聲圖。目標(biāo)是希望通過改變每個像素的顏色值以增加對該卷積核的激活,這里就使用了梯度上升法:
其中 itselong 為梯度上升的學(xué)習(xí)率。不斷重復(fù)上述過程,直到圖像 x 能夠讓第 i 層第 j 個卷積核具有較高的激活值。
對于具體的實現(xiàn)我們需要定義一個損失函數(shù),該損失函數(shù)將用于最大化某個指定卷積核的激活值。以該損失函數(shù)作為優(yōu)化目標(biāo),我們可以了解到底什么樣的圖片才可以使得這個卷積核取得更好的激活值。
現(xiàn)在我們使用Keras的后端來完成這個損失函數(shù), gradients(loss, variables)為返回loss函數(shù)關(guān)于variables的梯度。
start_time = time.time
# The loss is the activation of the neuron for the chosen class
loss = layer_output[0, class_index]
# we compute the gradient of the input picture wrt this loss
grads = K.gradients(loss, input_img)[0]
# this function returns the loss and grads given the input picture
# also add a flag to disable the learning phase (in our case dropout)
iterate = K.function([input_img, K.learning_phase()], [loss, grads])
np.random.seed(1337) # for reproducibility
# we start from a gray image with some random noise
input_img_data = np.random.normal(0, 10, (1,) + model.input_shape[1:])
# (1,) for batch axis
注意這里有個小小的trick——對計算出來的梯度進(jìn)行了L2正則化操作,使得梯度不會過小或過大,其帶來的好處是使梯度上升的過程平滑進(jìn)行。
后端函數(shù)function用傳遞來的參數(shù)實例化一個Keras的Function類返回。這相當(dāng)于Function的對象當(dāng)作函數(shù)來使用,相當(dāng)于重載了括號運算符,如outputs = self.train_function(inputs)。
根據(jù)剛剛定義的損失函數(shù)iterate_fun,現(xiàn)在可以根據(jù)梯度上升對卷積核的激活值進(jìn)行梯度上升計算。
# we run gradient ascent for 1000 steps
for i in range(1000):
loss_value, grads_value = iterate([input_img_data, 0]) # 0 for test phase
input_img_data += grads_value * learning_rate # Apply gradient to image
print('Current loss value:', loss_value)
# decode the resulting input image and add it to the list
img = deprocess(input_img_data[0])
kept_images.append((img, loss_value))
end_time = time.time
print('Filter %d processed in %ds' % (class_index, end_time - start_time))
最后輸出的圖像如下圖所示,這是魚頭?還是羊頭?不看ImageNet的連接我也不知道是什么頭。不過這樣的方法是我們讓輸入的圖像盡量與卷積核希望看到的東西一樣,通過該層卷積核的損失和梯度進(jìn)行上升補(bǔ)充,對輸入的原圖進(jìn)行填充細(xì)節(jié),最后得到可視化卷積核圖。
可視化所有卷積核圖
可視化卷積核是本例子最有趣的部分,也是筆者最喜歡的部分。通過分析CNN網(wǎng)絡(luò)模型中的卷積核,我們將會看到每一層卷積核到底提取的是什么樣的內(nèi)容、紋理、特征。當(dāng)我們深入了解CNN模型提取特征背后的意義,就可以有足夠信心去修改卷積神經(jīng)網(wǎng)絡(luò)CNN的參數(shù)。
下面我們將會利用已經(jīng)訓(xùn)練好的VGG16網(wǎng)絡(luò)模型,來系統(tǒng)地可視化各個網(wǎng)絡(luò)層的各個卷積核,看看CNN是對輸入進(jìn)行逐層分解提取特征的到底都是些什么。
最后的執(zhí)行結(jié)果如下圖所示,Block1_Conv1的卷積核主要完成如顏色、方向等編碼,到了Block2_Conv2的卷積核明顯比Block1_Conv1多了更多的紋理和不同的紋理方向,所表達(dá)的顏色也更加豐富多樣,并且在邊緣處可以看到有部分凹凸表現(xiàn)。
隨著VGG16網(wǎng)絡(luò)模型繼續(xù)深入,這些顏色和方向與基本的紋理進(jìn)行組合,逐漸生成特殊紋理。當(dāng)進(jìn)入Block3_Conv1后,方向和顏色的表現(xiàn)開始變少,開始出現(xiàn)更加復(fù)雜的紋理特征(圓形、螺旋形、多邊形、波浪等形狀組合),到了Block5_Conv1后可以清晰看到其紋理更加特別,卷積核隨著網(wǎng)絡(luò)空間信息的增長而出現(xiàn)了更加精細(xì)和復(fù)雜的特征。
卷積核變得越來越復(fù)雜,因為他們開始納入越來越大的空間范圍信息中,所呈現(xiàn)和表達(dá)的信息進(jìn)一步豐富。
細(xì)心的讀者經(jīng)過實際的嘗試之后或許會發(fā)現(xiàn):在同一卷積層中會出現(xiàn)少量的可視化卷積核是空白或者相同,這意味著該卷積核對后續(xù)的操作并沒有產(chǎn)生實際的作用,可以通過Dropout這些卷積核以減少網(wǎng)絡(luò)的計算量和減少過度擬合的可能性。
另外,也會有部分可視化卷積核可以通過旋轉(zhuǎn)平移,獲得另外一個可視化卷積核。這是一個很有趣的研究方向,我們或許可以通過尋找一種旋轉(zhuǎn)不變性的方法來潛在地代替網(wǎng)絡(luò)層中的其他卷積核,從而壓縮卷積核的數(shù)量。驚訝的是即使對于級別相對高的濾波器,如Block4_Conv1中,通過旋轉(zhuǎn)、平移獲得相同的可視化卷積核仍然成立。
可是到了block5 conv3的最后,經(jīng)過代碼實踐之后,我們會發(fā)現(xiàn)512個卷積核里面只有65個卷積核loss不為0,也就是其余的卷積核已經(jīng)不能再繼續(xù)提取高維紋理特征信息了,這是怎么一回事?為什么沒有了呢?這也是ResNet或者GoogleNet對于VGGNet的改進(jìn),可能有時候CNN網(wǎng)絡(luò)不是每一個卷積核(神經(jīng)元)都能參與網(wǎng)絡(luò)的計算,發(fā)揮作用,提取到高維特征。下圖為ResNet的跳層方式,因為VGG的block5 conv3、block5 conv2開始出現(xiàn)大量沒有用的卷積核,而block4 conv3卻有很多有用的信息可以向后傳遞。
有了對CNN網(wǎng)絡(luò)模型的可視化分析,我們更加深一步地理解卷積神經(jīng)網(wǎng)絡(luò)CNN的具體操作過程,具體提取的是什么樣的紋理特征信息。如果你有精力,還可以自己動手指定dropout的神經(jīng)元,查看自己的卷積神經(jīng)網(wǎng)絡(luò)為什么會過度擬合,可以怎么修剪網(wǎng)絡(luò)。這是一個很有趣的過程,也是一個考驗人類耐心的過程。
CNN真的理解視覺嗎卷積神經(jīng)網(wǎng)絡(luò)CNN的兩個主要作用是:1)把輸入的視覺空間圖像,解耦成分層次的卷積核組合。2)通過分層次的卷積核把輸入的數(shù)據(jù)映射到不同的幾何空間。
有的人會宣稱卷積神經(jīng)網(wǎng)絡(luò)CNN通過分層思想對輸入的圖像進(jìn)行解耦,這一過程模擬了人類視覺皮層,因此才能獲得如此精妙的效果??墒聦嵳娴氖沁@樣嗎?從科學(xué)的角度來看,這并不意味著我們真的在某種程度上解決了計算機(jī)視覺的問題,我們只是使用了數(shù)學(xué)的方法對輸入的圖像數(shù)據(jù)進(jìn)行抽樣和幾何空間映射。即使科學(xué)是這么解釋,但究其竟并沒有反駁視覺皮層不是這種工作方式。
深度學(xué)習(xí)雖然不能表達(dá)真正的智力,但毋庸置疑的是其預(yù)測效果如此驚人,以至于在近年來沒有其他算法可以比擬,甚至在某些情況下超過了人類的預(yù)測精度!我們不期待著算法學(xué)習(xí)我們的思考方式,而應(yīng)去擁抱數(shù)學(xué),用其特殊的方式去為人類服務(wù),繼續(xù)發(fā)現(xiàn)、繼續(xù)創(chuàng)造、繼續(xù)在模擬數(shù)字領(lǐng)域領(lǐng)跑!
參考文獻(xiàn):
Yosinski__2015__ICML_DL__Understanding_Neural_Networks_Through_Deep_Visualization
How convolutional neural networks see the world
地址:https://blog.keras.io/how-convolutional-neural-networks-see-the-world.html
雷鋒網(wǎng) AI科技評論。
聯(lián)系客服