TensorFlow 官方入门教程

本文翻译自 TensorFlow - Get Started 官方入门教程。

张量

TensorFlow 的核心单元是 张量(tensor)。张量是一个多维数组,其维度称为 (rank)。以下是张量的一些例子:

1
2
3
4
3 # a rank 0 tensor; this is a scalar with shape []
[1. ,2., 3.] # a rank 1 tensor; this is a vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]

TensorFlow Core 教程

导入 TensorFlow

TensorFlow 的规范 import 语句如下:

1
import tensorflow as tf

这使的 Python 访问所有 TensorFlow 的类,方法和符号。

计算图

TensorFlow 核心方案为由两个独立的部分组成:

  1. 构建计算图。
  2. 运行计算图。

计算图是一系列对节点图的 TensorFlow 操作。下面建立一个简单的计算图表,每个节点输入零个或多个张量,并输出一个张量。节点的一种类型是常量 constant,不需要输入,输出为内部存储的值。创建两个浮点张量 node1node2 如下:

1
2
3
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)

最后的 print 打印如下:

1
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)

请注意,节点的打印语句并不会输出节点的值 3.04.0 。要计算并输出节点的值,必须运行计算图的会话(Session)。会话封装了 TensorFlow 运行时的控制和状态。

以下代码创建一个 Session 对象,然后调用其 run 方法来通过计算图来得到 node1node2 的值。在会话中运行计算图,结果如下:

1
2
sess = tf.Session()
print(sess.run([node1, node2]))

输出结果为期望输出 3.04.0

1
[3.0, 4.0]

我们可以通过组合张量的节点与操作(操作也属于节点)来实现更复杂的计算。例如,我们可以添加我们的两个常量节点并生成一个新的图,如下所示:

1
2
3
node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))

最后两句 print 打印:

1
2
node3:  Tensor("Add_2:0", shape=(), dtype=float32)
sess.run(node3): 7.0

TensorFlow 提供了一个名为 TensorBoard 的实用工具,可以显示计算图的图像。下面是 TensorBoard 可视化界面的截图:

上面的图只能输出常量,如需接收变量并输出,可以使用占位符 placeholder

1
2
3
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # + provides a shortcut for tf.add(a, b)

上面三个语句有点类似于 Python 中的 lambda 函数,对输入的变量(a 和 b)进行操作。通过向占位符传值来进行图的计算:

1
2
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))

上述输出为:

1
2
7.5
[ 3. 7.]

在 TensorBoard 内,计算图如下:

添加以下操作来得到更加复杂的计算图:

1
2
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))

上述输出为:

1
22.5

在 TensorBoard 内,计算图如下所示:

在机器学习中,我们通常会想要一个可以接受任意输入的模型。为了使模型可训练,图形需要具有可修改性,使得在相同的输入下有新的输出。变量 Variable 允许我们向图中添加可训练的参数,具有类型和初始值:

1
2
3
4
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b

调用 tf.constant 时,常量被初始化,其中的值永远不会改变。相对地,调用 tf.Variable 时变量不会被初始化。要初始化 TensorFlow 程序中的所有变量,必须显式调用特殊的初始化方法,如下所示:

1
2
init = tf.global_variables_initializer()
sess.run(init)

init 是 TensorFlow 子图初始化所有的全局变量的句柄。这些变量在调用 sess.run 之后才会初始化。

由于 x 是占位符,我们可以同时对 linear_model 设置多个 x 的值,如下所示:

1
print(sess.run(linear_model, {x:[1,2,3,4]}))

输出为

1
[ 0.          0.30000001  0.60000002  0.90000004]

上面创建了一个模型,为了评估模型的表现,通过占位符 y 来提供所需的值,并且需要编写一个损失函数。

损失函数可以计算出当前模型与实际数据之间的偏差。我们将使用线性回归的标准损失模型,将当前模型与实际数据的差的平方相加。 创建一个向量 linear_model - y,其中每个元素都是对应的偏差量。我们先调用 tf.square 来计算偏差量的平方,然后通过 tf.reduce_sum 求和计算总的偏差:

1
2
3
4
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))

得到损失值为:

1
23.66

我们可以通过手动将 Wb 的值为设置为 -11,对模型进行改进。通过 tf.Variable 对变量进行初始化后,我们可以使用 tf.assign 等操作进行赋值。例如,W = -1b = 1 是我们的模型的最优参数。我们可以按照如下语句设置 Wb

1
2
3
4
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))

得到最优化输出:

1
0.0

我们可以通过手动设置猜测 Wb 的“完美”值,但机器学习的重点是可以自动设置正确的模型参数。下一节将展示如何完成此项工作。

tf.train API

本教程不对机器学习的过程进行介绍,建议先修相关知识。

TensorFlow 提供了优化器(optimizer),可以缓慢地更改每个变量,从而对损失函数进行最小化。最简单的优化器是梯度下降(gradient descent)。梯度下降根据变量的损失函数的导数,对每个变量进行修改。在 TensorFlow 中,优化器默认执行求导和反向传播等操作,如下所示:

1
2
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
1
2
3
4
5
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})

print(sess.run([W, b]))

得到最终的模型参数:

1
[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]

这里通过基本的 TensorFlow 核心代码完成了最简单的机器学习。如需将复杂的模型和方法将数据输入到自己的计算模型中,则需要更多的代码。因此,TensorFlow 对常见的模式、结构和函数提供了更高级别的抽象。我们将在下一节中学习如何使用其中的一些抽象。

完整程序

完整的可训练线性回归模型如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import numpy as np
import tensorflow as tf

# Model parameters
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})

# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))

运行后输出

1
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11

这个更复杂的程序仍然可以在 TensorBoard 中可视化:

tf.estimator

tf.estimator 是一个高级 TensorFlow 库,简化了机器学习的机制,包括:

  • 运行训练循环
  • 运行评估循环
  • 管理数据集

tf.estimator 定义了许多常见的模型。

基本用法

tf.estimator 可以高度简化线性回归程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# NumPy is often used to load, manipulate and preprocess data.
import numpy as np
import tensorflow as tf

# Declare list of features. We only have one numeric feature. There are many
# other types of columns that are more complicated and useful.
feature_columns = [tf.feature_column.numeric_column("x", shape=[1])]

# An estimator is the front end to invoke training (fitting) and evaluation
# (inference). There are many predefined types like linear regression,
# linear classification, and many neural network classifiers and regressors.
# The following code provides an estimator that does linear regression.
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)

# TensorFlow provides many helper methods to read and set up data sets.
# Here we use two data sets: one for training and one for evaluation
# We have to tell the function how many batches
# of data (num_epochs) we want and how big each batch should be.
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7, 0.])
input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)

# We can invoke 1000 training steps by invoking the method and passing the
# training data set.
estimator.train(input_fn=input_fn, steps=1000)

# Here we evaluate how well our model did.
train_metrics = estimator.evaluate(input_fn=train_input_fn)
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)

运行后输出:

1
2
train metrics: {'average_loss': 1.4833182e-08, 'global_step': 1000, 'loss': 5.9332727e-08}
eval metrics: {'average_loss': 0.0025353201, 'global_step': 1000, 'loss': 0.01014128}

虽然评估数据集具有相对更高的损失,但这个损失仍然接近于零,意味着学习过程有效。

自定义模型

tf.estimator 不要求使用其预定义的模型。我们可以创建自定义模型,同时仍然可以保持 tf.estimator 的数据集合,反馈,训练等的高级抽象。下面将展示如何通过低级 TensorFlow API 来实现​​具有相同功能的 LinearRegressor 模型。

要定义一个能够通过 tf.estimator 工作的自定义模型,需要使用 tf.estimator.Estimator。上节中的 tf.estimator.LinearRegressor 实际上是 tf.estimator.Estimator 的一个子类。为了实现相同功能,我们只需要提供一个函数 model_fn 来告诉 tf.estimator 如何计算预测结果,训练步骤和损失。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import numpy as np
import tensorflow as tf

# Declare list of features, we only have one real-valued feature
def model_fn(features, labels, mode):
# Build a linear model and predict values
W = tf.get_variable("W", [1], dtype=tf.float64)
b = tf.get_variable("b", [1], dtype=tf.float64)
y = W*features['x'] + b
# Loss sub-graph
loss = tf.reduce_sum(tf.square(y - labels))
# Training sub-graph
global_step = tf.train.get_global_step()
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = tf.group(optimizer.minimize(loss),
tf.assign_add(global_step, 1))
# EstimatorSpec connects subgraphs we built to the
# appropriate functionality.
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=y,
loss=loss,
train_op=train)

estimator = tf.estimator.Estimator(model_fn=model_fn)
# define our data sets
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7., 0.])
input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
{"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)

# train
estimator.train(input_fn=input_fn, steps=1000)
# Here we evaluate how well our model did.
train_metrics = estimator.evaluate(input_fn=train_input_fn)
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)

运行时输出:

1
2
train metrics: {'loss': 1.227995e-11, 'global_step': 1000}
eval metrics: {'loss': 0.01010036, 'global_step': 1000}

可以看到,自定义函数 model_fn() 与之前的 API 章节的手动循环训练过程非常类似。