In [1]:
import tensorflow as tf
from show import show_graph
/usr/local/lib/python3.5/dist-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters
In [2]:
# 显存管理
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,并没有实际的计算过程

In [3]:
x = tf.Variable(3, name='x')
y = tf.Variable(4, name='y')
f = x*x*y+y+2
In [4]:
f
Out[4]:
<tf.Tensor 'add_1:0' shape=() dtype=int32>

创建会话并进行计算

1. 直接创建会话,在指定会话上运行计算图后关闭

In [5]:
# 创建会话
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()
42

2. 通过with使用会话

In [6]:
# 在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)
42

3. 使用专门的变量初始化结点进行初始化,使用Interactive会话
InteractiveSession和Session的唯一区别在于——
InteractiveSession将自动设置为默认会话

In [7]:
# 创建一个结点用于初始化全局变量,但没有实际运行
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()
42

图管理

如果不为op指定图,那么他将创建在默认图上

In [8]:
# 重置图
tf.reset_default_graph()
In [9]:
# x1创建在默认图上
x1 = tf.Variable(1)

# 新建一个图graph,并在这个图上创建x2
graph = tf.Graph()
with graph.as_default():
    x2 = tf.Variable(2)
In [10]:
x1.graph is tf.get_default_graph()
Out[10]:
True
In [11]:
x2.graph is tf.get_default_graph()
Out[11]:
False
In [12]:
x2.graph is graph
Out[12]:
True

结点的生命周期

普通的结点只存在于图的运行期间;变量结点的声明周期起于初始化,随着会话的结束而结束生命周期。

在单进程tf中,任何会话之间都不共享状态,即使他们使用相同的图,那也是每个会话使用一个图的副本;
而在分布式tf中,会话的状态保存在服务器上,不同会话之间是共享状态的。

运行一个结点时,会先运行其依赖的结点

In [13]:
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
10
15

上边的代码在计算y和z使虽然都用到了x,但这里的x并没有复用;
也就是说,这里x的值计算了两次;
如果要复用中间结果,可以使y、z的计算过程同时进行——

In [14]:
with tf.Session(config=config) as sess:
    # 将需要同时计算的结点放入列表传递给run
    y_val, z_val = sess.run([y, z])
    print(y_val)  # 10
    print(z_val)  # 15
10
15

示例:线性回归

参考9.1和9.2

占位符和数据投喂

  • tf.placeholder() 创建占位符;
    指定占位符的数据类型;
    可以强制指定占位符的大小,None表示不作要求
  • 在run的时候加入关键字参数 feed_dict 向占位符投喂数据
In [15]:
tf.reset_default_graph()
In [16]:
# 创建占位符A,数据类型float32,不要求第零维度,要求第一维度大小为3
A = tf.placeholder(tf.float32, shape=(None, 3))
B = A + 5
In [17]:
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]]} )
In [18]:
B_val_1
Out[18]:
array([[6., 7., 8.]], dtype=float32)
In [19]:
B_val_2
Out[19]:
array([[ 9., 10., 11.],
       [12., 13., 14.]], dtype=float32)

保存模型

可以用 tf.train.Saver 来保存模型中变量的值

In [20]:
tf.reset_default_graph()
In [21]:
# 构建计算图
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})
In [22]:
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")
5

读取模型

也是使用 tf.train.Saver ,通过restore方法读取

In [23]:
tf.reset_default_graph()
In [24]:
w = tf.Variable(3, name='w')
x = tf.add(w, 2, name='x')
saver = tf.train.Saver()
In [25]:
with tf.Session(config=config) as sess:
    # 如果这里没有restore,在下一行执行sess.run(x)的时候将报错“w未初始化”
    saver.restore(sess, "/tmp/model.ckpt")
    print(sess.run(x))
INFO:tensorflow:Restoring parameters from /tmp/model.ckpt
5

甚至可以直接读取整个模型

  • tf.train.import_meta_graph 读入.ckpt.meta的计算图
  • get_tensor_by_name 读取Tensor对象(注意要指明索引)
  • get_operation_by_name 读取op对象
  • saver.restore 读入.ckpt的变量值
In [26]:
tf.reset_default_graph()
In [27]:
saver = tf.train.import_meta_graph('/tmp/model.ckpt.meta')
In [28]:
x = tf.get_default_graph().get_tensor_by_name('x:0')
In [29]:
with tf.Session(config=config) as sess:
    saver.restore(sess, "/tmp/model.ckpt")
    print(sess.run(x))
INFO:tensorflow:Restoring parameters from /tmp/model.ckpt
5

数据可视化Tensorboard

名字空间

模块化

用函数和列表生成器实现部分结点集合的模块化

In [30]:
tf.reset_default_graph()
In [31]:
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")
In [32]:
n_features = 3
In [33]:
# 输入占位符
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")
In [34]:
show_graph(tf.get_default_graph())

变量共享

1. 最简单:使用(引用)同一个变量

In [35]:
def my_module(X, shared_variable):
    with tf.name_scope("my_module"):
        # ...other code...
        return tf.add(shared_variable, 1)
In [36]:
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语言的静态变量)

In [37]:
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)
In [38]:
X = tf.placeholder(tf.float32, name="X")
my_modules = [my_module(X) for i in range(5)]

3. 使用tf提供的reuse_variables机制
指定一个变量空间,将其设置为可复用的;
然后通过 tf.get_variable() 来创建和引用这个共享的变量

In [39]:
# 创建变量空间,用get_variable创建一个变量
# 注意这时不能打开reuse,否则会因为在变量中空间中shared_variable未定义而报错
with tf.variable_scope("my_module"):
    shared_variable = tf.get_variable("shared_variable", shape=(), 
                                     initializer=tf.constant_initializer(0.))
In [40]:
# 复用变量空间,注意必须打开reuse,否则如果变量已经创建过了则会报错
# 在打开reuse的变量空间下创建的子变量空间也必定是打开reuse的
# reuse一经打开,在这个块内都不能关闭
with tf.variable_scope("my_module", reuse=True):
    shared_variable = tf.get_variable("shared_variable")
In [41]:
# 另一种复用的方式
with tf.variable_scope("my_module") as scope:
    scope.reuse_variables()
    shared_variable = tf.get_variable("shared_variable")