XDRush

Tensorflow使用notes

Graph理解

Tensorflow的程序一般都分为2个阶段,(1)定义计算图中的所有计算;(2)执行计算;

系统默认计算图

在tf程序中,系统会自动维护一个默认的计算图,通过tf.get_default_graph()可以获取当前默认的计算图;

1
2
3
4
5
6
7
a = tf.constant([2, 3], name='a')
b = tf.constant([4, 5], name='b')
result = tf.multiply(a, b, name='result')
print result.graph
print tf.get_default_graph()

比如上面这段代码的输出为:

1
2
<tensorflow.python.framework.ops.Graph object at 0x10eddc2d0>
<tensorflow.python.framework.ops.Graph object at 0x10eddc2d0>

可以看到,定义的这些节点都在系统默认的计算图中。

新建计算图

tf支持通过tf.Graph生成新的计算图,不同计算图上的张量和运算不会共享,也就是说,tf通过计算图来隔离资源。

Variable及name_space理解

对于一个复杂的tensorflow模型可能会有很多变量:

  • tf.variable_scope():提供了简单的命名空间以避免冲突;
  • tf.get_variable():从同一个变量范围内获取或者创建变量;
  • tf.Variable()用于定义一个变量;tf.get_variable()根据name值,返回该变量,如果name不存在的话,则会创建一个变量;

tf.variable_scope && tf.get_variable

tf.variable_scope和tf.get_variable,这两个函数通常放在一起使用,tf.variable_scope用于指定变量范围,如果在同一变量范围内,通过tf.get_variable获取已经定义的变量,在没有设置reuse=True时会报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
with tf.name_scope('a_name_scope'):
with tf.variable_scope('scope') as scope:
var1 = tf.get_variable(name='var1', shape=[1], dtype=tf.float32,
initializer=tf.constant_initializer(value=1))
with tf.name_scope('b_name_scope'):
with tf.variable_scope('scope', reuse=True):
var4 = tf.get_variable(name='var1')
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(var1.name, sess.run(var1))
print(var4.name, sess.run(var4))

上面代码给出了通常情况下tf.get_variable和tf.variable_scope用法。reuse=True表示tf.get_variable得到的变量可以在其他地方重复使用,只在需要时设置为True,不设置为False,其中variable_scope的名字可以为空。

特别说明一点,将参数设置为True,这样tf.get_variable()将只能获取已将创建过的变量。

具体说明可以参考这里:http://blog.csdn.net/lanchunhui/article/details/61914287

tf.Graph

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
graph1 = tf.Graph() // 新建一个计算图
with graph1.as_default():
variable = tf.get_variable("v", shape=[1], dtype=tf.float32,
initializer=tf.constant_initializer(value=1))
print variable.graph
graph2 = tf.Graph()
with graph2.as_default():
variable = tf.get_variable("v", shape=[1], dtype=tf.float32,
initializer=tf.constant_initializer(value=2))
print variable.graph
with tf.Session(graph=graph1) as sess: // 在计算图graph1上执行运算
sess.run(tf.global_variables_initializer())
with tf.variable_scope("", reuse=True):
print sess.run(tf.get_variable("v"))
with tf.Session(graph=graph2) as sess: // 在计算图graph2上执行计算
sess.run(tf.global_variables_initializer())
with tf.variable_scope("", reuse=True):
print sess.run(tf.get_variable("v"))

以上代码的结果为:

1
2
3
4
<tensorflow.python.framework.ops.Graph object at 0x114e326d0>
<tensorflow.python.framework.ops.Graph object at 0x117b3dcd0>
[ 1.]
[ 2.]

可以看到,变量v在不同的计算图中,它们之间的值也互相不影响。

tf.Graph.device

可以通过tf.Graph.device函数来指定运行计算的设备,这为tf提供了GPU的支持,下面的代码可以在GPU上执行:

1
2
3
graph3 = tf.Graph()
with graph3.device('/gpu:0'):
result = a + b

tf.global_variable_initializer()

这个函数作用是初始化当前计算图中的全局变量,关于一个tf.global_variable_initializer()用法一个注意的地方:

1
2
3
4
5
6
weights = tf.Variable(initial_value=tf.random_normal(shape=[2, 3], mean=0.0, stddev=2),
name='weights')
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
print sess.run(weights)

上面这段代码可以正确执行,再看下面这段代码:

1
2
3
4
5
6
init_op = tf.global_variables_initializer()
weights = tf.Variable(initial_value=tf.random_normal(shape=[2, 3], mean=0.0, stddev=2),
name='weights')
with tf.Session() as sess:
sess.run(init_op)
print sess.run(weights)

这段代码执行时会报错,错误信息如下:

1
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value weights

不难发现,tf.global_variable_initializer()须在所有变量都定义之后再去定义。

tf.get_collection()

在使用get_collection()时有个地方需要注意,在一次训练过程中,一直出现了这个错误:

1
2
3
4
tf.add_n(tf.get_collection('losses'))
// error message
raise ValueError("inputs must be a list of at least one Tensor with the "

刚开始也是百思不得其解,后来一检查,发现是regularizer为None,导致tf.get_collection()得到的tensor没有维度,因此tf.add_n时报错。

tf.softmax_cross_entropy_with_logits && tf.sparse_softmax_cross_entropy_with_logits

这两个函数都是用来直接计算交叉熵的,不同的地方是tf.sparse_sotfmax_cross_entropy_with_logits是直接用来处理稀疏性质的数据。

先来看下sotfmax_cross_entropy_with_logits,顾名思义,这个函数是将计算softmax和cross_entropy的过程融合起来了,看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 原始数据,也即label
ori_data = tf.constant([0, 1, 0, 0, 0])
// 经过softmax之前的输出数据,这里的数据可以呈任意分布,但要保证与label维度相同,这一点也比较好理解
predict_data = tf.constant([0.1, 6, 0.1, 0.1, 0.1])
// 通过这个函数,不再需要单独计算softmax,然后再分步计算cross_entropy了
loss = tf.nn.softmax_cross_entropy_with_logits(labels=ori_data,
logits=predict_data)
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
print sess.run(loss)
// 输出为:
0.0108981

再来看看tf.nn.sparse_softmax_cross_entropy_with_logits,

1
2
3
4
5
6
ori_data = tf.constant([0, 1, 0, 0, 0])
predict_data = tf.constant([0.1, 6, 0.1, 0.1, 0.1])
loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.argmax(ori_data, 0), logits=predict_data)
// 输出为:
0.0108981

可以看到,同上面函数相比,只对label进行了一次加工,而这次加工,正是说明了sparse的特性,tf.argmax(tensor)目的是获取稀疏的tensor中正确答案对应的编号,这个编号是一个整数

tf.app.flags.FLAGS

tf.app.flags.FLAGS主要用来处理相关输入,特别是在训练中需要动态指定训练数据目录时特别有用。典型用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import tensorflow as tf
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string('train_dir', './data/train/clsm',
'Directory for event logs and checkpoint')
tf.app.flags.DEFINE_string('log_dir', './log/file', 'Directory for log dir')
def main():
print FLAGS.train_dir
print FLAGS.log_dir
pass
if __name__ == '__main__':
main()

这里指定FLAGS.train_dir默认目录为”./data/train/clsm”,如果训练过程中,需要另外指定目录,则通过以下命令即可实现:

1
python clsm_train.py --train_dir=./data/xdrush/clsm

上面命令将train_dir目录改为./data/xdrush/clsm,但log_dir依旧时默认值。

tf.gfile相关

tf封装的文件处理相关模块。

1
2
3
4
5
6
// 判断文件夹是否存在
if tf.gfile.Exists(FLAGS.train_dir):
// 递归的删除文件夹
tf.gfile.DeleteRecursively(FLAGS.train_dir)
// 创建多层文件夹
tf.gfile.MakeDirs(FLAGS.train_dir)

tf.records文件读写

tf.TFRecordReader & tf.FixedLengthRecordReader

上面2个方法都是创建一个Reader来读取TFRecord文件中的样例。其中不同的reader对应不同的文件结构。对于二进制文件,FixedLengthRecordReader就比较好,因为每次读等长的一段数据。

tf.slice()的理解

函数原型:
tf.slice(inputs, begin, size, name=’’)
函数的用途是从inputs中抽取部分内容
inputs:可以是list,array,tensor
begin:n维列表,begin[i]表示从inputs中的第i维抽取数据时,相对0的起始偏移量,也就是从第i维的begin[i]开始抽取数据
size:n维列表,size[i]表示要抽取的第i维元素的数目

1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf
import numpy as np
print "test of tf.slice()"
y = np.arange(24).reshape([2, 3, 4])
print y
begin_y = [1, 0, 1]
size_y = [1, 2, 3]
out = tf.slice(y, begin_y, size_y)
with tf.Session() as sess:
print sess.run(out)

输出为:

1
2
3
4
5
6
7
8
9
10
11
12
## y是一个[2*3*4]的矩阵,输出为:
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
## 最终结果为:
[[[13 14 15]
[17 18 19]]]

begin_y = [1, 0, 1]分别表示从第一维的第二个元素开始抽取,0表示从第二维的第一个元素开始抽取,1表示从第三维的第1个元素开始抽取;
size_y = [1, 2, 3]表示第一维抽取一个,第二维抽取2个,第三维抽取3个;不难得出最终的结果。

如果数据不足,则会报错。比如如果将begin_y = [1, 0, 2],表示从第三位的第三个元素开始抽取,抽取个数维3,而此时第三维只剩下2个元素,因此会报错。

1
InvalidArgumentError (see above for traceback): Expected size[2] in [0, 2], but got 3

tf.transpose()的理解

tr.transpose(a, perm=None, name=’’)
默认情况下,如果不指定perm值,则该函数完成矩阵转置功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
matrix = np.arange(12).reshape([3, 4])
print "original matrix: \n", matrix
with tf.Session() as sess:
print sess.run(tf.transpose(matrix))
## 输出为
original matrix:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 0 4 8]
[ 1 5 9]
[ 2 6 10]
[ 3 7 11]]

接下来主要看下perm的含义:

1
2
3
4
matrix = np.arange(24).reshape([2, 3, 4])
print "original matrix: \n", matrix
with tf.Session() as sess:
print sess.run(tf.transpose(matrix, perm=[0, 1, 2]))

对于多维矩阵,当perm=[0, 1, 2]时,输出结果与原矩阵一样;
当perm=[2, 1, 0]时,实现的就是标准的矩阵转置。由此可知,perm控制的转置的维度。

tf.split()的理解

沿着某一维度将输入切割成多等份。
tf.split(value, num_or_size_split, axis=0, num=None, name=’’)
value:输入的tensor
num_or_size_split:切割的数量,可以是矩阵
axis:切割的维度,0表示按行切分,1表示案列切分
num:默认值不用管

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
matrix = np.arange(12).reshape([3, 4])
// 表示将第二维切成4等份
split_result = tf.split(matrix, 4, 1)
print "original matrix: \n", matrix
with tf.Session() as sess:
result = sess.run(split_result)
print result
## 输出为
[array([[0],
[4],
[8]]), array([[1],
[5],
[9]]), array([[ 2],
[ 6],
[10]]), array([[ 3],
[ 7],
[11]])]

当num_or_size_split为矩阵时,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
matrix = np.arange(12).reshape([3, 4])
// 表示将第二维切成1:3的两部分
split_result = tf.split(matrix, [1, 3], 1)
print "original matrix: \n", matrix
with tf.Session() as sess:
result = sess.run(split_result)
print result
// 输出结果为:
[array([[0],
[4],
[8]]), array([[ 1, 2, 3],
[ 5, 6, 7],
[ 9, 10, 11]])]

tensorflow与GPU

tf.ConfigProto

tf.ConfigProto在多GPU机器上的配置信息,比较重要。

tensorflow与layers

tf.layers模块里面包含tf定义的一些层,包括全连接层,池化层等。

tf.layers.max_pooling2d

1
2
3
4
def max_pooling2d(inputs,
pool_size, strides,
padding='valid', data_format='channels_last',
name=None):

类似的还包括max_pooling1d, max_pooling3d,用法可查文档。

tf.layers.dense

定义的是全连接层。