TensorFlow:graph、session和op


graph即 tf.Graph(),session即 tf.Session(),很多人经常将两者混淆,其实二者完全不是同一个东西。

  • graph定义了计算方式,是一些加减乘除等运算的组合,类似于一个函数。它本身不会进行任何计算,也不保存任何中间计算结果。
  • session用来运行一个graph,或者运行graph的一部分。它类似于一个执行者,给graph灌入输入数据,得到输出,并保存中间的计算结果。同时它也给graph分配计算资源(如内存、显卡等)。

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

关于graph

定义一个图:graph

g = tf.Graph()  # 该行可以省略
a = tf.constant(2) 
b = tf.constant(3)
x = tf.add(a, b) 

上面就定义了一个graph。tensorflow会默认给我们建立一个graph,所以g = tf.Graph()这句其实是可以省略的。上面的graph包含3个操作,即op,但凡是op,都需要通过session运行之后,才能得到结果。如果你直接执行print(a),那么输出结果是:

Tensor("a:0", shape=(), dtype=int32)

是一个张量(Tensor)。

需要执行print(tf.Session().run(a))
,才能得到2.

关于子图:subgraph

可以定义多个graph,例如一个graph实现z = x + y,另一个graph实现u = 2 * v

g1 = tf.Graph()
g2 = tf.Graph()
with g1.as_default():
    x = tf.constant(2)
    y = tf.constant(3)
    z = tf.add(x, y)
with g2.as_default():
    v = tf.constant(4)
    u = tf.mul(2, v)

但通常不建议这么做,原因如下:

  • 运行多个graph需要多个session,而每个session会试图耗尽所有的计算资源,开销太大;
  • graph之间没有数据通道,要人为通过python/numpy传数据;

事实上,你可以把所有的op都定义在一个graph中:

x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)
v = tf.constant(4)
u = tf.mul(2, v)

从上面graph的定义可以看到,x/y/z是一波,u/v是另一波,二者没有任何交集。这相当于在一个graph里有两个独立的subgraph。当你要计算z = x + y时,执行tf.Session().run(z);当你想计算u = 2 * v,就执行tf.Session().run(u),二者完全独立。但更重要的是,二者在同一个session上运行,系统会均衡地给两个subgraph分配合适的计算资源。

关于session

通常我们会显示地定义一个session来运行graph:

x = tf.constant(2)
y = tf.constant(3)
z = tf.add(x, y)

with tf.Session() as sess:
    result = sess.run(z)
    print(result)

输出结果是5。

关于op

tensorflow是一个符号式编程的框架,首先要定义一个graph,然后用一个session来运行这个graph得到结果。graph就是由一系列op构成的。上面的tf.constant()tf.add()tf.mul()都是op,都要现用session运行,才能得到结果。

很多人会以为tf.Variable()也是op,其实不是的。tensorflow里,首字母大写的类,首字母小写的才是op。tf.Variable()就是一个类,不过它包含了各种op,比如你定义了x = tf.Variable([2, 3], name = 'vector'),那么x就具有如下op:

  • x.initializer # 对x做初始化,即赋值为初始值[2, 3]
  • x.value() # 获取x的值
  • x.assign(…) # 赋值操作
  • x.assign_add(…) # 加法操作

tf.Variable()必须先初始化,再做运算,否则会报错。下面的写法就不是很安全,容易导致错误:

W = tf.Variable(tf.truncated_normal([700, 10]))
U = tf.Variable(2 * W)

truncated_normal 释义
在这里插入图片描述

要把W赋值给U,必须现把W初始化。但很多人往往忘记初始化,从而出错。保险起见,应该按照下面这样写:

W = tf.Variable(tf.truncated_normal([700, 10]))
U = tf.Variable(2 * W.intialized_value())

一个特殊的op: tf.placeholder()

placeholder,翻译过来就是占位符。其实它类似于函数里的自变量。比如z = x + y,那么x和y就可以定义成占位符。占位符,顾名思义,就这是占一个位子,平时不用关心它们的值,当你做运算的时候,你再把你的数据灌进去就行了。是不是和自变量很像?看下面的代码:

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里去

输出结果是[6, 7, 8]。上面代码中出现了feed_dict的概念,其实就是用[1, 2, 3]代替a的意思。相当于在本轮计算中,自变量a的取值为[1, 2, 3]。其实不仅仅是tf.placeholder才可以用feed_dict,很多op都可以。只要tf.Graph.is_feedable(tensor)返回值是True,那么这个tensor就可用用feed_dict来灌入数据。

tf.constant()是直接定义在graph里的,它是graph的一部分,会随着graph一起加载。如果通过tf.constant()定义了一个维度很高的张量,那么graph占用的内存就会变大,加载也会变慢。而tf.placeholder就没有这个问题,所以如果数据维度很高的话,定义成tf.placeholder是更好的选择。

global_step =tf.train.get_or_create_global_step()作用

tf.train.get_or_create_global_step(graph=None)
'''
Returns and create (if necessary) the global step tensor.
Args:
	graph: The graph in which to create the global step tensor. If missing, use default graph.
Returns:
	The global step tensor.
'''
  • global_step在滑动平均、优化器、指数衰减学习率等方面都有用到.
  • 这个变量的实际意义非常好理解:代表全局步数,比如在多少步该进行什么操作,现在神经网络训练到多少轮等等,类似于一个钟表。
  • global_step在训练中是计数的作用,每训练一个batch就加1

根据代码可以发现global_step的初始化值是0:

 global_step=tf.Variable(0, trainable=False)

测试代码如下:

import tensorflow as tf
import numpy as np
 
x = tf.placeholder(tf.float32, shape=[None, 1], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')
w = tf.Variable(tf.constant(0.0))
 
global_steps = tf.Variable(0, trainable=False)
 
 
learning_rate = tf.train.exponential_decay(0.1, global_steps, 10, 2, staircase=False)
loss = tf.pow(w*x-y, 2)
 
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_steps)
 
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(10):
        sess.run(train_step, feed_dict={x:np.linspace(1,2,10).reshape([10,1]),
            y:np.linspace(1,2,10).reshape([10,1])})
        print(sess.run(learning_rate))
        print(sess.run(global_steps))

输出为:

0.107177
1
0.11487
2
0.123114
3
0.131951
4
0.141421
5
0.151572
6
0.16245
7
0.17411
8
0.186607
9
0.2
10

如果把train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_steps)
后面部分的global_step=global_steps去掉,global_step的自动加一就会失效,输出如下:

0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
0.1
0
  • 因为指数衰减的学习率是伴随global_step的变化而衰减的,所以当global_step不改变时,学习率也变成一个定值。

综上所述:损失函数优化器的minimize()中global_step=global_steps能够提供global_step自动加一的操作。

  • 参考stackoverflow答主的回答

global_step refer to the number of batches seen by the graph. Everytime a batch is provided, the weights are updated in the direction that minimizes the loss. global_step just keeps track of the number of batches seen so far. When it is passed in the minimize() argument list, the variable is increased by one. Have a look at optimizer.minimize().
You can get the global_step value using tf.train.global_step().
The 0 is the initial value of the global step in this context.

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值