Keras 手寫阿拉伯數字辨識 CNN
卷積神經網路 CNN 是由 Yann LeCun 提出,以下是以 CNN 進行 mnist 資料辨識。
MLP 與 CNN 的差異是 CNN 增加了卷積層1、池化層1、卷積層2、池化層2 的處理,用以提取特徵。
CNN 可分為兩個部分
影像的特徵擷取
透過卷積層1、池化層1、卷積層2、池化層2 的處理,用以提取特徵
完全連結神經網路
包含平坦層、隱藏層、輸出層組成的類神經網路
卷積運算的效果很類似濾鏡,擷取了不同的特徵。
卷積運算
- 以隨機方式產生, filter weight 大小為 3x3
- 將要轉換的影像,由左至右、上至下,依序選取 3x3 的矩陣
- 影像選取的矩陣 3x3,及 filter weight 3x3 乘積的結果,算出第一列第一行的數字
依照同樣的方式,完成所有運算
使用單一 filter weight 卷積運算產生影像
以下是數字 7 (28x28) 的影像,使用隨機產生的 5x5 filter weight (w) 濾鏡,進行卷積運算後的結果。卷積運算不會改變圖片的大小,但運算後的結果,可提取輸入的不同特徵,ex: 邊緣、線條、角
使用多個 filter weight 卷積運算產生多個影像
接下來隨機產生 16 個 filter weight,也就是 16 個濾鏡
透過卷積運算,使用 16 個 filter weight,產生 16個影像,每一種影像提取了不同的特徵
Max-Pool 運算
可將影像縮減取樣 (downsampling),例如原本是 4x4 的影像,Max-Pool 後得到的影像為 2x2
使用 Max-Pool 轉換手寫數字影像
將 16 個 28x28 影像,縮小為 16 個 14x14 的影像,影像數量不變
Max-Pool downsampling 會縮小圖片,其優點是
- 減少需要處理的資料點:減少運算時間
- 讓影像位置差異變小:例如 7,在圖片中的位置不固定,可能偏某一側,但位置不同會影響辨識,減少影像大小,讓數字的位置差異變小
- 參數的數量與計算量下降:降低發生 overfitting 的狀況
MNIST CNN
步驟
資料預處理 Preprocess:處理後產生 features (影像特徵值)與 label (數字的真實值)
建立模型:建立 CNN model
訓練模型:輸入 features, label,執行 10 次訓練週期
評估模型準確率:使用測試資料評估模型準確率
預測:利用 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
沒有留言:
張貼留言