2020/10/19

Keras 手寫阿拉伯數字辨識 CNN

卷積神經網路 CNN 是由 Yann LeCun 提出,以下是以 CNN 進行 mnist 資料辨識。

MLP 與 CNN 的差異是 CNN 增加了卷積層1、池化層1、卷積層2、池化層2 的處理,用以提取特徵。

CNN 可分為兩個部分

  1. 影像的特徵擷取

    透過卷積層1、池化層1、卷積層2、池化層2 的處理,用以提取特徵

  2. 完全連結神經網路

    包含平坦層、隱藏層、輸出層組成的類神經網路

卷積運算的效果很類似濾鏡,擷取了不同的特徵。


卷積運算

  1. 以隨機方式產生, filter weight 大小為 3x3
  2. 將要轉換的影像,由左至右、上至下,依序選取 3x3 的矩陣
  3. 影像選取的矩陣 3x3,及 filter weight 3x3 乘積的結果,算出第一列第一行的數字
  1. 依照同樣的方式,完成所有運算

  2. 使用單一 filter weight 卷積運算產生影像

    以下是數字 7 (28x28) 的影像,使用隨機產生的 5x5 filter weight (w) 濾鏡,進行卷積運算後的結果。卷積運算不會改變圖片的大小,但運算後的結果,可提取輸入的不同特徵,ex: 邊緣、線條、角

  3. 使用多個 filter weight 卷積運算產生多個影像

    接下來隨機產生 16 個 filter weight,也就是 16 個濾鏡

    透過卷積運算,使用 16 個 filter weight,產生 16個影像,每一種影像提取了不同的特徵

  4. Max-Pool 運算

    可將影像縮減取樣 (downsampling),例如原本是 4x4 的影像,Max-Pool 後得到的影像為 2x2

  5. 使用 Max-Pool 轉換手寫數字影像

    將 16 個 28x28 影像,縮小為 16 個 14x14 的影像,影像數量不變

    Max-Pool downsampling 會縮小圖片,其優點是

    • 減少需要處理的資料點:減少運算時間
    • 讓影像位置差異變小:例如 7,在圖片中的位置不固定,可能偏某一側,但位置不同會影響辨識,減少影像大小,讓數字的位置差異變小
    • 參數的數量與計算量下降:降低發生 overfitting 的狀況

MNIST CNN

步驟

  1. 資料預處理 Preprocess:處理後產生 features (影像特徵值)與 label (數字的真實值)

  2. 建立模型:建立 CNN model

  3. 訓練模型:輸入 features, label,執行 10 次訓練週期

  4. 評估模型準確率:使用測試資料評估模型準確率

  5. 預測:利用 model,輸入測試資料進行預測

from keras.datasets import mnist
from keras.utils import np_utils
import numpy as np
np.random.seed(10)

## step 1 資料預處理 Preprocess:處理後產生 features (影像特徵值)與 label (數字的真實值)
# 讀取 mnist 資料
(x_Train, y_Train), (x_Test, y_Test) = mnist.load_data()
# 將 features (影像特徵值),轉換為 4 維矩陣
# 將 features,以 reshape 轉為 6000 x 28 x 28 x 1 的 4 維矩陣
x_Train4D = x_Train.reshape(x_Train.shape[0],28,28,1).astype('float32')
x_Test4D = x_Test.reshape(x_Test.shape[0],28,28,1).astype('float32')

# 將 features 標準化
x_Train4D_normalize = x_Train4D / 255
x_Test4D_normalize = x_Test4D / 255

# 以 Onehot Encoding 轉換 label
y_TrainOneHot = np_utils.to_categorical(y_Train)
y_TestOneHot = np_utils.to_categorical(y_Test)

#########
## step 2 建立模型:建立 CNN model
from keras.models import Sequential
from keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D

# 線性堆疊模型
model = Sequential()

# 建立卷積層1
# 輸入數字影像 28x28 的大小,執行一次卷積運算,產生 16 個影像,卷積運算不會改變影像大小,結果還是 28x28
# filters=16             建立 16 個 filter weight
# kernel_size=(5,5)      每一個濾鏡大小為 5x5
# padding='same'         讓卷積運算產生的影像大小不變
# input_shape=(28,28,1)  第1, 2 維,是輸入的影像形狀 28x28,第 3 維,因為是單色灰階影像,所以是 1
# activation='relu'      設定 ReLU 激活函數
model.add(Conv2D(filters=16,
                 kernel_size=(5,5),
                 padding='same',
                 input_shape=(28,28,1),
                 activation='relu'))

# 建立池化層
# 輸入參數 pool_size=(2, 2),執行第一次縮減取樣,將 16 個 28x28 影像,縮小為 16 個 14x14 的影像
model.add(MaxPooling2D(pool_size=(2, 2)))

# 建立卷積層2
# 執行第二次卷積運算,將原本的 16 個影像,轉換為 36 個影像,卷積運算不會改變影像大小,結果還是 14x14
model.add(Conv2D(filters=36,
                 kernel_size=(5,5),
                 padding='same',
                 activation='relu'))

# 建立池化層2,並加入Dropout 避免 overfitting
# 執行第二次縮減取樣,將 36 個 14x14 的影像,縮小為 36 個 7x7 的影像
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# 建立神經網路 (平坦層, 隱藏層, 輸出層)
# 建立平坦層
# 根據池化層2 的結果,共36 個 7x7 影像,轉換為 1維向量,長度是 36x7x7=1764,也就是 1764 個 float,正好對應到 1764 個神經元
model.add(Flatten())
# 建立隱藏層,共有 128 個神經元
model.add(Dense(128, activation='relu'))
# 加入 Dropout(0.5)
# 每次訓練迭代時,會隨機在神經網路中,放棄 50% 的神經元,以避免 overfitting
model.add(Dropout(0.5))
# 建立輸出層
# 共 10 個神經元,對應 0~9 共 10 個數字,並使用 softmax 激活函數進行轉換
# softmax 可將神經元的輸出,轉換為預測每一個數字的機率
model.add(Dense(10,activation='softmax'))

print(model.summary())

#######
## 訓練模型:輸入 features, label,執行 10 次訓練週期
model.compile(loss='categorical_crossentropy',
              optimizer='adam',metrics=['accuracy'])

# validation_split=0.2   80% 為訓練資料, 20% 驗證資料
# batch_size=300         每一批次300 筆資料
# verbose=2              顯示訓練過程
train_history=model.fit(x=x_Train4D_normalize,
                        y=y_TrainOneHot,validation_split=0.2,
                        epochs=20, batch_size=300,verbose=2)


# 模型訓練結果 結構存檔
from keras.models import model_from_json
json_string = model.to_json()
with open("model.config", "w") as text_file:
    text_file.write(json_string)

# 模型訓練結果 權重存檔
model.save_weights("model.weight")


import matplotlib.pyplot as plt
def save_train_history(train_acc,test_acc, filename):
    plt.clf()
    plt.plot(train_history.history[train_acc])
    plt.plot(train_history.history[test_acc])
    plt.title('Train History')
    plt.ylabel('Accuracy')
    plt.xlabel('Epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.savefig(filename)


save_train_history('accuracy','val_accuracy', 'acc.png')

save_train_history('loss','val_loss', 'loss.png')


#####
# step 4 評估模型準確率:使用測試資料評估模型準確率

scores = model.evaluate(x_Test4D_normalize , y_TestOneHot)
scores[1]

#####
# step 5 預測:利用 model,輸入測試資料進行預測
prediction=model.predict_classes(x_Test4D_normalize)
prediction[:10]

# 查看預測結果
import matplotlib.pyplot as plt
def plot_images_labels_prediction(images,labels,prediction,filename, 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')

        ax.set_title("label=" +str(labels[idx])+
                     ",predict="+str(prediction[idx])
                     ,fontsize=10)

        ax.set_xticks([]);ax.set_yticks([])
        idx+=1
    plt.savefig(filename)

plot_images_labels_prediction(x_Test,y_Test,prediction, 'predict.png', idx=0)

####
# confusion maxtrix

import pandas as pd
crosstab1 = pd.crosstab(y_Test,prediction,
            rownames=['label'],colnames=['predict'])

print()
print(crosstab1)

df = pd.DataFrame({'label':y_Test, 'predict':prediction})

df[(df.label==5)&(df.predict==3)]

列印 model

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 16)        416
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 16)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 14, 14, 36)        14436
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 36)          0
_________________________________________________________________
dropout_1 (Dropout)          (None, 7, 7, 36)          0
_________________________________________________________________
flatten_1 (Flatten)          (None, 1764)              0
_________________________________________________________________
dense_1 (Dense)              (None, 128)               225920
_________________________________________________________________
dropout_2 (Dropout)          (None, 128)               0
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290
=================================================================
Total params: 242,062
Trainable params: 242,062
Non-trainable params: 0

訓練過程,可發現 loss 越來越小,accuracy 越來越高

Train on 48000 samples, validate on 12000 samples
Epoch 1/20
 - 58s - loss: 0.4736 - accuracy: 0.8517 - val_loss: 0.1006 - val_accuracy: 0.9694
Epoch 2/20
 - 63s - loss: 0.1326 - accuracy: 0.9604 - val_loss: 0.0652 - val_accuracy: 0.9813
Epoch 3/20
 - 65s - loss: 0.0980 - accuracy: 0.9700 - val_loss: 0.0555 - val_accuracy: 0.9838
Epoch 4/20
 - 67s - loss: 0.0791 - accuracy: 0.9761 - val_loss: 0.0479 - val_accuracy: 0.9862
Epoch 5/20
 - 61s - loss: 0.0698 - accuracy: 0.9779 - val_loss: 0.0442 - val_accuracy: 0.9873
Epoch 6/20
 - 59s - loss: 0.0616 - accuracy: 0.9813 - val_loss: 0.0434 - val_accuracy: 0.9875
Epoch 7/20
 - 62s - loss: 0.0531 - accuracy: 0.9835 - val_loss: 0.0370 - val_accuracy: 0.9893
Epoch 8/20
 - 63s - loss: 0.0496 - accuracy: 0.9843 - val_loss: 0.0363 - val_accuracy: 0.9904
Epoch 9/20
 - 61s - loss: 0.0455 - accuracy: 0.9863 - val_loss: 0.0347 - val_accuracy: 0.9908
Epoch 10/20
 - 65s - loss: 0.0417 - accuracy: 0.9870 - val_loss: 0.0319 - val_accuracy: 0.9920
Epoch 11/20
 - 69s - loss: 0.0375 - accuracy: 0.9880 - val_loss: 0.0309 - val_accuracy: 0.9912
Epoch 12/20
 - 62s - loss: 0.0357 - accuracy: 0.9891 - val_loss: 0.0341 - val_accuracy: 0.9907
Epoch 13/20
 - 73s - loss: 0.0347 - accuracy: 0.9894 - val_loss: 0.0332 - val_accuracy: 0.9909
Epoch 14/20
 - 64s - loss: 0.0314 - accuracy: 0.9902 - val_loss: 0.0312 - val_accuracy: 0.9921
Epoch 15/20
 - 65s - loss: 0.0298 - accuracy: 0.9907 - val_loss: 0.0296 - val_accuracy: 0.9923
Epoch 16/20
 - 66s - loss: 0.0260 - accuracy: 0.9914 - val_loss: 0.0312 - val_accuracy: 0.9920
Epoch 17/20
 - 69s - loss: 0.0255 - accuracy: 0.9923 - val_loss: 0.0270 - val_accuracy: 0.9933
Epoch 18/20
 - 67s - loss: 0.0243 - accuracy: 0.9924 - val_loss: 0.0305 - val_accuracy: 0.9921
Epoch 19/20
 - 62s - loss: 0.0241 - accuracy: 0.9922 - val_loss: 0.0299 - val_accuracy: 0.9925
Epoch 20/20
 - 71s - loss: 0.0214 - accuracy: 0.9933 - val_loss: 0.0311 - val_accuracy: 0.9918

訓練與驗證的準確率都越來越高,誤差越來越低,且沒有 overfitting 的現象

預測的 scores,準確率 0.9926

[0.021972040887850517, 0.9926000237464905]

這是前 10 筆預測資料

crosstab 結果

predict    0     1     2    3    4    5    6     7    8    9
label
0        976     1     0    0    0    0    1     1    1    0
1          0  1133     1    0    0    0    0     1    0    0
2          2     0  1024    0    0    0    0     4    2    0
3          0     0     1  999    0    3    0     2    3    2
4          0     0     0    0  978    0    1     0    0    3
5          1     0     0    4    0  883    3     0    0    1
6          3     2     0    0    2    1  949     0    1    0
7          0     2     3    0    0    0    0  1022    1    0
8          3     1     1    1    0    0    0     0  967    1
9          1     0     0    0    6    2    0     4    1  995

References

TensorFlow+Keras深度學習人工智慧實務應用

何時使用MLP,CNN和RNN神經網絡

沒有留言:

張貼留言