tensorflow学习笔记:卷积神经网络最终笔记
這已經(jīng)是我的第五篇博客學習卷積神經(jīng)網(wǎng)絡了。之前的文章分別是:
1,Keras深度學習之卷積神經(jīng)網(wǎng)絡(CNN),這是開始學習Keras,了解到CNN,其實不懂的還是有點多,當然第一次筆記主要是給自己心中留下一個印象,知道什么是卷積神經(jīng)網(wǎng)絡,而且主要是學習Keras,順便走一下CNN的過程。
2,深入學習卷積神經(jīng)網(wǎng)絡(CNN)的原理知識,這次是對CNN進行深入的學習,對其原理知識認真學習,明白了神經(jīng)網(wǎng)絡如何識別圖像,知道了卷積如何運行,池化如何計算,常用的卷積神經(jīng)網(wǎng)絡都有哪些等等。
3,TensorFlow學習筆記——圖像識別與卷積神經(jīng)網(wǎng)絡,這里開始對TensorFlow進行完整的學習,以CNN為基礎開始對TensorFlow進行完整的學習,而這里練習的是 LeNet網(wǎng)絡。
4,深入學習卷積神經(jīng)網(wǎng)絡中卷積層和池化層的意義,這篇是對卷積神經(jīng)網(wǎng)絡中卷積層和池化層意義的深入學習,用一個簡單的例子,闡述了其原理。
今天構建兩個完整卷積神經(jīng)網(wǎng)絡,一個簡單的MNIST熱個身,一個稍微復雜的Cifar,今天學習完,不再做基礎的卷積神經(jīng)網(wǎng)絡的相關筆記,我相信自己已經(jīng)掌握了。
卷積神經(jīng)網(wǎng)絡是目前深度學習技術領域中非常具有代表性的神經(jīng)網(wǎng)絡之一,在圖像分析和處理領域取得了眾多突破性的進展,在學術界常用的標準圖像標注集ImageNet上,基于卷積神經(jīng)網(wǎng)絡取得了很多成就,包括圖像特征提取分類,場景識別等。卷積神經(jīng)網(wǎng)絡相較于傳統(tǒng)的圖像處理算法的優(yōu)點之一在于避免了對圖像復雜的前期預處理過程,尤其是人工參與圖像預處理過程,卷積神經(jīng)網(wǎng)絡可以直接輸入原始圖像進行一系列的工作,至今已經(jīng)廣泛應用于各類圖像相關的應用中。
卷積神經(jīng)網(wǎng)絡(Convolutional Neural Network CNN)最初是為解決圖像識別等問題設計的,當然其現(xiàn)在的應用不僅限于圖像和視頻,也可用于時間序列信號,比如音頻信號,文本數(shù)據(jù)等。在早期的圖像識別研究中,最大的挑戰(zhàn)是如何組織特征,因為圖像數(shù)據(jù)不像其他類型的數(shù)據(jù)那樣可以通過人工理解來提取特征。在股票預測等模型中,我們可以從原始數(shù)據(jù)中提取過往的交易價格波動,市盈率,市凈率,盈利增長等金融因子,這既是特征工程。但是在圖像中,我們很難根據(jù)人為理解提取出有效而豐富的特征。在深度學習出現(xiàn)之前,我們必須借助 SIFT,HoG等算法提取具有良好區(qū)分性的特征,再集合 SVM 等機器學習算法進行圖像識別。如下圖所示,SIFT對一定程度內(nèi)的縮放,平移,旋轉(zhuǎn),視覺改變,亮度調(diào)整等畸變,都具有不變性,是當前最重要的圖像特征提取方法之一。可以說在之前只能依靠SIFT等特征提取算法才能勉強進行可靠地圖像識別。
然而SIFT這類算法提取的特征還是有局限性的,在ImageNet ILSVRC 比賽的最好結果的錯誤率也有26%以上,而且常年難以產(chǎn)生突破。卷積神經(jīng)網(wǎng)絡提取的特征則可以達到更好的效果,同時它不需要將特征提取和分類訓練兩個過程分開,它在訓練時就自動提取了最有效的特征。CNN作為一個深度學習架構被提出的最初訴求,是降低對圖像數(shù)據(jù)預處理的要求,以及避免復雜的特征工程。CNN可以直接使用圖像的原始像素作為輸入,而不必使用SIFT等算法提取特征,減輕了使用傳統(tǒng)算法如SVM時必須做的大量重復,繁瑣的數(shù)據(jù)預處理工作。和SITF等算法類似,CNN的最大特點在于卷積的權值共享結構,可以大幅減少神經(jīng)網(wǎng)絡的參數(shù)量,防止過擬合的同時又降低了神經(jīng)網(wǎng)絡模型的復雜度。CNN的權重共享其實也很像早期的延時神經(jīng)網(wǎng)絡(TDNN),只不過后者是在時間這一個維度上進行權值共享,降低了學習時間序列信號的復雜度。
1,CNN在圖像分類問題上有什么優(yōu)勢?
這里使用水果分類來分析一下SVM以及神經(jīng)網(wǎng)絡的劣勢。
如果我們有一組水果的圖片,里面有草莓,香蕉和橘子。在圖片尺寸較大的情況下,使用SVM分類的步驟是:
人工提取特征,比如說大小,形狀,重量,顏色等。
根據(jù)上述特征,把每一張圖片映射到空間中的一個點,空間的維度和特征的數(shù)量相等。
相同類別的物體具有類似的特征,所以空間中標記為草莓的點肯定是聚在一起的,香蕉和橘子也是同理。這時候使用SVM算法在空間中畫出各類點之間的分界線就完成了分類。
在最后一步中,不使用SVM,使用別的分類器也是可以的,比如KNN,貝葉斯,甚至神經(jīng)網(wǎng)絡都是可以的。雖然不同算法中性能會有差異,但是這里我想說的就是在圖像分類問題上的瓶頸并不在算法的性能上,而是在特征的提取上。
區(qū)分草莓和橘子的特征是容易提取的,那橘子和橙子呢?如果上述四個特征不能很好的區(qū)分橘子和橙子,想要進一步提升算法的性能怎么辦?通常的做法是需要提取新的特征。那么新的特征如何選擇呢?對于我這種水果盲來說,這個問題是具有一定難度的。
除了橘子橙子問題,我們還有貓狗如何區(qū)分,狗品種如何識別等一系列問題。我想對于大部分人來說,狗狗品種的識別是非常有難度的。轉(zhuǎn)了一圈回來,突然發(fā)現(xiàn),圖像分類任務的瓶頸驚人出現(xiàn)在特征選擇上。
如果我們用神經(jīng)網(wǎng)絡直接對貓狗進行分類呢?這樣不就避開了特征提取這一步了啊?假設輸入圖片大小為30*30,那么設置900個輸入神經(jīng)元,隱含層設置1000個神經(jīng)元,輸出神經(jīng)元個數(shù)對應需要的輸出數(shù)量不就好了嗎?甚至用SVM也可以這樣做,把一張30*30的圖看做900維空間中的一個點,代表貓的點和代表狗的點在這個900維的空間中必然是相聚于兩個簇,然后我們就可以使用SVM來劃出分界線了。
但是這樣計算開銷就太大了,對于30*30的圖片我們也許可以這樣做,對于1000*1000的圖片我們這樣做的話就需要至少一百萬個隱層神經(jīng)元,這樣我們就至少更新10^12個參數(shù)。而SVM的話,則相當于在一百萬維的空間中運行了。運行量將會大的難以估計。另外圖中并不是所有的信息都和我們需要的。背景對我們的分類毫無價值,然而在這種一股腦全部拿來做輸入的情況下,背景也被當成了特征進入了模型當中,準確度自然會有所下降。
總之,如果不人工提取特征,那么計算量會非常大,精確度也無法保證。而人工提取特征的方式又會在某些問題下難以進行,比如狗狗品種分類。
而CNN通過它獨有的方式,成功的解決了這兩個問題。也就是說,CNN是一個可以自動提取特征而卻待訓練參數(shù)相對不那么多的神經(jīng)網(wǎng)絡,這就是CNN在圖像分類任務中決定性的優(yōu)勢。
2,為什么要使用卷積層?
和神經(jīng)網(wǎng)絡模型類似,CNN的設計靈感同樣來自于對神經(jīng)細胞的研究。
1981 年的諾貝爾醫(yī)學獎,頒發(fā)給了 David Hubel、TorstenWiesel,以及
Roger Sperry。他們的主要貢獻,是發(fā)現(xiàn)了人的視覺系統(tǒng)的信息處理是分級的。
從低級的V1區(qū)提取邊緣特征,再到V2區(qū)的形狀或者目標的部分等,再到更高層,
整個目標、目標的行為等。也就是說高層的特征是低層特征的組合,從低層到高層
的特征表示越來越抽象,越來越能表現(xiàn)語義或者意圖。而抽象層面越高,存在的可
能猜測就越少,就越利于分類。
值得一提的是,最低級的V1區(qū)需要提取邊緣特征,而在上面提到的分類中,神經(jīng)網(wǎng)絡實際上是把30 * 30 的圖片按照 900 個像素點處理的,那么有沒有一種方法能夠神經(jīng)網(wǎng)絡像人一樣,按照邊緣來理解呢?
卷積計算并不復雜,矩陣對應元素相乘的和就是卷積的結果,到了神經(jīng)網(wǎng)絡中會多出偏置b還有激活函數(shù),具體方法如下圖:
圖片中展示的是由九個權重組成的矩陣和圖片上九個像素點組成矩陣進行卷積過程。在偏置b為0,激活函數(shù)使用ReLU的情況下,過程就像圖片右下角的公式一樣,對應元素乘積的和,再加上值為0的b,然后外套激活函數(shù)得到輸出0。
你可能會想到這部分的計算和普通的神經(jīng)網(wǎng)絡沒神經(jīng)網(wǎng)絡沒什么差別,形式都是 f(wx + b)。那么這么處理和邊緣有什么關系?多做幾次卷積就知道了。
Filter 指的是權重組成矩陣,Input Layer中存的是圖片中的全部像素。Convolutional Layer存的是Filter與圖片中所有3*3矩陣依次卷積后得到的結果。在輸出中我們可以看到兩個3,他們比其他的元素0都要大。是什么決定了卷積結果的大小?觀察后發(fā)現(xiàn),圖中參與卷積的部分1的排序和活動窗口中1的排列完全一樣時,輸出為3。而像素的排列方式其實就是圖片中的形狀,這說明如果圖像中的形狀和Filter中的形狀相似的話,輸出值就大,不像就小。因此,卷積的操作建立了神經(jīng)網(wǎng)絡與圖像邊緣的聯(lián)系。
實際上CNN經(jīng)過訓練之后,F(xiàn)ilter中就是圖片的邊緣,角落之類的特征。也即是說,卷積層是在自動提取圖片中的特征。
除此之外,卷積還有一種局部連接的思想在里面。他對圖片的處理方式是一塊一塊的,并不是所有像素值一起處理。因此可以極大地降低參數(shù)值的總量。這里我需要使用那張經(jīng)典的圖片來說明卷積層是如何降低參數(shù)總量的:
對于一張1000*1000的圖片來說,對他進行分類,至少需要10的12次方個參數(shù)。而如果對圖片使用卷積操作,每個神經(jīng)元之和圖像上的10*10的像素連接的話,參數(shù)總量就變成了10的8次方。但是這樣的操作會導致一個問題,每個神經(jīng)元只對圖片一部分的內(nèi)容,那么這個神經(jīng)元學到的內(nèi)容就不能應用到其他神經(jīng)元上。比如說有這樣一個訓練集,同樣自私的貓出現(xiàn)在黑色神經(jīng)元負責的區(qū)域中,但是測試集中,貓可能出現(xiàn)在圖片的任何位置。按照局部連接的做法,其他區(qū)域的貓是無法被正確識別的。
而為了讓出現(xiàn)在任何位置的貓都能夠被正確的識別,提出了權重共享。讓紅綠藍黑神經(jīng)元中的參數(shù)全都一樣,這樣就可以使得模型的準確率不受物體位置的影響,看起來就像同一個Filter劃過了整個圖片。從提取特征的角度上來講,一個Filter顯然不能滿足需求,因此需要生成多個不同的Filter來對圖片進行卷積。更棒的是,為了獲得平移不變性而使用的權重共享法,又以外的再一次降低了待訓練參數(shù)總數(shù)。
就是使用100個權值共享的10*10 Filter來卷積,總參數(shù)也才10的4次方。也就是說,參數(shù)相較于普通的神經(jīng)網(wǎng)絡而言,總共下降了整整8個數(shù)量級,這種提升是夸張的。
所以,卷積層的工作方式已經(jīng)全部出來了,具體工作流程入下:
藍色的部分代表輸入圖像;綠色的部分代表輸出矩陣;周圍的虛線是padding操作,可以看做圖像的一部分;下方不斷移動的陰影就是FIlter,其大小,數(shù)量,一次移動的距離都是可以自定義的;陰影至上方綠色的連線代表相乘再加之后的結果輸出到了輸出矩陣的那個位置。卷積層的折中操作方式,成功的模擬了生物視覺系統(tǒng)中的邊緣特征提取部分。
而CNN中對于分級結構的模擬,是通過卷積層的層層疊加實現(xiàn)的。AlexNet的論文中不止一次的提到,網(wǎng)絡的深度對CNN性能的影響是顯著的。可以認為卷積層的不斷疊加會使得提取到的特征越來越復雜,整個流程就像上述引用中提到的人類的視覺系統(tǒng)的工作方式一樣運行,最終完成對圖片的分類。
那么現(xiàn)在就可以很輕松的回答上面的問題了,使用卷積層是因為卷積層本質(zhì)上是在自動提取圖片的特征,而且在提取特征的同時,極大的降低了網(wǎng)絡總體待訓參數(shù)的總量。這兩個特征使得CNN克服了特征提取困難,待訓參數(shù)龐大的問題,成功制霸圖片分類問題。
3,為什么要使用池化層?
你可能會問,卷積層就已經(jīng)把參數(shù)將了下來,還解決了特征提取的問題,那還加一個池化層干什么呢?(可能池化層只是工程上想讓網(wǎng)絡更深而做出的一個無奈之舉)
就以最精彩出現(xiàn)的最大池化為例,來看看所謂的池化操作有多隨便吧。
一個2*2 的最大池化操作如上圖,它做的就是把2*2窗口中的最大值存下來。所以綠色部分留下來了6;棕色部分是8;藍色部分是4。所以就很簡單,很隨便,但是這個操作有什么優(yōu)點呢?不知道,但是缺點卻顯而易見——損失了細節(jié)。為什么損失細節(jié)也要做這一步呢?可能就是要壓縮矩陣,這樣可以在模型中多加幾層卷積層,用來提取更高維,更復雜的特征。而從壓縮的角度來看,這一步可謂簡單有效,一個取最大值的操作,就讓矩陣大小變?yōu)樗姆种弧?/p>
AlexNet的出現(xiàn)時2012年,那時候用到的是GTX580,3G顯存,文章中提到只用一塊GPU是不行的,因為顯存會爆,因此用了兩塊GPU并行進行這個任務。可能作者也是苦于總是爆顯存,而不得不加上池化層。就算這樣,還要用到兩塊GPU才成功訓練了整個網(wǎng)絡。
然而池化層的應用似乎帶來更多的便利之處。由于其只取最大值,忽視掉了其他影響較小的值,所以在當內(nèi)容發(fā)生很小的變化的時候包括一些平移旋轉(zhuǎn),CNN任然能夠穩(wěn)定識別對應內(nèi)容。也就是說池化層給模型帶來了一定程序上的不變性。
而應不應該使用池化層還是一個正在討論的問題,有的網(wǎng)絡用,有的網(wǎng)絡不用。按照我的理解,在顯存夠用的情況下,不用池化層。這種丟失細節(jié)提升模型不變性的方法有點傷人傷己的意思。而且我們希望得到的模型并不是在不知道圖片變化了的情況下可以得到正確結果,我們希望的是模型可以認識到差異卻依然能做出正確的分類才對。
4,全連接層的作用
在經(jīng)過幾次卷積和池化的操作之后,卷積神經(jīng)網(wǎng)絡的最后一步是全連接層。這一步就和最最普通的神經(jīng)網(wǎng)絡沒有什么區(qū)別。我認為這里的神經(jīng)網(wǎng)絡就是充當一個分類器的作用,輸入時不同特征的特征值,輸出的是分類。其實可以在訓練好之后,把全連接層砍掉,把卷積部分的輸出當做是特征,全連接層換成SVM或者別的分類器,重新訓練,也是可以取得良好效果的。
5,總結
1,CNN之前的圖片分類算法性能受制于特征的提取以及龐大參數(shù)數(shù)量導致的計算困難。
2,使用卷積來模擬人類視覺系統(tǒng)的工作方式,而這種方式極大的降低了神經(jīng)網(wǎng)絡的帶訓練參數(shù)數(shù)量。
3,為了獲得平移不變形,使用了權重共享技術,該技術進一步降低了帶訓練參數(shù)數(shù)量
4,卷積層實際上是在自動提取圖片特征,解決了圖像特征提取這一難題。
5,使用池化層的根部原因是降低計算量,而其帶來的不變形并不是我們需要的。不過在以模型準確率為綱的大背景下,繼續(xù)使用無可厚非。
6,全連接層實質(zhì)上是一個分類器。
6,卷積層的步驟
一般的卷積神經(jīng)網(wǎng)絡由多個卷積層構成,每個卷積層中通常會進行如下幾個操作。
(1)圖像通過多個不同的卷積核的濾波,并加偏置(bias),提取出局部特征,每一個卷積核會映射出一個新的2D圖像。
(2)將前面卷積核的濾波輸出結果,進行非線性的激活函數(shù)處理。目前最常見的是使用ReLU函數(shù),而以前Sigmoid 函數(shù)用的比較多。
(3)對激活函數(shù)的結果再進行池化操作(即降采樣,比如將 2*2 的圖片降為 1*1 的圖片),目前一般是使用最大池化,保留最顯著的特征,并提升模型的畸變?nèi)萑棠芰Α?/p>
這幾個步驟就構成了最常見的卷積層,當然也可以加上一個 LRN(Local Response Normalization,局部響應歸一化層)層,目前非常流暢的Trick 還有 Batch Normalization等。
七,卷積神經(jīng)網(wǎng)絡發(fā)展趨勢
下面我們簡單回顧卷積神經(jīng)網(wǎng)絡的歷史,下圖所示大致勾勒出最近幾十年卷積神經(jīng)網(wǎng)絡的發(fā)展方向。Perceptron(感知機)于 1957年由 Frank Resenblatt 提出,而 Perceptron不僅是卷積網(wǎng)絡,也是神經(jīng)網(wǎng)絡的始祖。Neocognitron(神經(jīng)認知機)是一種多層級的神經(jīng)網(wǎng)絡,由日本科學家 Kunihiko Fukushima 于20世紀80年代提出,具有一定程度的視覺認知的功能,并直接啟發(fā)了后來的卷積神經(jīng)網(wǎng)絡。LeNet-5 由 CNN之父 Yann LeCun 于 1997年提出,首次提到了多層級聯(lián)的卷積結構,可對手寫數(shù)字進行有效識別。可以看到前面這三次關于卷積神經(jīng)網(wǎng)絡的技術突破,間隔時間非常長,需要十余年甚至更久才出先一次理論創(chuàng)新。而后于2012年,Hinton的學生Alex依靠8層深的卷積神經(jīng)網(wǎng)絡一舉獲得了 ILSVRC 2012 比賽管局,瞬間點燃了卷積神經(jīng)網(wǎng)絡研究的熱潮。AlexNet成功應用了 ReLU 激活函數(shù),Dropout,最大覆蓋池化,LRN層,CPU加速等新技術,并啟發(fā)了后續(xù)更多的技術創(chuàng)新,卷積神經(jīng)網(wǎng)絡的研究從此進入快車道。
在AlexNet之后,我們可以將卷積神經(jīng)網(wǎng)絡的發(fā)展分為兩類,一類是網(wǎng)絡結構上的改進調(diào)整(上圖的左側分支),另一類是網(wǎng)絡深度的增加(上圖的右側分支)。2013年,顏水成教授的 Network in Network 工作首次發(fā)布,優(yōu)化了卷積神經(jīng)網(wǎng)絡的結構,并推廣了 1*1 的卷積結構。在改進卷積網(wǎng)絡結構的工作中,后繼者還有2014年的Google Inception Net V1,提出了 Inception Module 這個可以反復堆疊的高效的卷積網(wǎng)絡結構,并獲得了當年 ILSVRC 比賽的冠軍。2015年初的 inception V2提出了 Batch Normalization,大大加速了訓練過程,并提升了網(wǎng)絡性能。2015年年末的 Inception V3則繼續(xù)優(yōu)化了網(wǎng)絡結構,提出來 Factorization in Small Convolutions 的思想,分解大尺寸卷積為多個小卷積乃至一維卷積。而另一分支上,許多研究工作則致力于加速深網(wǎng)絡層數(shù),2014年, ILSVRC 比賽的亞軍 VGGNet 全程使用 3*3的卷積,成功訓練了深達19層的網(wǎng)絡,當年的季軍 MSRA-Net 也使用了非常深的網(wǎng)絡。2015年,微軟的ResNet成功訓練了152層深的網(wǎng)絡,一舉拿下了當年 ILSVRC 比賽的冠軍,top-5錯誤率降低至 3.46%。其后又更新了 ResNet V2 ,增加了 Batch Normalization,并去除了激活層而使用 Identity Mapping或 Preactivation,進一步提升了網(wǎng)絡性能。此后,Inception ResNet V2融合了 Inception Net 優(yōu)良的網(wǎng)絡結構,和ResNet 訓練極深網(wǎng)絡的殘差學習模塊,集兩個方向之長,取得了更好的分類結果。
我們可以看到,自AlexNet于2012 年提出后,深度學習領域的研究發(fā)展及其迅速,基本上每年甚至沒幾個月都會出現(xiàn)新一代的技術。新的技術往往伴隨著新的網(wǎng)絡結構,更深的網(wǎng)絡的訓練方法等,并在圖像識別等領域不斷創(chuàng)造新的準確率記錄。至今,ILSVRC 比賽和卷積神經(jīng)網(wǎng)絡的研究依然處于高速發(fā)展期,CNN的技術日新月異。當然其中不可忽視的推動力是,我們擁有了更快的GPU計算資源用以實驗,以及非常方便的開眼工具(比如TensorFlow)可以讓研究人員快速的進行探索和嘗試。在以前,研究人員如果沒有像Alex那樣高超的編程能力讓自己實現(xiàn) cuda-convert,可能都沒辦法設計 CNN或者快速地進行實驗。現(xiàn)在有了 TensorFlow,研究人員和開發(fā)人員都可以簡單而快速的設計神經(jīng)網(wǎng)絡結構并進行研究,測試,部署乃至使用。
八,CNN在MNIST數(shù)據(jù)集上的圖像分類
代碼如下:
# _*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
sess = tf.InteractiveSession()
# 權重和偏置需要創(chuàng)建,我們這里定義好初始化函數(shù)以便重復使用
# 這里我們使用截斷的正態(tài)分布噪聲,標準差設為0.1
def weight_varibale(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 卷積層,池化層也是重復使用的,因此分別定義函數(shù),以便后面重復使用
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
padding='SAME')
# 定義輸入的: x 特征,y_ 真實的label
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
# 因為需要將1D 的輸入向量轉(zhuǎn)為2D的圖片結構,而且顏色只有一個通道
# 所以最終尺寸如下,當然-1代表樣本數(shù)量不固定,不用擔心
x_image = tf.reshape(x, [-1, 28, 28, 1])
# 第一個卷積層
W_conv1 = weight_varibale([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# 第二個卷積層(區(qū)別第一個卷積,卷積核變?yōu)?4,也就是說這一層會提取64個特征)
W_conv2 = weight_varibale([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# 當經(jīng)歷了兩次步長為2*2的最大池化,所以邊長已經(jīng)只有1/4
# 所以圖片尺寸由28*28邊長7*7
W_fc1 = weight_varibale([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 為了防止過擬合,下面使用一個Dropout層
# Dropout層是通過一個placeholder傳入 keep_prob比率來控制的
# 在訓練時候,我們隨機丟掉一部分節(jié)點的數(shù)據(jù)來減輕過擬合
# 在預測時候,我們則保留全部數(shù)據(jù)來追求最好的預測性能
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 將Dropout層的輸出連接一個Softmax層,得到最后的概率輸出
W_fc2 = weight_varibale([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
# 定義損失函數(shù)cross_entropy,但是優(yōu)化器使用Adam 并給予學習率1e-4
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.global_variables_initializer().run()
for i in range(20000):
batch = mnist.train.next_batch(50)
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x: batch[0], y_: batch[1], keep_prob: 1.0
})
print("step %d, training accuracy %g" % (i, train_accuracy))
train_step.run(feed_dict={x: batch[0], y_: batch[1],
keep_prob: 0.5})
print("test accuracy %g" % accuracy.eval(feed_dict={
x: mnist.test.images,
y_: mnist.test.labels,
keep_prob: 1.0
}))
結果如下:
step 0, training accuracy 0.1 step 100, training accuracy 0.9 step 200, training accuracy 0.86 step 300, training accuracy 0.92 step 400, training accuracy 0.92 step 500, training accuracy 0.94 step 600, training accuracy 0.98 step 700, training accuracy 0.96 ...... step 7500, training accuracy 1 step 7600, training accuracy 0.98 ... ... step 19700, training accuracy 1 step 19800, training accuracy 1 step 19900, training accuracy 1 test accuracy 0.9929
最后,這個CNN模型可以得到的準確率約為99.29%,基本可以滿足對手寫數(shù)字識別準確率的要求。相比之前MLP的2%的錯誤率,CNN的錯誤率下降了大約60%,這其中主要的性能提升都來自于更優(yōu)秀的網(wǎng)絡設計,即卷積網(wǎng)絡對圖像特征的提取和抽象能力。依靠卷積核的權值共享,CNN的參數(shù)量并沒有爆炸,降低計算量的同時也減輕了過擬合,因此整個模型的性能有較大的提升。
本文以CIFAR-10 為數(shù)據(jù)集,基于Tensorflow介紹CNN(卷積神經(jīng)網(wǎng)絡)圖像分類模型的構建過程,著重分析了在建模過程中卷積層,池化層,扁平化層,全連接層,輸出層的運算機理,以及經(jīng)過運算后圖像尺寸,數(shù)據(jù)維度等參數(shù)的變化情況。
九,CIFAR-10數(shù)據(jù)集介紹
官網(wǎng)地址:http://www.cs.toronto.edu/~kriz/cifar.html
Cifar數(shù)據(jù)集是一個影響力很大的圖像分類數(shù)據(jù)集,Cifar數(shù)據(jù)集分為了Cifar-10和Cifar-100兩個問題,他們都是圖像詞典項目(Visual Dictionary)中800萬張圖片的一個子集。
CIFAR-10數(shù)據(jù)集由60000張彩色圖片構成,其中包括50000張訓練集圖片、10000張測試集圖片,每張圖片的shape為(32,32,3),即圖片尺寸為32*32,通道數(shù)為3;所有圖片可以被分為10類,包括:
飛機, 汽車, 鳥, 貓, 鹿, 狗, 青蛙, 馬, 船以及卡車。
官網(wǎng)截圖如下所示:
和MNIST數(shù)據(jù)集類似,Cifar-10 中的圖片大小都是固定的且每一張圖片中僅僅包含一個種類的實體。但是和MNIST相比,Cifar數(shù)據(jù)集最大的區(qū)別在于圖片由黑白變成的彩色,且分類的難度也相對更高。在Cifar-10數(shù)據(jù)集上,人工標注的正確率大概為94%,這比MNIST數(shù)據(jù)集上的人工表現(xiàn)要低很多。目前在Cifar-10數(shù)據(jù)集上最好的圖像識別算法正確率為95.59%,達到這個正確率的算法使用了卷積神經(jīng)網(wǎng)絡。
本次學習的目標是建立一個用于識別圖像的相對較小的卷積神經(jīng)網(wǎng)絡,在這過程中,我們將會學到:
1 著重建立一個規(guī)范的網(wǎng)絡組織結構,訓練兵進行評估
2 為建立更大規(guī)模更加復雜的模型提供一個范例
選擇CIFAR-10是因為它的復雜程度足以用來檢驗TensorFlow中的大部分功能,并可將其擴展為更大的模型。與此同時由于模型較小所以訓練速度很快,比較適合用來測試新的想法,檢驗新的技術。
代碼組織
官網(wǎng)教程的代碼位于tensorflow/models/image/cifar10/.
| 文件 | 作用 |
|---|---|
cifar10_input.py |
讀取本地CIFAR-10的二進制文件格式的內(nèi)容。 |
cifar10.py |
建立CIFAR-10的模型。 |
cifar10_train.py |
在CPU或GPU上訓練CIFAR-10的模型。 |
cifar10_multi_gpu_train.py |
在多GPU上訓練CIFAR-10的模型。 |
cifar10_eval.py |
評估CIFAR-10模型的預測性能。 |
TensorFlow擅長訓練深度神經(jīng)網(wǎng)絡,被認定為是神經(jīng)網(wǎng)絡中最好用的庫之一。通過使用TensorFlow我們可以快速入門神經(jīng)網(wǎng)絡, 大大降低了深度學習(也就是深度神經(jīng)網(wǎng)絡)的開發(fā)成本和開發(fā)難度。
Tensorflow使用數(shù)據(jù)流圖進行數(shù)值計算,圖中的節(jié)點代表數(shù)學運算,圖中的邊代表在這些節(jié)點之間傳遞的多維數(shù)組(張量)。在使用其構建模型時,先搭建好流圖結構——類似于鋪好管道,然后再加載數(shù)據(jù)——向管道中注水,讓數(shù)據(jù)在各節(jié)點計算、沿各管道流動;數(shù)據(jù)在流圖中計算、傳遞時采用多維數(shù)組(張量)的形式,因此在Tensorflow中參與計算的均為數(shù)組數(shù)據(jù)類型。
本文使用Tensorflow構建簡單的CNN圖像多分類模型,其由3個卷積(含激活函數(shù))與池化層、1個扁平層、3個全連接層、1個輸出層構成,示意圖如下所示:
訓練自己的圖片(CNN):https://blog.csdn.net/Missayaaa/article/details/79119839
該數(shù)據(jù)集的頁面:http://www.cs.toronto.edu/~kriz/cifar.html
CIFAR-10和CIFAR-100是帶有標簽的數(shù)據(jù)集,都出自于規(guī)模更大的一個數(shù)據(jù)集,他有八千萬張小圖片(http://groups.csail.mit.edu/vision/TinyImages/。這個是一個大項目,你可以點擊那個big map提交自己的標簽,可以幫助他們訓練讓計算機識別物體的模型)
數(shù)據(jù)的下載:
(共有三個版本:python,matlab,binary version適用于C語言)
http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
http://www.cs.toronto.edu/~kriz/cifar-10-matlab.tar.gz
http://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz
十,CNN在Cifar-10數(shù)據(jù)集上的圖像分類
代碼:
#_*_coding:utf-8_*_
# import cifar10
import cifar10_input
import tensorflow as tf
import numpy as np
import time
max_steps = 3000
batch_size = 128
data_dir = 'cifar10CIFAR-10cifar-10-batches-bin'
def variable_with_weight_loss(shape, stddev, w1):
'''
權重初始化函數(shù)
:param shape: 卷積核參數(shù),格式類似于[5, 5, 3, 32],代表卷積核尺寸(前兩個數(shù)字通道數(shù)和卷積核個數(shù))
:param stddev: 標準差
:param w1: L2正則化的權重參數(shù)
:return: 返回帶有L2正則的初始化的權重參數(shù)
'''
# 截斷產(chǎn)生正態(tài)分布,即產(chǎn)生正態(tài)分布的值與均值的差值大于兩倍的標準差,那就重新寫
var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
if w1 is not None:
# 給權重W加上L2 正則,并用W1 參數(shù)控制L2 Loss大小
weight_loss = tf.multiply(tf.nn.l2_loss(var), w1, name='weight_loss')
# 將weight_loss 存在一個名為 “losses” 的collection里,后面會用到
tf.add_to_collection('losses', weight_loss)
return var
# 我們對圖像進行數(shù)據(jù)增強的操作需要耗費大量CPU時間,因此distorted_inputs使用了16個
# 獨立的線程來加速任務,函數(shù)內(nèi)部會產(chǎn)生線程池,在需要使用時會通過queue進行調(diào)度。
images_train, labels_train = cifar10_input.distorted_inputs(
data_dir=data_dir, batch_size=batch_size
)
# 生成測試數(shù)據(jù)
images_test, labels_test = cifar10_input.inputs(eval_data=True,
data_dir=data_dir,
batch_size=batch_size)
# 數(shù)據(jù)中圖片的尺寸為24*24,即裁剪后的大小,而顏色的通道數(shù)則為3,代表RGB
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])
# 第一個卷積層
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2,
w1=0.0)
kernel1 = tf.nn.conv2d(image_holder, weight1, [1, 1, 1, 1],
padding='SAME')
bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
padding='SAME')
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
# 第二個卷積層,上一層卷積核數(shù)量為64,所以本層卷積核尺寸的第三個維度也需調(diào)整64
# 這里bias值全部初始化為0.1,而不是0,最后調(diào)整了最大池化層和LRN層的順序,先LRN層處理
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, w1=0.0)
kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
padding='SAME')
# 全連接層
reshape = tf.reshape(pool2, [batch_size, -1])
dim = reshape.get_shape()[1].value
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, w1=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
# 全連接層2,和之前很像,只不過其隱含節(jié)點數(shù)下降了一半,只有192個,其他超參數(shù)不變
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, w1=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)
# 最后一層,先建立weight,其正態(tài)分布標準差為上一個隱含層的節(jié)點數(shù)的倒數(shù),并且不計入L2正則
weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, w1=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
logits = tf.add(tf.matmul(local4, weight5), bias5)
上面代碼就完成了整個網(wǎng)絡inference的部分。梳理整個網(wǎng)絡結構可以得到如下表,從上到下,依次是整個卷積神經(jīng)網(wǎng)絡從輸入到輸出的流程。可以觀察到,其實設計CNN主要就是安排卷積層,池化層,全連接層的分布和順序,以及其中超參數(shù)的設計,Trick的使用等。設計性能良好的CNN一定有規(guī)律可循的,但是要想針對某個問題設計最合適的網(wǎng)絡結構,是需要大量實踐摸索的。
完成了模型 inference 部分的構建,接下來計算 CNN的 loss。這里依然使用 cross_entropy,主要注意的是這里我們把softmax的計算和 cross_entropy_loss的計算合并在了一起。
#_*_coding:utf-8_*_
# import cifar10
import cifar10_input
import tensorflow as tf
import numpy as np
import time
max_steps = 3000 #訓練輪數(shù)(每一輪一個batch參與訓練)
batch_size = 128 # batch 大小
data_dir = 'cifar10CIFAR-10cifar-10-batches-bin' # 數(shù)據(jù)目錄
def variable_with_weight_loss(shape, stddev, w1):
'''
權重初始化函數(shù)
:param shape: 卷積核參數(shù),格式類似于[5, 5, 3, 32],代表卷積核尺寸(前兩個數(shù)字通道數(shù)和卷積核個數(shù))
:param stddev: 標準差
:param w1: L2正則化的權重參數(shù)
:return: 返回帶有L2正則的初始化的權重參數(shù)
'''
# 截斷產(chǎn)生正態(tài)分布,即產(chǎn)生正態(tài)分布的值與均值的差值大于兩倍的標準差,那就重新寫
var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
if w1 is not None:
# 給權重W加上L2 正則,并用W1 參數(shù)控制L2 Loss大小
weight_loss = tf.multiply(tf.nn.l2_loss(var), w1, name='weight_loss')
# 將weight_loss 存在一個名為 “losses” 的collection里,后面會用到
tf.add_to_collection('losses', weight_loss)
return var
def loss(logits, labels):
# 類型轉(zhuǎn)換為 tf.int64
labels = tf.cast(labels, tf.int64)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
logits=logits, labels=labels,
name='cross_entropy_per_example'
)
# 計算一個batch中交叉熵的均值
cross_entropy_mean = tf.reduce_mean(cross_entropy,
name='cross_entropy')
# 將交叉熵存在 losses的collection
tf.add_to_collection('losses', cross_entropy_mean)
return tf.add_n(tf.get_collection('losses'), name='total_loss')
# 我們對圖像進行數(shù)據(jù)增強的操作需要耗費大量CPU時間,因此distorted_inputs使用了16個
# 獨立的線程來加速任務,函數(shù)內(nèi)部會產(chǎn)生線程池,在需要使用時會通過queue進行調(diào)度。
# images_train, labels_train = cifar10_input.distorted_inputs(
# data_dir=data_dir, batch_size=batch_size
# )
images_train, labels_train = cifar10_input.distorted_inputs(
batch_size=batch_size
)
# 生成測試數(shù)據(jù),每次執(zhí)行都會生成一個 batch_size 的數(shù)量的測試樣本
# images_test, labels_test = cifar10_input.inputs(eval_data=True,
# data_dir=data_dir,
# batch_size=batch_size)
images_test, labels_test = cifar10_input.inputs(eval_data=True,
batch_size=batch_size)
# 數(shù)據(jù)中圖片的尺寸為24*24,即裁剪后的大小,而顏色的通道數(shù)則為3,代表RGB
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])
# 第一個卷積層
# 第一層權重初始化,產(chǎn)生64個三通道(RGB圖片),尺寸為5*5的卷積核,不帶L2正則(w1=0)
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2,
w1=0.0)
# 對輸出原始圖像進行卷積操作,步長為[1, 1, 1, 1]即將每一個像素點都計算到
kernel1 = tf.nn.conv2d(image_holder, weight1, [1, 1, 1, 1],
padding='SAME')
# 定義第一層的偏置參數(shù),由于有64個卷積核,這里有偏置尺寸為64
bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
# 卷積結果加偏置后采用relu激活
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
# 第一層的池化操作,使用尺寸為3*3,步長為2*2 的池化層進行操作
# 這里的ksize和strides 第一個和第四個數(shù)字一般都是1
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
padding='SAME')
# 用LRN對結果進行處理,使用比較大的值變得更大,比較小的值變得更小,模仿神經(jīng)系統(tǒng)的側抑制機制
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
# 第二個卷積層,上一層卷積核數(shù)量為64,所以本層卷積核尺寸的第三個維度也需調(diào)整64
# 這里bias值全部初始化為0.1,而不是0,最后調(diào)整了最大池化層和LRN層的順序,先LRN層處理
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, w1=0.0)
kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
padding='SAME')
# 全連接層
# 將上面的輸出結果展平,-1代表不確定多大
reshape = tf.reshape(pool2, [batch_size, -1])
# 得到數(shù)據(jù)扁平化后的長度
dim = reshape.get_shape()[1].value
# 建立一個隱含節(jié)點數(shù)為384的全連接層
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, w1=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)
# 全連接層2,和之前很像,只不過其隱含節(jié)點數(shù)下降了一半,只有192個,其他超參數(shù)不變
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, w1=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)
# 最后一層,先建立weight,其正態(tài)分布標準差為上一個隱含層的節(jié)點數(shù)的倒數(shù),并且不計入L2正則
weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, w1=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
# 注意這里,直接是網(wǎng)絡的原始輸出(即wx+b 的形式),沒有加softmax激活
logits = tf.add(tf.matmul(local4, weight5), bias5)
# 將logits節(jié)點和label_holder傳入loss函數(shù)獲得最終的loss
loss = loss(logits, label_holder)
#優(yōu)化器依然選擇 adam Optimizer 學習速率設置為1e-3
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
# 關于tf.nn.in_top_k函數(shù)的用法見http://blog.csdn.net/uestc_c2_403/article/details/73187915
# tf.nn.in_top_k會返回一個[batch_size, classes(類別數(shù))]大小的布爾型張量,記錄是否判斷正確
top_k_op = tf.nn.in_top_k(logits, label_holder, 1)
# 創(chuàng)建默認的session,接著初始化全部模型參數(shù)
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()
# 啟動圖片數(shù)據(jù)增強的線程隊列,這里一共使用了16個線程來進行加速
# 注意:如果不啟動線程,后續(xù)的inference及訓練的操作都是無法開始的
tf.train.start_queue_runners()
for step in range(max_steps):
start_time = time.time()
# 獲得一個batch的訓練數(shù)據(jù)
image_batch, label_batch = sess.run([images_train, labels_train])
# 運行訓練過程并獲得一個batch的total_loss
_, loss_value = sess.run([train_op, loss],
feed_dict={image_holder: image_batch,
label_holder: label_batch})
# 記錄跑一個batch所耗費的時間
duration = time.time() - start_time
# 每10個batch輸出信息
if step % 10 == 0:
# 計算每秒能跑多少個樣本
examples_per_sec = batch_size / duration
# 計算每個batch需要耗費的時間
sec_per_batch = float(duration)
format_str = ('step %d, loss=%.2f(%.1f examples/sec; %.3f sec/batch)')
print(format_str%(step, loss_value, examples_per_sec, sec_per_batch))
num_examples = 10000
import math
num_iter = int(math.ceil(num_examples / batch_size))
true_count = 0
total_sample_count = num_iter * batch_size
step = 0
while step < num_iter:
image_batch, label_batch = sess.run([images_test, labels_test])
predictions = sess.run([top_k_op], feed_dict={
image_holder: image_batch, label_holder: label_batch
})
true_count += np.sum(predictions)
step += 1
precision = true_count / total_sample_count
print('precsion @ 1 = %.3f' %precision)
部分結果如下:
step 0, loss=4.68(136.3 examples/sec; 0.939 sec/batch) step 10, loss=3.66(187.9 examples/sec; 0.681 sec/batch) step 20, loss=3.13(175.1 examples/sec; 0.731 sec/batch) step 30, loss=2.83(181.5 examples/sec; 0.705 sec/batch) step 40, loss=2.47(177.5 examples/sec; 0.721 sec/batch) step 50, loss=2.25(185.2 examples/sec; 0.691 sec/batch) step 60, loss=2.18(196.3 examples/sec; 0.652 sec/batch) step 70, loss=2.12(191.3 examples/sec; 0.669 sec/batch) step 80, loss=1.94(187.4 examples/sec; 0.683 sec/batch) ... ... step 160, loss=1.86(206.4 examples/sec; 0.620 sec/batch) step 170, loss=1.94(207.4 examples/sec; 0.617 sec/batch) step 180, loss=1.85(209.8 examples/sec; 0.610 sec/batch) ... ... step 310, loss=1.60(183.1 examples/sec; 0.699 sec/batch) step 320, loss=1.57(193.3 examples/sec; 0.662 sec/batch)
補充1:特征圖大小的計算
卷積中的特征圖大小計算方式有兩種,分別是“valid”和“same”,卷積和池化都使用。
如果計算方式采用“valid”:
如果計算方式采用“same”:
例題
輸入圖片大小為200×200,依次經(jīng)過一層卷積(kernel size 5×5,padding 1,stride 2),pooling(kernel size 3×3,padding 0,stride 1),又一層卷積(kernel size 3×3,padding 1,stride 1)之后,輸出特征圖大小為:
A. 95
B. 96
C. 97
D. 98
E. 99
F. 100
解析:
(注意:除不盡的計算結果 卷積向下取整,池化向上取整)
卷積層和池化層輸出大小計算公式
假設輸入數(shù)據(jù)維度為W*W(一般長寬一樣大,這樣的話我們只需要計算一個維度即可),filter大小為F*F,步長(stride)為S,padding(需要填充的0的個數(shù),指的是向外擴充的邊緣大小)的像素為P,則:
輸出尺寸 = (輸入尺寸 - filter +2*padding)/stride +1
本題中 1層卷積:(200-5+2*1)/2+1 為99.5 ,取99,則輸出99*99
池化層:(99-3)/1+1為97,輸出為97*97
2層卷積:(97-3+2*1)/1+1 為97,輸出97*97
研究過網(wǎng)絡的話看到stride為1的時候,當kernel為3 padding為1或者kernel為5 padding為2 一看就是卷積前后尺寸不變。
計算GoogLeNet全過程的尺寸也一樣。
補充2:TensorFlow中CNN的兩種padding方法‘SAME’和“VALID”
首先舉個例子,代碼如下:
import tensorflow as tf
x = tf.constant([[1.,2.,3.],[4.,5.,6.]])
print(x) #Tensor("Const:0", shape=(2, 3), dtype=float32)
x = tf.reshape(x,[1,2,3,1])
print(x) #Tensor("Reshape:0", shape=(1, 2, 3, 1), dtype=float32)
valid_pad = tf.nn.max_pool(x,[1,2,2,1],[1,2,2,1],padding='VALID')
same_pad = tf.nn.max_pool(x,[1,2,2,1],[1,2,2,1],padding='SAME')
print(valid_pad) #Tensor("MaxPool:0", shape=(1, 1, 1, 1), dtype=float32)
print(same_pad) #Tensor("MaxPool_1:0", shape=(1, 1, 2, 1), dtype=float32)
可以看出SAME的填充方式比VALID的填充方式多了一列。
讓我們來看看變量x是一個2*3的矩陣,max_pooling窗口為2*2,兩個維度的步長strides=2。
第一次由于窗口可以覆蓋,橙色區(qū)域做max_pooling,沒什么問題如下:
接下來,就是SAME和VALID的區(qū)別所在:由于步長為2,當向右滑動兩步之后,VALID方式發(fā)現(xiàn)余下的窗口不到2*2,所以直接 將第三列舍去,而SAME方式不會把多出的一列丟棄,但是只有一列了,不夠怎么辦?所以會填充!(但是為了不影響原始信息,所以填0)
由上圖所示,兩個padding方式輸出的形狀不同,一個是截斷,一個是填充
參考文獻:https://blog.csdn.net/BaiHuaXiu123
總結
以上是生活随笔為你收集整理的tensorflow学习笔记:卷积神经网络最终笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020国庆节 Angular stru
- 下一篇: Angular 自定义 structur