網絡上有著很多關於CNN入門的教程,但是總還是覺得缺少足夠簡易、直觀、全面的文章,能讓人通讀下來酣暢淋漓,將CNN概念盡收囊中。本篇文章就想嘗試一下,真正地帶小白同學們輕松入門。
這篇文章包含很多圖片,為瞭花這些圖筆者頗費瞭些功夫,認真看下來,相信你一定能有所收獲。
一、從前饋神經網絡說起
1.必會的內功:前饋神經網絡
前饋神經網絡(Feedforward Neural Networks)是最基礎的神經網絡模型,也被稱為多層感知機(MLP)。
它由多個神經元組成,每個神經元與前一層的所有神經元相連,形成一個“全連接”的結構。每個神經元會對其輸入數據進行線性變換(通過權重矩陣),然後通過一個非線性函數(如ReLU或Sigmoid)進行激活。這就是前饋神經網絡的基本操作。
在本專欄之前關於神經網絡入門的文章中介紹的就是前饋神經網絡。
圖1.前饋神經網絡結構示意
前饋神經網絡是深度學習的基礎,所以強烈建議同學們在正式學習CNN之前,先看下邊這篇文章:
Mr.看海:神經網絡15分鐘入門!足夠通俗易懂瞭吧
下邊關於CNN的講解,是建立在你已經看過上邊這篇文章的基礎上哦!
2.從軍體拳到降龍十八掌:從前饋神經網絡到CNN
許多初學者在深度學習的學習過程中,通常都會從學習卷積神經網絡(Convolutional Neural Network, 簡稱CNN)開始。很大程度上,是由於CNN的基本組成部分與前饋神經網絡有很緊密的關聯,甚至可以說,CNN就是一種特殊的前饋神經網絡。
這兩者的主要區別在於,CNN在前饋神經網絡的基礎上加入瞭卷積層和池化層(下邊會講到),以便更好地處理圖像等具有空間結構的數據。
現在畫圖說明一下。對於前饋神經網絡,我們可以將簡化後的網絡結構如下圖表示:
圖2. 前饋神經網絡的簡易表示
當然,【全連接層-ReLU】可以有多個,此時網絡結構可以表示為:
圖3. 具有N個隱藏層的前饋神經網絡結構
簡單地說,CNN就是在此基礎上,將全連接層換成卷積層,並在ReLU層之後加入池化層(非必須),那麼一個基本的CNN結構就可以表示成這樣:
圖3-2. N層的卷積神經網絡結構
好瞭,現在現在問題已經簡化為理解卷積層和池化層瞭。
二、從卷積層開始說卷積
1.為什麼要用卷積?
不知道大傢會不會有個疑問:所謂深度學習,不就是層數比較多的神經網絡嘛,那為什麼不能使用一種幾十層的前饋神經網絡,而要設計一個所謂的卷積神經網絡呢?
如果能想到這層,說明你是經過認真思考的。
卷積神經網絡最早主要是用來處理圖像信息的,在用全連接前饋網絡來處理圖像時,會存在以下幾個問題[1]:
- 參數太多:如果輸入圖像大小為100*100(即圖像高度為100,寬度為100,單顏色通道),那麼第一個隱藏層的每個神經元到輸入層都有100*100=10000個互相獨立的連接,每個連接對應一個權重參數,隨著隱藏層神經元數量的增多,參數的規模也會急劇增加,這會導致整個神經網絡的訓練效率非常低。
- 不利於表達空間結構: 圖像具有重要的空間結構信息。相鄰的像素在語義上通常更加相關。前饋神經網絡在處理圖像數據時,會將圖像展開成一個向量,這就破壞瞭圖像的空間結構。而CNN通過卷積操作能夠更好地保留和利用這些空間信息。
- 難以反映平移不變性: 對於圖像識別任務,對象出現在圖像中的具體位置並不重要,隻要能夠識別出對象的特征就行。CNN由於權重共享,可以無論特征在何處出現都能被檢測到,從而提供瞭一種平移不變性。
- 難以表征抽象層級: CNN通過多個卷積層和池化層的疊加,可以從低級的邊緣和紋理特征逐漸抽取出高級的語義特征。這個特性使得CNN非常適合於處理圖像等需要多層抽象表示的數據。
2.又談“什麼是卷積?”
之所以要說“又”,是因為之前我專門寫過一篇講卷積的文章,從信號處理的角度解釋瞭卷積的概念和特點:Mr.看海:這篇文章能讓你明白卷積。
卷積的過程,其實是一種濾波的過程,所以卷積核(Convolution Kernel)還有一個別名叫做Filter,也就是濾波器。
在之前我寫的一篇文章中曾得到過結論:當一組數像滑窗一樣滑過另外一組數時,將對應的數據相乘並求和得到一組新的數,這個過程必然和卷積有著莫大的關系。
圖4. 一維卷積過程
上圖演示的就是一維數據的卷積過程,其中權重系數都為1/3,也就是均值濾波的過程。
變換不同的權重系數,濾波器將展現出不同的濾波特性。
所以我們又可以得到一個結論:當權重系數(卷積核)的參數改變時,它可以提取的特征類型也會改變。所以訓練卷積神經網絡時,實質上訓練的是卷積核的參數。
將上述卷積過程拓展到二維,就是下圖這樣:
圖5. 來源:https://mlnotebook.github.io/post/CNN1/
中間的矩陣就是所謂的卷積核。
現在我通俗地講解一下這個卷積運算過程:
其中每一次運算的分解圖如下:
圖6. 來源https://mlnotebook.github.io/post/CNN1/,部分數值做瞭更正
3.卷積核的取值如何影響特征輸出
現在我們使用MNIST數據集作為案例。
MNIST是一個很有名的手寫數字識別數據集。對於每張照片,都是以一個28*28的矩陣存儲的。
圖7. MNIST數據集數字0~9示例
現在我們從數據集中隨便選一個數字8,並進行卷積運算。這個卷積計算過程如下圖(其中灰度深淺代表瞭數值大小):
圖8. 紅色為輸入,橙色為卷積層,綠色為卷積結果,使用的是平均卷積核
這裡我們選三種不同的卷積核看一下:
圖9. 三種不同的卷積核計算下卷積結果
上述使用瞭三種不同的卷積核,第一種卷積核所有元素值相同,所以它可以計算輸入圖像在卷積核覆蓋區域內的平均灰度值。這種卷積核可以平滑圖像,消除噪聲,但會使圖像變得模糊。第二種卷積核可以檢測圖像中的邊緣,可以看到輸入的8的邊緣部分顏色更深一些,在更大的圖片中這種邊緣檢測的效果會更明顯。第三種卷積核的四個角的權重為0.25,這是我隨意賦的值,得到的結果像是幾個窄窄的8重疊起來瞭。
需要註意的是,雖然上邊說道不同的卷積核有著不同的作用,但是在卷積神經網絡中,卷積核並不是手動設計出來的,而是通過數據驅動的方式學習得到的。這就是說,我們並不需要人工設計出特定的卷積核來檢測邊緣、紋理等特定的特征,而是讓模型自己從訓練數據中學習這些特征,即模型可以自動從復雜數據中學習到抽象和復雜的特征,這些特征可能人工設計難以達到。
這個自學習的過程具體是怎樣實現的,過會兒還會講到,我們先繼續講卷積運算。
4.卷積運算重要參數之——步長(Stride)
在卷積神經網絡(CNN)中,"步長"(stride)是一個重要的概念。步長描述的是在進行卷積操作時,卷積核在輸入數據上移動的距離。在兩維圖像中,步長通常是一個二元組,分別代表卷積核在垂直方向(高度)和水平方向(寬度)移動的單元格數。
例如,步長為1意味著卷積核在每次移動時,都隻移動一個單元格,這就意味著卷積核會遍歷輸入數據的每一個位置;同理,如果步長為2,那麼卷積核每次會移動兩個單元格。
下圖是步長為3時的卷積運算過程。
圖10. 步長值設置為3,卷積輸出結果尺寸大幅減小
步長的選擇會影響卷積操作的輸出尺寸。更大的步長會產生更小的輸出尺寸,反之同理。
之所以設置步長,主要考慮以下幾點:
- 降低計算復雜性:當步長大於1時,卷積核在滑動過程中會"跳過"一些位置,這將減少輸出的尺寸並降低後續層的計算負擔。
- 模型的可擴展性:增大步長可以有效地降低網絡層次的尺寸,使得模型能處理更大尺寸的輸入圖片。
- 控制過擬合:過擬合是指模型過於復雜,以至於開始"記住"訓練數據,而不是"理解"數據中的模式。通過減少模型的復雜性,我們可以降低過擬合的風險。
- 減少存儲需求:更大的步長將產生更小的特征映射,因此需要更少的存儲空間。
5.卷積運算重要參數之——零填充(zero-padding)
不知道大傢註意到沒有,在圖9中,輸入的28*28維矩陣在進行卷積計算之後,維度降為瞭24*24,是因為啥呢?
從下圖可以比較清楚地看出,卷積核從左上角開始掃描時,每條邊隻能滑動三次。如果定義輸入層的邊長是M,卷積核的邊長是K,那麼卷積後輸出的邊長是M-K+1。
圖11. M-K+1=5-3+1=3
所以從上邊公式看出來,數據在進行卷積運算之後尺寸會縮小。眾所周知,CNN是一種深度學習網絡,包含很多個卷積層,那麼這樣一直算到後邊,尺寸豈不是要變成1瞭?
即使沒有變成1,過小的數據尺寸也會導致信息的丟失。
對此,解決辦法就是在輸入數據進行卷積運算前,在四周補充一圈(或多圈)數字,通常補充的是0,所以就叫“零填充”。下圖中補充後的數據再進行卷積,計算結果就能保持5*5的維度啦。
圖12. 圖片來源https://mlnotebook.github.io/post/CNN1/
這裡我們將上述計算卷積輸出的矩陣大小的公式進一步擴展,引入填充的圈數P,以及步長的長度S,此時卷積輸出的矩陣邊長為:
L_{輸出邊長}=(M-K+2P)/S+1
需要註意,上述公式中的除法需向下取整。該公式不難推導,這裡就不展開說啦。
除瞭上邊說到的控制空間維度,防止信息丟失以外,零填充還有一個重要的意義是:更充分利用輸入數據的邊緣信息。
當未進行填充時,上下邊緣用到的次數較少,就像下圖:
圖13. 上下邊緣的數據隻被用到瞭1次,中間的用到瞭3次
當進行填充後,邊緣數據進行瞭更為充分的利用,就像下圖:
圖14. 這張不是動圖(∩_∩)
這種能夠更均勻地處理所有信息,包括邊緣信息的性質,是零填充在卷積神經網絡中被廣泛使用的重要原因之一。
三、再說激活層
1.ReLU在CNN中的位置
再回顧一下CNN的結構(下圖),卷積層後邊緊跟著就是ReLU激活層,雖然在以前的文章(Mr.看海:神經網絡15分鐘入門!足夠通俗易懂瞭吧)中講過ReLU,不過為瞭文章的完整性,這裡還是再簡單介紹一下。
圖15. 卷積神經網絡結構的二次出鏡
卷積層和全連接一樣,也是一種線性變換,無論進行多少次這樣的操作,都隻能獲得輸入數據的線性組合。如果沒有非線性的激活函數,那麼即使是多層的神經網絡,在理論上也可以被一個單層的神經網絡所表達,這極大地限制瞭網絡的表達能力。
ReLU函數是一個非線性函數,隻保留正數元素,將負數元素設置為0。這種簡單的修正線性單元具有許多優點,例如,它能夠緩解梯度消失問題,計算速度快,同時ReLU的輸出是稀疏的,這有助於模型的正則化。ReLU的響應函數圖像如下:
圖16. ReLU的理念其實很簡單。當輸入小於等於0時,輸出0;當輸入大於0時,輸出1。
2.ReLU激活的動態演示
下圖展示的是含負值的輸出層經過ReLU計算的過程,圖中紅色代表負數,灰色代表正數,顏色越深數值的絕對值越大:
圖16. 負值經過ReLU層後全部賦值為0
在我們上邊手寫字體的例子裡,由於輸入層數據和卷積核都不小於0,所以卷積計算結果全都不小於0,ReLU層前後數據不會發生變化,下邊我設置一個含負數的卷積核,這樣卷積運算的結果就會包含負數,此時卷積運算+ReLU非線性激活的過程就如下圖:
圖17. 卷積運算+ReLU非線性激活的過程
四、化繁為簡的池化層
1.什麼是池化層?
ReLU激活層之後就是池化層。
池化層的主要作用是對非線性激活後的結果進行降采樣,以減少參數的數量,避免過擬合,並提高模型的處理速度。
池化層主要采用最大池化(Max Pooling)、平均池化(Average Pooling)等方式,對特征圖進行操作。以最常見的最大池化為例,我們選擇一個窗口(比如 2×2)在特征圖上滑動,每次選取窗口中的最大值作為輸出,這就是最大池化的工作方式:
圖18. 最大池化(Max Pooling)的計算過程演示,左側圖像池化運算得到右側圖像
大致可以看出,經過池化計算後的圖像,基本就是左側特征圖的“低像素版”結果。也就是說池化運算能夠保留最強烈的特征,並大大降低數據體量。
對於平均池化,顧名思義,就是對窗口內的數據取平均值。相信大傢都很容易理解,不再贅述。
2.加入池化層-完整的“卷積單元”
第三次看下邊這張圖:
圖19. 我又來瞭——卷積神經網絡結構
到現在,“卷積層→ReLU→池化層”這樣一個CNN網絡中的基本組成單元的基礎概念就講完瞭。但是需要註意,卷積層、ReLU和池化層的組合是一種常見模式,但不是唯一的方式。比如池化層作為降低網絡復雜程度的計算環節,在算力硬件條件越來越好的當下,有些時候是可以減少采用次數的,也就是池化層可以在部分層設置、部分層不設置。
完整的“卷積層→ReLU→池化層”這樣一個運算單元,使用上述手寫字體的動圖可以表示如下:
圖20. “卷積→ReLU→池化”完整過程
五、關於輸出層
在卷積神經網絡中,最後一層(或者說最後一部分)通常被稱為輸出層。這個層的作用是將之前所有層的信息集合起來,產生最終的預測結果。
對於CNN進行分類任務時,輸出部分的網絡結構通常是一個或多個全連接層,然後連接Softmax。
當然,如果想要從卷積層過渡到全連接層,你需要對卷積層的輸出進行“展平”處理,簡而言之就是將二維數據逐行串起來,變成一維數據。
由於此時數據經過多層卷積和池化操作,數據量已大大減少,所以全連接層設計的參數就不會有那麼多瞭。
輸出層部分的結構我在之前的文章裡有詳細的講解,具體請看下邊這篇文章的第三節輸出的正規化和第四節如何衡量輸出的好壞:
Mr.看海:神經網絡15分鐘入門!足夠通俗易懂瞭吧
圖21. 輸出部分示意圖(對於分類問題)
六、通過實例完整理解CNN網絡結構
上邊的內容已經將CNN的核心知識點都講瞭一遍,就像樂高的幾種基本的積木模塊都介紹完瞭。
剩下的就是拼裝的過程。
1.基礎模塊:輸入層→卷積層→ReLU→池化層
- 1.1 輸入層:在上邊的例子中,輸入層就是28*28的矩陣,需要註意由於我們的分析對象是灰度圖,它是單通道圖像,所以更準確的說法是輸入層是1*28*28的矩陣,對於三通道的彩色圖片的話,輸入層就是3*28*28的維度瞭。
圖22. 彩圖通常是三通道,這裡使用黑白圖像作為示意,大傢理解就好
- 1.2 卷積運算:需要註意,在設置卷積核的時候,除瞭要設置卷積核的尺寸(即長和寬),還要設置卷積核的數量。卷積核的數量決定瞭卷積層的輸出特征圖的數量,也就決定瞭卷積層的深度。也就是說,如果你有4個卷積核,那麼你的卷積層就會輸出4個特征圖,形成一個深度為4的輸出。
所以在卷積運算這一步,需要設置或者說調試的參數有這幾個:卷積核的長度、寬度、數量、卷積運算步長、零填充的圈數。
此時卷積核的維度就是這樣的:
圖23. 卷積核維度,需要註意,每個卷積核矩陣中的數值應該都是不同的,這裡僅作為示意。
- 1.3 卷積運算輸出(特征圖):由於每個卷積核都會對應一張特征圖的輸出,所以4個卷積核就對應著4張特征圖。而特征圖的邊長的計算方法在上文講到過,綜合瞭輸入數據的邊長、步長、卷積核大小、零填充情況等等,可以計算出特征圖的邊長。
我們設置步長S為3,不進行零填充即P=0,輸入數據的邊長為M=28,卷積核邊長K=5,則:
L=(M−K+2P)/S+1 =(28−5+2∗0)/3+1 =8 (註意,此處除法向下取整)
所以此時特征圖維度如下:
圖24. 輸出特征圖維度,需註意,在實際計算中四張特征圖應該是不同的
- 1.4 ReLU非線性激活:ReLU的計算不影響特征圖尺寸,僅對特征圖數值進行調整計算,此時特征圖維度如下:
圖25. ReLU非線性激活的輸出維度,相對於上一層級未發生變化
- 1.5 池化層輸出:池化層的會影響特征圖尺寸,但不會影響特征圖數量,尺寸的變化也很容易計算,下圖是在經過2*2的最大池化窗口後的計算結果,特征圖的邊長變為:8/2=4。
圖26. 池化層輸出結果,數據量得到進一步縮減
將上述過程完整連接起來,可以表示為下圖:
圖27. 完整的輸入層→卷積層→ReLU→池化層
2.由基礎模塊搭建摩天大樓
上述圖27展示的是一層卷積層的典型結構,在實際應用中,CNN往往是由多個卷積層構成,後續再綴接卷積層,則就是將上一層的輸出作為後續的輸入,然後重復“輸入層→卷積層→ReLU→池化層”這個過程,當然池化層是非必須的。
此時卷積網絡整體結構可以這樣表示:
圖28. 常用卷積網絡整體結構通用表達方式,參考《神經網絡與深度學習》
一個卷積塊為連續M個卷積層和b個匯聚層,一個卷積網絡中可以堆疊N個連續卷積塊,然後在後面接著K個全連接層、Softmax以及交叉熵損失。
在上邊圖27的基礎上,我們嘗試再添加一個卷積層(這個不添加池化層瞭),此時就相當於將圖27中的池化層輸出的特征圖(維度4*4*4)作為瞭輸入,然後進行卷積和非線性激活操作,網絡結構如下圖:
圖29. 輸入層→卷積層→ReLU→池化層→卷積層→ReLU
為瞭方便大傢觀看,我將此圖縱向再貼一遍,手機黨可以橫屏查看:
圖30. 橫版大圖
3.連接輸出層實現最終功能
由於上邊的圖片太長瞭,在輸出層這部分,我們從最後一部分ReLU輸出開始畫起。
- 3.1 展平。展平是從卷積層到全連接層的一個必要步驟。卷積層的輸出是一個三維張量,具有寬度、高度和深度(特征圖的數量)。然而,全連接層期望的是一個一維向量的輸入。因此,我們需要將這個三維張量“展平”成一個一維向量。
圖31. 三維張量的“展開”過程
- 3.2 全連接層。全連接層的每一個節點都與前一層的所有節點相連。它的任務是學習卷積層和池化層提取出來的高級特征之間的非線性關系。全連接層通常用來執行分類任務,需註意全連接的輸出維度和分類類別數是相同的。因為手寫字體共有10種數字,所以類別就是10。
圖32. 全連接層
- 3.3 Softmax。Softmax可以將一個實數向量轉換成一個概率分佈。換句話說,Softmax 函數會輸出每個分類的概率,這些概率之和為1。在輸出層使用Softmax激活函數是分類任務中的常見做法,因為它可以直觀地給出每個類別的預測概率。
圖33. Softmax層
- 3.4 交叉熵損失。在訓練神經網絡的過程中,我們需要一個指標來衡量模型的性能,這就是損失函數的作用。對於分類問題,常用的損失函數是交叉熵損失。交叉熵損失可以衡量預測的概率分佈與真實分佈之間的差異。
將上述各層串聯起來,就是完整的輸出層網絡結構,即下圖:
圖34. 輸出層網絡結構
至此CNN神經網絡的正向傳播過程就講完瞭,上述例子完整的結構如下圖:
圖35. 輸入層→卷積層→ReLU→池化層→卷積層→ReLU→展平→全連接→Softmax→交叉熵損失
七、參數更新與迭代
關於反向傳播,我就化用之前文章的表述瞭:
當通過上述方式反復迭代之後,我們就得到 一個訓練好的CNN模型,他的本質就是一整套網絡結構參數。
此時如果導入任意一組手寫圖片,利用圖35的流程,就能得到分類結果啦。
結語
CNN的基本原理至此講完瞭。這篇文章前前後後花瞭大概半個月的時間,其中盡可能多用圖解,以希望讀到這篇文章的同學們能真的看懂弄通。
後邊我還會針對CNN進行一些補充講解,例如:
- 常見的卷積神經網絡結構
- CNN在MATLAB和Python中的快速實現
- CNN工程應用實例
- 常見問題答疑匯編等等
“一站式”讓大傢搞懂並且用上CNN。
撰寫不易,感覺文章有用的同學們,請在收藏的同時點個贊吧!
參考
- ^《深度網絡與深度學習》邱錫鵬 著
-
扫码下载安卓APP
-
微信扫一扫关注我们微信扫一扫打开小程序手Q扫一扫打开小程序
-
返回顶部