博客TensorFlow基础知识:计算图中的Op,边,和张量中有一句话,对于我来说是醍醐灌顶,一下子明白了TensorFlow的计算模式
计算图的定义和图的运算是分开的.tensorflow是一个’符号主义的库’.编程模式分为两类,命令式(imperative style)和符号式(symbolic style).命令式的程序很容易理解和调试,它按照原有的逻辑运行.符号式则相反,在现有的深度学习框架中,torch是命令式的,Caffe,Mxnet是两种模式的混合,tensorflow完全采用符号式.符号式编程,一般先定义各种变量,然后建立一个计算图(数据流图),计算图指定了各个变量之间的计算关系,此时对计算图进行编译,没有任何输出,计算图还是一个空壳,只有把需要运算的输入放入后,才会在模型中形成数据流.形成输出.
就是说TensorFlow是一个符号式编程模型,图上最关键的两个东西,一个数据相关的概念Tensor(张量),指的是对数据的一种引用,另一个是图上的概念Operation(操作),也就是计算图中的节点,在写TensorFlow程序的时候,我们就是在Graph上布置节点,如下代码,刚开始的时候,想象现在graph上一个op都没有,然后随着代码从上往下一直到with tf.Session() as sess:
这行,我在图上一共放了6个op,不同的op的之间是有联系的,比如matmul这个op需要吃一个x和一个weights,然后输出一个东西叫mat_res ,给tf.add这个op。
但是当打印mat_res的时候,返回的结果告诉我们,mat_res是一个Tensor,也就是说,个人理解,从数据的角度看,mat_res这个Tensor代表着matmul这个op的输出。
import tensorflow as tf
x = [[1.,2.],[-1.,3.]]
y = [1,0]
weights = tf.Variable([[1,-1],[-1,-1]],name='weights',dtype=tf.float32) #虽然是定义了一个Variable类,但是也相当于在设置了一个op
bias = tf.get_variable('bias',shape=[2],initializer=tf.zeros_initializer) #get_variable也是一个op
mat_res = tf.matmul(x,weights) #定义矩阵乘法计算,也是一个op,目前图上有3个op
print(mat_res) #Tensor("MatMul:0", shape=(2, 2), dtype=float32)
add_res = tf.add(mat_res,bias) #定义矩阵相加计算,也是一个op,目前图上有4个op
y_ = tf.nn.sigmoid(add_res) #定义sigmoid运算,也是一个op,目前图上有5个op
loss = tf.losses.sparse_softmax_cross_entropy(labels=y,logits=y_) #定义sigmoid运算,也是一个op,目前图上有6个op
# 以上就是在图上放op,一共6个op
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(weights))
print(sess.run([loss]))
看下面的代码
我定义了两个cross_entropy,获得losses1的时候,此时图上只有一个cross_entropy1;在获得losses2 的时候,此时图上有cross_entropy1和cross_entropy2
def test_loss():
cross_entropy1 = tf.losses.sparse_softmax_cross_entropy(labels = [1,0,1],logits=[[1/2,1/2],[1/2,1/2],[0.9,1/3]])
losses1 = tf.losses.get_losses()
#[]
total_loss1 = tf.losses.get_total_loss()
#Tensor("total_loss:0", shape=(), dtype=float32)
cross_entropy2 = tf.losses.sparse_softmax_cross_entropy(labels = [1,0,1],logits=[[1/2,1/2],[1/2,1/2],[0.9,1/3]])
losses2 = tf.losses.get_losses()
#[, ]
total_loss2=tf.losses.get_total_loss()
#Tensor("total_loss_1:0", shape=(), dtype=float32)
with tf.Session() as sess:
print(losses1)
print(total_loss1)
print(losses2)
print(total_loss2)
print(sess.run(cross_entropy1))
print(sess.run(cross_entropy2))
进一步理解Graph和op,下面的代码里我定义了好多好多op
Add:0
的张量,由于第一个tf.add
创建了一个op,Add:0
就作为这个op的输出;而第二个add_res是一个名字叫做Add_1:0
的张量,可以看到它的名字后面多了一个_1
,说明它和第一个add_res不是一个东西,它是由于我创建了第二个tf.add
,因此现在有两个相加的op,它作为第二个相加的op的输出。sparse_softmax_cross_entropy
来创建的,它的输出就是loss1;而get_total_loss又会创建一个op,这个op的作用是,获取全部的损失函数的和,而目前的损失只有一个loss1,所以get_total_loss这个op的输出的tensor的值等于loss1的值,但get_total_loss这个op的输出的tensor不等于loss1,他们仍然是两个tensor,你可以打印一下,他们的名字不同。想这个问题的时候,请想象一个图,然后在上面扔op
tf.reset_default_graph()
x = [[1.,2.],[-1.,3.]]
y = [1,0]
weights = tf.Variable([[1,-1],[-1,-1]],name='weights',dtype=tf.float32) #虽然是定义了一个Variable类,但是也相当于在设置了一个op
bias = tf.get_variable('bias',shape=[2],initializer=tf.zeros_initializer) #get_variable也是一个op
mat_res = tf.matmul(x,weights) #定义矩阵乘法计算,也是一个op,目前图上有3个op
add_res = tf.add(mat_res,bias) #定义矩阵相加计算,也是一个op,目前图上有4个op
print(add_res) #Tensor("Add:0", shape=(2, 2), dtype=float32)
y_ = tf.nn.sigmoid(add_res) #定义sigmoid运算,也是一个op,目前图上有5个op
loss1 = tf.losses.sparse_softmax_cross_entropy(labels=y,logits=y_) #定义sigmoid运算,也是一个op,目前图上有6个op
all_loss1 = tf.losses.get_total_loss() #获取全部损失,也是一个op,目前图上有7个op
print(all_loss1) #Tensor("total_loss:0", shape=(), dtype=float32)
val_x = [[4.,-2.]]
val_y = [0]
mat_res = tf.matmul(val_x,weights) #定义矩阵乘法计算,也是一个op,目前图上有8个op
add_res = tf.add(mat_res,bias) #定义矩阵相加计算,也是一个op,目前图上有9个op
print(add_res) #Tensor("Add_1:0", shape=(1, 2), dtype=float32)
y_ = tf.nn.sigmoid(add_res) #定义sigmoid运算,也是一个op,目前图上有10个op
val_loss = tf.losses.sparse_softmax_cross_entropy(labels=val_y,logits=y_) #定义sigmoid运算,也是一个op,目前图上有11个op
all_loss2 = tf.losses.get_total_loss() #第二次获取全部损失,也是一个op,目前图上有12个op
print(all_loss2) #Tensor("total_loss_1:0", shape=(), dtype=float32)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run([loss1,all_loss1,val_loss,all_loss2]))
补充:张量、op、variable的关系
看下面的代码,通过前三个打印可以看出,lr
和assign
都是Tensor
,前面说过,Tensor
是对数据的引用,那是对什么数据的引用呢?
以lr
为例,他是对exponential_decay_learning_rate:0
的引用,exponential_decay_learning_rate
就是一个op,也就是一个操作,那么lr
实际上指的是,exponential_decay_learning_rate
这个op的第0个输出
而从print(tf.global_variables())
可以看出,lr
并不是一个variable
,他只是某个操作的输出。
同理看assign
,他也是一个Tensor
下面的print(sess.run(assign))
执行,做的实际上就是,执行Assign
这个op,然后获取它的第0个输出。
def test_lr():
global_step = tf.train.get_or_create_global_step()
assign = tf.assign(global_step, 10)
lr = tf.train.exponential_decay(0.001, global_step, 500, 0.98, staircase=False,
name='exponential_decay_learning_rate')
saver = tf.train.Saver()
print(lr) # Tensor("exponential_decay_learning_rate:0", shape=(), dtype=float32)
print(assign) # Tensor("Assign:0", shape=(), dtype=int64_ref)
print(tf.global_variables()) # []
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(assign)) # 10
print(sess.run(global_step)) # 10
print(sess.run(lr)) # 0.0009995961
saver.save(sess, 'test/test', global_step=global_step)
如果我们把刚刚存好的ckpt重新读取进来,会发现global_step被设置为10了,这没问题,但lr是0.004997,这是因为lr并不是一个variable,存储的时候并没有把lr存进去,他只是个tensor,就像刚刚说的,run一个tensor的时候,做的就是执行op然后返回值,执行的时候用到了global_step,然后根据他的参数计算lr返回
def load_lr():
global_step = tf.train.get_or_create_global_step()
lr = tf.train.exponential_decay(0.005, global_step, 500, 0.98, staircase=False,
name='exponential_decay_learning_rate')
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver.restore(sess, 'test/test-10')
print(sess.run(global_step)) # 10
print(sess.run(lr)) # 0.00499798