基础示例:线性回归
与前面的NumPy和Eager Execution模式不同,TensorFlow的Graph Execution模式使用 符号式编程 来进行数值运算。首先,我们需要将待计算的过程抽象为数据流图,将输入、运算和输出都用符号化的节点来表达。然后,我们将数据不断地送入输入节点,让数据沿着数据流图进行计算和流动,最终到达我们需要的特定输出节点。以下代码展示了如何基于TensorFlow的符号式编程方法完成与前节相同的任务。其中, tf.placeholder()
即可以视为一种“符号化的输入节点”,使用 tf.get_variable()
定义模型的参数(Variable类型的张量可以使用 tf.assign()
进行赋值),而 sess.run(output_node, feed_dict={input_node: data})
可以视作将数据送入输入节点,沿着数据流图计算并到达输出节点并返回值的过程。
- import tensorflow as tf
- # 定义数据流图
- learning_rate_ = tf.placeholder(dtype=tf.float32)
- X_ = tf.placeholder(dtype=tf.float32, shape=[5])
- y_ = tf.placeholder(dtype=tf.float32, shape=[5])
- a = tf.get_variable('a', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
- b = tf.get_variable('b', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
- y_pred = a * X_ + b
- loss = tf.constant(0.5) * tf.reduce_sum(tf.square(y_pred - y_))
- # 反向传播,手动计算变量(模型参数)的梯度
- grad_a = tf.reduce_sum((y_pred - y_) * X_)
- grad_b = tf.reduce_sum(y_pred - y_)
- # 梯度下降法,手动更新参数
- new_a = a - learning_rate_ * grad_a
- new_b = b - learning_rate_ * grad_b
- update_a = tf.assign(a, new_a)
- update_b = tf.assign(b, new_b)
- train_op = [update_a, update_b]
- # 数据流图定义到此结束
- # 注意,直到目前,我们都没有进行任何实质的数据计算,仅仅是定义了一个数据图
- num_epoch = 10000
- learning_rate = 1e-3
- with tf.Session() as sess:
- # 初始化变量a和b
- tf.global_variables_initializer().run()
- # 循环将数据送入上面建立的数据流图中进行计算和更新变量
- for e in range(num_epoch):
- sess.run(train_op, feed_dict={X_: X, y_: y, learning_rate_: learning_rate})
- print(sess.run([a, b]))
在上面的两个示例中,我们都是手工计算获得损失函数关于各参数的偏导数。但当模型和损失函数都变得十分复杂时(尤其是深度学习模型),这种手动求导的工程量就难以接受了。TensorFlow提供了 自动求导机制 ,免去了手工计算导数的繁琐。利用TensorFlow的求导函数 tf.gradients(ys, xs)
求出损失函数loss关于a,b的偏导数。由此,我们可以将上节中的两行手工计算导数的代码
- # 反向传播,手动计算变量(模型参数)的梯度
- grad_a = tf.reduce_sum((y_pred - y_) * X_)
- grad_b = tf.reduce_sum(y_pred - y_)
替换为
- grad_a, grad_b = tf.gradients(loss, [a, b])
计算结果将不会改变。
甚至不仅于此,TensorFlow附带有多种 优化器 (optimizer),可以将求导和梯度更新一并完成。我们可以将上节的代码
- # 反向传播,手动计算变量(模型参数)的梯度
- grad_a = tf.reduce_sum((y_pred - y_) * X_)
- grad_b = tf.reduce_sum(y_pred - y_)
- # 梯度下降法,手动更新参数
- new_a = a - learning_rate_ * grad_a
- new_b = b - learning_rate_ * grad_b
- update_a = tf.assign(a, new_a)
- update_b = tf.assign(b, new_b)
- train_op = [update_a, update_b]
整体替换为
- optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_)
- grad = optimizer.compute_gradients(loss)
- train_op = optimizer.apply_gradients(grad)
这里,我们先实例化了一个TensorFlow中的梯度下降优化器 tf.train.GradientDescentOptimizer()
并设置学习率。然后利用其 compute_gradients(loss)
方法求出 loss
对所有变量(参数)的梯度。最后通过 apply_gradients(grad)
方法,根据前面算出的梯度来梯度下降更新变量(参数)。
以上三行代码等价于下面一行代码:
- train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_).minimize(loss)
简化后的代码如下:
- import tensorflow as tf
- learning_rate_ = tf.placeholder(dtype=tf.float32)
- X_ = tf.placeholder(dtype=tf.float32, shape=[5])
- y_ = tf.placeholder(dtype=tf.float32, shape=[5])
- a = tf.get_variable('a', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
- b = tf.get_variable('b', dtype=tf.float32, shape=[], initializer=tf.zeros_initializer)
- y_pred = a * X_ + b
- loss = tf.constant(0.5) * tf.reduce_sum(tf.square(y_pred - y_))
- # 反向传播,利用TensorFlow的梯度下降优化器自动计算并更新变量(模型参数)的梯度
- train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate_).minimize(loss)
- num_epoch = 10000
- learning_rate = 1e-3
- with tf.Session() as sess:
- tf.global_variables_initializer().run()
- for e in range(num_epoch):
- sess.run(train_op, feed_dict={X_: X, y_: y, learning_rate_: learning_rate})
- print(sess.run([a, b]))