如何用NumPy搭建卷積神經網路實現手寫數字識別(附程式碼)
- 2020 年 3 月 18 日
- 筆記
01
介紹
當Yann LeCun發表了他關於開發一種新型神經網路架構——卷積神經網路(Convolutional neural network, CNN)——的研究成果時,他的工作基本上沒有引起注意。在2012年的ImageNet電腦視覺大賽上,來自多倫多大學的一組研究人員花了14年的時間將CNN引入公眾視野。當他們從數千個類別的中對數百萬張圖片進行分類時,只產生了15.8%的錯誤。快進到現在,當前最先進的卷積神經網路實現的精度超過人類水平的性能。

ImageNet數據集上錯誤率
在這些有希望的結果的激勵下,我開始了解CNN的功能,以及它們是如何表現得如此出色的。因此為了全面了解人工智慧的這種進步,我在NumPy中從零開始構建了一個卷積神經網路。在完成這個項目之後,我覺得卷積神經網路在表面複雜性和它們實際複雜性之間存在著脫節。
02
挑戰
CNN以其識別影像模式的能力而聞名,因此本文中描述的網路的任務就是影像分類。衡量電腦視覺演算法執行情況的最常見基準之一是在MNIST手寫數字資料庫上對其進行訓練:該資料庫包含70,000個手寫數字及其對應的標籤。目標是訓練CNN在標記手寫數字(從0到9)時儘可能準確。經過大約5個小時的訓練和在訓練集上的兩次循環,這裡展示的網路能夠在測試數據上達到98%的準確率,這意味著它可以正確地猜測顯示給它的幾乎每一個手寫數字。

讓我們回顧一下構成網路的各個組件,以及它們如何連接在一起,從輸入數據形成預測。在解釋了每個組件之後,我們將對其功能進行編碼。在這篇文章的最後一部分,我們將使用NumPy對網路的每個部分進行編程和訓練。廢話少說,讓我們開始吧。
03
卷積神經網路如何學習
卷積層(Convolutions)
CNN利用過濾器(也被稱為內核)來檢測影像中存在哪些特徵,比如邊緣。過濾器只是一個值的矩陣,叫做權值,它被訓練來檢測特定的特徵。過濾器移動到影像的每個部分,檢查它要檢測的特徵是否存在。為了提供一個值來表示特定特徵的可信度,過濾器執行一個卷積操作,這是一個元素的乘積和兩個矩陣之間的和。

當特徵出現在影像的某一部分時,濾波器與該部分影像進行卷積運算,得到一個高值的實數。如果特性不存在,則結果值很低。
在下面的例子中,負責檢查右邊曲線的過濾器被傳遞到影像的一部分。由於影像的這一部分包含與濾波器所尋找的曲線相同的曲線,因此卷積運算的結果是一個很大的數(6600)。

但是,當相同的濾波器通過影像中具有相當不同的邊緣集的部分時,卷積的輸出很小,這意味著不存在很強的右手曲線。

為了使卷積神經網路能夠學習檢測輸入數據中特徵的濾波器的值,必須通過非線性映射來傳遞濾波器。濾波器與輸入影像卷積運算的輸出用偏置項求和,並通過非線性激活函數。激活函數的目的是將非線性引入到我們的網路中。由於我們的輸入數據是非線性的(對形成手寫簽名的像素進行線性建模是不可行的),我們的模型需要考慮這一點。
程式碼要點:
使用NumPy,我們可以很容易地對卷積運算進行編程。卷積函數利用for循環對影像上的所有過濾器進行卷積。在for循環的每個迭代中,使用兩個while循環將過濾器傳遞給影像。在每個步驟中,過濾器是多元素的(*)與輸入影像的一部分。然後使用NumPy 's sum方法將這個元素相乘的結果求和,得到一個單獨的值,然後添加一個偏差項。
縮減像素取樣(Downsampling)
為了加快訓練過程並減少網路消耗的記憶體,我們嘗試減少輸入特性中存在的冗餘。有幾種方法可以對影像進行降取樣,但在這篇文章中,我們將著眼於最常見的一種:max pooling(最大池化)。
在最大池化中,一個窗口根據設定的步長(每次移動多少單位)從一個影像上經過。在每個步驟中,窗口內的最大值被合併到一個輸出矩陣中,因此稱為最大池化。
在下面的影像中,大小為f=2的窗口以2的步長通過影像。f表示最大池化窗口的大小(紅色框),s表示窗口在x和y方向上移動的單元數。在每個步驟中,選擇窗口內的最大值:

最大池化極大地減少了表示大小,從而減少了所需的記憶體數量和以後在網路中執行的操作數量。
程式碼要點:
最大池操作歸結為一個for循環和幾個while循環。for循環用於遍歷輸入影像的每一層,while循環將窗口滑動到影像的每個部分。在每個步驟中,我們使用NumPy的max方法來獲得最大值
全連接層(fully-connected layer)
在神經網路的全連通操作中,輸入表示被壓扁成一個特徵向量,並通過神經元網路來預測輸出概率。
這些行被連接起來形成一個長特徵向量。如果存在多個輸入層,則將其行連接起來形成更長的特徵向量。
然後將特徵向量通過多個密集層。在每一稠密層,特徵向量乘以該層的權值,加上它的偏差,然後通過非線性。下圖顯示了全連通操作和稠密層:

程式碼要點:
NumPy使得編寫CNN的全連接層變得非常簡單。事實上,你可以用NumPy的reshape方法在一行程式碼中完成
輸出層(Output layer)
CNN的輸出層負責生成給定輸入影像的每個類(每個數字)的概率。為了獲得這些概率,我們初始化最後的緻密層,使其包含與類相同數量的神經元。然後,這個稠密層的輸出通過Softmax激活函數,該函數將所有最終的稠密層輸出映射到一個元素之和為1的向量。
04
結果
經過訓練,網路在測試集上的準確率平均為98%,我認為這是相當不錯的。將訓練時間延長2-3個epoch後,我發現測試集的性能下降了。我推測,在第三到第四個訓練循環中,網路開始過度擬合訓練集,不再泛化。
作者:Alejandro Escontrela
編譯:HuangweiAI
程式碼鏈接(NumPy版):https://github.com/Alescontrela/Numpy-CNN
·END·