Tensorflow 指南[译]

 

Tensorflow的简单入门...



【原创,转载请先联系本人(微信号:MuWenHappyEveryday)并注明来自公众号“数据挖掘机养成记”

【前言】

最近在参与Stanford CS224D 的课程翻译,以下是我翻译的课程中有关Tensorflow的入门ppt,原文请点击底部『阅读原文』链接,如有翻译错误或不当,欢迎指正

Tensorflow 指南(初稿)

Bharath Ramsundar

正式通知

  • PSet 1的截止日期今天
  • PSet 2明天发布
  • 举手之劳,通过调查问卷给我们反馈课程建议

深度学习包汇总

  • Torch
  • Caffe
  • Theano (Keras, Lasagne)
  • CuDNN
  • Tensorflow
  • Mxnet
  • Etc.




深度学习包的选择

  • 模型设置方式:
  • [list]
  • 高级语言:Lua(Torch)
  • 脚本语言:Python(Theano, Tensorflow)
  • 其他
  • 通过配置文件(如 Caffe, DistBelief, CNTK)
  • 通过程序生成
[*]我们在课程中选择使用python,因为python有丰富的社区和第三方库

[/*][/list]

Tensorflow和Theano的对比

  • 都是使用python封装的深度学习库,Tensorflow 借鉴了 Theano
  • Theano 和 Tensorflow 是很相似的系统。不同之处在于,Tensorflow 对分布式系统支持更好,同时还是Google提供资金研发的,而 Theano 是一个学术性质的项目

Tensorflow 简介

  • Tensorflow 是最近由Google开源的深度学习库
  • Tensorflow 提供张量(tensors)定义的函数原型并且自动计算他们的导数

什么是张量

  • 张量的正式定义:从向量空间到实数域的多重现性映射(multilinear maps)(V是向量空间,V*是对偶空间)
  • [list]
  • 标量是张量()(译者注: 标量是用实数表示零维空间的点
  • 向量是张量()(译者注: 向量是用实数表示一维空间的点,也即向量中的某个元素
  • 矩阵是张量( )(译者注: 矩阵是用实数表示二维空间的点,也即矩阵的某个元素
  • 通常来说,张量可以用多维数组来表示

Tensorflow 和 Numpy 对比

  • 很少有人把这两者作对比,但他们确实很相似(都是提供N维数组的库)
  • Numpy 有 Ndarray 支持,但不提供创建张量函数和自动求导的方法,也不提供GPU支持

Numpy 简单回顾

In [23]: [b]import[/b] numpy [b]as[/b] np
In [24]: a = np.zeros((2,2)); b = np.ones((2,2))
In [25]: np.sum(b, axis=1)
Out[25]: array([ 2.,  2.])
In [26]: a.shape
Out[26]: (2, 2)
In [27]: np.reshape(a, (1,4))
Out[27]: array([[ 0.,  0.,  0.,  0.]])

对比 Tensorflow 中同样的操作

In [31]: [b]import[/b] tensorflow [b]as[/b] tf
In [32]: tf.InteractiveSession()
In [33]: a = tf.zeros((2,2)); b = tf.ones((2,2))
In [34]: tf.reduce_sum(b, reduction_indices=1).eval()
Out[34]: array([ 2.,  2.], dtype=float32)
In [35]: a.get_shape()
Out[35]: TensorShape([Dimension(2), Dimension(2)])
In [36]: tf.reshape(a, (1, 4)).eval()
Out[36]: array([[ 0.,  0.,  0.,  0.]], dtype=float32)
以上代码中提到的
session
.eval()
将在下文细述,
TensorShape
类似Python的tuple类型

Numpy 与 Tensorflow 部分操作的对比

NumpyTensorflowa = np.zeros((2,2)); b = np.ones((2,2))a = tf.zeros((2,2)), b = tf.ones((2,2))np.sum(b, axis=1)tf.reduce_sum(a,reduction_indices=[1])a.shapea.get_shape()np.reshape(a, (1,4))tf.reshape(a, (1,4))b*5+1b*5+1np.dot(a,b)tf.matmul(a, b)a[0,0], a[:,0], a[0,:]a[0,0], a[:,0], a[0,:]

Tensorflow 需要显式地输出(evaluation)!

In [37]: a = np.zeros((2,2))
In [38]: ta = tf.zeros((2,2))
In [39]: print(a)
[[ 0.  0.]

[ 0.  0.]]
In [40]: print(ta)
Tensor("zeros_1:0", shape=(2, 2), dtype=float32)
In [41]: print(ta.eval())
[[ 0.  0.]
[ 0. 0.]]
从以上代码不难发现,Tensorflow通过计算图(computation graph)定义一个计算过程,此过程不产生数值结果,除非用
.eval()
函数输出

Tensorflow的Session对象

  • 『Session对象封装了用于输出Tensor对象估值的环境』-Tensorflow文档
    In [20]: a = tf.constant(5.0)In [21]: b = tf.constant(6.0)In [22]: c = a * bIn [23]: [b]with[/b] tf.Session() [b]as[/b] sess:....:     print(sess.run(c))....:     print(c.eval())....:30.0 30.0
    以上代码中,
    c.eval()
    sess.run(c)
    在当前session下的语法糖(syntactic sugar)
  • ft.InteractiveSession
    也是一种语法糖,是为了在ipython中保持默认session处于打开状态
  • sess.run(c)
    是Tensorflow Fetch 方法的一个特例,后面会详细讨论

Tensorflow 的计算图

  • 『用Tensorflow编写的程序一般由两部分构成,一是构造部分,包含了计算流图,二是执行部分,通过session 来执行图中的计算 』Tensorflow文档
  • 所有的计算将在全局默认的图中增加节点(文档)

Tensorflow 的变量(Variables)

  • 『当你训练一个模型时,你使用变量来保持和更新参数值,跟张量一样,变量也保存在内存缓冲区当中』-Tensorflow文档
  • 在本文先前的代码中,我们所用到的张量都是常值张量(constant tensors),而非变量
    In [32]: W1 = tf.ones((2,2))In [33]: W2 = tf.Variable(tf.zeros((2,2)), name="weights")In [34]: [b]with[/b] tf.Session() [b]as[/b] sess:print(sess.run(W1))sess.run(tf.initialize_all_variables())print(sess.run(W2))....:[[ 1.  1.][ 1.  1.]][[ 0.  0.][ 0. 0.]]
    注意上面第34步
    tf.initialize_all_variables
    ,要预先对变量初始化(initialization)
  • Tensorflow 的变量必须先初始化然后才有值!跟常值张量的区别也在于此
    In [38]: W = tf.Variable(tf.zeros((2,2)), name="weights")In [39]: R = tf.Variable(tf.random_normal((2,2)), name="random_weights")In [40]: [b]with[/b] tf.Session() [b]as[/b] sess:....:     sess.run(tf.initialize_all_variables())....:     print(sess.run(W))....:     print(sess.run(R))....:
    从上面第38、39步可以看出,变量可以通过常数或者随机值定义初值,第40步则是通过初始化使得变量被赋予相应值

更新变量的状态

In [63]: state = tf.Variable(0, name="counter")
In [64]: new_value = tf.add(state, tf.constant(1))
In [65]: update = tf.assign(state, new_value)
In [66]: [b]with[/b] tf.Session() [b]as[/b] sess:

sess.run(tf.initialize_all_variables())

print(sess.run(state))    [b]for[/b] _ [b]in[/b] range(3):

sess.run(update)

print(sess.run(state))0123
译者注:以上定义了一个计算流图:

st=>start: 开始
op1=>operation: sess.run(tf.initialize_all_variables())
state = 0
op2=>operation: sess.run(update)

state = state+1
cond=>condition: 循环结束?
e=>end: 结束
st->op1->op2->cond
cond(yes)->e
cond(no)->op2->cond

获取变量的状态

In [82]: input1 = tf.constant(3.0)
In [83]: input2 = tf.constant(2.0)
In [84]: input3 = tf.constant(5.0)
In [85]: intermed = tf.add(input2, input3)
In [86]: mul = tf.mul(input1, intermed)
In [87]: [b]with[/b] tf.Session() [b]as[/b] sess:

....:       result = sess.run([mul, intermed])

....:       print(result)

....:
[21.0, 7.0]
以上第87步,调用
sess.run(var1)
函数可以获取变量
var1
的值,而
sess.run([var1,var2])
可以同时获取
var1 var2
的值(译者注:这个特性有点像python当中交换两个变量值的语句:
var1,var2 = var2,var1

上面代码的执行过程可以用下面这张图表示:


数据输入

  • 前面所有例子都是手动定义张量,如何将外部数据输入Tensorflow呢?
  • 一个简单的解决方案:从Numpy导入:
    In [93]: a = np.zeros((3,3))In [94]: ta = tf.convert_to_tensor(a)In [95]: [b]with[/b] tf.Session() [b]as[/b] sess:....:     print(sess.run(ta))....:[[ 0.  0.  0.][0. 0. 0.] [0. 0. 0.]]

Placeholders 和 Feed 词典

  • 通过
    tf.convert_to_tensor()
    输入数据很方便,但不通用
  • 建议使用
    tf.placeholder
    设置变量(dummy节点,提供数据输入到计算流图的入口)
  • feed_dict
    是一个python字典,描述从
    tf.placeholder
    变量(或者变量名)到数据(numpy的数组,python的列表等等)的映射,具体见下面的例子
In [96]: input1 = tf.placeholder(tf.float32)
In [97]: input2 = tf.placeholder(tf.float32)
In [98]: output = tf.mul(input1, input2)
In [99]: [b]with[/b] tf.Session() [b]as[/b] sess:

....:       print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

....:
[array([ 14.], dtype=float32)]
第96、97步,定义
tf.placeholder
对象作为数据入口,第99步,
sess.run()
用于获取计算流图的输出值,
feed_dict
用于将数据输入到计算流图。上述计算过程可以表示为:


变量作用域

  • 一个复杂的 Tensorflow 模型可能有上百个变量
  • [list]
  • tf.variable_scope()
    提供简化的命名空间(name-spaceing)以避免冲突
  • tf.get_variable()
    能在变量作用域内创建、获取变量
[*]变量作用域是一个简单的命名空间,在作用域内,可以加入预先设置好的变量名,比如

[/*][/list]
[b]with[/b] tf.variable_scope("foo"):    [b]with[/b] tf.variable_scope("bar"):

v = tf.get_variable("v", [1])[b]assert[/b] v.name == "foo/bar/v:0"
译者注:从上面例子可以看出,不同命名空间下同样的变量名,在全局范围内有不同的
name


  • 变量作用域控制变量使用和复用(reuse)
[b]with[/b] tf.variable_scope("foo"):

v = tf.get_variable("v", [1])

tf.get_variable_scope().reuse_variables()

v1 = tf.get_variable("v", [1])[b]assert[/b] v1 == v
译者注:如果没有
reuse_variables()
这句,会报错。我理解这里的
v1
类似给
v
取了个别名)
(提醒:作业中涉及RNN时,要用到
reuse_variables()
)

理解
get_variable

  • 其结果取决于是否开启变量复用
  • 例1.
    reuse
    设置为
    false
    译者注:默认是
    false
    ),创建和返回新变量
    [b]with[/b] tf.variable_scope("foo"):v = tf.get_variable("v", [1])[b]assert[/b] v.name == "foo/v:0"
  • 例2.
    reuse
    设置为
    true
    ,搜索已存在的变量名,如果没找到,将抛
    ValueError
    异常
    [b]with[/b] tf.variable_scope("foo"):v = tf.get_variable("v", [1])[b]with[/b] tf.variable_scope("foo", reuse=[b]True[/b]):v1 = tf.get_variable("v", [1])[b]assert[/b] v1 == v

小练习:用TensorFlow实现线性回归

# 译者注:因本人使用的是matplotlib,故以下代码有改动,原版请见原文[b]import[/b] numpy [b]as[/b] np[b]from[/b] pylab [b]import[/b] *# 产生输入数据X_data = np.arange(100, step=.1)
y_data = X_data + 20 * np.sin(X_data/10)# 绘制输入数据的图形plot(X_data, y_data)
plt.show()
数据分布如下


# 定义数据大小n_samples = 1000batch_size = 100# Tensorflow对数据的shape敏感,需要重整数组# 译者注:X_data原来是一个一维数组(n),这里把他变成二维数组(nx1)X_data = np.reshape(X_data, (n_samples,1))
y_data = np.reshape(y_data, (n_samples,1))# 为输入定义 placeholdersX = tf.placeholder(tf.float32, shape=(batch_size, 1))
y = tf.placeholder(tf.float32, shape=(batch_size, 1))
译者注
batch_size
定义批量训练的样本量大小,这里是每次用100个样本点(而非全量1000)进行训练)

# 定义待学习的变量[b]with[/b] tf.variable_scope("linear-regression"):

W = tf.get_variable("weights", (1, 1),initializer=tf.random_normal_initializer())

b = tf.get_variable("bias", (1,),initializer=tf.constant_initializer(0.0))

y_pred = tf.matmul(X, W) + b

loss = tf.reduce_sum((y - y_pred)**2/n_samples)
译者注
weights
定义为(1,1)而非(1,),是为了跟
X
计算矩阵乘法,这点跟numpy中类似)
最后一步的loss 函数的原型为



# 运行一步梯度下降的样例代码In [136]: opt = tf.train.AdamOptimizer()
In [137]: opt_operation = opt.minimize(loss)
In [138]: [b]with[/b] tf.Session() [b]as[/b] sess:

.....:     sess.run(tf.initialize_all_variables())

.....:     sess.run([opt_operation], feed_dict={X: X_data, y: y_data})
译者注:上面第136步所用的
Adam
优化法据称是目前最棒的梯度下降系列的优化方法)
上面第137步中,虽然
loss
这个变量已经不在python的
scope
内(译者注:因为
loss
是在上上段代码的
with
中定义的,但
with
作用域已经结束),但仍然在tensorflow的
scope


# 运行梯度下降# 定义优化器操作opt_operation = tf.train.AdamOptimizer().minimize(loss)[b]with[/b] tf.Session() [b]as[/b] sess:    # 初始化计算流图中的变量

sess.run(tf.initialize_all_variables())      # 循环500次梯度下降

[b]for[/b] _ [b]in[/b] range(500):    # 随机选取batch

indices = np.random.choice(n_samples, batch_size)

X_batch, y_batch = X_data[indices], y_data[indices]        # 梯度下降

_, loss_val = sess.run([opt_operation, loss], feed_dict={X: X_batch, y: y_batch})
以上计算过程定义的计算流图如下:


最后拟合的直线和原数据图如下:


概念:自动微分

  • 在线性回归的小练习当中,需要计算线性回归系统的
    L2
    损失函数,我们是如何拟合数据的呢?
  • [list]
  • tf.train.Optimizer
    创建一个优化求解器
  • tf.train.Optimizer.minimize(loss, var_list)
    将优化操作加入计算流图当中
[*]Tensorflow能通过自带的微分操作自动计算梯度,不需用户自己输入

[/*][/list]

Tensorflow梯度计算过程

  • Tensorflow计算流图中的节点已经附带了梯度运算
  • 使用后向传播(在节点中具体设置梯度操作)为计算流图中所有变量计算其所需要的梯度

Tensorflow的调试方法

  • 将张量转换成numpy的数组并打印
  • Tensorflow对张量的类型和维度要求很严格,检查一下所有张量的类型/维度是否匹配
  • Tensorflow的API没有numpy的API成熟,numpy中许多高阶操作(比如负责的数组切片)在Tensorflow中尚未实现
  • 如果在某个地方卡住了,可以尝试使用纯粹的numpy操作实现前向计算
  • 当使用shell时(译者注:如IPython),可以使用
    tf.InteractiveSession()

有关作业的提示:在Tensorflow中定义词向量

# 为输入定义 Placeholderstrain_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])# 在输入中查找词向量# 将在Pset 2中使用这些# You’ll use this for PSet 2embeddings = tf.Variable(

tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
embed = tf.nn.embedding_lookup(embeddings, train_inputs)


    关注 数据挖掘机养成记


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册