import tensorflow as tf
from show import show_graph
# 显存管理
import os
os.environ["CUDA_VISIBLE_DEVICES"] = '0' # 指定第一块GPU可用
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.5 # 最多允许占用50%显存
config.gpu_options.allow_growth = True # 按需申请显存
$$f=x^2 y + y + 2$$
计算图只是定义了一些op,并没有实际的计算过程
x = tf.Variable(3, name='x')
y = tf.Variable(4, name='y')
f = x*x*y+y+2
f
1. 直接创建会话,在指定会话上运行计算图后关闭
# 创建会话
sess = tf.Session(config=config)
# 在sess会话中对变量x、y进行初始化
sess.run(x.initializer)
sess.run(y.initializer)
# 在sess会话中计算f并打印
result = sess.run(f)
print(result)
# 计算完毕,关闭会话
sess.close()
2. 通过with使用会话
# 在with块中,计算图都运行于默认会话当中
with tf.Session(config=config) as sess:
x.initializer.run() # tf.get_default_session().run(x.initializer)
y.initializer.run() # tf.get_default_session().run(y.initializer)
result = f.eval() # tf.get_default_session().run(f)
print(result)
3. 使用专门的变量初始化结点进行初始化,使用Interactive会话
InteractiveSession和Session的唯一区别在于——
InteractiveSession将自动设置为默认会话
# 创建一个结点用于初始化全局变量,但没有实际运行
init = tf.global_variables_initializer()
# 创建一个交互式会话
sess = tf.InteractiveSession(config=config)
# 运行计算图
init.run() # tf.get_default_session().run(init)
result = f.eval() # tf.get_default_session().run(f)
# 打印结果并关闭会话
print(result)
sess.close()
如果不为op指定图,那么他将创建在默认图上
# 重置图
tf.reset_default_graph()
# x1创建在默认图上
x1 = tf.Variable(1)
# 新建一个图graph,并在这个图上创建x2
graph = tf.Graph()
with graph.as_default():
x2 = tf.Variable(2)
x1.graph is tf.get_default_graph()
x2.graph is tf.get_default_graph()
x2.graph is graph
普通的结点只存在于图的运行期间;变量结点的声明周期起于初始化,随着会话的结束而结束生命周期。
在单进程tf中,任何会话之间都不共享状态,即使他们使用相同的图,那也是每个会话使用一个图的副本;
而在分布式tf中,会话的状态保存在服务器上,不同会话之间是共享状态的。
运行一个结点时,会先运行其依赖的结点
w = tf.constant(3)
x = w + 2
# y、z都依赖于x,x依赖于w
y = x + 5
z = x * 3
with tf.Session(config=config) as sess:
# 计算y:w -> x -> y
print(y.eval()) # 10
# 计算z:w -> x -> z
print(z.eval()) # 15
上边的代码在计算y和z使虽然都用到了x,但这里的x并没有复用;
也就是说,这里x的值计算了两次;
如果要复用中间结果,可以使y、z的计算过程同时进行——
with tf.Session(config=config) as sess:
# 将需要同时计算的结点放入列表传递给run
y_val, z_val = sess.run([y, z])
print(y_val) # 10
print(z_val) # 15
参考9.1和9.2
tf.placeholder()
创建占位符;feed_dict
向占位符投喂数据 tf.reset_default_graph()
# 创建占位符A,数据类型float32,不要求第零维度,要求第一维度大小为3
A = tf.placeholder(tf.float32, shape=(None, 3))
B = A + 5
with tf.Session(config=config) as sess:
B_val_1 = B.eval( feed_dict={A: [[1, 2, 3]]} )
B_val_2 = B.eval( feed_dict={A: [[4, 5, 6], [7, 8, 9]]} )
B_val_1
B_val_2
可以用 tf.train.Saver
来保存模型中变量的值
tf.reset_default_graph()
# 构建计算图
w = tf.Variable(3, name='w')
x = tf.add(w, 2, name='x') # x = w + 2创建的是一个没有指定名称的结点,不方便读取模型后使用
# 构建完计算图后,接一个Saver结点
saver = tf.train.Saver()
# # 若只想保存部分变量(比如将w保存为weights),可以在参数中用字典指明
# saver = tf.train.Saver({'weights': w})
with tf.Session(config=config) as sess:
sess.run(w.initializer)
print( sess.run(x) )
# 将sess会话的变量的值都保存到/tmp/model.ckpt文件夹中
saver.save(sess, "/tmp/model.ckpt")
也是使用 tf.train.Saver
,通过restore方法读取
tf.reset_default_graph()
w = tf.Variable(3, name='w')
x = tf.add(w, 2, name='x')
saver = tf.train.Saver()
with tf.Session(config=config) as sess:
# 如果这里没有restore,在下一行执行sess.run(x)的时候将报错“w未初始化”
saver.restore(sess, "/tmp/model.ckpt")
print(sess.run(x))
甚至可以直接读取整个模型
tf.train.import_meta_graph
读入.ckpt.meta
的计算图 get_tensor_by_name
读取Tensor对象(注意要指明索引)get_operation_by_name
读取op对象saver.restore
读入.ckpt
的变量值tf.reset_default_graph()
saver = tf.train.import_meta_graph('/tmp/model.ckpt.meta')
x = tf.get_default_graph().get_tensor_by_name('x:0')
with tf.Session(config=config) as sess:
saver.restore(sess, "/tmp/model.ckpt")
print(sess.run(x))
用函数和列表生成器实现部分结点集合的模块化
tf.reset_default_graph()
def relu(X):
# 利用命名空间将每个RELU整合成一个模块,在tensorboard中展示出来更佳清晰
with tf.name_scope("relu"):
w_shape = (int(X.get_shape()[1]), 1)
w = tf.Variable(tf.random_normal(w_shape), name="weights")
b = tf.Variable(0., name='bias')
z = tf.add(tf.matmul(X, w), b, name='z')
return tf.maximum(z, 0., name="relu")
n_features = 3
# 输入占位符
X = tf.placeholder(tf.float32, shape=(None, n_features), name='X')
# 创建5个RELU
relus = [relu(X) for i in range(5)]
# 用tf.add_n将5个RELU相加输出
output = tf.add_n(relus, name="output")
show_graph(tf.get_default_graph())
1. 最简单:使用(引用)同一个变量
def my_module(X, shared_variable):
with tf.name_scope("my_module"):
# ...other code...
return tf.add(shared_variable, 1)
shared_variable = tf.Variable(0, name="shared_variable")
X = tf.placeholder(tf.float32, name="X")
my_modules = [my_module(X, shared_variable) for i in range(5)]
2. 设置函数属性(类似C语言的静态变量)
def my_module(X):
with tf.name_scope("my_module"):
if not hasattr(my_module, "shared_variable"):
my_module.shared_variable = tf.Variable(0, name="shared_variable")
# ...other code...
return tf.add(shared_variable, 1)
X = tf.placeholder(tf.float32, name="X")
my_modules = [my_module(X) for i in range(5)]
3. 使用tf提供的reuse_variables机制
指定一个变量空间,将其设置为可复用的;
然后通过 tf.get_variable()
来创建和引用这个共享的变量
# 创建变量空间,用get_variable创建一个变量
# 注意这时不能打开reuse,否则会因为在变量中空间中shared_variable未定义而报错
with tf.variable_scope("my_module"):
shared_variable = tf.get_variable("shared_variable", shape=(),
initializer=tf.constant_initializer(0.))
# 复用变量空间,注意必须打开reuse,否则如果变量已经创建过了则会报错
# 在打开reuse的变量空间下创建的子变量空间也必定是打开reuse的
# reuse一经打开,在这个块内都不能关闭
with tf.variable_scope("my_module", reuse=True):
shared_variable = tf.get_variable("shared_variable")
# 另一种复用的方式
with tf.variable_scope("my_module") as scope:
scope.reuse_variables()
shared_variable = tf.get_variable("shared_variable")