1.Tensorflow
首先提提 Tensorflow 和 theano,它俩都是 python 封装的深度学习库,非常容易上手,说起来 Tensorflow 还是受 Theano 启发,借鉴了一部分它的思想。不同之处在于,Tensorflow 对分布式系统支持更好,同时还是 Google 提供资金研发的,而 Theano 是一个学术性质的项目。
Tensorflow 可以对定义在张量(tensors,你可以先简单理解成标量、向量或者矩阵,一会儿会提到) 上的函数自动求导,因此神经网络中 BP 算法可以很轻松地实现。
在开始 Tensorflow 之前,需要先让大家对 Tensorflow 的过程有个直观的理解。
在 Tensorflow 里:
- 使用
张量(tensor)
表示数据。 - 使用
图(graph)
来表示计算任务。 - 在被称之为
会话(Session)
的上下文 (context)
中执行图。 - 通过
变量 (Variable)
维护状态。 - 使用
feed
和fetch
可以为任意的操作(arbitrary operation) 赋值或者从其中获取数据。
严格意义上来说 TensorFlow 算是一个编程系统,它使用 图
来表示计算任务,图中的 节点
被称之为 operation(可以缩写成 op
),一个节点获得 0 个或者多个 张量
(tensor,下文会介绍到),执行计算,产生 0 个或多个张量。TensorFlow 的一个 图
描述了一个计算过程,为了进行计算, 图
必须在 会话(Session)
里被启动, 会话(Session)
将 图
的 op
分发到 CPU 或 GPU 之类的设备上,同时提供执行 op
的方法,这些方法执行后,将产生的 张量(tensor)
返回。返回的张量因语言不同而有不同,在 python 里是 numpy ndarry 对象;在 C/C++语言中,是 tensorflow::Tensor 实例。
下面咱们来详细说说上面提到的概念。
1.1 什么是张量
既然 Tensorflow 里面的定义和运算都是基于张量这个概念,我们就先来看看,什么是张量。
- 张量的正式定义:从向量空间到实数域的多重现性映射(multilinear maps)(
是向量空间,
是对偶空间)
- 标量是张量(
)( 译者注: 标量是用实数表示零维空间的点 )
- 向量是张量(
)( 译者注: 向量是用实数表示一维空间的点,也即向量中的某个元素 )
- 矩阵是张量(
)( 译者注: 矩阵是用实数表示二维空间的点,也即矩阵的某个元素 )
- 通常来说, 张量可以用多维数组来表示
1.2 Tensorflow 与 Numpy
- 看似差别甚远的 2 个 package,说起来可能也很少有人把这两者作对比,但他们“长得”确实很相似(都是提供 N 维数组的库)
- Numpy 有 Ndarray(N 维数组) 支持,但不提供创建张量函数和自动求导的方法,也不提供 GPU 支持
1.3 Numpy 与 Tensorflow 定义与操作对比
# numpy 定义与操作
In [23]: import numpy as 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]: import tensorflow as 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 中的部分定义和操作做成了一张一一对应的表格,方便大家查看。
Numpy | Tensorflow |
---|---|
a = 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.shape | a.get_shape() |
np.reshape(a, (1,4)) | tf.reshape(a, (1,4)) |
b*5+1 | b*5+1 |
np.dot(a,b) | tf.matmul(a, b) |
a[0,0], a[:,0], a[0,:] | a[0,0], a[:,0], a[0,:] |
Tensorflow 的输出要稍微注意一下,我们需要显式地输出(evaluation,也就是说借助 eval() 函数)!
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()
函数输出。
1.4 Tensorflow 的计算图
用 Tensorflow 编写的程序一般由两部分构成,一是构造部分,包含了计算流图,二是执行部分,通过 session 来执行图中的计算,具体可以参考 Tensorflow 文档
我们先来看看怎么构建图。构件图的第一步是创建 源节点
(source op)。 源节点
不需要任何输入,它的输出传递给其它节点(op) 做运算。python 库中,节点构造器的返回值即当前节点的输出,这些返回值可以传递给其它节点(op) 作为输入。
TensorFlow Python 库中有一个 默认图
(default graph),在默认图的基础上, 节点构造器(op 构造器)
可以为其增加节点。这个默认图对许多程序来说已经足够用了,更多管理视图的细节可以阅读 官方 Graph 类文档 。
我们来看一个简单的构建图例子:
import tensorflow as tf
# 创建一个常量节点, 产生一个 1x2 矩阵,这个 op 被作为一个节点
# 加到默认视图中
# 构造器的返回值代表该常量节点的返回值
matrix1 = tr.constant([[3., 3.]])
# 创建另一个常量节点,产生一个 2x1 的矩阵
matrix2 = tr.constant([[2.], [2.]])
# 创建一个矩阵乘法 matmul 节点,把 matrix1 和 matrix2 作为输入:
product = tf.matmul(matrix1, matrix2)
上面代码里的默认图现在有三个节点,两个 constant() 节点和 matmul() 节点。不过这仅仅是构建图,为了真正进行矩阵的乘法,你必须在 会话(Session,马上提到)
里启动这个图。
1.5 Tensorflow 与 Session 对象
上面我们知道了 Tensorflow 需要先构造一个图用于计算,但是图怎么启动呢?启动图的第一步需要创建一个 Session 对象。比如:
# 创建 session,启动默认图
sess = tf.Session()
# 调用 sess 的'run()' 方法来执行矩阵乘法节点操作,传入'product'作为该方法的参数。'product'代表了矩阵乘法节点的输出,传入它是告诉方法我们希望取回矩阵乘法节点的输出。
#整个执行过程是自动化的,会话负责传递节点所需的全部输入。节点通常是并发执行的。
# 函数调用'run(product)'会触发图中三个节点(上面例子里提到的两个常量节点和一个矩阵乘法节点)的执行。
# 返回值'result'是一个 numpy 'ndarray'对象。
result = sess.run(product)
print result
# 结果为[[12.]]
# 完成任务,记得关闭会话
sess.close()
Session 对象在使用完成后,记得关闭以释放资源,当然,除了显式调用 close 关闭外,也可以使用 with 代码来自动完成关闭动作:
# 用 with 代码来自动完成 session 里的图运算并关闭
with tf.Session() as sess:
result = sess.run([product])
print result
为了便于使用像 IPython 这样的 python 交互环境,可以使用 InteractiveSession 代替 Session 类,使用 Tensor.eval() 和 Operation.run() 方法代替 Session.run()。这样做的好处是可以在 ipython 中保持默认 session 处于打开状态:
# 进入一个交互式 Tensorflow 会话
import tensorflow as tf
sess = tf.InteractiveSession()
x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0]);
# 使用初始化器的 run() 方法初始化 x
x.initializer.run()
# 增加一个减法节点,从 x 减去 a。运行减法 op,输出结果
sud = tf.sub(x, a)
print sub.eval()
# 结果为[-2. -1.]
1.6 关于 session 和多 GPU 运算
我们一直在说,Tensorflow 是支持分布式的深度学习框架/包,这是因为它能将图定义转换成分布式执行的操作,以充分利用可以利用的计算资源(如 CPU 或 GPU)。不过一般情况下,你不需要显式指定使用 CPU 还是 GPU,Tensorflow 能自动检测。如果检测到 GPU,Tensorflow 会优先使用找到的第一个 GPU 来执行操作。
如果机器上有超过一个可用的 GPU,默认状况下除了第一个外的其他 GPU 是不参与计算的。为了让 Tensorflow 使用这些 GPU,你必须将节点运算明确地指派给它们执行。其中 with…Device 语句用来指派特定的 CPU 或 GPU 操作:
# 手动指定给某个 gpu 执行
with tf.Session() as sess:
with tf.device("/gpu:1"):
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.], [2.]])
product = tf.matmul(matrix1, matrix2)
指定设备的书写格式如下:
/cpu:0
:机器的 CPU/gpu:0
:机器的第一个 GPU,如果有的话/gpu:1
:机器的的第二个 GPU,其他 GPU 以此类推
1.7 Tensorflow 的变量(Variables)
我们训练一个模型的时候,会用到 Tensorflow 中的 变量(Variables)
,我们需要它来保持和更新参数值,和 张量
一样, 变量
也保存在内存缓冲区当中。
有很多同学会问,前面不是提到了一个概念叫做张量,为什么还需要这个新的变量呢?需要说明一下的是,如果大家仔细看之前的代码,会发现我们所用到的张量都是常值张量(constant tensors),而非变量,而参数值是需要动态调整的内容。
比如下面的代码里我们设定了一组权重为变量:
In [32]: W1 = tf.ones((2,2))
In [33]: W2 = tf.Variable(tf.zeros((2,2)), name="weights")
In [34]: with tf.Session() as 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 的变量必须先初始化然后才有值!而常值张量是不需要的
再具体一点,比如下面的代码,其实 38 和 39 步,我们初始化定义初值是可以通过常数或者随机数等任何一种方式初始化的,但是直到第 40 步才真正通过 Tensorflow 的 initialize_all_variables 对这些变量赋初值。
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]: with tf.Session() as sess:
....: sess.run(tf.initialize_all_variables())
....: print(sess.run(W))
....: print(sess.run(R))
....:
比如我们来看一个 计算图
中变量的状态更新过程,代码如下:
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]: with tf.Session() as sess:
sess.run(tf.initialize_all_variables())
print(sess.run(state))
for _ in range(3):
sess.run(update)
print(sess.run(state))
0
1
2
3
上面的代码定义了一个如下的计算图,同时其中变量的状态是循环变化的:*
Created with Raphaël 2.1.0 开始 sess.run(tf.initialize_all_variables()) (e.g. state=0)sess.run(update) (e.g. state = new_value = state+1) 循环结束?结束 yesno
1.8 Tensorflow 的 Fetch(获取) 操作
如果想取回定义的计算图中的节点运算输出结果,可以在使用 Session 对象的 run() 调用执行图时,传入一些张量,这些张量可以帮助你取回结果。而且不仅仅是单个节点的状态或者结果,可以输出多个节点的结果,比如下面这个简单例子:
input1 = tf.constant(3.0)
input2 = tf.constant(4.0)
input3 = tf.constant(5.0)
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)
with tf.Session() as sess:
result = sess.run([mul, intermed])
print result
# print
# 输出最后的乘法结果,和之前的加法结果[27.0, 9.0]
1.9 Tensorflow 与 Feed(传入) 操作
1.8 里我们提到了在计算图中引入张量,以获取节点状态或者输出结果。Tensorflow 还提供了 feed 机制,该机制可以临时替代图中的任意操作中的张量,也就是说,可以对图中任何操作提交补丁,直接插入一个新的张量。
feed 可以使用一个张量值临时替换某个操作的输出结果,你只需要提供 feed 数据作为 run() 调用的参数。需要说明的是,feed 只在调用它的方法内有效,方法结束则 feed 就会消失。最常见的用例是将某些特殊的操作指定为 feed 操作,标记的方法是使用 tf.placeholder() 为这些操作创建占位符(可以先想成一个容器,这个在之后的内容里会提到,不要着急)。
input1 = tf.placeholder(tf.types.float32)
input2 = tf.placeholder(tf.types.float32)
output = tf.mul(input1, input2)
# 手动提供 feed 数据作为 run 的参数
with tf.Session() as see:
print sess.run([output], feed_dict={input:[7.], input2:[2.]})
# print
# 结果是[array([ 14.], dtype=float32)]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论