前言

本文梳理了TensorFlow的基本概念:数据流图、Tensor、Operation、Variable、Placeholder、Session、Optimizer、minimize。并对每个概念进行了详细的讲解和扩展。

阅读本文需要对深度学习有一定了解,并知道tensorflow是做什么的。

一. 编程范式:数据流图

声明式编程和命令式编程

声明式编程:做什么

命令式编程:怎么做

TensorFlow采用了声明式编程范式。

声明式编程的优点:

1.代码可读性强:以目标而非过程为导向。2.支持引用透明:函数的调用语句可以由它的返回值取代。3.提供预编译优化能力:先构建出数据流图,无依赖逻辑的并行化计算、无效逻辑去除、公共逻辑提取等。

tensorflow数据流图

数据流图定义:用节点和有向边描述数学运算的有向无环图。

数据流图中的节点代表各类操作,包括数学运算、数据填充、结果输出和变数读写等等,每个节点的操作都需要分配到具体的物理设备(cpu、gpu)上执行。有向边描述了节点间的输入输出关系。

1.节点

前向图中的节点有:

  • 数学函数或表达式
  • 存储模型参数的变数(variable)
  • 占位符(placeholder)

后向图中的节点有:

  • 梯度值
  • 更新模型参数的操作
  • 更新后的模型参数

2.有向边

多数用来传输数据,少数用来控制依赖。

3.执行原理

数据流图执行顺序的实现参考了拓扑排序的设计思想。

当我们使用tensorflow执行指定数据流图时,其执行过程可分为以下4个步骤
  • a.以节点名称作为关键字,其所依赖的个数为值,创建一个散列表,将所有节点放入这个散列表。
  • b.为此数据流图创建一个可执行队列,将三列表中依赖个数为0的节点加入队列,并在散列表中删除它们。
  • c.一次执行队列中的每个一节点,执行成功后将此节点输出项的节点的依赖数减1,更新散列表。
  • d.重复b和c,直到执行队列为空。

二. 数据载体:张量

在tensorflow中,张量(Tensor)是数据流图上的数据载体,通常是多维数组。还有一种叫SpareseTensor,存放稀疏数据(0的数量远多于非0元素),旨在减少稀疏数据的内存占用。

在物理实现时,它含有指向张量数据的内存缓冲区的指针,当它不被任何操作依赖时,会释放该内存缓冲区。

1.创建

一般tensor都不是直接创建的,而是定义常量和代数计算操作而间接创建的。

import tensorflow as tf
a = tf.constant(1, name="a", dtype=tf.int32)
b = tf.constant(2, name="b", dtype=tf.int32)
c = tf.add(a, b, name="c")
print(a)
print(b)
print(c)
Tensor("a:0", shape=(), dtype=int32)
Tensor("b:0", shape=(), dtype=int32)
Tensor("c:0", shape=(), dtype=int32)

2.求解

如果想要求解某个张量的值,则需要创建会话,然后执行张量的eval方法或会话的run方法。

with tf.Session() as sess:
print(c.eval())
print(sess.run(c))
3
3

3.成员方法

张量公共成员方法:

import tensorflow as tf
a = tf.constant(1, name="a", dtype=tf.int32)
b = tf.constant(2, name="b", dtype=tf.int32)
c = tf.add(a, b, name="c")

print(a.consumers())
[<tf.Operation c type=Add>] # 可以看出c这个操作依赖tensora

4.操作

tensorflow提供了大量操作,以便构建数据流图,实现演算法模型。

三. 模型载体:操作

数据流图由节点和邮箱边组成,每个节点代表一种操作,因此操作是模型功能的实际载体。数据流图中的节点按功能不同可以分为以下三种:

  1. 计算节点:计算或控制操作。
  2. 存储节点:变数,一般用来存储模型参数。
  3. 数据节点:占位符操作,描述待输入数据的属性。

计算节点:Operation

计算操作的基本属性:

并不需要直接定义计算节点,只要你用了类似tf.add这种操作,它就自动构造了Operation实例。下面整理了TensorFlow Python API提供的典型操作:

存储节点:Variable

作用:多次执行数据流图时存储特定的参数。

构建变数时必须给定一个值。

变数值要初始化tf.global_variables_initializer()之后才能看到。

# 清空图
import tensorflow as tf
aa = tf.Variable([1,2,3], name="aa")
print(aa.dtype) # 输出其数据类型
print(aa.initial_value) # 输出初始化值
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(aa)) # 输出初始化之后的变数值
<dtype: int32_ref>
Tensor("aa/initial_value:0", shape=(3,), dtype=int32)
[1 2 3]

数据节点:Placeholder

数据流图在填充数据之前不会执行任何计算,通常,我们在定义模型之前就已经明确了输入数据的类型和形状等属性,而模型的第一步计算很可能就要用到这些输入数据。数据节点Placeholder的作用就是定义好待输入数据的类型和形状,先完成模型的构建,最后数据流图要执行的时候再把数据填充(feed)到Placehoder中来。

import tensorflow as tf
a = tf.constant([[1,2],[3,4]], dtype=tf.int32, name="a")
b = tf.placeholder(shape=[2,2], dtype=tf.int32, name="b")
c = a + b
with tf.Session() as sess:
print(sess.run(c, feed_dict={b:[[1,1],[1,1]]}))
[[2 3]
[4 5]]

四. 运行环境:会话

tensorflow数据流图描述了要计算的拓扑结构和所需的数据属性,但它仅仅是个壳。还需要向图中填充数据、选择待求解的张量、执行各种操作才能得到结果。而tensorflow的会话提供了这个计算过程的运行环境,它的本质是维护一段运行时间的上下文。

会话通过提取和切分数据流图、调度并执行操作节点,来将数据流图在计算机上执行。

流程:创建会话、运行会话、关闭会话。

sess = tf.Session() # 创建会话
sess.run(...) # 运行会话
sess.close() # 关闭会话

# 或者:
with tf.Session() as sess:# 自动关闭
sess.run(...)

target在执行单机任务时不用管它。

graph指向默认图,当有多个数据流图时需要显式指定。config描述会话的配置信息。配置的内容可以包括并行线程数、GPU分配策略、运算超时时间等参数,最常用的几个参数:

用法:

# minimize的参数:
minimize(self, loss, global_step=None, var_list=None,
gate_gradients=GATE_OP, aggregation_method=None,
colocate_gradients_with_ops=False, name=None,
grad_loss=None)

五. 训练工具:优化器

优化器是tensorflow实现优化演算法的载体,它实现了自动求梯度并更新参数的功能。优化器根据模型结构和损失函数,利用链式求导法则求出每个参数的梯度并更新它们,以完成一次完整的训练。

tensorflow提供了很多种Optimizer,定义在tf.train中。

使用minimize方法训练模型

# minimize的参数:
minimize(self, loss, global_step=None, var_list=None,
gate_gradients=GATE_OP, aggregation_method=None,
colocate_gradients_with_ops=False, name=None,
grad_loss=None)

实现原理参见 源码:tensorflow/python/training/optimizer.py

其中常用的参数:

训练模型实例:

# 定义模型
...
# 定义损失函数
loss = ...
# 优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01) # 选择优化方法,给定学习率
golbal_step = tf.Variable(0, name=global_step, trainable=False)
train = optimizer.minimize(loss, global_step=global_step)

with tf.Session() as sess:
sess.run(global_variables_initializer()) # 初始化参数
for step in 10000:
sess.run(train, feed_dict={...}) # 一步训练

References:

[1] 深入理解TensorFlow框架设计与实现原理 第三章


推荐阅读:
相关文章