下載 Mnist 資料
我們將建立以下 Keras 程式, 下載並讀取 mnist 資料.
STEP1. 匯入 Keras 及相關模組
首先匯入 Keras 及相關模組:
- import numpy as np
- import pandas as pd
- from keras.utils import np_utils # 用來後續將 label 標籤轉為 one-hot-encoding
- np.random.seed(10)
- from keras.datasets import mnist
STEP3. 讀取與查看 mnist 資料
- (X_train_image, y_train_label), (X_test_image, y_test_label) = mnist.load_data()
- print("\t[Info] train data={:7,}".format(len(X_train_image)))
- print("\t[Info] test data={:7,}".format(len(X_test_image)))
由上可以知道 training data 共有 60,000 筆; testing data 共有 10,000 筆.
查看訓練資料
接著我們來看載入資料的長相與格式.
STEP1. 訓練資料是由 images 與 labels 所組成
- print("\t[Info] Shape of train data=%s" % (str(X_train_image.shape)))
- print("\t[Info] Shape of train label=%s" % (str(y_train_label.shape)))
訓練資料是由 images 與 labels 所組成共有 60,000 筆, 每一筆代表某個數字的影像為 28x28 pixels.
STEP2. 定應 plot_image 函數顯示數字影像
- import matplotlib.pyplot as plt
- def plot_image(image):
- fig = plt.gcf()
- fig.set_size_inches(2,2)
- plt.imshow(image, cmap='binary') # cmap='binary' 參數設定以黑白灰階顯示.
- plt.show()
以下程式呼叫 plot_image 函數, 傳入 X_train_image[0], 也就是順練資料集的第 0 筆資料, 顯示結果可以看到這是一個數字 5 的圖形:
查看多筆訓練資料 images 與 labels
接下來我們將建立 plot_images_labels_predict 函數, 可以顯示多筆資料的影像與 label.
STEP1. 建立 plot_images_labels_predict() 函數
因為後續我們希望能很方便查看數字圖形, 真實的數字與預測結果, 所以我們建立了以下函數:
- def plot_images_labels_predict(images, labels, prediction, idx, num=10):
- fig = plt.gcf()
- fig.set_size_inches(12, 14)
- if num > 25: num = 25
- for i in range(0, num):
- ax=plt.subplot(5,5, 1+i)
- ax.imshow(images[idx], cmap='binary')
- title = "l=" + str(labels[idx])
- if len(prediction) > 0:
- title = "l={},p={}".format(str(labels[idx]), str(prediction[idx]))
- else:
- title = "l={}".format(str(labels[idx]))
- ax.set_title(title, fontsize=10)
- ax.set_xticks([]); ax.set_yticks([])
- idx+=1
- plt.show()
多層感知器模型資料前處理
接下來我們建立 多層感知器模型 (MLP), 我們必須先將 images 與 labels 的內容進行前處理, 才能餵進去 Keras 預期的資料結構.
STEP1. features (數字影像的特徵值) 資料前處理
首先將 image 以 reshape 轉換為二維 ndarray 並進行 normalization (Feature scaling):
- x_Train = X_train_image.reshape(60000, 28*28).astype('float32')
- x_Test = X_test_image.reshape(10000, 28*28).astype('float32')
- print("\t[Info] xTrain: %s" % (str(x_Train.shape)))
- print("\t[Info] xTest: %s" % (str(x_Test.shape)))
- # Normalization
- x_Train_norm = x_Train/255
- x_Test_norm = x_Test/255
label 標籤欄位原本是 0-9 數字, 而為了配合 Keras 的資料格式, 我們必須進行 One-hot-encoding 將之轉換為 10 個 0 或 1 的組合, 例如數字 7 經過 One-hot encoding 轉換後是 0000001000, 正好對應到輸出層的 10 個神經元. 下面簡單測試過程:
建立模型
我們將建立以下多層感知器 Multilayer Perceptron 模型, 輸入層 (x) 共有 28x28=784 個神經元, Hidden layers (h) 共有 256 層; 輸出層 (y) 共有 10 個 神經元:
對應代碼:
- from keras.models import Sequential
- from keras.layers import Dense
- model = Sequential() # Build Linear Model
- model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu')) # Add Input/hidden layer
- model.add(Dense(units=10, kernel_initializer='normal', activation='softmax')) # Add Hidden/output layer
- print("\t[Info] Model summary:")
- model.summary()
- print("")
進行訓練
當我們建立深度學習模型後, 就可以使用 Backpropagation 進行訓練.
STEP1. 定義訓練方式
在訓練模型之前, 我們必須先使用 compile 方法, 對訓練模型進行設定, 代碼如下:
- model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
STEP2. 開始訓練
執行訓練的程式碼如下:
- train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=200, verbose=2)
某次的執行過程如下:
STEP3. 建立 show_train_history 顯示訓練過程
之前訓練步驟會將每一個訓練週期的 accuracy 與 loss 記錄在 train_history 變數. 我們可以使用下面程式碼讀取 train_history 以圖表顯示訓練過程:
- import matplotlib.pyplot as plt
- def show_train_history(train_history, train, validation):
- plt.plot(train_history.history[train])
- plt.plot(train_history.history[validation])
- plt.title('Train History')
- plt.ylabel(train)
- plt.xlabel('Epoch')
- plt.legend(['train', 'validation'], loc='upper left')
- plt.show()
- show_train_history(train_history, 'acc', 'val_acc')
如果 "acc 訓練的準確率" 一直提升, 但是 "val_acc 的準確率" 卻一直沒有增加, 就有可能是 Overfitting 的現象 (更多說明請參考 Bias, Variance, and Overfitting). 在完成所有 (epoch) 訓練週期後, 在後面還會使用測試資料來評估模型準確率, 這是另外一組獨立的資料, 所以計算準確率會更客觀. 接著我們來看 loss 誤差的執行結果:
- show_train_history(train_history, 'loss', 'val_loss')
以測試資料評估模型準確率與預測
我們已經完成訓練模型, 現在要使用 test 測試資料來評估模型準確率.
STEP1. 評估模型準確率
使用下面代碼評估模型準確率:
- scores = model.evaluate(x_Test_norm, y_TestOneHot)
- print()
- print("\t[Info] Accuracy of testing data = {:2.1f}%".format(scores[1]*100.0))
STEP2. 進行預測
前面我們建立模型並於訓練後達成可以接受的 97% 準確率, 接著我們將使用此模型進行預測.
- print("\t[Info] Making prediction to x_Test_norm")
- prediction = model.predict_classes(x_Test_norm) # Making prediction and save result to prediction
- print()
- print("\t[Info] Show 10 prediction result (From 240):")
- print("%s\n" % (prediction[240:250]))
- if isDisplayAvl():
- plot_images_labels_predict(X_test_image, y_test_label, prediction, idx=240)
- print("\t[Info] Error analysis:")
- for i in range(len(prediction)):
- if prediction[i] != y_test_label[i]:
- print("\tAt %d'th: %d is with wrong prediction as %d!" % (i, y_test_label[i], prediction[i]))
上面可以發現有個預測結果為 2, 但實際 label 為 4.
顯示混淆矩陣 (Confusion matrix)
如果我們想要進一步知道建立的模型中, 那些數字預測準確率最高, 那些數字最容易混淆, 此時可以使用混淆矩陣 (Confusion matrix). 在機器學習領域, 特別是統計分類的問題, 混淆矩陣 (也稱為 error matrix) 是一種特定的表格顯示方式, 可以讓我們以視覺化的方式, 了解 Supervisored Learning 的結果, 看出訓練出來的模型在各個類別的表現狀況.
STEP1. 使用 pandas crosstab 建立混淆矩陣 (Confusion matrix)
- print("\t[Info] Display Confusion Matrix:")
- import pandas as pd
- print("%s\n" % pd.crosstab(y_test_label, prediction, rownames=['label'], colnames=['predict']))
由上可以發現:
STEP2. 建立真實與預測的 dataframe
我們希望找出那些 label 結果為 "5" 的結果被預測成 "3" 的資料, 所以建立的下面的 dataframe:
STEP3. 查詢 label=5; prediction=3 的資料
Pandas Dataframe 可以讓你很方便的查詢資料:
STEP4. 查看第 340 筆資料
隱藏層增加為 1000 個神經元
為了增加準確率, 我們將 Hidden layers 的數目從 256 提升到 1000 個神經元:
STEP1. 修改模型
- from keras.models import Sequential
- from keras.layers import Dense
- model = Sequential() # Build Linear Model
- model.add(Dense(units=1000, input_dim=784, kernel_initializer='normal', activation='relu')) # Modify hidden layer from 256 -> 1000
- model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))
- print("\t[Info] Model summary:")
- model.summary()
- print("")
先來看看模型的 summary:
在最後一輪的 Epoch 得到的結果:
從下面的 "accuracy" vs "validation accuracy" 的圖可以看出兩者差距拉大 (training accuracy > validation accuracy), 說明 Overfitting 問題變嚴重:
最後在 testing data 上的 accuracy 有微微上升: 97.6% -> 97.9%:
多層感知器加入 DropOut 功能以避免 Overfitting
為了解決 Overfitting 問題, 接下來會加入 Dropout 功能, 以避免 Overfitting, 關於 Dropout 的簡單說明如下:
STEP1. 修改隱藏層加入 DropOut 功能
- ...
- from keras.models import Sequential
- from keras.layers import Dense
- from keras.layers import Dropout # ***** Import DropOut mooule *****
- model = Sequential()
- model.add(Dense(units=1000, input_dim=784, kernel_initializer='normal', activation='relu'))
- model.add(Dropout(0.5)) # ***** Add DropOut functionality *****
- model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))
- print("\t[Info] Model summary:")
- model.summary()
- print("")
- ...
模型摘要:
最後一個 Epoch 的執行結果可以發現 acc 與 val_acc 接近許多, 說明 Overfitting 問題有被解決:
這也反應在 accuracy 的圖表上:
testing data 的 accuracy 也上到了 98%:
建立多層感知器模型 (包含兩個 Hidden Layers)
為了進一步提升準確率, 我們打算提升多元感知器 Hidden layer 的層數.
STEP1. 變更模型使用兩個 Hidden Layers 並加入 DropOut 功能
- ...
- from keras.models import Sequential
- from keras.layers import Dense
- from keras.layers import Dropout # Import DropOut mooule
- model = Sequential() # Build Linear Model
- model.add(Dense(units=1000, input_dim=784, kernel_initializer='normal', activation='relu')) # Add Input/ first hidden layer
- model.add(Dropout(0.5)) # Add DropOut functionality
- model.add(Dense(units=1000, kernel_initializer='normal', activation='relu')) # Add second hidden layer
- model.add(Dropout(0.5)) # Add DropOut functionality
- model.add(Dense(units=10, kernel_initializer='normal', activation='softmax')) # Add Hidden/output layer
- print("\t[Info] Model summary:")
- model.summary()
- print("")
- ...
由 accuracy 圖可以看出 training accuracy 與 validation accuracy 已經相當接近, 說明 Overfitting 的影響又被改善了:
最後一輪 Epoch 的結果與 testing data 的 accuracy 如下:
完整代碼連結如下:
Supplement
* Matplotlib - Basic Introduction For ML/DataScience
* Deep learning:四十一(Dropout簡單理解)
很讚!!
回覆刪除建議可以買書來看 (其他的章節也不錯), 支持一下原著瞜. :p
刪除請問是哪本書~?
刪除Check http://www.books.com.tw/products/0010754327
刪除Hi John
刪除Nice post.
類別 "5" 的準確率最低共有 852 筆.
類別 "5" 的準確率最低應該是856筆吧。
Hi 東海林將司, You are right. Thanks for the feedback!
刪除