目录
- 简介
- TensorFlow的特性
- TensorFlow组件
-
TensorFlow用法介绍
一、简介
TensorFlow™ 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。2015年11月9日,Google发布人工智能系统TensorFlow并宣布开源。其命名来源于本身的原理,Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算。Tensorflow运行过程就是张量从图的一端流动到另一端的计算过程。张量从图中流过的直观图像是其取名为"TensorFlow"的原因。该灵活性也让TensorFlow框架可以在多个平台上进行计算,例如:台式计算机、服务器、移动设备等。
What is Data Flow Graphs?
数据流图使用节点(Node)和线(Edges)的有向图描述数学计算;节点一般用来表示施加的数学操作,也可以表示数据输入(feed in)的起点和输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。线(Edges)表示的是节点之间的输入/输出关系,这些线可以输运"size可动态调整"的多维数组,即张量(Tensor)。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算。
备注:TensorFlow的开发过程中,重点在于构建执行流图。
二、TensorFlow的特性
2.1 几种框架比较:
库名称 |
Caffe |
MXNet |
TensorFlow |
Deeplearning4j |
开发语言 |
c++/cuda |
c++/cuda |
c++/cuda/python |
java |
支持接口 |
c++/python/matlab |
python/R/Julia |
c++/python |
java/scala |
安装难度 |
难 |
比较难 |
简单 |
简单 |
示例数量 |
多 |
比较多 |
少 |
少 |
支持模型 |
CNN |
CNN/RNN |
CNN/RNN/... |
CNN/RNN/... |
上手难度 |
比较难 |
简单 |
难 |
简单 |
2.2 TensorFlow的特性
- 高度的灵活性:只要能够将计算表示成为一个数据流图,那么就可以使用TensorFlow。
- 可移植性:TensorFlow支持CPU和GPU的运算,并且可以运行在台式机、服务器、手机移动端设备等等。
- 自动求微分:TensorFlow内部实现了自动对于各种给定目标函数求导的方式。
- 多种语言支持:Python、C++
- 性能高度优化
三、TensorFlow组件
3.1 TensorFlow组成
- 图(Graph):图描述了计算的过程,TensorFlow使用图来表示计算任务。
- 张量(Tensor):TensorFlow使用tensor表示数据。每个Tensor是一个类型化的多维数组。
- 操作(op):图中的节点被称为op(opearation的缩写),一个op获得/输入0个或多个Tensor,执行计算,产生0个或多个Tensor。
- 会话(Session):图必须在称之为"会话"的上下文中执行。会话将图的op分发到诸如CPU或GPU之类的设备上执行。
- 变量(Variable):运行过程中可以被改变,用于维护状态。
- 边:TensorFlow的边即有两种连接关系:数据依赖和控制依赖
- 实线边表示数据依赖,代表数据,即张量。任意维度的数据统称为张量。在机器学习算法中,张量在数据流图中从前往后流动一遍就完成一次前向传播,而残差从后向前流动一遍就完成一次反向传播。
- 虚线边表示控制依赖,可以用于控制操作的运行,这被用来确保happensbefore关系,这类边上没有数据流过,但源节点必须在目的节点开始执行前完成
3.2 数据类型:
数据类型 |
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_UINT8 |
tf.uint8 |
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位无符号整型 |
3.3 节点
节点又称为算子,它代表一个操作,一般用来表示施加的数字运算,也可以表示数据输入的起点以及输出的重点,或者是读取/写入持久变量的终点
类别 |
示例 |
数学运算操作 |
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用法介绍
4.1 TensorFlow安装和卸载
CPU版本:只支持程序在CPU上计算/运算,默认运行就是CPU上运行
卸载: pip uninstall tensorflow
pip uninstall tensorflow-tensorboard
安装:pip install tensorflow==1.4.0
GPU版本:支持GPU、CPU上的计算/运算,默认运行在GPU上,但是可以明确指定在CPU上运行(因为GPU的运行速度比CPU要快,所以会仅可能的将程序运行在GPU上)
卸载:pip uninstall tensorflow-gpu tensorflow-tensorboard
安装:pip install tensorflow-gpu==1.4.0
备注:必须安装CUDA和cuDNN; 必须要求机器有NVIDIA显卡
CUDA下载安装链接:https://developer.nvidia.com/cuda-toolkit-archive
cuDNN下载安装链接:https://developer.nvidia.com/rdp/cudnn-archive
4.2 TensorFlow基本用法
我们可以认为TensorFlow就是一个"工具箱",然后我们使用TensorFlow这个"工具箱"中的各种"工具"(方法/API)来实现各种功能,比如使用TensorFlow实现基本的数值计算、机器学习、深度学习等;使用TensorFlow必须理解下列概念:
- 使用图(graph)来表示计算任务;
- 在会话(session)的上下文中执行图;
- 使用tensor表示数据;
- 通过变量(Variable)来维护状态 ;
- 使用feed和fetch可以为任意的操作(Operation/op)赋值或者从其中获取数据。
4.3 TensorFlow程序结构
TensorFlow的程序一般分为两个阶段:构建阶段和执行阶段;
- 构建阶段:op的执行步骤被描述称为一个图,然后使用TensorFlow提供的API构建这个图。
- 执行阶段:将构建好的执行图(Operation Graph)在给定的会话中执行,并得到执行结果。
4.4 TensorFlow图
- TensorFlow编程的重点是根据业务需求,使用TensorFlow的API将业务转换为执行图(有向无环图);图中的节点是Tensor,节点之间的连线是节点之间的操作,连线前的节点可以认为是操作的输入,连线后的节点可以认为操作的输出;根据节点的特性(是否有输入输出),可以将节点分为源节点、中间节点和最终的结果节点。
- 图构建的第一步就是创建源op(source op); 源op不需要任何的输入。op构造器的返回值代表被构造出的op的输出,这些返回值可以传递给其它op构造器作为输入或者直接获取结果。
- TensorFlow库中有一个默认图(default graph),op构造器可以直接为其添加节点,一般情况下,使用默认的Graph即可完成程序代码的实现。不过TensorFlow也支持通过Graph类管理多个图。
- 不使用默认图(Graph),使用多个图来进行编程;但是注意:操作必须属于同一个图,不同图中的节点不能相连。以下实例会强调。
举例:
1 # 使用新的构建的图 2 graph = tf.Graph() 3 with graph.as_default()#此代码块中,使用的就是新定义的图graph(相当于把默认图换成了graph) 4 d = tf.constant(5.0, name='d') 5 print("变量d是否在新图graph中:{}".format(d.graph is graph)) 6 with tf.Graph().as_default() as g2: 7 e = tf.constant(6.0) 8 print("变量e是否在新图g2中:{}".format(e.graph is g2)) 9 # 这段代码是错误的用法,记住:不能使用两个图中的变量进行操作,只能对同一个图中的变量对象(张量)进行操作(op) 10 f = tf.add(d, e)
结果:
变量d是否在新图graph中:True
变量e是否在新图g2中:True
ValueError: Tensor("Const:0", shape=(), dtype=float32) must be from the same graph as Tensor("d:0", shape=(), dtype=float32).
4.5 TensorFlow会话
当执行图构建完成后,才能给启动图,进入到执行阶段;启动图的第一步就是创建一个Session对象,如果无任何参数的情况下,会话构造器将启动默认图。
- 调用sess的run方法来执行矩阵的乘法,得到一个的结果值(所以将这个要求的值作为参数传递进去),
- 不需要考虑图中间的运算,在运行的时候只需要关注最终结果对应的对象以及所需要的输入数据值;只需要传递进去所需要得到的结果对象,会自动的根据图中的依赖关系触发所有相关的OP操作的执行
- 如果op之间没有依赖关系,tensorflow底层会并行的执行op(有资源) --> 自动进行
- fetches:表示获取那个op操作的结果值;
- 如果传递的fetches是一个列表,那么返回值是一个list集合
- 当一个会话关闭后,不能再使用了,所以下面两行代码错误
1 import tensorflow as tf 2 # 1. 定义常量矩阵a和矩阵b ,name属性只是给定这个操作一个名称而已 3 a = tf.constant([[1, 2], [3, 4]], dtype=tf.int32, name='a') 4 print(type(a)) # <class 'tensorflow.python.framework.ops.Tensor'> 5 b = tf.constant([5, 6, 7, 8], dtype=tf.int32, shape=[2, 2], name='b') 6 # 2. 以a和b作为输入,进行矩阵的乘法操作 7 c = tf.matmul(a, b, name='matmul') 8 print(type(c)) # <class 'tensorflow.python.framework.ops.Tensor'> 9 # 3. 以a和c作为输入,进行矩阵的相加操作 10 g = tf.add(a, c, name='add') 11 # 4. 添加减法 12 h = tf.subtract(b, a, name='b-a') 13 l = tf.matmul(h, c) 14 r = tf.add(g, l) 15 # 会话构建&启动 16 sess = tf.Session()# print(sess) 返回<tensorflow.python.client.session.Session object at 0x000001C203DD4F60> 17 # result = sess.run(fetches=c) 18 # print("type:{}, value: {}".format(type(result), result)) #type:<class 'numpy.ndarray'> 19 result = sess.run(fetches=[r, c]) #传输一个列表 20 print("type:{}, value: {}".format(type(result), result)) 21 # 会话关闭 22 sess.close() 23 # # 当一个会话关闭后,不能再使用了,所以下面两行代码错误 24 # # result2 = sess.run(c) 25 # # print(result2)
以上语句的sess = tf.Session()和sess.close()可以使用with语句块替换,在with语句块执行完成后,自动的关闭session。具体如下所示
with tf.Session() as sess2:
print("sess2 run:{}".format(sess2.run(c)))
tf.Session在构建会话的时候,如果不给定任何参数,那么构建出来Session对应的内部的Graph其实就是默认Graph,不过我们可以通过参数给定具体对应的是哪一个Graph以及当前Session对应的配合参数。Session的构造主要有三个参数,作用如下:
target:给定连接的url,只有当分布式运行的时候需要给定(后面分布式运行讲);
graph:给定当前Session对应的图,默认为TensorFlow中的默认图;
config:给定当前Session的相关参数,参数详见:
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/protobuf/config.proto 中的[ConfigProto] 通过Session的config参数可以对TensorFlow的应用的执行进行一些优化调整,主要涉及到的参数如下:
属性 |
作用 |
gpu_options |
GPU相关参数, 主要参数: per_process_gpu_memory_fraction和allow_growth |
allow_soft_placement |
是否允许动态使用CPU和GPU, 默认为False; 当我们的安装方式为GPU的时候 建议该参数设置为True, 因为TensorFlow中的部分op只能在CPU上运行。 |
log_device_placement |
是否打印日志, 默认为False, 不打印日志 |
graph_options |
Graph优化相关参数, 一般不需要给定, 默认即可, 主要参数: optimizer_options(do_common_subexpression_elimination、do_constant_folding 和opt_level) |
其中第一个参数per_process_gpu_memory_fraction表示分配给GPU的占比大小,这个较为常见,其它使用较少。
实例:在上文程序中,加入以下:
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess2:
# 获取张量c的结果: 通过Session的run方法获取
print("sess2 run:{}".format(sess2.run(c)))
# 获取张量r的结果:通过张量对象的eval方法获取,和Session的run方法一致
print("c eval:{}".format(r.eval()))
会增加打印日志如下:
4.5 InteractiveSession
在TensorFlow中,除了可以使用Session表示这个会话外,还可以通过InteractiveSession来表示会话,InteractiveSession的意思是:交互式会话,使用交互式会话可以降低代码的复杂度,使用Tensor.eval()或者Operation.run()来代替Session.run()方法,这样可以避免一个变量来维持会话;
备注:Session也可以使用Tensor.eval()和Operation.run()获取数据/执行操作(只要明确当前会话)。但是这种方式使用不是很普遍,建议还是使用session的方式。实例如下所示:
sess3 = tf.InteractiveSession()
print(r.eval())
4.6 Tensor张量
TensorFlow使用Tensor数据结构来代表所有数据,计算图中,操作间传递的数据都是Tensor。Tensor可以看作是一个n维的数组或者列表,一个Tensor主要由一个静态数据类型和动态类型的维数(Rank、Shape)组成。Tensor可以在图中的节点之间流通。
4.7 TensorFlow变量
变量(Variables)是维护图执行过程中的状态信息。在训练模型过程中,可以通过变量来存储和更新参数。变量包含张量(Tensor)存放于内存的缓存区。建模的时候变量必须被明确的初始化,模型训练后变量必须被存储到磁盘。这些变量的值可以在之后的模型训练和分析中被加载。
在构建变量的时候,必须将一个张量或者可以转化为张量的Python对象作为初始值传入构造函数Variable中。
注意:变量的全局初始化(tf.initialize_all_variables())是并行执行的,如果存在变量之间的依赖关系的时候,再进行初始化的时候要特别注意。(1.4版本之后就没有这个问题了),案例如下:
1 # 1. 定义一个变量,必须给定初始值(图的构建,没有运行) 2 a = tf.Variable(initial_value=3.0, dtype=tf.float32) 3 # 2. 定义一个张量 4 b = tf.constant(value=2.0, dtype=tf.float32) 5 c = tf.add(a, b) 6 # 3. 进行初始化操作(推荐:使用全局所有变量初始化API)相当于在图中加入一个初始化全局变量的操作 7 init_op = tf.global_variables_initializer() 8 # 3. 图的运行 9 with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess: 10 sess.run(init_op) # 运行init op进行变量初始化,一定要放到所有运行操作之前 11 # init_op.run() # 和上面代码效果一样,但是要求明确给定当前代码块对应的默认 #session(tf.get_default_session())是哪个,底层使用默认session来运行 12 # 获取操作的结果 13 print("result:{}".format(sess.run(c))) 14 print("result:{}".format(c.eval()))
4.8 TensorFlow Fetch
- 为了取回操作的输出内容,可以在使用Session对象的run方法调用执行图的时候,传入一些tensor,通过run方法就可以获取这些tensor对应的结果值。
- 如果需要获取多个tensor的值,那么尽量一次运行就获取所有的结果值,而不是采用逐个获取的方式。
4.9 TensorFlow Feed
- Tensorflow还提供了填充机制(feed),可以在构建图时使用placeholder类型的API临时替代任意操作的张量(占位符),在调用Session对象的run()方法去执行图时,使用填充数据作为调用的参数,调用结束后,填充数据就消失了。
- feed使用一个tensor值临时替换一个操作的输出结果,在获取数据的时候必须给定对应的feed数据作为参数。feed只有在调用它的方法内有效,方法结束,feed就消失了。
- feed可以使用placeholder类型的API创建占位符,常见API:tf.placeholder、tf.placeholder_with_default
1 # 构建一个矩阵的乘法,但是矩阵在运行的时候给定 2 m1 = tf.placeholder(dtype=tf.float32, shape=[2, 3], name='placeholder_1') 3 m2 = tf.placeholder(dtype=tf.float32, shape=[3, 2], name='placeholder_2') 4 m3 = tf.matmul(m1, m2) 5 with tf.Session(config=tf.ConfigProto(log_device_placement=True, allow_soft_placement=True)) as sess: 6 print("result: {}".format(sess.run(fetches=m3, feed_dict={m1: [[1, 2, 3], [4, 5, 6]], m2: [[9, 8], [7, 6], [5, 4]]}))) 7 print("result: {}".format(m3.eval(feed_dict={m1: [[1, 2, 3], [4, 5, 6]], m2: [[9, 8], [7, 6], [5, 4]]})))
结果:
result:
[[ 38. 32.]
[101. 86.]]
result:
[[ 38. 32.]
[101. 86.]]
附件:
GitHub:https://github.com/tensorflow/tensorflow
Website:https://tensorflow.org/ or https://tensorflow.google.cn/
尝试操作的网站: