1. 程式人生 > >keras多層感知器識別手寫數字

keras多層感知器識別手寫數字

2.Keras建立多層感知器模型(接上一篇)

2.1簡單介紹多層感知器模型

注:以下模型及其說明來自於《TensorFlow+Keras深度學習人工智慧實踐應用》林大貴 著

以矩陣方式模擬多層感知器模型的工作方式(如下圖所示)

多層感知器模型的工作方式 建立輸入與隱藏層的公式:

h1=ReLu(x*w1+b1)

變數名 說明
輸入層 x模擬輸入神經元接收外界傳送訊息,如上圖所示,共有784個神經元。
隱藏層h1 隱藏層h1模擬內部神經元,如上圖所示,共有256個隱藏神經元。
權重 權重模擬神經元的軸突,連線輸入與接收神經元,負責傳送資訊。連線輸入層(784個神經元)與隱藏層(256個神經元),為了讓兩層的每一個神經元都相互連線,總共需要784*256=200704個突觸。所以w1(權重)必須是784*256的矩陣,用來模擬這些突觸的功能。
偏差值b1 偏差值b1模擬突觸的結構,代表接收神經元容易被活化的程度,偏差值越高,越容易被活化並傳遞資訊。如上圖所示,因為隱藏層共有256個神經元,所以偏差值是長度為256的向量。
啟用函式 啟用函式模擬神經傳導的工作方式,在此我們使用ReLu啟用函式接收刺激的總和:(x*w1+b1),經過啟用函式ReLu的運算,大於臨界值時,會傳遞至下一個神經元。

建立隱藏層與輸出層公式:

y=softmax(h1*w2+b2)

變數名 說明
隱藏層h1 隱藏層h1模擬內部神經元,共有256個隱藏神經元。
輸出層y 模擬輸出神經元,就是預測的結果,共有10個輸出神經元。對應我們希望預測的數字,從0到9共有10個結果。
權重w2 權重模擬神經元的軸突,連線輸入與接收神經元,負責傳送資訊。連線隱藏層(256個神經元)與輸出層(10個神經元),為了讓兩層的每一個神經元互相連線,總共需要25610=2560個軸突。所以(w2)權重必須是25610的矩陣,用來模擬這些軸突的功能
偏差值b2 偏差值b2模擬突觸的結構,代表接收神經元容易被活化的程度,偏差值越高,越容易被活化並傳遞資訊。如上圖所示,因為接收神經元是輸出層(10個神經元),所以偏差值是長度為10的向量。
啟用函式 在輸出層中,我們使用softmax啟用函式,接收刺激的總和(w2*h1+b2)經過softmax運算後的輸出是一個概率分佈,共有10個輸出,數值越高代表概率越高,例如輸出結果由0算起第5個數字數值最高,代表預測結果是5。

2.2建立多層感知器模型的步驟

建立多層感知器模型識別MNIST資料集中的手寫數字步驟如下圖所示 在這裡插入圖片描述

2.3對資料進行預處理

輸入上一篇文章講到的關鍵程式碼

import numpy as np                             #匯入相關包
import pandas as pd
from keras.utils import np_utils
from keras.datasets import mnist
np.random.seed(10)

(X_train_image,y_train_label),(X_test_image,y_test_label)=mnist.load_data() #讀取MNIST資料

import matplotlib.pyplot as plt
def plot_image(image):                     #定義顯示某個數字影象的函式
    fig=plt.gcf()
    fig.set_size_inches(2,2)
    plt.imshow(image,cmap='binary')
    plt.show()

def plot_images_labels_prediction(images,labels,prediction,idx,num=10):   #定義顯示features,labels和prediction的函式
    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="label="+str(labels[idx])
        if len(prediction)>0:
            title+=",prediction="+str(prediction[idx])
        ax.set_title(title,fontsize=10)
        ax.set_xticks([])
        ax.set_yticks([])
        idx+=1
    plt.show()

X_Train=X_train_image.reshape(60000,784).astype('float32')   #將特徵值轉換為一維向量
X_Test=X_test_image.reshape(10000,784).astype('float32')

X_Train_normalize=X_Train/255  #將一維向量的數字標準化
X_Test_normalize=X_Test/255

y_TrainOneHot=np_utils.to_categorical(y_train_label)  #將labels轉換為One-Hot Encoding
y_TestOneHot=np_utils.to_categorical(y_test_label)

2.4建立模型並開始訓練

匯入相關模組

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout

建立Sequential模型,並且加入‘’輸入層“,”Dropout功能“和”輸出層“

model=Sequential()                    #建立模型
model.add(Dense(units=256,            #定義”隱藏層“神經元個數為256
                input_dim=784,        #定義”輸入層“神經元個數為784,因為28*28=784
                kernel_initializer='normal',  #使用normal distribution正態分佈來初始化權重(weight)和偏差
                activation='relu'))  #定義啟用函式為relu
model.add(Dropout(0.5))              #加入Doupout功能,防止過擬合
model.add(Dense(units=10,            #定義”輸出層“神經元個數為10,代表0~9個數
               kernel_initializer='normal',
               activation='softmax')) #啟用函式為softmax

定義訓練方式

model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

各引數的解釋如下表

引數名 說明
loss 設定損失函式,在深度學習中使用cross_entropy(交叉熵)訓練的效果比較好
optimizer 設定訓練時,在深度學習中使用adam優化器可以讓訓練速度更快,還可以提高準確率
metrics 設定評估模型的方式是準確率

接下來開始訓練

train_history=model.fit(x=X_Train_normalize,y=y_TrainOneHot,validation_split=0.2,epochs=10,batch_size=200,verbose=2)
引數名 說明
x=X_Train_normalize x代表要輸入的特徵值,所以將輸入特徵X_Train_normalize賦值給x
y=y_TrainOneHot y代表要輸入的標籤,所以將標籤值y_TrainOneHot賦給y
validation_split=0.2 表示要把訓練資料集中的80%用於訓練模型,20%用於驗證模型
epochs=10 表示要訓練10個週期
batch_size=200 表示每個週期中的每一批次的資料量大小是200
verbose=2 顯示訓練過程
train_history 訓練結果會儲存在train_history中

執行程式碼,執行結果如下 在這裡插入圖片描述 可以看到,訓練樣本原來是60000的,把其中的48000作為訓練集,剩下的12000作為驗證集。另外,我們還注意到,loss(訓練集的損失函式)和val_loss(驗證集的損失函式)在逐步減小,acc(訓練集的準確率)和val_acc(驗證集的準確率)在提升

接下來我們定義一個函式顯示訓練過程

def show_train_history(train_histroy,train,validation): #輸入引數為train_history和train_histroy.history中的索引train,validation
    plt.plot(train_histroy.history[train])            #train_histroy.history是一個字典
    plt.plot(train_history.history[validation])
    plt.title('Train Histroy')
    plt.ylabel(train)
    plt.xlabel('epoch')
    plt.legend(['train','validation'],loc='upper left')
    plt.show()

畫出準確率曲線

show_train_history(train_history,'acc','val_acc')

執行結果 在這裡插入圖片描述 可以看到,訓練和驗證的準確率都隨著epoch增加而增加,即準確率越來越高。

順便再看下損失函式的曲線圖

show_train_history(train_history,'loss','val_loss')

在這裡插入圖片描述 損失函式在逐步減小。

接下來,模型訓練完後,在開始預測前,我們先評估一下訓練模型的準確率是多少

scores=model.evaluate(X_Test_normalize,y_TestOneHot)
print(scores)

執行結果 在這裡插入圖片描述 說明:X_Test_normalize為測試集,y_TestOneHot為標籤,scores的第一項為損失函式,第二項為準確率,可以看到用測試集評估該模型的準確率為0.9768。

最後,開始預測

prediction=model.predict_classes(X_Test)

我們看下前25項的預測結果

plot_images_labels_prediction(X_test_image,y_test_label,prediction,idx=1,num=25)

執行結果 在這裡插入圖片描述 容易得知,10000個測試資料中肯定有預測錯的,我們可以定義一個函式來檢視預測錯誤的數量和圖形

def show_wrong(images,labels,prediction):
    fig=plt.gcf()
    fig.set_size_inches(20,60)
    array=[]
    for i in range(0,10000):
        if(str(labels[i])!=str(prediction[i])):            #把測試集的標籤和預測結果進行比較,不相等的就是預測錯誤的,用一個數組儲存不相等的索引
            array.append(i)
    for i in range(0,len(array)):
        ax=plt.subplot(30,8,1+i)
        ax.imshow(images[array[i]],cmap='binary')
        title="label="+str(labels[array[i]])+",prediction="+str(prediction[array[i]])
        ax.set_title(title,fontsize=12)
        ax.set_xticks([])
        ax.set_yticks([])
    plt.show()
    print("預測錯誤的一共有"+str(len(array))+"個")

執行

show_wrong(x_Test,y_Test,prediction)

在這裡插入圖片描述 由於一共有240個圖形是被預測錯誤的,篇幅較長,所以只截了最後的一小部分圖

另外,我們還可以建立一個混淆矩陣來統計檢視什麼數字的預測準確率最高,哪些數字最容易被預測錯誤。 這裡用pandas建立一個混淆矩陣

import pandas as pd
pd.crosstab(y_test_label,prediction,rownames=['lable'],colnames=['prediction'])

執行結果 在這裡插入圖片描述

根據經驗,可以看出多層感知器模型識別的準確率不是很高,只有0.9768,要想提高準確率,還可以增加隱藏層神經元的個數,但是這樣做會增加訓練的時間,並且效率也很低,本人試過把隱藏層的神經元個數增加到1000個,準確率是0.9779,或者再增加一個隱藏層,得到的準確率是0.9797。如果想進一步提高準確率,就應該使用卷積神經網路了。