深度学习之Learning Speed:二次代价函数和交叉熵函数在不同初值情况下的学习速度对比实验
一、实验说明
本文是《深入浅出神经网络与深度学习》第三章 改进神经网络的学习方法,第3.1节交叉熵代价函数,关于学习速度实验的复现。
实验用一个只包含一个输入的神经元,该神经元输入
1
1
1,输出
0
0
0,激活函数为
s
i
g
m
o
i
d
sigmoid
sigmoid 函数,神经元结构如下:
实验通过设置不同的初始权重和偏置,对比分别二次代价函数和交叉熵代价函数下的网络学习速度。
我们知道,梯度下降算法是通过计算权重和偏置的偏导数
∂
C
∂
w
\frac{\partial C}{\partial w}
∂w∂C,
∂
C
∂
b
\frac{\partial C}{\partial b}
∂b∂C,来更新新的权重和偏置,所以代价函数的偏导数决定了参数的更新速度,即学习速度。基于二次代价函数
C
=
(
y
−
a
)
2
2
C = \frac{(y-a)^2}{2}
C=2(y−a)2,可以计算上述单个神经元的权重和偏置的导数:
∂
C
∂
w
=
(
a
−
y
)
σ
′
(
z
)
x
=
a
σ
′
(
z
)
\frac{\partial C}{\partial w}=(a-y)\sigma^\prime(z)x=a\sigma^\prime(z)
∂w∂C=(a−y)σ′(z)x=aσ′(z)
∂
C
∂
b
=
(
a
−
y
)
σ
′
(
z
)
=
a
σ
′
(
z
)
\frac{\partial C}{\partial b}=(a-y)\sigma^\prime(z)=a\sigma^\prime(z)
∂b∂C=(a−y)σ′(z)=aσ′(z)
其中,
a
=
σ
(
z
)
a=\sigma(z)
a=σ(z),
z
=
w
x
+
b
z=wx+b
z=wx+b。从上述偏导数可知,当神经元的输出值
a
a
a 接近于
1
1
1时,
σ
′
(
z
)
\sigma^\prime(z)
σ′(z)的值就会很小,这点从
s
i
g
m
o
i
d
sigmoid
sigmoid 函数的图像上可以很明显的看到,如下图:
而当采用交叉熵作为代价函数时,权重和偏置的偏导数计算如下:
∂
C
∂
w
=
x
(
σ
(
z
)
−
y
)
\frac{\partial C}{\partial w}=x(\sigma(z)-y)
∂w∂C=x(σ(z)−y)
∂
C
∂
b
=
(
σ
(
z
)
−
y
)
\frac{\partial C}{\partial b}=(\sigma(z)-y)
∂b∂C=(σ(z)−y)
由上式可知,采用交叉熵代价函数时,梯度的更新与偏差有关,偏差越大,梯度值也越大,所以其学习速度也越快。
二、实验结果
通过如下四个小实验,可以直观的感受到,不同初始权重和偏置情况下,采用二次代价函数和交叉熵代价函数对网络学习速度的影响。因此,在选择
s
i
g
m
o
i
d
sigmoid
sigmoid 函数作为激活函数时,我们往往选择交叉熵来作为代价函数,因为它的学习速度更快。
实验一:二次代价函数,权重和偏置初始化为
0.6
0.6
0.6 和
0.9
0.9
0.9,损失曲线如下图。从图中可以看到,前40个迭代期,损失曲线比较陡。说明梯度下降学习算法在这个阶段快速的学习到了时代价函数下降的参数。
实验二:二次代价函数,权重和偏置初始化为
2.0
2.0
2.0 和
2.0
2.0
2.0,损失曲线如下图。从图中可以看到,与实验一不同的是,在前期的迭代期内,损失曲线比较平缓,大约到第100个迭代期时,曲线才变得陡峭。说在迭代前期算法的学习速度比较缓慢。
实验三:交叉熵函数,权重和偏置初始化为
0.6
0.6
0.6 和
0.9
0.9
0.9,损失曲线如下图。从图中可以看到,与实验一类似,损失曲线从一开始就比较陡,而且比实验一的曲线还要陡。说明在这个阶段,在相同权重和偏置初始值时,采用交叉熵作为代价函数的神经元的学习速度要更快。
实验四:交叉熵函数,权重和偏置初始化为
2.0
2.0
2.0 和
2.0
2.0
2.0,损失曲线如下图。从图中可以看到,跟实验二前期平缓的曲线相比,实验四的损失曲线显示其学习速度要大大的快于实验二,其陡峭程度与实验三基本是一致的。也就是说,在采用 sigmoid 函数作为激活函数,交叉熵作为代价函数的情况下,权重、偏置的初始值,对网络的学习速度影响有限。
三、实验代码
#!/user/bin/env python3
# -*- coding : utf-8 -*-
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
def main():
Wi,Bi = 0.6,0.9 # w,b的初始值
# Wi,Bi = 2.0,2.0 # w,b的初始值
w = tf.Variable(Wi) # 用于网络中更新的权重w
b = tf.Variable(Bi) # 用于网络中更新的偏置b
lr = 0.15 # 学习率
# 定义优化方法(随机梯度下降)和损失函数(均方误差函数)
opt = tf.optimizers.SGD(learning_rate=lr)
# lossf = tf.keras.losses.MeanSquaredError() # 二次代价函数
lossf = tf.keras.losses.BinaryCrossentropy() # 交叉熵函数
loss_array = [] # 用于存储每轮的训练损失
epochs = 300 # 迭代次数
for epoch in range(epochs):
x,y = np.array([1.0]),np.array([0.0]) # 输入和输出,网络实现输入“1”,输出“0”
with tf.GradientTape() as tape:
pred = tf.keras.activations.sigmoid(w*x+b) # 计算输出
loss = lossf(y,pred) # 计算损失
gradients = tape.gradient(target=loss,sources=[w,b]) # 求导
opt.apply_gradients(zip(gradients,[w,b])) # 将导数更新到w,b中
# 打印每轮训练的损失和权重与偏置
print('epoch %3d/%3d, loss:{%f}, weight:{%f}, bias:{%f}.'%(epoch,epochs,loss,w.numpy(),b.numpy()))
# 存储每轮的损失
loss_array.append(loss)
# 作图
fig = plt.figure()
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
fig.suptitle('loss curve of training')
plt.plot(range(epochs),loss_array,color='magenta')
plt.title('loss function = %s, initial w,b = %f,%f, learing rate = %f'%(lossf.name,Wi,Bi,lr),fontsize=9)
plt.ylabel("Loss")
plt.xlabel("Epoch")
plt.grid()
plt.show()
if __name__=='__main__':
main()
四、参考资料
[1] 《深入浅出神经网络与深度学习》 (英文版链接)