1、引言
今天要介绍的是如何通过反向传播算法(backpropagation )和梯度下降算法(gradient decent )调整神经网络中参数的取值。梯度下降算法主要用于优化单个参数的取值,而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法,从而使神经网络模型在训练数据上的损失函数尽可能小。
反向传播算法是训练神经网络的核心算法,它可以根据定义好的损失函数优化神经网络中参数的取值,从而使神经网络模型在训练数据集上的损失函数达到一个较小值。神经网络模型中参数的优化过程直接决定了模型的质量,是使用神经网络时非常重要的一步。今天我们将主要介绍神经网络优化过程的基本概念和主要思想,而略去算法的数学推导和证明,数学的推导网上有很好的文章讲解,这里推荐一篇文章:https://zhuanlan.zhihu.com/p/25081671
2、梯度下降法
假设用θ表示神经网络中的参数,J(θ)表示在给定的参数取值下,训练数据集上损失函数的大小。那么整个优化过程可以抽象为寻找一个参数θ,使得J(θ)最小。因为目前没有一个通用的方法可以对任意损失函数直接求解最佳的参数取值,所以在实践中,梯度下降算法是最常用的神经网络优化方法。梯度下降算法会法代式更新参数θ,不断沿着梯度的反方向让参数朝着总损失更小的方向更新。下图展示了梯度下降算法的基本原理。
图1中x轴表示参数θ的取值,y轴表示损失函数J(θ)的值。曲线表示了在参数θ取不同值时,对应损失函数J(θ)的大小。假设当前的参数和损失值对应图中小圆点的位置,那么梯度下降算法会将参数向x 轴左侧移动,从而使得小圆点朝着箭头的方向移动。参数的梯度可以通过求偏导的方式计算,对于参数θ,其梯度为。有了梯度,还需要定义一个学习率η(learning rate)来定义每次参数更新的幅度。从直观上理解,可以认为学习率定义的就是每次参数移动的幅度。通过参数的梯度和学习率,参数更新的公式为:
可以看出,神经网络的优化过程可以分为两个阶段,第一个阶段先通过前向传播算法计算得到预测值,井将预测值和真实值做对比得出两者之间的差距。然后在第二个阶段通过反向传播算法计算损失函数对每一个参数的梯度,再根据梯度和学习率使用梯度下降算法更新每一个参数。
3、存在的问题
需要注意的是,梯度下降算法并不能保证被优化的函数达到全局最优解。如下图2所示,图中给出的函数就有可能只能得到局部最优解而不是全局最优解。在小黑点处,损失函数的偏导为0 ,于是参数就不会再进一步更新。在这个样例中,如果参数x的初始值落在右侧深色的区间中,那么通过梯度下降得到的结果都会落到小黑点代表的局部最优解。只有当x的初始值落在左侧浅色的区间时梯度下降才能给出全局最优答案。由此可见在训练神经网络时,参数的初始值会很大程度影响最后得到的结果。只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优解。
除了不一定能达到全局最优,梯度下降算法的另外一个问题就是计算时间太长。因为要在全部训练数据上最小化损失,所以损失函数是在所有训练数据上的损失和。这样在每一轮迭代中都需要计算在全部训练数据上的损失函数。在海量训练数据下,要计算所有训练数据的损失函数是非常消耗时间的。为了加速训练过程,可以使用随机梯度下降的算法(stochastic gradient descent)。这个算法优化的不是在全部训练数据上的损失函数,而是在每一轮法代中,随机优化某一条训练数据上的损失函数。这样每一轮参数更新的速度就大大加快了。因为随机梯度下降算法每次优化的只是某一条数据上的损失函数,所以它的问题也非常明显:在某一条数据上损失函数更小并不代表在全部数据上损失函数更小,于是使用随机梯度下降优化得到的神经网络甚至可能无法达到局部最优。
为了综合梯度下降算法和随机梯度下降算法的优缺点,在实际应用中一般采用这两个算法的折中一一每次计算一小部分训练数据的损失函数。这一小部分数据被称之为一个batch 。通过矩阵运算,每次在一个batch 上优化神经网络的参数并不会比单个数据慢太多。另一方面,每次使用一个batch 可以大大减小收敛所需要的法代次数,同时可以使收敛到的结果更加接近梯度下降的效果。
4、一个例子
# 在此仅展示神经网络训练遵循的大致过程:
import tensorflow as tf
batch_size = n
# 每次读取一小部分数据作为当前的训练数据来执行反向传播算法
x = tf.placeholder(tf.float32, shape=(batch_size, 2), name='x-input')
y_ = tf.placeholder(tf.float32, shape=(batch_size, 1), name='y-input')
# 定义神经网络结构和优化算法
loss = ...
train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
# 训练神经网络
with tf.Session() as sess:
# 参数初始化
...
# 迭代地更新参数
for i in range(steps):
# 准备batch_size个训练数据。一般讲所有数据随机打乱后再选取可以得到更好的优化效果
current_X =, current_Y = ...
sess.run(train_step, feed_dict={x: current_X, y: current_Y})
参考文献
书籍:Tensorflow:实战Google深度学习框架(第二版)