使用 Keras + CNN 識別 MNIST 手寫數字


導入模塊:

from keras.datasets import mnist
from keras.utils import np_utils
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D

下載手寫數據集:

(x_Train,y_Train),(x_Test,y_Test)=mnist.load_data()
print(x_Train.shape)
print(y_Train.shape)
print(x_Test.shape)
print(y_Test.shape)


 

 

訓練數據60000個,長度和寬度都是28,標簽也是6000個。

測試數據10000個。

圖形化數據集,查看前10個數據集:

def polt_images_label_prediction(images,labels,idx,num=10):
    fig=plt.gcf()
    fig.set_size_inches(12,14)
    for i in range(num):
        ax=plt.subplot(5,2,1+i)
        ax.imshow(images[idx],cmap='binary')
        title='label='+str(labels[idx])
        ax.set_title(title,fontsize=10)
        ax.set_xticks([])
        ax.set_yticks([])
    plt.show()
polt_images_label_prediction(x_Train,y_Train,0)

數據預處理:

將features以reshape轉化為6000*28*28*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')
x_Train4D_normalize = x_Train4D / 255
x_Test4D_normalize = x_Test4D / 255

數據預處理,對標簽進行one-hot編碼處理:

#未轉化第一個數是 5
print(y_Train[:1])
y_TrainOneHot = np_utils.to_categorical(y_Train)
#轉化  One-Hot Encoding 都是以0 1 表示,5 在第六個位置
print(y_TrainOneHot[:1])

建立模型:

#建立一個 Sequential 線性堆疊模型
model=Sequential()
#建立第一個卷積層,input_shape 輸入數字圖像大小為 28*28*1, filters 卷積核個數 16 個,kernel_size 卷積核大小 3*3
#padding 是否零填充 same 表示填充, activation 激活函數 relu
model.add(Conv2D(filters = 16,
          kernel_size = (3, 3),
          padding = 'same',
          input_shape = (28, 28, 1),
          activation = 'relu'))
#建立第一個池化層 pool_size 池化窗口 2
model.add(MaxPooling2D(pool_size = (2, 2)))
#建立第二個卷積層, filters 卷積核個數 36 個,kernel_size 卷積核大小 3*3
#padding 是否零填充 same 表示填充, activation 激活函數 relu
model.add(Conv2D(filters = 36,
                kernel_size = (3, 3),
                padding = 'same',
                activation = 'relu'))
#建立第二個池化層 pool_size 池化窗口 2
model.add(MaxPooling2D(pool_size = (2, 2)))
#加入Dropout避免過度擬合
model.add(Dropout(0.25))
#建立平坦層,將多維向量轉化為一維向量
model.add(Flatten())
#建立隱藏層,隱藏層有 128 個神經元, activation 激活函數用 relu
model.add(Dense(128, activation = 'relu'))
#加入Dropout避免過度擬合
model.add(Dropout(0.25))
#建立輸出層,一共有 10 個神經元,因為 0 到 9 一共有 10 個類別, activation 激活函數用 softmax 這個函數用來分類
model.add(Dense(10, activation = 'softmax'))

查看模型摘要:

print(model.summary())

訓練模型:

#定義訓練模型, loss 損失函數用 categorical_crossentropy, optimizer 優化器用 adam, metrics 度量用 accuracy
model.compile(loss = 'categorical_crossentropy', optimizer = 'adam', metrics = ['accuracy'])
#開始訓練模型, x 是訓練數據集, y 是訓練數據集的標簽, validation_split 是把訓練數據集分為 8 份訓練數據集 2 份驗證集
#epochs 是迭代次數 20, batch_size 是批量 256, verbose 為 2 顯示訓練過程
train_history = model.fit(x = x_Train4D_normalize,
                          y = y_TrainOneHot,
                         validation_split = 0.2,
                         epochs = 20,
                         batch_size = 256,
                         verbose = 2)

查看訓練模型loss和accuracy:

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, 'loss', 'val_loss')
show_train_history(train_history, 'accuracy', 'val_accuracy')

評估模型,准確率為99.21%。

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

預測模型:

#預測測試集第一個數字
prediction = np.argmax(model.predict(x_Test4D_normalize[:1]))
print('預測值:', prediction)
print('真實值:', np.argmax(y_TestOneHot[:1]))

 


 

【分析】

由訓練過程數據以及精度圖卡伊看出,當迭代到10次的時候就達到了最高精度,后面的另外10次迭代並沒有讓精度變得更好。所以把epochs改成10再仿真一次:

模型評估,准確率為99.11%,比迭代20次時准確率降低了0.1%。


 

看網上說,訓練模型的時候先用小數據大參數,先讓它過擬合,再慢慢調小,filter能用256就不用128,所以我先調一下filter參數看看是否會過擬合。

學到的一條經驗就是在看訓練參數的時候不要看准確率這個指標,而是要看損失函數這個指標,畢竟優化的就是loss。

 

 

兩層的卷積核個數分別改成了32和64.

 

 但是並沒有過擬合。

 

 准確率99.16%也並沒有什么大的變化。

所以增大卷和核數量,改為64和128。

 

 

 

 

 

 

 

 准確率99.14%,這是已經過擬合了吧,畢竟訓練的accuracy和測試的val_accuracy分別是99.72%和99.15%差的還挺多的。

 

再調大參數試試:

 

 

 

 

准確率99.32%。

感覺糾結與此已然沒有太多意義,本身就是想練習一下調參。所以打算把參數改成原來的,並且增加一層卷積層試試:


#建立第三個卷積層, filters 卷積核個數 64 個,kernel_size 卷積核大小 3*3
#padding 是否零填充 same 表示填充, activation 激活函數 relu
model.add(Conv2D(filters = 64,
                kernel_size = (3, 3),
                padding = 'same',
                activation = 'relu'))
#建立第三個池化層 pool_size 池化窗口 2
model.add(MaxPooling2D(pool_size = (2, 2)))

多加一層,10次迭代,結果准確率99.17%:

 

 

 

 


 

 

就算模型只有一層卷積層,准確率也有98.6%。

——2019.11.12

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM