⑴ 神經網路的全連接層
全連接層(fully connected layers,FC)在整個神經網路中起到「分類器」的作用。
如果說卷積層、池化層和激活函數層等操作是將原始數據映射到隱層特徵空間的話,全連接層將學到的「分布式特徵表示」映射到「樣本標記空間」。
在實際使用中,全連接層可由卷積操作實現:對前層是全連接的全連接層可以轉化為卷積核為1x1的卷積;而前層是卷積層的全連接層可以轉化為卷積核為h*w的全局卷積,h和w分別為前層卷積結果的高和寬。
由於全連接層的參數冗餘(僅全連接層參數就可占整個網路參數80%左右),有些性能優異的網路模型如ResNet和GoogLeNet等均用全局平均池化(global average pooling,GAP)取代全連接層,來融合學到的深度特徵,最後仍用softmax等損失函數作為網路目標函數來指導學習過程。
1、全連接神經網路解析:對n-1層和n層而言,n-1層的任意一個節點,都和第n層所有節點有連接。即第n層的每個節點在進行計算的時候,激活函數的輸入是n-1層所有節點的加權。
2、全連接的神經網路示意圖:
3、「全連接」是一種不錯的模式,但是網路很大的時候,訓練速度回很慢。部分連接就是認為的切斷某兩個節點直接的連接,這樣訓練時計算量大大減小。
⑶ CNN網路簡介
卷積神經網路簡介(Convolutional Neural Networks,簡稱CNN)
卷積神經網路是近年發展起來,並引起廣泛重視的一種高效識別方法。20世紀60年代,Hubel和Wiesel在研究貓腦皮層中用於局部敏感和方向選擇的神經元時發現其獨特的網路結構可以有效地降低反饋神經網路的復雜性,繼而提出了卷積神經網路(Convolutional
Neural
Networks-簡稱CNN)。現在,CNN已經成為眾多科學領域的研究熱點之一,特別是在模式分類領域,由於該網路避免了對圖像的復雜前期預處理,可以直接輸入原始圖像,因而得到了更為廣泛的應用。
K.Fukushima在1980年提出的新識別機是卷積神經網路的第一個實現網路。隨後,更多的科研工作者對該網路進行了改進。其中,具有代表性的研究成果是Alexander和Taylor提出的「改進認知機」,該方法綜合了各種改進方法的優點並避免了耗時的誤差反向傳播。
一般地,CNN的基本結構包括兩層,其一為特徵提取層,每個神經元的輸入與前一層的局部接受域相連,並提取該局部的特徵。一旦該局部特徵被提取後,它與其它特徵間的位置關系也隨之確定下來;其二是特徵映射層,網路的每個計算層由多個特徵映射組成,每個特徵映射是一個平面,平面上所有神經元的權值相等。特徵映射結構採用影響函數核小的sigmoid函數作為卷積網路的激活函數,使得特徵映射具有位移不變性。此外,由於一個映射面上的神經元共享權值,因而減少了網路自由參數的個數。卷積神經網路中的每一個卷積層都緊跟著一個用來求局部平均與二次提取的計算層,這種特有的兩次特徵提取結構減小了特徵解析度。
CNN主要用來識別位移、縮放及其他形式扭曲不變性的二維圖形。由於CNN的特徵檢測層通過訓練數據進行學習,所以在使用CNN時,避免了顯示的特徵抽取,而隱式地從訓練數據中進行學習;再者由於同一特徵映射面上的神經元權值相同,所以網路可以並行學習,這也是卷積網路相對於神經元彼此相連網路的一大優勢。卷積神經網路以其局部權值共享的特殊結構在語音識別和圖像處理方面有著獨特的優越性,其布局更接近於實際的生物神經網路,權值共享降低了網路的復雜性,特別是多維輸入向量的圖像可以直接輸入網路這一特點避免了特徵提取和分類過程中數據重建的復雜度。
1. 神經網路
首先介紹神經網路,這一步的詳細可以參考資源1。簡要介紹下。神經網路的每個單元如下:
其對應的公式如下:
其中,該單元也可以被稱作是Logistic回歸模型。當將多個單元組合起來並具有分層結構時,就形成了神經網路模型。下圖展示了一個具有一個隱含層的神經網路。
其對應的公式如下:
比較類似的,可以拓展到有2,3,4,5,…個隱含層。
神經網路的訓練方法也同Logistic類似,不過由於其多層性,還需要利用鏈式求導法則對隱含層的節點進行求導,即梯度下降+鏈式求導法則,專業名稱為反向傳播。關於訓練演算法,本文暫不涉及。
2 卷積神經網路
在圖像處理中,往往把圖像表示為像素的向量,比如一個1000×1000的圖像,可以表示為一個1000000的向量。在上一節中提到的神經網路中,如果隱含層數目與輸入層一樣,即也是1000000時,那麼輸入層到隱含層的參數數據為1000000×1000000=10^12,這樣就太多了,基本沒法訓練。所以圖像處理要想練成神經網路大法,必先減少參數加快速度。就跟辟邪劍譜似的,普通人練得很挫,一旦自宮後內力變強劍法變快,就變的很牛了。
2.1 局部感知
卷積神經網路有兩種神器可以降低參數數目,第一種神器叫做局部感知野。一般認為人對外界的認知是從局部到全局的,而圖像的空間聯系也是局部的像素聯系較為緊密,而距離較遠的像素相關性則較弱。因而,每個神經元其實沒有必要對全局圖像進行感知,只需要對局部進行感知,然後在更高層將局部的信息綜合起來就得到了全局的信息。網路部分連通的思想,也是受啟發於生物學裡面的視覺系統結構。視覺皮層的神經元就是局部接受信息的(即這些神經元只響應某些特定區域的刺激)。如下圖所示:左圖為全連接,右圖為局部連接。
在上右圖中,假如每個神經元只和10×10個像素值相連,那麼權值數據為1000000×100個參數,減少為原來的千分之一。而那10×10個像素值對應的10×10個參數,其實就相當於卷積操作。
2.2 參數共享
但其實這樣的話參數仍然過多,那麼就啟動第二級神器,即權值共享。在上面的局部連接中,每個神經元都對應100個參數,一共1000000個神經元,如果這1000000個神經元的100個參數都是相等的,那麼參數數目就變為100了。
怎麼理解權值共享呢?我們可以這100個參數(也就是卷積操作)看成是提取特徵的方式,該方式與位置無關。這其中隱含的原理則是:圖像的一部分的統計特性與其他部分是一樣的。這也意味著我們在這一部分學習的特徵也能用在另一部分上,所以對於這個圖像上的所有位置,我們都能使用同樣的學習特徵。
更直觀一些,當從一個大尺寸圖像中隨機選取一小塊,比如說 8×8 作為樣本,並且從這個小塊樣本中學習到了一些特徵,這時我們可以把從這個
8×8 樣本中學習到的特徵作為探測器,應用到這個圖像的任意地方中去。特別是,我們可以用從 8×8
樣本中所學習到的特徵跟原本的大尺寸圖像作卷積,從而對這個大尺寸圖像上的任一位置獲得一個不同特徵的激活值。
如下圖所示,展示了一個33的卷積核在55的圖像上做卷積的過程。每個卷積都是一種特徵提取方式,就像一個篩子,將圖像中符合條件(激活值越大越符合條件)的部分篩選出來。
2.3 多卷積核
上面所述只有100個參數時,表明只有1個100*100的卷積核,顯然,特徵提取是不充分的,我們可以添加多個卷積核,比如32個卷積核,可以學習32種特徵。在有多個卷積核時,如下圖所示:
上圖右,不同顏色表明不同的卷積核。每個卷積核都會將圖像生成為另一幅圖像。比如兩個卷積核就可以將生成兩幅圖像,這兩幅圖像可以看做是一張圖像的不同的通道。如下圖所示,下圖有個小錯誤,即將w1改為w0,w2改為w1即可。下文中仍以w1和w2稱呼它們。
下圖展示了在四個通道上的卷積操作,有兩個卷積核,生成兩個通道。其中需要注意的是,四個通道上每個通道對應一個卷積核,先將w2忽略,只看w1,那麼在w1的某位置(i,j)處的值,是由四個通道上(i,j)處的卷積結果相加然後再取激活函數值得到的。
所以,在上圖由4個通道卷積得到2個通道的過程中,參數的數目為4×2×2×2個,其中4表示4個通道,第一個2表示生成2個通道,最後的2×2表示卷積核大小。
2.4 Down-pooling
在通過卷積獲得了特徵 (features)
之後,下一步我們希望利用這些特徵去做分類。理論上講,人們可以用所有提取得到的特徵去訓練分類器,例如 softmax
分類器,但這樣做面臨計算量的挑戰。例如:對於一個 96X96
像素的圖像,假設我們已經學習得到了400個定義在8X8輸入上的特徵,每一個特徵和圖像卷積都會得到一個 (96 − 8 + 1) × (96 − 8+ 1) = 7921 維的卷積特徵,由於有 400 個特徵,所以每個樣例 (example) 都會得到一個 892 × 400 =3,168,400 維的卷積特徵向量。學習一個擁有超過 3 百萬特徵輸入的分類器十分不便,並且容易出現過擬合 (over-fitting)。
為了解決這個問題,首先回憶一下,我們之所以決定使用卷積後的特徵是因為圖像具有一種「靜態性」的屬性,這也就意味著在一個圖像區域有用的特徵極有可能在另一個區域同樣適用。因此,為了描述大的圖像,一個很自然的想法就是對不同位置的特徵進行聚合統計,例如,人們可以計算圖像一個區域上的某個特定特徵的平均值(或最大值)。這些概要統計特徵不僅具有低得多的維度 (相比使用所有提取得到的特徵),同時還會改善結果(不容易過擬合)。這種聚合的操作就叫做池(pooling),有時也稱為平均池化或者最大池化 (取決於計算池化的方法)。
至此,卷積神經網路的基本結構和原理已經闡述完畢。
2.5 多層卷積
在實際應用中,往往使用多層卷積,然後再使用全連接層進行訓練,多層卷積的目的是一層卷積學到的特徵往往是局部的,層數越高,學到的特徵就越全局化。
3 ImageNet-2010網路結構
ImageNetLSVRC是一個圖片分類的比賽,其訓練集包括127W+張圖片,驗證集有5W張圖片,測試集有15W張圖片。本文截取2010年AlexKrizhevsky的CNN結構進行說明,該結構在2010年取得冠軍,top-5錯誤率為15.3%。值得一提的是,在今年的ImageNetLSVRC比賽中,取得冠軍的GoogNet已經達到了top-5錯誤率6.67%。可見,深度學習的提升空間還很巨大。
下圖即為Alex的CNN結構圖。需要注意的是,該模型採用了2-GPU並行結構,即第1、2、4、5卷積層都是將模型參數分為2部分進行訓練的。在這里,更進一步,並行結構分為數據並行與模型並行。數據並行是指在不同的GPU上,模型結構相同,但將訓練數據進行切分,分別訓練得到不同的模型,然後再將模型進行融合。而模型並行則是,將若干層的模型參數進行切分,不同的GPU上使用相同的數據進行訓練,得到的結果直接連接作為下一層的輸入。
上圖模型的基本參數為:
輸入:224×224大小的圖片,3通道
第一層卷積:5×5大小的卷積核96個,每個GPU上48個。
第一層max-pooling:2×2的核。
第二層卷積:3×3卷積核256個,每個GPU上128個。
第二層max-pooling:2×2的核。
第三層卷積:與上一層是全連接,3*3的卷積核384個。分到兩個GPU上個192個。
第四層卷積:3×3的卷積核384個,兩個GPU各192個。該層與上一層連接沒有經過pooling層。
第五層卷積:3×3的卷積核256個,兩個GPU上個128個。
第五層max-pooling:2×2的核。
第一層全連接:4096維,將第五層max-pooling的輸出連接成為一個一維向量,作為該層的輸入。
第二層全連接:4096維
Softmax層:輸出為1000,輸出的每一維都是圖片屬於該類別的概率。
4 DeepID網路結構
DeepID網路結構是香港中文大學的Sun
Yi開發出來用來學習人臉特徵的卷積神經網路。每張輸入的人臉被表示為160維的向量,學習到的向量經過其他模型進行分類,在人臉驗證試驗上得到了97.45%的正確率,更進一步的,原作者改進了CNN,又得到了99.15%的正確率。
如下圖所示,該結構與ImageNet的具體參數類似,所以只解釋一下不同的部分吧。
上圖中的結構,在最後只有一層全連接層,然後就是softmax層了。論文中就是以該全連接層作為圖像的表示。在全連接層,以第四層卷積和第三層max-pooling的輸出作為全連接層的輸入,這樣可以學習到局部的和全局的特徵。
⑷ 卷積神經網路的 卷積層、激活層、池化層、全連接層
數據輸入的是一張圖片(輸入層),CONV表示卷積層,RELU表示激勵層,POOL表示池化層,Fc表示全連接層
全連接神經網路需要非常多的計算資源才能支撐它來做反向傳播和前向傳播,所以說全連接神經網路可以存儲非常多的參數,如果你給它的樣本如果沒有達到它的量級的時候,它可以輕輕鬆鬆把你給他的樣本全部都記下來,這會出現過擬合的情況。
所以我們應該把神經元和神經元之間的連接的權重個數降下來,但是降下來我們又不能保證它有較強的學習能力,所以這是一個糾結的地方,所以有一個方法就是 局部連接+權值共享 ,局部連接+權值共享不僅權重參數降下來了,而且學習能力並沒有實質的降低,除此之外還有其它的好處,下來看一下,下面的這幾張圖片:
一個圖像的不同表示方式
這幾張圖片描述的都是一個東西,但是有的大有的小,有的靠左邊,有的靠右邊,有的位置不同,但是我們構建的網路識別這些東西的時候應該是同一結果。為了能夠達到這個目的,我們可以讓圖片的不同位置具有相同的權重(權值共享),也就是上面所有的圖片,我們只需要在訓練集中放一張,我們的神經網路就可以識別出上面所有的,這也是 權值共享 的好處。
而卷積神經網路就是局部連接+權值共享的神經網路。
現在我們對卷積神經網路有一個初步認識了,下面具體來講解一下卷積神經網路,卷積神經網路依舊是層級結構,但層的功能和形式做了改變,卷積神經網路常用來處理圖片數據,比如識別一輛汽車:
在圖片輸出到神經網路之前,常常先進行圖像處理,有 三種 常見的圖像的處理方式:
均值化和歸一化
去相關和白化
圖片有一個性質叫做局部關聯性質,一個圖片的像素點影響最大的是它周邊的像素點,而距離這個像素點比較遠的像素點二者之間關系不大。這個性質意味著每一個神經元我們不用處理全局的圖片了(和上一層全連接),我們的每一個神經元只需要和上一層局部連接,相當於每一個神經元掃描一小區域,然後許多神經元(這些神經元權值共享)合起來就相當於掃描了全局,這樣就構成一個特徵圖,n個特徵圖就提取了這個圖片的n維特徵,每個特徵圖是由很多神經元來完成的。
在卷積神經網路中,我們先選擇一個局部區域(filter),用這個局部區域(filter)去掃描整張圖片。 局部區域所圈起來的所有節點會被連接到下一層的 一個節點上 。我們拿灰度圖(只有一維)來舉例:
局部區域
圖片是矩陣式的,將這些以矩陣排列的節點展成了向量。就能更好的看出來卷積層和輸入層之間的連接,並不是全連接的,我們將上圖中的紅色方框稱為filter,它是2*2的,這是它的尺寸,這不是固定的,我們可以指定它的尺寸。
我們可以看出來當前filter是2*2的小窗口,這個小窗口會將圖片矩陣從左上角滑到右下角,每滑一次就會一下子圈起來四個,連接到下一層的一個神經元,然後產生四個權重,這四個權重(w1、w2、w3、w4)構成的矩陣就叫做卷積核。
卷積核是演算法自己學習得到的,它會和上一層計算,比如,第二層的0節點的數值就是局部區域的線性組合(w1 0+w2 1+w3 4+w4 5),即被圈中節點的數值乘以對應的權重後相加。
卷積核計算
卷積操作
我們前面說過圖片不用向量表示是為了保留圖片平面結構的信息。 同樣的,卷積後的輸出若用上圖的向量排列方式則丟失了平面結構信息。 所以我們依然用矩陣的方式排列它們,就得到了下圖所展示的連接,每一個藍色結點連接四個黃色的結點。
卷積層的連接方式
圖片是一個矩陣然後卷積神經網路的下一層也是一個矩陣,我們用一個卷積核從圖片矩陣左上角到右下角滑動,每滑動一次,當然被圈起來的神經元們就會連接下一層的一個神經元,形成參數矩陣這個就是卷積核,每次滑動雖然圈起來的神經元不同,連接下一層的神經元也不同,但是產生的參數矩陣確是一樣的,這就是 權值共享 。
卷積核會和掃描的圖片的那個局部矩陣作用產生一個值,比如第一次的時候,(w1 0+w2 1+w3 4+w4 5),所以,filter從左上到右下的這個過程中會得到一個矩陣(這就是下一層也是一個矩陣的原因),具體過程如下所示:
卷積計算過程
上圖中左邊是圖矩陣,我們使用的filter的大小是3 3的,第一次滑動的時候,卷積核和圖片矩陣作用(1 1+1 0+1 1+0 0+1 1+1 0+0 1+0 0+1 1)=4,會產生一個值,這個值就是右邊矩陣的第一個值,filter滑動9次之後,會產生9個值,也就是說下一層有9個神經元,這9個神經元產生的值就構成了一個矩陣,這矩陣叫做特徵圖,表示image的某一維度的特徵,當然具體哪一維度可能並不知道,可能是這個圖像的顏色,也有可能是這個圖像的輪廓等等。
單通道圖片總結 :以上就是單通道的圖片的卷積處理,圖片是一個矩陣,我們用指定大小的卷積核從左上角到右下角來滑動,每次滑動所圈起來的結點會和下一層的一個結點相連,連接之後就會形成局部連接,每一條連接都會產生權重,這些權重就是卷積核,所以每次滑動都會產生一個卷積核,因為權值共享,所以這些卷積核都是一樣的。卷積核會不斷和當時卷積核所圈起來的局部矩陣作用,每次產生的值就是下一層結點的值了,這樣多次產生的值組合起來就是一個特徵圖,表示某一維度的特徵。也就是從左上滑動到右下這一過程中會形成一個特徵圖矩陣(共享一個卷積核),再從左上滑動到右下又會形成另一個特徵圖矩陣(共享另一個卷積核),這些特徵圖都是表示特徵的某一維度。
三個通道的圖片如何進行卷積操作?
至此我們應該已經知道了單通道的灰度圖是如何處理的,實際上我們的圖片都是RGB的圖像,有三個通道,那麼此時圖像是如何卷積的呢?
彩色圖像
filter窗口滑的時候,我們只是從width和height的角度來滑動的,並沒有考慮depth,所以每滑動一次實際上是產生一個卷積核,共享這一個卷積核,而現在depth=3了,所以每滑動一次實際上產生了具有三個通道的卷積核(它們分別作用於輸入圖片的藍色、綠色、紅色通道),卷積核的一個通道核藍色的矩陣作用產生一個值,另一個和綠色的矩陣作用產生一個值,最後一個和紅色的矩陣作用產生一個值,然後這些值加起來就是下一層結點的值,結果也是一個矩陣,也就是一張特徵圖。
三通道的計算過程
要想有多張特徵圖的話,我們可以再用新的卷積核來進行左上到右下的滑動,這樣就會形成 新的特徵圖 。
三通道圖片的卷積過程
也就是說增加一個卷積核,就會產生一個特徵圖,總的來說就是輸入圖片有多少通道,我們的卷積核就需要對應多少通道,而本層中卷積核有多少個,就會產生多少個特徵圖。這樣卷積後輸出可以作為新的輸入送入另一個卷積層中處理,有幾個特徵圖那麼depth就是幾,那麼下一層的每一個特徵圖就得用相應的通道的卷積核來對應處理,這個邏輯要清楚,我們需要先了解一下 基本的概念:
卷積計算的公式
4x4的圖片在邊緣Zero padding一圈後,再用3x3的filter卷積後,得到的Feature Map尺寸依然是4x4不變。
填充
當然也可以使用5x5的filte和2的zero padding可以保持圖片的原始尺寸,3x3的filter考慮到了像素與其距離為1以內的所有其他像素的關系,而5x5則是考慮像素與其距離為2以內的所有其他像素的關系。
規律: Feature Map的尺寸等於
(input_size + 2 * padding_size − filter_size)/stride+1
我們可以把卷積層的作用 總結一點: 卷積層其實就是在提取特徵,卷積層中最重要的是卷積核(訓練出來的),不同的卷積核可以探測特定的形狀、顏色、對比度等,然後特徵圖保持了抓取後的空間結構,所以不同卷積核對應的特徵圖表示某一維度的特徵,具體什麼特徵可能我們並不知道。特徵圖作為輸入再被卷積的話,可以則可以由此探測到"更大"的形狀概念,也就是說隨著卷積神經網路層數的增加,特徵提取的越來越具體化。
激勵層的作用可以理解為把卷積層的結果做 非線性映射 。
激勵層
上圖中的f表示激勵函數,常用的激勵函數幾下幾種:
常用的激勵函數
我們先來看一下激勵函數Sigmoid導數最小為0,最大為1/4,
激勵函數Sigmoid
Tanh激活函數:和sigmoid相似,它會關於x軸上下對應,不至於朝某一方面偏向
Tanh激活函數
ReLU激活函數(修正線性單元):收斂快,求梯度快,但較脆弱,左邊的梯度為0
ReLU激活函數
Leaky ReLU激活函數:不會飽和或者掛掉,計算也很快,但是計算量比較大
Leaky ReLU激活函數
一些激勵函數的使用技巧 :一般不要用sigmoid,首先試RELU,因為快,但要小心點,如果RELU失效,請用Leaky ReLU,某些情況下tanh倒是有不錯的結果。
這就是卷積神經網路的激勵層,它就是將卷積層的線性計算的結果進行了非線性映射。可以從下面的圖中理解。它展示的是將非線性操作應用到一個特徵圖中。這里的輸出特徵圖也可以看作是"修正"過的特徵圖。如下所示:
非線性操作
池化層:降低了各個特徵圖的維度,但可以保持大分重要的信息。池化層夾在連續的卷積層中間,壓縮數據和參數的量,減小過擬合,池化層並沒有參數,它只不過是把上層給它的結果做了一個下采樣(數據壓縮)。下采樣有 兩種 常用的方式:
Max pooling :選取最大的,我們定義一個空間鄰域(比如,2x2 的窗口),並從窗口內的修正特徵圖中取出最大的元素,最大池化被證明效果更好一些。
Average pooling :平均的,我們定義一個空間鄰域(比如,2x2 的窗口),並從窗口內的修正特徵圖算出平均值
Max pooling
我們要注意一點的是:pooling在不同的depth上是分開執行的,也就是depth=5的話,pooling進行5次,產生5個池化後的矩陣,池化不需要參數控制。池化操作是分開應用到各個特徵圖的,我們可以從五個輸入圖中得到五個輸出圖。
池化操作
無論是max pool還是average pool都有分信息被舍棄,那麼部分信息被舍棄後會損壞識別結果嗎?
因為卷積後的Feature Map中有對於識別物體不必要的冗餘信息,我們下采樣就是為了去掉這些冗餘信息,所以並不會損壞識別結果。
我們來看一下卷積之後的冗餘信息是怎麼產生的?
我們知道卷積核就是為了找到特定維度的信息,比如說某個形狀,但是圖像中並不會任何地方都出現這個形狀,但卷積核在卷積過程中沒有出現特定形狀的圖片位置卷積也會產生一個值,但是這個值的意義就不是很大了,所以我們使用池化層的作用,將這個值去掉的話,自然也不會損害識別結果了。
比如下圖中,假如卷積核探測"橫折"這個形狀。 卷積後得到3x3的Feature Map中,真正有用的就是數字為3的那個節點,其餘數值對於這個任務而言都是無關的。 所以用3x3的Max pooling後,並沒有對"橫折"的探測產生影響。 試想在這里例子中如果不使用Max pooling,而讓網路自己去學習。 網路也會去學習與Max pooling近似效果的權重。因為是近似效果,增加了更多的參數的代價,卻還不如直接進行最大池化處理。
最大池化處理
在全連接層中所有神經元都有權重連接,通常全連接層在卷積神經網路尾部。當前面卷積層抓取到足以用來識別圖片的特徵後,接下來的就是如何進行分類。 通常卷積網路的最後會將末端得到的長方體平攤成一個長長的向量,並送入全連接層配合輸出層進行分類。比如,在下面圖中我們進行的圖像分類為四分類問題,所以卷積神經網路的輸出層就會有四個神經元。
四分類問題
我們從卷積神經網路的輸入層、卷積層、激活層、池化層以及全連接層來講解卷積神經網路,我們可以認為全連接層之間的在做特徵提取,而全連接層在做分類,這就是卷積神經網路的核心。
⑸ 卷積神經網路(CNN)基礎
在七月初七情人節,牛郎織女相見的一天,我終於學習了CNN(來自CS231n),感覺感觸良多,所以趕快記下來,別忘了,最後祝大家情人節快樂5555555.正題開始!
CNN一共有卷積層(CONV)、ReLU層(ReLU)、池化層(Pooling)、全連接層(FC(Full Connection))下面是各個層的詳細解釋。
卷積,尤其是圖像的卷積,需要一個濾波器,用濾波器對整個圖像進行遍歷,我們假設有一個32*32*3的原始圖像A,濾波器的尺寸為5*5*3,用w表示,濾波器中的數據就是CNN的參數的一部分,那麼在使用濾波器w對A進行濾波的話,可以用下面的式子表示:
其中x為原始圖像的5*5*3的一部分,b是偏置項置為1。在對A進行濾波之後,產生的是一個28*28*1的數據。那麼假設我們存在6個濾波器,這六個濾波器之間彼此是獨立的,也就是他們內部的數據是不同的且沒有相關性的。可以理解為一個濾波器查找整幅圖像的垂直邊緣,一個查找水平邊緣,一個查找紅色,一個查找黑色這樣。那麼我就可以產生6個28*28*1的數據,將它們組合到一起就可以產生28*28*6的數據,這就是卷積層主要做的工作。
CNN可以看作一系列的卷積層和ReLU層對原始數據結構進行處理的神經網路,處理的過程可以用下面這幅圖表示
特別要注意的是濾波器的深度一定要與上一層傳來的數據的深度相同,就像上圖的第二個卷積層在處理傳來的28*28*6的數據時要使用5*5*6的濾波器.
濾波器在圖像上不斷移動對圖像濾波,自然存在步長的問題,在上面我們舉的例子都是步長為1的情況,如果步長為3的話,32*32*3的圖像經過5*5*3的濾波器卷積得到的大小是(32-5)/3+1=10, 註:步長不能為2因為(32-5)/2+1=14.5是小數。
所以當圖像大小是N,濾波器尺寸為F時,步長S,那麼卷積後大小為(N-F)/S+1
我們從上面的圖中可以看到圖像的長和寬在逐漸的減小,在經過超過5層之後極可能只剩下1*1的空間尺度,這樣是十分不好的,而且也不利於我們接下來的計算,所以我們想讓卷積層處理完之後圖像在空間尺度上大小不變,所以我們引入了pad the border的操作。pad其實就是在圖像周圍補0,擴大圖像的尺寸,使得卷積後圖像大小不變。在CNN中,主要存在4個超參數,濾波器個數K,濾波器大小F,pad大小P和步長S,其中P是整數,當P=1時,對原始數據的操作如圖所示:
那麼在pad操作後卷積後的圖像大小為:(N-F+2*P)/S+1
而要想讓卷積層處理後圖像空間尺度不變,P的值可以設為P=(F-1)/2
卷積層輸入W 1 *H 1 *D 1 大小的數據,輸出W 2 *H 2 *D 2 的數據,此時的卷積層共有4個超參數:
K:濾波器個數
P:pad屬性值
S:濾波器每次移動的步長
F:濾波器尺寸
此時輸出的大小可以用輸入和超參計算得到:
W 2 =(W 1 -F+2P)/S+1
H 2 =(H 1 -F+2P)/S+1
D 2 =D 1
1*1的濾波器也是有意義的,它在深度方向做卷積,例如1*1*64的濾波器對56*56*64的數據卷積得到56*56的數據
F通常是奇數,這樣可以綜合考慮上下左右四個方向的數據。
卷積層從神經元的角度看待可以有兩個性質: 參數共享和局域連接 。對待一個濾波器,例如5*5*3的一個濾波器,對32*32*3的數據卷積得到28*28的數據,可以看作存在28*28個神經元,每個對原圖像5*5*3的區域進行計算,這28*28個神經元由於使用同一個濾波器,所以參數相同,我們稱這一特性為 參數共享 。
針對不同的濾波器,我們可以看到他們會看到同一區域的圖像,相當於在深度方向存在多個神經元,他們看著相同區域叫做 局域連接
參數共享減少了參數的數量,防止了過擬合
局域連接為查找不同特徵更豐富的表現圖像提供了可能。
卷積就像是對原圖像的另一種表達。
激活函數,對於每一個維度經過ReLU函數輸出即可。不改變數據的空間尺度。
通過pad操作,輸出圖像在控制項上並沒有變化,但是深度發生了變化,越來越龐大的數據給計算帶來了困難,也出現了冗餘的特徵,所以需要進行池化操作,池化不改變深度,只改變長寬,主要有最大值和均值兩種方法,一般的池化濾波器大小F為2步長為2,對於最大值池化可以用下面的圖像清晰的表示:
卷積層輸入W 1 *H 1 *D 1 大小的數據,輸出W 2 *H 2 *D 2 的數據,此時的卷積層共有2個超參數:
S:濾波器每次移動的步長
F:濾波器尺寸
此時輸出的大小可以用輸入和超參計算得到:
W 2 =(W 1 -F)/S+1
H 2 =(H 1 -F)/S+1
D 2 =D 1
將最後一層(CONV、ReLU或Pool)處理後的數據輸入全連接層,對於W 2 *H 2 *D 2 數據,我們將其展成1*1*W 2 *H 2 *D 2 大小的數據,輸入層共有W 2 *H 2 *D 2 個神經元,最後根據問題確定輸出層的規模,輸出層可以用softmax表示。也就是說,全連接層就是一個常見的BP神經網路。而這個網路也是參數最多的部分,是接下來想要去掉的部分。完整的神經網路可以用下面的圖表示:
[(CONV-ReLU)*N-POOL?]*M-(FC-RELU)*K,SoftMax
1.更小的濾波器與更深的網路
2.只有CONV層而去掉池化與全鏈接
最早的CNN,用於識別郵編,結構為:
CONV-POOL-CONV-POOL-CONV-FC
濾波器大小5*5,步長為1,池化層2*2,步長為2
2012年由於GPU技術所限,原始AlexNet為兩個GPU分開計算,這里介紹合起來的結構。
輸入圖像為227*227*3
1.首次使用ReLU
2.使用Norm layers,現在已經拋棄,因為效果不大
3.數據經過預處理(例如大小變化,顏色變化等)
4.失活比率0.5
5.batch size 128
6.SGD Momentum 參數0.9(SGD和Momentum見我的其他文章)
7.學習速率 0.01,准確率不在提升時減少10倍,1-2次後達到收斂
8.L2權重減少0.0005
9.錯誤率15.4%
改進自AlexNet,主要改變:
1.CONV1的濾波器從11*11步長S=4改為7*7步長為2.
2.CONV3,4,5濾波器數量有384,384,256改為512,1024,512(濾波器數量為2的n次冪有利於計算機計算可以提高效率)
錯誤率:14.8%後繼續改進至11.2%
當前最好的最易用的CNN網路,所有卷積層濾波器的大小均為3*3,步長為1,pad=1,池化層為2*2的最大值池化,S=2。
主要參數來自全連接層,這也是想要去掉FC的原因。
具有高度的統一性和線性的組合,易於理解,十分方便有VGG-16,VGG-19等多種結構。
錯誤率7.3%
完全移除FC層,參數只有500萬,使用Inception模塊(不太理解,有時間繼續看)
准確率6.67%
准確率3.6%
擁有極深的網路結構,且越深准確率越高。是傳統CNN不具備的特點,傳統CNN並非越深越准確。需要訓練時間較長但是快於VGG
1.每個卷積層使用Batch Normalization
2.Xavier/2初始化
3.SGD+Momentum(0.9)
4.Learning rate:0.1,准確率不變減小10倍(因為Batch Normalization所以比AlexNet大)
5.mini-batch size 256
6.Weight decay of 0.00001
7.不適用失活(因為Batch Normalization)
具體的梯度過程學完ResNet再說吧。
⑹ LeNet神經網路
LeNet神經網路由深度學習三巨頭之一的Yan LeCun提出,他同時也是卷積神經網路 (CNN,Convolutional Neural Networks)之父。LeNet主要用來進行手寫字元的識別與分類,並在美國的銀行中投入了使用。LeNet的實現確立了CNN的結構,現在神經網路中的許多內容在LeNet的網路結構中都能看到,例如卷積層,Pooling層,ReLU層。雖然LeNet早在20世紀90年代就已經提出了,但由於當時缺乏大規模的訓練數據,計算機硬體的性能也較低,因此LeNet神經網路在處理復雜問題時效果並不理想。雖然LeNet網路結構比較簡單,但是剛好適合神經網路的入門學習。
LeNet的神經網路結構圖如下:
LeNet網路的執行流程圖如下:
接下來我們來具體的一層層的分析LeNet的網路結構。首先要了解圖像(輸入數據)的表示。在LeNet網路中,輸入圖像是手寫字元,圖像的表示形式為二維數據矩陣,如下念老圖所示:
LeNet網路除去輸入輸出層總共有六層網路。第一層是卷積層(C1層),卷積核的大小為 5*5 ,卷積核數量為 6 個,輸入圖像的大小為 32*32 ,因此輸入數據在進行第一層卷積之後,輸出結果為大小為 28*28 ,數量為 6 個的feature map。卷積操作如下面兩幅圖所示:
卷積操作的過程可描述為:卷積核在圖像上滑動,滑動步長為1(即每次移動一格,水平方向從左到右,到最右邊之後再從最左邊開始,向下移動一格,重復從左到右滑動),當卷積核與圖像的一個局部塊重合時進行卷積運行,卷積計算方式為圖像塊對應位置的數與卷積核對應位置的數相乘,然後將所有相乘結果相加即為feature map的值, 相乘累加之後的結果位於卷積核中心點的位置 ,因此如果是 3*3 的卷積核,feature map比原圖像在水平和垂直方向上分別減少兩行(上下各一行)和兩列(左右各一列),因此上面圖像原圖為 5*5 ,卷積核為 3*3 ,卷積結果大小為 3*3 ,即 (5-2)*(5-2) ,如果卷積核為 5*5 ,則卷積結果大小為 (5-4)*(5-4) 。上圖中的卷積核為:
由於神經網路層與層的結構是通過連接來實現的,因此輸入層與第一個卷積層的連接數量應為 (32-2-2)*(32-2-2)*(5*5+1)*6= 28*28*156 =122304 。
卷積的作用主要是:通過卷積運算,可以使原信號特徵增強,並且降低噪音。在圖像上卷積之後主要是減少圖像雜訊,提取圖像的特徵。例如sobel運算元就是一種卷積運算,主要是提友喊取圖像的邊緣特徵。卷積網路能很好地適應圖像的平移不變性:例如稍稍移動一幅貓的圖像,它仍然是一幅貓的圖像。卷積操作保留了圖像塊之間的空間信息,進行卷積操作的圖像塊之間的相對位置關系沒有改變。圖像在不同卷積仔告升核上進行卷積之後的效果圖如下:
圖像在LeNet網路上進行第一層卷積之後,結果為大小為 28*28 ,數量為 6 個的feature map。LeNet網路的第二層為pooling層(S2層),也稱為下采樣。在圖像處理中,下采樣之後,圖像的大小會變為原來的 1/4 ,即水平方向和垂直方向上圖像大小分別減半。Pooling有多種,這里主要介紹兩種,max-pooling和average-pooling。max-pooling即為從四個元素中選取一個最大的來表示這四個元素,average-pooling則用四個元素的平均值來表示這四個元素。Pooling示意圖如下:
在LeNet在進行第二層Pooling運算後,輸出結果為 14*14 的 6 個feature map。其連接數為 (2*2+1) * 14 * 14 *6 = 5880 。Pooling層的主要作用就是減少數據,降低數據緯度的同時保留最重要的信息。在數據減少後,可以減少神經網路的緯度和計算量,可以防止參數太多過擬合。LeNet在這一層是將四個元素相加,然後乘以參數w再加上偏置b,然後計算sigmoid值。
LeNet第三層(C3層)也是卷積層,卷積核大小仍為 5*5 ,不過卷積核的數量變為 16 個。第三層的輸入為 14*14 的 6 個feature map,卷積核大小為 5*5 ,因此卷積之後輸出的feature map大小為 10*10 ,由於卷積核有 16 個,因此希望輸出的feature map也為 16 個,但由於輸入有 6 個feature map,因此需要進行額外的處理。輸入的 6 個feature map與輸出的 16 個feature map的關系圖如下:
如上圖所示,第一個卷積核處理前三幅輸入的feature map,得出一個新的feature map。
上一層卷積運算之後,結果為大小為 10*10 的 16 個feature map,因此在第四層(S4層)進行pooling運算之後,輸出結果為 16 個大小為 5*5 的feature map。與S2層進行同樣的操作。
LeNet第五層是卷積層(C5層),卷積核數目為120個,大小為 5*5 ,由於第四層輸出的feature map大小為 5*5 ,因此第五層也可以看成全連接層,輸出為120個大小為 1*1 的feature map。
LeNet第六層是全連接層(F6層),有84個神經元(84與輸出層的設計有關),與C5層全連接。
LeNet神經網路結構在Caffe中的配置文件如下:
參考資料:
1. https://ujjwalkarn.me/2016/08/11/intuitive-explanation-convnets/
⑺ CNN之Lenet5
LeNet誕生於 1994 年,是最早的卷積神經網路之一,並且推動了深度學習領域的發展。
LeNet-5是Yann LeCun等人在多次研究後提出的最終卷積神經網路結構,主要用於手寫數字識別
LeNet5的網路結構如下所示:
LeNet-5包含七層,不包括輸入,每一層都包含可訓練參數(權重),當時使用的輸入數據是32*32像素的圖像。下面逐層介紹LeNet-5的結構,並且,卷積層將用Cx表示,子采樣層則被標記為Sx,全連接層被標記為Fx,其中x是層索引。
該層使用了6個卷積核,每個卷積核的大小為5×5,這樣就得到了6個feature map(特徵圖)。
每個卷積核(5×5)與原始的輸入圖像(32×32)進行卷積,這樣得到的feature map(特徵圖)大小為(32-5+1)×(32-5+1)= 28×28
卷積核與輸入圖像按卷積核大小逐個區域進行匹配計算,匹配後原始輸入圖像的尺寸將變小,因為邊緣部分卷積核無法越出界,只能匹配一次,匹配計算後的尺寸變為Cr×Cc=(Ir-Kr+1)×(Ic-Kc+1),其中Cr、Cc,Ir、Ic,Kr、Kc分別表示卷積後結果圖像、輸入圖像、卷積核的行列大小。
由於參數(權值)共享的原因,對於同個卷積核每個神經元均使用相同的參數,因此,參數個數為(5×5+1)×6= 156,其中5×5為卷積核參數,1為偏置參數
卷積後的圖像大小為28×28,因此每個特徵圖有28×28個神經元,每個卷積核參數為(5×5+1)×6,因此,該層的連接數為(5×5+1)×6×28×28=122304
這一層主要是做池化或者特徵映射(特徵降維),池化單元為2×2,因此,6個特徵圖的大小經池化後即變為14×14。池化單元之間沒有重疊,在池化區域內進行聚合統計後得到新的特徵值,因此經2×2池化後,每兩行兩列重新算出一個特徵值出來,相當於圖像大小減半,因此卷積後的28×28圖像經2×2池化後就變為14×14。
這一層的計算過程是:2×2 單元里的值相加,然後再乘以訓練參數w,再加上一個偏置參數b(每一個特徵圖共享相同的w和b),然後取sigmoid值(S函數:0-1區間),作為對應的該單元的值。卷積操作與池化的示意圖如下:
S2層由於每個特徵圖都共享相同的w和b這兩個參數,因此需要2×6=12個參數
下采樣之後的圖像大小為14×14,因此S2層的每個特徵圖有14×14個神經元,每個池化單元連接數為2×2+1(1為偏置量),因此,該層的連接數為(2×2+1)×14×14×6 = 5880
C3層有16個卷積核,卷積模板大小為5×5。
與C1層的分析類似,C3層的特徵圖大小為(14-5+1)×(14-5+1)= 10×10
需要注意的是,C3與S2並不是全連接而是部分連接,有些是C3連接到S2三層、有些四層、甚至達到6層,通過這種方式提取更多特徵,連接的規則如下表所示:
例如第一列表示C3層的第0個特徵圖(feature map)只跟S2層的第0、1和2這三個feature maps相連接,計算過程為:用3個卷積模板分別與S2層的3個feature maps進行卷積,然後將卷積的結果相加求和,再加上一個偏置,再取sigmoid得出卷積後對應的feature map了。其它列也是類似(有些是3個卷積模板,有些是4個,有些是6個)。因此,C3層的參數數目為(5×5×3+1)×6 +(5×5×4+1)×9 +5×5×6+1 = 1516
卷積後的特徵圖大小為10×10,參數數量為1516,因此連接數為1516×10×10= 151600
與S2的分析類似,池化單元大小為2×2,因此,該層與C3一樣共有16個特徵圖,每個特徵圖的大小為5×5。
與S2的計算類似,所需要參數個數為16×2 = 32
連接數為(2×2+1)×5×5×16 = 2000
該層有120個卷積核,每個卷積核的大小仍為5×5,因此有120個特徵圖。由於S4層的大小為5×5,而該層的卷積核大小也是5×5,因此特徵圖大小為(5-5+1)×(5-5+1)= 1×1。這樣該層就剛好變成了全連接,這只是巧合,如果原始輸入的圖像比較大,則該層就不是全連接了。
與前面的分析類似,本層的參數數目為120×(5×5×16+1) = 48120
由於該層的特徵圖大小剛好為1×1,因此連接數為48120×1×1=48120
F6層有84個單元,之所以選這個數字的原因是來自於輸出層的設計,對應於一個7×12的比特圖,如下圖所示,-1表示白色,1表示黑色,這樣每個符號的比特圖的黑白色就對應於一個編碼。
該層有84個特徵圖,特徵圖大小與C5一樣都是1×1,與C5層全連接。
由於是全連接,參數數量為(120+1)×84=10164。跟經典神經網路一樣,F6層計算輸入向量和權重向量之間的點積,再加上一個偏置,然後將其傳遞給sigmoid函數得出結果。
由於是全連接,連接數與參數數量一樣,也是10164。
Output層也是全連接層,共有10個節點,分別代表數字0到9。如果第i個節點的值為0,則表示網路識別的結果是數字i。
該層採用徑向基函數(RBF)的網路連接方式,假設x是上一層的輸入,y是RBF的輸出,則RBF輸出的計算方式是:
上式中的Wij的值由i的比特圖編碼確定,i從0到9,j取值從0到7×12-1。RBF輸出的值越接近於0,表示當前網路輸入的識別結果與字元i越接近。
由於是全連接,參數個數為84×10=840
由於是全連接,連接數與參數個數一樣,也是840
from skimage import io,transform
import os
import glob
import numpy as np
import tensorflow as tf
#將所有的圖片重新設置尺寸為32*32
w = 32
h = 32
c = 1
#mnist數據集中訓練數據和測試數據保存地址
train_path = "E:/data/datasets/mnist/train/"
test_path = "E:/data/datasets/mnist/test/"
#讀取圖片及其標簽函數
'''os.listdir()返回指定的文件夾包含的文件或文件夾的名字,存放於一個列表中;os.path.isdir()判斷某一路徑是否為目錄
enumerate()將一個可遍歷的數據對象(如列表、元組或字元串)組合為一個索引序列,數據下標和相應數據'''
def read_image(path):
label_dir = [path+x for x in os.listdir(path) if os.path.isdir(path+x)]
images = []
labels = []
for index,folder in enumerate(label_dir):
for img in glob.glob(folder+'/*.png'):
print("reading the image:%s"%img)
image = io.imread(img)
image = transform.resize(image,(w,h,c))
images.append(image)
labels.append(index)
return np.asarray(images,dtype=np.float32),np.asarray(labels,dtype=np.int32)
#讀取訓練數據及測試數據
train_data,train_label = read_image(train_path)
test_data,test_label = read_image(test_path)
#打亂訓練數據及測試數據 np.arange()返回一個有終點和起點的固定步長的排列,
train_image_num = len(train_data)
train_image_index = np.arange(train_image_num) ##起始點0,結束點train_image_num,步長1,返回類型array,一維
np.random.shuffle(train_image_index)
train_data = train_data[train_image_index]
train_label = train_label[train_image_index]
test_image_num = len(test_data)
test_image_index = np.arange(test_image_num)
np.random.shuffle(test_image_index)
test_data = test_data[test_image_index]
test_label = test_label[test_image_index]
#搭建CNN 此函數可以理解為形參,用於定義過程,在執行的時候再賦具體的值,形參名X,y_
x = tf.placeholder(tf.float32,[None,w,h,c],name='x')
y_ = tf.placeholder(tf.int32,[None],name='y_')
def inference(input_tensor,train,regularizer):
#第一層:卷積層,過濾器的尺寸為5×5,深度為6,不使用全0補充,步長為1。
#尺寸變化:32×32×1->28×28×6
'''參數的初始化:tf.truncated_normal_initializer()或者簡寫為tf.TruncatedNormal()、tf.RandomNormal() 去掉_initializer,大寫首字母即可
生成截斷正態分布的隨機數,這個初始化方法好像在tf中用得比較多mean=0.0, stddev=1.0 正態分布
http://www.mamicode.com/info-detail-1835147.html'''
with tf.variable_scope('layer1-conv1'):
conv1_weights = tf.get_variable('weight',[5,5,c,6],initializer=tf.truncated_normal_initializer(stddev=0.1))
conv1_biases = tf.get_variable('bias',[6],initializer=tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding='VALID')
relu1 = tf.nn.relu(tf.nn.bias_add(conv1,conv1_biases))
#第二層:池化層,過濾器的尺寸為2×2,使用全0補充,步長為2。
#尺寸變化:28×28×6->14×14×6
with tf.name_scope('layer2-pool1'):
pool1 = tf.nn.max_pool(relu1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#第三層:卷積層,過濾器的尺寸為5×5,深度為16,不使用全0補充,步長為1。
#尺寸變化:14×14×6->10×10×16
with tf.variable_scope('layer3-conv2'):
conv2_weights = tf.get_variable('weight',[5,5,6,16],initializer=tf.truncated_normal_initializer(stddev=0.1))
conv2_biases = tf.get_variable('bias',[16],initializer=tf.constant_initializer(0.0))
conv2 = tf.nn.conv2d(pool1,conv2_weights,strides=[1,1,1,1],padding='VALID')
relu2 = tf.nn.relu(tf.nn.bias_add(conv2,conv2_biases))
#第四層:池化層,過濾器的尺寸為2×2,使用全0補充,步長為2。
#尺寸變化:10×10×6->5×5×16
with tf.variable_scope('layer4-pool2'):
pool2 = tf.nn.max_pool(relu2,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#將第四層池化層的輸出轉化為第五層全連接層的輸入格式。第四層的輸出為5×5×16的矩陣,然而第五層全連接層需要的輸入格式
#為向量,所以我們需要把代表每張圖片的尺寸為5×5×16的矩陣拉直成一個長度為5×5×16的向量。
#舉例說,每次訓練64張圖片,那麼第四層池化層的輸出的size為(64,5,5,16),拉直為向量,nodes=5×5×16=400,尺寸size變為(64,400)
pool_shape = pool2.get_shape().as_list()
nodes = pool_shape[1]*pool_shape[2]*pool_shape[3]
reshaped = tf.reshape(pool2,[-1,nodes])
#第五層:全連接層,nodes=5×5×16=400,400->120的全連接
#尺寸變化:比如一組訓練樣本為64,那麼尺寸變化為64×400->64×120
#訓練時,引入dropout,dropout在訓練時會隨機將部分節點的輸出改為0,dropout可以避免過擬合問題。
#這和模型越簡單越不容易過擬合思想一致,和正則化限制權重的大小,使得模型不能任意擬合訓練數據中的隨機雜訊,以此達到避免過擬合思想一致。
#本文最後訓練時沒有採用dropout,dropout項傳入參數設置成了False,因為訓練和測試寫在了一起沒有分離,不過大家可以嘗試。
'''tf.matmul()這個函數是專門矩陣或者tensor乘法,而不是矩陣元素對應元素相乘
tf.multiply()兩個矩陣中對應元素各自相乘
tf.nn.dropout(x, keep_prob):TensorFlow裡面為了防止或減輕過擬合而使用的函數,它一般用在全連接層,
x:指輸入;keep_prob: 設置神經元被選中的概率,使輸入tensor中某些元素變為0,其它沒變0的元素變為原來的1/keep_prob大小,可以想像下,比如某些元素棄用
在初始化時keep_prob是一個佔位符,keep_prob = tf.placeholder(tf.float32).
tensorflow在run時設置keep_prob具體的值,例如keep_prob: 0.5,train的時候才是dropout起作用的時候
keep_prob: A scalar Tensor with the same type as x. The probability that each element is kept.'''
with tf.variable_scope('layer5-fc1'):
fc1_weights = tf.get_variable('weight',[nodes,120],initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None:
tf.add_to_collection('losses',regularizer(fc1_weights))
fc1_biases = tf.get_variable('bias',[120],initializer=tf.constant_initializer(0.1))
fc1 = tf.nn.relu(tf.matmul(reshaped,fc1_weights) + fc1_biases)
if train:
fc1 = tf.nn.dropout(fc1,0.5)
#第六層:全連接層,120->84的全連接
#尺寸變化:比如一組訓練樣本為64,那麼尺寸變化為64×120->64×84
'''tf.add_to_collection:把變數放入一個集合,把很多變數變成一個列表
tf.get_collection:從一個結合中取出全部變數,是一個列表
tf.add_n:把一個列表的東西都依次加起來'''
with tf.variable_scope('layer6-fc2'):
fc2_weights = tf.get_variable('weight',[120,84],initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None:
tf.add_to_collection('losses',regularizer(fc2_weights))
fc2_biases = tf.get_variable('bias',[84],initializer=tf.truncated_normal_initializer(stddev=0.1))
fc2 = tf.nn.relu(tf.matmul(fc1,fc2_weights) + fc2_biases)
if train:
fc2 = tf.nn.dropout(fc2,0.5)
#第七層:全連接層(近似表示),84->10的全連接
#尺寸變化:比如一組訓練樣本為64,那麼尺寸變化為64×84->64×10。最後,64×10的矩陣經過softmax之後就得出了64張圖片分類於每種數字的概率,
#即得到最後的分類結果。
with tf.variable_scope('layer7-fc3'):
fc3_weights = tf.get_variable('weight',[84,10],initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None:
tf.add_to_collection('losses',regularizer(fc3_weights))
fc3_biases = tf.get_variable('bias',[10],initializer=tf.truncated_normal_initializer(stddev=0.1))
logit = tf.matmul(fc2,fc3_weights) + fc3_biases
return logit
#正則化,交叉熵,平均交叉熵,損失函數,最小化損失函數,預測和實際equal比較,tf.equal函數會得到True或False,
#accuracy首先將tf.equal比較得到的布爾值轉為float型,即True轉為1.,False轉為0,最後求平均值,即一組樣本的正確率。
#比如:一組5個樣本,tf.equal比較為[True False True False False],轉化為float型為[1. 0 1. 0 0],准確率為2./5=40%。
'''規則化可以幫助防止過度配合,提高模型的適用性。(讓模型無法完美匹配所有的訓練項。)(使用規則來使用盡量少的變數去擬合數據)
規則化就是說給需要訓練的目標函數加上一些規則(限制),讓他們不要自我膨脹。
TensorFlow會將L2的正則化損失值除以2使得求導得到的結果更加簡潔
如tf.contrib.layers.apply_regularization/l1_regularizer/l2_regularizer/sum_regularizer
https://blog.csdn.net/liushui94/article/details/73481112
sparse_softmax_cross_entropy_with_logits()是將softmax和cross_entropy放在一起計算
https://blog.csdn.net/ZJRN1027/article/details/80199248'''
regularizer = tf.contrib.layers.l2_regularizer(0.001)
y = inference(x,False,regularizer)
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=y_)
cross_entropy_mean = tf.rece_mean(cross_entropy)
loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
correct_prediction = tf.equal(tf.cast(tf.argmax(y,1),tf.int32),y_)
accuracy = tf.rece_mean(tf.cast(correct_prediction,tf.float32))
#每次獲取batch_size個樣本進行訓練或測試
def get_batch(data,label,batch_size):
for start_index in range(0,len(data)-batch_size+1,batch_size):
slice_index = slice(start_index,start_index+batch_size)
yield data[slice_index],label[slice_index]
#創建Session會話
with tf.Session() as sess:
#初始化所有變數(權值,偏置等)
sess.run(tf.global_variables_initializer())
#將所有樣本訓練10次,每次訓練中以64個為一組訓練完所有樣本。
#train_num可以設置大一些。
train_num = 10
batch_size = 64
for i in range(train_num):
train_loss,train_acc,batch_num = 0, 0, 0
for train_data_batch,train_label_batch in get_batch(train_data,train_label,batch_size):
_,err,acc = sess.run([train_op,loss,accuracy],feed_dict={x:train_data_batch,y_:train_label_batch})
train_loss+=err;train_acc+=acc;batch_num+=1
print("train loss:",train_loss/batch_num)
print("train acc:",train_acc/batch_num)
test_loss,test_acc,batch_num = 0, 0, 0
for test_data_batch,test_label_batch in get_batch(test_data,test_label,batch_size):
err,acc = sess.run([loss,accuracy],feed_dict={x:test_data_batch,y_:test_label_batch})
test_loss+=err;test_acc+=acc;batch_num+=1
print("test loss:",test_loss/batch_num)
print("test acc:",test_acc/batch_num)