前馈神经网络
前馈神经网络是一种最简单的神经网络,各神经元分层排列,每个神经元只与前一层的神经元相连。接收前一层的输出,并输出给下一层,各层之间没有反馈。是目前应用最广泛、发展最迅速的人工神经网络之一。——百度百科
借用百度百科中的前馈神经网络示意图
这是一个多层前馈神经网络模型,除开输入层和输出层外,有两层隐藏层。
这里是一个全连接的情况,即除开输入层外,每一层的每个神经元都与前一层所有的神经元相连。
根据这样的一个模型,前馈神经网络可以通过逐层的信息传递,得到整个网络的最终输出
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(L−1)→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(i−1)进行计算后得到的数据。
这个 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 one−hot向量表示;
通过梯度下降算法进行参数学习;
激活函数使用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%。
总结
神经网络的使用,突破了很多深度学习任务的瓶颈。使用神经网络去训练得到的结果,比一般的线性模型要好很多。虽然在激活函数、损失函数等等函数的使用上,跟简单的线性模型使用的一样,但是仅仅是三层神经网络,它的模型就优于线性模型很多了。
而且在训练次数、学习率、训练样本、网络深度、网络结构等等超参数上还可以进行人为调整,那么在一定范围内调整这些超参数,还可以得到更高更好的分类结果。