2.1 Tensorflow的计算模型——计算图
根据lesson1中所讲,Tensorflow实际原理是张量的流动,讲述输入的多维数组(张量)通过不同节点进行不同的运算进行转化的一个过程,Tensorflow的每一个计算都是计算图的一个节点,涉及到一次张量的转化。多个节点构成一个神经元。Tensorflow如果不定义计算图,会自动生成一个默认的计算图,并将定义的计算全部加入默认图。
如果说类比的话,计算图就像是我们在面向对象编程里的类(看下述代码可以理解),在一个计算图里,每个数据都像是类中的变量,每个计算节点都像是一个成员函数,两个图里的变量名字可以相同但是在指定运行不同的图时,得到结果根据各自图运算过程而定。
计算图有隔离张量和计算的作用,并对张量和计算进行管理,例如限定计算所使用的GPU等,这个后续学到再说,我现在也不太会😂,而且我前面介绍的安装是CPU版本。自定义图的示例代码如下(python 的缩进很重要,不要漏掉):
import tensorflow as tf
g1 = tf.Graph()
with g1.as_default():
# 在计算图g1中定义变量“v”,初始化为0
v = tf.get_variable("v",initializer=tf.zeros_initializer(shape=[1]))
g2 = tf.Graph()
with g2.as_default():
# 在计算图g2中定义变量“v”,初始化为1
v = tf.get_variable("v",initializer=tf.ones_initializer(shape=[1]))
# 在计算图g1中读取变量“v”的值
with tf.Session(graph = g1) as sess:
tf.initialize_all_variables().run()
with tf.variable_scope("",reuse=True):
# 在计算图g1中,“v”为0,所以下述语句应该输出0
print(sets.run(tf.get_variable("v")))
# 在计算图g2中读取变量“v”的值
with tf.Session(graph = g2) as sess:
tf.initialize_all_variables().run()
with tf.variable_scope("",reuse=True):
# 在计算图g1中,“v”为1,所以下述语句应该输出1
print(sets.run(tf.get_variable("v")))
使用图限定计算用的GPU
g = tf.Graph()
# 指定计算运行的设备
with g.device('/gpu:0'):
result = a + b
2.2 Tensorflow的数据模型——张量
2.2.1 概念
张量是Tensorflow管理数据的形式,可以简单理解为多维数组,不过Tensor的存储和Numpy的多维数组有所不同,Numpy简介参见这里,观察如下代码:
import tensorflow as tf
#. td.constant 是一个计算,个人感觉和申请const变量差不多,不过结果是一个张量,存储在变量a中
a = tf.constant([1.0,2.0],name="a")
b = tf.constant([2.0,3.0],name="b")
c = tf.constant([2.0,3.0])
d = tf.constant([2.0,3.0])
result = tf.add(a,b,name="add")
print(result) # output : Tensor("add:0",shape=(2,),dtype=float32)
print(a) # output: Tensor("a:0", shape=(2,), dtype=float32)
print(c) # output: Tensor("Const:0", shape=(2,), dtype=float32)
print(d) # output: Tensor("Const_1:0", shape=(2,), dtype=float32)
sess = tf.Session()
sess.run(result) # output : array([3., 5.], dtype=float32)
less.run(a) # output : array([1., 2.], dtype=float32)
从上述代码可以发现张量中主要保存了三个属性:名字(name),维度(shape)和类型(type),其中名字遵循“node:src_output”,比如result为add计算节点的第0个输出,所以命名为add:0,因为有的计算节点并不一定输出一个张量,所以src_output为输出序号,node为计算节点名称,tf.constant()和tf.add()均为计算,所以a为a:0,其中节点的名称可以由我们定义,如果我们不自己定义,Tensorflow有自己的默认命名,比如c的"Const:0" 以及d的“Const_1:0”
所以,我感觉Tensor不能单纯的理解为多维数组,它包含了他是从哪个计算节点计算而来的,以及它的大小,我们可以理解为Tensorflow分为两部分工作,第一部分工作是定义计算图,也就是定义张量是如何流动的,但是每一步运算都并没有实质上的运行,张量就是这样而存在的。第二部分是对计算图进行实质上的通过CPU或者GPU进行计算,这就需要运行Session,Session的使用会在后面小节具体描述,从上述代码可以看到运行sess.run()就会输出运算结果
2.2.2 张量的使用
观察下列代码的运行
import tensorflow as tf
a = tf.constant([1,2],name="a")
b = tf.constant([2.0,3.0],name="b")
c = tf.constant([1,2],name="c",dtype=tf.float32)
result = a + b # Get ERROR bellow
# ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)'
result = b+c # no error
对于Tensorflow,如果不声明类型dtype,那么没有小数点默认类型为int32,有小数点默认为float32,这个和我们编程不一样,不存在强制类型转换,需要注意。Tensorflow所支持的数据类型:
- 浮点数(tf.float32、tf.float64)
- 整数(tf.int8、tf.int16、tf.int32、tf.int64、 tf.unit8)
- 布尔型(tf.bool)
- 复数(tf.complex64、tf.complex128)
import tensorflow as tf
a = tf.constant([1.0,2.0],name="a")
b = tf.constant([2.0,3.0],name="b")
result = a + b; # 和tf.add()效果相同,根据需要选择,使用tf.add()可以自定义节点的其他属性,比如计算节点的名字
# 和上述代码效果一样,但可读性差
result_1 = tf.constant([1.0,2.0],name="a") +
tf.constant([2.0,3.0],name="b")
从上述代码可以看到,中间变量a和b只是为了存储中间张量,可以不写,但是为了可读性应该使用类似的中间变量增加代码可读性,优点如下:
- 对于python:由于python的灵活的类型转化无需声明数据类型,设置必要中间变量方便调试
- 对于Tensorflow: 由于有的网络巨大,在设计计算图时需要考虑张量的大小问题,设置中间变量可以方便的获取中间步骤张量的大小,省去人为计算
2.3 Tensorflow运行模型——会话
前面两节介绍了Tensorflow如何组织数据和运算,可以认为是计算图的设计部分,而会话(session)是对前面两个部分设计结果的执行。会话可以管理Tensorflow程序运行时的所有资源。计算完成后需要关闭绘画帮助系统回收资源,可以理解为一个指针,这个指针指向计算图的某个节点,该节点就被激活并执行计算,当所有运算执行完,关闭会话,类似于指针的free。
Tensorflow的会话的使用一般有两种,如下所示,所有计算完成后关闭资源,但是这种方法有一个问题就是当在session.close()执行之前,程序出现异常而退出,那么会造成资源泄漏。不过我喜欢。。😏
# 创建一个会话
sess = tf.Session()
# 使用这个会话得到关心的运算结果,比如上节使用的sess.run(result)
session.run(...)
#关闭会话释放资源
session.close()
还有一种写法如下,不会泄漏资源,但是我感觉写起来很麻烦
# 创建一个会话,通过Python的上下文管理器来管理这个会话。
with tf.Session() as session:
# 使用创建的会话计算关心的结果
session.run(...)
# 不需要调用Session.close()函数
# 当上下文退出后会话关闭并进行资源释放
以上两种介绍的是Session的简单的也是常用的写法,其实会话的使用很灵活,主要表现在默认会话的设置和使用上。Session和计算图Graph类似存在默认会话,在默认的会话下,无需调用sess.run(),使用方法如下所示
sess = tf.Session()
#设置为默认会话
with sess.as_default():
print(result.eval())
同样效果的等价写法还有如下两种:
sess = tf.Session()
# 如下两行等价,均未设置默认会话
print(sess.run(result))
print(result.eval(session=sess))
如果Session设置为每一步计算节点的默认会话,仅需调用运算节点的eval()成员函数即可得到运算结果,Tensorflow提供一个将自动生成的会话直接注册为默认会话的函数,作用于全局,用法如下:
# 生成一个注册为默认会话的会话sess
sess = tf.InteractiveSession()
print(result.eval())
sess.close()
至此,Tensorflow的基本思想已经简单介绍完毕,下一部分将根据一些网络的设计和使用展开对Tensorflow展开进阶介绍