TensorFlow—相关

来源:互联网 发布:sql查询相同身份证号 编辑:程序博客网 时间:2024/05/17 22:48

主要来自:http://blog.csdn.net/u012436149/article/details/53140869

1、TensorFlow的运行说明:

TensorFlow把复杂的计算放在Python之外完成,但是因为计算完成之后要切回到Python,为了避免这种开销,TensorFlow不单独地运行单一的复杂计算,而是让我们可以先用图描述一系列可交互的计算操作,然后全部一起在Python之外运行。

2、placeholder:

x=tf.placeholder(tf.float32,[None,784])

其中x不是一个特定的值,而是一个占位符placeholder,在TensorFlow运行计算时输入这个值,这里[None,784]是个2维的张量,第一维None表示可以是任意长度(大小),第二维长度(大小)是784。

//2017/3/27

1、tensorflow ConfigProto有什么用:

tf.ConfigProto一般用在创建session的时候。用来对session进行参数配置,参数包括:

a)记录设备指派情况:为了获取你的 operations 和 Tensor 被指派到哪个设备上运行, 用 log_device_placement 新建一个 session, 并设置为 True:sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))这将会打印出各个操作在哪个设备(cpu或者gpu)上运行。

另:可以手动设置哪些操作在cpu或者GPU上运行,即:with tf.device('/cpu:0'):,这就设定了设备环境为cpu0,在这个设备环境下的操作都将在cpu0上进行。

b)为了避免出现你指定的设备不存在这种情况, 你可以在创建的 session 里把参数 allow_soft_placement 设置为 True, 这样 tensorFlow 会自动选择一个存在并且支持的设备来运行 operation.

c)config = tf.ConfigProto()
config.gpu_options.allow_growth = True:刚一开始分配少量的GPU容量,然后按需慢慢的增加,由于不会释放内存,所以会导致碎片。

d)gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.7)
config=tf.ConfigProto(gpu_options=gpu_options):设置每个GPU应该拿出多少容量给进程使用,0.4代表 40%

参考:http://blog.csdn.net/u012436149/article/details/53837651

http://wiki.jikexueyuan.com/project/tensorflow-zh/how_tos/using_gpu.html

e)intra_op_parallelism_threads和inter_op_parallelism_threads:Choose how many cores to use

参考:http://stackoverflow.com/questions/41233635/tensorflow-inter-and-intra-op-parallelism-configuration

2、TensorFlow的共享变量:

数据的初始化式:random_uniform_initializer(min,max,seed,dtype):创建一个大小在min和max之间的一系列数据。

参考:http://wiki.jikexueyuan.com/project/tensorflow-zh/how_tos/variable_scope/index.html

//2017/3/28

1、TensorFlow的reshape中索引为负一(-1)的情况:

# tensor 't' is [[[1, 1, 1],
#                 [2, 2, 2]],
#                [[3, 3, 3],
#                 [4, 4, 4]],
#                [[5, 5, 5],
#                 [6, 6, 6]]]

# -1 is inferred to be 2:
reshape(t, [-1, 9]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]

//2017/4/1

1、TensorFlow如何运用构建的rnn模型,加载训练好的参数,进行测试:

with tf.Session(graph=self.graph) as session:

    saver = tf.train.Saver()

    saver.restore(session,ckpt_file)

其中self.graph是已经构建的rnn网络,而ckpt_file是保存的模型参数文件

2、如何将要测试的数据输入到网络中,进行测试:

对于网络中的测试节点,调用eval()方法,以feed的方式传入要测试的数据:

 self.sample_prediction = tf.nn.softmax(tf.nn.xw_plus_b(sample_output, w, b))     这个是网络graph(图)的节点——(预测)的节点

真正的运行测试:

prediction=self.sample_prediction.eval({self.sample_input:[sample_input], self.keep_prob:1.0})

//2017/4/12

1、tf的命令行解析:

flags = tf.flags,这里flags是一个文件:flags.py,用于处理命令行参数的解析工作:

调用flags内部的DEFINE_string函数来制定解析规则:
flags.DEFINE_string("para_name_1","default_val", "description")
flags.DEFINE_bool("para_name_2","default_val", "description")

FLAGS是一个对象,保存了解析后的命令行参数:
FLAGS = flags.FLAGS

调用命令行输入的参数:

def main(_):

FLAGS.para_name

解析命令行参数,调用main函数 main(sys.argv):

if __name__ = "__main__":

tf.app.run()

调用方式:

python script.py --para_name_1=name --para_name_2=name2

2、tf.reduce_**:



3、tf.concat:

t1 = [[1, 2, 3], [4, 5, 6]]
t2 = [[7, 8, 9], [10, 11, 12]]

tf.concat(0, [t1, t2]) ==> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

tf.concat(1, [t1, t2]) ==> [[1, 2, 3, 7, 8, 9], [4, 5, 6, 10, 11, 12]]

4、tf.squeeze:

# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t)) ==> [2, 3]
Or, to remove specific size 1 dimensions:
# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t, [2, 4])) ==> [1, 2, 3, 1]

5、tf.split:

 'value' is a tensor with shape [5, 30]
 Split 'value' into 3 tensors along dimension 1
split0, split1, split2 = tf.split(1, 3, value)
tf.shape(split0) ==> [5, 10]

6、embedding:

mat = np.array([1,2,3,4,5,6,7,8,9]).reshape((3,-1))
ids = [[1,2], [0,1]]
res = tf.nn.embedding_lookup(mat, ids)
res.eval()
array([[[4, 5, 6],
        [7, 8, 9]],


       [[1, 2, 3],
        [4, 5, 6]]])

6、tf.expand_dims:

# 't' is a tensor of shape [2]
#一次扩展一维
shape(tf.expand_dims(t, 0)) ==> [1, 2]
shape(tf.expand_dims(t, 1)) ==> [2, 1]
shape(tf.expand_dims(t, -1)) ==> [2, 1]
# 't2' is a tensor of shape [2, 3, 5]
shape(tf.expand_dims(t2, 0)) ==> [1, 2, 3, 5]
shape(tf.expand_dims(t2, 2)) ==> [2, 3, 1, 5]
shape(tf.expand_dims(t2, 3)) ==> [2, 3, 5, 1]

7、tf.slice:

x=[[1,2,3],[4,5,6]] 

begin_x=[1,0]

size_x=[1,2]

out=tf.slice(x,begin_x,size_x):结果:[[4 5]] 

y=np.arange(24).reshape([2,3,4])

begin_y=[1,0,0]  
size_y=[1,2,3]  
out=tf.slice(y,begin_y,size_y) : 结果:[[[12 13 14] [16 17 18]]] 

a = np.array([[1,2,3,4,5],[4,5,6,7,8],[9,10,11,12,13]])
tf.slice(a,[1,2],[-1,2]).eval():结果:array([[ 6,  7], [11, 12]])

8、tf.stack:

# 'x' is [1, 4]
# 'y' is [2, 5]
# 'z' is [3, 6]
stack([x, y, z]) => [[1, 4], [2, 5], [3, 6]]  # Pack along first dim.
stack([x, y, z], axis=1) => [[1, 2, 3], [4, 5, 6]]

9、tf.gather:

temp = tf.range(0,10)*10 + tf.constant(1,shape=[10])
temp2 = tf.gather(temp,[1,5,9])

temp:[ 1 11 21 31 41 51 61 71 81 91] 
tmp2:[11 51 91]

10、tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None):(http://blog.csdn.net/mao_xiao_feng/article/details/53382790)

第一个参数logits:就是神经网络最后一层的输出,如果有batch的话,它的大小就是[batchsize,num_classes],单样本的话,大小就是num_classes
第二个参数labels:实际的标签,大小同上

具体的执行流程大概分为两步:
第一步是先对网络最后一层的输出做一个softmax,这一步通常是求取输出属于某一类的概率,对于单样本而言,输出就是一个num_classes大小的向量([Y1,Y2,Y3...]其中Y1,Y2,Y3...分别代表了是属于该类的概率)

softmax的公式是:

第二步是softmax的输出向量[Y1,Y2,Y3...]和样本的实际标签做一个交叉熵,公式如下:


其中指代实际的标签中第i个的值(用mnist数据举例,如果是3,那么标签是[0,0,0,1,0,0,0,0,0,0],除了第4个值为1,其他全为0)
就是softmax的输出向量[Y1,Y2,Y3...]中,第i个元素的值
显而易见,预测越准确,结果的值越小(别忘了前面还有负号),最后求一个平均,得到我们想要的loss
注意!!!这个函数的返回值并不是一个数,而是一个向量,如果要求交叉熵,我们要再做一步tf.reduce_sum操作,就是对向量里面所有元素求和,最后才得到,如果求loss,则要做一步tf.reduce_mean操作,对向量求均值!

例子:

import tensorflow as tf  
  
#our NN's output  
logits=tf.constant([[1.0,2.0,3.0],[1.0,2.0,3.0],[1.0,2.0,3.0]])  
#step1:do softmax  
y=tf.nn.softmax(logits)  
#true label  
y_=tf.constant([[0.0,0.0,1.0],[0.0,0.0,1.0],[0.0,0.0,1.0]])  
#step2:do cross_entropy  
cross_entropy = -tf.reduce_sum(y_*tf.log(y))  
#do cross_entropy just one step  
cross_entropy2=tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(logits, y_))#dont forget tf.reduce_sum()!!  
  
with tf.Session() as sess:  
    softmax=sess.run(y)  
    c_e = sess.run(cross_entropy)  
    c_e2 = sess.run(cross_entropy2)  
    print("step1:softmax result=")  
    print(softmax)  
    print("step2:cross_entropy result=")  
    print(c_e)  
    print("Function(softmax_cross_entropy_with_logits) result=")  
    print(c_e2)  

结果:

step1:softmax result=  
[[ 0.09003057  0.24472848  0.66524094]  
 [ 0.09003057  0.24472848  0.66524094]  
 [ 0.09003057  0.24472848  0.66524094]]  
step2:cross_entropy result=  1.22282  
Function(softmax_cross_entropy_with_logits) result=  1.2228  

结论:最后大家可以试试e^1/(e^1+e^2+e^3)是不是0.09003057,发现确实一样!!这也证明了输出是符合公式逻辑的

11、tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None):(http://dataunion.org/26447.html)

交叉熵(Cross Entropy)是Loss函数的一种(也称为损失函数或代价函数),用于描述模型预测值与真实值的差距大小。

交叉熵的定义如下:


这里用于计算的“a”也是经过sigmoid激活的,取值范围在0到1。

sigmoid_cross_entropy_with_logits,为什么呢,因为它的实现和前面的交叉熵算法定义是一样的,也是TensorFlow最早实现的交叉熵算法。这个函数的输入是logits和targets,logits就是神经网络模型中的 W * X矩阵,注意不需要经过sigmoid,而targets的shape和logits相同,就是正确的label值,例如这个模型一次要判断100张图是否包含10种动物,这两个输入的shape都是[100, 10]。注释中还提到这10个分类之间是独立的、不要求是互斥,这种问题我们成为多目标,例如判断图片中是否包含10种动物,label值可以包含多个1或0个1,还有一种问题是多分类问题,例如我们对年龄特征分为5段,只允许5个值有且只有1个值为1,这种问题可以直接用这个函数吗?答案是不可以,我们先来看看sigmoid_cross_entropy_with_logits的代码实现吧:


12、tesorflow中的激活函数(所有激活函数 输入 和 输出 的维度是一样的)

tf.nn.relu()
tf.nn.sigmoid()
tf.nn.tanh()
tf.nn.elu()
tf.nn.bias_add()
tf.nn.crelu()
tf.nn.relu6()
tf.nn.softplus()
tf.nn.softsign()
tf.nn.dropout()
tf.nn.relu_layer(x, weights, biases,name=None)

//2017/4/13

1、def nce_loss(nce_weights, nce_biases, embed, train_labels, num_sampled, vocabulary_size):(http://stackoverflow.com/questions/41475180/understanding-tf-nn-nce-loss-in-tensorflow)
#word2vec中用到了这个函数

2、def sequence_loss_by_example(logits, targets, weights,
                             average_across_timesteps=True,
                             softmax_loss_function=None, name=None):(http://blog.csdn.net/appleml/article/details/54017873)

3、保存tensorflow训练好的模型参数:

saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(init_op)
    #训练模型。。。
    #使用saver提供的简便方法去调用 save op
    saver.save(sess, "save_path/file_name.ckpt") #file_name.ckpt如果不存在的话,会自动创建
#后缀可加可不加

4、restore变量的子集,然后使用初始化op初始化其他变量
#想要实现这个功能的话,必须从Saver的构造函数下手
saver=tf.train.Saver([sub_set])
init = tf.initialize_all_variables()
with tf.Session() as sess:
  #这样你就可以使用restore的变量替换掉初始化的变量的值,而其它初始化的值不受影响
  sess.run(init)
  if restor_from_checkpoint:
      saver.restore(sess,"file.ckpt")
  # train
  saver.save(sess,"file.ckpt")

5、TensorFlow的cnn操作:

http://www.jianshu.com/p/e3a79eac554f

6、dropout:

dropout对于防止过拟合效果不错 
dropout一般用在全连接的部分,卷积部分不会用到dropout,输出曾也不会使用dropout:

另种dropout:

1.tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None) 
2.tf.nn.rnn_cell.DropoutWrapper(rnn_cell, input_keep_prob=1.0, output_keep_prob=1.0)

7、获取feed数据的维度tf.shape(x)和tensor.get_shape():

1、shape=tf.placeholder(tf.float32, shape=[None, 227,227,3] )
我们经常会这样来feed数据,如果在运行的时候想知道None到底是多少,这时候,只能通过tf.shape(x)[0]这种方式来获得.

2、tensor.get_shape()
只有tensor有这个方法, 返回是一个tuple

3、local_response_normalization:

local_response_normalization出现在论文”ImageNet Classification with deep Convolutional Neural Networks”中,论文中说,这种normalization对于泛化是有好处的:

bix,y=aix,y(k+αmin(0,i+n/2)j=max(0,in/2)(ajx,y)2)β

经过了一个conv2d或pooling后,我们获得了[batch_size, height, width, channels]这样一个tensor.现在,将channels称之为层,不考虑batch_size 
- i代表第i层 
- aix,y就代表 第i层的 (x,y)位置所对应的值 
- n个相邻feature maps. 
- k...α...n...β是hyper parameters 
- 可以看出,这个函数的功能就是, aix,y需要用他的相邻的map的同位置的值进行normalization 
在alexnet中, k=2,n=5,α=10−4,β=0.75

tf.nn.local_response_normalization(input, depth_radius=None, bias=None, alpha=None, beta=None, name=None):

depth_radius: 就是公式里的n/2
bias : 公式里的k
input: 将conv2d或pooling 的输出输入就行了[batch_size, height, width, channels]
return :[batch_size, height, width, channels], 正则化后

4、batch_normalization:

batch_normalization, 故名思意,就是以batch为单位进行normalization

 输入:mini_batch: In={x1,x2,..,xm} 
γ,β,需要学习的参数,都是向量 
ϵ: 一个常量 
- 输出: Out={y1,y2,...,ym} 

算法如下: 
(1)mini_batch mean: 

μIn1mi=1mxi

(2)mini_batch variance 
σ2In=1mi=1m(xiμIn)2

(3)Normalize 
x^i=xiμInσ2In+ϵ

(4)scale and shift 
yi=γx^i+β

可以看出,batch_normalization之后,数据的维数没有任何变化,只是数值发生了变化 

Out作为下一层的输入 

def batch_normalization(x,mean, variance, offset,scale,variance_epsilon,name=None):

5、tensorflow中操作gradient-clip:

在训练深度神经网络的时候,我们经常会碰到梯度消失和梯度爆炸问题,scientists提出了很多方法来解决这些问题,本篇就介绍一下如何在tensorflow中使用clip来address这些问题:

train_op = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)

在调用minimize方法的时候,底层实际干了两件事: 
- 计算所有 trainable variables 梯度 
- apply them to variables
随后, 在我们 sess.run(train_op) 的时候, 会对 variables 进行更新

clip:
那我们如果想处理一下计算完的 gradients ,那该怎么办呢? 
官方给出了以下步骤 
1. Compute the gradients with compute_gradients(). 计算梯度 
2. Process the gradients as you wish. 处理梯度 
3. Apply the processed gradients with apply_gradients(). apply处理后的梯度给variables
这样,我们以后在train的时候就会使用 processed gradient去更新 variable 

框架:
# Create an optimizer.optimizer必须和variable在一个设备上声明
opt = GradientDescentOptimizer(learning_rate=0.1)


# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)


# grads_and_vars is a list of tuples (gradient, variable).  Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]


# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)

例子:
#return a list of trainable variable in you model
params = tf.trainable_variables()


#create an optimizer
opt = tf.train.GradientDescentOptimizer(self.learning_rate)


#compute gradients for params
gradients = tf.gradients(loss, params)


#process gradients
clipped_gradients, norm = tf.clip_by_global_norm(gradients,max_gradient_norm)


train_op = opt.apply_gradients(zip(clipped_gradients, params)))

这时, sess.run(train_op) 就可以进行训练了

//2017/4/14

1、variable scop:

get_varibale.name 以创建变量的 scope 作为名字的prefix:

with variable_scope.variable_scope("tet1"):
    var3 = tf.get_variable("var3",shape=[2],dtype=tf.float32)
    print var3.name
    with variable_scope.variable_scope("tet2"):
        var4 = tf.get_variable("var4",shape=[2],dtype=tf.float32)
        print var4.name
#输出为****************
#tet1/var3:0
#tet1/tet2/var4:0
#*********************

def te2():
    with variable_scope.variable_scope("te2"):
        var2 = tf.get_variable("var2",shape=[2], dtype=tf.float32)
        print var2.name
        def te1():
            with variable_scope.variable_scope("te1"):
                var1 = tf.get_variable("var1", shape=[2], dtype=tf.float32)
            return var1
        return te1() #在scope te2 内调用的
res = te2()
print res.name
#输出*********************
#te2/var2:0
#te2/te1/var1:0
#************************

观察和上个程序的不同:

def te2():
    with variable_scope.variable_scope("te2"):
        var2 = tf.get_variable("var2",shape=[2], dtype=tf.float32)
        print var2.name
        def te1():
            with variable_scope.variable_scope("te1"):
                var1 = tf.get_variable("var1", shape=[2], dtype=tf.float32)
            return var1
    return te1()  #在scope te2外面调用的
res = te2()
print res.name
#输出*********************
#te2/var2:0
#te1/var1:0
#************************

需要注意一点的是tf.variable_scope("name") 与 tf.variable_scope(scope)的区别,看下面代码:

import tensorflow as tf
with tf.variable_scope("scope"):
    tf.get_variable("w",shape=[1])#这个变量的name是 scope/w
    with tf.variable_scope("scope"):
        tf.get_variable("w", shape=[1]) #这个变量的name是 scope/scope/w
# 这两个变量的名字是不一样的,所以不会产生冲突

对比:

import tensorflow as tf
with tf.variable_scope("yin"):
    tf.get_variable("w",shape=[1])
    scope = tf.get_variable_scope()#这个变量的name是 scope/w
    with tf.variable_scope(scope):#这种方式设置的scope,是用的外部的scope
        tf.get_variable("w", shape=[1])#这个变量的name也是 scope/w
# 两个变量的名字一样,会报错

2、共享变量
共享变量的前提是,变量的名字是一样的,变量的名字是由变量名和其scope前缀一起构成, tf.get_variable_scope().reuse_variables() 是允许共享当前scope下的所有变量。reused variables可以看同一个节点。

例子:

def my_image_filter(input_images):
    conv1_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]),
        name="conv1_weights")
    conv1_biases = tf.Variable(tf.zeros([32]), name="conv1_biases")
    conv1 = tf.nn.conv2d(input_images, conv1_weights,
        strides=[1, 1, 1, 1], padding='SAME')
    return  tf.nn.relu(conv1 + conv1_biases)

有两个变量(Variables)conv1_weighs,conv1_biases和一个操作(op)conv1,如果直接调用两次,不会出什么问题,但是会生成两套变量:

result1 = my_image_filter(image1)

result2 = my_image_filter(image2)

上面两个式子没有问题,但是如果把tf.variable改成tg.get_variable,还像上面两式那样调用,就会出问题:

result1 = my_image_filter(image1)

result2 = my_image_filter(image2)

解决上面问题的方法就是使用tf.variable_scope及在一个作用于scope内共享一些变量:

1)

with tf.variable_scope("image_filters") as scope:
    result1 = my_image_filter(image1)
    scope.reuse_variables() # or 
    #tf.get_variable_scope().reuse_variables()
    result2 = my_image_filter(image2)

2)

with tf.variable_scope("image_filters1") as scope1:
    result1 = my_image_filter(image1)
with tf.variable_scope(scope1, reuse = True)
    result2 = my_image_filter(image2)

3、name_scope和variable_scope:

两个scope的区别:

程序1:

with tf.name_scope("hello") as name_scope:
    arr1 = tf.get_variable("arr1", shape=[2,10],dtype=tf.float32)


    print name_scope
    print arr1.name
    print "scope_name:%s " % tf.get_variable_scope().original_name_scope

输出:

hello/ 
arr1:0 
scope_name:

可以看出: 
- tf.name_scope() 返回的是 一个string,”hello/” 
- 在name_scope中定义的variable的name并没有 “hello/”前缀 
- tf.get_variable_scope()的original_name_scope 是空

程序2:

with tf.variable_scope("hello") as variable_scope:
    arr1 = tf.get_variable("arr1", shape=[2, 10], dtype=tf.float32)


    print variable_scope
    print variable_scope.name #打印出变量空间名字
    print arr1.name
    print tf.get_variable_scope().original_name_scope
    #tf.get_variable_scope() 获取的就是variable_scope


    with tf.variable_scope("xixi") as v_scope2:
        print tf.get_variable_scope().original_name_scope
        #tf.get_variable_scope() 获取的就是v _scope2

输出:

<tensorflow.python.ops.variable_scope.VariableScope object at 0x7f82741e7910>
hello
hello/arr1:0
hello/
hello/xixi/

程序3:

with tf.name_scope("name1"):
    with tf.variable_scope("var1"):
        w = tf.get_variable("w",shape=[2])
        res = tf.add(w,[3])
print w.name
print res.name
输出:
var1/w:0
name1/var1/Add:0

可以看出对比三个个程序可以看出: 
- name_scope对 get_variable()创建的变量 的名字不会有任何影响,而创建的op会被加上前缀. 
- tf.get_variable_scope() 返回的只是 variable_scope,不管 name_scope.所以以后我们在使用tf.get_variable_scope().reuse_variables() 时可以无视name_scope:variable scope和name scope都会给op的name加上前缀

其它:

1)

with tf.name_scope("scope1") as scope1:
    with tf.name_scope("scope2") as scope2:
        print scope2
#输出:scope1/scope2/

2)

import tensorflow as tf
with tf.variable_scope("scope1") as scope1:
    with tf.variable_scope("scope2") as scope2:
        print scope2.name
#输出:scope1/scope2

简单来看 
1. 使用tf.Variable()的时候,tf.name_scope()和tf.variable_scope() 都会给 Variable 和 op 的 name属性加上前缀。 
2. 使用tf.get_variable()的时候,tf.name_scope()就不会给 tf.get_variable()创建出来的Variable加前缀。

name_scope可以用来干什么:

典型的 TensorFlow 可以有数以千计的节点,如此多而难以一下全部看到,甚至无法使用标准图表工具来展示。为简单起见,我们为变量名划定范围,并且可视化把该信息用于在图表中的节点上定义一个层级。默认情况下, 只有顶层节点会显示。下面这个例子使用tf.name_scope在hidden命名域下定义了三个操作:

import tensorflow as tf


with tf.name_scope('hidden') as scope:
  a = tf.constant(5, name='alpha')
  W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0), name='weights')
  b = tf.Variable(tf.zeros([1]), name='biases')
  print a.name
  print W.name
  print b.name

结果是得到了下面三个操作名:
hidden/alpha 
hidden/weights 
hidden/biases 
name_scope 是给op_name加前缀, variable_scope是给variable_name加前缀.也许有人会问,get_variable()返回的不也是op吗?我们可以这么理解,只有get_variable().name打印出来的是variable_name

tf.variable_scope有时也会处理命名冲突:

import tensorflow as tf
def test(name=None):
    with tf.variable_scope(name, default_name="scope") as scope:
        w = tf.get_variable("w", shape=[2, 10])


test()
test()
ws = tf.trainable_variables()
for w in ws:
    print(w.name)


#scope/w:0
#scope_1/w:0
#可以看出,如果只是使用default_name这个属性来创建variable_scope
#的时候,会处理命名冲突

3、Multiple GPUs(多GPU):


如何设置训练系统

(1)每个GPU上都会有model的副本 
(2)对模型的参数进行同步更新

抽象名词:

计算单个副本inference和 gradients 的函数称之为tower,使用tf.name_scope()为tower中的每个op_name加上前缀
使用tf.device('/gpu:0') 来指定tower中op的运算设备
框架:

with tf.Graph().as_default(), tf.device('/cpu:0'):
    # Create an optimizer that performs gradient descent.
    opt = tf.train.GradientDescentOptimizer(lr)
    tower_grads=[]
    for i in xrange(FLAGS.num_gpus):
        with tf.device('/gpu:%d' % i):
            with tf.name_scope('%s_%d' % (TOWER_NAME, i)) as scope:
                #这里定义你的模型
                #ops,variables

                #损失函数
                loss = yourloss
                # Reuse variables for the next tower.
                tf.get_variable_scope().reuse_variables()

                # Calculate the gradients for the batch of data on this tower.
                grads = opt.compute_gradients(loss)

                # Keep track of the gradients across all towers.
                tower_grads.append(grads)
    # We must calculate the mean of each gradient. Note that this is the
    # synchronization point across all towers.
    grads = average_gradients(tower_grads)

    # Apply the gradients to adjust the shared variables.
    apply_gradient_op = opt.apply_gradients(grads)

4、gRPC(Google remote procedure call)谷歌远程过程调用:

分布式TensorFlow底层的通信是gRPC,这个通信就是:假设你在本机上执行一段代码num=add(a,b),它调用了一个过程 call,然后返回了一个值num,你感觉这段代码只是在本机上执行的, 但实际情况是,本机上的add方法是将参数打包发送给服务器,然后服务器运行服务器端的add方法,返回的结果再将数据打包返回给客户端.

5、cluster、job及Task:cluster.Job.Task:

Job是Task的集合. 

Cluster是Job的集合

6、为什么要分成Cluster,Job,和Task呢?
首先,我们介绍一下Task:Task就是主机上的一个进程,在大多数情况下,一个机器上只运行一个Task.

为什么Job是Task的集合呢? 在分布式深度学习框架中,我们一般把Job划分为Parameter和Worker,Parameter Job是管理参数的存储和更新工作.Worker Job是来运行ops.如果参数的数量太大,一台机器处理不了,这就要需要多个Tasks.

Cluster 是 Jobs 的集合: Cluster(集群),就是我们用的集群系统了

7、如何创建集群

从上面的描述我们可以知道,组成Cluster的基本单位是Task(动态上理解,主机上的一个进程,从静态的角度理解,Task就是我们写的代码).我们只需编写Task代码,然后将代码运行在不同的主机上,这样就构成了Cluster(集群)。

8、如何编写Task代码
首先,Task需要知道集群上都有哪些主机,以及它们都监听什么端口.tf.train.ClusterSpec()就是用来描述这个:

tf.train.ClusterSpec({
    "worker": [
        "worker_task0.example.com:2222",# /job:worker/task:0 运行的主机
        "worker_task1.example.com:2222",# /job:worker/task:1 运行的主机
        "worker_task2.example.com:2222"# /job:worker/task:3 运行的主机
    ],
    "ps": [
        "ps_task0.example.com:2222",  # /job:ps/task:0 运行的主机
        "ps_task1.example.com:2222"   # /job:ps/task:0 运行的主机
    ]})

这个ClusterSec告诉我们,我们这个Cluster(集群)有两个Job(worker.ps),worker中有三个Task(即,有三个Task执行Tensorflow op操作)

9、然后,将ClusterSpec当作参数传入到 tf.train.Server()中,同时指定此Task的Job_name和task_index.

#jobName和taskIndex是函数运行时,通过命令行传递的参数
server = tf.train.Server(cluster, job_name=jobName, task_index=taskIndex)

10、下面代码描述的是,一个cluster中有一个Job,叫做(worker), 这个job有两个task,这两个task是运行在两个主机上的:

#在主机(10.1.1.1)上,实际是运行以下代码
cluster = tf.train.ClusterSpec({"worker": ["10.1.1.1:2222", "10.1.1.2:3333"]})
server = tf.train.Server(cluster, job_name="local", task_index=0)

#在主机(10.1.1.2)上,实际运行以下代码
cluster = tf.train.ClusterSpec({"worker": ["10.1.1.1:2222", "10.1.1.2:3333"]})
server = tf.train.Server(cluster, job_name="local", task_index=1)

11、tf.trian.Server干了些什么呢? 
首先,一个tf.train.Server包含了: 本地设备(GPUs,CPUs)的集合,可以连接到到其它task的ip:port(存储在cluster中), 还有一个session target用来执行分布操作.还有最重要的一点就是,它创建了一个服务器,监听port端口,如果有数据传过来,他就会在本地执行(启动session target,调用本地设备执行运算),然后结果返回给调用者.

12、我们继续来写我们的task代码:在你的model中指定分布式设备


with tf.device("/job:ps/task:0"):
  weights_1 = tf.Variable(...)
  biases_1 = tf.Variable(...)


with tf.device("/job:ps/task:1"):
  weights_2 = tf.Variable(...)
  biases_2 = tf.Variable(...)


with tf.device("/job:worker/task:0"): #映射到主机(10.1.1.1)上去执行
  input, labels = ...
  layer_1 = tf.nn.relu(tf.matmul(input, weights_1) + biases_1)
  logits = tf.nn.relu(tf.matmul(layer_1, weights_2) + biases_2)
with tf.device("/job:worker/task:1"): #映射到主机(10.1.1.2)上去执行
  input, labels = ...
  layer_1 = tf.nn.relu(tf.matmul(input, weights_1) + biases_1)
  logits = tf.nn.relu(tf.matmul(layer_1, weights_2) + biases_2)
  # ...
  train_op = ...
with tf.Session("grpc://10.1.1.2:3333") as sess:#在主机(10.1.1.2)上执行run
  for _ in range(10000):
    sess.run(train_op)

其中:with tf.Session("grpc://..")是指定gprc://..为master,master将op分发给对应的task。

13、写分布式程序时,我们需要关注一下问题: 
(1) 使用In-graph replication还是Between-graph replication

In-graph replication:一个client(显示调用tf::Session的进程),将里面的参数和ops指定给对应的job去完成.数据分发只由一个client完成.

Between-graph replication:下面的代码就是这种形式,有很多独立的client,各个client构建了相同的graph(包含参数,通过使用tf.train.replica_device_setter,将这些参数映射到ps_server上.)

另外的解释:

在TensorFlow中启动分布式深度学习模型训练任务也有两种模式。一种为In-graph replication。在这种模式下神经网络的参数会都保存在同一个TensorFlow计算图中,只有计算会分配到不同计算服务器。另一种为Between-graph replication,这种模式下所有的计算服务器也会创建参数,但参数会通过统一的方式分配到参数服务器。因为In-graph replication处理海量数据的能力稍弱,所以Between-graph replication是一个更加常用的模式。

(2)同步训练,还是异步训练

Synchronous training:在这种方式中,每个graph的副本读取相同的parameter的值,并行的计算gradients,然后将所有计算完的gradients放在一起处理.Tensorlfow提供了函数(tf.train.SyncReplicasOptimizer)来处理这个问题(在Between-graph replication情况下),在In-graph replication将所有的gradients平均就可以了

Asynchronous training:自己计算完gradient就去更新paramenter,不同replica之间不会去协调进度。


14、一个完整的例子,来自官网:

import tensorflow as tf
# Flags for defining the tf.train.ClusterSpec
tf.app.flags.DEFINE_string("ps_hosts", "",
                           "Comma-separated list of hostname:port pairs")
tf.app.flags.DEFINE_string("worker_hosts", "",
                           "Comma-separated list of hostname:port pairs")

# Flags for defining the tf.train.Server
tf.app.flags.DEFINE_string("job_name", "", "One of 'ps', 'worker'")
tf.app.flags.DEFINE_integer("task_index", 0, "Index of task within the job")

FLAGS = tf.app.flags.FLAGS

由于是相同的代码运行在不同的主机上,所以要传入job_name和task_index加以区分,而ps_hosts和worker_hosts对于所有主机来说,都是一样的,用来描述集群的。

def main(_):
  ps_hosts = FLAGS.ps_hosts.split(",")
  worker_hosts = FLAGS.worker_hosts.split(",")

  # Create a cluster from the parameter server and worker hosts.
  cluster = tf.train.ClusterSpec({"ps": ps_hosts, "worker": worker_hosts})

  # Create and start a server for the local task.
  server = tf.train.Server(cluster,
                           job_name=FLAGS.job_name,
                           task_index=FLAGS.task_index)

  if FLAGS.job_name == "ps":
    server.join()

我们都知道,服务器进程如果执行完的话,服务器就会关闭.为了是我们的ps_server能够一直处于监听状态,我们需要使用server.join().这时,进程就会block在这里.至于为什么ps_server刚创建就join呢:原因是因为下面的代码会将参数指定给ps_server保管,所以ps_server静静的监听就好了:

elif FLAGS.job_name == "worker":


    # Assigns ops to the local worker by default.
    with tf.device(tf.train.replica_device_setter(
        worker_device="/job:worker/task:%d" % FLAGS.task_index,
        cluster=cluster)):

其中:tf.train.replica_device_setter(ps_tasks=0, ps_device='/job:ps', worker_device='/job:worker', merge_devices=True, cluster=None, ps_ops=None)),返回值可以被tf.device使用,指明下面代码中variable和ops放置的设备。

example:


# To build a cluster with two ps jobs on hosts ps0 and ps1, and 3 worker
# jobs on hosts worker0, worker1 and worker2.
cluster_spec = {
    "ps": ["ps0:2222", "ps1:2222"],
    "worker": ["worker0:2222", "worker1:2222", "worker2:2222"]}
with tf.device(tf.replica_device_setter(cluster=cluster_spec)):
  # Build your graph
  v1 = tf.Variable(...)  # assigned to /job:ps/task:0
  v2 = tf.Variable(...)  # assigned to /job:ps/task:1
  v3 = tf.Variable(...)  # assigned to /job:ps/task:0
# Run compute

这个例子是没有指定参数worker_device和ps_device的,你可以手动指定。

继续代码注释,下面就是,模型的定义了:
      # Build model...variables and ops
      loss = ...
      global_step = tf.Variable(0)

      train_op = tf.train.AdagradOptimizer(0.01).minimize(
          loss, global_step=global_step)

      saver = tf.train.Saver()
      summary_op = tf.merge_all_summaries()
      init_op = tf.initialize_all_variables()

    # Create a "supervisor", which oversees the training process.
    sv = tf.train.Supervisor(is_chief=(FLAGS.task_index == 0),
                             logdir="/tmp/train_logs",
                             init_op=init_op,
                             summary_op=summary_op,
                             saver=saver,
                             global_step=global_step,
                             save_model_secs=600)

    # The supervisor takes care of session initialization, restoring from
    # a checkpoint, and closing when done or an error occurs.
    with sv.managed_session(server.target) as sess:
      # Loop until the supervisor shuts down or 1000000 steps have completed.
      step = 0
      while not sv.should_stop() and step < 1000000:
        # Run a training step asynchronously.
        # See `tf.train.SyncReplicasOptimizer` for additional details on how to
        # perform *synchronous* training.
        _, step = sess.run([train_op, global_step])

    # Ask for all the services to stop.
    sv.stop()

考虑一个场景(Between-graph),我们有一个parameter server(存放着参数的副本),有好几个worker server(分别保存着相同的graph的副本).更通俗的说,我们有10台电脑,其中一台作为parameter server,其余九台作为worker server.因为同一个程序在10台电脑上同时运行(不同电脑,job_name,task_index不同),所以每个worker server上都有我们建立的graph的副本(replica).这时我们可以使用Supervisor帮助我们管理各个process.Supervisor的is_chief参数很重要,它指明用哪个task进行参数的初始化工作.sv.managed_session(server.target)创建一个被sv管理的session

if __name__ == "__main__":
  tf.app.run()

To start the trainer with two parameter servers and two workers, use the following command line (assuming the script is called trainer.py):


# On ps0.example.com:
$ python trainer.py \
     --ps_hosts=ps0.example.com:2222,ps1.example.com:2222 \
     --worker_hosts=worker0.example.com:2222,worker1.example.com:2222 \
     --job_name=ps --task_index=0
# On ps1.example.com:
$ python trainer.py \
     --ps_hosts=ps0.example.com:2222,ps1.example.com:2222 \
     --worker_hosts=worker0.example.com:2222,worker1.example.com:2222 \
     --job_name=ps --task_index=1
# On worker0.example.com:
$ python trainer.py \
     --ps_hosts=ps0.example.com:2222,ps1.example.com:2222 \
     --worker_hosts=worker0.example.com:2222,worker1.example.com:2222 \
     --job_name=worker --task_index=0
# On worker1.example.com:
$ python trainer.py \
     --ps_hosts=ps0.example.com:2222,ps1.example.com:2222 \
     --worker_hosts=worker0.example.com:2222,worker1.example.com:2222 \
     --job_name=worker --task_index=1

可以看出,我们只需要写一个程序,在不同的主机上,传入不同的参数使其运行。

15、分布式TensorFlow注意事项:

版本 tensorflow0.11.0

适用于 between-graph&synchronous

(1) 一定要指定 chief task

(2) chief task 要增加两个op:

init_token_op = opt.get_init_tokens_op()
chief_queue_runner = opt.get_chief_queue_runner()

(3) chief task要执行上面两个op:

sv.start_queue_runners(sess, [chief_queue_runner])
sess.run(init_token_op)

(4) 使用 sv.prepare_or_wait_for_session创建sess的时候,一定不要使用with block
# wrong:
with sv.prepare_or_wait_for_session(server.target) as sess:
  ...

会出现错误: 只有chief task在训练,other task一直打印start master session...,不知是什么原因.

# right
sess = sv.prepare_or_wait_for_session(server.target)

(5) opt.minimize()或opt.apply_gradients()的时候一定要传入global_step(用来同步的)
(6) 创建sv的时候,一定要传入logdir(共享文件夹).简便方法:传入log_dir = tempfile.mktemp()

16、tensorflow的可视化是使用summary和tensorboard合作完成的.

17、首先明确一点,summary也是op。

18、输出网络结构
with tf.Session() as sess:
  writer = tf.summary.FileWriter(your_dir, sess.graph)

命令行运行tensorboard --logdir=your_dir,然后浏览器输入127.0.1.1:6006 
这样你就可以在tensorboard中看到你的网络结构图了

19、可视化参数:

#ops
loss = ...
tf.summary.scalar("loss", loss)
merged_summary = tf.summary.merge_all()

init = tf.global_variable_initializer()
with tf.Session() as sess:
  writer = tf.summary.FileWriter(your_dir, sess.graph)
  sess.run(init)
  for i in xrange(100):
    _,summary = sess.run([train_op,merged_summary], feed_dict)
    writer.add_summary(summary, i)

这时,打开tensorboard,在EVENTS可以看到loss随着i的变化了,如果看不到的话,可以在代码最后加上writer.flush()试一下,原因后面说明。

20、tf.summary.merge_all: 将之前定义的所有summary op整合到一起。

21、FileWriter: 创建一个file writer用来向硬盘写summary数据。

22、tf.summary.scalar(summary_tags, Tensor/variable): 用于标量的 summary

23、tf.summary.image(tag, tensor, max_images=3, collections=None, name=None):tensor,必须4维,形状[batch_size, height, width, channels],max_images(最多只能生成3张图片的summary),觉着这个用在卷积中的kernel可视化很好用.max_images确定了生成的图片是[-max_images: ,height, width, channels],还有一点就是,TensorBord中看到的image summary永远是最后一个global step的原因。

24、tf.summary.histogram(tag, values, collections=None, name=None):values,任意形状的tensor,生成直方图summary。

25、tf.summary.audio(tag, tensor, sample_rate, max_outputs=3, collections=None, name=None)。

26、FileWriter
注意:add_summary仅仅是向FileWriter对象的缓存中存放event data。而向disk上写数据是由FileWrite对象控制的。下面通过FileWriter的构造函数来介绍这一点:

tf.summary.FileWriter.__init__(logdir, graph=None, max_queue=10, flush_secs=120, graph_def=None)
#Creates a FileWriter and an event file.
# max_queue: 在向disk写数据之前,最大能够缓存event的个数
# flush_secs: 每多少秒像disk中写数据,并清空对象缓存

27、如果使用writer.add_summary(summary,global_step)时没有传global_step参数,会使scarlar_summary变成一条直线。

28、只要是在计算图上的Summary op,都会被merge_all捕捉到, 不需要考虑变量生存空间问题!

29、如果执行一次,disk上没有保存Summary数据的话,可以尝试下file_writer.flush()。

30、如果想要生成的summary有层次的话,记得在summary外面加一个name_scope
with tf.name_scope("summary_gradients"):
    tf.summary.histgram("name", gradients)

这样,tensorboard在显示的时候,就会有一个sumary_gradients一集目录。

//2017/4/17

1、tf.train.Supervisor 

在不使用Supervisor的时候,我们的代码经常是这么组织的:

variables
...
ops
...
summary_op
...
merge_all_summarie
saver
init_op

with tf.Session() as sess:
  writer = tf.tf.train.SummaryWriter()
  sess.run(init)
  saver.restore()
  for ...:
    train
    merged_summary = sess.run(merge_all_summarie)
    writer.add_summary(merged_summary,i)
  saver.save

下面介绍如何用Supervisor来改写上面程序:

import tensorflow as tf
a = tf.Variable(1)
b = tf.Variable(2)
c = tf.add(a,b)
update = tf.assign(a,c)
tf.scalar_summary("a",a)
init_op = tf.initialize_all_variables()
merged_summary_op = tf.merge_all_summaries()
sv = tf.train.Supervisor(logdir="/home/keith/tmp/",init_op=init_op) #logdir用来保存checkpoint和summary
saver=sv.saver #创建saver
with sv.managed_session() as sess: #会自动去logdir中去找checkpoint,如果没有的话,自动执行初始化
    for i in xrange(1000):
        update_ = sess.run(update)
        print update_
        if i % 10 == 0:
            merged_summary = sess.run(merged_summary_op)
            sv.summary_computed(sess, merged_summary,global_step=i)
        if i%100 == 0:
            saver.save(sess,logdir="/home/keith/tmp/",global_step=i)

总结

从上面代码可以看出,Supervisor帮助我们处理一些事情 
(1)自动去checkpoint加载数据或初始化数据 
(2)自身有一个Saver,可以用来保存checkpoint 
(3)有一个summary_computed用来保存Summary 
所以,我们就不需要: 
(1)手动初始化或从checkpoint中加载数据 
(2)不需要创建Saver,使用sv内部的就可以 
(3)不需要创建summary writer

2、tf.Variable与tf.get_variable()的区别:

使用tf.Variable时,如果检测到命名冲突,系统会自己处理。使用tf.get_variable()时,系统不会处理冲突,而会报错:

import tensorflow as tf
w_1 = tf.Variable(3,name="w_1")
w_2 = tf.Variable(1,name="w_1")
print w_1.name
print w_2.name
#输出
#w_1:0
#w_1_1:0


import tensorflow as tf


w_1 = tf.get_variable(name="w_1",initializer=1)
w_2 = tf.get_variable(name="w_1",initializer=2)
#错误信息
#ValueError: Variable w_1 already exists, disallowed. Did
#you mean to set reuse=True in VarScope?

基于这两个函数的特性,当我们需要共享变量的时候,需要使用tf.get_variable()。在其他情况下,这两个的用法是一样的

3、tf.ConfigProto一般用在创建session的时候。用来对session进行参数配置:

with tf.Session(config = tf.ConfigProto(...),...):

#tf.ConfigProto()的参数
log_device_placement=True : 是否打印设备分配日志
allow_soft_placement=True : 如果你指定的设备不存在,允许TF自动分配设备
tf.ConfigProto(log_device_placement=True,allow_soft_placement=True)

4、控制GPU资源使用率:

#allow growth
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.Session(config=config, ...)
# 使用allow_growth option,刚一开始分配少量的GPU容量,然后按需慢慢的增加,由于不会释放
#内存,所以会导致碎片

# per_process_gpu_memory_fraction
gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.7)
config=tf.ConfigProto(gpu_options=gpu_options)
session = tf.Session(config=config, ...)
#设置每个GPU应该拿出多少容量给进程使用,0.4代表 40%

5、控制使用哪块GPU:

~/ CUDA_VISIBLE_DEVICES=0  python your.py#使用GPU0
~/ CUDA_VISIBLE_DEVICES=0,1 python your.py#使用GPU0,1

6、tensorflow的collection提供一个全局的存储机制,不会受到变量名生存空间的影响。一处保存,到处可取:

#向collection中存数据
tf.Graph.add_to_collection(name, value)

#Stores value in the collection with the given name.
#Note that collections are not sets, so it is possible to add a value to a collection
#several times.
# 注意,一个‘name’下,可以存很多值; add_to_collection("haha", [a,b]),这种情况下
#tf.get_collection("haha")获得的是 [[a,b]], 并不是[a,b]
tf.add_to_collection(name, value)
#这个和上面函数功能上没有区别,区别是,这个函数是给默认图使用的

#从collection中获取数据
tf.Graph.get_collection(name, scope=None)
Returns a list of values in the collection with the given name.

7、merge_all引发的错误:

1)在训练深度神经网络的时候,我们经常会使用Dropout,然而在test的时候,需要把dropout撤掉.为了应对这种问题,我们通常要建立两个模型,让他们共享变量。详情.

2)为了使用Tensorboard来可视化我们的数据,我们会经常使用Summary,最终都会用一个简单的merge_all函数来管理我们的Summary。

错误示例:

当1)和2)这两种情况相遇时,bug就产生了,看代码:

import tensorflow as tf
import numpy as np
class Model(object):
    def __init__(self):
        self.graph()
        self.merged_summary = tf.summary.merge_all()# 引起血案的地方
    def graph(self):
        self.x = tf.placeholder(dtype=tf.float32,shape=[None,1])
        self.label = tf.placeholder(dtype=tf.float32, shape=[None,1])
        w = tf.get_variable("w",shape=[1,1])
        self.predict = tf.matmul(self.x,w)
        self.loss = tf.reduce_mean(tf.reduce_sum(tf.square(self.label-self.predict),axis=1))
        self.train_op = tf.train.GradientDescentOptimizer(0.01).minimize(self.loss)
        tf.summary.scalar("loss",self.loss)
def run_epoch(session, model):
    x = np.random.rand(1000).reshape(-1,1)
    label = x*3
    feed_dic = {model.x.name:x, model.label:label}
    su = session.run([model.merged_summary], feed_dic)
def main():
    with tf.Graph().as_default():
        with tf.name_scope("train"):
            with tf.variable_scope("var1",dtype=tf.float32):
                model1 = Model()
        with tf.name_scope("test"):
            with tf.variable_scope("var1",reuse=True,dtype=tf.float32):
                model2 = Model()
        with tf.Session() as sess:
            tf.global_variables_initializer().run()
            run_epoch(sess,model1)
            run_epoch(sess,model2)
if __name__ == "__main__":
    main()

运行情况是这样的: 执行run_epoch(sess,model1)时候,程序并不会报错,一旦执行到run_epoch(sess,model1),就会报错

报错内容:

error
tensorflow.Python.framework.errors_impl.InvalidArgumentError: You must feed a value for placeholder tensor ‘train/var1/Placeholder’ with dtype float 
[Node: train/var1/Placeholder = Placeholder[dtype=DT_FLOAT, shape=[], _device=”/job:localhost/replica:0/task:0/gpu:0”]]

错误原因:

class Model(object):
    def __init__(self):
        self.graph()
        self.merged_summary = tf.summary.merge_all()# 引起血案的地方
...
with tf.name_scope("train"):
    with tf.variable_scope("var1",dtype=tf.float32):
        model1 = Model() # 这里的merge_all只是管理了自己的summary
with tf.name_scope("test"):
    with tf.variable_scope("var1",reuse=True,dtype=tf.float32):
        model2 = Model()# 这里的merge_all管理了自己的summary和上边模型的Summary

由于Summary的计算是需要feed数据的,所以会报错。

解决方法:

我们只需要替换掉merge_all就可以解决这个问题。看代码:

class Model(object):
    def __init__(self,scope):
        self.graph()
        self.merged_summary = tf.summary.merge(
        tf.get_collection(tf.GraphKeys.SUMMARIES,scope)
        )
...
with tf.Graph().as_default():
    with tf.name_scope("train") as train_scope:
        with tf.variable_scope("var1",dtype=tf.float32):
            model1 = Model(train_scope)
    with tf.name_scope("test") as test_scope:
        with tf.variable_scope("var1",reuse=True,dtype=tf.float32):
            model2 = Model(test_scope)

总结:当有多个模型时,出现类似错误,应该考虑使用的方法是不是涉及到了其他的模型。

8、tensorflow中有一个计算梯度的函数tf.gradients(ys, xs),要注意的是,xs中的x必须要与ys相关,不相关的话,会报错:

代码中定义了两个变量w1, w2, 但res只与w1相关

#wrong
import tensorflow as tf
w1 = tf.Variable([[1,2]])
w2 = tf.Variable([[3,4]])
res = tf.matmul(w1, [[2],[1]])
grads = tf.gradients(res,[w1,w2])

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    re = sess.run(grads)
    print(re)

错误信息 
TypeError: Fetch argument None has invalid type

# right
import tensorflow as tf
w1 = tf.Variable([[1,2]])
w2 = tf.Variable([[3,4]])
res = tf.matmul(w1, [[2],[1]])
grads = tf.gradients(res,[w1])
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    re = sess.run(grads)
    print(re)
#  [array([[2, 1]], dtype=int32)]

9、tf.stop_gradient()阻挡节点BP的梯度:

import tensorflow as tf


w1 = tf.Variable(2.0)
w2 = tf.Variable(2.0)


a = tf.multiply(w1, 3.0)
a_stoped = tf.stop_gradient(a)


# b=w1*3.0*w2
b = tf.multiply(a_stoped, w2)
gradients = tf.gradients(b, xs=[w1, w2])
print(gradients)
#输出
#[None, <tf.Tensor 'gradients/Mul_1_grad/Reshape_1:0' shape=() dtype=float32>]

可见,一个节点被 stop之后,这个节点上的梯度,就无法再向前BP了。由于w1变量的梯度只能来自a节点,所以,计算梯度返回的是None。

a = tf.Variable(1.0)
b = tf.Variable(1.0)
c = tf.add(a, b)
c_stoped = tf.stop_gradient(c)
d = tf.add(a, b)
e = tf.add(c_stoped, d)
gradients = tf.gradients(e, xs=[a, b])
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(gradients))
#输出 [1.0, 1.0]

虽然 c节点被stop了,但是a,b还有从d传回的梯度,所以还是可以输出梯度值的。

import tensorflow as tf
w1 = tf.Variable(2.0)
w2 = tf.Variable(2.0)
a = tf.multiply(w1, 3.0)
a_stoped = tf.stop_gradient(a)
# b=w1*3.0*w2
b = tf.multiply(a_stoped, w2)
opt = tf.train.GradientDescentOptimizer(0.1)
gradients = tf.gradients(b, xs=tf.trainable_variables())
tf.summary.histogram(gradients[0].name, gradients[0])# 这里会报错,因为gradients[0]是None
#其它地方都会运行正常,无论是梯度的计算还是变量的更新。总觉着tensorflow这么设计有点不好,
#不如改成流过去的梯度为0
train_op = opt.apply_gradients(zip(gradients, tf.trainable_variables()))
print(gradients)
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(train_op))
    print(sess.run([w1, w2]))

10、构建多GPU代码:

如何实现multi_gpu_model函数:

def multi_gpu_model(num_gpus=1):
  grads = []
  for i in range(num_gpus):
    with tf.device("/gpu:%d"%i):
      with tf.name_scope("tower_%d"%i):
        model = Model(is_training, config, scope)
        # 放到collection中,方便feed的时候取
        tf.add_to_collection("train_model", model)
        grads.append(model.grad) #grad 是通过tf.gradients(loss, vars)求得
        #以下这些add_to_collection可以直接在模型内部完成。
        # 将loss放到 collection中, 方便以后操作
        tf.add_to_collection("loss",model.loss)
        #将predict放到collection中,方便操作
        tf.add_to_collection("predict", model.predict)
        #将 summary.merge op放到collection中,方便操作
        tf.add_to_collection("merge_summary", model.merge_summary)
        # ...
  with tf.device("cpu:0"):
    averaged_gradients = average_gradients(grads)# average_gradients后面说明
    opt = tf.train.GradientDescentOptimizer(learning_rate)
    train_op=opt.apply_gradients(zip(average_gradients,tf.trainable_variables()))
  return train_op

如何feed data:

def generate_feed_dic(model, feed_dict, batch_generator):
  x, y = batch_generator.next_batch()
  feed_dict[model.x] = x
  feed_dict[model.y] = y

如何实现run_epoch:

#这里的scope是用来区别 train 还是 test
def run_epoch(session, data_set, scope, train_op=None, is_training=True):
  batch_generator = BatchGenerator(data_set, batch_size)
  ...
  ...
  if is_training and train_op is not None:
    models = tf.get_collection("train_model")
    # 生成 feed_dict
    feed_dic = {}
    for model in models:
      generate_feed_dic(model, feed_dic, batch_generator)
    #生成fetch_dict
    losses = tf.get_collection("loss", scope)#保证了在 test的时候,不会fetch train的loss
    ...
    ...

main 函数干了以下几件事: 
1. 数据处理 
2. 建立多GPU训练模型 
3. 建立单/多GPU测试模型 
4. 创建Saver对象和FileWriter对象 
5. 创建session 
6. run_epoch:

data_process()
with tf.name_scope("train") as train_scope:
  train_op = multi_gpu_model(..)
with tf.name_scope("test") as test_scope:
  model = Model(...)
saver = tf.train.Saver()
# 建图完毕,开始执行运算
with tf.Session() as sess:
  writer = tf.summary.FileWriter(...)
  ...
  run_epoch(...,train_scope)
  run_epoch(...,test_scope)

如何编写average_gradients函数:

def average_gradients(grads):#grads:[[grad0, grad1,..], [grad0,grad1,..]..]
  averaged_grads = []
  for grads_per_var in zip(*grads):
    grads = []
    for grad in grads_per_var:
      expanded_grad = tf.expanded_dim(grad,0)
      grads.append(expanded_grad)
    grads = tf.concat_v2(grads, 0)
    grads = tf.reduce_mean(grads, 0)
    averaged_grads.append(grads)
  return averaged_grads


11、tensorflow 中的 Saver 对象是用于 参数保存和恢复的。如何使用:

v1 = tf.Variable(..., name='v1')
v2 = tf.Variable(..., name='v2')


# Pass the variables as a dict:
saver = tf.train.Saver({'v1': v1, 'v2': v2})


# Or pass them as a list.
saver = tf.train.Saver([v1, v2])
# Passing a list is equivalent to passing a dict with the variable op names
# as keys:
saver = tf.train.Saver({v.op.name: v for v in [v1, v2]})

这里使用了三种不同的方式来创建 saver 对象, 但是它们内部的原理是一样的。我们都知道,参数会保存到 checkpoint 文件中,通过键值对的形式在 checkpoint中存放着。如果 Saver 的构造函数中传的是 dict,那么在 save 的时候,checkpoint文件中存放的就是对应的 key-value。如下:

import tensorflow as tf
# Create some variables.
v1 = tf.Variable(1.0, name="v1")
v2 = tf.Variable(2.0, name="v2")

saver = tf.train.Saver({"variable_1":v1, "variable_2": v2})
# Use the saver object normally after that.
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    saver.save(sess, 'test-ckpt/model-2')

我们通过官方提供的工具来看一下 checkpoint 中保存了什么:

from tensorflow.python.tools.inspect_checkpoint import print_tensors_in_checkpoint_file


print_tensors_in_checkpoint_file("test-ckpt/model-2", None, True)
# 输出:
#tensor_name:  variable_1
#1.0
#tensor_name:  variable_2
#2.0

如果构建saver对象的时候,我们传入的是 list, 那么将会用对应 Variable 的 variable.op.name 作为 key:

import tensorflow as tf
# Create some variables.
v1 = tf.Variable(1.0, name="v1")
v2 = tf.Variable(2.0, name="v2")


saver = tf.train.Saver([v1, v2])
# Use the saver object normally after that.
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    saver.save(sess, 'test-ckpt/model-2')

我们再使用官方工具打印出 checkpoint 中的数据,得到:

tensor_name:  v1
1.0
tensor_name:  v2
2.0

如果我们现在想将 checkpoint 中v2的值restore到v1 中,v1的值restore到v2中,我们该怎么做? 
这时,我们只能采用基于 dict 的 saver:

import tensorflow as tf
# Create some variables.
v1 = tf.Variable(1.0, name="v1")
v2 = tf.Variable(2.0, name="v2")

saver = tf.train.Saver({"variable_1":v1, "variable_2": v2})
# Use the saver object normally after that.
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    saver.save(sess, 'test-ckpt/model-2')

save 部分的代码如上所示,下面写 restore 的代码,和save代码有点不同:

```python
import tensorflow as tf
# Create some variables.
v1 = tf.Variable(1.0, name="v1")
v2 = tf.Variable(2.0, name="v2")
#restore的时候,variable_1对应到v2,variable_2对应到v1,就可以实现目的了。
saver = tf.train.Saver({"variable_1":v2, "variable_2": v1})
# Use the saver object normally after that.
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    saver.restore(sess, 'test-ckpt/model-2')
    print(sess.run(v1), sess.run(v2))
# 输出的结果是 2.0 1.0,如我们所望

我们发现,其实 创建 saver对象时使用的键值对就是表达了一种对应关系: 
- save时, 表示:variable的值应该保存到 checkpoint文件中的哪个 key下 
- restore时,表示:checkpoint文件中key对应的值,应该restore到哪个variable

12、tf.cond(pred, fn1, fn2, name=None)

等价于:res = fn1() if pred else fn2()

注意:pred不能是 Python bool, pred是个标量Tensor 

官网例子:

z = tf.mul(a, b)
result = tf.cond(x < y, lambda: tf.add(x, z), lambda: tf.square(y))

13、tf.case(pred_fn_pairs, default, exclusive=False, name=’case’):

pred_fn_pairs:以下两种形式都是正确的 
1. [(pred_1, fn_1), (pred_2, fn_2)] 
2. {pred_1:fn_1, pred_2:fn_2}
tf.case()等价于:

if pred_1:
  return fn_1()
elif pred_2:
  return fn_2()
else:
  return default()

14、tf.group() 与 tf.tuple():

如果我们有很多 tensor 或 op想要一起run,这时这两个函数就是一个很好的帮手了。

w = tf.Variable(1)
mul = tf.multiply(w, 2)
add = tf.add(w, 2)
group = tf.group(mul, add)
tuple = tf.tuple([mul, add])
# sess.run(group)和sess.run(tuple)都会求Tensor(add)
#Tensor(mul)的值。区别是,tf.group()返回的是`op`
#tf.tuple()返回的是list of tensor。
#这样就会导致,sess.run(tuple)的时候,会返回 Tensor(mul),Tensor(add)的值.
#而 sess.run(group)不会

15、learning rate decay

在训练神经网络的时候,通常在训练刚开始的时候使用较大的learning rate, 随着训练的进行,我们会慢慢的减小learning rate。对于这种常用的训练策略,tensorflow 也提供了相应的API让我们可以更简单的将这个方法应用到我们训练网络的过程中。

接口 
tf.train.exponential_decay(learning_rate, global_step, decay_steps, decay_rate, staircase=False, name=None) 
参数: 
learning_rate : 初始的learning rate 
global_step : 全局的step,与 decay_step 和 decay_rate一起决定了 learning rate的变化。

更新公式:

decayed_learning_rate = learning_rate *
                        decay_rate ^ (global_step / decay_steps)

用法:

import tensorflow as tf
global_step = tf.Variable(0, trainable=False)
initial_learning_rate = 0.1 #初始学习率
learning_rate = tf.train.exponential_decay(initial_learning_rate,
                                           global_step=global_step,
                                           decay_steps=10,decay_rate=0.9)
opt = tf.train.GradientDescentOptimizer(learning_rate)

add_global = global_step.assign_add(1)
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(learning_rate))
    for i in range(10):
        _, rate = sess.run([add_global, learning_rate])
        print(rate)

16、tf.sparse_to_dense的用法(http://blog.csdn.net/mao_xiao_feng/article/details/53365889):

tf.sparse_to_dense(sparse_indices, output_shape, sparse_values, default_value, name=None)
除去name参数用以指定该操作的name,与方法有关的一共四个参数:
第一个参数sparse_indices:稀疏矩阵中那些个别元素对应的索引值。
     有三种情况:
     sparse_indices是个数,那么它只能指定一维矩阵的某一个元素
     sparse_indices是个向量,那么它可以指定一维矩阵的多个元素
     sparse_indices是个矩阵,那么它可以指定二维矩阵的多个元素
第二个参数output_shape:输出的稀疏矩阵的shape
第三个参数sparse_values:个别元素的值。
     分为两种情况:
     sparse_values是个数:所有索引指定的位置都用这个数
     sparse_values是个向量:输出矩阵的某一行向量里某一行对应的数(所以这里向量的长度应该和输出矩阵的行数对应,不然报错)
第四个参数default_value:未指定元素的默认值,一般如果是稀疏矩阵的话就是0了

例子:


假设一个batch有6个样本,每个样本的label是0,2,3,6,7,9:

BATCHSIZE=6  

label=tf.expand_dims(tf.constant([0,2,3,6,7,9]),1)  

生成一个index表明一个batch里面每个样本对应的序号:

index=tf.expand_dims(tf.range(0, BATCHSIZE),1) 

最后把他们两个矩阵进行连接,连接以后的矩阵是这样的:

concated = tf.concat(1, [index, label])  :

[[0 0]  
 [1 2]  
 [2 3]  
 [3 6]  
 [4 7]  
 [5 9]]

最后一步,调用tf.sparse_to_dense输出一个onehot标签的矩阵,输出的shape就是行数为BATCHSIZE,列数为10的矩阵,指定元素值为1.0,其余元素值为0.0:

onehot_labels = tf.sparse_to_dense(concated, tf.pack([BATCHSIZE,10]), 1.0, 0.0)  

0 0
原创粉丝点击