对象检测
首发于对象检测
TensorFlow 中的几个关键概念:Tensor,Operation,Graph,Session

TensorFlow 中的几个关键概念:Tensor,Operation,Graph,Session

前言:TensorFlow是一种符号式编程框架,首先要构造一个图(graph),然后在这个图上做运算。打个比方,graph就像一条生产线,session就像生产者。生产线具有一系列的加工步骤(加减乘除等运算),生产者把原料投进去,就能得到产品。不同生产者都可以使用这条生产线,只要他们的加工步骤是一样的就行。同样的,一个graph可以供多个session使用,而一个session不一定需要使用graph的全部,可以只使用其中的一部分。

一、Tensorflow的工作流程

  1. 根据需求,创建计算图Graph
  2. 开启会话Session,读取数据运行Graph
  3. 获取结果

二、TensorFlow 几个关键概念:Tensor,Operation,Graph,Session

1.Tensor(数据节点)

(1)Tensor定义

在 TensorFlow 中,所有在节点之间传递的数据都为 Tensor 对象。

Tensor定义:A Tensor is a symbolic handle to one of the outputs of an Operation. It does not hold the values of that operation’s output, but instead provides a means of computing those values in a TensorFlow tf.Session

也就是说,Tensor本身是不存储数据的,创建一个Tensor实际就是声明了一个数据节点。只有开启Session进行运算的时候,才会获取到数据。

(2)Tensor的阶

如下图所示,Rank为0、1、2时分别称为标量、向量和矩阵,Rank为3时是3阶张量,Rank大于3时是N阶张量。这些标量、向量、矩阵和张量里的元素可以是数组,可以是tensor或者其他python基本数据类型。当所有元素类型一致且均为tensor(数组)时,则可将Rank的阶数当做tensor(数组)的阶数。通常我们讨论的数据类型是指元素的数据类型。

Tensor的阶数

(3)Tensor的几个重要属性

shape:类似于Numpy中ndarray.shape,比方说一个2行3列的二维矩阵,他的形状就是2行3列。

dtype:类似于Numpy中ndarray.dtype,常用的类型有:

tf.uint8: 8-bit unsigned integer.
tf.int32: 32-bit signed integer.
tf.int64: 64-bit signed integer.
tf.String: String.
tf.float32: 32-bit single-precision floating-point.
tf.float64: 64-bit double-precision floating-point.

name:每个Tensor都必须有name属性。在创建Tensor时,如果用户没有指定name,Tensorflow会自动设置;在同一张Graph中,不会有Tensor重名,当用户设定的name重名时,Tensorlfow会自动加入后缀进行区分。

(4)几种Tensor

  • 常量Tensor:值不能改变,最常见的常量创建方式为tf.constant(value, dtype=None, shape=None, name="Const", verify_shape=False),其中value不可少,verify_shape表示常量的形状是否可以被更改,默认不可更改。初此之外还有以下常量的创建方法:
# 产生全 0 的张量
tensor_a = tf.zeros(shape=[3,4], dtype=tf.float32, name=None)
a = tf.zeros_like(tensor_a, dtype=None, name=None)#tensor_a with all elements set to zero.
# 产生全 1 的张量
tensor_b = tf.ones(shape=[3,4], dtype=tf.float32, name=None)
b = tf.ones_like(tensor_b, dtype=None, name=None)
# Creates a tensor of shape  and fills it with value
#tf.fill(dims, value, name=None)
tf.fill([2, 3], 9) ==> [[9, 9, 9]
                        [9, 9, 9]]
# 产生常量 Tensor, value 值可为 python 标准数据类型、Numpy 等
tf.constant(-1.0, shape=[2, 3]) => [[-1., -1., -1.]  # Note: 注意 shape 的用法(广播机制) 
                                    [-1., -1., -1.]]
tf.constant([1,2,3,4,5,6], shape=[2,3]) => [[1, 2, 3]
                                            [4, 5, 6]]

注意常量tensor无需初始化可以直接获取tensor的值
但仍然遵循tensorflow的运行机制即数据只会在session中流动
即无论是初始化赋值还是取出tensor的值都必须在session中执行
  • 变量Tensor:值可以改变,可训练。在神经网络中,变量一般可作为储存权重和其他信息的矩阵,而常量可作为储存超参数或其他结构信息的变量。变量的创建方法如下(变量的创建均需要指定shape,且shape要以list或tuple的形式传入):
tf.Variable(
    initial_value, 
    dtype=None, 
    name=None, 
    trainable=True,
    collections=None,
    validate_shape=True)
# 参数
initial_value:A Tensor或Python对象可转换为a Tensor.变量的初始值.必须具有指定的形状,除非 validate_shape设置为False.
trainable:如果True默认值也将该变量添加到图形集合GraphKeys.TRAINABLE_VARIABLES,该集合用作Optimizer类要使用的变量的默认列表
collections:图表集合键列表,新变量添加到这些集合中.默认为[GraphKeys.VARIABLES]
validate_shape:如果False允许使用未知形状的值初始化变量,如果True默认形状initial_value必须提供.
name:变量的可选名称,默认'Variable'并自动获取

#必须初始化变量(将初始值initial_value传给tensor)后才能取出tensor的值,常见的初始化方法如下:

"""变量初始化:
    全局变量初始化(其实全局变量初始化方法就是将初始值通过变量的assign方法向变量分配值)
    将一个变量的值赋值给另一个变量;
    运行变量的初始化函数variable.initializer();
    restore恢复变量值
    """

#1.全局变量初始化:tf.global_variables_initializer()
import tensorflow as tf

weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35), name="weights")
biases = tf.Variable(tf.zeros([200]), name="biases")

# Add an Op to initialize global variables.
init_op = tf.global_variables_initializer()

# Launch the graph in a session.
with tf.Session() as sess:
    # Run the Op that initializes global variables.
    sess.run(init_op)
    print(weights.eval(),biases.eval())#初始化成功才能取出tensor的值

#2.将一个变量的值赋值给另一个变量:initialized_value()
import tensorflow as tf

w = tf.Variable(tf.ones(shape=[3]), name='w')
w2 = tf.Variable(w.initialized_value(), name='w2')#用已经初始化的w的值作为w2的初始值
w_twice = tf.Variable(w.initialized_value() * 2, name='w_twice')

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())#注意这里必须使用全局变量初始化。w,w2,w_twice都只是计算图中的一个op
    print(w.eval(), w2.eval(), w_twice.eval())#初始化成功才能取出tensor的值

#3.运行变量的初始化函数:variable.initializer()
import tensorflow as tf

w = tf.Variable(tf.ones(shape=[3]), name='w')
with tf.Session() as sess:
    sess.run(w.initializer)#仅仅初始化w本身,等价于w.initializer.run()
    print(w.eval())

#4.restore恢复变量值
import tensorflow as tf

#首先恢复graph
saver = tf.train.import_meta_graph('./checkpoints/model.ckpt-999.meta')
with tf.Session() as sess:
    #恢复最新保存的权重
    saver.restore(sess, tf.train.latest_checkpoint('./checkpoints'))
    #指定一个权重恢复
    saver.restore(sess, './checkpoints/model.ckpt-999')#注意不要加文件后缀名


tf.get_variable(
    name,
    shape=None,
    dtype=None,
    initializer=None,
    trainable=True,
    regularizer=None,
    collections=None,
    caching_device=None,
    partitioner=None,
    validate_shape=True,
    use_resource=None,
    custom_getter=None)

Note:
name 参数必须要指定;shape也必须指定 

#在使用模型之前,必须初始化变量,初始化的方法跟上面一样,这里再介绍参数initializer的几种常用初始化方式
#(这里的initializer初始化之后并没有实际的数据,当开启session时,才将相应的初始化数据传给tensor)
#(1)初始化initializer为常量
init = tf.constant_initializer([[1,2],[23,2]])
init_zeros = tf.zeros_initializer()
init_ones = tf.ones_initializer()
x =  tf.get_variable(initializer=init, name='x', shape=[2,2])

init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    print(x.eval())

#(2)初始化为initializer为 标准/截断 正态分布
init_random = tf.random_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
init_truncated = tf.truncated_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
y = tf.get_variable(initializer=init_random, name='y', shape=[10])
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    print(y.eval())

#(3)初始化initializer为均匀分布
init_uniform = tf.random_uniform_initializer(minval=0, maxval=10, seed=None, dtype=tf.float32)
z = tf.get_variable(initializer=init_uniform, name='z', shape=[10])
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    print(z.eval())


补充tf.Variable()与tf.get_variable()的区别
1变量共享
使用tf.Variable()如果系统检测到命名相同系统会自动加后缀不支持变量共享
使用tf.get_variable()如果系统检测到命名相同且参数reuse=True那么直接共享之前的变量值支持变量共享

import tensorflow as tf

with tf.variable_scope('scope1'):
    w1 = tf.Variable(1, name='w1')
    w2 = tf.get_variable(initializer=2.0, name='w2')

with tf.variable_scope('scope1', reuse=True):
    w1_p = tf.Variable(5, name='w1_P')
    w2_p = tf.get_variable(initializer=3.0, name='w2')

init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    print(w1.eval())
    print(w2.eval())
    print(w1_p.eval())
    print(w2_p.eval())

print(w1.name, w1_p.name)
print(w2.name, w2_p.name)

assert w1 != w1_p
assert  w2 == w2_p

2tf.variable_scope()可以对所有op加上前缀指明作用域空间而tf.name_scope()不能给tf.get_variable()变量指明作用域空间
import tensorflow as tf

with tf.name_scope('scope1'):
    w0 = tf.constant(1, name='w0')
    w1 = tf.Variable(1, name='w1')
    w2 = tf.get_variable(initializer=2.0, name='w2')

with tf.variable_scope('scope2'):
    w0_p = tf.constant(2, name='w0_p')
    w1_p = tf.Variable(5, name='w1_P')
    w2_p = tf.get_variable(initializer=3.0, name='w2_p')

print(w0.name, w1.name, w2.name)
print(w0_p.name, w1_p.name, w2_p.name)
  • 占位符 placeholder: tf.placeholder(dtype=tf.float32, shape = [2,3],name='b')

为什么要使用tf.placeholder?
因为每一个tensor在graph上都是一个op。当我们将train数据分成一个个minibatch然后传入网络进行训练时,每一个minibatch都将是一个op,这样的话,一副graph上的op未免太多,也会产生巨大的开销;于是就有了tf.placeholder,我们每次可以将 一个minibatch传入到x = tf.placeholder(tf.float32,[None,32])上,下一次传入的x都替换掉上一次传入的x,这样就对于所有传入的minibatch x就只会产生一个op,不会产生其他多余的op,进而减少了graph的开销。

import tensorflow as tf
a = tf.placeholder(tf.float32, shape=[3]) # a是一个3维向量
b = tf.constant([5, 5, 5], tf.float32)
c = a + b
with tf.Session() as sess:
    print(sess.run(c, feed_dict = {a: [1, 2, 3]})) # 把[1, 2, 3]灌到a里去
注意占位符不需要初始化可以把feed_dict看作它特有的一种初始化方式

SparseTensor(稀疏张量):定义时只需要定义非0的数,其他的数会自动填充。

#Example: The sparse tensor
tf.SparseTensor(values=[1, 2], indices=[[0, 0], [1, 2]], dense_shape=[3, 4])

#represents the dense tensor
[[1, 0, 0, 0]
 [0, 0, 2, 0]
 [0, 0, 0, 0]]

2.Operation(计算节点)

将多个Tensor连接在一起,形成新的Tensor。例如tf.add、tf.mul等操作。tensor在graph上也是一个Operation(简称op)。但凡是op,都需要通过session运行之后,才能得到结果。

3.Graph(数据节点+计算节点)

Graph

Graph就是由一系列op构成的。具体来说就是由W、b、x等数据节点及Add、MatMul等计算节点共同构成一个Graph。

在Tensorflow中,始终存在一个默认的Graph。如果要将Operation添加到默认Graph中,只需要调用定义Operation的函数(例如tf.add())。如果我们需要定义多个Graph,则需要在with语句中调用Graph.as_default()方法将某个graph设置成默认Graph,于是with语句块中调用的Operation或Tensor将会添加到该Graph中。

import tensorflow as tf

#定义一个图:只有一个图时,这个图就是默认图,所有操作都自动添加到这个图中
g = tf.Graph()#tensorflow会默认给我们建立一个graph,这句话可以省略
a = tf.constant(2)#将Operation添加到默认Graph中,只需要调用定义Operation的函数

print(a.graph)
print(tf.get_default_graph())#通过调用tf.get_default_graph()访问默认创建的图的位置

#定义多个图:需要声明某个操作是定义在哪个图中的
g1 = tf.Graph()
g2 = tf.Graph()
#将某个graph设置成默认Graph,with语句块中调用的Operation或Tensor将会添加到该Graph中
with g1.as_default():
    x = tf.constant(2)
    y = tf.constant(3)
    z = tf.add(x, y)
    print(x.graph, y.graph, z.graph)
    print(tf.get_default_graph())

with g2.as_default():
    v = tf.constant(4)
    u = tf.add(2, v)
    print(v.graph, u.graph)
    print(tf.get_default_graph())

#e不是定义在with语句里面的,e会包含在tensorflow默认创建的图中。也就是说e与a在同一个图中
e=tf.constant(value=15)
print(e.graph)

如果在创建Session时没有指定Graph,则该Session会加载默认Graph。如果创建了多个Graph,则需要创建不同的Session来加载每个Graph,而每个Graph则可以加载在多个Session中进行计算。

import tensorflow as tf
g1 = tf.Graph()
with g1.as_default():
    c1 = tf.constant([1.0])
with tf.Graph().as_default() as g2:
    c2 = tf.constant([2.0])

with tf.Session(graph=g1) as sess1:
    print(sess1.run(c1))
with tf.Session(graph=g2) as sess2:
    print(sess2.run(c2))

# result:
# [ 1.0 ]
# [ 2.0 ]
#如果将上面例子的sess1.run(c1)和sess2.run(c2)中的c1和c2交换一下位置,运行会报错。
# 因为sess1加载的g1中没有c2这个Tensor,同样地,sess2加载的g2中也没有c1这个Tensor。

4.Session(对Graph进行计算)

Tensorflow先构造Graph,然后开启session在这个Graph上做运算。Graph是由一系列op组成。但凡是op,都需要通过session运行之后,才能得到结果。Session的作用就是执行Operation(Tensor也可以看作一种Operation)。

执行Operation有两种方式:

  • 调用Session.run()方法: 该方法的定义如下所示,参数fetches便是一个或者多个Operation或者Tensor。
tf.Session.run(fetches, feed_dict=None)
  • 调用Tensor.eval()方法: 这个方法接收参数session,用于指定在哪个session中计算。该参数默认为None,表示在默认session中计算。设置默认session有两种方式:
#设置默认session的方式一
import tensorflow as tf

a = tf.constant([1.0, 2.0])
b = tf.constant([3.0, 4.0])
c = tf.add(a, b)

with tf.Session():
	#with语句块中的语句
   print(c.eval())

#设置默认session的方式二
import tensorflow as tf

a = tf.constant([1.0, 2.0])
b = tf.constant([3.0, 4.0])
c = tf.add(a, b)

sess = tf.Session()
with sess.as_default():
	print(c.eval())
sess.close()

session.run()与tensor.eval()都是开启对Graph的计算,下面比较一下两者:

首先,tensor.eval()只适用于tensor。而session.run()不仅适用于tensor,还可用于没有输出的op。对于tensor,调用session.run()与tensor.eval()是等价的。

import tensorflow as tf
t = tf.constant(42.0)
sess = tf.Session()
#calling t.eval() is equivalent to calling tf.get_default_session().run(t).
with sess.as_default():   # or `with sess:` to close on exit
    assert sess is tf.get_default_session()
    assert t.eval() == sess.run(t)
sess.close()

其次,你可以使用session.run()在同一步骤中获取许多张量的值,而tensor.eval()却只能一次获得一个张量的值。

import tensorflow as tf

t = tf.constant(42.0)
u = tf.constant(37.0)
tu = tf.add(t, u)
ut = tf.add(u, t)

sess = tf.Session()
with sess.as_default():
   tu.eval()  # runs one step
   ut.eval()  # runs one step
   sess.run([tu, ut])  # evaluates both tensors in a single step
sess.close()


参考:

Tensorflow 中eval()和sess.run()的关系

Tensorflow中 Graph和Session的关系

TensorFlow进阶(三)---变量的创建、初始化 - 时间&煮雨~ - 博客园

Tensorflow学习笔记2:About Session, Graph, Operation and Tensor

TensorFlow学习笔记1:graph、session和op - Jiax - 博客园

TensorFlow学习(三):Graph和Session - 谢小小XH - CSDN博客

编辑于 2019-01-21

文章被以下专栏收录