65.9K
CodeProject 正在变化。 阅读更多。
Home

如何开始使用 TensorFlow

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (7投票s)

2017年10月24日

CPOL

9分钟阅读

viewsIcon

18979

在本节中,我们将开始使用低级 TensorFlow API。

本文摘录自全新 edition 的 Python Machine Learning,作者为 Sebastian Raschka 和 Vahid Mirjalili。

什么是 TensorFlow?

TensorFlow 是一个可扩展且跨平台的编程接口,用于实现和运行机器学习算法,包括深度学习的便捷封装。

TensorFlow 由 Google Brain 团队的研究人员和工程师开发;虽然主要开发由 Google 的研究人员和软件工程师团队领导,但其开发也得到了开源社区的许多贡献。TensorFlow 最初仅供 Google 内部使用,但后来于 2015 年 11 月根据宽松的开源许可证发布。

为了提高训练机器学习模型的性能,TensorFlow 允许在 CPU 和 GPU 上执行。但是,当使用 GPU 时,可以发现其最大的性能能力。TensorFlow 正式支持支持 CUDA 的 GPU。对支持 OpenCL 的设备的支持仍处于实验阶段。但是,OpenCL 很可能在不久的将来得到官方支持。

TensorFlow 目前支持多种编程语言的前端接口。幸运的是,作为 Python 用户,TensorFlow 的 Python API 目前是最完整的 API,因此吸引了许多机器学习和深度学习从业者。此外,TensorFlow 还提供 C++ 的官方 API。

其他语言(如 Java、Haskell、Node.js 和 Go)的 API 尚未稳定,但开源社区和 TensorFlow 开发者正在不断改进它们。TensorFlow 计算依赖于构建一个有向图来表示数据流。尽管构建图听起来很复杂,但 TensorFlow 提供了高层 API,使得构建图变得非常容易。

我们将如何学习 TensorFlow

我们将首先学习低级 TensorFlow API。虽然在此级别实现模型可能一开始有些繁琐,但低级 API 的优点在于它为程序员提供了更大的灵活性,可以组合基本操作并开发复杂的机器学习模型。从 TensorFlow 版本 1.1.0 开始,在低级 API 之上添加了高层 API(例如,所谓的 Layers 和 Estimators API),这使得构建和原型化模型的速度大大加快。

在学习了低级 API 之后,我们将继续探索两个高层 API,即 TensorFlow LayersKeras。不过,让我们先从 TensorFlow 低级 API 开始,逐步了解一切如何运作。

TensorFlow 入门

在本节中,我们将开始使用低级 TensorFlow API。根据您的系统设置,通常可以使用 Python 的 pip 安装程序,通过在终端中执行以下命令从 PyPI 安装 TensorFlow:

pip install tensorflow

如果您想使用 GPU,则需要安装 CUDA Toolkit 和 NVIDIA cuDNN 库;然后,您可以安装支持 GPU 的 TensorFlow,如下所示:

pip install tensorflow-gpu

TensorFlow 正在积极开发中;因此,每隔几个月就会发布新版本,并进行重大更改。在撰写本文时,最新的 TensorFlow 版本是 1.3.0。您可以通过终端验证您的 TensorFlow 版本,如下所示:

python -c 'import tensorflow as tf; print(tf.__version__)'

注意

如果您在安装过程中遇到问题,我建议您进一步阅读 https://tensorflowcn.cn/install/ 提供的系统和平台特定建议。请注意,本章中的所有代码都可以在 CPU 上运行;使用 GPU 是完全可选的,但如果您想充分享受 TensorFlow 的优势,则建议使用 GPU。如果您有显卡,请参考安装页面进行相应设置。此外,您可能会发现此 TensorFlow-GPU 设置指南很有用,该指南解释了如何在 Ubuntu 上安装 NVIDIA 显卡驱动程序、CUDA 和 cuDNN(非必需,但您可以在此处找到在 GPU 上运行 TensorFlow 的推荐要求)。

TensorFlow 构建于一个计算图之上,该计算图由一组节点组成。每个节点代表一个可能具有零个或多个输入或输出的操作。流经计算图边缘的值称为张量

张量可以理解为标量、向量、矩阵等的泛化。更具体地说,标量可以定义为秩-0 张量,向量可以定义为秩-1 张量,矩阵可以定义为秩-2 张量,堆叠在第三维度的矩阵可以定义为秩-3 张量。

一旦计算图构建完成,就可以在 TensorFlow Session 中启动该图来执行图中的不同节点。作为热身练习,我们将从使用 TensorFlow 的简单标量开始,计算具有权重 w 和偏置 b 的一维数据集上样本点 x 的净输入 z。

以下代码展示了此方程在低级 TensorFlow API 中的实现:

import tensorflow as tf

## create a graph
g = tf.Graph()
with g.as_default():
   x = tf.placeholder(dtype=tf.float32,
            shape=(None), name='x')
   w = tf.Variable(2.0, name='weight')
   b = tf.Variable(0.7, name='bias')

   z = w*x + b

   init = tf.global_variables_initializer()
## create a session and pass in graph g
with tf.Session(graph=g) as sess:
   ## initialize w and b:
   sess.run(init)
   ## evaluate z:
   for t in [1.0, 0.6, -1.8]:
     print('x=%4.1f --> z=%4.1f'%(
        t, sess.run(z, feed_dict={x:t})))

执行完前面的代码后,您应该会看到以下输出:

x= 1.0 --> z= 2.7
x= 0.6 --> z= 1.9
x=-1.8 --> z=-2.9

这相当直接,对吧?通常,当我们使用 TensorFlow 低级 API 开发模型时,我们需要为输入数据(xy 和有时其他可调参数)定义占位符;然后,定义权重矩阵并从输入到输出构建模型。如果这是一个优化问题,我们应该定义损失或成本函数并确定使用哪个优化算法。TensorFlow 将创建一个包含我们在该图中定义的符号的图。

在这里,我们为 x 创建了一个占位符,其 shape=(None)。这允许我们一次以元素形式以及一次以输入数据批次的形式输入值,如下所示:

>>> with tf.Session(graph=g) as sess:
...   sess.run(init)
...   print(sess.run(z, feed_dict={x:[1., 2., 3.]}))

[ 2.70000005  4.69999981  6.69999981]

注意

请注意,为了提高长代码示例的可读性并避免不必要的文本换行,我们在本章的几个地方省略了 Python 的命令行提示符;这是因为 TensorFlow 的函数和方法名称可能非常冗长。

另外,请注意,官方 TensorFlow 风格指南建议使用两个字符的缩进。但是,我们选择了四个字符的缩进,因为它与官方 Python 风格指南更一致,并且有助于在许多文本编辑器中正确显示代码语法高亮以及此处附带的 Jupyter 代码笔记本。

处理数组结构

让我们讨论如何在 TensorFlow 中使用数组结构。通过执行以下代码,我们将创建一个大小为  的简单的秩-3 张量,对其进行重塑,并使用 TensorFlow 的优化表达式计算列和。由于我们事先不知道批次大小,因此我们在占位符 xshape 参数中为批次大小指定了 None

import tensorflow as tf
import numpy as np

g = tf.Graph()
with g.as_default():
   x = tf.placeholder(dtype=tf.float32,
            shape=(None, 2, 3),
            name='input_x')

   x2 = tf.reshape(x, shape=(-1, 6),
           name='x2')

   ## calculate the sum of each column
   xsum = tf.reduce_sum(x2, axis=0, name='col_sum')

   ## calculate the mean of each column
   xmean = tf.reduce_mean(x2, axis=0, name='col_mean')

with tf.Session(graph=g) as sess:
   x_array = np.arange(18).reshape(3, 2, 3)

   print('input shape: ', x_array.shape)
   print('Reshaped:\n',
      sess.run(x2, feed_dict={x:x_array}))
   print('Column Sums:\n',
      sess.run(xsum, feed_dict={x:x_array}))
   print('Column Means:\n',
      sess.run(xmean, feed_dict={x:x_array}))

执行上述代码后显示的输出如下:

input shape:  (3, 2, 3)
Reshaped:
 [[  0.  1.  2.  3.  4.  5.]
  [  6.  7.  8.  9.  10.  11.]
  [ 12.  13.  14.  15.  16.  17.]]

Column Sums:
  [ 18.  21.  24.  27.  30.  33.]

Column Means:
  [  6.  7.  8.  9.  10.  11.]

在此示例中,我们使用了三个函数:tf.reshapetf.reduce_sumtf.reduce_mean。请注意,在重塑时,我们对第一个维度使用了值 -1。这是因为我们不知道批次大小;在重塑张量时,如果为特定维度使用 -1,该维度的尺寸将根据张量的总尺寸和剩余维度进行计算。因此,tf.reshape(tensor, shape=(-1,)) 可用于展平张量。

欢迎随意通过 此处 的官方文档探索其他 TensorFlow 函数。

使用低级 TensorFlow API 开发简单模型

现在我们已经熟悉了 TensorFlow,让我们来看一个非常实用的例子,并实现最小二乘法OLS)回归。(您可以在 Python Machine Learning 第 10 章“使用回归分析预测连续目标变量”中了解更多关于回归分析的信息。)

让我们从创建一个具有 10 个训练样本的小型一维玩具数据集开始:

>>> import tensorflow as tf
>>> import numpy as np
>>>
>>> X_train = np.arange(10).reshape((10, 1))
>>> y_train = np.array([1.0, 1.3, 3.1,
...           2.0, 5.0, 6.3,
...           6.6, 7.4, 8.0,
...           9.0])

给定此数据集,我们想训练一个线性回归模型来根据输入 x 预测输出 y。让我们在一个名为 TfLinreg 的类中实现这个模型。为此,我们需要两个占位符——一个用于输入 x,一个用于 y,以便将数据输入我们的模型。接下来,我们需要定义可训练变量——权重 w 和偏置 b。

然后,我们可以将线性回归模型定义为  ,然后将成本函数定义为均方误差MSE)。为了学习模型的权重参数,我们使用梯度下降优化器。代码如下:

class TfLinreg(object):
   def __init__(self, x_dim, learning_rate=0.01,
         random_seed=None):
     self.x_dim = x_dim
     self.learning_rate = learning_rate
     self.g = tf.Graph()
     ## build the model
     with self.g.as_default():
       ## set graph-level random-seed
       tf.set_random_seed(random_seed)
       
       self.build()
       ## create initializer
       self.init_op = tf.global_variables_initializer()
     
   def build(self):
     ## define placeholders for inputs
     self.X = tf.placeholder(dtype=tf.float32,
                 shape=(None, self.x_dim),
                 name='x_input')
     self.y = tf.placeholder(dtype=tf.float32,
                 shape=(None),
                 name='y_input')
     print(self.X)
     print(self.y)
     ## define weight matrix and bias vector
     w = tf.Variable(tf.zeros(shape=(1)),
             name='weight')
     b = tf.Variable(tf.zeros(shape=(1)),
             name="bias")
     print(w)
     print(b)

     self.z_net = tf.squeeze(w*self.X + b,
                 name='z_net')
     print(self.z_net)
     
     sqr_errors = tf.square(self.y - self.z_net,
                name='sqr_errors')
     print(sqr_errors)
     self.mean_cost = tf.reduce_mean(sqr_errors,
                     name='mean_cost')
     
     optimizer = tf.train.GradientDescentOptimizer(
           learning_rate=self.learning_rate,
           name='GradientDescent')
     self.optimizer = optimizer.minimize(self.mean_cost)

到目前为止,我们已经定义了一个类来构建我们的模型。我们将创建该类的实例并将其命名为 lrmodel,如下所示:

>>> lrmodel = TfLinreg(x_dim=X_train.shape[1], learning_rate=0.01)

我们在 build 方法中编写的 print 语句将显示有关图中六个节点的信息——Xywbz_netsqr_errors——以及它们的名称和形状。

这些 print 语句是可选的练习;但是,检查变量的形状对于调试复杂模型非常有帮助。在构建模型时,将打印以下行:

Tensor("x_input:0", shape=(?, 1), dtype=float32)
Tensor("y_input:0", dtype=float32)
<tf.Variable 'weight:0' shape=(1,) dtype=float32_ref>
<tf.Variable 'bias:0' shape=(1,) dtype=float32_ref>
Tensor("z_net:0", dtype=float32)
Tensor("sqr_errors:0", dtype=float32)

下一步是实现一个训练函数来学习线性回归模型的权重。请注意,b 是偏置单元(x = 0 时的 y 轴截距)。

对于训练,我们实现了一个单独的函数,该函数需要 TensorFlow 会话、模型实例、训练数据和训练轮数作为输入参数。在此函数中,我们首先使用模型中定义的 init_op 操作初始化 TensorFlow 会话中的变量。然后,我们迭代并调用模型的 optimizer 操作,同时输入训练数据。此函数将作为副产品返回训练成本列表。

def train_linreg(sess, model, X_train, y_train, num_epochs=10):
   ## initialiaze all variables: W and b
   sess.run(model.init_op)
   
   training_costs = []
   for i in range(num_epochs):
     _, cost = sess.run([model.optimizer, model.mean_cost],
              feed_dict={model.X:X_train,
                    model.y:y_train})
     training_costs.append(cost)
     
   return training_costs

因此,现在我们可以创建一个新的 TensorFlow 会话来启动 lrmodel.g 图,并将所有必需的参数传递给 train_linreg 函数进行训练:

>>> sess = tf.Session(graph=lrmodel.g)
>>> training_costs = train_linreg(sess, lrmodel, X_train, y_train)

让我们可视化这 10 个训练轮之后的训练成本,看看模型是否收敛:

>>> import matplotlib.pyplot as plt
>>> plt.plot(range(1,len(training_costs) + 1), training_costs)
>>> plt.tight_layout()
>>> plt.xlabel('Epoch')
>>> plt.ylabel('Training Cost')
>>> plt.show()

正如我们在下图中所见,这个简单的模型在几个训练轮后就很快收敛了:

到目前为止一切顺利。从成本函数来看,我们似乎为这个特定的数据集构建了一个有效的回归模型。现在,让我们编译一个新的函数,根据输入特征进行预测。对于这个函数,我们需要 TensorFlow 会话、模型和测试数据集:

def predict_linreg(sess, model, X_test):
   y_pred = sess.run(model.z_net,
            feed_dict={model.X:X_test})
   return y_pred

实现预测函数相当直接;只需运行图中定义的 z_net 即可计算预测输出值。接下来,让我们绘制训练数据上的线性回归拟合图:

>>> plt.scatter(X_train, y_train,
...       marker='s', s=50,
...       label='Training Data')
>>> plt.plot(range(X_train.shape[0]),
...      predict_linreg(sess, lrmodel, X_train),
...      color='gray', marker='o',
...      markersize=6, linewidth=3,
...      label='LinReg Model')
>>> plt.xlabel('x')
>>> plt.ylabel('y')
>>> plt.legend()
>>> plt.tight_layout()
>>> plt.show()

正如我们在结果图中看到的,我们的模型对训练数据点进行了适当的拟合:

希望您喜欢这篇来自 Python Machine Learning 2nd Edition 的摘录。如果您想了解更多关于 TensorFlow 的知识以及如何构建和应用机器学习和深度学习算法来解决各种不同问题,请访问出版商网站了解更多信息。

© . All rights reserved.