• 基础示例:线性回归

    基础示例:线性回归

    与前面的NumPy和Eager Execution模式不同,TensorFlow的Graph Execution模式使用 符号式编程 来进行数值运算。首先,我们需要将待计算的过程抽象为数据流图,将输入、运算和输出都用符号化的节点来表达。然后,我们将数据不断地送入输入节点,让数据沿着数据流图进行计算和流动,最终到达我们需要的特定输出节点。以下代码展示了如何基于TensorFlow的符号式编程方法完成与前节相同的任务。其中, tf.placeholder() 即可以视为一种“符号化的输入节点”,使用 tf.get_variable() 定义模型的参数(Variable类型的张量可以使用 tf.assign() 进行赋值),而 sess.run(output_node, feed_dict={input_node: data}) 可以视作将数据送入输入节点,沿着数据流图计算并到达输出节点并返回值的过程。

    1. import tensorflow as tf
    2.  
    3. # 定义数据流图
    4. learning_rate_ = tf.placeholder(dtype=tf.float32)
    5. X_ = tf.placeholder(dtype=tf.float32, shape=[5])
    6. y_ = tf.placeholder(dtype=tf.float32, shape=[5])
    7. a = tf.get_variable('a', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
    8. b = tf.get_variable('b', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
    9.  
    10. y_pred = a * X_ + b
    11. loss = tf.constant(0.5) * tf.reduce_sum(tf.square(y_pred - y_))
    12.  
    13. # 反向传播,手动计算变量(模型参数)的梯度
    14. grad_a = tf.reduce_sum((y_pred - y_) * X_)
    15. grad_b = tf.reduce_sum(y_pred - y_)
    16.  
    17. # 梯度下降法,手动更新参数
    18. new_a = a - learning_rate_ * grad_a
    19. new_b = b - learning_rate_ * grad_b
    20. update_a = tf.assign(a, new_a)
    21. update_b = tf.assign(b, new_b)
    22.  
    23. train_op = [update_a, update_b]
    24. # 数据流图定义到此结束
    25. # 注意,直到目前,我们都没有进行任何实质的数据计算,仅仅是定义了一个数据图
    26.  
    27. num_epoch = 10000
    28. learning_rate = 1e-3
    29. with tf.Session() as sess:
    30. # 初始化变量a和b
    31. tf.global_variables_initializer().run()
    32. # 循环将数据送入上面建立的数据流图中进行计算和更新变量
    33. for e in range(num_epoch):
    34. sess.run(train_op, feed_dict={X_: X, y_: y, learning_rate_: learning_rate})
    35. print(sess.run([a, b]))

    在上面的两个示例中,我们都是手工计算获得损失函数关于各参数的偏导数。但当模型和损失函数都变得十分复杂时(尤其是深度学习模型),这种手动求导的工程量就难以接受了。TensorFlow提供了 自动求导机制 ,免去了手工计算导数的繁琐。利用TensorFlow的求导函数 tf.gradients(ys, xs) 求出损失函数loss关于a,b的偏导数。由此,我们可以将上节中的两行手工计算导数的代码

    1. # 反向传播,手动计算变量(模型参数)的梯度
    2. grad_a = tf.reduce_sum((y_pred - y_) * X_)
    3. grad_b = tf.reduce_sum(y_pred - y_)

    替换为

    1. grad_a, grad_b = tf.gradients(loss, [a, b])

    计算结果将不会改变。

    甚至不仅于此,TensorFlow附带有多种 优化器 (optimizer),可以将求导和梯度更新一并完成。我们可以将上节的代码

    1. # 反向传播,手动计算变量(模型参数)的梯度
    2. grad_a = tf.reduce_sum((y_pred - y_) * X_)
    3. grad_b = tf.reduce_sum(y_pred - y_)
    4.  
    5. # 梯度下降法,手动更新参数
    6. new_a = a - learning_rate_ * grad_a
    7. new_b = b - learning_rate_ * grad_b
    8. update_a = tf.assign(a, new_a)
    9. update_b = tf.assign(b, new_b)
    10.  
    11. train_op = [update_a, update_b]

    整体替换为

    1. optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_)
    2. grad = optimizer.compute_gradients(loss)
    3. train_op = optimizer.apply_gradients(grad)

    这里,我们先实例化了一个TensorFlow中的梯度下降优化器 tf.train.GradientDescentOptimizer() 并设置学习率。然后利用其 compute_gradients(loss) 方法求出 loss 对所有变量(参数)的梯度。最后通过 apply_gradients(grad) 方法,根据前面算出的梯度来梯度下降更新变量(参数)。

    以上三行代码等价于下面一行代码:

    1. train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_).minimize(loss)

    简化后的代码如下:

    1. import tensorflow as tf
    2.  
    3. learning_rate_ = tf.placeholder(dtype=tf.float32)
    4. X_ = tf.placeholder(dtype=tf.float32, shape=[5])
    5. y_ = tf.placeholder(dtype=tf.float32, shape=[5])
    6. a = tf.get_variable('a', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
    7. b = tf.get_variable('b', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
    8.  
    9. y_pred = a * X_ + b
    10. loss = tf.constant(0.5) * tf.reduce_sum(tf.square(y_pred - y_))
    11.  
    12. # 反向传播,利用TensorFlow的梯度下降优化器自动计算并更新变量(模型参数)的梯度
    13. train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_).minimize(loss)
    14.  
    15. num_epoch = 10000
    16. learning_rate = 1e-3
    17. with tf.Session() as sess:
    18. tf.global_variables_initializer().run()
    19. for e in range(num_epoch):
    20. sess.run(train_op, feed_dict={X_: X, y_: y, learning_rate_: learning_rate})
    21. print(sess.run([a, b]))