等了一個月,吳教授的Deeplearning.ai的第四部分,也就是關于卷積神經網絡(Convolutional Neural Network,簡寫CNN)的課。
作為吳教授的忠實門徒(自封),除了在課堂上受到吳教授秀恩愛暴擊外,當然也要好好做筆記。雖然之前也有過一些CNN基礎,但這次收益還是挺多的。
CNN目前主要用于?
CNN目前主要用于CV領域,也就是計算機視覺領域。
很多當前最先進的計算機視覺方面的應用,如圖片識別,物體檢測,神經風格轉換等等,都大量使用到了CNN。
而且不光是CV領域,很多CNN在CV領域應用的很多技巧,其他領域也可以從其中借鑒到很多,如自然語言處理。
為什么要用CNN
首先,假如我們要進行一個圖像識別任務?這里就不用已經都快被玩爛了的的MNIST,用吳教授課上的SIGNS數據集(包含了用手勢表示的從0到5的圖片)。目標任務就是,給一個手勢,識別出代表數字幾。
如果是上過之前吳教授機器學習的同學的話,那么可能會問,為什么要用CNN呢?
何不直接像機器學習課里面一樣,將圖片拍扁(flatten),也就是直接將(高 x 寬 x 通道(如RGB))的圖片展成一個一維向量呢。如SIGNS,本來是64x64x3的圖片,我們將它拍成1x12288的向量。
之后直接輸入全連接網絡(即一般我們認為的神經網絡),中間加點隱層,最后一層用softmax壓一下直接輸出結果就好了。
我們可以這樣設計網絡結構,第一層12288個單元,然后中間三個隱層,分別是1028個單元、256個單元、64個單元,最后一層6個單元,也就是我們的分類數。
搭建好網絡后,然后訓練。這樣子確實也可以得到還過得去的結果,大概70~80%。但是相信大家也發現了,這樣子搭建的網絡需要很多很多參數,如這個例子里面
也就是大約一千三百萬個參數。
而這個例子還只是很小的圖片,因為只有64x64大小,而現在隨意用手機拍張照片就比這大很多。處理這些圖片時,用全連接網絡搭建出來的網絡的參數個數,將會是一個天文數字,不光優化困難,而且性能也不怎么樣。
而這時候,就是CNN大展神威的時候了。只需要多少參數呢?之后再揭曉揭曉。
CNN基礎:卷積層
卷積 Convolution
CNN網絡最主要的計算部分就是首字母C,卷積(Convolutional)。
如下圖,這里的一次卷積運算指的是,當我們有一個過濾器(filter),即下圖移動的方塊,將這個方塊對應要處理的輸入數據的一部分,然后位置一一對應相乘,然后把結果再相加得到一個數。
一個過濾器對一張圖片進行卷積運算時,往往要進行多次卷積運算,對一部分進行計算完之后,然后移動一點距離再計算,繼續移動,計算... 直到處理完整張圖片。
為什么叫做過濾器呢(也有叫做kernel(核)的)?
因為我們可以把每個過濾器,當做是設定了一定條件的特征檢測器,把不屬于檢測條件的都過濾掉。只有當前卷積的區域符合這個過濾器設置的條件時,卷積計算結果才會得到一個比較大的數。
舉個栗子,如用于檢測垂直和水平邊線的過濾器。
可以試著自己計算一下。如下圖,如果用灰度表示的話,黑的地方是255,白的地方是0。用上面的Gx來卷積下面這張圖的話,就會在中間黑白邊界獲得比較大的值。
而CNN中會有很多個過濾器,每個過濾器對圖片進行卷積后,會得到下一個結果的一個通道。CNN厲害的地方在于,過濾器的特征并不是人為設定的,而是通過大量圖片自己訓練出來的。
這樣也就增加了它的靈活性,而且因為視覺底層特征的兼容特性,因此也保證了遷移學習的可能。
步長(Stride)與填充(Padding)
上面說了卷積計算,但還有一些小的細節沒提。
如果用(f, f)的過濾器來卷積一張$(h, w)$大小的圖片,每次移動一個像素的話,那么得出的結果就是(h-f+1, w-f+1)的輸出結果。f是過濾器大小,h和w分別是圖片的高寬。
如果每次不止移動一個像素,而是兩個像素,或者是s個像素會怎么樣呢。
那么結果就會變為
這個s被稱為步長。
但是如果每次這樣子進行卷積的話,會出現一個問題。只要是$f$和$s$的值比1要大的話,那么每次卷積之后結果的長寬,要比卷積前小一些。
因為這樣子的卷積,實際上每次都丟棄了一些圖片邊緣的信息。一直這樣卷積的話,一旦卷積層過多,就會有很多信息丟失。為了防止這種情況的發生,我們就需要對原來的圖片四周進行填充(padding)。
一般都會用“0”值來填充,填充1就代表對周圍填充一圈,如上圖。填充2就填充兩圈,填充為p就是p圈,長寬各增加2p。
有了填充之后,每次卷積后的大小會變為
此時假設卷積后高不變
那么我們可以獲得
假設s步長為1,那么
也就是如果過濾器的高h=5的話,那么為了保持輸出高寬不變,那么就需要p=2。
上面這種保持卷積前后高寬不變的填充方式叫做"Same(相同)填充"。
分數情況
之后來討論一下分數情況吧。
萬一f是4或者6這樣的數呢,那么得到的p豈不是分數,怎么填充。答案是,那f就不要取偶數嘛。這就是為什么一般默認的過濾器大小都是5、7、11這樣的單數。
好的,解決了填充的情況,那么如果輸出的
是分數怎么辦。如h=6,f=3,p=0,s=2的情況下,按照公式計算會得到2.5。一般的處理是,只取整數部分。
而這種p=0,然后結果取整數部分的處理方式,叫做"Valid(有效)填充"。
CNN基礎:池化(Pooling)層
除了上面的卷積層,CNN里面還有一個很重要的部分,池化層。
一般會跟在卷積層之后,主要用到的Pooling層主要由有Max Pooling和Average Pooling, 也就是最大池化和平均池化。
其實概念很簡單,最大池化就如下圖。假設有一個2x2的窗口,每次移動步長也為2,每次對窗口里的數取最大值,然后輸出。
同樣的平均池化,則就把取最大值這個操作換成取平均值就行了。
除了上面兩種池化方式,當然還有一些其他的池化方法,如k-Max Pooling之類的,但是應用很少。在最大和平均兩個里面,也是最大池化比較常用。
池化層輸出大小的轉換也和卷積層比較類似,用
來計算,一般池化層不用填充。而且池化層沒有要需要訓練的參數。
CNN基礎:組合
有了卷積層和池化層兩大部件之后,就只剩下組合了。下圖是吳教授手繪LeNet-5網絡。
結構很簡單大概是這樣子Input -> Conv1 -> Pool1 -> Conv2 -> Pool2 -> (Flatten) -> FC3 -> FC4 -> Output
。
Conv是卷積層,Pool是池化層,FC指的是全連接網絡層(Full-Connected)。其中Flatten指的是,因為卷積網絡輸出的數據形狀(3維),和緊接著的全連接網絡的輸入形狀(1維)不吻合,所以需要進行一些處理。
就是之前在直接用全連接網絡提到的,把卷積網絡的輸出拍扁(flatten),把三維的拍成一維,之后再輸入全連接網絡。
建議大家可以按照前面提到的形狀變換公式,還有吳教授的手繪圖,親自把LeNet-5過一遍,對之后編程CNN很有幫助的。
關于前面的參數個數的問題,用上面這個LeNet-5來處理的話
也就是大概114萬。
一下子就縮小了一個數量級,當然對于越大的圖片這個差還會更加大。
結尾
這就是吳教授CNN課堂的第一周上的內容了。這次在通道這個概念有了很多新的看法。
如果想要自己修的話,直接Coursera Deeplearning ai就能搜索到課程。
Coursera小技巧,點擊注冊,不想付錢的話,點左下角的那個小小的旁聽就好了。之后想拿證的時候再充值。
習題很簡單,這里是我的練習解答,如有困難可以參考。 repo