LeNet - Python中的卷積神經網絡

本教程將  主要面向代碼,  旨在幫助您 深刻學習和卷積神經網絡。因爲這個意圖,我  不會花不少時間討論激活功能,池層或密集/徹底鏈接的層 - 未來會有  不少教程在PyImageSearch博客上將覆蓋  每一個層類型/概念  在不少細節。css

再次,本教程是您  第一個端到端的例子,您能夠訓練一個現實的CNN(並在實際中看到它)。咱們將在本系列帖子中稍後介紹激活功能,聚集層和徹底鏈接層的細節(儘管您應該已經知道卷積運算的基本知識); 可是在此期間,只需跟隨,享受教訓,並  學習如何使用Python和Keras實現您的第一個卷積神經網絡。python

MNIST數據集


圖1: MNIST數字識別數據集。git

您可能已經看過MNIST數據集,不管是在PyImageSearch博客上仍是在您研究的其餘地方。在任何一種狀況下,我將繼續查看數據集,以確保您  準確瞭解咱們正在使用的數據。編程

MNIST數據集能夠說是計算機視覺和機器學習文獻中研究最多,數據最多的數據集,它是您在深刻學習之旅中使用的出色的「第一個數據集」。windows

注意:正如咱們將會發現的那樣,即便在CPU上,在這個數據集上得到> 98%的分類精度也很容易,訓練時間最短。緩存

該數據集的目標是對手寫數字0-9進行分類。咱們共得到了7萬張圖像,一般有6萬張圖像用於培訓,10,000張用於評估; 可是,咱們能夠自由分割這些數據,由於咱們認爲合適。共同分拆包括標準60,000 / 10,000,75%/ 25%和66.6%/ 33.3%。我將在博客文章中使用2/3的數據進行培訓和1/3的數據進行測試。markdown

每一個數字表示爲  28 x 28  灰度圖像(來自MNIST數據集的示例能夠在上圖中看到)。這些灰度像素強度是無符號整數,像素值落在[0,255]的範圍內  。 全部數字都放置在  黑色背景上 ,具備淺色前景(即,數字自己)爲  白色和  各類灰色。網絡

值得注意的是,許多庫(如scikit-learn)都有內置的幫助方法來下載MNIST數據集,將其緩存到磁盤上,而後加載它。這些幫助方法一般將每一個圖像表示爲  784-d向量。架構

784來自哪裏?dom

簡單。這只是  平坦的  28 x 28 = 784的形象。

要從784-d矢量恢復咱們的原始圖像,咱們簡單地將陣列重塑爲  28×28 圖像。

在本博客的上下文中,咱們的目標是培訓LeNet,以便咱們最大限度地提升咱們的測試集的準確性。

LeNet架構


圖2: LeNet架構由兩組卷積,激活和合並層組成,後面是徹底鏈接的層,激活,另外一個徹底鏈接,最後是一個softmax分類器(圖像源)。

LeNet架構是卷積神經網絡的一個很好的「第一架構」(特別是在MNIST數據集上進行了培訓時,手寫數字識別的圖像數據集)。

LeNet很小,易於理解 - 但足夠大,能夠提供有趣的結果。此外,LeNet + MNIST的組合可以在CPU上運行,使初學者可以輕鬆地在深度學習和卷積神經網絡中邁出第一步。

在許多方面,LeNet + MNIST是「Hello,World」等同於Deep Learning的圖像分類。

LeNet架構由如下層組成:

而不是解釋每層的卷積過濾器數量,過濾器自己的大小以及如今徹底鏈接節點的數量,我將保存這個討論,直到咱們的  「使用Python和Keras實現LeNet」  部分博客文章,其中的源代碼將做爲輔助解除。

同時,咱們來看看咱們的項目結構 - 一個結構,咱們將  在之後的PyImageSearch博客文章中屢次重用

注意:原來的LeNet架構使用 TANH   激活功能而不是 RELU  。咱們  在這裏使用RELU的緣由是由於它有更好的分類精度,由於一些很好的,理想的屬性(我將在將來的博客文章中討論)。若是您在LeNet中進行任何其餘討論,您可能會看到他們使用 TANH,   而不是再想想。

咱們的CNN項目結構

在咱們潛入任何代碼以前,咱們先來看看咱們的項目結構:

爲了保持代碼的組織,咱們將定義一個名爲pyimagesearch的包  。在 pyimagesearch   模塊中,咱們將建立一個 cnn   子模塊 - 這是咱們將存儲卷積神經網絡實現的地方,以及與CNN相關的任何幫助實用程序。

看看cnn裏面  ,你會看到 網絡   子模塊:這是  網絡實現自己將被存儲的地方。顧名思義,這一點py   文件將定義一個名爲LeNet的類  ,這是咱們在Python + Keras中實際的LeNet實現。

該 lenet_mnist py   腳本將是咱們的驅動程序,用於實例化LeNet網絡架構,訓練模型(或加載模型,若是咱們的網絡是預先訓練的),而後評估MNIST數據集上的網絡性能。

最後, 輸出   目錄將在咱們的LeNet   模型訓練完成後存儲 ,從而容許咱們在後續調用lenet_mnist  時對數字進行分類 py,沒必要從新訓練網絡。

過去一年,我我的一直在使用這個項目結構(或項目結構很是類似)。 我發現它頗有條理,易於擴展 - 隨着咱們用更多的網絡架構和幫助功能添加到這個庫中,這將在將來的博客文章中變得更加明顯。

用Python和Keras實現LeNet

首先,我會假設你已經有Kerasscikit學習,和OpenCV的系統上安裝(和可選,啓用GPU支持)。

不然,打開 禮物py   文件並插入如下代碼:

第2-7行處理從keras   庫導入所需的函數/類 

所述 LeNet   類被定義在  第9行,其次是 構建   於方法  11號線每當我定義一個新的網絡架構,我  老是將它放在本身的類中(主要用於命名空間和組織目的),而後建立一個  靜態 構建   函數。

該 構建   方法,顧名思義,須要提供的任何參數,其在  最低限度 包括:

  • 輸入圖像的  寬度
  • 輸入圖像的  高度
  • 輸入圖像的  深度(即通道數)。
  • 和數量  在咱們的數據集(即類標籤的惟一的號碼)。

我一般還包括一個  可用於加載預訓練模型的 權值路徑給定這些參數, 構建   函數負責構建網絡架構。

談到構建LeNet架構時,  第13行將實例化一個 Sequential   類,咱們將用它來構建網絡。

如今模型已初始化,咱們能夠開始添加圖層:

在  第15-19行,咱們建立了第一組 CONV RELU POOL   圖層集。

咱們的 CONV   層將學習20個卷積濾波器,每一個濾波器的大小爲  5 x 5該值的輸入尺寸與輸入圖像的寬度,高度和深度相同(在本例中爲MNIST數據集),因此咱們將有  28 x 28個輸入,單個通道用於深度(灰度)。

而後,咱們將xy方向上應用ReLU激活功能,而後  x和  y方向上移動2 x 2的 最大值池  (假設一個2 x 2的滑動窗口,經過激活體積「滑動」,進行最大運算,同時在水平和垂直方向上採起2像素的步驟)。

注:本教程主要是基於代碼的意思是你 第一次接觸到實現卷積神經網絡-我會去到 不少更 詳細的關於卷積層,激活功能,和最大集中在將來的博客帖子層。在此期間,只需試着跟隨代碼。 

咱們如今準備應用咱們的第二組 CONV RELU POOL   層:

這一次,咱們將學習  50個卷積濾波器,而不是像上一個圖層集中的  20個卷積濾波器

一般,在網絡的更深層次上,觀察到的CONV   濾波器數量的 增長

接下來,咱們來到LeNet架構的徹底鏈接的層(一般稱爲「密集」層):

在  第27行,咱們取上一個MaxPooling2D   層的輸出,  並將其  平坦化爲一個向量,使咱們能夠應用密集/徹底鏈接的層。若是您有神經網絡的任何先前經驗,那麼您將知道一個密集/徹底鏈接的層是網絡中的「標準」類型的層,其中上一層中的每一個節點都鏈接到下一層的每一個節點(所以,術語「徹底鏈接」)。

咱們的徹底鏈接的層將包含500個單位(28行),咱們經過另外一個非線性ReLU激活。

第32行是  很是重要的,雖然它很容易忽視 - 這一行定義了另外一個 Dense   類,但接受一個變量(即,不是硬編碼)的大小。這個大小是由    變量表示的類標籤的  數量 在MNIST數據集的狀況下,咱們有10個類(咱們正在嘗試學習識別的十位數中的每一個一個)。

最後,咱們應用一個softmax分類器(多項式邏輯迴歸),它將返回  機率列表,一個用於10個類標籤中的每個(第33行)。具備最大機率的類標籤將被選爲網絡的最終分類。

咱們的最後一個代碼塊處理加載一個預先存在的 weightsPath   (若是這樣一個文件存在)並將構造的模型返回給調用函數:

建立LeNet驅動程序腳本

如今咱們已經使用Python + Keras實現了LeNet卷積神經網絡架構,如今是定義lenet_mnist的時候了 py   驅動腳本將處理:

  1. 加載MNIST數據集。
  2. 將MNIST分紅培訓和  測試分組  
  3. 加載和編譯LeNet架構。
  4. 培訓網絡
  5. 可選地將序列化的網絡權重保存到磁盤,以即可以重用(而沒必要從新訓練網絡)。
  6. 顯示  網絡輸出的可視示例,以證實咱們的實現確實正常工做。

打開你的 lenet_mnist py   文件並插入如下代碼:

第2-9行處理導入咱們須要的Python包。注意咱們如何  從cnn   和 pyimagesearch網絡   子模塊 導入咱們的 LeNet類  。

注意:若是您跟隨此博客文章並打算執行代碼,  使用此帖子底部的「下載」部分。爲了保持這個短短的簡潔,我已經省略了 __init__ py   更新可能會拋棄新的Python開發人員。

從那裏,  第12-19行解析三個可選的命令行參數,每一個參數詳細以下:

  • 保存模型  :指示器變量,用於指定在培訓LeNet後是否 模型保存到磁盤。
  • 負載模型  :另外一個指示器變量,此時間指定咱們是否應該 加載從磁盤預先訓練的模型。
  • 權重  :在這種狀況下 保存模型   提供的 路徑   應該指向咱們要 保存序列化的模型。而在這狀況下 負載模型   提供的 權重   應該指向預先存在的權重文件咱們的系統上的生活。

咱們如今能夠加載MNIST數據集並將其分爲咱們的培訓和測試分裂:

第25行從磁盤加載MNIST數據集。若是這是您首次  使用「MNIST Original」   字符串調用fetch_mldata函數 ,則須要下載MNIST數據集。MNIST數據集是一個55MB的文件,因此根據你的互聯網鏈接,這個下載可能須要幾秒到幾分鐘的時間。

在下載MNIST數據集以後,咱們將 數據   從一組  784-d特徵向量(即原始像素強度)重構爲  28×28灰度圖像,咱們能夠經過網絡(30行)。

咱們的 數據   矩陣如今具備形狀 70000 28 28   ; 然而,  存在一個問題 --Keras假定咱們將爲每一個圖像提供  至少 1個通道,所以咱們須要在數據   陣列中添加一個額外的維度第31行)。這一行執行後,新形狀 數據   矩陣將是: 70000 28 28    - ,如今適合於經過咱們的LeNet架構。

最後,  第32-33行執行訓練和測試拆分,使用2/3的數據進行訓練,剩下的1/3用於測試。咱們也能夠將圖像從  [0,255]縮小至  [ 0,1.0 ],這是一種常見的縮放技術。

下一步是處理咱們的標籤,以便它們能夠與分類交叉熵損失函數一塊兒使用:

39和40行處理咱們的培訓和測試標籤(即,MNIST數據集中每一個圖像的「地面真相」標籤)。

因爲咱們使用分類交叉熵損失函數,咱們須要應用  將整數的標籤從整數轉換爲  向量to_categorical函數  ,其中每一個向量範圍從 ]  。該函數爲每一個類標籤生成一個向量  ,其中正確標籤的索引設置爲  1,全部其餘條目設置爲  0。

在MNIST數據集的狀況下,咱們有10個lass標籤,所以每一個標籤如今表示爲  10-d向量。例如,考慮培訓標籤  「3」應用 to_categorical   函數後,咱們的向量如今看起來像:

注意,除了如今設置爲1的第三個索引以外向量中的全部條目都爲零  

咱們如今準備創建咱們的 LeNet   架構,可選擇從磁盤加載任何預先訓練的權重,而後訓練咱們的網絡:

咱們將使用隨機梯度降低(SGD)訓練咱們的網絡 ,學習率爲 0.01  。分類交叉熵將被用做咱們的損失函數,這是在使用具備兩個以上類標籤的數據集時至關標準的選擇。而後咱們的模型被編譯並加載到第45-48行的內存中  

在這種狀況下 負載模型  不提供,咱們要培養咱們的網絡(52號線)。

培訓咱們的網絡是經過打電話來完成的   實例化模型的擬合方法   (第54和55行)。咱們將容許咱們的網絡訓練  20個紀元(代表咱們的網絡將「看到」每一個訓練示例共20次,以學習每一個數字類的區分過濾器)。

而後咱們對測試數據進行評估(59-61行),並將結果顯示給咱們的終端。

接下來,咱們檢查一下咱們是否應該將網絡權重序列化爲文件,以便咱們運行 lenet_mnistpy   腳本  後續時間,無需從頭開始從新訓練網絡:

咱們的最後一個代碼塊能夠從咱們的測試集中隨機選擇幾位數字,而後經過咱們訓練有素的LeNet網絡進行分類:

對於每一個隨機選擇的數字,咱們使用LeNet模型(第71行對圖像進行分類

  咱們網絡的實際 預測是經過找到具備最大機率的類標籤的索引得到的  記住,咱們的網絡將經過softmax函數返回一組機率,每一個類別標籤一個,所以網絡的實際「預測」是具備最大機率的類標籤。

76-80行處理將28 x 28圖像調整  到  96 x 96  像素,以便咱們能夠更好地可視化,而後繪製 圖像   上的 預測  。

最後,  第82-86行將結果顯示在咱們的屏幕上。

用Python和Keras訓練LeNet

要在MNIST數據集上訓練LeNet,請確保已使用 本教程底部找到「下載」表單下載源代碼  這個 zip   文件包含本教程中詳細介紹的全部代碼 - 此外,此代碼的組織方式與上面詳細描述的  相同的項目結構,  確保在系統上正常運行(若是您的環境配置正確)。

下載後 郵政編碼   存檔,您能夠經過執行如下命令在MNIST上訓練LeNet:

個人機器輸出結果以下:


圖3:在個人Titan X上的MNIST數據集上訓練LeNet每一個時期須要大約3秒鐘。通過20個時代,LeNet在培訓數據上達到98.90%的分類精度,測試數據的精度達到98.49%。

在個人Titan X GPU上,每一個紀元須要大約3秒鐘,容許  整個訓練過程在大約60秒內完成。

只有20個時代,LeNet 在MNIST數據集上達到  98.49%的分類精度 -  在全部計算時間只有60秒的時間裏,差很少!

注意:若是執行 lenet_mnist py   腳本在咱們的CPU而不是GPU,指望每一個時代的時間跳到70-90秒您仍然能夠在您的CPU上訓練LeNet,只須要一段時間。

用Python和Keras評估LeNet

下面我列出了LeNet + MNIST實現的幾個示例評估圖像:


圖4:應用LeNet對MNIST數據集中的數字進行分類。

在上述圖像中,咱們能夠正確地將數字分類爲  「6」

在這個圖像中,LeNet正確地將數字識別爲  「2」


圖5:在Python和Keras中實現LeNet。

下面的圖像是CNN濾波器學習的卷積濾波器的魯棒性,區分性質的一個很好的例子:這個  「6」是至關扭曲的,在數字的圓形區域之間留下了不多的差距,但LeNet仍然可以正確分類數字:


圖6:使用LeNet和卷積神經網絡正確分類特別難讀的數字。

這是另外一個圖像,此次分類嚴重偏斜的  「1」


圖7:使用卷積神經網絡對偶數位進行正確分類。

最後,最後一個例子演示了分類「2」的LeNet模型  


圖8:使用LeNet和Deep Learning對數字進行分類的最終示例。

Running the serialized LeNet model

After our lenet_mnist.py  script finishes executing the first time (provided you supplied both--save-model  and --weights ), you should now have a lenet_weights.hdf5  file in youroutput  directory.

Instead of re-training our network on subsequent runs of lenet_mnist.py , we can instead load these weights and use them to classify digits.

To load our pre-trained LeNet model, just execute the following command:

I’ve included a GIF animation of LeNet used to correctly classify handwritten digits below:


圖9: LeNet的示例動畫正確分類數字。

概要

在今天的博文中,我演示瞭如何使用Python編程語言和Keras庫實現LeNet架構,用於深刻學習。

LeNet架構是一個偉大的  「你好,世界」網絡,讓你的腳深刻學習和卷積神經網絡。網絡自己很簡單,內存佔用空間小,當應用於MNIST數據集時,能夠在CPU或GPU上運行,使其成爲實驗和學習的理想選擇,尤爲是在深刻學習新手時。

本教程主要以  代碼爲重點,所以,我須要跳過  重要的卷積神經網絡概念的詳細評論,例如激活層,池層和密集/徹底鏈接的層(不然這個帖子可能  很容易被5x爲長)。

在未來的博客文章中,我會詳細介紹  每一個這些圖層類型 - 同時,您只需熟悉代碼並嘗試本身執行。若是您感受  真的很大膽,請嘗試調整每一個卷積層的過濾器數量和過濾器尺寸,看看會發生什麼!

不管如何,我但願你喜歡這篇博客文章 - 我必定會在未來作更深刻的學習和圖像分類工做。

相關文章
相關標籤/搜索