
2.1 理解TensorFlow 1.x
通常来说,学习使用任何计算机语言时,编写的第一个程序都是“Hello World”。在本书中我们也保持惯例!从Hello World程序开始:

让我们细看下这段简单的代码。第一行导入了tensorflow
,第二行使用tf.con-stant
定义了变量message
,第三行使用with
定义了Session()
,第四行使用run()
运行上述会话(session)。注意,这时的运行结果是“字节字符串”(byte string)。为了移除字符串引号和b(对于字节),使用了decode()
方法。
2.1.1 TensorFlow 1.x计算图程序结构
TensorFlow 1.x不同于其他编程语言,需要先为要创建的神经网络构建一个蓝图,具体实现是通过将程序分为两个部分:定义计算图和执行计算图。
计算图
计算图是由节点和边构成的神经网络。在本节中,要使用的数据称为张量对象(常量、变量、占位符)。要执行的计算称为操作对象。每个节点可以有零个或多个输入,但只有一个输出。网络中的节点表示对象(张量和操作),边表示不同操作之间传递的张量。计算图定义了神经网络的蓝图,但其中的张量尚未与“值”关联。
占位符只是一个变量,随后会为其分配数据。它让我们不需要数据即可构建计算图。
要构建一个计算图,需要定义所有用到的常量、变量和操作。在后续几节中,我们使用一个简单示例来描述计算图的程序结构,定义并执行一个图来添加两个向量。
计算图执行
计算图的执行由会话对象实现,它封装了张量对象和操作对象求值运算的环境。实际的计算过程和信息的层际传递都在此部分发生。在此之前,所有张量对象的值仅仅是被初始化、访问以及保存在会话对象中,这只是一些抽象定义。直至执行时,它们才开始有了“生命”。
为什么要使用计算图
使用计算图有很多原因。首先,计算图是描述(深度)神经网络最自然的隐喻。其次,可通过移除通用子表达式、融合内核以及剪除多余表达式等方法对计算图进行自动优化。再次,在训练过程中可轻松分发计算图,并将其部署到不同的运行环境(比如CPU、GPU或TPU,以及云、物联网、移动或传统服务器)中。总之,如果你熟悉函数式编程,那么计算图就是一个常用概念,可把它看作简单基本类型的组合(这在函数式编程中很常见)。TensorFlow从计算图中借鉴了很多概念,并做了一些内部优化。
从一个示例开始
对于一个将两个向量相加的简单示例,其计算图如下所示。

定义计算图的相应代码为:

在会话中执行图:

或者:

结束后会打印两个向量的和:

记住,每个会话都需要使用close()
显式关闭。
计算图的构建非常简便,只需添加变量和运算并将它们传递(使张量流动)。如此你就可以逐层构建神经网络。另外,TensorFlow还允许使用tf.device()
将特定设备(CPU/GPU)与不同的计算图对象一起使用。在示例中,计算图由三个节点组成。其中,v_1
和v_2
表示两个向量,v_add
表示要在v_1
和v_2
上执行的操作。现在,为了使该图生效,首先需要使用tf.Session()
定义一个会话对象。我们将会话对象命名为sess
。接下来,使用Session
类中定义的run
方法运行它:

该方法将评估fetches
参数的张量。示例中fetches
参数的张量为v_add
。run
方法将执行图中导入v_add
的每个张量和每个操作。假如fetches
是v_1
而不是v_add
,则结果将是向量v_1
的值:

fetches
可以是单个(或多个)张量对象或者操作对象。例如,如果fetches
为[v_1,v_2,v_add]
,则输出为:

同一程序代码中可以有许多会话对象。在本节中,我们看到了TensorFlow 1.x的计算图程序结构的示例。下面将更详细地介绍TensorFlow 1.x的编程结构。
2.1.2 常量、变量和占位符的使用
简而言之,TensorFlow提供了一个库用来定义和执行带有张量的不同数学运算。张量一般是n维数组。所有类型的数据(即标量、向量和矩阵)都是张量的特殊类型:

TensorFlow支持三种类型的张量:
1. 常量:常量是值不可变的张量。
2. 变量:当在会话中需要更新值时,应使用变量张量。例如,神经网络在训练期间需要更新权重,这通过将权重声明为变量来实现。变量在使用前需要进行显式初始化。另外要注意,常量存储在计算图定义中,而且每次加载图时都会加载它们,所以会占用大量内存。与之不同,变量是独立存储的,可以存储在参数服务器上。
3. 占位符:占位符用于将值注入TensorFlow的计算图中,通常与参数feed_dict
一起来注入数据。在训练神经网络时,通常用来提供新的训练示例。在会话中运行计算图时,我们将值分配给占位符。它们使我们无须任何数据即可创建操作对象并构建计算图。需要注意的重要细节是,占位符不含任何数据,因此无须初始化。
2.1.3 操作对象示例
让我们看看TensorFlow 1.x中一些不同操作对象的示例。
1. 常量
下面是一些常见的常量。
- 声明一个标量常量:

示例:形状为[1, 3]的常数向量:

- 使用
tf.zeros()
创建一个所有元素都为零的张量。以下语句创建数据类型为dtype
(int32、float32
等),形状为[M,N]
的零矩阵:

示例:zero_t = tf.zeros([2,3],tf.int32) ==>[[0 0 0], [0 0 0]]
- 获取张量的形状:
示例:print(tf.zeros([2,3],tf.int32).shape) ==> (2, 3)
- 还可用以下代码创建与现有NumPy数组形状相同的张量变量或张量常量:

- 创建一个所有元素为1的张量。接下来,创建一个形状为
[M,N]
的ones
矩阵:

示例:ones_t = tf.ones([2,3],tf.int32) ==>[[0 0 0], [0 0 0]]
- 可以用与NumPy相似的方式进行广播:
示例:t = tf.Variable([[0., 1., 2.], [3., 4., 5.], [6., 7., 8]])

2. 序列
- 生成在起止区间内均匀间隔,总长度为num的等距向量序列:

示例:range_t = tf.linspace(2.0,5.0,5) ==> [ 2. 2.75 3.5 4.25 5. ]
- 生成从
start
(默认为0)开始、增量为delta
(默认为1)且不包括limit
的数字序列:

示例:range_t = tf.range(10) ==> [0 1 2 3 4 5 6 7 8 9]
3. 随机张量
TensorFlow允许创建具有不同分布特征的随机张量:
- 为创建服从形状[M,N]、均值
mean
(默认为0.0)、标准差stddev
(默认为1.0)和seed
的正态分布的随机值,可使用:

- 为创建服从形状[M,N]、均值
mean
(默认为0.0)、标准差stddev
(默认为1.0)和seed
的截断正态分布的随机值,可使用:

- 为创建服从形状[M,N]、区间[
minval
(默认为0),maxval
]和seed
的gamma分布的随机值,可使用:

- 将给定张量随机裁剪为指定大小:

- 每当需要以随机顺序呈现训练样本时,都可使用
tf.random_shuffle()
方法沿张量的第一维度随机洗牌张量。假设t_random
是待洗牌的张量,可使用:

- 随机生成的张量会受到初始种子值的影响。为了在多个运行或会话中获得相同的随机数,应将种子设置为一个常量值。在大量使用随机张量时,可用
tf.set_random_seed()
为所有随机生成的张量设置种子。以下命令将所有会话的随机张量的种子设置为54:

4. 变量
使用类tf.Variable
创建变量。定义变量应包括初始化常数或随机值。在下面的代码中,我们创建了两个不同的张量变量t_a
和t_b
。用形状[50, 50]、minval=0
和maxval=10
的随机均匀分布对它们进行初始化:

变量常用于表示神经网络的权重和偏差:

此处,我们用可选参数name
为计算图中定义的变量命名。在上述所有示例中,变量初始化源自一个常量。我们还可以指定另一个变量来初始化变量。以下语句用上面定义的权重初始化weight2
:

初始化变量:变量的定义需要指定如何初始化,因为必须显式初始化所有声明的变量。在计算图的定义中,通过声明一个操作对象来实现这一点:

运行计算图时,还可以使用tf.Variable.initializer
分别初始化每个变量:

保存变量:可以使用Saver
类保存所有变量。为此,定义一个操作对象saver
:

占位符:采用如下语法定义占位符:

dtype
用来指定placeholder
的数据类型,且须声明时指定。下面,我们将x定义为占位符,并使用feed_dict
计算随机4×5矩阵的y = 2x(记住,feed_dict
用于将值注入TensorFlow占位符中):

所有变量和占位符都在代码的“计算图”部分中确定。如果在定义部分中使用print
语句,则将仅获得有关张量类型的信息,而不是张量的值。
要获知其值,我们需要创建会话图,并使用所需的张量值显式运行run
命令,代码如下所示:

2.1.4 TensorFlow 2.x中的TensorFlow 1.x示例
我们可以看到TensorFlow 1.x API为创建和操控表示(深度)神经网络和其他类型机器学习程序的计算图提供了灵活的方式。相对而言,TensorFlow 2.x提供了更高级别的API,这些API抽象隐藏了更多的底层实现细节。最后,让我们回到上一章中遇到的TensorFlow 1.x程序示例。此处,我们还添加了一行代码来显示计算图:

注意,语句x*W+b
正是上一章中定义的线性感知器。现在,我们启动一个名为“Tensor-Board”的可视化应用程序以显示计算图:

然后打开浏览器访问http://localhost:6006/#graphs&run=
。
你应该看到如图2-1所示的内容。

图2-1 计算图的示例
本节概述了TensorFlow 1.x编程范式。现在,让我们将注意力转向TensorFlow 2.x。