在Keras環境下構建多層感知器模型,對數字影像進行精確識別。
模型不消耗大量計算資源,使用了cpu版本的keras,以Tensorflow 作為backended,在ipython互動環境jupyter notebook中進行編寫。
1.資料來源
在Yann LeCun的部落格頁面上下載開源的mnist資料庫:
http://yann.lecun.com/exdb/mn…
此資料庫包含四部分:訓練資料集、訓練資料集標籤、測試資料集、測試資料集標籤。由於訓練模型為有監督型別的判別模型,因此標籤必不可少。若使用該資料集做k-means聚類,則不需要使用標籤。將資料整合之後放入user\.keras\datasets資料夾以供呼叫。
也可以直接從keras建議的url直接下載:
https://s3.amazonaws.com/img-…
其中訓練資料集包含了60000張手寫數字的圖片和這些圖片分別對應的標籤;測試資料集包含了10000張手寫數字的圖片和這些圖片分別對應的標籤.
2.資料格式和前期處理(在此不涉及)
訓練資料集包含60000張圖片,測試資料集包含10000張,所有圖片都被當量化為28pixel*28pixel的大小。為減少向量長度,將圖片灰度處理,每個畫素用一個RGB值表示(0~255),這是因為灰度處理後的RGB值加了歸一約束,向量長度相是灰度處理前的1/3。至此,每個圖片都可以用28*28的向量表示。
3.匯入依賴庫
開啟jupyter notebook後匯入依賴庫numpy,此處的seed為隨機量的標籤,可隨意設定:
1 2 3 |
from __future__ import print_function import numpy as np np.random.seed(9999) |
繼續從keras中匯入使用到的模組:
1 2 3 4 5 |
from keras.datasets import mnist from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation from keras.optimizers import SGD, Adam, RMSprop from keras.utils import np_utils |
mnist為之前準備的資料集,Dense為全連線神經元層,Dropout為神經元輸入的斷接率,Activation為神經元層的激勵函式設定。
匯入繪圖工具,以便之後繪製模型簡化圖:
1 |
from keras.utils.vis_utils import plot_model as plot |
4.處理匯入的資料集
處理資料集
- 為了符合神經網路對輸入資料的要求,原本為60000*28*28shape的三維ndarray,改變成了尺寸為60000*784的2維陣列,每行為一個example,每一列為一個feature。
- 神經網路用到大量線性與求導運算,將輸入的feature的數值型別改變為32位float。
- 將feature值歸一化,原本0~255的feature歸一為0~1。
- 測試資料集同理。
1 2 3 4 5 6 7 |
(X_train, y_train), (X_test, y_test) = mnist.load_data() X_train = X_train.reshape(60000, 28*28) X_test = X_test.reshape(10000, 28*28) X_train = X_train.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_test /= 255 |
處理標籤
文字識別問題本質是一個多元分類問題。將類向量轉換為二進位制數表示的類矩陣,其中每一行都是每一個example對應一個label。label為10維向量,每一位代表了此label對應的example屬於特定類(0~10)的概率。此時Y_train為60000*10的向量,Y_test為10000*10的向量
1 2 |
Y_train = np_utils.to_categorical(y_train, nb_classes) Y_test = np_utils.to_categorical(y_test, nb_classes) |
5.用keras建立神經網路模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
batch_size = 128 nb_classes = 10 nb_epoch = 20 model = Sequential() model.add(Dense(500, input_shape=(28*28,))) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(Dense(500)) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(Dense(500)) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(Dense(10)) model.add(Activation('softmax')) |
每次iter時,每一批次梯度下降運算所包含的example數量為128;
softmax輸出值為10維向量;
一共迭代20次iteration。
三層的神經網路,其中輸入層為28*28=784維的全連線層。
Hidden Layer有3層,每一層有500個神經元,input layer->hidden layer->output layer都是全連線方式(DENSE)。
hidden layer的啟用函式採用ReLu函式,表示式:
相比與傳統的sigmoid函式,ReLU更容易學習優化。因為其分段線性性質,導致其前傳、後傳、求導都是分段線性。而傳統的sigmoid函式,由於兩端飽和,在傳播過程中容易丟棄資訊。且Relu在x
文字識別本質是多元分類(此處為10元分類),因此輸出層採用softmax函式進行feature處理,如下圖所示:
其中第j個輸出層神經元輸出值與當層輸入feature的關係為:
呼叫summary方法做一個總覽:
1 |
model.summary() |
結果如下:
該神經網路一共有898510個引數,即在後向反饋過程中,每一次用梯度下降都要求898510次導數。
用plot函式列印model:
1 |
plot(model, to_file='mlp_model.png',show_shapes=True) |
如下圖所示:
編譯模型,使用cross_entropy交叉熵函式作為loss function,公式如下圖所示:
用交叉熵可量化輸出向量與標籤向量的差異,p與q分別為輸出向量與標籤向量。對於每一個example,其交叉熵值就是要通過迭代儘量往小優化的值。優過程使用梯度演算法,計算過程中使用反向傳播演算法求導。
交叉熵的作用如下圖所示:
在此分類神經網路中,使用判別結果的accuracy作為引數值好壞的度量標準。
6.用資料訓練和測試網路
1 2 3 |
history = model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch, verbose=1, validation_data=(X_test, Y_test)) |
在這個地方執行碰到warning,原因是最新版的keras使用的iteration引數名改成了epoch,而非之前沿用的nb_epoch。將上面的程式碼作修改即可。
訓練結果如下所示。第一次迭代,通過對60000/128個的batch訓練,已經達到了比較好的結果,accuracy已經高達0.957。之後Loss值繼續下降,精確度繼續上升。從第9個itearation開始,loss函式值(交叉熵cross_entropy)開始震盪在0.05附近,accuracy保持在0.98以上。說明前9次迭代就已經訓練了足夠好的θ值和bias,不需要後11次訓練。
7.評估模型
用score函式列印模型評估結果:
1 2 3 |
score = model.evaluate(X_test, Y_test, verbose=0) print('Test score:', score[0]) print('Test accuracy:', score[1]) |
輸出結果如下圖所示:
訓練的multi-layer_perceptron神經網路在對數字文字識別時具有98.12%的準確率。
手寫數字圖片資料庫和Iris_Flower_dataset一樣,算是dl界的基本素材,可以拿來做很多事情,比如k-means聚類,LSTM(長短記憶網路)。