深度學習中的動手實踐:在CIFAR-10上進行影象分類
你想開始進行深度學習嗎? 這有一篇關於Keras的深度學習的文章(地址見下方連結),對影象分類的神經網路做了一個總體概述。然而,它缺少一個關鍵的因素——實際的動手練習。本文將試圖填補這一空白。
- 文章:http://p.migdal.pl/2017/04/30/teaching-deep-learning.html
實用性的深度學習 深度學習有一個骯髒的祕密——不管你知道多少,總會有很多反覆的嘗試和錯誤。你需要測試各種網路體系架構、資料預處理方法、引數和優化器等等。即使是頂尖的深度學習專家,也不能只寫一個神經網路程式,執行它,並在一天內呼叫它。
每次你看到一個最先進的神經網路,然後問自己“為什麼這裡會有6個卷積層?”或者“為什麼他們會把dropout率提高到0.3?”答案是,他們嘗試了各種各樣的引數,並選擇了他們在經驗基礎上所做的那個。然而,對其他解決方案的瞭解確實給我們提供了一個很好的起點。理論知識建立了一種直觀的看法,即哪些想法是值得嘗試的,哪些想法是不可能改善神經網路的。
解決任何深度學習問題的一個相當普遍的方法是:
- 對於給定的一類問題,使用一些最先進的體系架構。
- 修改它以優化你的特定問題的效能。
修改既包括更改其架構(例如,層數、新增或刪除輔助層,如Dropout或Batch Normalization)和調優其引數。唯一重要的效能指標是驗證分數(validation score),也就是說,如果在一個數據集上訓練的一個網路能夠對它從未遇到過的新資料做出良好的預測,其他的一切都歸結為實驗和調整。
一個良好的資料集——用於影象分類的CIFAR-10 許多關於深度學習的影象分類的介紹都是從MNIST開始的,MNIST是一個手寫數字的標準資料集。它不僅不會產生令人感嘆的效果或展示深度學習的優點,而且它也可以用淺層機器學習技術解決。在這種情況下,普通的K近鄰(KNN)演算法會產生超過97%的精度(甚至在資料預處理的情況下達到99.5%)。此外,MNIST並不是一個典型的影象資料集——控制它不太可能教給你可遷移的技能,而這些技能對於其他分類問題是有用的。
如果你真的需要使用28×28灰度影象資料集,那麼可以看看notMNIST資料集和一個MNIST-like fashion product資料集(一個非常有趣的資料集,也是10分類問題,不過是時尚相關的)。
- notMNIST資料集:http://yaroslavvb.blogspot.com/2011/09/notmnist-dataset.html
- MNIST-like fashion product資料集:https://github.com/zalandoresearch/fashion-mnist
然而,我認為沒有任何理由可以避免使用真實的照片。
我們將在CIFAR-10上工作,這是一個經典的小彩色影象集。60000個32×32彩色影象,10個類,每個類有6000個影象。有50000個訓練影象(也就是我們用來訓練神經網路的那個)和10000個測試影象。
- CIFAR-10:https://www.cs.toronto.edu/~kriz/cifar.html
看一下這些樣本圖片:
帶有示例影象的CIFAR-10類
動手實踐 在開始之前:
- 建立一個Neptune賬戶,建立地址☞ https://neptune.ml/
- 克隆或複製https://github.com/neptune-ml/hands-on-deep-learning;我們使用的所有指令碼都需要從cifar_image_classification目錄中執行。
- 在Neptune上,點選專案,建立一個新的CIFAR-10(使用程式碼:CIF)。
程式碼在Keras中,地址☞ https://keras.io/
我們將使用Python 3和TensorFlow後端。該程式碼中唯一的特定於Neptune的部分是logging。如果你想在另一個基礎設施上執行它,只需更改幾行。
架構和塊(在Keras中)
將深度學習與經典機器學習區別開來的是它的組合架構。我們不再使用one-class分類器(即邏輯迴歸、隨機森林或XGBoost),而是建立一個由塊(稱為層)構成的網路。
深度學習隱喻:將ConvNet層比作Jenga塊
邏輯迴歸 讓我們從一個簡單的“多類邏輯迴歸”開始。它是一種“淺層”的機器學習技術,但可以用神經網路語言表達。它的體系架構只包含一個有意義的層。在Keras,我們寫如下:
model= Sequential()
model.add(Flatten(input_shape=(32,32,3)))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer=’adam’,
loss='categorical_crossentropy',
metrics=['accuracy'])
如果我們想一步一步地看到資料流的變化,關於維度和要優化的權重的數量,我們可以使用一個keras-sequential-ascii指令碼:
- 指令碼地址:https://github.com/stared/keras-sequential-ascii
OPERATION DATA DIMENSIONS WEIGHTS(N) WEIGHTS(%)
Input ##### 32 32 3
Flatten |||||------------------- 0 0.0%
##### 3072
Dense XXXXX------------------- 30730 100.0%
softmax ##### 10
Flatten層只是將(x, y, channels)轉換為畫素值的flat向量。密集層將所有輸入連線到所有的輸出。然後,Softmax將實數轉化為概率。
要執行它,只需輸入終端:
$ neptune send lr.py--environment keras-2.0-gpu-py3--worker gcp-gpu-medium
這將開啟一個瀏覽器選項卡,在這個選項卡中你可以跟蹤訓練過程。你甚至可以檢視錯誤分類的圖片。然而,這個線性模型主要是在影象上尋找顏色和它們的位置。
Neptune通道儀表盤中顯示的錯誤分類的影象
整體得分並不令人印象深刻。我在訓練集上的準確率達到了41%,更重要的是,37%的準確率在驗證上。請注意,10%是進行隨機猜測的基線。
多層感知器 老式的神經網路由幾個密集的層組成。在層之間,我們需要使用一個啟用函式。該函式分別應用於每個元件,使我們可以使其非線性,使用比邏輯迴歸更復雜的模式。之前的方法(由生物神經網路的抽象所激發)使用了一個S函式。
model= Sequential()
model.add(Flatten(input_shape=(32,32,3)))
model.add(Dense(128, activation='sigmoid'))
model.add(Dense(128, activation='sigmoid'))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer=adam,
loss='categorical_crossentropy',
metrics=['accuracy'])
這對我們的資料意味著什麼?
OPERATION DATA DIMENSIONS WEIGHTS(N) WEIGHTS(%)
Input ##### 32 32 3
Flatten |||||------------------- 0 0.0%
##### 3072
Dense XXXXX------------------- 393344 95.7%
sigmoid ##### 128
Dense XXXXX------------------- 16512 4.0%
sigmoid ##### 128
Dense XXXXX------------------- 1290 0.3%
softmax ##### 10
我們使用了兩個額外的(所謂的隱藏的)層,每個層都帶有S函式作為其啟用函式。讓我們來執行它!
$ neptune send mlp.py--environment keras-2.0-gpu-py3--worker gcp-gpu-medium
我建議在一個繪圖上建立一個結合訓練和驗證通道(validation channels)的自定義圖表。
訓練集和驗證集的準確性和log-loss
原則上,即使有一個隱藏的層,也可以近似任何函式 (參見:萬能近似定理,universal approximation theorem)。然而,這並不意味著它在實踐中工作得很好,因為資料量十分有限。如果隱藏層太小,它就無法近似任何函式。當它變得太大時,網路很容易就會變得過度擬合——也就是記憶訓練資料,但不能概括為其他影象。任何時候,你的訓練分數都會以驗證分數的成本上升,然後你的網路就會變得不過度擬合。
我們可以在驗證集上獲得大約45%的準確率,這是對邏輯迴歸的改進。不過,我們可以做得更好。如果你想要使用這種網路——編輯檔案,執行它(我建議在命令列中新增——tags my-experiment),看看你是否能做得更好。採取一些方法,看看結果如何。
提示:
- 使用20個以上的epoch。
- 在實踐中,神經網路使用2-3個密集層。
- 做大的改變來看看區別。在這種情況下,將隱藏層的大小更改為2x,甚至是10x。
僅僅因為理論上你應該能夠用畫圖的方式來建立任何圖片(或者甚至是任何照片),這並不意味著它將在實踐中起作用。我們需要利用空間結構,並使用卷積神經網路(CNN)。
卷積神經網路 我們可以用更智慧的方式處理影象,而不是試圖把所有東西都連線起來。卷積是在影象的每個部分執行相同的區域性操作的操作。卷積可以做的一些例子包括模糊,放大邊緣或者檢測顏色梯度。參見:http://setosa.io/ev/image-kernels/
每一個卷積層都根據之前的內容產生新的通道。首先,我們從紅色、綠色和藍色(RGB)元件的三個通道開始。接下來,通道變得越來越抽象。
當我們建立表示影象的各種屬性的通道時,我們需要降低解析度(通常使用max-pooling)。此外,現代網路通常使用ReLU作為啟用功能,因為它對更深層的模型效果更好。
model= Sequential()
model.add(Conv2D(32, (3,3), activation='relu',
input_shape=(32,32,3)))
model.add(MaxPool2D())
model.add(Conv2D(64, (3,3), activation='relu'))
model.add(MaxPool2D())
model.add(Flatten())
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
網路架構是這樣的:
OPERATION DATA DIMENSIONS WEIGHTS(N) WEIGHTS(%)
Input ##### 32 32 3
Conv2D |/ ------------------- 896 2.1%
relu ##### 30 30 32
MaxPooling2D Ymax ------------------- 0 0.0%
##### 15 15 32
Conv2D |/ ------------------- 18496 43.6%
relu ##### 13 13 64
MaxPooling2D Ymax ------------------- 0 0.0%
##### 6 6 64
Flatten |||||------------------- 0 0.0%
##### 2304
Dense XXXXX------------------- 23050 54.3%
softmax ##### 10
要執行它,我們需要輸入:
$ neptune send cnn_simple.py--environment keras-2.0-gpu-py3--worker gcp-gpu-medium
即使使用這個簡單的神經網路,我們在驗證上的準確率也達到了70%。這比我們用邏輯迴歸或者多層感知器產生的結果要多得多!
現在,你可以自由地進行實驗。
提示:
- 一般來說,3×3卷積是最好的;堅持使用它們(和只使用混合通道的1×1卷積)。
- 在進行每個MaxPool操作之前,你要有1-3個卷積層。
- 新增一個密集層可能會有所幫助。
- 在密集層之間,你可以使用Dropout,以減少過度擬合(例如,如果你發現訓練的準確性高於驗證的準確性)。
這僅僅是個開始。
要比較結果,請單擊專案名稱(project name)。你將看到整個專案列表。在Manage columns中,記錄所有的accuracy score(以及可能的log-loss)。你可以使用驗證精度(validation accuracy)來整理你的結果。
除了架構(這是一個大問題),優化器還會顯著地改變總體結果的準確性。通常情況下,我們通過新增更多的epoch(即整個訓練資料集的次數)來獲得更好的結果,同時降低學習率(learning rate)。
例如,試試這個網路:
OPERATION DATA DIMENSIONS WEIGHTS(N) WEIGHTS(%)
Input ##### 32 32 3
Conv2D |/ ------------------- 896 0.1%
relu ##### 32 32 32
Conv2D |/ ------------------- 1056 0.2%
relu ##### 32 32 32
MaxPooling2D Ymax ------------------- 0 0.0%
##### 16 16 32
BatchNormalization μ|σ ------------------- 128 0.0%
##### 16 16 32
Dropout | ||------------------- 0 0.0%
##### 16 16 32
Conv2D |/ ------------------- 18496 2.9%
relu ##### 16 16 64
Conv2D |/ ------------------- 4160 0.6%
relu ##### 16 16 64
MaxPooling2D Ymax ------------------- 0 0.0%
##### 8 8 64
BatchNormalization μ|σ ------------------- 256 0.0%
##### 8 8 64
Dropout | ||------------------- 0 0.0%
##### 8 8 64
Conv2D |/ ------------------- 73856 11.5%
relu ##### 8 8 128
Conv2D |/ ------------------- 16512 2.6%
relu ##### 8 8 128
MaxPooling2D Ymax ------------------- 0 0.0%
##### 4 4 128
BatchNormalization μ|σ ------------------- 512 0.1%
##### 4 4 128
Dropout | ||------------------- 0 0.0%
##### 4 4 128
Flatten |||||------------------- 0 0.0%
##### 2048
Dense XXXXX------------------- 524544 81.6%
relu ##### 256
Dropout | ||------------------- 0 0.0%
##### 256
Dense XXXXX------------------- 2570 0.4%
softmax ##### 10
$ neptune send cnn_adv.py--environment keras-2.0-gpu-py3--worker gcp-gpu-medium
這個過程需要大約半個小時,但是結果會更好—驗證的準確性應該在83%左右!