前饋神經網路進行MNIST資料集分類

燈火繾綣發表於2020-12-28

前饋神經網路

前饋神經網路是一種最簡單的神經網路,各神經元分層排列,每個神經元只與前一層的神經元相連。接收前一層的輸出,並輸出給下一層,各層之間沒有反饋。是目前應用最廣泛、發展最迅速的人工神經網路之一。——百度百科

借用百度百科中的前饋神經網路示意圖

在這裡插入圖片描述

這是一個多層前饋神經網路模型,除開輸入層和輸出層外,有兩層隱藏層。

這裡是一個全連線的情況,即除開輸入層外,每一層的每個神經元都與前一層所有的神經元相連。

根據這樣的一個模型,前饋神經網路可以通過逐層的資訊傳遞,得到整個網路的最終輸出 a ( L ) a^{(L)} a(L),這樣我們可以把整個網路看成是一個複合函式 ϕ ( x ; W , b ) \phi(x;W,b) ϕ(x;W,b),將向量 x = [ x 1 , x 2 , . . . , x n ] T x=[x_1,x_2,...,x_n]^T x=[x1,x2,...,xn]T作為第一層的輸入 a ( 0 ) a^{(0)} a(0),將第 L L L層的輸出 a ( L ) a^{(L)} a(L)作為整個函式的輸出。
x = a ( 0 ) → z ( 1 ) → a ( 1 ) → a ( 2 ) → ⋅ ⋅ ⋅ → a ( L − 1 ) → z ( L ) → a ( L ) = ϕ ( x ; W , b ) x=a^{(0)}→z^{(1)}→a^{(1)}→a^{(2)}→···→a^{(L-1)}→z^{(L)}→a^{(L)}=\phi(x;W,b) x=a(0)z(1)a(1)a(2)a(L1)z(L)a(L)=ϕ(x;W,b)
從輸出層開始為第0層, a ( i ) a^{(i)} a(i)是第 i i i層輸出給第 i + 1 i+1 i+1層的資料, z ( i ) z^{(i)} z(i)是第 i i i層將 a ( i − 1 ) a^{(i-1)} a(i1)進行計算後得到的資料。

這個 a ( i ) a^{(i)} a(i)的含義也稱作第 i i i層神經元的活性值, z ( i ) z^{(i)} z(i)稱作第 i i i層的淨活性值。

具體神經元進行計算的過程如下:
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ z^{(i)}&=W^{(i…
將兩層的公式合併,就可以寫成:
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ z^{(i)}&=W^{(i…

引數學習

前饋神經網路的引數個數與網路模型的規模有關。

本文只考慮“每個神經元都與前一層的所有神經元相連”的情況,前饋神經網路顧名思義,資料從前往後順序輸入輸出,引數學習通過梯度下降演算法不斷迭代每一個引數。

由於MNIST分類問題依然是個十分類問題,所以損失函式依然採用交叉熵損失函式;

並且將標籤 y y y替換為 o n e − h o t one-hot onehot向量表示;

通過梯度下降演算法進行引數學習;

啟用函式使用relu啟用函式。

具體演算法實現

import tensorflow as tf
import matplotlib.pyplot as plt
# 輸入層 784 
# 隱藏層 256
# 隱藏層 128 
# 輸出層 10
w1 = tf.Variable(tf.random.truncated_normal([784,256],stddev=0.1))
w2 = tf.Variable(tf.random.truncated_normal([256,128],stddev=0.1))
w3 = tf.Variable(tf.random.truncated_normal([128,10],stddev=0.1))

b1 = tf.Variable(tf.zeros([256]))
b2 = tf.Variable(tf.zeros([128]))
b3 = tf.Variable(tf.zeros([10]))

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = tf.convert_to_tensor(x_train,dtype=tf.float32)/255.

# (60000,28,28) -> (60000,784)
x_train = tf.reshape(x_train,[-1,28*28])
# one-hot
y_train = tf.one_hot(y_train,depth=10)

lr = 0.01
# 開始訓練
All_loss = []
for step in range(4001):
    with tf.GradientTape() as tape:
        tape.watch([w1,b1,w2,b2,w3,b3])
        out3 = tf.nn.softmax(tf.nn.relu(tf.nn.relu(x_train@w1 + b1)@w2 + b2)@w3+b3)
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_train,logits=out3))
    grads = tape.gradient(loss,[w1,b1,w2,b2,w3,b3])
    All_loss.append(loss)
    # 更新引數
    w1.assign_sub(lr*grads[0])
    b1.assign_sub(lr*grads[1])
    w2.assign_sub(lr*grads[2])
    b2.assign_sub(lr*grads[3])
    w3.assign_sub(lr*grads[4])
    b3.assign_sub(lr*grads[5]) 

訓練次數為4000次,學習率 α = 0.01 \alpha=0.01 α=0.01

檢視loss值下降曲線

plt.plot(All_loss)

在這裡插入圖片描述

loss值從1500次之後下降變得平緩,逐漸穩定在1.7以下。

檢視模型在測試集上的效果

x_r_test = x_test
y_r_test = y_test
x_test = tf.convert_to_tensor(x_test,dtype=tf.float32)/255.

x_test = tf.reshape(x_test,[-1,28*28])
y_test=tf.one_hot(y_test,depth=10)

out3 = tf.nn.softmax(tf.nn.relu(tf.nn.relu(x_test@w1 + b1)@w2 + b2)@w3+b3)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_test,logits=out3))

import numpy as np
plt.figure(figsize=(18,18))
for i in range(10):
    plt.subplot(2,5,i+1)
    plt.title(str(y_r_test[i])+str(",")+str(np.argmax(out3[i])))
    plt.imshow(x_r_test[i])
plt.show()

抽取測試集中前10個資料樣本,繪圖檢視結果:

在這裡插入圖片描述

在這裡插入圖片描述

可以看到,這十副圖片的預測結果都是比較準確的。

(圖片標題中,第一個數字是真實值,第二個數字是預測值)

檢視正確率

acc_count = 0
for i in range(len(x_test)):
    if np.argmax(out3[i]) == y_r_test[i]:
        acc_count = acc_count + 1
acc_count/len(x_test)

在這裡插入圖片描述

4000次的迭代,10000個資料的測試集上的預測正確率已經達到了 83.27 % 83.27\% 83.27%

acc_count = 0
for i in range(len(x_train)):
    if np.argmax(out3_train[i]) == y_r_train[i]:
        acc_count = acc_count + 1
acc_count/len(x_train)

在這裡插入圖片描述

而在60000個資料的訓練集上,預測正確率達到了 82.34 % 82.34\% 82.34%

總結

神經網路的使用,突破了很多深度學習任務的瓶頸。使用神經網路去訓練得到的結果,比一般的線性模型要好很多。雖然在啟用函式、損失函式等等函式的使用上,跟簡單的線性模型使用的一樣,但是僅僅是三層神經網路,它的模型就優於線性模型很多了。

而且在訓練次數、學習率、訓練樣本、網路深度、網路結構等等超引數上還可以進行人為調整,那麼在一定範圍內調整這些超引數,還可以得到更高更好的分類結果。

相關文章