# MNIST 手寫數字辨識資料集 ## 一、下載 Mnist 資料 ```python= # 1.匯入 Keras 及相關模組 import numpy as np import pandas as pd from keras.utils import np_utils # 用來後續將 label 標籤轉為 one-hot-encoding np.random.seed(10) # 2.下載 mnist data from keras.datasets import mnist # 3.讀取與查看 mnist data (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))) ``` >輸出: >>>>[Info] train data= 60,000 >>>>[Info] test data= 10,000 <br> <br> ## 二、查看訓練資料 ```python= # 1.訓練資料是由 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. # 2.建立 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() # 3.執行 plot_image 函數查看第 0 筆數字影像與 label 資料 plot_image(X_train_image[0]) print(y_train_label[0]) # 得:呼叫 plot_image 函數, 傳入 X_train_image[0], 也就是順練資料集的第 0 筆資料, 顯示結果可以看到這是一個數字 5 的圖形 ``` > 輸出: >>>>[Info] Shape of train data=(60000, 28, 28) >>>>[Info] Shape of train label=(60000,) <br> >>>>   >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/1e8c718157647a116a14c82d9ef4e65ceead2ec7/123.png?token=ATTETNCY7PJE45RAAEMU2NLEKBVM4) <br> >>>>5 <br> <br> ## 三、查看多筆訓練資料 images 與 labels ```python= # 1.建立 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() plot_images_labels_predict(X_train_image, y_train_label, [], 0, 10) ``` > 輸出: >>>>![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/a11006a881d02caaa4c6fc04c228296c32d78e1f/image.png?token=ATTETNAF5LZYGLBIDT6UVGTEKBXT4) <br> <br> ## 四、多層感知器模型資料前處理 * 建立 多層感知器模型 (MLP), 須先將 images 與 labels 的內容進行前處理, 才能餵進去 Keras 預期的資料結構. ```python= # 1.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 ``` > 輸出: >>>>[Info] xTrain: (60000, 784) >>>>[Info] xTest: (10000, 784) <br> ```python= # 2.labels (影像數字真實的值) 資料前處理 y_TrainOneHot = np_utils.to_categorical(y_train_label) # 將 training 的 label 進行 one-hot encoding y_TestOneHot = np_utils.to_categorical(y_test_label) # 將測試的 labels 進行 one-hot encoding # 檢視 training labels 第一個 label 的值 print (y_train_label[0]) # 檢視第一個 label 在 one-hot encoding 後的結果, 會在第六個位置上為 1, 其他位置上為 0 print(y_TrainOneHot[:1]) ``` > 輸出: >>>>5 >>>>[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]] :::info label 標籤欄位原本是 0-9 數字, 而為了配合 Keras 的資料格式, 必須進行 One-hot-encoding 將之轉換為 10 個 0 或 1 的組合, 例如數字 7 經過 One-hot encoding 轉換後是 0000001000, 正好對應到輸出層的 10 個神經元. ::: <br> <br> ## 五、建立模型 * 建立多層感知器 Multilayer Perceptron 模型: 輸入層 (x) 共有 28x28=784 個神經元, Hidden layers (h) 共有 256 層; 輸出層 (y) 共有 10 個 神經元 ```python= 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("") ``` > 輸出: >>>>![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/1.png?token=GHSAT0AAAAAACCDHM3UEA4MMEL4VT5HIZHUZCQPQPA) <br> <br> ## 六、進行訓練 * 建立深度學習模型後, 使用 Backpropagation 進行訓練 ```python= # 1.定義訓練方式 # 在訓練模型之前, 我們必須先使用 compile 方法, 對訓練模型進行設定 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) ``` :::info * loss: 設定 loss function, 在深度學習通常使用 cross_entropy (Cross entropy) 交叉摘順練效果較好. * optimizer: 設定訓練時的優化方法, 在深度學習使用 adam 可以讓訓練更快收斂, 並提高準確率. * metrics: 設定評估模型的方式是 accuracy (準確率) ::: ```python= # 2.開始訓練 train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=200, verbose=2) ``` :::info #### 上面訓練過程會儲存於 train_history 變數中: * x=x_Train_norm: features 數字的影像特徵值 (60,000 x 784 的陣列). * y=y_Train_OneHot: label 數字的 One-hot encoding 陣列 (60,000 x 10 的陣列) * validation_split = 0.2: 設定訓練資料與 cross validation 的資料比率. 也就是說會有 0.8 * 60,000 = 48,000 作為訓練資料; 0.2 * 60,000 = 12,000 作為驗證資料. * epochs = 10: 執行 10 次的訓練週期. * batch_size = 200: 每一批次的訓練筆數為 200 * verbose = 2: 顯示訓練過程. 共執行 10 次 epoch (訓練週期), 每批 200 筆, 也就是每次會有 240 round (48,000 / 200 = 240). 每一次的 epoch 會計算 accuracy 並記錄在 train_history 中. ::: ```python= # 3.建立 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,'accuracy','val_accuracy') show_train_history(train_history,'loss','val_loss') ``` > 輸出: >>>>![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/12.png?token=GHSAT0AAAAAACCDHM3UVULLK3AOMCEQODNGZCQPNQA) <br> >>>>![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/321.png?token=GHSAT0AAAAAACCDHM3VVWYLRYFHWSWXXQAKZCQPO2A) :::info 如果 "accuracy 訓練的準確率" 一直提升,但是 "val_accuracy 的準確率" 卻一直沒有增加,可能是 Overfitting 的現象. 在完成所有 (epoch) 訓練週期後,在後面還會使用測試資料來評估模型準確率, 這是另外一組獨立的資料,所以計算準確率會更客觀. #### 總共執行 10 個 Epoch 訓練週期, 發現: * 不論訓練與驗證, 誤差越來越低. * 在 Epoch 訓練後期, "loss 訓練的誤差" 比 "val_loss 驗證的誤差" 小. ::: <br> <br> ## 七、以測試資料評估模型準確率與預測 * 已經完成訓練模型, 現在要使用 test 測試資料來評估模型準確率. ```python= # 1.評估模型準確率 # 使用下面代碼評估模型準確率: scores = model.evaluate(x_Test_norm, y_TestOneHot) print() print("\t[Info] Accuracy of testing data = {:2.1f}%".format(scores[1]*100.0)) ``` >>>>[Info] Accuracy of testing data = 97.6% ```python= # 2.進行預測 print("\t[Info] Making prediction to x_Test_norm") prediction = model.predict(x_Test_norm) prediction = np.argmax(prediction,axis=1) print() print("\t[Info] Show 10 prediction result (From 240):") print("%s\n" % (prediction[240:250])) plot_images_labels_predict(X_test_image, y_test_label, prediction, idx=240) ``` >>>>[Info] Making prediction to x_Test_norm >>>313/313 [==============================] - 0s 1ms/step >>>[Info] Show 10 prediction result (From 240): >   [5 9 8 7 2 3 0 6 4 2] :::info prediction = model.predict_classes(x_Test_norm) predict_classes()函數在 TensorFlow 2.6 版本中被移除了以: 1. prediction = (model.predict(x_Test_norm) > 0.5).astype("int32") 2. prediction = model.predict(x_Test_norm) prediction = np.argmax(prediction,axis=1) 最後選擇第二選擇,因為前者為二進制 ::: <br> <br> ## 八、顯示混淆矩陣 (Confusion matrix) * 如果想要進一步知道建立的模型中,那些數字預測準確率最高,那些數字最容易混淆,此時可以使用混淆矩陣(Confusion matrix). * 在機器學習領域,特別是統計分類的問題,混淆矩陣(也稱為 error matrix)是一種特定的表格顯示方式,可以以視覺化的方式,了解Supervisored Learning的結果,看出訓練出來的模型在各個類別的表現狀況. ```python= # 1.使用 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'])) ``` >輸出: >>>>![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/12313.png?token=GHSAT0AAAAAACCDHM3V63IFACCGQVFJU3W2ZCQQKMA) :::info * 對角線是預測結果正確的數字, 我們發現類別 "1" 的預測準確率最高共有 1,125 筆; 類別 "5" 的準確率最低共有 852 筆. * 其他非對角線的數字, 代表將某一類別預測成其他類別的錯誤. 例如將類別 "5" 預測成 "3" 共發生 12 次. ::: ```python= # 2.建立真實與預測的 dataframe # 如找出那些 label 結果為 "5" 的結果被預測成 "3" 的資料, 所以建立的下面的 dataframe: df = pd.DataFrame({'label':y_test_label, 'predict':prediction}) df[:2] # 顯示前兩筆資料 ``` ```python= # 3.查詢 label=5; prediction=3 的資料 # Pandas Dataframe 可以讓你很方便的查詢資料: out = df[(df.label==5) & (df.predict==3)] # 查詢 label=5; predict=3 的 records out.__class__ # 輸出是另一個 DataFrame print(out) ``` >輸出: >>>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/222.png?token=GHSAT0AAAAAACCDHM3VES2FKBM6TKZAY4FQZCQQYTA) ```python= # 4.查看第 340 筆資料 plot_images_labels_predict(X_test_image, y_test_label, prediction, idx=340, num=1) ``` >輸出: >>>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/123133.png?token=GHSAT0AAAAAACCDHM3UZHHRFJQ6GT2TKTH2ZCQQ6QQ) ### 到目前為止模型準確率為97.8% <br> <br> ## 九、隱藏層增加為 1000 個神經元 * 為了增加準確率, 我們將 Hidden layers 的數目從 256 提升到 1000 個神經元: ```python= #1. 修改模型 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("") show_train_history(train_history,'accuracy','val_accuracy') ``` >輸出: >>>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/432.png?token=GHSAT0AAAAAACCDHM3VWG64AC4EEZ226SS6ZCQRLIA) <br> >>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/3211.png?token=GHSAT0AAAAAACCDHM3UXPJYHACS4ATZFOEQZCQS4VA) :::info #### 檢視執行結果 從下面的 "accuracy" vs "validation accuracy" 的圖可以看出兩者差距拉大 (training accuracy > validation accuracy), 說明 Overfitting 問題變嚴重 ::: <br> <br> ## 十、多層感知器加入 DropOut 功能以避免 Overfitting * 為了解決 Overfitting 問題, 接下來會加入 Dropout 功能, 以避免 Overfitting Dropout 是指在模型訓練時隨機讓網絡某些隱含層節點的權重不工作,不工作的那些節點可以暫時認為不是網絡結構的一部分,但是它的權重得保留下來(只是暫時不更新而已),因為下次樣本輸入時它可能又得工作了。 ```python= #1. 修改隱藏層加入 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("") show_train_history(train_history,'accuracy','val_accuracy') ``` >輸出: >>>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/43211.png) <br> >>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/4321.png) :::info #### 檢視進行訓練後結果 最後一個 Epoch 的執行結果可以發現 acc 與 val_acc 接近許多, 說明 Overfitting 問題有被解決. ::: <br> <br> ## 十一、建立多層感知器模型 (包含兩個 Hidden Layers) * 為了進一步提升準確率, 可提升多元感知器 Hidden layer 的層數. ```python= #1. 變更模型使用兩個 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("") show_train_history(train_history,'accuracy','val_accuracy') ``` >輸出: >>>> >![](https://raw.githubusercontent.com/ZengYuXuan0716/ai/main/44444.png) <br> >>> >![](https://github.com/ZengYuXuan0716/ai/blob/main/333.png?raw=true) :::info #### 進行訓練並察看結果 由 accuracy 圖可以看出 training accuracy 與 validation accuracy 已經相當接近, 說明 Overfitting 的影響又被改善了 ::: ### CNN模型準確率為97.8% ## 結論 多層感知器 Multilayer perceptron 模型,辨識手寫字嘗試將模型加寬加深,加入drop以提高準確度,避免 overfitting ,但多層感知器有其極限,若要提高準確度,就要使用卷積神經網路 CNN . <br> <br>