TensorFlow是目前最流行的深度学习框架。我们先引用一段官网对于TensorFlow的介绍,来看一下Google对于它这个产品的定位。
TensorFlow™ is an open source software library for numerical computation using data flow graphs. Nodes in the graph represent mathematical operations, while the graph edges represent the multidimensional data arrays (tensors) communicated between them. The flexible architecture allows you to deploy computation to one or more CPUs or GPUs in a desktop, server, or mobile device with a single API.
上文并没有提到大红大紫的Deep Learning,而是聚焦在一个更广泛的科学计算应用领域。引文的关键词有:
TensorFlow提供多种语言的支持,其中支持最完善的是Python语言,因此本文将聚焦于Python API。
In [1]:
import numpy as np
import tensorflow as tf
# Create 100 phony x, y data points in NumPy, y = x * 0.1 + 0.3
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3
# Try to find values for W and b that compute y_data = W * x_data + b
# (We know that W should be 0.1 and b 0.3, but TensorFlow will
# figure that out for us.)
W = tf.Variable(tf.random_uniform([1], -1, 1))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b
# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
# Before starting, initialize the variables. We will 'run' this first.
init = tf.global_variables_initializer()
# Launch the graph.
with tf.Session() as sess:
sess.run(init)
# Fit the line.
for step in range(201):
sess.run(train)
if step % 20 == 0:
print(step, sess.run(W), sess.run(b))
下面我们来剖析一下关键代码。TensorFlow的代码往往由两个部分组成:
Session是一个类,作用是把graph ops部署到Devices(CPUs/GPUs),并提供具体执行这些op的方法。
为什么要这么设计呢?考虑到Python运行性能较低,我们在执行numerical computing的时候,都会尽量使用非python语言编写的代码,比如使用NumPy这种预编译好的C代码来做矩阵运算。
在Python内部计算环境和外部计算环境(如NumPy)切换需要花费的时间称为overhead cost。对于一个简单运算,比如矩阵运算,从Python环境切换到Numpy,Numpy运算得到结果,再从Numpy切回Python,这个成本,比纯粹在Python内部做同类运算的成本要低很多。但是,一个复杂数值运算由多个基本运算组合而成,如果每个基本运算来一次这种环境切换,overhead cost就不可忽视了。为了减少来回的环境切换,TensorFlow的做法是,先在Python内定义好整个Graph,然后在Python外运行整个完整的Graph。因此TensorFlow的代码结构也就对应为两个阶段了。
In [2]:
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
tf.Variable是TensorFlow的一个类,是取值可变的Tensor,构造函数的第一个参数是初始值initial_value。
initial_value: A Tensor, or Python object convertible to a Tensor, which is the initial value for the Variable.
tf.zeros(shape, dtype=tf.float32, name=None)是一个op,用于生成取值全是0的Constant Value Tensor。
tf.random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None, name=None)是一个op,用于生成服从uniform distribution的Random Tensor。
In [3]:
y = W * x_data + b
y是线性回归运算产生的Tensor。运算符 * 和 + ,等价为tf.multiple()和tf.add()这两个TensorFlow提供的数学类ops。
tf.multiple()的输入是W和x_data;
TensorFlow的ops接收到ndarray的输入时,会将其转化为tensor。tf.multiple()的输出是一个tensor,和b一起交给optf.add(),得到输出结果y。
至此,线性回归的模型已经建立好,但这只是Graph的一部分,还需要定义损失。
In [4]:
loss = tf.reduce_mean(tf.square(y - y_data))
loss是最小二乘法需要的目标函数,是一个Tensor,具体的op不再赘述。
In [5]:
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
这一步指定求解器,并设定求解器的最小化目标为损失。train代表了求解器执行一次的输出Tensor。这里我们使用了梯度下降求解器,每一步会对输入loss求一次梯度,然后将loss里Variable类型的Tensor按照梯度更新取值。
In [6]:
init = tf.global_variables_initializer()
Build Graph阶段的代码,只是在Python内定义了Graph的结构,并不会真正执行。在Launch Graph阶段,所有的变量要先进行初始化。每个变量可以单独初始化,但这样做有些繁琐,所以TensorFlow提供了一个方便的函数global_variables_initializer()可以在graph中添加一个初始化所有变量的op。
When you launch the graph, variables have to be explicitly initialized before you can run Ops that use their value. All variables are automatically collected in the graph where they are created. By default, the constructor adds the new variable to the graph collection GraphKeys.GLOBAL_VARIABLES. The convenience function global_variables() returns the contents of that collection. The most common initialization pattern is to use the convenience function global_variables_initializer() to add an Op to the graph that initializes all the variables.
In [7]:
# Launch the graph.
sess = tf.Session()
sess.run(init)
In [8]:
for step in range(201):
sess.run(train)
train操作对应梯度下降法的一步迭代。当step为0时,train里的variable取值为初始值,根据初始值可以计算出梯度,然后将初始值根据梯度更新为更好的取值;当step为1时,train里的variable为上一步更新的值,根据这一步的值可以计算出一个新的梯度,然后将variable的取值更新为更好的取值;以此类推,直到达到最大迭代次数。
In [9]:
print(step, sess.run(W), sess.run(b))
如果我们将sess.run()赋值给Python环境的变量,或者传给Python环境的print,可以fetch执行op的输出Tensor取值,这些取值会转化为numpy的ndarray结构。因此,这就需要一次环境的切换,会增加overhead cost。所以我们一般会每隔一定步骤才fetch一下计算结果,以减少时间开销。
TensorFlow是一个面向数值计算的通用平台,可以方便地训练线性模型。下面这几篇文章采用TensorFlow完成Andrew Ng主讲的Deep Learning课程练习题,提供了整套源码。
TensorFlow虽然是面向通用的数值计算,但是对深度学习的支持是它最大的特色,也是它能够引爆业界获得目前这么大的流行度的主要原因。下面这几篇文章采用TensorFlow对MNIST进行建模,涵盖了Deep Learning中最重要的两类模型:卷积神经网络CNN和循环神经网络RNN。
TensorFlow安装时自带了一个TensorBoard,可以对数据集进行可视化地探索分析,可以对学习过程进行可视化,可以对Graph进行可视化,对于我们分析问题和改进模型有极大的帮助。