第9章Tensorflow基础
Tensorflow的系统架构和数据流程图的介绍能够方便读者先对Tensorflow运行的基础有所了解,然后引出Tensorflow程序设计中的基本概念,包括张量(Tensor),算子(Operation),变量(Variables),占位符(Place Holder),图(Graph)等,最后介绍如何在Tensorflow可视化数据流程图和Tensorflow的分布式运行。
9.1 Tensorflow系统结构
https://www.tensorflow.org/extend/architecture
TensorFlow的系统结构如下图所示,中间部分是C的API接口,上面是可编程的客户端,下面是后端执行系统。客户端系统提供多语言支持的编程模型,负责构造计算图。后端执行系统是TensorFlow的运行时系统,主要负责计算图的执行过程,包括计算图的剪枝,设备分配,子图计算等。
图9.1 Tensorflow系统结构
下面分别介绍Tensorflow系统结构的核心组件:
Client是前端系统的主要组成部分,它是一个支持多语言的编程环境。它提供基于计算图的编程模型,方便用户构造各种复杂的计算图,实现各种形式的模型设计。
Distributed Master从计算图中反向遍历,找到所依赖的最小子图,再把最小子图分割成子图片段派发给Worker Service。随后Worker Service启动子图片段的执行过程。
Worker Service 可以管理多个设备。Worker Service将按照从Distributed Master接收的子图在设备上调用的Kernel实现完成运算,并发送结果给其他Work Service,以及接受其他Worker Service的运算结果。
Kernel是Operation在不同硬件设备的运行和实现,它负责执行具体的运算。
图9.2 Tensorflow运行机制
如上图,客户端启动Session并把定义号的数据流程图传给执行层,Distributed Master进程拆解最小子图分发给Worker Service,Worker Service调用跨设备的Kernel的Operation,利用各个的资源完成运算。
9.2数据流程图
Tensorflow的特点之一就是用数据流程图定义程序,本节会介绍数据流程图的基本知识。数据流程图用来定义数据在链接在一起的函数中运算,由节点(Node),边(Edge)来确定数据运算的路径图,每个函数会把输出传递给后续函数。在数据流程图的语境中,节点通常以圆圈、椭圆和方框表示,代表了对数据所做的运算或某种算子(Operation)。边对应于向算子传入和从算子传出的实际数值,通常以箭头表示。可从概念上将边视为不同算子之间的连接,因为它们将控制信息流动走向,让信息从一个节点传输到另一个节点。
图9.3 数据流程图
首先上图Data Flow1由节点a, b, c, d, e 和相应的边组成,有两个输入和一个输出,定义了以下计算:
当时,Data Flow 1就可以完成计算 .
如果我们把 Data Flow 1整体视为一个构件,那么其他的数据流程图也可以成为它的输入或输出,在可视化的时候可以把每个数据流程图内部的运算隐藏起来,从而能更容易展示运算的结构链路。
9.3 Tensorflow基本概念
Abadi M, Agarwal A, Barham P, et al. Tensorflow: Large-scale machine learning on heterogeneous distributed systems[J]. arXiv preprint arXiv:1603.04467, 2016.
TensorFlow就是采用数据流图程(Data Flow Graphs)对张量来进行计算。让我们先Tensorflow的几个基本概念做简单介绍,包括张量(Tensor),算子(Operation),会话(Session),变量(Variables),占位符(Place Holder)和图(Graph),图的部分将在下一小节介绍。
名副其实,TensorFlow,就是张量的流动。张量(Tensor),即任意维度的数据,张量有多种. 零阶张量为纯量或标量 (scalar) 也就是一个数值,比如 [1];一阶张量为向量 (Vector), 比如 一维的 [1, 2, 3];二阶张量为矩阵 (Matrix), 比如二维的 [[1, 2, 3],[4, 5, 6],[7, 8, 9]]; 以此类推。另外张量对象用形状(Shape)属性来定义结构, Python中的元组和列表都可以定义张量的形状。张量每一维可以是固定长度,也可以用None表示为可变长度。
###指定0阶张量的形状,可以是任意整数,如1, 3, 5, 7等t_list=[]t_tuple=()###指定一个长度为2的向量,例如[2,3]t_1=[2]###指定一个2*3矩阵的形状##例如 [[1,2,3],[4,5,6]]t_2=(2, 3)###示任意长度的向量t_3=[None]###表示行数任意列数为2的矩阵的形状t_4=(None, 2)###表示第一维长度为3,第二维长度为2,第三维长度任意的3阶张量t_5=[3, 2, None]
[/cc]
Tensorflow和Numpy有很好的兼容,Tensorflow的数据类型是基于Numpy的数据类型的。Tensorflow支持的数据类型如下表:
数据类型 | 描述 |
tf.float32 | 32 位浮点数 |
tf.float64 | 64 位浮点数 |
tf.int64 | 64 位有符号整型 |
tf.int32 | 32 位有符号整型 |
tf.int16 | 16 位有符号整型 |
tf.int8 | 8 位有符号整型 |
tf.uint8 | 8 位无符号整型 |
tf.string | 可变长度的字节数组.每一个张量元素都是一个字节数组 |
tf.bool | 布尔型 |
tf.complex64 | 由两个32位浮点数组成的复数:实数和虚数 |
tf.qint32 | 用于量化Ops的32位有符号整型 |
tf.qint8 | 用于量化Ops的8位有符号整型 |
tf.quint8 | 用于量化Ops的8位无符号整型 |
表9.1 Tensorflow数据类型
对张量进行计算的函数叫算子,算子是不变的,但流动的张量会经函数运算而发生改变。TensorFlow的运行时包括数值计算,多维数组操作,控制流,状态管理等。
(Tensorflow支持的算子类型总结)
客户端使用会话来和执行系统交互,主要包括建立会话和执行数据流图。Tf.session()用来启动会话,Session.run()用来执行会话或初始化变量,返回的张量为Numpy数组。
###引入tensorflow和 numpyimport tensorflow as tfimport numpy as np###定义张量t_1为标量,常数 2t_1=tf.constant(2)###定义t_2 张量为2*1阶矩阵t_2=np.array([[2],[4]],dtype=np.int32)###定义 1*2 阶矩阵,数据类型为int32t_3=np.array([[6,8]],dtype=np.int32)###定义2*1阶矩阵,数据类型为int32t_4=np.array([[1],[3]],dtype=np.int32)###定义对张量 t_2 和t_3做矩阵乘法算子的运算t_5=tf.multiply(t_2,t_3)###定义对张量 t_2和t_4做矩阵加法算子的运算t_6=tf.add(t_2,t_4)###建立会话sess=tf.Session()###用Session.run()执行运算并显示结果print("t_5 :",sess.run(t_5))print ("t_6 :",sess.run(t_6))
以上实例运行结果为:
('t_5 :', array([[12, 16], [24, 32]], dtype=int32))('t_6 :', array([[3], [7]], dtype=int32))
Tensorflow中的变量用来保持运算中Tensor的句柄,但与Python不同的是必须要明确定义为变量,并在完成初始化后才能正常运算。Variable对象初值为全0,全1或随机数填的张量。Tensorflow提供了一些Operaiton来方便变量初值的定义,如tf.zeros()、 tf.ones()、 tf.random_normal ()和 tf.random_uniform ()tf.truncated_normal ()
###引入Tensorflowimport tensorflow as tf###定义变量counter,指定初始值为0counter = tf.Variable(0, name="counter")###定义算子add_one完成加1操作 add_one = tf.add(counter, 1)###加1的结果再赋值给变量counterupdate = tf.assign(counter, add_one)###定义会话with tf.Session() as sess:###初始化变量,此时才完成对counter赋值为0的操作sess.run(tf.global_variables_initializer())###循环执行3次counter加1并再赋值给变量counter的运算并每次都打印结果for _ in range(3): sess.run(update) print(sess.run(counter))###关闭会话sess.close()
以上代码的运行结果为:
123
在运行时指定张量对象的变量叫占位符(Place holder)。在调用Session.run()时,需要由feed_dict来给占位符传入Tensor对象。
###引入Tensorflow和Numpyimport tensorflow as tfimport numpy as np###定义张量x为3*1阶矩阵x = tf.placeholder(dtype=np.float32, shape=[3, 1])###定义张量y为1*3阶矩阵y = tf.placeholder(dtype=np.float32, shape=[1, 3])###定义张量z为矩阵x乘以矩阵yz = tf.matmul(x, y)###定义会话with tf.Session() as sess:###在运算时给x和y传入值,完成运算z_output = sess.run(z, feed_dict={x: [[1],[2],[3]], y:[[1,2,3]]})###打印运算结果print(z_output)###关闭会话sess.close()
以上代码的运行结果为:
[ [1. 2. 3.] [ 2. 4. 6.] [ 3. 6. 9.]]
9.4 TensorFlow实现数据流程图
如数据流图的部分介绍,如果把边当作张量把结点当作算子,那么数据流图就Tensorflow中把张量,算子,变量等元素定义成链接在一起的函数运算。以下代码就是用Tensorflow定义了图9.3的数据流程图,完成了对应的运算。算子的name属性可以帮助标识
###引入Tensorflowimport tensorflow as tf###定义算子a=tf.constant(2)b=tf.constant(4)c=tf.multiply(a,b)d=tf.add(a,b)e=tf.add(c,d)###定义会话sess=tf.Session()#会话调用运算output=sess.run(e)#打印运算结果print(output)###关闭会话sess.close()
以上代码运行结果为:
14
9.5可视化数据流程图
会话会默认初始化一个图对象,可以通过tf.get_default_graph()引用,用户也可以自定义新的Graph (tf.Graph())。如果给算子定义名称,并把生成的数据流程图对象写入log文件,那么最终Tensorboard Sever可以将数据流程图以可视化的形式展现出来。还是以上一小节的实例为基础,代码稍作修改就可以实现数据流程图的可视化。
###引入Tensorflowimport tensorflow as tf###定义算子及算子名称a=tf.constant(2,name="input_a")b=tf.constant(4,name="input_b")c=tf.multiply(a,b,name="mul_c")d=tf.add(a,b,name="add_d")e=tf.add(c,d,name="add_e")sess=tf.Session()output=sess.run(e)print(output)###将数据流程图写入log文件writer=tf.summary.FileWriter('home/feigu/tmp',sess.graph)writer.close()sess.close()
在执行完以上代码,还需要启动 Tensorboard Server来查看数据流图。启动命令为 Tensorflow –logdir=”folder of log”,以下是实例:
然后在浏览器中进入Torsenboard( http://localhost:6006),点击进入GRAPHS就可以看到以上程序实例的可视化的数据流程图,如下图所示:
9.4 数据流图实例
现实中的模型运算会更复杂,需要对图中的运算进行封装来获得更好的可视化, Tensorflow采用作用域(name space) 来组织运算的封装。
import tensorflow as tfgraph=tf.Graph()with graph.as_default(): in_1=tf.placeholder(tf.float32, shape=[], name="input_a") in_2=tf.placeholder(tf.float32, shape=[], name="input_b") const=tf.constant(3, dtype=tf.float32, name="static_value") with tf.name_scope("Transformation"): with tf.name_scope("A"): A_mul=tf.multiply(in_1, const) A_out=tf.subtract(A_mul, in_1) with tf.name_scope("B"): B_mul=tf.multiply(in_2, const) B_out=tf.subtract(B_mul, in_2) with tf.name_scope("C"): C_div=tf.div(A_out, B_out) C_out=tf.add(C_div, const) with tf.name_scope("D"): D_div=tf.div(B_out, A_out) D_out=tf.add(D_div, const) out=tf.maximum(C_out, D_out)writer=tf.summary.FileWriter('home/feigu/tmp', graph=graph)writer.close()
执行完以上代码,在Tensorboard Server启动的情况下,在浏览器中进入 http://localhost:6006,点击进入GRAPHS可以看到如下定义的数据流图:
图9.5 数据流图name space实例
9.6 分布式TensorFlow
### 加载图像
### 图像格式
### 把图像转换为TFRecord文件
### 读取文件
### 图像处理实例