【CS 20SI】TensorFlow for Deep Learning Research Lecture note 2: TensorFlow Ops

英文原文地址:http://web.stanford.edu/class/cs20si/lectures/notes_02.pdf
代码运行环境:python3.5,tensorflow 1.1.0,win10

1、有趣的TensorBoard

在TensorFlow中,您可以将常量,变量,运算符一起称为操作(Ops)。TensorFlow不仅仅是一个软件库,而是一套软件,包括TensorFlow,TensorBoard和TensorServing。为了充分使用TensorFlow,我们应该知道如何将这些结合在一起使用。在本课程中,我们将首先介绍TensorBoard。

TensorBoard是标准TensorFlow安装的图形可视化软件。Google说过:“使用TensorFlow进行类似训练的大量深层神经网络的计算可能是很复杂、很混乱的。为了更容易理解、调试和优化TensorFlow程序,我们提供了一套名为TensorBoard的可视化工具。”

完成配置后,TensorBoard将看起来像这样。 下面图片来自TensorBoard的网站。

当用户在启动了TensorBoard的TensorFlow程序中执行某些操作时,这些操作将导出到事件文件。TensorBoard能够将这些事件文件转换为能够深入了解模型行为的图形。越早经常学习使用TensorBoard将会使得TensorFlow更加愉快和高效。

我们先写一个TensorFlow程序,并用TensorBoard进行可视化。

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

为了在这个程序使用TensorBoard,需要在构图之后,训练前加上这行代码

writer = tf.summary.FileWriter(logs_dir,sess.graph)

上面的行是创建一个写入对象来写入事件文件的操作,存储在logs_dir文件夹中。 您可以选择logs_dir为“./graphs”。

import tensorflow as tf
a = tf.constant(2)
b = tf.constant(3)
x = tf.add(a,b)
with tf.Session() as sess:
    #增加这行来使用TensorBoard
    writer = tf.ummary.FileWriter('./graphs',sess.graph)
    print(sess.run(x))
#结束时关闭
writer.close()

接下来,在cmd中运行程序。确保目前的工作目录与运行Python代码的位置相同。

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

打开浏览器,访问http://localhost:6006/(或者点击你从cmd中获得的地址),你可以看到如下图所示

转到GRAPHS选项,可以看到一个有三个节点的计算图。

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,name="a")
b = tf.constant(3,name="b")
x = tf.add(a,b,name="add")

现在你可以重新运行TensorBoard,如下图

图形本身定义了操作和依赖关系,但不显示值。当运行会话时,只关心值,并获取一些值。

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

注意:当多次运行程序时,会在’./graphs’或者log_dir下有多个事件文件,TF只会运行和展示最后一次的图,并警告有多个事件文件。如果要不警告,需要删除这些你不需要的事件文件。

2、常量类型

你可以创建一个常数或者一个常量tensor值。

tf.constant(value, dtype=None, shape=None,name='Const', verify_shape=False)
# 1d tensor常量 (向量)
a = tf.constant([2,2],name="vector")
# 2x2 tensor常量 (矩阵)
b = tf.constant([[0,1],[2,3]],name="b")

可以创建元素具有特定值的tensor

tf.zeros(shape,dtype=tf.float32,name=None)
# 创建一个全0的tensor
tf.zeros([2,3],tf.int32) ==> [[0,0,0],[0,0,0]]

tf.zeros_like(input_tensor,dtype=None,name=None,optimize=True)
# 创建一个跟input_tensor形状一样,值全为0的tensor
# input_tensor : [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)
# 创建一个全1的tensor
tf.ones([2,3],tf.int32) ==> [[1,1,1],[1,1,1]]

tf.ones_like(input_tensor,dtype=None,name=None,optimize=True)
# 创建一个跟input_tensor形状一样,值全为1的tensor
# input_tensor : [0, 1], [2, 3], [4, 5]]
tf.ones_like(input_tensor) ==> [[1,1],[1,1],[1,1]]

tf.fill(dims,value,name=None)
# 创建一个形状为dims,值全为value的tensor
tf.ones([2,3],8) ==> [[8,8,8],[8,8,8]]

也可以创建常量序列tensor

tf.linspace(start, stop, num, name=None) 
# 创建一个start开始,stop结束,个数为num的等差数列tensor
tf.linspace(10.0, 13.0, 4) ==> [10.0 11.0 12.0 13.0]

tf.range(start, limit=None, delta=1, dtype=None, name='range')
# 创建一个以start为首相,比值为delta的等比数列tensor
# 'start'3, 'limit'18, 'delta'3
tf.range(start, limit, delta) ==> [3, 6, 9, 12, 15]
# 'limit'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.")

还可以从某些分布中生成随机常数tensor

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、数学操作
TensorFlow的数学操作跟Numpy中的一样标准。

a = tf.constant([3, 6])
b = tf.constant([2, 2])
tf.add(a, b) # >> [5 8]
tf.add_n([a, b, b]) # >> [7 10]. 即 a + b + b
tf.mul(a, b) # >> [6 12] mul是每个数单独相乘
tf.matmul(a, b) # >> ValueError
tf.matmul(tf.reshape(a, [1, 2]), tf.reshape(b, [2, 1])) # >> [[18]]
tf.div(a, b) # >> [1 3]
tf.mod(a, b) # >> [1 0]

下面是Python的Ops表,由“深入学习基础”一书的作者提供。

4、数据类型
Python原生类型
TensorFlow包含Python原生类型,如Python布尔值,数值(整数,浮点数)和字符串。单个值将被转换为0-d tensor(或标量),值列表将被转换为1-d tensor(向量),列表的列表将被转换为2-d tensor(矩阵),等等 。下面的例子是从“TensorFlow for Machine Intelligence”一书中进行了修改的。

# 0-d tensor, or "scalar"
t_0 = 19
tf.zeros_like(t_0) # ==> 0
tf.ones_like(t_0) # ==> 1

# 1-d tensor, or "vector"
t_1 = ['apple', 'peach', 'banana']
tf.zeros_like(t_1) # ==> ['' '' '']
tf.ones_like(t_1) # ==> TypeError: Expected string, got 1 of type 'int' instead.

# 2x2 tensor, or "matrix"
t_2 = [[True, False, False],
[False, False, True],
[False, True, False]]
tf.zeros_like(t_2) # ==> 2x2 tensor, all elements are False
tf.ones_like(t_2) # ==> 2x2 tensor, all elements are True

TensorFlow原生类型
像NumPy一样,TensorFlow也有自己的数据类型,如你所看到的tf.int32,tf.float32。

Numpy数据类型
目前为止,可以发现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数组。

TL; DR:大多数时候,可以互换使用TensorFlow类型和NumPy类型。

Note 1:字符串类型有一些不同。对于数字和布尔类型,TensorFlow和Numpy相对应。但是,由于NumPy处理字符串的方式,tf.string在NumPy中没有完全匹配。TensorFlow仍然可以从NumPy导入字符串数组,只是不用NumPy中明确指定一个dtype!

Note 2:TensorFlow和Numpy都是n-d数组库。Numpy支持ndarray,但是Numpy并不提供创建tensor的函数,也不支持自动求导和GPU计算,所有尽量使用TensorFlow的数据类型。

Note 3:使用Python类型来指定TensorFlow对象是快速和容易的,它对于原型设计的想法很有用。但是,这样做有一个重要的缺陷。Python类型缺乏明确数据类型的能力,但是TensorFlow的数据类型更为具体。例如,Python所有整数都是相同的类型,但是TensorFlow具有8位,16位,32位和64位整数。因此,如果您使用Python类型,TensorFlow必须推断是哪种数据类型。

当您将数据传递到TensorFlow中时,可以将数据转换为适当的类型,但某些数据类型仍可能难以正确声明,例如复数。因此,通常像Numpy数组一样创建Tensor对象。但是,尽可能的经常使用TensorFlow类型,因为TensorFlow和Numpy慢慢会互相兼容。

5、变量
常量很有趣,但是我认为现在你们对于变量也有足够的了解。常量和变量之间的区别
- 1.常量是不变的,变量是可变的。
- 2.常数的值存储在图形中,并且其值被复制到加载图形的地方。一个变量被单独存储,并且可以存在于一个参数服务器上。

点2说明了常量被存储在图定义中。当常量在内存存储比较昂贵的时候,每次你必须加载图形都会很慢。要查看图形的定义和图形定义中存储的内容,只需打印出图形的原型。 Protobuf代表协议缓冲区,“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())

输出:

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: 21
}

变量申明
要声明一个变量,你创建一个类tf.Variable的实例。 注意,它是tf.constant,但是tf.Variable而不是tf.variable,因为tf.constant是一个操作,而tf.Variable是一个类。

# 创建一个常值变量
a = tf.Variable(2, name="scalar")
# 创建一个向量变量
b = tf.Variable([2, 3], name="vector")
# 创建一个2x2矩阵变量
c = tf.Variable([[0, 1], [2, 3]], name="matrix")
# 创建一个全0784 x 10 tensor
W = tf.Variable(tf.zeros([784,10]))

tf.Variable还有这些操作:

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

在使用变量之前必须先初始化。当你没有初始化而对变量进行计算时,你会跳转到FailedPreconditionError: Attempting to use uninitialized
value tensor.
最简单的初始化全部变量的方法:tf_global_variables_initializer()

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

注意你没有获取任何值来使用tf.run()来初始化。

要初始化一个变量子集,可以使用tf.variables_initializer()来初始化一些变量。

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

你也可以使用tf.Variable.initializer来单独初始化每一个变量。

W = tf.Variable(tf.zeros([784,10]))
with tf.Session() as sess:
    sess.run(W.initializer)

另一个初始化变量的方法是从保存文件中恢复。 我们会在几个星期内讨论。

评估变量的值
如果我们打印初始化的变量,我们只看到tensor对象。

W = tf.Variable(tf.truncated_normal([700, 10]))
with tf.Session() as sess:
    sess.run(W.initializer)
    print(W)

>> 'Variable:0' shape=(700, 10) dtype=float32_ref>

为了获得变量的值,我们需要使用eval()

W = tf.Variable(tf.truncated_normal([700, 10]))
with tf.Session() as sess:
    sess.run(W.initializer)
    print(W.eval())

>> [[ 0.88864553 -0.55380118  1.30751967 ...,  0.08146137 -0.92331994
   0.09765373]
 [ 0.02007066  0.569022    0.23260169 ..., -0.22853851  0.70450258
  -1.20768178]
 [-1.61542034  0.55419093 -0.43542355 ..., -1.03709829  1.38219023
  -0.00239466]
 ...,
 [-0.8796438   0.70934832 -0.32579809 ..., -0.700481    0.82873458
   0.10355152]
 [ 1.86596513 -1.0120163   0.19678293 ..., -0.65310007  1.37543499
  -1.20439768]
 [ 0.7738061   0.05669999 -0.32372263 ...,  1.4470402  -0.54410595
   0.33213395]]

变量赋值
我们可以使用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)没有把100赋值给W,但是创建了一个赋值操作。为了让这个操作起作用,我们可以在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()已经为我们做了。实际上,初始化操作是赋值操作中用初始化取赋值的。

self._initializer_op = state_ops.assign(self._variable, self._initial_value,validate_shape=validate_shape).op

有趣的例子

# create a variable whose original value is 2
my_var = tf.Variable(2, name="scalar")
# assign a * 2 to a and call that op a_times_two
a_times_two = a.assign(2 * a)
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    sess.run(a_times_two) # >> 4
    sess.run(a_times_two) # >> 8
    sess.run(a_times_two) # >> 16

每次获取a_times_two时,TensorFlow都会执行a* 2。

对于变量的简单递增和递减,TensorFlow有tf.Variable.assign_add()和tf.Variable.assign_sub()方法。不同于tf.Variable.assign(),tf.Variable.assign_add()和tf.Variable.assign_sub()不需要初始化你的变量,因为这些操作取决于变量的初始值。

my_var = tf.Variable(10)
With tf.Session() as sess:
    sess.run(my_var.initializer)
    sess.run(my_var.assign_add(10)) # >> 20
    sess.run(my_var.assign_sub(2)) # >> 18

因为TensorFlow会话分别维护值,所以每个Session可以为图中定义的变量拥有自己的当前值。

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(2 * W)

在这种情况下,您应该使用initialized_value()来确保在使用W之前初始化W。

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

6、InteractiveSession
有时会看到InteractiveSession而不是Session。 唯一的区别是
InteractiveSession使自己成为默认会话,因此您可以调用run()或eval()而不显式调用会话。 这在交互式shell和IPython笔记本中非常方便,因为它避免了将显式的Session对象传递给运行操作。但是,当您有多个会话运行时,是很复杂的。

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

tf.InteractiveSession.close()关闭InteractiveSession。
tf.get_default_session()返回当前线程的默认会话。返回的会话将是已经输入了Session或Session.as_default()上下文的最内层会话。

7、控制依赖性
有时候我们两个独立的操作,但是我们要选择哪个先执行,我们可以使用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和feed_dict
在课程1中,TensorFlow通常有2个部分:
- 1.组合一个图
- 2.使用一个会话在图上计算

因此,我们可以先组合图,而不必知道计算所需的值。相当于定义x,y函数而不需要知道x,y的值。
例如f(x,y)=2*x+y,x,y是实际值的占位符。

随着图形的组合,我们或我们的客户可以在需要执行计算时随时提供自己的数据。

定义一个placeholder

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

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

Shape指定可被接受为placeholder实际值的tensor的形状。shape = None意味着任何形状的tensor将被接受。
使用shape = None是很容易构造图,但是难调试。你应该始终尽可能详细地定义你的placeholder的形状。

你也可以在TensorFlow给你的palceholder一个名字。

# 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)
with tf.Session() as sess:
    print(sess.run(c)) # Error because a doesn’t have any value

这是一个错误,因为要计算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]})) # the tensor a is the key, not the stringa’
# >> [6, 7, 8]

让我们看看它在TensorBoard怎么样展示的,增加

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

在cmd中运行

$ tensorboard --logdi='my_graph'


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

通过迭代数据集,我们可以按照我们想要的方式将任何数据点作为数据点提供给占位符,并一次提供一个值。

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

你可以为不是占位符的张量提供值。任何tensor可以被用来提供。要检查张量是否可以提供,请使用:

tf.Graph.is_feedable(tensor)
# create operations, tensors, etc (using the default graph)
a = tf.add(2, 5)
b = tf.mul(a, 3)
with tf.Session() as sess:
    # 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不会浪费时间进行不必要的计算。

你可能感兴趣的:(TensorFlow)