前言: 

       梯度下降算法主要用于优化单个参数的取值, 而反向传播算法给出了一个高效的方式在所有参数上使用梯度下降算法,从而使神经网络 模型在训练数据上的损失函数尽可能小。反向传播算法是训练神经网络的核心算法,它可以根据定义好的损失函数优化神经网络中参数的取值,从而使神经网络模型在训练数据集上的损失函数达到一个较小值。本文将给出一个具体的样例来解释使用梯度下降算法优化参数取值的过程。

一、梯度下降法        

       假设用

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪

表示神经网络中的参数,

神经网络一维数据降噪 神经网络降噪算法_梯度下降算法_02

表示在给定的参数取值下,训练数据集上损失函数的大小,那么整个优化过程可以抽象为寻找一个参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪

,使得

神经网络一维数据降噪 神经网络降噪算法_梯度下降算法_02

最小。因为目前没有一个通用的方法可以对任意损失函数直接求解最佳的参数取值,所以在实践中,梯度下降算法是最常用的神经网络优化方法。梯度下降算法会迭代式更新参数不断沿着梯度的反方向让参数朝着总损失更小的方向更新。通过参数的梯度和学习率,参数更新的公式为:

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_05

其中

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_06

为学习率,用来定义每次参数更新的幅度。从直观上理解,可以认为学习率定义的就是每次参数移动的幅度。        

       下面给出了一个具体的例子来说明梯度下降算法是如何工作的:

      假设要通过梯度下降算法来优化参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_07

,使得损失函数

神经网络一维数据降噪 神经网络降噪算法_数据_08

的值尽量小。梯度下降算法的第一步需要随机产生一个参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_07

的初始值,然后再通过梯度和学习率来更新参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_07

的取值。在这个样例中,参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_07

的梯度为

神经网络一维数据降噪 神经网络降噪算法_数据_12

,那么使用梯度下降算法每次对参数

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_07

的更新公式为

神经网络一维数据降噪 神经网络降噪算法_梯度下降算法_14

。假设参数的初始值为5,学习率为0.3,那么这个优化过程可以总结为下表:

神经网络一维数据降噪 神经网络降噪算法_神经网络一维数据降噪_15

 从表中可以看出,经过5次迭代之后,参数x的值变成了 0.0512,这个和参数最 优值0已经比较接近了。虽然这里给出的是一个非常简单的样例,但是神经网络的优化过 程也是可以类推的。神经网络的优化过程可以分为两个阶段,第一个阶段先通过前向传播算法计算得到预测值,并将预测值和真实值做对比得出两者之间的差距。然后在第二个阶段通过反向传播算法计算损失函数对每一个参数的梯度,再根据梯度和学习率使用梯度下 降算法更新每一个参数。

需要注意的是,梯度下降算法存在两个问题:

(1)梯度下降算法并不能保证被优化的函数达到全局最优解,在训练神经网络时,参数的初始值会很大程度影响最后得到的结果。只有当损失函数为凸函数时,梯度下降算法才能保证达到全局最优解。

(2)梯度下降算法的另外一个问题就是计算时间太长。因为要在全部训练数据上最小化损失,所以损失函数

神经网络一维数据降噪 神经网络降噪算法_梯度下降算法_02

是在所有训练数据上的损失和。这样在每一轮迭代中都需要计算在全部训练数据上的损失函数。在海量训练数据下,要计算所有训练数据的损失函数是非常消耗时间的。


 为了避免梯度下降算法缺点,在实际应用中一般会这样做:每次计算一小部分训练数据的损失函数。这一小部分数据被称之为一个batch。通过矩阵运算,每次在一个batch上优化神经网络的参数并不会比单个数据慢太多。 另一方面,每次使用一个batch可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果。以下代码给出了在TensorFlow中如何实现神经网络的训 练过程:

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='x-input')
#定义神经网络结构和优化算法。
loss = 自定义
learning_rate = 0.001
train_step = tf.train.AdamOptimizer(learning_rate ).minimize(loss)
#训练神经网络。
with tf.Session() as sess :
    # 参数初始化
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    #参数迭代更新
    for i in range(STEPS):
    #准备batch_SiZe个训练数据。一般将所有训练数据随机打乱之后再选取可以得到 
    # 更好的优化效果。 
        current_X, current_Y =…
        sess.run(train_step, feed_dict={x: current_X, y_: current_Y})


二、学习率的设置

      在训练神经网络时,需要设置学习率(learning rate)控制参数更新的速度。学习率决定了参数每次更新的幅度。如果幅度过大, 那么可能导致参数在极优值的两侧来回移动。相反,当学习率过小时,虽然能保证收敛性,但是这会大大降低优化速度。我们会需要更多轮的迭代才能达到一个比较理想的优化效果。

      因此,学习率既不能过大,也不能过小。为了解决设定学习率的问题,TensorFlow提供了一种更加灵活的学习率设置方法——指数衰减法。 tf.train.exponential_decay函数实现了指数衰减学习率。通过这个函数,可以先使用较大的学 习率来快速得到一个比较优的解,然后随着迭代的继续逐步减小学习率,使得模型在训练后 期更加稳定。exponential_decay函数会指数级地减小学习率,它实现了以下代码的功能:

decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)

      其中decayed_leaming_rate为每一轮优化时使用的学习率,leaming_rate为事先设定的初始学习率,decay_rate为衰减系数,decay_steps为衰减速度。tf.train.exponential_decay函数可以通过设置参数staircase选择不同的衰减方式。staircase的默认值为False。当staircase的值被设置为True时,global_step / decay_steps会被转化成整数。这使得学习率成为一个阶梯函数(staircase ftmction)。在这样的设置下,decay_steps通常代表了完整的使用一遍训练数据所需要的迭代轮数。这个迭代轮数也就是总训练样本数除以每一个batch中的训练样本数。这种设置的常用场景是每完整地过完一遍训练数据,学习率就减小一次。这可以使得训练数据集中的所有数据对模型训练有相等的作用。

     下面给出了一段代码来示范如何在TensorFlow中使用tf.train.exponential_decay函数:

global_step = tf.Variable(0)

learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
#通过exponential_decay函数生成学习率。其中,初始学习率为0.1,因为指定了StaircaSe=True,所以每训练100轮后学习率乘以0.96。

learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize (自定义loss_function, global_step = global_step)
# 使用指数衰减的学习率。在minimize函数中传入global_step将自动更新
# global_Step参数,从而使得学习率也得到相应更新。


      一般来说初始学习率、衰减系数和衰减速度都是根据经验设置的。 而且损失函数下降的速度和迭代结束之后总损失的大小没有必然的联系。也就是说并不能通过前几轮损失函数下降的速度来比较不同神经网络的效果。



参考文献:

[1]  郑泽宇著  《TensorFlow实战Google深度学习框架》

[2] 黄文坚著  《TensorFlow实战》