实现反向传播

使用 TensorFlow 的一个好处是它可以跟踪操作并根据反向传播自动更新模型变量。在本文中,我们将介绍如何在训练机器学习模型时将此方面用于我们的优势。

做好准备

现在,我们将介绍如何以最小化损失函数的方式更改模型中的变量。我们已经学会了如何使用对象和操作,并创建了测量我们的预测和目标之间距离的损失函数。现在,我们只需告诉 TensorFlow 如何通过我们的计算图反向传播错误来更新变量并最小化损失函数。这是通过声明优化函数完成的。一旦我们声明了一个优化函数,TensorFlow 将通过并计算出图中所有计算的反向传播项。当我们输入数据并最小化 los 函数时,TensorFlow 将相应地修改图中的变量。

对于这个秘籍,我们将做一个非常简单的回归算法。我们将从正态分布中抽取随机数,均值为 1,标准差为 0.1。然后,我们将通过一个操作来运行数字,这将是它们乘以变量A。由此,损失函数将是输出和目标之间的 L2 范数,其总是值 10.理论上,A 的最佳值将是数字 10,因为我们的数据将具有平均值 1。

第二个例子是一个非常简单的二分类算法。在这里,我们将从两个正态分布N(-1,1)N(3,1)生成 100 个数字。来自N(-1, 1)的所有数字将在目标等级 0 中,并且来自N(3, 1)的所有数字将在目标等级 1 中。用于区分这些数字的模型将是翻译的 S 形函数。换句话说,模型将是sigmoid(x + A),其中A是我们将适合的变量。从理论上讲,A 将等于-1。我们得到这个数字是因为如果m1m2是两个正常函数的平均值,那么加到它们以将它们等距离转换为零的值将是 - (m1 + m2) / 2。我们将在第二个例子中看到 TensorFlow 如何达到该数字。

虽然指定良好的学习率有助于算法的收敛,但我们还必须指定一种优化。从前两个例子中,我们使用标准梯度下降。这是通过GradientDescentOptimizer() TensorFlow 函数实现的。

操作步骤

以下是回归示例的工作原理:

  1. 我们首先加载numpytensorflow数值 Python 包:
import numpy as np 
import tensorflow as tf
  1. 现在,我们开始图会话:
sess = tf.Session()
  1. 接下来,我们创建数据,占位符和A变量:
x_vals = np.random.normal(1, 0.1, 100) 
y_vals = np.repeat(10., 100) 
x_data = tf.placeholder(shape=[1], dtype=tf.float32) 
y_target = tf.placeholder(shape=[1], dtype=tf.float32) 
A = tf.Variable(tf.random_normal(shape=[1]))
  1. 我们将乘法运算添加到图中:
my_output = tf.mul(x_data, A)
  1. 接下来,我们在乘法输出和目标数据之间添加 L2 Loss函数:
loss = tf.square(my_output - y_target)
  1. 现在,我们必须声明一种优化图中变量的方法。我们声明了一种优化算法。大多数优化算法需要知道每次迭代中的步进距离。该距离由学习率控制。如果我们的学习率太大,我们的算法可能会超过最小值,但如果我们的学习率太小,我们的算法可能需要很长时间才能收敛;这与消失和爆炸的梯度问题有关。学习率对收敛有很大影响,我们将在本节末尾讨论这个问题。虽然我们在这里使用标准梯度下降算法,但是有许多不同的优化算法可以不同地运行,并且可以根据问题做得更好或更差。有关不同优化算法的精彩概述,请参阅 Sebastian Ruder 在本文末尾的另请参阅部分中的文章:
my_opt = tf.train.GradientDescentOptimizer(learning_rate=0.02)
train_step = my_opt.minimize(loss)
  1. 现在我们可以初始化我们的模型变量:
init = tf.global_variable_initializer()
sess.run(init)

There is a lot of theory on which learning rates are best. This is one of the harder things to figure out in machine learning algorithms. Good papers to read about how learning rates are related to specific optimization algorithms are listed in the See also section at the end of this recipe.

  1. 最后一步是循环我们的训练算法并告诉 TensorFlow 多次训练。我们将这样做 101 次,并且每 25 次迭代打印出结果。为了训练,我们将选择随机xy条目并通过图提供。 TensorFlow 将自动计算损失,并略微改变A偏差以最小化损失:
for i in range(100): 
    rand_index = np.random.choice(100) 
    rand_x = [x_vals[rand_index]] 
    rand_y = [y_vals[rand_index]] 
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y}) 
    if (i + 1) % 25 == 0: 
        print('Step #' + str(i+1) + ' A = ' + str(sess.run(A))) 
        print('Loss = ' + str(sess.run(loss, feed_dict={x_data: rand_x, y_target: rand_y}))) 
# Here is the output: 
Step #25 A = [ 6.23402166] 
Loss = 16.3173 
Step #50 A = [ 8.50733757] 
Loss = 3.56651 
Step #75 A = [ 9.37753201] 
Loss = 3.03149 
Step #100 A = [ 9.80041122] 
Loss = 0.0990248

现在,我们将介绍简单分类示例的代码。如果我们先重置图,我们可以使用相同的 TensorFlow 脚本。请记住,我们将尝试找到一个最佳平移A,它将两个分布转换为原点,而 sigmoid 函数将两个分为两个不同的类:

  1. 首先,我们重置图并重新初始化图会话:
from tensorflow.python.framework import ops 
ops.reset_default_graph() 
sess = tf.Session()
  1. 接下来,我们从两个不同的正态分布N(-1, 1)N(3, 1)中提取数据。我们还将生成目标标签,数据占位符和偏差变量A
x_vals = np.concatenate((np.random.normal(-1, 1, 50), np.random.normal(3, 1, 50))) 
y_vals = np.concatenate((np.repeat(0., 50), np.repeat(1., 50))) 
x_data = tf.placeholder(shape=[1], dtype=tf.float32) 
y_target = tf.placeholder(shape=[1], dtype=tf.float32) 
A = tf.Variable(tf.random_normal(mean=10, shape=[1]))

我们将A初始化为大约 10 的值,远离理论值-1。我们这样做的目的是为了说明算法如何从 10 的值收敛到最佳值-1。

  1. 接下来,我们将转换操作添加到图中。请记住,我们不必将它包装在 sigmoid 函数中,因为 loss 函数将为我们执行此操作:
my_output = tf.add(x_data, A)
  1. 由于特定损失函数需要具有与之关联的额外维度的批量数据(添加的维度,即批次编号),因此我们将使用expand_dims()函数为输出添加额外维度。在下一节中,我们将讨论如何在训练中使用可变大小的批次。现在,我们将再次使用一个随机数据点:
my_output_expanded = tf.expand_dims(my_output, 0) 
y_target_expanded = tf.expand_dims(y_target, 0)
  1. 接下来,我们将初始化我们的一个变量A
init = tf.initialize_all_variables() 
sess.run(init)
  1. 现在,我们宣布我们的损失函数。我们将使用带有未缩放的 logits 的交叉熵,它使用 sigmoid 函数对它们进行转换。在名为nn.sigmoid_cross_entropy_with_logits()的神经网络包中,TensorFlow 为我们提供了这一函数。如前所述,它希望参数具有特定的维度,因此我们必须相应地使用扩展的输出和目标:
xentropy = tf.nn.sigmoid_cross_entropy_with_logits( my_output_expanded, y_target_expanded)
  1. 与回归示例一样,我们需要向图中添加优化器函数,以便 TensorFlow 知道如何更新图中的偏差变量:
my_opt = tf.train.GradientDescentOptimizer(0.05) 
train_step = my_opt.minimize(xentropy)
  1. 最后,我们循环遍历随机选择的数据点数百次并相应地更新A变量。每 200 次迭代,我们将打印出A的值和损失:
for i in range(1400): 
    rand_index = np.random.choice(100) 
    rand_x = [x_vals[rand_index]] 
    rand_y = [y_vals[rand_index]] 
    sess.run(train_step, feed_dict={x_data: rand_x, y_target: rand_y}) 
    if (i + 1) % 200 == 0: 
        print('Step #' + str(i+1) + ' A = ' + str(sess.run(A))) 
        print('Loss = ' + str(sess.run(xentropy, feed_dict={x_data: rand_x, y_target: rand_y}))) 
Step #200 A = [ 3.59597969] 
Loss = [[ 0.00126199]] 
Step #400 A = [ 0.50947344] 
Loss = [[ 0.01149425]] 
Step #600 A = [-0.50994617] 
Loss = [[ 0.14271219]] 
Step #800 A = [-0.76606178] 
Loss = [[ 0.18807337]] 
Step #1000 A = [-0.90859312] 
Loss = [[ 0.02346182]] 
Step #1200 A = [-0.86169094] 
Loss = [[ 0.05427232]] 
Step #1400 A = [-1.08486211] 
Loss = [[ 0.04099189]]

工作原理

有关回顾和解释,对于这两个示例,我们执行了以下操作:

  1. 创建了数据。这两个示例都需要通过占位符加载数据。
  2. 初始化占位符和变量。这些是非常相似的数据占位符。变量非常相似,它们都有乘法矩阵A,但第一个分类算法有一个偏差项来找到数据中的分裂。
  3. 创建了损失函数,我们使用 L2 损失进行回归,使用交叉熵损失进行分类。
  4. 定义了一种优化算法。两种算法都使用梯度下降。
  5. 迭代随机数据样本以迭代更新我们的变量。

更多

如前所述,优化算法对学习率的选择很敏感。重要的是要以简洁的方式总结这种选择的效果:

学习率大小 优点缺点 用途
较小的学习率 收敛速度较慢但结果更准确 如果解决方案不稳定,请先尝试降低学习率
学习率更高 不太准确,但收敛速度更快 对于某些问题,有助于防止解决方案停滞不前

有时,标准梯度下降算法会显着卡住或减速。当优化卡在马鞍的平坦点时,可能会发生这种情况。为了解决这个问题,还有另一种算法考虑了动量项,它增加了前一步骤的梯度下降值的一小部分。 TensorFlow 内置了MomentumOptimizer()函数。

另一种变体是为我们模型中的每个变量改变优化器步骤。理想情况下,我们希望为较小的移动变量采取较大的步骤,为较快的变化变量采取较短的步骤。我们不会深入研究这种方法的数学,但这种思想的常见实现称为 Adagrad 算法。该算法考虑了变量梯度的整个历史。 TensorFlow 中的函数称为AdagradOptimizer()

有时候,Adagrad 会过早地强调梯度为零,因为它考虑了整个历史。解决方法是限制我们使用的步数。这样做称为 Adadelta 算法。我们可以使用AdadeltaOptimizer()函数来应用它。

还有一些不同的梯度下降算法的其他实现。对于这些,我们会将读者引用到 TensorFlow 文档: https://www.tensorflow.org/api_guides/python/train

另见

有关优化算法和学习率的一些参考,请参阅以下文章和文章:

results matching ""

    No results matching ""