本教程將 主要面向代碼, 旨在幫助您 深刻學習和卷積神經網絡。因爲這個意圖,我 不會花不少時間討論激活功能,池層或密集/徹底鏈接的層 - 未來會有 不少教程在PyImageSearch博客上將覆蓋 每一個層類型/概念 在不少細節。css
再次,本教程是您 第一個端到端的例子,您能夠訓練一個現實的CNN(並在實際中看到它)。咱們將在本系列帖子中稍後介紹激活功能,聚集層和徹底鏈接層的細節(儘管您應該已經知道卷積運算的基本知識); 可是在此期間,只需跟隨,享受教訓,並 學習如何使用Python和Keras實現您的第一個卷積神經網絡。python
您可能已經看過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架構是卷積神經網絡的一個很好的「第一架構」(特別是在MNIST數據集上進行了培訓時,手寫數字識別的圖像數據集)。
LeNet很小,易於理解 - 但足夠大,能夠提供有趣的結果。此外,LeNet + MNIST的組合可以在CPU上運行,使初學者可以輕鬆地在深度學習和卷積神經網絡中邁出第一步。
在許多方面,LeNet + MNIST是「Hello,World」等同於Deep Learning的圖像分類。
LeNet架構由如下層組成:
而不是解釋每層的卷積過濾器數量,過濾器自己的大小以及如今徹底鏈接節點的數量,我將保存這個討論,直到咱們的 「使用Python和Keras實現LeNet」 部分博客文章,其中的源代碼將做爲輔助解除。
同時,咱們來看看咱們的項目結構 - 一個結構,咱們將 在之後的PyImageSearch博客文章中屢次重用。
注意:原來的LeNet架構使用 TANH 激活功能而不是 RELU 。咱們 在這裏使用RELU的緣由是由於它有更好的分類精度,由於一些很好的,理想的屬性(我將在將來的博客文章中討論)。若是您在LeNet中進行任何其餘討論,您可能會看到他們使用 TANH, 而不是再想想。
在咱們潛入任何代碼以前,咱們先來看看咱們的項目結構:
爲了保持代碼的組織,咱們將定義一個名爲pyimagesearch的包 。在 pyimagesearch 模塊中,咱們將建立一個 cnn 子模塊 - 這是咱們將存儲卷積神經網絡實現的地方,以及與CNN相關的任何幫助實用程序。
看看cnn裏面 ,你會看到 網絡 子模塊:這是 網絡實現自己將被存儲的地方。顧名思義,這一點。py 文件將定義一個名爲LeNet的類 ,這是咱們在Python + Keras中實際的LeNet實現。
該 lenet_mnist 。py 腳本將是咱們的驅動程序,用於實例化LeNet網絡架構,訓練模型(或加載模型,若是咱們的網絡是預先訓練的),而後評估MNIST數據集上的網絡性能。
最後, 輸出 目錄將在咱們的LeNet 模型訓練完成後存儲 ,從而容許咱們在後續調用lenet_mnist 時對數字進行分類 。py,而沒必要從新訓練網絡。
過去一年,我我的一直在使用這個項目結構(或項目結構很是類似)。 我發現它頗有條理,易於擴展 - 隨着咱們用更多的網絡架構和幫助功能添加到這個庫中,這將在將來的博客文章中變得更加明顯。
首先,我會假設你已經有Keras,scikit學習,和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個輸入,單個通道用於深度(灰度)。
而後,咱們將在x和y方向上應用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 (若是這樣一個文件存在)並將構造的模型返回給調用函數:
如今咱們已經使用Python + Keras實現了LeNet卷積神經網絡架構,如今是定義lenet_mnist的時候了 。py 驅動腳本將處理:
打開你的 lenet_mnist 。py 文件並插入如下代碼:
第2-9行處理導入咱們須要的Python包。注意咱們如何 從cnn 和 pyimagesearch的網絡 子模塊 導入咱們的 LeNet類 。
注意:若是您跟隨此博客文章並打算執行代碼, 請使用此帖子底部的「下載」部分。爲了保持這個短短的簡潔,我已經省略了 __init__ 。py 更新可能會拋棄新的Python開發人員。
從那裏, 第12-19行解析三個可選的命令行參數,每一個參數詳細以下:
咱們如今能夠加載MNIST數據集並將其分爲咱們的培訓和測試分裂:
第25行從磁盤加載MNIST數據集。若是這是您首次 使用「MNIST Original」 字符串調用fetch_mldata函數 ,則須要下載MNIST數據集。MNIST數據集是一個55MB的文件,因此根據你的互聯網鏈接,這個下載可能須要幾秒到幾分鐘的時間。
在下載MNIST數據集以後,咱們將 數據 從一組 784-d特徵向量(即原始像素強度)重構爲 28×28灰度圖像,咱們能夠經過網絡(30行)。
咱們的 數據 矩陣如今具備形狀 (70000 ,28 ,28 ) ; 然而, 存在一個問題 --Keras假定咱們將爲每一個圖像提供 至少 1個通道,所以咱們須要在數據 陣列中添加一個額外的維度(第31行)。這一行執行後,新形狀 數據 矩陣將是: (70000 ,1 ,28 ,28 ) - ,如今適合於經過咱們的LeNet架構。
最後, 第32-33行執行訓練和測試拆分,使用2/3的數據進行訓練,剩下的1/3用於測試。咱們也能夠將圖像從 [0,255]縮小至 [ 0,1.0 ],這是一種常見的縮放技術。
下一步是處理咱們的標籤,以便它們能夠與分類交叉熵損失函數一塊兒使用:
39和40行處理咱們的培訓和測試標籤(即,MNIST數據集中每一個圖像的「地面真相」標籤)。
因爲咱們使用分類交叉熵損失函數,咱們須要應用 將整數的標籤從整數轉換爲 向量的to_categorical函數 ,其中每一個向量範圍從 [ 0 ,類] 。該函數爲每一個類標籤生成一個向量 ,其中正確標籤的索引設置爲 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_mnist。py 腳本 後續時間,無需從頭開始從新訓練網絡:
咱們的最後一個代碼塊能夠從咱們的測試集中隨機選擇幾位數字,而後經過咱們訓練有素的LeNet網絡進行分類:
對於每一個隨機選擇的數字,咱們使用LeNet模型(第71行)對圖像進行分類。
咱們網絡的實際 預測是經過找到具備最大機率的類標籤的索引得到的 。記住,咱們的網絡將經過softmax函數返回一組機率,每一個類別標籤一個,所以網絡的實際「預測」是具備最大機率的類標籤。
76-80行處理將28 x 28圖像調整 到 96 x 96 像素,以便咱們能夠更好地可視化,而後繪製 圖像 上的 預測 。
最後, 第82-86行將結果顯示在咱們的屏幕上。
要在MNIST數據集上訓練LeNet,請確保已使用 本教程底部找到的「下載」表單下載源代碼 。這個 。zip 文件包含本教程中詳細介紹的全部代碼 - 此外,此代碼的組織方式與上面詳細描述的 相同的項目結構, 確保在系統上正常運行(若是您的環境配置正確)。
下載後 。郵政編碼 存檔,您能夠經過執行如下命令在MNIST上訓練LeNet:
個人機器輸出結果以下:
在個人Titan X GPU上,每一個紀元須要大約3秒鐘,容許 整個訓練過程在大約60秒內完成。
只有20個時代,LeNet 在MNIST數據集上達到 98.49%的分類精度 - 在全部計算時間只有60秒的時間裏,差很少!
注意:若是執行 lenet_mnist 。py 腳本在咱們的CPU而不是GPU,指望每一個時代的時間跳到70-90秒。您仍然能夠在您的CPU上訓練LeNet,只須要一段時間。
下面我列出了LeNet + MNIST實現的幾個示例評估圖像:
在上述圖像中,咱們能夠正確地將數字分類爲 「6」。
在這個圖像中,LeNet正確地將數字識別爲 「2」:
下面的圖像是CNN濾波器學習的卷積濾波器的魯棒性,區分性質的一個很好的例子:這個 「6」是至關扭曲的,在數字的圓形區域之間留下了不多的差距,但LeNet仍然可以正確分類數字:
這是另外一個圖像,此次分類嚴重偏斜的 「1」:
最後,最後一個例子演示了分類「2」的LeNet模型 :
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:
在今天的博文中,我演示瞭如何使用Python編程語言和Keras庫實現LeNet架構,用於深刻學習。
LeNet架構是一個偉大的 「你好,世界」網絡,讓你的腳深刻學習和卷積神經網絡。網絡自己很簡單,內存佔用空間小,當應用於MNIST數據集時,能夠在CPU或GPU上運行,使其成爲實驗和學習的理想選擇,尤爲是在深刻學習新手時。
本教程主要以 代碼爲重點,所以,我須要跳過 重要的卷積神經網絡概念的詳細評論,例如激活層,池層和密集/徹底鏈接的層(不然這個帖子可能 很容易被5x爲長)。
在未來的博客文章中,我會詳細介紹 每一個這些圖層類型 - 同時,您只需熟悉代碼並嘗試本身執行。若是您感受 真的很大膽,請嘗試調整每一個卷積層的過濾器數量和過濾器尺寸,看看會發生什麼!
不管如何,我但願你喜歡這篇博客文章 - 我必定會在未來作更深刻的學習和圖像分類工做。