文章目录
一、Tensorflow
介绍
TensorFlow
是一个采用 数据流图(Data flow graphs),用于 数值计算 的开源软件库。TensorFlow
最初由Google大脑小组的研究员和工程师们开发出来,用于 机器学习和深度神经网络 方面的研究,但这个系统的通用性使其也可广泛用于其他计算领域。它是谷歌基于 DistBelief
进行研发的第二代人工智能学习系统。2015年11月9日,Google发布人工智能系统TensorFlow并宣布开源。
其命名来源于本身的原理,Tensor
(张量)意味着
N
N
N 维数组,Flow
(流)意味着基于 数据流图 的计算。Tensorflow
运行过程就是 张量从图的一端流动到另一端的计算过程。张量从图中流过的直观图像是其取名为 TensorFlow
的原因。
TensorFlow
的关键点是:Data Flow Graphs
,表示 TensorFlow
是一种基于图的计算框架,其中节点(Nodes
)在图中表示 数学操作 ,线(Edges
)则表示 在节点间相互联系的多维数据数组,即张量(Tensor
),这种基于流的架构让 TensorFlow
具有非常高的灵活性,该灵活性也让TensorFlow
框架可以在多个平台上进行计算,例如:台式计算机、服务器、移动设备等。
备注:TensorFlow的开发过程中,重点在于 构建执行流图 。
1、数据流图
数据流图使用节点(Node
)和线(Edges
)的 有向图 描述数学计算:
- 节点:一般用来表示施加的数学操作 ,也可以表示数据输入(
feed in
)的起点和输出(push out
)的终点,或者是读取/写入持久变量(persistent variable
)的终点。 - 线:表示的是节点之间的输入/输出关系,这些线可以输运 “size可动态调整” 的多维数组,即张量(
Tensor
)。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算。
图中包含:输入(input)、塑形(reshape)、ReLu层(ReLu Layer)、Logit层(Logit Layer)、Softmax、交叉熵(cross entropy)、梯度(gradient)、SGD训练(SGD Trainer)等部分,是一个简单的回归模型
- 计算过程:
- 首先从输入开始,经过塑形后,一层一层进行前向传播运算。
- ReLu层(隐藏层)里面有两个参数,即 W h 1 W_{h_1} Wh1 和 b h 1 b_{h1} bh1 ,在输出前使用ReLu激活函数做非线性处理;
- 然后进入Logit层(输出层),学习两个参数 W s m W_{sm} Wsm 和 b s m b_{sm} bsm,用Softmax来计算输出结果中的各个类别的概率分布,用交叉熵来度量两个概率分布之间的相似性
- 然后开始计算梯度,这里需要更新参数 W h 1 W_{h_1} Wh1 , b h 1 b_{h1} bh1, W s m W_{sm} Wsm 和 b s m b_{sm} bsm,以及交叉熵后的结果
- 随后进入SGD训练,也就是反向传播的过程,从上往下计算每一层的参数,依次进行更新
2、Tensorflow
特性
- 高度的灵活性:只要能够将计算表示成为一个数据流图,那么就可以使用
TensorFlow
。 - 可移植性:
TensorFlow
支持CPU
和GPU
的运算,并且可以运行在台式机、服务器、手机移动端设备等等。 - 自动求微分:
TensorFlow
内部实现了自动对于各种给定目标函数求导的方式。 - 多种语言支持:
Python
、C++
- 性能高度优化
3、TensorFlow
安装
- 要求:
Python
必须是64位
; - 根据
TensorFlow
的计算方式,TensorFlow
的安装分为CPU版本
和GPU版本
; - 对于
Python3.5
或者Python3.6
,可以使用pip install tensorflow
(安装CPU版本)和pip install tensorflow-gpu
(安装GPU版本)
备注:安装GPU版本,自己的机器需要支持安装GPU版本,需要有NVIDIA的显卡,可百度查询。
需要下载安装两个驱动:CUDA
和CuDNN
(需要注意这两个驱动的版本对应)
博主环境:
- Windows 10 64位
- Python 3.6(anaconda中)
- CUDA 9.0
- tensorflow-gpu 1.9
在安装GPU版本的时候,可以先通过
pip install tensorflow-gpu==1.9.0
安装,然后通过import tensorflow as tf
导入,因为还没有安装CUDA
和CuDNN
,会报需要安装的CUDA
的版本,然后再去安装对应版本的CUDA
,CuDNN
选择CUDA
的对应版本。
本篇所有代码可见:Github
测试下:
import tensorflow as tf
# 创建图
a = tf.constant(10)
b = tf.constant(15)
# 创建会话
sess = tf.Session()
# 执行会话
print(sess.run(a + b)) # 输出25
二、TensorFlow
基本概念
- 图(Graph):图描述了计算的过程,
TensorFlow
使用图来表示计算任务。 - 张量(Tensor):
TensorFlow
使用tensor
表示数据。每个Tensor
是一个类型化的多维数组。 - 操作(op):图中的节点被称为op(opearation的缩写),一个op获得/输入0个或多个Tensor,执行计算,产生0个或多个Tensor。
- 会话(Session):图必须在称之为“会话”的上下文中执行。会话将图的op分发到诸如CPU或GPU之类的设备上执行。
- 变量(Variable):运行过程中可以被改变,用于维护状态。
1、边
Tensorflow
的边有两个连接关系:数据依赖和控制依赖;
- 数据依赖:用实线边表示,即张量,在机器学习算法中,张量在数据流图中从前向后流动一遍完成一次前向传播,而残差从后向前流动一遍完成一次反向传播;
- 控制依赖:用虚线边表示,可以用于控制操作的运行,这类边上没有数据流过,但是源节点必须在目的节点开始执行前完成执行。
Tensorflow支持的张量:
数据类型 | Python类型 | 描述 |
---|---|---|
DT_FLOAT | tf.float32 | 32位浮点型 |
DT_DOUBLE | tf.float64 | 64位浮点型 |
DT_INT64 | tf.int64 | 64位有符号整型 |
DT_INT32 | tf.int32 | 32位有符号整型 |
DT_INT16 | tf.int16 | 16位有符号整型 |
DT_INT8 | tf.int8 | 8位有符号整型 |
DT_UNIT8 | tf.unit8 | 8位无符号整型 |
DT_STRING | tf.string | 可变长度的字节数组,每一个张量元素是一个字节数组 |
DT_BOOL | tf.bool | 布尔型 |
DT_COMPLEX64 | tf.complex64 | 由两个32位浮点数组成的复数:实部和虚部 |
DT_QINT32 | tf.qint32 | 用于量化操作的32 位有符号整型 |
DT_QINT8 | tf.qint8 | 用于量化操作的8 位有符号整型 |
DT_QUINT8 | tf.quint8 | 用于量化操作的8 位无符号整型 |
2、节点
图中的节点代表一个操作(operation,OP),一般用来表示施加的数学运算,也可以表示数据输入(feed in)的起点以及输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。
Tensorflow支持的操作:
类别 | 示例 |
---|---|
数学运算操作 | Add、Subtract、Multiply、Div、Exp、Log、Greater、Less、Equal … |
数组运算操作 | Concat、Slice、Split、Constant、Rank、Shape、Shuffle … |
矩阵运算操作 | MatMul、MatrixInverse、MatrixDeterminant … |
有状态的操作 | Variable、Assign、AssignAdd … |
神经网络构建操作 | SoftMax、Sigmoid、ReLU、Convolution2D、MaxPool … |
检查点操作 | Save、Restore |
队列和同步操作 | Enqueue、Dequeue、MutexAcquire、MutexRelease … |
控制张量流动的操作 | Merge、Switch、Enter、Leave、NextIteration |
三、TensorFlow
基本用法
TensorFlow
可以认为是一种编程工具,使用 TensorFlow
来实现具体的业务需求,所以我们可以认为TensorFlow
就是一个“工具箱”,然后我们使用 TensorFlow
这个“工具箱”中的各种“工具”(方法/API)来实现各种功能,比如使用 TensorFlow
实现基本的数值计算、机器学习、深度学习等;使用 TensorFlow
必须理解下列概念:
- 使用 图(graph) 来表示计算任务;
- 在 会话(session) 的上下文中执行图;
- 使用 tensor 表示数据;
- 通过 变量(Variable) 来维护状态 ;
- 使用 feed和fetch 可以为任意的操作(Operation/op)赋值或者从其中获取数据。
TensorFlow
的程序一般分为两个阶段:构建阶段 和 执行阶段:
- 构建阶段:op的执行步骤被描述称为一个图,然后使用
TensorFlow
提供的API构建这个 图。 - 执行阶段:将构建好的执行图(Operation Graph)在给定的 会话 中执行,并得到执行结果。
四、图(Graph
)
TensorFlow
编程的重点是根据业务需求,使用 TensorFlow
的API将业务转换为执行图(有向无环图)。
图中的节点是Tensor,节点之间的连线是节点之间的操作,连线前的节点可以认为是操作的输入,连线后的节点可以认为操作的输出;根据节点的特性(是否有输入输出),可以将节点分为源节点、中间节点和最终的结果节点。
- 图构建的第一步就是创建源op(source op); 源op不需要任何的输入。op构造器的返回值代表被构造出的op的输出,这些返回值可以传递给其它op构造器作为输入或者直接获取结果。
TensorFlow
库中有一个默认图(default graph
),op构造器可以直接为其添加节点,一般情况下,使用默认的Graph即可完成程序代码的实现。不过TensorFlow
也支持通过Graph类管理多个图。
1、使用默认图
import tensorflow as tf
# 1. 定义常量矩阵a和矩阵b
# name属性只是给定这个操作一个名称而已
# 如果给定[[1, 2], [3, 4]],形状已定,可以不指定shape参数
a = tf.constant([[1, 2], [3, 4]], dtype=tf.int32, name='a')
print(type(a))
# 如果给定[5, 6, 7, 8],后面可以通过参数shape来确定形状
b = tf.constant([5, 6, 7, 8], dtype=tf.int32, shape=[2, 2], name='b')
# 2. 以a和b作为输入,进行矩阵的乘法操作
c = tf.matmul(a, b, name='matmul')
print(type(c))
# 3. 以a和c作为输入,进行矩阵的相加操作
g = tf.add(a, c, name='add')
print(type(g))
print("变量a是否在默认图中:{}".format(a.graph is tf.get_default_graph()))
2、不使用默认图
注意:操作必须属于同一个图,不同图中的节点不能相连
# 使用新的构建的图
graph = tf.Graph()
with graph.as_default():
# 此时在这个代码块中,使用的就是新的定义的图graph(相当于把默认图换成了graph)
d = tf.constant(5.0, name='d')
print("变量d是否在新图graph中:{}".format(d.graph is graph))
with tf.Graph().as_default() as g2:
e = tf.constant(6.0)
print("变量e是否在新图g2中:{}".format(e.graph is g2))
# 这段代码是错误的用法,记住:不能使用两个图中的变量进行操作,只能对同一个图中的变量对象(张量)进行操作(op)
# f = tf.add(d, e)
# 报出:Tensor("Const:0", shape=(), dtype=float32) must be from the same graph as Tensor("d:0", shape=(), dtype=float32).
五、会话(Session
)
当执行图构建完成后,才能启动图,进入到执行阶段;启动图的第一步就是创建一个 Session
对象,如果无任何参数的情况下,会话构造器将启动默认图。
1、Session
会话
# 会话构建&启动(默认情况下(不给定Session的graph参数的情况下),创建的Session属于默认的图)
sess = tf.Session()
result = sess.run(fetches=[c])
# result = sess.run(fetches=[c, g])
print("type:{}, value:\n{}".format(type(result), result))
# 会话关闭
sess.close()
# 当一个会话关闭后,不能再使用了,所以下面两行代码错误
# result2 = sess.run(c)
# print(result2)
- 调用
sess
的run
方法来执行矩阵的乘法,得到c的结果值(所以将c作为参数传递进去)- 不需要考虑图中间的运算,在运行的时候只需要关注最终结果对应的对象以及所需要的输入数据值
- 只需要传递进去所需要得到的结果对象,会自动的根据图中的依赖关系触发所有相关的OP操作的执行
- 如果op之间没有依赖关系,
tensorflow
底层会并行的执行op(有资源) --> 自动进行- 如果传递的
fetches
是一个列表,那么返回值是一个list
集合fetches
:表示获取那个op操作的结果值
c和g结果:
- c的结果:
[ 1 2 3 4 ] × [ 5 6 7 8 ] = [ 19 22 43 50 ] \left[ \begin{matrix} 1 & 2 \\ 3 & 4 \end{matrix} \right] \times \left[ \begin{matrix} 5 & 6 \\ 7 & 8 \end{matrix} \right] = \left[ \begin{matrix} 19 & 22 \\ 43 & 50 \end{matrix} \right] [1324]×[5768]=[19432250]- g的结果:
[ 1 2 3 4 ] + [ 19 22 43 50 ] = [ 20 24 46 54 ] \left[ \begin{matrix} 1 & 2 \\ 3 & 4 \end{matrix} \right] + \left[ \begin{matrix} 19 & 22 \\ 43 & 50 \end{matrix} \right]= \left[ \begin{matrix} 20 & 24 \\ 46 & 54 \end{matrix} \right] [1324]+[19432250]=[20462454]
补充:(可以使用with语句块来开启会话,自动关闭会话)
# 使用with语句块,会在with语句块执行完成后,自动的关闭session
# allow_soft_placement:是否允许动态使用CPU和GPU,默认为False;
# 当我们的安装方式为GPU的时候,建议该参数设置为True,因为TensorFlow中的部分op只能在CPU上运行。
# log_device_placement: 是否打印日志,默认为False,不打印日志
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True)) as sess2:
print(sess2)
# 获取张量c的结果: 通过Session的run方法获取
print("sess2 run:{}".format(sess2.run(c)))
# 获取张量g的结果:通过张量对象的eval方法获取,和Session的run方法一致
print("c eval:{}".format(g.eval()))
代码可见:01_tensorflow.py
tf.Session
在构建会话的时候,如果不给定任何参数,那么构建出来的 Session
对应内部的 Graph
,其实就是默认 Graph
,不过我们可以通过参数给定具体对应的是那一个 Graph
以及当前 Session
对应的配合参数。Session
的构造主要有三个参数,作用如下:
target
:给定连接的url,只有当分布式运行的时候需要给定;graph
:给定当前Session对应的图,默认为TensorFlow中的默认图;config
:给定当前Session的相关参数
import tensorflow as tf
# 构建一个图
a = tf.constant('10', tf.string, name='a_const')
b = tf.string_to_number(a, out_type=tf.float64, name='str_2_double')
c = tf.to_double(5.0, name='to_double')
d = tf.add(b, c, name='add')
# 构建Session并执行图
# 1、构建GPU相关参数
gpu_options = tf.GPUOptions()
# per_process_gpu_memory_fraction:给定对于每一个进程,分配多少的GPU内存,默认为1
# 设置为0.5表示分配50%的GPU内存
gpu_options.per_process_gpu_memory_fraction = 0.5
# allow_growth:设置为True表示在GPU内存分配的时候,采用动态分配方式,默认为False
# 动态分配的意思是指,在启动之前,不分配全部的内存,根据需要后面动态的进行分配
# 在开启动态分配后,GPU内存部分自动释放,所以复杂、长时间运行的任务不建议开启
gpu_options.allow_growth = True
# 2、构建Graph优化的相关参数
optimizer = tf.OptimizerOptions(
do_common_subexpression_elimination=True, # 设置为True表示开启公共执行子句优化
do_constant_folding=True, # 设置为True表示开始常数折叠优化
opt_level=0 # 设置为0,表示开启上述两个优化,默认为0
)
graph_options = tf.GraphOptions(optimizer_options=optimizer)
# 3、构建Session的Config相关参数
config_proto = tf.ConfigProto(allow_soft_placement=True, log_device_placement=True,
graph_options=graph_options, gpu_options=gpu_options)
# 4、构建Session并运行
with tf.Session(config=config_proto) as sess:
print(sess.run(d))
代码可见:02_Session_Config.py
2、InteractiveSession
会话(交互式会话)
使用交互式会话可以降低代码的复杂度,使用 Tensor.eval()
或者 Operation.run()
来代替 Session.run()
方法,这样可以避免一个变量来维持会话;
备注:Session也可以使用Tensor.eval()和Operation.run()获取数据/执行操作(只要明确当前会话)。
import tensorflow as tf
# 构建一个图
a = tf.constant(4)
b = tf.constant(3)
c = tf.multiply(a, b)
# 运行
with tf.Session():
print(c.eval())
# 进入交互式会话
sess = tf.InteractiveSession()
# 定义变量和常量
x = tf.constant([1.0, 2.0])
a = tf.constant([2.0, 4.0])
# 进行减操作
sub = tf.subtract(x, a)
# 输出结果
print(sub.eval())
print(sess.run(sub))
代码可见:03_InteractiveSession.py
六、张量(Tensor
)
TensorFlow
使用 Tensor
数据结构来代表所有数据,计算图中,操作间传递的数据都是 Tensor
。Tensor
可以看作是一个
N
N
N 维的数组或者列表,一个 Tensor
主要由一个静态数据类型和动态类型的维数(Rank、Shape)组成。Tensor
可以在图中的节点之间流通。
[
D
0
,
D
1
,
.
.
.
,
D
n
]
[D_0,D_1,...,D_n]
[D0,D1,...,Dn]表示一个
N
N
N 维的张量。
七、变量(Variables
)
变量(Variables
)是维护图执行过程中的状态信息。在训练模型过程中,可以通过变量来存储和更新参数。变量包含张量(Tensor
)存放于内存的缓存区。建模的时候变量必须被明确的初始化,模型训练后变量必须被存储到磁盘。这些变量的值可以在之后的模型训练和分析中被加载。
在构建变量的时候,必须将一个 张量
或者 可以转化为张量的 Python对象
作为初始值传入构造函数Variable
中。
简单变量案例:
import tensorflow as tf
# 创建一个变量,初始化值为变量3.0
a = tf.Variable(3.0)
# 创建一个常量
b = tf.constant(2.0)
c = tf.add(a, b)
# 启动图后,变量必须先进行初始化操作
# 增加一个初始化变量的op到图中
init_op = tf.initialize_all_variables()
# 启动图
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
# 运行init_op
sess.run(init_op)
# 获取值
print("a = {}".format(sess.run(a)))
print("c = {}".format(c.eval))
代码可见:04_Variables1.py
变量依赖案例:
import tensorflow as tf
# 创建一个变量
w1 = tf.Variable(tf.random_normal([10], stddev=0.5, dtype=tf.float32), name='w1')
# 基于第一个变量创建第二个变量
a = tf.constant(2, dtype=tf.float32)
w2 = tf.Variable(w1.initialized_value() * a, name='w2')
# 进行全局初始化
init_op = tf.initialize_all_variables()
# 启动图
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
# 运行init_op
sess.run(init_op)
# 获取值
result = sess.run([w1, w2])
print("w1 = {}\nw2 = {}".format(result[0], result[1]))
代码可见:05_Variables2.py
八、取回(fetch
)
为了取回操作的输出内容,可以在使用 Session
对象的 run
方法调用执行图的时候,传入一些tensor
,通过 run
方法就可以获取这些 tensor
对应的结果值。
如果需要获取多个tensor的值,那么尽量一次运行就获取所有的结果值,而不是采用逐个获取的方式。
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
# 运行init_op
sess.run(init_op)
# 获取值
result = sess.run(fetches=[w1, w2])
print("w1 = {}\nw2 = {}".format(result[0], result[1]))
九、填充(feed
)
在构建图时使用 placeholder
类型的 API
临时替代任意操作的张量(占位符),在调用 Session
对象的run()
方法去执行图时,使用填充数据作为调用的参数,调用结束后,填充数据就消失了。
feed
使用一个 tensor
值临时替换一个操作的输出结果,在获取数据的时候必须给定对应的 feed
数据作为参数。feed
只有在调用它的方法内有效,方法结束,feed
就消失了。
feed
可以使用 placeholder
类型的API创建占位符,常见API:
- tf.placeholder
- tf.placeholder_with_default
import tensorflow as tf
# 创建占位符,创建图
m1 = tf.placeholder(tf.float32)
m2 = tf.placeholder(tf.float32)
m3 = tf.placeholder_with_default(4.0, shape=None)
output = tf.multiply(m1, m2)
ot1 = tf.add(m1, m3)
# 运行图
with tf.Session() as sess:
print(sess.run(output, feed_dict={m1: 3, m2: 4}))
print(output.eval(feed_dict={m1: 8, m2: 10}))
print(sess.run(ot1, feed_dict={m1: 3, m3: 3}))
print(sess.run(ot1, feed_dict={m1: 3}))
代码可见:06_feed_placeholder.py
十、变量更新(assign
)
1、累加器
import tensorflow as tf
# 1. 定义一个变量
x = tf.Variable(0, dtype=tf.int32, name='v_x')
# 2. 变量的更新
assign_op = tf.assign(ref=x, value=x + 1)
# 3. 变量初始化操作
x_init_op = tf.global_variables_initializer()
# 3. 运行
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# 变量初始化
sess.run(x_init_op)
# 模拟迭代更新累加器
for i in range(5):
# 执行更新操作
sess.run(assign_op)
r_x = sess.run(x)
print(r_x)
2、动态的更新变量的维度数目
import tensorflow as tf
# 1. 定义一个不定形状的变量
x = tf.Variable(
initial_value=[], # 给定一个空值
dtype=tf.float32,
trainable=False,
validate_shape=False # 设置为True,表示在变量更新的时候,进行shape的检查,默认为True
)
# 2. 变量更改
concat = tf.concat([x, [0.0, 0.0]], axis=0)
assign_op = tf.assign(x, concat, validate_shape=False)
# 3. 变量初始化操作
x_init_op = tf.global_variables_initializer()
# 3. 运行
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# 变量初始化
sess.run(x_init_op)
# 模拟迭代更新累加器
for i in range(5):
# 执行更新操作
sess.run(assign_op)
r_x = sess.run(x)
print(r_x)
3、求阶乘(控制依赖)
我们可以通过 Variable
和 assign
完成变量的定义和更新,但是如果在更新变量之前需要更新其它变量,那么会导致一个比较严重的问题:也就是需要多次调用 sess.run
方法来进行变量的更新。通过这种方式,代码复杂程度上升,同时也没有执行效率。
- 解决该问题的方案就是:控制依赖。
- 通过
TensorFlow
中提供的一组函数来处理不完全依赖的情况下的操作排序问题(即给定哪个操作先执行的问题), 通过tf.control_dependencies
API完成。
import tensorflow as tf
# 1. 定义一个变量
sum = tf.Variable(1, dtype=tf.int32)
# 2. 定义一个占位符
i = tf.placeholder(dtype=tf.int32)
# 3. 更新操作
tmp_sum = sum * i
# tmp_sum = tf.multiply(sum, i)
assign_op = tf.assign(sum, tmp_sum)
with tf.control_dependencies([assign_op]):
# 如果需要执行这个代码块中的内容,必须先执行control_dependencies中给定的操作/tensor
sum = tf.Print(sum, data=[sum, sum.read_value()], message='sum:')
# 4. 变量初始化操作
x_init_op = tf.global_variables_initializer()
# 5. 运行
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# 变量初始化
sess.run(x_init_op)
# 模拟迭代更新累加器
for j in range(1, 6):
# 执行更新操作
# sess.run(assign_op, feed_dict={i: j})
# 通过control_dependencies可以指定依赖关系,这样的话,就不用管内部的更新操作了
r = sess.run(sum, feed_dict={i: j})
print("5!={}".format(r))
代码可见:07_assign.py
十一、设备(device
)
设备 是指一块可以用来运算并且拥有自己的地址空间的硬件,如 CPU
和 GPU
。Tensorflow
为了在执行操作的时候,充分利用计算资源,可以明确指定操作在哪个设备上执行。
一般情况下,不需要显示指定使用 CPU
还是 GPU
,TensorFlow
会自动检测。如果检测到 GPU
,TensorFlow
会尽可能地利用第一个GPU
来执行操作。
注意:如果机器上有超过一个可用的
GPU
,那么除了第一个外其它GPU
默认是不参与计算的。所以,在实际TensorFlow
编程中,经常需要明确给定使用的CPU
和GPU
。
“/cpu:0”
:表示使用机器CPU运算“/gpu:0”
:表示使用第一个GPU运算,如果有的话“/gpu:1”
:表示使用第二个GPU运算,以此类推
import tensorflow as tf
# with tf.device("/cpu:0"):
with tf.device("/gpu:0"):
a = tf.constant([1, 2, 3], name='a')
b = tf.constant(2, name='b')
c = tf.multiply(a, b)
# 新建Seesion,并将log_device_placement设置为True
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
# 运行这个op
print(sess.run(c))
代码可见:08_device.py
十二、变量作用域(scope
)
通过 tf.Variable
我们可以创建变量,但是当模型复杂的时候,需要构建大量的变量集,这样会导致我们对于变量管理的复杂性,而且没法共享变量(存在多个相似的变量)。针对这个问题,可以通过 TensorFlow
提供的变量作用域机制来解决,在构建一个图的时候,就可以非常容易的使用共享命名过的变量。
Tensorflow
中有两个作用域:
- 一个是
name_scope
- 另一个是
variable_scope
1、variable_scope
变量作用域机制在 TensorFlow
中主要通过两部分组成:
tf.get_variable
:通过所给定的名字创建或者返回一个对应的变量
tf.get_variable
方法在调用的时候,主要需要给定参数名称name
,形状shape
,数据类型dtype
以及初始化方式initializer
四个参数。- 该API底层执行的时候,根据
variable score
的属性reuse
的值决定采用何种方式来获取变量。
- 当
reuse
值为False
的时候(不允许设置),作用域就是创建新变量设置的,此时要求对应的变量不存在,否则报错;- 当
reuse
值为True
的时候,作用域就是为重用变量所设置的,此时要求对应的变量必须存在,否则报错。- 当
reuse
的值为tf.AUTO_REUSE
的时候,表示如果变量存在就重用变量,如果变量不存在,就创建新变量返回。(备注:
reuse
一般设置在variable score
对象上)
tf.variable_scope
:为通过创建的变量或者操作Operation
指定命名空间
import tensorflow as tf
# 方式一:不加作用域的
def my_func1(x):
w1 = tf.Variable(tf.random_normal([1]))[0]
b1 = tf.Variable(tf.random_normal([1]))[0]
result1 = w1 * x + b1
w2 = tf.Variable(tf.random_normal([1]))[0]
b2 = tf.Variable(tf.random_normal([1]))[0]
result2 = w2 * x + b2
return result1, w1, b1, result2, w2, b2
# 下面两行代码还是属于图的构建
x = tf.constant(3, dtype=tf.float32)
r = my_func1(x)
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# 初始化
tf.global_variables_initializer().run()
# 执行结果
print(sess.run(r))
# 方式二:加作用域
def my_func2(x):
# initializer:初始化器
w = tf.get_variable('weight', [1], initializer=tf.random_normal_initializer())[0]
b = tf.get_variable('bias', [1], initializer=tf.random_normal_initializer())[0]
result = w * x + b
return result, w, b
def func(x):
with tf.variable_scope('op1', reuse=tf.AUTO_REUSE):
r1 = my_func2(x)
with tf.variable_scope('op2', reuse=tf.AUTO_REUSE):
r2 = my_func2(x)
return r1, r2
# 下面两行代码还是属于图的构建
x1 = tf.constant(3, dtype=tf.float32, name='x1')
x2 = tf.constant(4, dtype=tf.float32, name='x2')
with tf.variable_scope('func1'): # 支持嵌套
r1 = func(x1)
with tf.variable_scope('func2'):
r2 = func(x2)
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# 初始化
tf.global_variables_initializer().run()
# 执行结果
print(sess.run([r1, r2]))
代码可见:09_variable_scope1.py
TF底层使用
'变量作用域/变量名称:0'
的方式标志变量(eg:func/op1/weight:0
)
tf.get_variable
常用的 initializer
初始化器:
初始化器 | 描述 |
---|---|
tf.constant_initializer(value) | 初始化为给定的常数值value |
tf.random_uniform_initializer(a, b) | 初始化为从a到b的均匀分布的随机值 |
tf.random_normal_initializer(mean, stddev) | 初始化为均值为mean 、方差为stddev 的服从高斯分布的随机值 |
tf.orthogonal_initializer(gini=1.0) | 初始化一个正交矩阵,gini 参数作用是最终返回的矩阵是随机矩阵乘以gini 的结果 |
tf.identity_initializer(gini=1.0) | 初始化一个单位矩阵,gini 参数作用是最终返回的矩阵是随机矩阵乘以gini 的结果 |
tf.variable_score
方法的作用就是定义一个作用域,定义在 variable_score
作用域中的变量和操作,会将variable score
的名称作为前缀添加到变量/操作名称前,支持嵌套的作用域,添加前缀规则和文件目录路径的规则类似。
tf.variable_score
参数如果给定的是一个已经存在的作用域对象的时候,那么构建变量的时候表示直接跳过当前作用域前缀,直接成为一个完全不同与现在的作用域(直接创建给定作用域下的变量)。但是构建操作的时候,还是和嵌套的方式一样,直接添加子作用域。
tf.variable_score
参数中,可以给定当前作用域中默认的初始化器initializer
,并且子作用域会直接继承父作用域的相关参数(是否重用、默认初始化器等)
import tensorflow as tf
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
with tf.variable_scope('foo', initializer=tf.constant_initializer(4.0)) as foo:
v = tf.get_variable("v", [1])
w = tf.get_variable("w", [1], initializer=tf.constant_initializer(3.0))
with tf.variable_scope('bar'):
l = tf.get_variable("l", [1])
with tf.variable_scope(foo):
h = tf.get_variable('h', [1])
g = v + w + l + h
with tf.variable_scope('abc'):
a = tf.get_variable('a', [1], initializer=tf.constant_initializer(5.0))
b = a + g
sess.run(tf.global_variables_initializer())
print("{},{}".format(v.name, v.eval()))
print("{},{}".format(w.name, w.eval()))
print("{},{}".format(l.name, l.eval()))
print("{},{}".format(h.name, h.eval()))
print("{},{}".format(g.name, g.eval()))
print("{},{}".format(a.name, a.eval()))
print("{},{}".format(b.name, b.eval()))
代码可见:10_variable_scope2.py
2、name_scope
name_score
的主要作用是为 op_name
前加前缀,variable_score
是为 get_variable
创建的变量的名字加前缀。
name_score
的主要作用就是:Tensorflow
中常常会有数以千计的节点,在可视化的过程中很难一下子展示出来,因此用 name_scope
为变量划分范围,在可视化中,这表示在计算图中的一个层级。name_scope
会影响op_name
,不会影响用get_variable()
创建的变量,而会影响通过Variable()
创建的变量。
简单来讲:使用
tf.Variable
创建的变量受name_score
和variable_score
的影响,会给变量添加前缀,但是使用tf.get_variable
创建变量只受variable_score
的影响。
import tensorflow as tf
with tf.Session() as sess:
with tf.name_scope('name1'):
with tf.variable_scope('variable1'):
v = tf.Variable(1.0, name='v')
w = tf.get_variable(name='w', shape=[1], initializer=tf.constant_initializer(2.0))
h = v + w
with tf.variable_scope('variable2'):
with tf.name_scope('name2'):
v2 = tf.Variable(2.0, name='v2')
w2 = tf.get_variable(name='w2', shape=[1], initializer=tf.constant_initializer(2.0))
h2 = v2 + w2
sess.run(tf.global_variables_initializer())
print("{},{}".format(v.name, v.eval()))
print("{},{}".format(w.name, w.eval()))
print("{},{}".format(h.name, h.eval()))
print("{},{}".format(v2.name, v2.eval()))
print("{},{}".format(w2.name, w2.eval()))
print("{},{}".format(h2.name, h2.eval()))
代码可见:11_name_scope.py
十三、可视化(Tensorboard
)
TensorFlow
提供了一套可视化工具:TensorBoard
,在通过pip
安装 TensorFlow
的情况下,默认也会安装TensorBoard
。通过TensorBoard
可以展示TensorFlow
的图像、绘制图像生成的定量指标以及附加数据等信息。
TensorBoard
通过读取 TensorFlow
的事件文件来运行,TensorFlow
的事件文件包括了在 TensorFlow
运行中涉及到的主要数据,比如:scalar
、image
、audio
、histogram
和graph
等。
通过 tf.summary
相关API,将数据添加 summary
中,然后在 Session
中执行这些操作得到一个序列化Summary protobuf
对象,然后使用 FileWriter
对象将汇总的序列数据写入到磁盘,然后使用 tensorboard
命令进行图标展示,默认访问端口是:6006
import tensorflow as tf
with tf.variable_scope("foo"):
with tf.device("/cpu:0"):
x_init1 = tf.get_variable('init_x', [10], tf.float32, initializer=tf.random_normal_initializer())[0]
x = tf.Variable(initial_value=x_init1, name='x')
y = tf.placeholder(dtype=tf.float32, name='y')
z = x + y
with tf.variable_scope("bar"):
a = tf.constant(3.0) + 4.0
# update x
assign_op = tf.assign(x, x + 1)
with tf.control_dependencies([assign_op]):
with tf.device('/gpu:0'):
out = x * y
with tf.device('/cpu:0'):
with tf.variable_scope("bar"):
a = tf.constant(3.0) + 4.0
w = z * a
# 开始记录信息(需要展示的信息的输出)
tf.summary.scalar('scalar_init_x', x_init1)
tf.summary.scalar(name='scalar_x', tensor=x)
tf.summary.scalar('scalar_y', y)
tf.summary.scalar('scalar_z', z)
tf.summary.scalar('scala_w', w)
tf.summary.scalar('scala_out', out)
with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess:
# merge all summary
merged_summary = tf.summary.merge_all()
# 得到输出到文件的对象
writer = tf.summary.FileWriter('./result', sess.graph)
# 初始化
sess.run(tf.global_variables_initializer())
for i in range(1, 5):
summary, r_out, r_x, r_w = sess.run([merged_summary, out, x, w], feed_dict={y: i})
writer.add_summary(summary, i)
print("{},{},{}".format(r_out, r_x, r_w))
# 关闭操作
writer.close()
查看可视化结果步骤:
- 打开cmd命令行输入:
tensorboard --helpfull
可以查看相关帮助命名 - 输入:
tensorboard --logdir F:\All_worlspace\Pycharm_workspace\DeepLearning\Tensorflow-basic\result
这里的路径是你保存输出文件对象的路径
- 在浏览器中输入上图红色框内的
双击图中的
foo
或bar
会打开,如下图
代码可见:12_tensorboard.py
十四、模型保存、提取(Saver
)
TensorFlow
使用 tf.train.Saver
类实现模型的保存和提取。
- Saver对象的
saver
方法将TensorFlow模型保存到指定路径中。 - 通过Saver对象的
restore
方法可以加载模型,并通过保存好的模型变量相关值重新加载完全加载进来。
如果不希望重复定义计算图上的运算,可以直接加载已经持久化的图,通过 tf.train.import_meta_graph
方法直接加载
备注:在加载的时候,可以在Saver对象构建的时候,明确给定变量名之间的映射关系
import tensorflow as tf
# # 模型保存
# v1 = tf.Variable(tf.constant(3.0), name='v1')
# v2 = tf.Variable(tf.constant(4.0), name='v2')
# result = v1 + v2
#
# saver = tf.train.Saver()
# with tf.Session() as sess:
# sess.run(tf.global_variables_initializer())
# sess.run(result)
# # 模型保存到model文件夹下,文件前缀为:model.ckpt
# saver.save(sess, './model/model.ckpt')
# 模型的提取(完整提取:需要完整恢复保存之前的数据格式)
v1 = tf.Variable(tf.constant(1.0), name='v1')
v2 = tf.Variable(tf.constant(4.0), name='v2')
result = v1 + v2
saver = tf.train.Saver()
with tf.Session() as sess:
# 会从对应的文件夹中加载变量、图等相关信息
saver.restore(sess, './model/model.ckpt')
print(sess.run([result]))
# 直接加载图,不需要定义变量了
saver = tf.train.import_meta_graph('./model/model.ckpt.meta')
with tf.Session() as sess:
saver.restore(sess, './model/model.ckpt')
print(sess.run(tf.get_default_graph().get_tensor_by_name("add:0")))
# 模型的提取(给定映射关系)
a = tf.Variable(tf.constant(1.0), name='a')
b = tf.Variable(tf.constant(2.0), name='b')
result = a + b
saver = tf.train.Saver({"v1": a, "v2": b})
with tf.Session() as sess:
# 会从对应的文件夹中加载变量、图等相关信息
saver.restore(sess, './model/model.ckpt')
print(sess.run([result]))
代码可见:13_Saver.py
十五、总结
至此,关于Tensorflow的基础入门大概就这么多,此外还会涉及到线程和队列、数据读取、分布式训练等,可以在以后深入学习,理解。