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)
- 通过程序生成
[/*][/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))从以上代码不难发现,Tensorflow通过计算图(computation graph)定义一个计算过程,此过程不产生数值结果,除非用
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.]]
.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)以上第87步,调用
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]
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)第96、97步,定义
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)]
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()(译者注:上面第136步所用的
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})
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)
将优化操作加入计算流图当中
[/*][/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)
关注 数据挖掘机养成记
微信扫一扫关注公众号