深入理解TensorFlow之基础概念:数据流图、张量、操作、会话、优化器

博客已迁至知乎,本文链接:https://zhuanlan.zhihu.com/p/70877190

前言

本文梳理了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.成员方法
张量公共成员方法:

方法名称 功能说明
eval 取出张量值
get_shape 取出张量形状
srt_shape 修改张量形状
consumers 获取直接依赖此张量的所有操作
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'这个操作依赖tensor'a'

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

操作类型 典型操作
一元代数操作 abs、neg和invert
二元代数操作 add、multiply和sub(也可以用+ - * /)
形状操作 chip、reshape、slice和shuffle
规约操作 reduce_mean和reduce_sum
神经网络操作 conv、pool、softmax和relu
条件操作 cond

三. 模型载体:操作

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

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

计算节点:Operation

计算操作的基本属性:

属性名称 功能说明
name 名称
type 操作的类型名称,比如加法是add
inputs 输入张量的列表
contril_inputs 输入控制依赖向量
outputs 输出的张量列表
device 操作执行用的设备名称
graph 操作所属的数据流图
traceback 操作实例化时的调用栈

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

操作类型 典型操作
基础算术 add、multiply、mod、sqrt、sin、trace、fft、argmin
数组运算 size、rank、split、reverse、cast、one_hot、quantize
梯度裁剪 clip_by_value、clip_by_norm、clip_by_global_norm
逻辑控制和调试 identity、logical_and、equal、less、is_finite、is_nan
数据流控制 enqeue、dequeue、size、take_grad、apply_grad
初始化操作 zeros_initializer、random_normal_initializer、orthogonal_initializer
神经网络运算 convolution、pool、bias_add、softmax、dropout、erosion2d
随机运算 random_normal、random_shuffle、multinomial、random_gamma
字符串运算 string_to_hash_bucket、reduce_join、substr、encode_base64
图像运算处理 encode_png、resize_images、rot90、hsv_to_rgb、adjust_gamma

存储节点:Variable

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

属性名称 功能说明
name 名称
dtype 数据类型
shape 变量形状
initial_value 变量的初始值
initizlizer 计算前为变量赋值的初始化操作
device 操作执行用的设备名称
graph 操作所属的数据流图
op 变量操作

构建变量时必须给定一个值。
变量值要初始化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中来。

属性名称 功能说明
name 名称
dtype 数据类型
shape 变量形状
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 会话启动时的配置项

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

参数 描述
allow_soft_placement=True 允许tensorflow自动分配设备
log_device_placement=True 记录每个节点分配到哪个设备上日志
gpu_options.allow_growth=True 用于动态申请显存,从少到多慢慢增加gpu容量
gpu_options.per_process_gpu_memory_fraction 用于限制gpu使用率,=0.3表示拿出30%给进程使用

用法:

config = tf.ConfigProto(allow_soft_placement=True, 
                        log_device_placement=True)
config.gpu_options.allow_growth=True
config.gpu_options.per_process_gpu_memory_fraction = 0.3

with tf.Session(config = config,...)

五. 训练工具:优化器

优化器是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 损失值 Tensor
global_step 全局训练步数,自增 Variable
name 优化器的名称 String

训练模型实例:

# 定义模型
...
# 定义损失函数
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框架设计与实现原理 第三章

你可能感兴趣的:(深度学习)