TensorFlow学习之路(一)

从一些基本概念开始

Posted by Wenqian on May 2, 2021

写在前面

本文的目的在于梳理个人学习过程中遇到的一些问题以及收获,并不是一个完全适合新手的系统性教程。网上相关的教学课程、视频以及教程很多,有兴趣的同学可以自行搜寻。本系列中采用的tf版本为1.15,python版本为3.6,如果不做特殊说明,TensorFlow都代指tf1。不使用tf2的原因是公司目前的代码还没迁到tf2上,因此先从tf1学起。

TensorFlow中的一些基本概念

TensorFlow虽然是个很复杂的系统工程,但是我们可以把其框架的构成抽象成几个基本元素:

  1. 计算图(Graph)
  2. 会话(Session)
  3. 张量(Tensor)
  4. 操作(Operation)

计算图

TensorFlow采用的是一种静态图框架,即用户需要先构建一个计算图,然后再运行它以获取结果。相比于Pytorch这类的动态图框架,静态图框架的最大优势是可以对同一个计算图进行多次运行,并且由于计算图的构建先于执行,因此编译器可以对其进行更高程度地优化。其最大的劣势则在于比较难调试,并且对新手不友好。

计算图由多个相同或不同的操作和张量构成(也就是上面的第三个和第四个基本元素)。其中操作(Operation)表示计算的单元,而张量(Tensor)则表示操作间流动数据的单元。下图就是TensorBoard中一个表示两层神经网络的Tensorflow计算图的可视化结果:

img

另外,上面提到的编译器优化操作主要包括:

  1. 通过折叠(fold)计算图中的常量节点来静态推断张量的取值。
  2. 把计算图中不相关的子部分分隔开,从而可以分配到不同线程或设备上进行并行计算。
  3. 通过消除共用的子表达式来简化数学计算。

另外需要注意的是,TensorFlow会提供默认图 (default graph), 这在很多情况下就已经够用了。我们训练并保存后的模型也是以图的形式存在的

会话

会话可以理解为TensorFlow里面的计算环境,在此环境下可以执行对应计算图内的操作以及计算计算图内的张量结果。

会话在执行完毕后需要被关闭(显式调用close函数,或者采用with代码块)。交互式会话(Interactive Session)这里就先不介绍了。

张量

张量可以理解为是一种任意维度的数据类型,其维度从0开始覆盖到任意一个正整数。0维张量等同于标量,一维张量等同于向量,二维张量等同于矩阵。

操作

操作是张量间的一种映射关系。其输入是0或多个张量,输出是另外0或多个张量。例如加减乘除就是典型的操作。

由此我们总结一下:多个张量之间的映射构成操作,多个操作和张量构成计算图。对于某个计算图,我们可以生成一个会话来运行图内的操作和张量计算,从而得到想要的结果。

下面我们通过一个例子来看一下如何运用这些基本单元得到我们想要的结果。

常量与变量

这里采用极客学院教程[2]中的一个例子来说明:

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
# 创建一个变量, 初始化为标量 0.
state = tf.Variable(0, name="counter")

# 创建一个 op, 其作用是使 state 增加 1
one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# 启动图后, 变量必须先经过`初始化` (init) op 初始化,
# 首先必须增加一个`初始化` op 到图中.
init_op = tf.global_variables_initializer()

# 启动图, 运行 op
with tf.Session() as sess:
  # 运行 'init' op
  sess.run(init_op)
  # 打印 'state' 的初始值
  print sess.run(state)
  # 运行 op, 更新 'state', 并打印 'state'
  for _ in range(3):
    sess.run(update)
    print sess.run(state)

# 输出:

# 0
# 1
# 2
# 3

在上面的例子中,我们定义了一个常量one,以及一个变量state。变量需要通过tf.Variable来生成,其第一个参数是初始值,而name的作用则是为了给这个变量起一个名字(打印出来的时候看着更清楚一点,否则都叫Variable)。global_variables_initializer的作用是初始化所有全局变量,只有在进行此操作后我们才能运用变量。常量则是通过tf.constant来生成。

TensorBoard的简单使用

前面提到TensorBoard可以帮助我们可视化计算图,例如上面这段代码对应的TensorBoard中生成的图像为:

img

可以看到,进行操作除了对counter初始化以外,就是循环地加上一个常数,并且assign回counter变量。

除了可视化计算图,TensorBoard也可以帮助我们可视化一些变量的变化过程,这里要用到tf.summary。一般来说,用的比较多的是tf.summary.scalar以及tf.summary.histogram,前者可以获取某个标量的变化信息(例如训练过程中loss的变化或者学习率的变化等),而后者则提供某一维度下的直方图信息,可以用作特定层梯度的可视化或者权重分布的可视化等。

Summary(摘要)需要在会话期间生成。我们可以通过定义tf.merge_all_summaries操作来一次性运行得到所有摘要。之后需要使用tf.summary.FileWriter来讲摘要写入事件文件。完整的代码如下:

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
# 创建一个变量, 初始化为标量 0.
state = tf.Variable(0, name="counter")

# 创建一个 op, 其作用是使 state 增加 1
one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# 启动图后, 变量必须先经过`初始化` (init) op 初始化,
# 首先必须增加一个`初始化` op 到图中.
init_op = tf.initialize_all_variables()

tf.summary.scalar('state', state)
merged_summaries = tf.summary.merge_all()

# 启动图, 运行 op
with tf.Session() as sess:
    writer = tf.summary.FileWriter('summary_dir', sess.graph)
    # 运行 'init' op
    sess.run(init_op)
    # 运行 op, 更新 'state', 并打印 'state'
    for i in range(3):
        sess.run(update)
        summary = sess.run(merged_summaries)
        writer.add_summary(summary=summary, global_step=i+1)

TensorBoard中打印的结果为:

img

下篇文章我将简单介绍一下placeholdervariable_scope的作用,并且尝试用TensorFlow训练一个简单地MLP网络。

Reference

[1] TensorFlow官方文档

[2] https://wiki.jikexueyuan.com/project/tensorflow-zh/get_started/basic_usage.html

[3] https://github.com/apachecn/apachecn-dl-zh/blob/master/docs/tf-1x-dl-cookbook/01.md