假设现在有一个分类问题:
- feature是2维的向量
- 目标类别有3种
- 一共有4个样本:
转者注:上面虽然使用的是Sigmoid函数,其实分类问题使用的是softmax函数,softmax可以看做sigmoid在对分类问题上的推广。
转者注:上面的 y 1 ′ ( 1 − y 1 ′ ) y'_1(1-y'_1) y1′(1−y1′)是a对z的导数,a是softmax函数的输出。有关softmax函数的求导请参考博客。
import matplotlib.pyplot as plt
import numpy as np
A = np.linspace(0, 1, 100)
plt.plot(A, A ** 2 * (1 - A))
plt.xlabel("absolute error")
plt.ylabel("$\delta w_{11}$")
plt.title("$\delta w_{11}$=f(A)")
plt.show()
随着绝对误差的增大,权值需要调整的幅度先变大后变小,这就导致当绝对误差很大时,模型显得“自暴自弃”不肯学习随着绝对误差的增大,权值需要调整的幅度先变大后变小,这就导致当绝对误差很大时,模型显得“自暴自弃”不肯学习
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
x = tf.placeholder(dtype=tf.float32, shape=(), name="x")
y = tf.placeholder(dtype=tf.float32, shape=(), name="y")
w = tf.Variable(0.8)
b = tf.Variable(0.2)
yy = tf.sigmoid(w * x + b)
cross = -y * tf.log(yy)
mse = (yy - y) ** 2
cross_grad = tf.gradients(cross, [w, b])
mse_grad = tf.gradients(mse, [w, b])
abs_error = tf.abs(y - yy)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
w_value_list = np.linspace(-8, 8, 100)
cross_grad_w_list, mse_grad_w_list, abs_error_list = [], [], []
for w_value in w_value_list:
sess.run(tf.assign(w, w_value))
abs_error_value, (cross_grad_w, _), (mse_grad_w, _) = sess.run([abs_error, cross_grad, mse_grad], feed_dict={
x: 1,
y: 1
})
cross_grad_w_list.append(cross_grad_w)
mse_grad_w_list.append(mse_grad_w)
abs_error_list.append(abs_error_value)
plt.plot(abs_error_list, cross_grad_w_list, label="cross_w=f(A)")
plt.plot(abs_error_list, mse_grad_w_list, label="mse_w=f(A)")
plt.title("why do we use cross_entropy?")
plt.legend()
plt.show()
实验证明,理论推导是正确的:交叉熵使得梯度与绝对误差成正比,二范数导致梯度变得扭曲参考资料。
总结:
其实之前面试的时候我被问到过这个问题,当时我是懵的,现在总结一下:
- 神经网络中如果预测值与实际值的误差越大,那么在反向传播训练的过程中,各种参数调整的幅度就要更大,从而使训练更快收敛,如果预测值与实际值的误差小,各种参数调整的幅度就要小,从而减少震荡。
- 使用平方误差损失函数,误差增大参数的梯度会增大,但是当误差很大时,参数的梯度就会又减小了。
- 使用交叉熵损失是函数,误差越大参数的梯度也越大,能够快速收敛。
参考文章:为什么使用交叉熵作为损失函数 - weiyinfu的文章 - 知乎
除了知乎的回答外,一篇博客也很棒,两个结合效果更佳交叉熵代价函数(作用及公式推导).