My blogs

The greatest test of courage on earth is to bear defeat without losing heart.

0%

CNN_mnist

介紹

MNIST DATABASE CNN 架構手寫數字辨識。


Mnist

MNIST 是一個手寫數字的圖像資料集,我們將用於圖像辨識的範例之中。

MNIST 資料集,可至 THE MNIST DATABASE 下載。

MNIST 資料集常用於機器學習訓練和測試的教學。
資料集包含 60000 個訓練圖片和 10000 個測試圖片,每個圖片大小是 28 * 28 像素


Import

  • os 模組 用指定 GPU運行,如未裝 Nvidia cuda toolkit,可移除前三行 。

  • mnist datasets 已經包在tensorflow底下可直接呼叫使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import os
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
from tensorflow.keras.datasets import mnist
from keras.utils import np_utils
# to_categorical就是将类别向量转换为二进制(只有0和1)的矩阵类型表示。其表现为将原有的类别向量转换为独热编码的形式
import tensorflow as tf
import numpy as np
# np處理陣列
import matplotlib.pyplot as plt
# 建立圖表工具

## step 2 建立模型:建立 CNN model
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D
# 計算時間模組
import time



Preprocessing

x_Train 為訓練資料,x_Train 為訓練資料label ,可查看資料數
print(x_Train.shape)=> (60000, 28, 28) (資料筆數,像素,像素)。
print(y_Train) => [5 0 4 … 5 6 8] > 三維轉為四維矩陣以符合CNN的需求
MNIST 圖片為灰階因此其channe 為 1
x_Train4D / 255 =>標準化 數字 0 or 1 ,圖像的像素點介於 0~255 之間
Onehot Encoding =>
[5 0 4 ... 5 6 8] => [0000010000 ,0000000000,0000100000,.......]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 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(60000,28,28,1).astype('float32')
x_Test4D = x_Test.reshape(10000,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)


Construct Model

附圖為卷積進行圖
卷積進行圖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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())

下方為模型圖
model


Configure Training method && Train

loss='categorical_crossentropy'目標值應該是分類格式。
optimizer='adam'偏離校正,使得每一次的學習率都會有個確定的範圍。

1
2
3
4
5
6
7
8
9
10
model.compile(loss='categorical_crossentropy',
optimizer='adam',metrics=['accuracy'])
# epochs=50 次

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

Evaluate && Save Model

1
2
3
4
5
6
7
8
9
10
11
# step 4 評估模型準確率:使用測試資料評估模型準確率

scores = model.evaluate(x_Test4D_normalize , y_TestOneHot, verbose=0)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])
# step 5 預測:利用 model,輸入測試資料進行預測
# 查看預測結果
model.save('CNN_model.h5')

prediction = model.predict(x_Test4D_normalize)
prediction = np.round(prediction).astype(int)

Figure presentation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import matplotlib.pyplot as plt
def plot_images_labels_prediction(images,labels,prediction,filename, idx, num=30):
fig = plt.gcf()
fig.set_size_inches(22, 22)
#if num>25: num=25
for i in range(0,num):
ax=plt.subplot(6,6, 1+i)
ax.imshow(images[i], cmap='binary')

ax.set_title("label=" +str(labels[i])+"\n"+
"predict="+str(prediction[i])+"\n"+str(y_TestOneHot[i])
,fontsize=10)

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

plot_images_labels_prediction(x_Test,y_Test,prediction, 'CNN_predict.png', idx=0)
d =time.time()
print('time elapsed: ' + str(round(d-s, 2)) + ' seconds')

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')

設備為 NVIDIA GeForce GTX 1650。

predict

下方預測圖
predict
下方為 acc
predict
下方為 loss
predict


放入照片辨識

使用 opencv 進行照片處理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from keras.models import load_model
import numpy as np
import cv2

model = load_model('you path')
print('輸入數字:')

a=str(input())
# ex test img path
im1 ='D:\\mnist\\'+a+'.png'
#讀檔
img = cv2.imread(im1)
#彩色轉灰階
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 照片大小符合模型訓練照片大小
crop_size = (28, 28)
# cv2.INTER_CUBIC 4x4像素邻域的双三次插值
img = cv2.resize(img, crop_size, interpolation = cv2.INTER_CUBIC)

# 顯示圖片
cv2.imshow('My Image', img)
# 按下任意鍵則關閉所有視窗
cv2.waitKey(0)
cv2.destroyAllWindows()

# reshape 大小須轉為四維符合CNN 模型
img_2D = img.reshape((1,28,28,1)).astype('float32')
# 正規化 1或0
img_norm=img_2D/255
img = img_norm

predict =model.predict(img)
predict=np.argmax(predict,axis=1)

print ('識別爲:')
print(predict)

下方為小畫家寫字辨識,實測結果百分之四十正確,白底黑字。
predict

最好使用的是黑底白字,識別效果會好一點。因爲 MNIST 數據集中的字體也是黑底白字。雖然也能識別,但是最好還是用黑底白字
下方為小畫家寫字辨識,實測結果百分之六十正確,黑底白字。

predict

本文中有錯解說,歡迎指教!

可至此連結 點選

圖片建議放黑底白字進行辨識


參考網站


如果您喜歡我的文章,請幫我按五下 ,感謝大家。