Lecture note 2: TensorFlow Ops

Lecture note 2: TensorFlow Ops

1. Fun with TensorBoard

       在TensorFlow中,我们将常量、变量、操作符统称为操作符(Ops)。TensorFlow不仅是一个软件库,还包括一套软件,包括TensorFlow、TensorBoard和TensorServing。为了最大限度地利用TensorFlow,我们应该知道如何将以上所有的应用结合在一起。在这节课中,我们将首先介绍TensorBoard。
       TensorBoard是图形可视化软件,包括任何标准的TensorFlow安装。用谷歌自己的话来说:“你将使用TensorFlow的计算——就像训练一个巨大的深度神经网络——可能是复杂而令人困惑的。为了更容易地理解、调试和优化TensorFlow程序,我们已经包含了一套叫做TensorBoard的可视化工具。
       当完全配置时,TensorBoard将会是这样的。图像从TensorBoard的网站截取。

这里写图片描述
       当用户在tensorboard被激活的TensorFlow程序中执行某些操作时,这些操作将被导出到一个事件文件中。TensorBoard能够将这些事件文件转换为图形,从而能够洞察模型的行为。尽早学会使用TensorBoard,经常会让TensorFlow的工作变得更加有趣和高效。
       让我们写下你的第一个TensorFlow项目用TensorBoard来可视化 它。

a ​=​ tf​.​constant​(​2)      
b ​=​ tf​.​constant​(​3)  
x ​=​ tf​.​add​(​a​,​ b)           
with​ tf​.​Session​()​ ​as​ sess:     
    print​ sess​.​run​(​x)

       为了在这个程序中激活Tensorboard,在建立graph后加入一行,正好就在进行训练之前
writer ​=​ tf​.​summary​.​FileWriter​(​logs_dir​,​ sess​.​graph)
       上面的一行代码是创建一个writer对象来将操作写到事件文件中,存储在文件夹logsdir中。您可以选择logsdir为“./graphs”之类的东西。

import​ tensorflow ​as​ tf 
a ​=​ tf​.​constant​(​2) 
b ​=​ tf​.​constant​(​3) x ​=​ tf​.​add​(​a​,​ b) 
with​ tf​.​Session​()​ ​as​ sess:        
    writer ​=​ tf​.​summary​.​FileWriter​(​'./graphs'​,​ sess​.​graph) 
    print​ sess​.​run​(​x) 
# close the writer when you’re done using it 
writer.close()  

       接下来,到终端,运行程序。确保您当前的工作目录与运行Python代码的位置相同。

$ python ​[​yourprogram​.​py​]  
$ tensorboard ​--​logdir​=​"./graphs"  

       打开浏览器,转到http://localhost:6006/(或运行tensorboard命令后返回的链接)。
       转到网页,你会看到这样的东西:
这里写图片描述

点击Graph,可以看到3个节点的图

a ​=​ tf​.​constant​(​2)   
b ​=​ tf​.​constant​(​3)   
x ​=​ tf​.​add​(​a​,​ b)   

       “Const”和“Const_1”对应于a和b,而节点“Add”对应于x。我们给它们的名称(a、b和x)是我们在需要时访问它们的。它们对内部TensorFlow来说毫无意义。要让TensorBoard显示你的操作的名称,你必须显式地命名它们。

a ​=​ tf​.​constant​([​2​,​ ​2​],​ name​=​"a") 
b ​=​ tf​.​constant​([​3​,​ ​6​],​ name​=​"b") 
x ​=​ tf​.​add​(​a​,​ b​,​ name​=​"add")

       现在,如果你再次运行TensorBoard,你会看到这张图:
这里写图片描述

图本身定义了操作和依赖关系,但不显示值。它只关心在运行会话session时获取值。快速提醒一下,如果你忘了:

tf​.​Session​.​run​(​fetches​,​ feed_dict​=​None​,​ options​=​None​,​ run_metadata​=​None)  

注意:如果您已经多次运行您的代码,将会有多个事件文件’~/dev/cs20si/graphs/lecture01’,TF只显示最新的图并显示多个事件文件的警告。要删除警告,删除您不再需要的所有事件文件。

2. Constant types

Link to documentation: https://www.tensorflow.org/api_docs/python/constant_op/“>​https://www.tensorflow.org/api_docs/python/constant_op/
你可以用constants创建标量或张量的值。

tf​.​constant​(​value​,​ dtype​=​None​,​ shape​=​None​,​ name​=​'Const'​,​ verify_shape​=​False) 
# constant of 1d tensor (vector) 
a ​=​ tf​.​constant​([​2​,​ ​2​],​ name​=​"vector") 
# constant of 2x2 tensor (matrix) 
b ​=​ tf​.​constant​([[​0​,​ ​1​],​ ​[​2​,​ ​3​]],​ name​=​"b") 

你可以创建一个张量,它的元素是一个特定的值,它与numpy相似。

numpy.zeros, numpy.zeros_like, numpy.ones, numpy.ones_lik的相同点:

tf​.​zeros​(​shape​,​ dtype​=​tf​.​float32​,​ name​=​None) 
# create a tensor of shape and all elements are zeros 
tf​.​zeros​([​2​,​ ​3​],​ tf​.​int32​)​ ​==>​ ​[[​0​,​ ​0​,​ ​0​],​ ​[​0​,​ ​0​,​ ​0​]]     

tf​.​zeros_like​(​input_tensor​,​ dtype​=​None​,​ name​=​None​,​ optimize​=​True) 
# create a tensor of shape and type (unless type is specified) as the input_tensor but all elements are zeros. 
# input_tensor is [0, 1], [2, 3], [4, 5]] 
tf​.​zeros_like​(​input_tensor​)​ ​==>​ ​[[​0​,​ ​0​],​ ​[​0​,​ ​0​],​ ​[​0​,​ ​0​]] 

tf​.​ones​(​shape​,​ dtype​=​tf​.​float32​,​ name​=​None) 
# create a tensor of shape and all elements are ones 
tf​.​ones​([​2​,​ ​3​],​ tf​.​int32​)​ ​==>​ ​[[​1​,​ ​1​,​ ​1​],​ ​[​1​,​ ​1​,​ ​1​]]  

tf​.​ones_like​(​input_tensor​,​ dtype​=​None​,​ name​=​None​,​ optimize​=​True) 
# create a tensor of shape and type (unless type is specified) as the input_tensor but all elements are ones. 
# input_tensor is [0, 1], [2, 3], [4, 5]] 
tf​.​ones_like​(​input_tensor​)​ ​==>​ ​[[​1​,​ ​1​],​ ​[​1​,​ ​1​],​ ​[​1​,​ ​1​]]   

tf​.​fill​(​dims​,​ value​,​ name​=​None​)  
# create a tensor filled with a scalar value. 
tf​.​fill([​2​,​ ​3​],​ ​8​)​ ​==>​ ​[[​8​,​ ​8​,​ ​8​],​ ​[​8​,​ ​8​,​ ​8​]]

你可以创建一个常数序列
tf​.​linspace()

tf​.​linspace​(​start​,​ stop​,​ num​,​ name​=​None) 
# create a sequence of ​num evenly-spaced values are generated beginning at ​start. If num > 1, the values in the sequence increase by stop - start / num - 1, so that the last one is exactly ​stop
# start, stop, num must be scalars
# comparable to but slightly different from numpy.linspace 
# numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None) 
tf​.​linspace​(​10.0​,​ ​13.0​,​ ​4​,​ name​=​"linspace"​)​ ​==>​ ​[​10.0​ ​11.0​ ​12.0​ ​13.0]  

tf.range()

tf​.​range​(​start​,​ limit​=​None​,​ delta​=​1​,​ dtype​=​None​,​ name​=​'range') 
# create a sequence of numbers that begins at start and extends by increments of delta up to but not including limit 
# slight different from range in Python 
# 'start' is 3, 'limit' is 18, 'delta' is 3 
tf​.​range​(​start​,​ limit​,​ delta​)​ ​==>​ ​[​3​,​ ​6​,​ ​9​,​ ​12​,​ ​15] 
# 'start' is 3, 'limit' is 1,  'delta' is -0.5 
tf​.​range​(​start​,​ limit​,​ delta​)​ ​==>​ ​[​3​,​ ​2.5​,​ ​2​,​ ​1.5] 
# 'limit' is 5 
tf​.​range​(​limit​)​ ​==>​ ​[​0​,​ ​1​,​ ​2​,​ ​3​,​ ​4]

注意,与NumPy或Python序列不同,TensorFlow序列是不可迭代的。

for​ _ ​in​ np​.​linspace​(​0​,​ ​10​,​ ​4​):​ ​# OK 
for​ _ ​in​ tf​.​linspace​(​0​,​ ​10​,​ ​4​):​ ​# TypeError("'Tensor' object is not iterable.") 

for​ _ ​in​ range​(​4​):​ ​# OK 
for​ _ ​in​ tf​.​range​(​4​):​ ​# TypeError("'Tensor' object is not iterable.") 

你也可以从某些分布中产生随机常数

tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) 
tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None) 
tf.random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None, name=None) 
tf.random_shuffle(value, seed=None, name=None) 
tf.random_crop(value, size, seed=None, name=None) 
tf.multinomial(logits, num_samples, seed=None, name=None) 
tf.random_gamma(shape, alpha, beta=None, dtype=tf.float32, seed=None, name=None) 

3. Math Operations

TensorFlow的数学运算是相当标准的,非常类似于NumPy。
详细请见https://www.tensorflow.org/api_docs/python/math_ops/arithmetic_operators​

a = tf.constant([3, 6])
b = tf.constant([2, 2])
tf.add(a, b) # >[5 8]
tf.add_n([a, b, b]) # >[7 10]. Equivalent to a + b + b
tf.mul(a, b) # >[6 12] because mul is element wise
tf.matmul(a, b) # >ValueError
tf.matmul(tf.reshape(a, shape=[1, 2]),
tf.reshape(b, shape=[2, 1])) # >[[18]]
tf.div(a, b) # >[1 3]
tf.mod(a, b) # >[1 0]

下面是Python中的操作表,由““Fundamentals of Deep Learning”的作者提供。
这里写图片描述

4. Data Types

Python Native Types
TensorFlow采用Python的原有类型,例如Python布尔值、数值(整数、浮点数)和字符串。单个值将被转换为0-d张量(或标量),lists将被转换为1-d张量(向量),lists的list将被转换为2-d张量(矩阵),以此类推。下面的例子来自“TensorFlow for Machine Intelligence”。

t_0 ​=​ ​19​ ​# Treated as a 0-d tensor, or "scalar"  
tf​.​zeros_like​(​t_0​)​ ​# ==> 0 tf​.​ones_like​(​t_0​)​ ​# ==> 1 
t_1 ​=​ ​[​b​"apple"​,​ b​"peach"​,​ b​"grape"​]​ ​# treated as a 1-d tensor, or "vector" 
tf​.​zeros_like​(​t_1​)​ ​# ==> ['' '' ''] 
tf​.​ones_like​(​t_1​)​ ​# ==> TypeError: Expected string, got 1 of type 'int' instead. 
t_2 ​=​ ​[[​True​,​ ​False​,​ ​False​],        ​
       [​False​,​ ​False​,​ ​True​],
       ​[​False​,​ ​True​,​ ​False​]]​ ​# treated as a 2-d tensor, or "matrix" 
tf​.​zeros_like​(​t_2​)​ ​# ==> 2x2 tensor, all elements are False 
tf​.​ones_like​(​t_2​)​ ​# ==> 2x2 tensor, all elements are True  

TensorFlow Native Types
和NumPy一样,TensorFlow也有自己的数据类型,就像你看到的tf.int32,tf。float32。
下面是来自TensorFlow官方文档的当前TensorFlow数据类型的列表。
这里写图片描述
NumPy Data Types
现在,您可能已经注意到了NumPy和TensorFlow之间的相似之处。TensorFlow被设计为与Numpy无缝集成,这一方案已经成为数据科学的通用语言。

TensorFlow的数据类型是基于NumPy的;实际上,np.int32==tf.int32返回True。您可以将NumPy类型传递给TensorFlow操作。
例子:

tf​.​ones​([​2​,​ ​2​],​ np​.​float32​)​ ​==>​ ​[[​1.0​ ​1.0​],​ ​[​1.0​ ​1.0​]]

还记得tf.Session.run(fetches)​吗?如果它需要一个tensor,那么输出就是一个NumPy数组
大多数情况下,你可以交替使用TensorFlow类型和NumPy类型。

Note 1​: 这里有一个字符串数据类型的捕获。对于数值型和布尔型类型,TensorFlow和NumPy dtypes都是匹配的。然而,tf.string,由于NumPy处理字符串的方式,字符串在NumPy中没有确切的匹配。TensorFlow仍然可以从NumPy中导入字符串数组,这完全可以——只是不要在NumPy中指定dtype!
Note 2​: TensorFlow和NumPy都是n-d数组库。NumPy支持ndarray,但不提供创建张量函数和自动计算导数的方法,也不提供GPU支持。所以TensorFlow还是赢了!
Note 3:使用Python类型来指定TensorFlow对象是快速而简单的,并且它对于原型设计非常有用。然而,这样做有一个很重要的陷阱。Python类型缺乏显式地声明数据类型的能力,但是TensorFlow的数据类型更具体。例如,所有的整数都是相同的类型,但是TensorFlow有8位、16位、32位和64位的整数。因此,如果使用Python类型,TensorFlow必须推断出您的数据类型。

当您将数据转换为TensorFlow时,可以将数据转换为适当的类型,但是某些数据类型仍然难以正确地声明,例如复数。因此,将手动定义的张量对象创建为NumPy数组是很常见的。但是,在可能的情况下,总是使用TensorFlow类型,因为TensorFlow和NumPy都可以发展到这样一个点,这样的兼容性就不再存在了。

5. Variables 变量

常数一直都很有趣,但我认为你们现在已经足够了解变量了。常数和变量的不同点在于:
1.常数是恒定的,而变量可以被赋值,它的值可以被改变。
2.一个常量的值存储在图中,它的值在加载图的任何地方都被复制。变量是单独存储的,并且可以在一个参数服务器上生存。

第2点基本上意味着常量存储在图定义中。当常量在内存中,每次加载图形时都会很慢。要查看图的定义和存储在图的定义中的内容,只需打印出图的原始buf。原生buf表示协议缓冲区,“Google的语言中立、平台无关、可扩展的用于序列化结构化数据的机制——考虑XML,但更小、更快、更简单。”

import tensorflow as tf 

my_const = tf.constant([1.0, 2.0], name="my_const") print tf.get_default_graph().as_graph_def() 

Output: 

node {     
    name: "my_const"     
    op: "Const"     
    attr {       
        key: "dtype"       
        value {  
            type: DT_FLOAT  
        }  
    }   
    attr {     
        key: "value"     
        value {       
            tensor {         
                dtype: DT_FLOAT         
                tensor_shape {           
                    dim {             
                        size: 2           
                    }         
                }         
                tensor_content: "\000\000\200?\000\000\000@"       
            }    
        }   
    } 
} 
versions {   
    producer: 17 
}  

Declare variables

要声明一个变量,你需要创建一个类 tf.Variable的实例。请注意,tf.constant,tf.Variable 不是tf.variable 因为tf.constant 是一个 op,而 tf.Variable 是一个类。

#create variable a with scalar value 
a ​=​ tf​.​Variable​(​2​,​ name​=​"scalar"​)  

#create variable b as a vector 
b ​=​ tf​.​Variable​([​2​,​ ​3​],​ name​=​"vector"​)  

 #create variable c as a 2x2 matrix 
c ​=​ tf​.​Variable​([[​0​,​ ​1​],​ ​[​2​,​ ​3​]],​ name​=​"matrix"​)  

# create variable W as 784 x 10 tensor, filled with zeros 
W ​=​ tf​.​Variable​(​tf​.​zeros​([​784​,​10​])) 


tf.Variable holds several ops:  

x = tf.Variable(...)  

x.initializer # init  x.value() # read op  x.assign(...) # write op  x.assign_add(...)  # and more 

你要在使用变量前先初始化他. 如果在初始化这些变量之前尝试使用这些变量,那么就会报错:尝试使用未初始化的张量。Attempting to use uninitialized value tensor.
最简单的方法就是一次性初始化所有的变量,用tf.global_variables_initializer()

init ​=​ tf​.​global_variables_initializer​() 
 with​ tf​.​Session​()​ ​as​ sess: tf​.​run​(​init)

提示:当你使用tf.run()来初始化的时候,并没有给他们任何值。
要初始化一个变量的子集,您需要使用tf.variables_initializer(),加上你想要初始化的变量列表

init_ab ​=​ tf​.​variables_initializer​([​a​,​ b​],​ name​=​"init_ab") 
with​ tf​.​Session​()​ ​as​ sess: tf​.​run​(​init_ab)  

还可以分别初始化每个变量,用 tf.Variable.initializer

# create variable W as 784 x 10 tensor, filled with zeros 
W ​=​ tf​.​Variable​(​tf​.​zeros​([​784​,​10​])) 
with​ tf​.​Session​()​ ​as​ sess: 
    tf​.​run​(​W​.​initializer)  

另一个初始化的方法是从保存文件里重新加载。接下来会讲。
Evaluate values of variables
如果输出初始化后的变量,我们只能看到tensor

# W is a random 700 x 100 variable object 
W ​=​ tf​.​Variable​(​tf​.​truncated_normal​([​700​,​ ​10​])) 
with​ tf​.​Session​()​ ​as​ sess: 
    sess​.​run​(​W​.​initializer) print​ W 

>>​ ​Tensor​(​"Variable/read:0"​,​ shape​=(​700​,​ ​10​),​ dtype​=​float32)  

如果想要获取变量的值,需要使用eval()

# W is a random 700 x 100 variable object 
W ​=​ tf​.​Variable​(​tf​.​truncated_normal​([​700​,​ ​10​])) 
with​ tf​.​Session​()​ ​as​ sess:
 sess​.​run​(​W​.​initializer)
 print​ W​.​eval​() 

>>​ ​[[-​0.76781619​ ​-​0.67020458​  ​1.15333688​ ​...,​ ​-​0.98434633​ ​-​1.25692499
   ​-​0.90904623]  
    ​[-​0.36763489​ ​-​0.65037876​ ​-​1.52936983​ ​...,​  ​0.19320194​
   ​-​0.38379928    ​0.44387451]  
    ​[​ ​0.12510735​ ​-​0.82649058​  ​0.4321366​  ​...,​ ​-​0.3816964​   ​0.70466036    ​1.33211911]
      ​...,   ​
    [​ ​0.9203397​  ​-​0.99590844​  ​0.76853162​ ​...,​ ​-​0.74290705​  ​0.37568584    ​0.64072722]  ​
    [-​0.12753558​  ​0.52571583​  ​1.03265858​ ​...,​  ​0.59978199​ ​-​0.91293705   ​-​0.02646019]  ​
    [​ ​0.19076447​ ​-​0.62968266​ ​-​1.97970271​ ​...,​ ​-​1.48389161​  ​0.68170643    ​1.46369624​]]   

Assign values to variables
可以通过 tf.Variable.assign() 给变量赋值

W ​=​ tf​.​Variable​(​10​) W​.​assign​(​100​)
with​ tf​.​Session​()​ ​as​ sess:
 sess​.​run​(​W​.​initializer)
 print​ W​.​eval​() # >> 10 

为什么是10而不是100呢?W.assign(100)没有将W赋值为100,而是创建了一个赋值操作。要让这个操作起作用,需要在session中运行它。

W ​=​ tf​.​Variable​(​10​) 
assign_op = W​.​assign​(​100​) 
with​ tf​.​Session​()​ ​as​ sess:
 sess​.​run​(​assign_op)
 print​ W​.​eval​() # >> 100   


注意:我们并没有初始化W,因为​assign​(​)已经为我们初始化了。事实上,初始化操作是分配给变量本身初始值的赋值操作。

# in the ​source code 
self._initializer_op = state_ops.assi(self._variable,self._initial_value,  
                                      validate_shape=validate_shape).op   

有趣的例子:

# create a variable whose original value is 2 
a ​=​ tf​.​Variable​(​2​,​ name​=​"scalar"​)  

# assign a * 2 to a and call that op a_times_two 
a_times_two ​=​ a​.​assign​(​a ​*​ ​2) 
init ​=​ tf​.​global_variables_initializer​() 
with​ tf​.​Session​()​ ​as​ sess:
 sess​.​run​(​init)
 # have to initialize a, because a_times_two op depends on the value of a sess​.​run​(​a_times_two​)​ ​# >> 4   
 sess​.​run​(​a_times_two​)​ ​# >> 8
 sess​.​run​(​a_times_two​)​ ​# >> 16 

为了实现简单的变量加减, TensorFlow 有tf.Variable.assign_add() 和 tf.Variable.assign_sub() 方法, 与tf.Variable.assign(),不同,tf.Variable.assign_add() 和 tf.Variable.assign_sub() 不能初始化变量, 因为这些操作依赖变量的初始值。

W = tf.Variable(10) 

with tf.Session() as sess:
 sess.run(W.initializer)
 print sess.run(W.assign_add(10)) # >> 20
 print sess.run(W.assign_sub(2)) # >> 18 

因为TensorFlow会话单独维护值,每个会话都可以为在图中定义的变量拥有自己的当前值。

W = tf.Variable(10) 

sess1 = tf.Session() sess2 = tf.Session() 

sess1.run(W.initializer) sess2.run(W.initializer) 

print sess1.run(W.assign_add(10)) # >> 20 print sess2.run(W.assign_sub(2)) # >> 8 

print sess1.run(W.assign_add(100)) # >> 120 print sess2.run(W.assign_sub(50)) # >> -42 

sess1.close()
sess2.close()  

当然,您可以声明一个依赖于其他变量的变量。
假设,你想声明一个U = W * 2

# W is a random 700 x 100 tensor 
W ​=​ tf​.​Variable​(​tf​.​truncated_normal​([​700​,​ ​10​])) 
U ​=​ tf​.​Variable​(​W ​*​ ​2)

这样的话,就要保证在初始化U之前用 initialized_value() 先初始化W。

U ​=​ tf​.​Variable​(​W​.​intialized_value​()​ ​*​ ​2)  

6. InteractiveSession

你有时候会看到InteractiveSession 而不是 Session,他们两唯一的区别是唯一的区别是,InteractiveSession使自己成为默认的会话,这样您就可以调用run()或eval(),而不需要显式地调用会话。
这在交互式shell和IPython笔记本中是很方便的,因为它可以避免通过显式的会话对象来运行操作。
但是,当您有多个会话要运行时,这是很复杂的。

sess ​=​ tf​.​InteractiveSession​() 
a ​=​ tf​.​constant​(​5.0) 
b ​=​ tf​.​constant​(​6.0) 
c ​=​ a ​*​ b 
# We can just use 'c.eval()' without passing 'sess' 
print​(​c​.​eval​()) 
sess​.​close​()   

用tf.InteractiveSession.close()来关闭一个 InteractiveSession。
tf.get defaultsession()返回当前线程的默认会话。
返回的会话将是Session或Session.as_default()。

7. Control Dependencies ##

有时,我们会有两个独立的操作但是你想要指定应该先运行哪个op,那就要使用 tf.Graph.control_dependencies(control_inputs)
例子:

# your graph g have 5 ops: a, b, c, d, e with​ 
g​.​control_dependencies​([​a​,​ b​,​ c​]):
   ​# `d` and `e` will only run after `a`, `b`, and `c` have executed.
   d ​=​ ​...
   e ​=​ …   

8. Placeholders and feed_dict ##

记得第一节课说TensorFlow程序包括两个阶段,

阶段1:建立一张图
阶段2:使用一个会话来执行图中的操作。

因此,我们可以先建立图,而不需要知道计算所需的值。
这等价于定义x,y的函数,不知道x,y的值。
例如:f(x, y) = x*2 + y
x,y是实际值的占位符。
在图建立完成后,我们或客户端可以在需要执行计算时提供自己的数据。
定义一个占位符,我们使用:

tf​.​placeholder​(​dtype​,​ shape​=​None​,​ name​=​None)   

Dtype是指定占位符值数据类型的必需参数。

shape指定了被接受为占位符的实际值的张量的形状。
shape=None意味着任何形状的张量都将被接受。
使用shape=None很容易构造图,但调试的时候却是噩梦。
你应该尽可能详细地定义你的占位符的shape。

你也可以给你的占位符起一个名字,就像你在TensorFlow中的任何其他操作一样。

更多关于官方文档中占位符的信息。

# create a placeholder of type float 32-bit, shape is a vector of 3 elements 
a ​=​ tf​.​placeholder​(​tf​.​float32​,​ shape​=[​3​]) 

# create a constant of type float 32-bit, shape is a vector of 3 elements 
b ​=​ tf​.​constant​([​5​,​ ​5​,​ ​5​],​ tf​.​float32) 

# use the placeholder as you would a constant or a variable 
c ​=​ a ​+​ b  ​# Short for tf.add(a, b) 
If​ we ​try​ to fetch c​,​ we will run ​into​ error. 
with​ tf​.​Session​()​ ​as​ sess: print​(​sess​.​run​(​c​)) 

>>​ ​NameError 

在运行的时候会报错,是因为计算C,需要a的值,但是a只是一个占位符,没有实际的值。我们需要先给a喂进一个值

with​ tf​.​Session​()​ ​as​ sess:
 # feed [1, 2, 3] to placeholder a via the dict {a: [1, 2, 3]}
 # fetch value of c print​(​sess​.​run​(​c​,​ ​{​a​:​ ​[​1​,​ ​2​,​ ​3​]})) 

 >>​ ​[​6.​ ​7.​ ​8.]   

让我们看看在 TensorBoard上是什么样的。加上

writer ​=​ tf​.​summary​.​FileWriter​(​'./my_graph'​,​ sess​.​graph)  

并且在终端上输入

$ tensorboard ​--​logdir​=​'my_graph'   

这里写图片描述

正如你所看到的,占位符和其他操作一样被处理,3是占位符的shape。

在前面的例子中,我们为占位符提供一个单一的值。如果我们想要将多个数据点提供给占位符,该怎么样呢?
这是一个合理的假设,因为我们经常需要在训练或测试集中通过多个数据点进行计算。

我们可以通过遍历数据集并以一个值一次的feed来输入占位符,以满足占位符的任何数据点。

with​ tf​.​Session​()​ ​as​ sess:
 for​ a_value ​in​ list_of_a_values:
 print​(​sess​.​run​(​c​,​ ​{​a​:​ a_value​}))   

你也可以把值喂给一个tensor,任何一个可以被喂值的tensor都可以。判断一个tensor是否可以被喂值,用

tf.Graph.is_feedable(tensor)

# create Operations, Tensors, etc (using the default graph) 
a = tf.add(2, 5) b = tf.mul(a, 3) 

# start up a `Session` using the default graph 
sess = tf.Session() 

# define a dictionary that says to replace the value of `a` with 15
replace_dict = {a: 15} 

# Run the session, passing in `replace_dict` as the value to`feed_dict`  
sess.run(b, feed_dict=replace_dict) # returns 45 

feed_dict对您的模型进行测试是非常有用的。当你有一个大的图并且想要测试某些部分时,你可以提供虚拟的值。因此,TensorFlow不会浪费时间去做不必要的计算

9. The trap of lazy loading* ##

我看到的最常见的TensorFlow的非bug的 bug(我曾经提到过)是我的朋友Danijar和我所称的“lazy loading”。
lazy loading是指当您延迟声明/初始化一个对象直到它被加载时,它指的是一个编程模式。
在TensorFlow中
,意思是你推迟创建一个op,直到你需要计算它。
例如,这是正常的加载:你在建立图时创建了op z。

x = tf.Variable(10, name='x')
y = tf.Variable(20, name='y') 
z = tf.add(x, y) 

with tf.Session() as sess:
     sess.run(tf.global_variables_initializer())
     for _ in range(10):
        sess.run(z)
     writer.close()   

这是当某人决定聪明,并使用延迟加载来保存一行代码时所发生的事情:

 x = tf.Variable(10, name='x')
 y = tf.Variable(20, name='y') 

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for _ in range(10):
        sess.run(tf.add(x, y))
        # create the op add only when you need to compute it
    writer.close() 

让我们在TensorBoard上看看这个图。
正常的加载图看起来和我们期望的一样。
这里写图片描述
这里写图片描述

好吧,节点“Add”缺失了,这是可以理解的,因为我们在给FileWriter写了图之后添加了“Add”。
这使得阅读图表变得更加困难,但它并不是一个错误。那么,有什么大不了的呢?
让我们看一下图像的定义。
记住,要打印出图形的定义,我们使用:

print tf.get_default_graph().as_graph_def()   

在正常加载图的protobuf中,图的原型只有1个节点“添加”:

node {
   name: "Add"
   op: "Add"
   input: "x/read"
   input: "y/read"
   attr {
     key: "T"
     value {
       type: DT_INT32
     }
   }
 }   

而在延迟加载的图上的protobuf中有10个“Add”节点。
每次你想要计算z时,它都会添加一个新的节点“添加”。

node {
   name: "Add"
   op: "Add"
   ...
 }
 node { 
  name: "Add_9"
   op: "Add"
   ...
 }   

你可能会想:“这太蠢了。为什么我要多次计算相同的值呢?并且认为这是一个没有人会提交的错误。
它发生的频率比你想象的要高。例如,你可能想要计算相同的损失函数,或者在一定数量的培训样本之后进行一些预测。
在发现解它之前,你已经计算了数千次,并在图中添加了数千个不必要的节点。
你的图定义变得冗余,加载速度慢,并且代价昂贵。

有两种方法可以避免这种bug。
首先,在可以的时候,总是将操作的定义和它们的执行分开。
但是如果不可能,因为你想将相关的操作组分组到类中,您可以使用Python属性来确保你的函数在第一次调用时只加载一次。
这不是一门Python课程,所以我不会深入探讨如何去做。
但是,如果你想知道,看看Danijar Hafner的这篇精彩的博客文章。​

猜你喜欢

转载自blog.csdn.net/qq_32799915/article/details/77074119