MXNet手寫體識別

在本教程中,我們將逐步介紹如何使用MNIST數據集構建手寫的數字分類器.對于一個剛剛接觸深度學習的朋友來說,這個練習可以說是“Hello World”.

Note:This blog without all the resources that are outside the original,reproduced please indicate the source.

官網:mxnet.incubator.apache.org,文中如有任何錯漏的地方請諒解并指出,不勝感激.

MNIST是一個廣泛使用的數據集,用于手寫數字分類任務。它由70,000個標記為28x28像素的手寫數字的灰度圖像組成。數據集被分成60,000個訓練圖像和10,000個測試圖像。它總共有10個類(每個10個數字對應一個)。目前的任務是用6萬張訓練圖像訓練一個模型,然后測試其在10,000個測試圖像上的分類精度。

圖一:來自MNIST數據集的示例圖像

前提

完成教程,您需要:

- ?安裝好MXNet 0.10 以上版本
- 安裝python以及Jupyter Notebook.
$ pip install requests jupyter

加載數據

在我們定義模型之前,讓我們先獲取MNIST數據集。

下面的源代碼下載并將圖像和相應的標簽載入內存。

import mxnet as mx
mnist = mx.test_utils.get_mnist()

在運行上述源代碼之后,整個MNIST數據集應該被完全加載到內存中。注意,對于大型數據集,預先加載整個數據集是不可行的,就像我們在這里做的那樣。我們需要的是一種機制,讓我們可以快速有效地從源頭直接流數據。MXNet數據迭代器通過提供精確的數據來補償這里。數據迭代器是我們將輸入數據輸入到MXNet訓練算法中的一種機制,它們對于初始化和使用非常簡單,并且對速度進行了優化。在訓練過程中,我們通常會小批量的訓練樣本,并且在整個訓練周期中,我們會最后多次處理每個訓練實例。在本教程中,我們將配置數據迭代器來以100為批次的方式提供示例。請記住,每個示例都是一個28x28灰度圖像和相應的標簽。

圖像批次通常用一個4-D(?四維)數組表示(行如:batch_size,num_channels,width,height)。對于MNIST數據集,由于圖像是灰度的,所以只有一個顏色通道。另外,因為圖像是28x28像素,所以每個圖像的寬度和高度等于28。因此,輸入應該是這樣:(batch_size,1,28,28)。另一個重點關注的是輸入樣本的順序。當提供訓練樣本時,我們不能連續地給樣本提供相同的標簽,這是非常重要的。這樣做會減緩訓練。數據迭代器通過隨機打亂輸入來解決這個問題。注意,我們只需要打亂訓練數據。這個順序對測試數據沒有影響。

下面的源代碼初始化了MNIST數據集的數據迭代器。請注意,我們初始化了兩個迭代器:一個用于訓練數據,一個用于測試數據。

batch_size = 100
train_iter = mx.io.NDArrayIter(mnist['train_data'], mnist['train_label'], batch_size, shuffle=True)
val_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)

訓練

我們將介紹幾個執行手寫數字識別任務的方法。第一種方法利用傳統的深度神經網絡結構,稱為多層感知器(MLP)。我們將討論它的缺點,并以此為動機引入第二種更高級的方法,稱為卷積神經網絡(CNN),它已經被證明能夠很好地處理圖像分類任務。

多層感知器

第一個方法是利用多層感知器來解決這個問題。我們將使用MXNet的符號接口定義MLP。我們首先為輸入數據創建一個place holder變量。在使用MLP時,我們需要將28x28的圖像壓縮成一個平面一維結構的784(28 * 28)原始像素值。在平坦的矢量中,像素值的順序并不重要,只要我們在所有圖像上都是一致的。

data = mx.sym.var('data')
# Flatten the data from 4-D shape into 2-D (batch_size, num_channel*width*height)
data = mx.sym.flatten(data=data)

您可能會有疑問,我們是否通過扁平化來丟棄有價值的信息。這確實是事實,在保留輸入形狀的問題上我們會在討論卷積神經網絡的時候進行詳細的介紹。現在,我們將繼續使用扁平的圖像。

MLP包含幾個完全連接層。一個完全連接層或簡稱FC層,是層中的每個神經元與前一層中的每個神經元相連接的地方。從線性代數的角度來看,FC層對n x m輸入矩陣x進行仿射變換,并輸出n x k大小的矩陣Y,其中kFC層中的神經元數。k也被稱為隱藏的大小。輸出Y是根據等式Y = WX + b計算的,FC層有兩個可學習的參數,即mxk權重矩陣W和mx1偏差向量b

在MLP中,大多數FC層的輸出都被輸入到一個激活函數中,適用于非線性元素。這一步是至關重要的,它給神經網絡提供了分類輸入的能力,而這些輸入不是線性可分的。激活函數的常見選擇是sigmoidtanhrecUNK linear unit(ReLU)。在這個示例中,我們將使用具有多個理想屬性的ReLU激活函數,通常他被認為是默認選項。

下面的代碼聲明了兩個完全連接層,每個層有128個和64個神經元。此外,這些FC層被夾在ReLU激活層之間,每個層負責執行在FC層輸出上執行元素的ReLU轉換。

# The first fully-connected layer and the corresponding activation function
fc1  = mx.sym.FullyConnected(data=data, num_hidden=128)
act1 = mx.sym.Activation(data=fc1, act_type="relu")

# The second fully-connected layer and the corresponding activation function
fc2  = mx.sym.FullyConnected(data=act1, num_hidden = 64)
act2 = mx.sym.Activation(data=fc2, act_type="relu")

最后一個完全連接的層通常有其隱藏的大小,等于數據集中的輸出類的數量。這個層的激活函數將是softmax函數。Softmax層將其輸入映射為每一類輸出的概率分數。在訓練階段,一個損失函數計算網絡預測的概率分布(softmax output)與標簽給出的真實概率分布之間的交叉熵。

下面的源代碼聲明了最終全連接的10級的層。順便說一下,10是數字的總數。該層的輸出被輸入到一個軟maxoutput層,在一個過程中執行軟max和交叉熵損失計算。請注意,損失計算只在訓練期間發生。

# MNIST has 10 classes
fc3  = mx.sym.FullyConnected(data=act2, num_hidden=10)
# Softmax with cross entropy loss
mlp  = mx.sym.SoftmaxOutput(data=fc3, name='softmax')
圖二:MNIST的MLP網絡架構

現在,數據迭代器和神經網絡都被定義了,我們可以開始訓練了。在這里,我們將使用MXNet中的模塊特性,它為在預定義網絡上運行訓練和推理提供高級抽象。模塊API允許用戶指定適當的參數來控制培訓的進展。

下面的源代碼初始化一個模塊來訓練我們上面定義的MLP網絡。對于我們的培訓,我們將使用隨機梯度下降(SGD)優化器。特別地,我們將使用迷你批處理SGD。標準的SGD進程一次只處理一個示例。在實踐中,這是非常緩慢的,一個可以通過小批量的例子來加快進程。在這種情況下,我們的批量大小為100,這是一個合理的選擇。我們在這里選擇的另一個參數是學習速率,它控制優化器獲取解決方案所需的步驟大小。我們會選擇一個0.1的學習率,也是一個合理的選擇。諸如批量大小和學習速率等設置通常被稱為超參數。我們給予他們的價值觀會對他們的訓練有很大的影響。為了本教程的目的,我們將從一些合理和安全的值開始。在其他教程中,我們將討論如何為最佳模型性能尋找超參數的組合。

通常情況下,一個運行訓練直到收斂,這意味著我們從訓練數據中學習了一組很好的模型參數(權重+偏差)。為了本教程的目的,我們將運行10次訓練并停止。?一次訓練是整個列車數據的一個完整的傳遞。

import logging
logging.getLogger().setLevel(logging.DEBUG)  # logging to stdout
# create a trainable module on CPU
mlp_model = mx.mod.Module(symbol=mlp, context=mx.cpu())
mlp_model.fit(train_iter,  # train data
              eval_data=val_iter,  # validation data
              optimizer='sgd',  # use SGD to train
              optimizer_params={'learning_rate':0.1},  # use fixed learning rate
              eval_metric='acc',  # report accuracy during training
              batch_end_callback = mx.callback.Speedometer(batch_size, 100), # output progress for each 100 data batches
              num_epoch=10)  # train for at most 10 dataset passes
              

預測

在上述培訓完成后,我們可以通過對測試數據的預測來評估培訓的模型。下面的源代碼計算每個測試圖像的預測概率得分。prob[i][j]是第i個測試圖像包含j - th輸出類的概率。

test_iter = mx.io.NDArrayIter(mnist['test_data'], None, batch_size)
prob = mlp_model.predict(test_iter)
assert prob.shape == (10000, 10)

由于數據集也有所有的測試圖像的標簽,我們可以計算精度指標如下:

test_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)
# predict accuracy of mlp
acc = mx.metric.Accuracy()
mlp_model.score(test_iter, acc)
print(acc)
assert acc.get()[1] > 0.96

如果一切順利,我們應該看到一個大約0.96的精度值,這意味著我們能夠準確地預測96%的測試圖像中的數字。這是一個很好的結果。但正如我們將在本教程的下一部分中看到的,我們可以做得更好。

卷積神經網絡

上文中,我們簡要介紹了MLP的一個缺點,我們說到我們需要丟棄輸入圖像的原始形狀,并將它作為一個矢量來壓平,然后我們可以把它作為輸入到MLP的第一個完全連接的層。這是一個很重要的問題,因為我們沒有利用圖像中的像素在水平和垂直軸上具有自然空間相關的事實。一個卷積神經網絡(CNN)旨在通過使用更結構化的權重表示來解決這個問題。它沒有把圖像壓平,而是做一個簡單的矩陣乘法,而是使用一個或多個卷積層,每個層在輸入圖像上執行二維的卷積。

單個卷積層由一個或多個過濾器組成,每個過濾器都扮演特性檢測器的角色。在訓練過程中,CNN學習了這些過濾器的適當表示(參數)。類似于MLP,卷積層的輸出通過應用非線性轉換。除了卷積層之外,CNN的另一個關鍵方面是匯聚層。一個匯聚層可以使CNN的平移不變:即使在向左/右/向上移動一些像素時,數字仍然保持不變。池層將n個x m補丁減少為單個值,以使網絡對空間位置不敏感。在CNN的每一個conv(+激活)層之后都要包含池層。

下面的代碼定義了一個稱為LeNet的卷積神經網絡架構。LeNet是一個很受歡迎的網絡,它可以很好地處理數字分類任務。我們將使用與原來的LeNet實現稍微不同的版本,用tanh激活來代替神經元的sigmoid激活

data = mx.sym.var('data')
# first conv layer
conv1 = mx.sym.Convolution(data=data, kernel=(5,5), num_filter=20)
tanh1 = mx.sym.Activation(data=conv1, act_type="tanh")
pool1 = mx.sym.Pooling(data=tanh1, pool_type="max", kernel=(2,2), stride=(2,2))
# second conv layer
conv2 = mx.sym.Convolution(data=pool1, kernel=(5,5), num_filter=50)
tanh2 = mx.sym.Activation(data=conv2, act_type="tanh")
pool2 = mx.sym.Pooling(data=tanh2, pool_type="max", kernel=(2,2), stride=(2,2))
# first fullc layer
flatten = mx.sym.flatten(data=pool2)
fc1 = mx.symbol.FullyConnected(data=flatten, num_hidden=500)
tanh3 = mx.sym.Activation(data=fc1, act_type="tanh")
# second fullc
fc2 = mx.sym.FullyConnected(data=tanh3, num_hidden=10)
# softmax loss
lenet = mx.sym.SoftmaxOutput(data=fc2, name='softmax')
圖三

現在我們用同樣的超參數來訓練LeNet。注意,如果GPU可用,我們建議使用它。這大大加快了計算速度,因為LeNet比之前的多層感知器更加復雜和計算更密集。為此,我們只需要將mx. cpu()改為mx.gpu(),而MXNet則負責其余部分。就像以前一樣,我們將在10次訓練之后停止訓練。

# create a trainable module on GPU 0
lenet_model = mx.mod.Module(symbol=lenet, context=mx.cpu())
# train with the same
lenet_model.fit(train_iter,
                eval_data=val_iter,
                optimizer='sgd',
                optimizer_params={'learning_rate':0.1},
                eval_metric='acc',
                batch_end_callback = mx.callback.Speedometer(batch_size, 100),
                num_epoch=10)                

預測

最后,我們將使用經過訓練的LeNet模型來生成對測試數據的預測。

test_iter = mx.io.NDArrayIter(mnist['test_data'], None, batch_size)
prob = lenet_model.predict(test_iter)
test_iter = mx.io.NDArrayIter(mnist['test_data'], mnist['test_label'], batch_size)
# predict accuracy for lenet
acc = mx.metric.Accuracy()
lenet_model.score(test_iter, acc)
print(acc)
assert acc.get()[1] > 0.98

如果一切順利,我們應該會看到使用LeNet的預測更準確。在CNN,我們應該能夠正確預測98%的測試圖像。

小結

在本教程中,我們學習了如何使用MXNet來解決一個標準的計算機視覺問題:將手寫數字的圖像分類。您已經了解了如何快速、輕松地構建、訓練和評估諸如MLPCNN等模型,并使用MXNet

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容