(代码基于tensorflow 1.14 cpu版本,读者需要具有良好的python基础线性代数知识)

第三章主要介绍TensorFlow计算模型数据模型运行模型,对TensorFlow的工作原理能有一个大致了解。

TensorFlow程序一般分为两个阶段。第一阶段:定义计算图中所有的计算,然后定义一个计算来得到他们的和;第二阶段:执行计算。

1 计算图————TensorFlow的计算模型

1.1 计算图的概念

TensorFlow这个单词由两部分组成:tensor代表张量,是数据模型;flow代表流,是计算模型。下面就引出Flow的具体内涵。

流动的事务具有有向性,计算图是一个具有 “每一个节点都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系” 性质的有向图。(计算图和数据结构这门课程中的有向图具有高度的相似性,现在可以回忆一下这段记忆)

计算图由边(表示依赖关系,又被叫做“张量”)和节点(又被叫做“神经元”、“算子”)组成。如果tensorflow程序只定义了一个Graph,那其内部包含了所有的op和tensor

1.2 计算图的使用

在TensorFlow程序中,系统会自动维护一个默认的计算图,通过tf.get_default_graph()函数可以获取这个默认的计算图。

import tensorflow as tf

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([1.0, 2.0], name='b')

result = a + b

#通过a.graph属性可以查看这个张量所属的计算图。因为没有特意指定,所以这个计算图应该等于当前默认的计算图
print(a.graph is tf.get_default_graph())

'''
输出>>>
True
'''
Tensorflow通过计算图把张量和算子等组合在一起,而在很多TensorFlow程序中,看不到Graph,这是为何?这是因为TensorFlow有个默认缺省的graph
(即Graph.as_default()),我们添加的tensor和op等都会自动添加到这个缺省计算图中,如果没有特别要求,使用这个默认缺省的Graph即可。当然,如果需要一些更复杂的计算,比如需要创建两个相互之间没有交互的模型,就需要自定义计算图。

除了使用默认的计算图,TensorFlow支持通过tf.Graph函数来生成新的计算图。TensorFlow中的计算图不仅可以隔绝张量和计算,它还提供了管理张量和计算的机制不同计算图上的张量和运算不会共享
以下代码示意了如何通过在不同的计算图上定义和使用变量。

import tensorflow as tf

g1 = tf.Graph()
with g1.as_default():
    v = tf.get_variable('v', shape=[1], initializer=tf.zeros_initializer)

g2 = tf.Graph()
with g2.as_default():
    v = tf.get_variable('v', shape=[1], initializer=tf.ones_initializer)
'''
tf.Graph()没有实现__enter__()方法,做不到下面那优雅pythonic的写法。
with tf.Graph() as g2:
    v = tf.get_variable('v', shape=[1], initializer=tf.ones_initializer)
    
这样写了就会抛出AttributeError异常

Traceback (most recent call last):
  File "D:/bb/TensorFlow/Untitled1.py", line 7, in <module>
    with tf.Graph() as g2:
AttributeError: __enter__
'''

with tf.Session(graph=g1) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope("", reuse=True):
        print(sess.run(tf.get_variable('v')))

with tf.Session(graph=g2) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope("", reuse=True):
        print(sess.run(tf.get_variable('v')))
'''
输出>>>
[0.]
[1.]
注:先不要管代码是什么含义,继续往下看
'''

如果有多个Graph,建议不使用默认的Graph,直接无视或者为其分配一个句柄,避免发生混乱

import tensorflow as tf

graph1 = tf.Graph()
graph2 = tf.Graph()  # 直接无视默认缺省的Graph
# graph2 = tf.get_default_graph() 为其分配一个句柄

with graph1.as_default():
    pass

with graph2.as_default():
    pass

2 张量————TensorFlow数据类型

2.1 张量的概念

在TensorFlow的程序中,所有的数据都通过张量(tensor)的形式表示。

张量(tensor)理解为多维数组(multidimensional array),0阶张量是标量(scalar),1阶是向量(vector)(即一维数组),2阶是二维数组,n阶为n维数组。

TensorFlow的运算结果不是一个数,而是一个张量结构。(运算和运行在tensorflow语境中不同,运算就是用过运算符操作,运行就和会话有关,3.3会提到)

import tensorflow as tf

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([1.0, 2.0], name='b')

result = tf.add(a, b, name='add')

print(result)
'''
输出>>>
Tensor("add:0", shape=(2,), dtype=float32)
'''

一个张量保存了三个属性:名字(name)、维度(shape)和类型(type)

  1. 名字name一个张量的唯一标识符以及这个张量是如何计算出来的。

张量的命名形式:“node:src_output”node为节点的名称,src_output表示当前张量来自来自节点的第几个输出。
比如上面的代码的输出"add:0"说明张量result是计算节点“add”输出的第一个结果(编号从0开始)

张量和计算图上节点的计算结果是一一对应的。

  1. 维度shape:描述了一个张量的维度信息。

上面样例的shape(2,)表示一个一维数组,这个数组的长度是2。

  1. 类型type:每一个张量都有一个唯一的类型,常用的是tf.int32,tf.float32。参与运算的张量需要保持数据类型相同,不然会报错。
TensorFlow支持的14种数据类型
有符号整型
tf.int8:8位整数
tf.int16:16位整数
tf.int32:32位整数
tf.int64:64位整数
无符号整型
tf.uint8:8位无符号整数
tf.uint16:16位无符号整数
浮点型
tf.float16:16位浮点数
tf.float32:32位浮点数
tf.float64:64位浮点数
tf.double:等同于tf.float64
字符串型
tf.string:字符串
布尔型
tf.bool:布尔型
复数型
tf.complex64:64位复数
tf.complex128:128位复数

TensorFlow数据类型和Python原生数据类型的关系

TensorFlow接受了Python自己的原生数据类型,例如Python中的布尔值类型,数值数据类型(整数,浮点数)和字符串类型。单一值将转换为0维张量(标量),列表值将转换为1维张量(向量),列表套列表将被转换成2维张量(矩阵)

TensorFlow数据类型和Numpy数据类型的关系

你可能已经注意到了Numpy和TensorFlow有很多相似之处。TensorFlow在设计之初就希望能够与Numpy有着很好的集成效果。Numpy软件包现在已经成为数据科学的通用语言。

TensorFlow数据类型很多也是基于Numpy的,事实上,如果你令 np.int32==tf.int32将会返回True.你也可以直接传递Numpy数据类型直接给TensorFlow中的ops。

tf.ones([2, 2], np.float32) ==> [[1.0 1.0], [1.0 1.0]]

请记得,我们的好朋友会话tf.Session.run(),要求的输入对象是一个Tensor但是它的输出是一个Numpy数组。事实上,在绝大多数场合,你可以同时混合使用TensorFlow类型和Numpy类型。

2.2 张量的使用

张量使用可以归结为两大类。

1.第一类对中间计算结果的引用,提升代码的可读性

使用张量和不使用张量的对比

import tensorflow as tf

#使用张量记录
a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([1.0, 2.0], name='b')
result1 = a + b

#不使用张量,简洁但是可读性降低
result2 = tf.constant([1.0, 2.0], name='a') + tf.constant([1.0, 2.0], name='b')

#jie'guo'xiang't
print(result1)
print(result2)

'''
输出>>>
Tensor("add:0", shape=(2,), dtype=float32)
Tensor("add_1:0", shape=(2,), dtype=float32)
'''

2.第二类情况是计算图构造完成后,张量可以用来获取计算结果
使用tf.Session().run(result)语句可以得到计算结果

3 会话————TensorFlow运行模型

3.1会话的概念

计算图描述计算
张量是组织数据
会话执行定义好的运算

会话管理TensorFlow程序运行时的所有资源,并在运行结束后释放所有的资源。

会话机制类似于计算图机制:
计算图:在一开始就有一个默认的计算图存在,并且没有特别指定,运算会自动加入到这个默认的计算图中
会话:会要手动创建,张量自动添加。

3.2会话的使用

TensorFlow使用会话模式有两种方式

1.普通模式

import tensorflow as tf

# 加入了一个异常处理机制,确保释放资源
try:
    sess = tf.Session()  # 创建一个会话
    sess.run(...)  # 运行,得到计算结果
except Exception:
    pass
finally:
    sess.close()  # 关闭会话,释放资源

2.上下文模式
这才是pythonic的写法,极力推荐

import tensorflow as tf

with tf.Session as sess:
    sess.run()
#运行到这个位置会自动释放sess的资源,优雅尼克

这是2.2程序的引用,会话的写法多种多样,我们应用最优雅、最pythinc的代码去阐述。

import tensorflow as tf

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([1.0, 2.0], name='b')

result = tf.add(a, b, name='add')

with tf.Session() as sess:
    # tf.Session.run(),要求的输入对象是一个Tensor但是它的输出是一个Numpy数组
    print(sess.run(result))
'''
输出>>>
[2. 4.]
'''

4 其他基本概念——常量、变量、占位符

4.1 常量

Python中使用常量很简单,如a=123,b='python'。TensorFlow表示常量稍微麻烦一点,需要使用tf.constant这个类,具体格式如下:

tf.constant(value, dtype=None, shape=None, name="Const", verify_shape=False)

其中各参数说明如下:

  • value : 一个dtype类型(如果指定了)的常量值(列表)。要注意的是,若value是一个列表,那么列表的长度不能够超过形状参数指定的大小(如果指定了)。如果列表长度小于指定的大小,那么多余的空间由列表的最后一个元素来填充。
  • dtype : 返回的tensor的类型
  • shape : 返回的tensorflow的形状
  • name : tensor的名字
  • verify_shape : 布尔值,用于验证值的形状。

示例如下:

import tensorflow as tf

# 构建计算图
a = tf.constant('something', name='a')

print(a)

with tf.Session() as sess:  # 创造会话
    result = sess.run(a)  # 在会话中执行张量a
    print(result)

'''
输出>>>
Tensor("a:0", shape=(2,), dtype=float32)
b'something'
'''

4.2 变量

变量是TensorFlow中的核心概念,创建一个类使用tf.Variable,具体格式如下:

tf.Variable(initial_value=None,trainable=None,collections=None,validate_shape=True,caching_device=None,name=None,variable_def=None,dtype=None,expected_shape=None,import_scope=None,constraint=None,use_resource=None,synchronization=VariableSynchronization.AUTO, aggregation=VariableAggregation.NONE,shape=None)

主要参数说明

  • initial_value一个 Tensor类型或者是能够转化为Tensor的python对象类型。它是这个变量的初始值。这个初始值必须指定形状信息,不然后面的参数validate_shape需要设置为False。当然,也能够传入一个无参数可调用并且返回指定初始值的对象,在这种情况下,dtype必须指定。
  • trainable :如果设置为True (默认也为True),这个变量可以被优化器类(optimizer)自动修改Variable的值;如果设置为False,则说明Variable 只能手工修改,不允许使用优化器类自动修改。
  • collections :图的collection键列表,新的变量被添加到这些collection 中。默认是
  • validate_shape :如果是False的话,就允许变量能够被一个形状未知的值初始化,默认是True,表示必须知道形状。
  • caching_device :可选,描述设备的字符串,表示哪个设备用来为读取缓存。默认是变量的device。
  • name :可选,变量的名称。
  • dtype如果被设置,初始化的值就会按照这里的类型来定。

4.2.1.创建变量

1.现在让我们使用tf.Variable()创建变量,这是最简单的、最常用的变量创建方法。用的最多的两个参数initial_value和name

import tensorflow as tf

girl = tf.Variable('安静宇', name='big')  # 这个name大有作用,后面会讲到,需要注意的是,name的值不可以是中文,中文会报错

god = tf.Variable('安静宇', name='big')  # 使用tf.Variable()创建的实例,实例名是唯一标识符( Variable()内的参数都可以重复 )
# 写作girl = tf.Variable('安静宇', name='big'),就会覆盖第一个girl

print('initializer', girl.initializer)
print('initial_value', girl.initial_value)
print('graph', girl.graph)
print('shape', girl.shape)
print('name', girl.name)
print('dtype', girl.dtype)
print('value', girl.value())

'''
输出>>>
initializer name: "big/Assign"
op: "Assign"
input: "big"
input: "big/initial_value"
attr {
  key: "T"
  value {
    type: DT_STRING
  }
}
attr {
  key: "_class"
  value {
    list {
      s: "loc:@big"
    }
  }
}
attr {
  key: "use_locking"
  value {
    b: true
  }
}
attr {
  key: "validate_shape"
  value {
    b: true
  }
}

initial_value Tensor("big/initial_value:0", shape=(), dtype=string)
graph <tensorflow.python.framework.ops.Graph object at 0x000001DF1B542EF0>
shape ()
name big:0
dtype <dtype: 'string_ref'>
value Tensor("big/read:0", shape=(), dtype=string)
'''

2.除了使用tf.Variable()创建变量,还有一个孪生兄弟,tf.get_variable(),他两的业务逻辑还是很不一样的,使用的唯一标识符不一样,前者使用实例名作为唯一标志,后者使用name参数作为唯一标志。

具体用法可以参考这篇博客Variable和get_variable的用法以及区别

按照习惯,get开头的函数都是“查询”、“获取”的意思,但是tf.get_variable()是创建变量,get取的是“创建”的意思

4.2.2.初始化变量

tensorflow中,程序存在变量,在使用前必须初始化

初始化变量有两个方法

  • 一个一个手动初始化
  • 一次性全局初始化

手动初始化就是

ses.run(var1.initializer)
ses.run(var2.initializer)
ses.run(var3.initializer)

一次性全局初始化只要使用一个函数 tf.global_variables_initializer()

init_op = tf.global_variables_initializer()
sess.run(init_op)

贴一个完整代码

import tensorflow as tf

girl = tf.Variable('安静宇', name='cute')

god = tf.Variable('安静宇', name='pretty')

with tf.Session() as sess:
    # 必须先初始化变量才能使用,tf.global_variables_initializer()可以初始化所有的变量
    sess.run(girl.initializer)
    sess.run(god.initializer)
    # 等价于 sess.run(tf.global_variables_initializer())
    print(sess.run(girl).decode())
    print(sess.run(god).decode())

'''
输出>>>
安静宇
安静宇
'''

4.2.3.保存和恢复变量

最后给你们看看安静宇
安静宇


universe_king
3.4k 声望680 粉丝