从TensorFlow这个名字中,我们可以发现,tensor(张量),flow(流),在TensorFlow中两个最重要的概念,一个TensorFlow程序主要是由计算图、张量以及模型回话三个部分组成。
一、计算图
一个使用TensorFlow编写的程序主要分为两个部分,一个是构建计算图部分,一个是执行计算图。下面,我来构建一个非常简单的计算图。
-
import tensorflow as tf
-
if __name__ == "__main__":
-
a = tf.constant([1.0,2.0],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = a + b
在上面的代码中,TensorFlow会自动将定义的计算转化成计算图上的节点,系统还会自动维护一个默认的计算图。我们可以通过下面的代码来获取当前默认的计算图
-
#通过a.graph来获取当前节点所属的计算图
-
print(a.graph)
扫描二维码关注公众号,回复: 5516484 查看本文章 -
# <tensorflow.python.framework.ops.Graph object at 0x000001E15BDEC908>
-
#判断当前的张量是不是属于默认的计算图
-
print(a.graph is tf.get_default_graph())
-
# True
TensorFlow提供了tf.Graph()方法来产生一个新的计算图,在不同的计算图中张量不会共享。
-
g1 = tf.Graph()
-
#将计算图g1设置为默认计算图
-
with g1.as_default():
-
# 在计算图个g1中定义变量c,并将变量c初始化为0
-
c = tf.get_variable("c",initializer=tf.zeros_initializer,shape=(1))
-
#定义第二个计算图
-
g2 = tf.Graph()
-
#将计算图g2设置为默认计算图
-
with g2.as_default():
-
# 在计算图g2中定义变量c,并将变量c初始为1
-
c = tf.get_variable("c",initializer=tf.ones_initializer,shape=(1))
-
#在计算图g1中读取变量c
-
with tf.Session(graph=g1) as sess:
-
# 初始化变量
-
tf.initialize_all_variables().run()
-
with tf.variable_scope("",reuse=True):
-
# 在计算图g1中,定义变量c为0
-
print(sess.run(tf.get_variable("c")))
-
#[ 0.]
-
#在计算图g2中读取变量c
-
with tf.Session(graph=g2) as sess:
-
#初始化变量
-
tf.initialize_all_variables().run()
-
with tf.variable_scope("",reuse=True):
-
# 在计算图g2中定义变量c为1
-
print(sess.run(tf.get_variable("c")))
-
#[ 1.]
分别在计算图g1和g2中都定义张量c,在g1中初始化为0,在g2中初始化为1,从上面的代码可以看出,当我们运行不同的计算图的时候张量c的值是不一样的。所以,在TensorFlow中可以通过计算图来隔离张量的运算,除此之外,TensorFlow还为计算图提供了管理张量的机制,我们可以设置运行是在GPU上进行还是CPU,通过设置使用GPU可以加速运行,需要电脑上有gpu
-
g = tf.Graph()
-
#指定计算图g在gpu 0(电脑上有多个gpu,需要指定)上运行
-
with g.device("/gpu:0"):
-
result = a + b
二、张量
在TensorFlow中,可以将张量理解为数组。如果是0阶张量,那么将代表这个张量是一个标量,也就是一个数字,如果是一阶张量可以理解为向量或者是一维数组,n阶张量可以理解为n维的数组。但,TensorFlow张量的实现并没有直接采用数组的形式,张量它只是对运算结果的引用,从下面的例子中可以发现,TensorFlow的张量和numpy的数组是不一样的,TensorFlow的计算结果不是一个数组而是一个张量的结构形式,在这个张量中,它包含了三个重要的属性,名字、维度、类型。
-
import tensorflow as tf
-
import numpy as np
-
if __name__ == "__main__":
-
a = tf.constant([1.0,2.0],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = tf.add(a,b,name="add")
-
print(result)
-
# Tensor("add:0", shape=(2,), dtype=float32)
-
np_a = np.array([1.0,2.0])
-
np_b = np.array([2.0,3.0])
-
np_result = np_a + np_b
-
print(np_result)
-
# [ 3. 5.]
张量的名字,是张量的唯一标识符,通过名字可以发现张量是如何计算出来的。计算图中的每一个节点都代表了一个计算,而计算的结果就保存在张量之中,张量和计算图上的节点所代表的计算结果是对应的。“add:0”代表的是计算节点"add"的第一个输出结果(编号都是从0开始)。
张量的维度,说明了张量的维度信息,如shape=(2,)代表张量是一个二维数组。
张量的类型,说明了张量的数据类型,每一个张量都会有一个对应的数据类型。在TensorFlow的计算中,先会对参与运算的所有张量进行类型检测,如果发现张量的类型不匹配的时候就会报错。
-
a = tf.constant([1,2],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = tf.add(a,b,name="add")
-
print(result)
运行上面的代码的时候,会报类型不匹配的错误:TypeError: Input 'y' of 'Add' Op has type float32 that does not match type int32 of argument 'x'.从错误提示中可以发现,float32与int32类型不匹配,在定义张量a和b的时候,我们并没有指定类型,所以是按默认类型指定的,[1,2]默认指定的是int32,而[2.0,3.0]指定的是float32。解决这种错误有两个方法,目的就是保证类型一致就可以了。
第一种方法,指定张量a的类型
a = tf.constant([1,2],name="a",dtype=tf.float32)
第二种方法,加小数点
a = tf.constant([1.,2.],name="a")
TensorFlow一共支持14种不同的类型,主要包括实数(tf.float32、tf.float64)、整数(tf.int8、tf.int32、tf.int64、tf.uint8)、布尔型(tf.bool)和复数(tf.complex64、tf.complex128)。
TensorFlow张量的使用主要包括两大类,第一大类当使用TensorFlow计算的时候,有许多的中间变量可以通过张量来提高代码的可读性。第二类就是当计算图构造完成之后,可以通过张量来获取计算的结果,需要配合使用Session。
三、Session(会话)
会话:用来执行定义好的运算,而且会话拥有和管理程序运行时的所有资源。当计算完成之后,需要通过关闭会话来帮助系统回收资源,否则可能导致资源泄露的问题。在TensorFlow中使用会话有两种方式。第一种需要明确调用会话生成函数和关闭会话函数
-
a = tf.constant([1.,2.],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = tf.add(a,b,name="add")
-
# 创建session
-
sess = tf.Session()
-
#获取运算结果
-
sess.run(result)
-
#关闭会话,释放资源
-
sess.close()
在使用这种模式的时候,需要明确的调用sess.close()来关闭会话释放资源。如果,当程序因为异常而退出的时候,sess.close()有可能不会执行从而导致资源泄露问题的发生。为了解决这种情况,TensorFlow提供了通过python的上下文管理器来使用会话,也就是第二种方式
-
#通过python的上下文管理器来创建Session,管理会话
-
#通过上下文管理来管理会话,不需要调用sess.close()来关闭会话
-
#当上下文退出的时候会话会自动关闭和释放资源
-
with tf.Session() as sess:
-
sess.run(result)
TensorFlow也还提供了一种类似于默认计算图一样的默认会话功能,但与默认计算图不同的是,默认会话不会自动创建,它需要手动自动。
-
a = tf.constant([1.,2.],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = tf.add(a,b,name="add")
-
#创建一个会话
-
sess = tf.Session()
-
#设置默认会话
-
with sess.as_default():
-
#获取张量result的值
-
print(result.eval())
-
#[ 3. 5.]
如果不设置默认会话,下面的代码也可以完成同样的功能
-
#创建一个会话
-
sess = tf.Session()
-
sess.run(result)
-
print(result.eval(session=sess))
-
#[ 3. 5.]
TensorFlow还提供在交互式环境下,直接将创建的会话设置为默认会话
-
a = tf.constant([1.,2.],name="a")
-
b = tf.constant([2.0,3.0],name="b")
-
result = tf.add(a,b,name="add")
-
#创建一个默认会话
-
sess = tf.InteractiveSession()
-
print(result.eval())
-
#[ 3. 5.]
除此之外,TensorFlow也还提供了一种通过ConfigProto来配置生成的会话。
-
config = tf.ConfigProto(allow_soft_placement=True,log_device_placement=True)
-
sess1 = tf.InteractiveSession(config=config)
-
sess2 = tf.Session(config=config)
通过ConfigProto()函数,可以配置会话并行的线程数、GPU的分配策略、运算超时等参数。在这些参数中,最常用的就是上面配置的两个参数,第一个就是allow_soft_placement,这是一个bool类型的参数,当它设置为True的时候,满足下面的任意一个条件的时候,GPU上的运算可以放到CPU上进行:
1、运算无法在GPU上执行。
2、没有GPU资源(电脑上不存在GPU设备或者,指定程序在第二个GPU上运行,但是机器只有一个GPU)。
3、运算输入包含对CPU计算结果的引用。
allow_soft_placement参数默认设置为False,为了增强代码的可移植性,在有GPU的情况下一般都会将其设置为True。即使当前设置无法支持GPU的时候,也可以自动在CPU上运行。第二个参数log_device_placement,也是一个bool类型的参数,当它设置为True的时候日志将会记录每个节点被安排了在哪个设备上运行。一般,在生产环境中将这个参数设置为False,以减少日志的输出。