提交 20d6bd61 编写于 作者: W wizardforcel

2020-07-11 16:26:48

上级 0af696af
此差异已折叠。
## 前言一
由于计算,海量数据存储和互联网技术等关键领域的共同发展,机器学习领域呈现了巨大的发展。许多人的日常生活中的许多技术和事件,直接或间接地受到自动学习的影响。语音识别,手机上的图像分类或垃圾邮件检测等技术的例子,使得一些应用成为可能,它们只出现在十年前科幻小说中。股票市场模型或医疗模型中的学习的使用,对我们的社会产生了巨大的影响。此外,具有巡航控制,无人机和各种机器人的汽车将在不久的将来影响社会。
深度学习是机器学习的一个子类型,自 2006 年重新发现以来,无疑是爆发性扩张的领域之一。事实上,硅谷的许多创业公司都专注于此,而谷歌,Facebook,微软或 IBM 等大型科技公司都有开发和研究团队。深度学习甚至引起了大学之外和研究领域的兴趣:许多专业杂志(如 Wired)甚至是通用杂志(如纽约时报,Bloomberg 或 BBC)为这个主题撰写了很多文章。
这种兴趣促使许多学生,企业家和投资者加入深度学习。由于产生的所有兴趣,几个软件包已被制作成“开源”的。作为库的主要推动者之一,我们在 2012 年作为博士生在伯克利(Caffe)开发了它。我可以说,TensorFlow 将成为研究人员和中小企业公司用于实现他们的深度学习和机器学习的想法的主要工具之一,它出现在本书中并由 Google(加州)设计,我自 2013 年以来一直在那里研究它。对此的保证是参与该项目的工程师和顶尖研究人员的数量,它最终得到了开源。
我希望这本入门书能够帮助有兴趣在这个非常有趣的领域开始冒险的读者。我要感谢作者,我很高兴了解到它传播这项技术的努力。在开源项目发布两个月后,他在创纪录的时间内写了这本书(首先是西班牙语版本)。这是巴塞罗那活力的另一个例子,它有兴趣成为这一技术场景中的参与者之一,无疑将影响我们的未来。
Oriol Vinyals,Google Brain 的研究科学家
## 前言二
> 教育是你用来改变世界的最有力的武器。
>
> Nelson Mandela
本书的目的是有助于将这些知识转播给工程师,它们希望在激动人心的机器学习世界中扩展智慧。我相信任何具有工程背景的人都可能会发现,深度学习和机器学习的应用对他们的工作很有价值。
鉴于我的背景,读者可能会想知道为什么我提出了编写这种新的深度学习技术的挑战。我的研究重点是逐步从超级计算架构和运行时转向大数据工作负载的执行中间件,最近转向大规模数据的机器学习平台。
正是作为一名工程师,而不是数据科学家,我认为我可以为这一主题贡献这种介绍性的方法,并且它对早期阶段的许多工程师都有帮助;然后他们会选择深入了解他们的需求。
我希望这本书能为这个我非常喜爱的教育世界增添一些价值。我认为知识就是解放,应该让所有人都能获得。因此,本书的内容将在网站 [www.JordiTorres.eu/TensorFlow](http://www.jorditorres.eu/TensorFlow) 上完全免费提供。如果读者发现内容有用并认为适当补偿作者的写作,网站上有一个标签可以用于捐赠。另一方面,如果读者更喜欢选择纸质副本,你可以通过 Amazon.com 购买该书。
本书还提供西班牙语版本。事实上,这本书是西班牙语的翻译,该书于去年 1 月完成,并在 GEMLeB Meetup(Grup d'Estudi de Machine Learning de Barcelona)中展示,我是其中一个共同组织者。
感谢你阅读本书!它使我感到安慰,并证明了我写作的努力。那些了解我的人,知道技术传播是我的激情之一。它激励我继续学习。
Jordi Torres,2016 年 2 月
## 一种实用的方法
> 告诉我,我会忘记。教我,我会记得。让我参与,我会学习。
>
> 本杰明·富兰克林
深度学习的一个常见应用包括模式识别。因此,当你开始编程时,有个传统是打印“Hello World”,与它相同,在深度学习中,通常构造用于识别手写数字的模型 [1]。我将提供的第一个神经网络示例,也将允许我介绍这种名为 TensorFlow 的新技术。
但是,我不打算写一本关于机器学习或深度学习的研究书籍,我只想尽快为每个人提供这个新的机器学习软件包 TensorFlow。因此,我向我的数据科学家们道歉,为了与普通读者分享这些知识,我允许自己进行某些简化。
读者会在这里找到我在课堂上使用的常规结构;这会邀请你在学习的同时使用计算机的键盘。我们称之为“从实践中学习”,而我作为 UPC 教授的经历告诉我,这种方法对于尝试开始新主题的工程师来说非常有效。
出于这个原因,这本书具有实用性,因此我尽可能地减少了理论部分。然而,当学习过程需要时,文本中已包含某些数学细节。
我假设读者对机器学习有一些基本的理解,所以我将使用一些流行的算法逐步组织读者在 TensorFlow 中的训练。
在第一章中,除了介绍TensorFlow将扮演重要角色的场景之外,我还借此机会解释TensorFlow程序的基本结构,并简要解释它在内部维护的数据。
在第二章中,通过线性回归的一个例子,我将介绍一些代码基础知识,同时,如何调用学习过程中的各种重要组件,如损失函数或梯度下降优化算法。
在第三章中,我展示了一个聚类算法,我将详细介绍 TensorFlow 的基本数据结构,称为`tensor`(张量),以及 TensorFlow 包提供的用于创建和管理张量的不同类和函数。
第四章详细介绍了如何构建识别手写数字的单层神经网络。这将允许我们归纳上面提出的所有概念,以及查看创建和测试模型的整个过程。
下一章首先介绍基于前一章中所见的神经网络概念,并介绍如何构建多层神经网络来获得更好的手写数字识别结果。它将更详细地介绍所谓的卷积神经网络。
在第六章中,我们将讨论一个更具体的问题,利用 GPU 提供的计算能力,可能不是所有读者都感兴趣。如第 1 章所述,GPU 在神经网络的训练过程中发挥着重要作用。
本书以后记结束,其中我强调了一些结论。我想强调的是,本书中的代码示例可以从本书 [2] 的 github 仓库下载。
## 1. TensorFlow 基础知识
在本章中,我将简要介绍 TensorFlow 的代码及其编程模型。在本章的最后,读者可以在他们的个人计算机上安装 TensorFlow 软件包。
### 开源软件包
学术界已经对机器学习进行了数十年的调查,但直到近几年,它的渗透率在企业中也有所增加。这要归功于它已经拥有的大量数据以及现在可用的前所未有的计算能力。
在这种情况下,毫无疑问,在 Alphabet 的支持下,谷歌是机器学习技术在其所有虚拟计划和产品中发挥关键作用的最大公司之一。
去年10月,当 Alphabet 宣布那个季度谷歌的业绩,销售额和利润大幅增加时,首席执行官桑达皮采清楚地说:“机器学习是一种核心的,变革性的方式,我们正在重新思考我们正在做的一切”。
从技术上讲,我们正面临着谷歌不是唯一一个重要角色的时代变迁。其他技术公司,如微软,Facebook,亚马逊和苹果等众多公司也在增加对这些领域的投资。
在此背景下,几个月前谷歌在开源许可证(Apache 2.0)下发布了 TensorFlow 引擎。想要将机器学习纳入其项目和产品的开发人员和研究人员可以使用 TensorFlow,就像 Google 在内部使用 Gmail,Google 照片,搜索,语音识别等不同的商业产品一样。
TensorFlow 最初是由 Google Brain Team 开发的,目的是进行机器学习和深度神经网络研究,但该系统足以应用于各种其他机器学习问题。
由于我是一名工程师,而且我正在与工程师交谈,因此本书将深入了解数据流图如何表示算法。TensorFlow 可以看作是使用数据流图进行数值计算的库。图中的节点表示数学运算,而图的边表示多维数据数组(张量),它们将节点互连。
TensorFlow 围绕构建和操作计算图的基本思想构建,象征性地表示要执行的数值运算。这使得 TensorFlow 现在可以从 Linux 64 位平台(如 Mac OS X)以及 Android 或 iOS 等移动平台中利用 CPU 和 GPU。
这个新软件包的另一个优点是它的可视 TensorBoard 模块,它提供了大量有关如何监视和显示算法运行的信息。在创建更好的模型的过程中,能够测量和显示算法的行为是非常重要的。我感觉目前许多模型都是通过一个小型的盲目过程,通过试错来调优,明显浪费资源,以及最重要时间。
### TensorFlow 服务
最近 Google 推出了 TensorFlow 服务 [3],这有助于开发人员将他们的 TensorFlow 机器学习模型(即使如此,也可以扩展来服务其他类型的模型)投入生产。TensorFlow 服务是一个开源服务系统(用 C++ 编写),现在可以在 Apache 2.0 许可下[在 GitHub ](http://tensorflow.github.io/serving/)上获得。
TensorFlow 和 TensorFlow 服务有什么区别? 在 TensorFlow 中,开发人员更容易构建机器学习算法,并针对某些类型的数据输入进行训练,TensorFlow 服务专门使这些模型可用于生产环境。我们的想法是开发人员使用 TensorFlow 训练他们的模型,然后他们使用 TensorFlow 服务的 API 来响应来自客户端的输入。
这允许开发人员根据实际数据大规模试验不同的模型,并随时间变化,保持稳定的架构和 API。
典型的流水线是将训练数据提供给学习器,学习器输出模型,模型在被验证之后准备好部署到 TensorFlow 服务系统。 随着时间的推移和新数据的出现,改进模型,启动和迭代我们的模型是很常见的。事实上,在 Google 的博文中 [4] 中,他们提到在谷歌,许多流水线都在持续运行,随着新数据的出现,产生了新的模型版本。
![TensorFlowServing](https://jorditorres.org/wp-content/uploads/2016/04/TensorFlowServing.png)
开发人员用来与 TensorFlow 服务进行通信的前端实现,基于 [gRPC](http://www.grpc.io/) ,这是一种来自 Google 的高性能开源RPC框架。
如果你有兴趣了解 TensorFlow 服务的更多信息,我建议你先阅读服务架构概述 [5] 部分,设置你的环境并开始阅读基础教程 [6]。
### TensorFlow 的安装
是时候做一些事情了。从现在开始,我建议你交替阅读和在计算机上练习。
TensorFlow 有 Python API(以及 C/C++),需要安装 Python 2.7(我假设任何阅读本书的工程师都知道如何操作)。
通常,在使用 Python 时,应使用虚拟环境`virtualenv`。 `virtualenv`是一种工具,用于在同一台计算机的不同部分中保持不同项目所需的 Python 依赖关系。如果我们使用`virtualenv`来安装 TensorFlow,这将不会覆盖需要 TensorFlow 的其他项目的现有 Python 包版本。
首先,如果尚未安装`pip``virtualenv`,则应安装,如下面的脚本所示:
```
# Ubuntu/Linux 64-bit
$ sudo apt-get install python-pip python-dev python-virtualenv
# Mac OS X
$ sudo easy_install pip
$ sudo pip install --upgrade virtualenv
```
`~/tensorflow`目录中的环境`virtualenv`
`$ virtualenv --system-site-packages ~/tensorflow`
下一步是激活`virtualenv`。这可以按如下方式完成:
```
$ source ~/tensorflow/bin/activate # with bash
$ source ~/tensorflow/bin/activate.csh # with csh
(tensorflow)$
```
我们工作的虚拟环境的名称,将从现在开始显示在每个命令行的开头。激活`virtualenv`后,你可以使用`pip`在其中安装 TensorFlow:
```
# Ubuntu/Linux 64-bit, CPU only:
(tensorflow)$ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.7.1-cp27-none-linux_x86_64.whl
# Mac OS X, CPU only:
(tensorflow)$ sudo easy_install --upgrade six
(tensorflow)$ sudo pip install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.7.1-cp27-none-any.whl
```
我建议你访问此处提供的官方文档,来确保你安装的是最新版本。
如果运行代码的平台具有 GPU,要使用的包不同。我建议你访问官方文档,了解你的 GPU 是否符合支持 Tensorflow 所需的规范。运行 Tensorflow GPU 需要安装其他软件,所有信息都可以在下载和设置 TensorFlow [7] 网页上找到。对于使用 GPU 的更多信息,我建议阅读第 6 章。
最后,当你完成后,你应该按如下方式禁用虚拟环境:
```
(tensorflow)$ deactivate
```
鉴于本书的介绍性质,我们建议读者访问上述官方文档页面,来查找安装 Tensorflow 的其他方法的更多信息。
### 我在 TensorFlow 中的第一个代码
正如我在开始时提到的那样,我们将通过很少的理论和大量练习来探索 TensorFlow 星球。开始吧!
从现在开始,最好使用任何文本编辑器编写 python 代码并使用扩展名`.py`保存(例如`test.py`)。要运行代码,使用命令`python test.py`就足够了。
为了获得 TensorFlow 程序的第一印象,我建议编写一个简单的乘法程序;代码看起来像这样:
```py
import tensorflow as tf
a = tf.placeholder("float")
b = tf.placeholder("float")
y = tf.mul(a, b)
sess = tf.Session()
print sess.run(y, feed_dict={a: 3, b: 3})
```
在此代码中,在导入 Python 模块`tensorflow`之后,我们定义“符号”变量,称为占位符,以便在程序执行期间操作它们。然后,我们将这些变量作为参数,调用 TensorFlow 提供的乘法函数。`tf.mul`是 TensorFlow 为操纵张量而提供的众多数学运算之一。在这个时候,张量可以认为是动态大小的多维数据数组。
主要运算如下表所示:
| 运算 | 描述 |
| --- | --- |
| `tf.add` | 加法 |
| `tf.sub` | 减法 |
| `tf.mul` | 乘法 |
| `tf.div` | 除法 |
| `tf.mod` | 模 |
| `tf.abs` | 返回绝对值 |
| `tf.neg` | 返回负值 |
| `tf.sign` | 返回标志 |
| `tf.inv` | 返回倒数 |
| `tf.square` | 计算平方 |
| `tf.round` | 返回最接近的整数 |
| `tf.sqrt` | 计算平方根 |
| `tf.pow` | 计算指数 |
| `tf.exp` | 计算自然指数 |
| `tf.log` | 计算自然对数 |
| `tf.maximum` | 返回最大值 |
| `tf.minimum` | 返回最小值 |
| `tf.cos` | 计算余弦 |
| `tf.sin` | 计算正弦 |
TensorFlow 还为程序员提供了许多函数,来对矩阵执行数学运算。一些列在下面:
| 运算 | 描述 |
| --- | --- |
| `tf.diag` | 返回具有给定对角线值的对角张量 |
| `tf.transpose` | 返回参数的转置 |
| `tf.matmul` | 返回由参数列出的两个张量的张量积 |
| `tf.matrix_determinant` | 返回由参数指定的方阵的行列式 |
| `tf.matrix_inverse` | 返回由参数指定的方阵的逆 |
下一步,最重要的一步是创建一个会话来求解指定的符号表达式。实际上,到目前为止,这个 TensorFlow 代码尚未执行任何操作。我要强调的是,TensorFlow 既是表达机器学习算法的接口,又是运行它们的实现,这是一个很好的例子。
程序通过使用`Session()`创建会话来与 Tensorflow 库交互;只有在我们调用`run()`方法时才会创建这个会话,这就是它真正开始运行指定代码的时候。在此特定示例中,使用`feed_dict`参数将变量的值传给`run()`方法。这里,相关代码求解表达式,并且从显示器返回 9 作为结果。
通过这个简单的例子,我试图介绍在 TensorFlow 中编程的常规方法,首先指定整个问题,并最终创建一个可以运行相关计算的会话。
然而,有时我们感兴趣的是构造代码的更多的灵活性,插入操作来构建某个图,这些操作运行它的一部分。例如,当我们使用 Python 的交互式环境时,例如 IPython [8],就会发生这种情况。为此,TesorFlow 提供了`tf.InteractiveSession()`类。
这种编程模型的动机超出了本书的范围。但是,为了继续下一章,我们只需要知道所有信息都在内部保存在图结构中,它包含所有操作和数据的信息。
该图描述了数学运算。节点通常实现数学运算,但它们也可以表示数据输入,输出结果或读/写持久变量。边描述节点与其输入和输出之间的关系,同时携带张量,即 TensorFlow 的基本数据结构。
将信息表示为图允许 TensorFlow 知道事务之间的依赖关系,并异步并行地将操作分配给设备,当这些操作已经具有可用的相关张量(在边缘输入中指示)时。
因此,并行性是使我们能够加速一些计算昂贵的算法的执行的因素之一,但也因为 TensorFlow 已经有效地实现了一组复杂的操作。此外,大多数这些操作都具有关联的内核,这些内核是为特定设备(如 GPU)设计的操作的实现。下表总结了最重要的操作/内核 [9]:
| 操作组 | 操作 |
| --- | --- |
| 数学 | 加,减,乘,除,指数,对数,大于,小于,等于 |
| 排列 | 连接,切片,分割,常数,阶,形状,打乱 |
| 矩阵 | MatMul,MatrixInverse,MatrixDeterminant |
| 神经网络 | SoftMax,Sigmoid,ReLU,Convolution2D,MaxPool |
| 检查点 | 保存,还原 |
| 队列和同步 | Enqueue,Dequeue,MutexAcquire,MutexRelease |
| 流量控制 | 合并,切换,进入,离开,NextIteration |
### 显示面板 Tensorboard
为了使其更加全面,TensorFlow 包含了名为 TensorBoard 的可视化工具来调试和优化程序的功能。TensorBoard 可以以图形方式查看计算图任何部分的参数和细节的不同类型的统计信息。
TensorBoard 模块显示的数据在 TensorFlow 执行期间生成,并存储在跟踪文件中,其数据来自摘要操作。在 TensorFlow 的文档页面 [10] 中,你可以找到 Python API 的详细说明。
我们调用它的方式非常简单:从命令行中使用 Tensorflow 命令启动服务,它包含要跟踪的文件作为参数。
```
(tensorflow)$ tensorboard --logdir=
```
你只需要使用`http//localhost:6006 /`从浏览器中 [11] 访问本地套接字 6006。
名为 TensorBoard 的可视化工具超出了本书的范围。对于 Tensorboard 如何工作的更多详细信息,读者可以访问 TensorFlow 教程页面中的 TensorBoard 图形可视化 [12] 部分。
## 2. TensorFlow 中的线性回归
在本章中,我将开始使用简单模型:线性回归来探索 TensorFlow 编程。基于这个例子,我将介绍一些代码基础知识,以及,如何调用学习过程中的各种重要组件,如函数函数或算法梯度下降。
### 变量之间的关系模型
线性回归是一种用于衡量变量之间关系的统计技术。它的有趣之处在于实现它的算法在概念上不复杂,并且还可以适应各种各样的情况。由于这些原因,我发现用线性回归的例子开始深入研究 TensorFlow 很有意思。
请记住,在两个变量(简单回归)和两个以上变量(多元回归)的情况下,线性回归拟合因变量和自变量之间的关系`xi`和随机项`b`
在本节中,我将创建一个简单的示例来解释 TensorFlow 如何工作,假设我们的数据模型对应简单的线性回归`y = W * x + b`。为此,我使用一个简单的 Python 程序在二维空间中创建数据,然后我会要求 TensorFlow 在这些点上寻找最适合的直线。
首先要做的是导入我们将用于生成点的 NumPy 包。我们创建的代码如下:
```py
import numpy as np
num_points = 1000
vectors_set = []
for i in xrange(num_points):
x1= np.random.normal(0.0, 0.55)
y1= x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
vectors_set.append([x1, y1])
x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]
```
从代码中可以看出,我们根据关系`y = 0.1 * x + 0.3`生成了点,尽管有一些正态分布的变化,因此这些点并不完全对应一条线,让我们编写一个更有趣的例子。
在我们的例子中,所得到的点云是:
![](https://jorditorres.org/wp-content/uploads/2016/02/image014.png)
读者可以使用以下代码查看它们(这里,我们需要导入`matplotlib`包的一些函数,运行`pip install matplotlib` [13]):
```py
import matplotlib.pyplot as plt
plt.plot(x_data, y_data, 'ro', label='Original data')
plt.legend()
plt.show()
```
这些点是我们将考虑的模型的训练数据集的数据。
### 损失函数和梯度下降算法
下一步是训练我们的学习算法,以便能够获得从输入数据`x_data`估计的输出值`y`。在这种情况下,正如我们事先所知,它是线性回归,我们只能用两个参数表示我们的模型:`W``b`
目标是生成 TensorFlow 代码,它能够找到最佳的参数`W``b`,它来自输入数据`x_data`,将其拟合到输出数据`y_data`,我们这里它是一条直线,由`y_data = W * x_data + b`定义。读者知道`W`应接近 0.1 且`b`为 0.3,但 TensorFlow 不知道它,必须自己实现。
解决此类问题的一种标准方法是,遍历数据集的每个值并修改参数`W``b`,以便每次都能获得更精确的答案。为了确定我们是否在这些迭代中有所改进,我们将定义一个损失函数(也称为“误差函数”)来衡量某条线有多“好”(实际上是有多“坏”)。
该函数接收参数`W``b`,并根据线与数据的拟合程度返回一个误差值。在我们的例子中,我们可以使用均方误差 [14] 作为损失函数。利用均方误差,我们得到“误差”的平均值,基于实际值与算法每次迭代估计值之间距离。
稍后,我将详细介绍损失函数及其替代方法,但对于这个介绍性示例,均方误差有助于我们一步一步向前推进。
现在是时候用 TensorFlow 编写我 解释过的所有内容了。为此,首先我们将使用以下语句创建三个变量:
```py
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b
```
现在,我们可以继续前进,只知道方法`Variable`的调用定义了一个变量,驻留在 TensorFlow 的内部图数据结构中,我在上面已经说过了。 稍后我们将回到方法参数的更多信息,但是现在我认为最好继续前进来推进第一种方法。
现在,通过定义这些变量,我们可以基于每个点与函数`y = W * x + b`计算的点之间的距离,来表示我们之前讨论的损失函数。之后,我们可以计算其平方和的平均值。 在 TensorFlow 中,此损失函数表示如下:
```py
loss = tf.reduce_mean(tf.square(y - y_data))
```
如我们所见,此表达式计算我们知道的`y_data`点与从输入`x_data`计算的点`y`之间的平方距离的平均值。
此时,读者可能已经怀疑最适合我们数据的直线是误差值较小的直线。 因此,如果我们使误差函数最小,我们将找到我们数据的最佳模型。
目前没有太多细节,这就是使函数最小的优化算法,称为梯度下降 [15]。 理论上,梯度下降是一种算法,它接受由一组参数定义的函数,它以一组初始参数值开始,并迭代地移向一组使函数最小的值。 在函数梯度 [16] 的负方向上移动来实现迭代式最小化。 通常计算距离平方来确保它是正的并且使误差函数可微分以便计算梯度。
算法从一组参数的初始值开始(在我们的例子中为`W``b`),然后算法以某种方式迭代地调整这些变量的值,在过程结束时,变量的值使成本函数最小。
要在 TensorFlow 中使用此算法,我们只需执行以下两个语句:
```py
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
```
现在,这足以让 TensorFlow 在其内部数据结构中创建相关数据,并且在这个结构中也实现了一个可以由`train`调用的优化器,它是针对定义的成本函数的梯度下降算法。稍后,我们将讨论名为学习率的函数参数(在我们的示例中,值为 0.5)。
### 运行算法
正如我们之前所见,在代码的这个位置上,特定于 TensorFlow 库的调用,只向其内部图添加了信息,而 TensorFlow 的运行时尚未运行任何算法。因此,与前一章的示例一样,我们必须创建会话,调用`run`方法并传递`train`作为参数。另外,因为在代码中我们已经指定了变量,所以我们必须先使用以下调用对它们进行初始化:
```py
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
```
现在我们可以开始迭代过程,这将允许我们找到`W``b`的值,它定义最适合输入点的模型直线。 训练过程一直持续到模型在训练数据上达到所需的准确度。 在我们的特定示例中,如果我们假设只有 8 次迭代就足够了,代码可能是:
```py
for step in xrange(8):
sess.run(train)
print step, sess.run(W), sess.run(b)
```
运行此代码的结果表明,`W``b`的值接近我们事先知道的值。 在我的例子中,`print`的结果是:
```py
(array([ 0.09150752], dtype=float32), array([ 0.30007562], dtype=float32))
```
并且,如果我们使用以下代码以图形方式显示结果:
```py
plt.plot(x_data, y_data, 'ro')
plt.plot(x_data, sess.run(W) * x_data + sess.run(b))
plt.legend()
plt.show()
```
我们可以用图形方式,看到参数`W = 0.0854``b = 0.299`定义的直线,只需 8 次迭代:
![](https://jorditorres.org/wp-content/uploads/2016/02/image016.png)
请注意,我们只执行了八次迭代来简化说明,但如果我们运行更多,参数值会更接近预期值。 我们可以使用以下语句来打印`W``b`的值:
```py
print(step, sess.run(W), sess.run(b))
```
在我们的例子中,`print`输出是:
```py
(0, array([-0.04841119], dtype=float32), array([ 0.29720169], dtype=float32))
(1, array([-0.00449257], dtype=float32), array([ 0.29804006], dtype=float32))
(2, array([ 0.02618564], dtype=float32), array([ 0.29869056], dtype=float32))
(3, array([ 0.04761609], dtype=float32), array([ 0.29914495], dtype=float32))
(4, array([ 0.06258646], dtype=float32), array([ 0.29946238], dtype=float32))
(5, array([ 0.07304412], dtype=float32), array([ 0.29968411], dtype=float32))
(6, array([ 0.08034936], dtype=float32), array([ 0.29983902], dtype=float32))
(7, array([ 0.08545248], dtype=float32), array([ 0.29994723], dtype=float32))
```
你可以观察到算法以`W = -0.0484``b = 0.2972`(在我们的例子中)的初始值开始,然后算法以一种方式迭代调整变量的值使损失函数最小。
你还可以检查损失函数是否随之减少
```py
print(step, sess.run(loss))
```
在这种情况下,`print`输出是:
```py
(0, 0.015878126)
(1, 0.0079048825)
(2, 0.0041520335)
(3, 0.0023856456)
(4, 0.0015542418)
(5, 0.001162916)
(6, 0.00097872759)
(7, 0.00089203351)
```
我建议读者在每次迭代时绘图,让我们可以直观地观察算法如何调整参数值。 在我们的例子中,8 个截图是:
![](https://jorditorres.org/wp-content/uploads/2016/02/image018.png)
正如读者可以看到的,在算法的每次迭代中,直线更适合数据。 梯度下降算法如何更接近最小化损失函数的参数值?
由于我们的误差函数由两个参数(`W``b`)组成,我们可以将它可视化为二维表面。 该二维空间中的每个点代表一条直线。 每个点的函数高度是该直线的误差值。 在该表面上,一些直线产生的误差值小于其他直线。 当 TensorFlow 运行梯度下降搜索时,它将从该表面上的某个位置开始(在我们的示例中,点`W = -0.04841119``b = 0.29720169`)并向下移动来查找具有最小误差的直线。
要在此误差函数上运行梯度下降,TensorFlow 会计算其梯度。 梯度将像指南针一样,总是引导我们向下走。 为了计算它,TensorFlow 将对误差函数微分,在我们的情况下意味着它需要计算`W``b`的偏导数,它表明每次迭代中要移动的方向。
之前提到的学习率参数控制每次迭代期间 TensorFlow 的每一步的下降程度。 如果我们引入的参数太大,我们可能会越过最小值。 但是,如果我们让 TensorFlow 采取较小步骤,则需要多次迭代才能达到最小值。 因此,使用良好的学习率至关重要。 有不同的技术来调整学习率参数的值,但它超出了本入门书的范围。 确保梯度下降算法正常工作的一种好方法,是确保每次迭代中的误差减小。
请记住,为了便于读者测试本章所述的代码,你可以从本书的 Github [17] 下载`regression.py`。 在这里,你将发现所有东西都在一起以便跟踪:
```py
import numpy as np
num_points = 1000
vectors_set = []
for i in xrange(num_points):
x1= np.random.normal(0.0, 0.55)
y1= x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
vectors_set.append([x1, y1])
x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]
import matplotlib.pyplot as plt
#Graphic display
plt.plot(x_data, y_data, 'ro')
plt.legend()
plt.show()
import tensorflow as tf
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for step in xrange(8):
sess.run(train)
print(step, sess.run(W), sess.run(b))
print(step, sess.run(loss))
#Graphic display
plt.plot(x_data, y_data, 'ro')
plt.plot(x_data, sess.run(W) * x_data + sess.run(b))
plt.xlabel('x')
plt.xlim(-2,2)
plt.ylim(0.1,0.6)
plt.ylabel('y')
plt.legend()
plt.show()
```
在本章中,我们已经开始探索 TensorFlow 软件包的可能性,首先采用直观方法处理两个基本组件:损失函数和梯度下降算法,使用基本线性回归算法来介绍。 在下一章中,我们将详细介绍 TensorFlow 包使用的数据结构。
此差异已折叠。
此差异已折叠。
## 5. TensorFlow 中的多层神经网络
在本章中,我将与读者一起编写一个简单的深度学习神经网络,该网络使用与前一章相同的 MNIST 数字识别问题。
随着我的前进,深度学习神经网络由叠在一起的多个层组成。 具体来说,在本章中我们将构建一个卷积网络,这是深度学习的典型例子。 卷扬神经网络由 Yann LeCunn 等人于 1998 年推出并推广。 这些卷积网络最近引领了图像识别领域的最新技术;例如:在我们的数字识别案例中,它们的准确度高于 99%。
在本章的其余部分,我将以示例代码为主,我将解释这些网络的两个最重要的概念:卷积和池化,而不输入参数的细节,鉴于本书的介绍性质。 但是,读者将能够运行所有代码,我希望它能让你了解卷积网络背后的通用思想。
### 卷积神经网络
卷积神经网络(也称为 CNN 或 CovNets)是深度学习的一个特例,并且在计算机视觉领域产生了重大影响。
CNN 的典型特征是它们几乎总是将图像作为输入,这产生了更有效的实现并且减少所需参数的数量。 让我们看看我们的 MNIST 数字识别示例:在读取 MNIST 数据并使用 TensorFlow 定义占位符之后,就像我们在上一个示例中所做的那样:
```py
import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
import tensorflow as tf
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
```
我们可以重建输入数据图像的原始形状。 我们可以这样做:
```py
x_image = tf.reshape(x, [-1,28,28,1])
```
这里我们将输入形状更改为 4D 张量,第二维和第三维对应于图像的宽度和高度,而最后一维对应于颜色通道的数量,在这种情况下为 1。
通过这种方式,我们可以将神经网络的输入视为大小为`28×28`的二维空间,如图所示:
![](https://jorditorres.org/wp-content/uploads/2016/02/image072-300x282.png)
定义卷积神经网络有两个基本原则:滤波器和特征映射。 这些原则可以表示为特定的神经元分组,我们将很快看到。 但首先,鉴于它们在 CNN 中的重要性,我们将简要介绍这两个原则。
直觉上,我们可以说卷积层的主要目的是检测图像中的特征或视觉特征,考虑边缘,线条,颜色斑点等。 这是由我们刚刚讨论过的,连接输入层的隐藏层来处理的。 在我们感兴趣的 CNN 的案例中,输入数据没有完全连接到第一个隐藏层的神经元;这只发生在输入神经元中的一个小型局部空间中,输入神经元存储图像像素值。 这可以看作:
![](https://jorditorres.org/wp-content/uploads/2016/02/image074.png)
更确切地说,在给定的示例中,隐藏层的每个神经元与输入层的`5×5`小区域(因此是 25 个神经元)连接。
我们可以认为这是一个大小为`5×5`的窗口,它滑过包含输入图像的整个`28×28`大小的输入层。 窗口滑过整个神经元层。 对于窗口的每个位置,隐藏层中都有一个处理该信息的神经元。
我们可以通过假设窗口从图像的左上角开始来可视化;这将信息提供给隐藏层的第一个神经元。 然后窗口向右滑动一个像素;我们将这个`5×5`区域与隐藏层中的第二个神经元连接起来。 我们继续这样,直到整个空间从上到下,从左到右被窗口覆盖。
![](https://jorditorres.org/wp-content/uploads/2016/02/image076.png)
分析我们提出的具体案例,我们观察到,给定一个大小为`28×28`的输入图像和一个大小为`5×5`的窗口,在第一个隐藏层中产生了`24×24`的神经元,因为我们只能这样做,在触及输入图像的右下边缘之前,将窗口向下移动 23 次,向右移动 23 次。 这假设窗口每次只移动 1 个像素,因此新窗口与刚刚前进的旧窗口重叠。
但是,可以在卷积层中一次移动多于 1 个像素,该参数称为`stride`(步长)。 另一个扩展是用零(或其他值)填充边缘,以便窗口可以在图像的边缘上滑动,这可以产生更好的结果。 控制此功能的参数称为`padding`(填充)[39],你可以使用该参数确定填充的大小。 鉴于本书的介绍性质,我们不会进一步详细介绍这两个参数。
鉴于我们的研究案例,并遵循前一章的形式,我们将需要一个偏置值`b`和一个`5×5`的权重矩阵`W`来连接隐层和输入层的神经元。CNN的一个关键特性是,该权重矩阵`W`和偏置`b`在隐藏层中的所有神经元之间共享;我们对隐藏层中的神经元使用相同的`W``b`。 在我们的情况下,这是`24×24`(576)个神经元。 读者应该能够看到,与完全连接的神经网络相比,这大大减少了人们需要的权重参数。 具体而言,由于共享权重矩阵`W`,这从 14000(`5x5x24x24`)减少到仅 25(`5x5`)。
这个共享矩阵`W`和偏置`b`通常在 CNN 的上下文中称为核或过滤器。 这些过滤器类似于用于修饰图像的图像处理程序,在我们的例子中用于查找微分特征。 我建议查看 GIMP [40] 手册中的示例,以便了解卷积过程的工作原理。
矩阵和偏置定义了核。 核只检测图像中的某个相关特征,因此建议使用多个核,每个核对应我们想要检测的每个特征。 这意味着 CNN 中的完整卷积层由几个核组成。 表示几个核的常用方法如下:
![](https://jorditorres.org/wp-content/uploads/2016/02/image078.png)
第一个隐藏层由几个核组成。 在我们的例子中,我们使用 32 个核,每个核由`5×5`的权重矩阵`W`和偏置`b`定义,偏置`b`也在隐层的神经元之间共享。
为了简化代码,我定义了以下两个与权重矩阵`W`和偏置`b`相关的函数:
```py
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
```
在没有详细说明的情况下,习惯上用一些随机噪声初始化权重,偏置值略微为正。
除了我们刚才描述的卷积层之外,通常卷积层后面跟着一个所谓的池化层。 池化层简单地压缩来自卷积层的输出,并创建卷积层输出的信息的紧凑版本。 在我们的示例中,我们将使用卷积层的`2×2`区域,我们使用池化将它的数据汇总到单个点:
![](https://jorditorres.org/wp-content/uploads/2016/02/image080.png)
有几种方法可以执行池化来压缩信息;在我们的示例中,我们将使用名为最大池化的方法。 通过仅保留所考虑的`2×2`区域中的最大值来压缩信息。
如上所述,卷积层由许多核组成,因此,我们将分别对每个核应用最大池化。 通常,可以有多层池化和卷积:
![](https://jorditorres.org/wp-content/uploads/2016/02/image082.png)
这使`24×24`的卷积层结果,被对应`12×12`的最大池化层转换为`12×12`的空间,其中每个块来源于`2×2`的区域。 请注意,与卷积层不同,数据是平铺的,而不是由滑动窗口创建的。
直观上,我们可以解释最大池化,来确定特定特征是否存在于图像中的任何位置,特征的确切位置不如对于其他特征的相对位置重要。
### 模型的实现
在本节中,我将基于可在 TensorFlow [41] 网站上找到的高级示例(Deep MNIST for experts),提供编写 CNN 的示例代码。 正如我在开始时所说的那样,参数的许多细节需要处理和理论方法,比本书中给出的更详细。 因此,我将仅概述代码,而不涉及 TensorFlow 参数的许多细节。
正如我们已经看到的,我们必须为卷积和池化层定义几个参数。 我们将在每个维度中使用大小为 1 的步幅(这是滑动窗口的步长)和零填充模型。 我们将应用的池化是`2×2`的最大池化。 与上面类似,我建议使用以下两个通用函数来编写涉及卷积和最大池化的更清晰的代码。
```py
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
```
现在是时候实现第一个卷积层,然后是池化层。 在我们的示例中,我们有 32 个过滤器,每个过滤器的窗口大小为`5×5`。 我们必须定义一个张量,来保持这个权重矩阵`W`的形状为`[5,5,1,32]`:前两个维度是窗口的大小,第三个是通道的数量,在我们的例子中为 1 。 最后一个定义了我们想要使用的过滤器数量。 此外,我们还需要为 32 个权重矩阵中的每一个定义偏置。 使用先前定义的函数,我们可以在 TensorFlow 中编写它,如下所示:
```py
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
```
ReLU(整流线性单元)激活函数最近成为深度神经网络隐藏层中使用的默认激活函数。 这个简单的函数返回`max(0, x)`,因此它为负值返回 0,否则返回`x`。 在我们的示例中,我们将在卷积层之后的隐藏层中使用此激活函数。
我们编写的代码首先将卷积应用于输入图像`x_image`,它在 2D 张量`W_conv1`中,返回图像卷积的结果,然后加上偏置,最终应用 ReLU 激活函数。 最后一步,我们将最大池化应用于输出:
```py
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
```
在构建深度神经网络时,我们可以将多个层叠在一起。 为了演示如何执行此操作,我将创建一个带有 64 个过滤器和`5×5`窗口的辅助卷积层。 在这种情况下,我们必须传递 32 作为我们需要的通道数,因为它是前一层的输出大小:
```py
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
```
由于我们将`5×5`窗口应用于步长为 1 的`12×12`空间,因此卷积的结果输出具有`8×8`的维数。 下一步是将一个全连接的层添加到`8×8`输出,然后将其输入到最后的 softmax 层,就像我们在前一章中所做的那样。
我们将使用 1024 个神经元的一层,允许我们处理整个图像。 权重和偏置的张量如下:
```py
W_fc1 = weight_variable([8 * 8 * 64, 1024])
b_fc1 = bias_variable([1024])
```
请记住,张量的第一个维度表示来自第二个卷积层的大小为`8x8`的 64 个过滤器,而第二个参数是层中神经元的数量,我们可以自由选择(在我们的例子中是 1024)。
现在,我们想将张量展开为向量。 我们在前一章中看到,softmax 需要将向量形式的展开图像作为输入。 这通过将权重矩阵`W_fc1`与展开向量相乘,加上偏置`b_fc1`,再应用 ReLU 激活函数来实现:
```py
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
```
下一步将使用称为 dropout 的技术减少神经网络中的有效参数量。 这包括删除节点及其传入和传出连接。 丢弃和保留哪些神经元是随机决定的。 为了以一致的方式执行此操作,我们将在代码中为丢弃或保留的神经元分配概率。
在没有太多细节的情况下,dropout 降低了模型的过拟合风险。 当隐藏层具有大量神经元并因此可以产生非常富有表现力的模型时,这可能发生;在这种情况下,可能会对随机噪声(或误差)建模。 这被称为过拟合,如果与输入的维度相比,模型具有大量参数,则更有可能。 最好是避免这种情况,因为过拟合的模型具有较差的预测表现。
在我们的模型中,我们应用 dropout,它包括在最终的 softmax 层之前使用 dropout 函数 `tf.nn.dropout`。 为此,我们构造一个占位符来存储在 dropout 期间保留神经元的概率:
```py
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
```
最后,我们将 softmax 层添加到我们的模型中,就像前一章中所做的那样。 请记住,sofmax 返回输入属于每个类的概率(在我们的例子中为数字),以便总概率加起来为 1。 softmax 层代码如下:
```py
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
```
### 模型的训练和评估
我们现在通过调整卷积层和及全连接层中的所有权重,来准备训练我们刚刚定义的模型,并获得我们的带标签的图像的预测。 如果我们想知道模型的执行情况,我们必须遵循上一章中的示例。
以下代码与前一章中的代码非常相似,但有一个例外:我们用 ADAM 优化器替换梯度下降优化器,因为该算法实现了不同的优化器,根据文献 [42],它具有某些优点。
我们还需要在`feed_dict`参数中包含附加参数`keep_prob`,该参数控制我们之前讨论过的 dropout 层的概率。
```py
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess = tf.Session()
sess.run(tf.initialize_all_variables())
for i in range(20000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = sess.run( accuracy, feed_dict={x:batch[0], y_: batch[1], keep_prob: 1.0})
print("step %d, training accuracy %g"%(i, train_accuracy))
sess.run(train_step,feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print("test accuracy %g"% sess.run(accuracy, feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
```
与之前的模型一样,整个代码可以在本书的 Github 页面上找到,可以验证该模型的准确率达到 99.2%。
以下是使用 TensorFlow 构建,训练和评估深度神经网络的简要介绍。 如果读者设法运行提供的代码,他或她已经注意到该网络的训练时间明显长于前几章的训练时间;你可以想象,拥有更多层的网络需要花费更长的时间来训练。 我建议你阅读下一章,其中解释了如何使用 GPU 进行训练,这将减少你的训练时间。
本章的代码可以在本书 github 页面 [43] 的`CNN.py`中找到,用于研究目的的代码在下面:
```py
import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
import tensorflow as tf
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
x_image = tf.reshape(x, [-1,28,28,1])
print "x_image="
print x_image
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess = tf.Session()
sess.run(tf.initialize_all_variables())
for i in range(200):
batch = mnist.train.next_batch(50)
if i%10 == 0:
train_accuracy = sess.run( accuracy, feed_dict={ x:batch[0], y_: batch[1], keep_prob: 1.0})
print("step %d, training accuracy %g"%(i, train_accuracy))
sess.run(train_step,feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print("test accuracy %g"% sess.run(accuracy, feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
```
## 6. 并行
2015 年 11 月发布的第一个 TensorFlow 软件包,已准备好在具有可用 GPU 的服务器上运行,并同时在其中执行训练操作。 2016 年 2 月,更新添加了分布式和并行化处理的功能。
在这个简短的章节中,我将介绍如何使用 GPU。 对于那些想要了解这些设备如何工作的读者,有些参考文献将在上一节中给出。但是,鉴于本书的介绍性,我不会详细介绍分布式版本,但对于那些感兴趣的读者,一些参考将在上一节中给出。
### 带有 GPU 的执行环境
支持 GPU 的 TensorFlow 软件包需要 CudaToolkit 7.0 和 CUDNN 6.5 V2。 对于安装环境,我们建议访问 cuda 安装 [44] 网站,为了不会深入细节,同时信息也是最新的。
在 TensorFlow 中引用这些设备的方法如下:
+ `/cpu:0`:引用服务器的 CPU。
+ `/gpu:0`:服务器的 GPU(如果只有一个可用)。
+ `/gpu:1`:服务器的第二个 GPU,依此类推。
要知道我们的操作和张量分配在哪些设备中,我们需要创建一个`sesion`,选项`log_device_placement``True`。 我们在下面的例子中看到它:
```py
import tensorflow as tf
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
printsess.run(c)
```
当读者在计算机中测试此代码时,应出现类似的输出:
```
. . .
Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c, pci bus id: 0000:08:00.0
. . .
b: /job:localhost/replica:0/task:0/gpu:0
a: /job:localhost/replica:0/task:0/gpu:0
MatMul: /job:localhost/replica:0/task:0/gpu:0
[[ 22.28.]
[ 49.64.]]
```
此外,使用操作的结果,它通知我们每个部分的执行位置。
如果我们想要在特定设备中执行特定操作,而不是让系统自动选择设备,我们可以使用变量`tf.device`来创建设备上下文,因此所有操作都在上下文将分配相同的设备。
如果我们在系统中拥有更多 GPU,则默认情况下将选择具有较低标识符的 GPU。 如果我们想要在不同的 GPU 中执行操作,我们必须明确指定它。 例如,如果我们希望先前的代码在 GPU#2 中执行,我们可以使用`tf.device('/gpu:2')`,如下所示:
```py
import tensorflow as tf
with tf.device('/gpu:2'):
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
printsess.run(c)
```
### 多个 GPU 的并行
如果我们有更多的 GPU,通常我们希望一起使用它们来并行地解决同样的问题。 为此,我们可以构建我们的模型,来在多个 GPU 之间分配工作。 我们在下一个例子中看到它:
```py
import tensorflow as tf
c = []
for d in ['/gpu:2', '/gpu:3']:
with tf.device(d):
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
c.append(tf.matmul(a, b))
with tf.device('/cpu:0'):
sum = tf.add_n(c)
# Creates a session with log_device_placement set to True.
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
print sess.run(sum)
```
正如我们所看到的,代码与前一代码相同,但现在我们有 2 个 GPU,由`tf.device`指定,它们执行乘法(两个 GPU 在这里都做同样的操作,以便简化示例代码),稍后 CPU 执行加法。 假设我们将`log_device_placement`设置为`true`,我们可以在输出中看到,操作如何分配给我们的设备 [45]。
```py
. . .
Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: Tesla K40c
/job:localhost/replica:0/task:0/gpu:1 -> device: 1, name: Tesla K40c
/job:localhost/replica:0/task:0/gpu:2 -> device: 2, name: Tesla K40c
/job:localhost/replica:0/task:0/gpu:3 -> device: 3, name: Tesla K40c
. . .
. . .
Const_3: /job:localhost/replica:0/task:0/gpu:3
I tensorflow/core/common_runtime/simple_placer.cc:289] Const_3: /job:localhost/replica:0/task:0/gpu:3
Const_2: /job:localhost/replica:0/task:0/gpu:3
I tensorflow/core/common_runtime/simple_placer.cc:289] Const_2: /job:localhost/replica:0/task:0/gpu:3
MatMul_1: /job:localhost/replica:0/task:0/gpu:3
I tensorflow/core/common_runtime/simple_placer.cc:289] MatMul_1: /job:localhost/replica:0/task:0/gpu:3
Const_1: /job:localhost/replica:0/task:0/gpu:2
I tensorflow/core/common_runtime/simple_placer.cc:289] Const_1: /job:localhost/replica:0/task:0/gpu:2
Const: /job:localhost/replica:0/task:0/gpu:2
I tensorflow/core/common_runtime/simple_placer.cc:289] Const: /job:localhost/replica:0/task:0/gpu:2
MatMul: /job:localhost/replica:0/task:0/gpu:2
I tensorflow/core/common_runtime/simple_placer.cc:289] MatMul: /job:localhost/replica:0/task:0/gpu:2
AddN: /job:localhost/replica:0/task:0/cpu:0
I tensorflow/core/common_runtime/simple_placer.cc:289] AddN: /job:localhost/replica:0/task:0/cpu:0
[[44.56.]
[98.128.]]
. . .
```
### GPU 的代码示例
为了总结这一简短的章节,我们提供了一段代码,其灵感来自 DamienAymeric 在 Github [46] 中共享的代码,计算`An + Bn``n=10`,使用 Python `datetime`包,将 1 GPU 的执行时间与 2 个 GPU 进行比较。
首先,我们导入所需的库:
```py
import numpy as np
import tensorflow as tf
import datetime
```
我们使用`numpy`包创建两个带随机值的矩阵:
```py
A = np.random.rand(1e4, 1e4).astype('float32')
B = np.random.rand(1e4, 1e4).astype('float32')
n = 10
```
然后,我们创建两个结构来存储结果:
```py
c1 = []
c2 = []
```
接下来,我们定义`matpow()`函数,如下所示:
```py
defmatpow(M, n):
if n < 1: #Abstract cases where n < 1
return M
else:
return tf.matmul(M, matpow(M, n-1))
```
正如我们所见,要在单个 GPU 中执行代码,我们必须按如下方式指定:
```py
with tf.device('/gpu:0'):
a = tf.constant(A)
b = tf.constant(B)
c1.append(matpow(a, n))
c1.append(matpow(b, n))
with tf.device('/cpu:0'):
sum = tf.add_n(c1)
t1_1 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
sess.run(sum)
t2_1 = datetime.datetime.now()
```
对于 2 个 GPU 的情况,代码如下:
```py
with tf.device('/gpu:0'):
#compute A^n and store result in c2
a = tf.constant(A)
c2.append(matpow(a, n))
with tf.device('/gpu:1'):
#compute B^n and store result in c2
b = tf.constant(B)
c2.append(matpow(b, n))
with tf.device('/cpu:0'):
sum = tf.add_n(c2) #Addition of all elements in c2, i.e. A^n + B^n
t1_2 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
# Runs the op.
sess.run(sum)
t2_2 = datetime.datetime.now()
```
最后,我们打印计算时间的结果:
```py
print "Single GPU computation time: " + str(t2_1-t1_1)
print "Multi GPU computation time: " + str(t2_2-t1_2)
```
### TensorFlow 的分布式版本
正如我之前在本章开头所说,2016 年 2 月,Google 发布了 TensorFlow 的分布式版本,该版本由 gRPC 支持,这是一个用于进程间通信的高性能开源 RPC 框架(TensorFlow 服务使用的相同协议)。
对于它的用法,必须构建二进制文件,因为此时包只提供源代码。 鉴于本书的介绍范围,我不会在分布式版本中解释它,但如果读者想要了解它,我建议从 TensorFlow 的分布式版本的官网开始 [47]。
与前面的章节一样,本书中使用的代码可以在本书的 Github [48] 中找到。 我希望本章足以说明如何使用 GPU 加速代码。
此差异已折叠。
# 与 TensorFlow 的初次接触
![](cover.png)
> 原文:[First Contact With TensorFlow](https://jorditorres.org/research-teaching/tensorflow/first-contact-with-tensorflow-book/first-contact-with-tensorflow/)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
+ [与 TensorFlow 的初次接触](README.md)
+ [前言](0.md)
+ [1. TensorFlow 基础知识](1.md)
+ [2. TensorFlow 中的线性回归](2.md)
+ [3. TensorFlow 中的聚类](3.md)
+ [4. TensorFlow 中的单层神经网络](4.md)
+ [5. TensorFlow 中的多层神经网络](5.md)
+ [6. 并行](6.md)
+ [后记](7.md)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# TensorFlow 学习指南
> 原文:[LearningTensorFlow.com](https://learningtensorflow.com)
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
+ [在线阅读](https://www.gitbook.com/book/wizardforcel/learning-tf/details)
+ [PDF格式](https://www.gitbook.com/download/pdf/book/wizardforcel/learning-tf)
+ [EPUB格式](https://www.gitbook.com/download/epub/book/wizardforcel/learning-tf)
+ [MOBI格式](https://www.gitbook.com/download/mobi/book/wizardforcel/learning-tf)
+ [代码仓库](https://github.com/apachecn/learning-tf-zh)
+ [TensorFlow 学习指南](README.md)
+ [一、基础](1.md)
+ [二、线性模型](2.md)
+ [三、学习](3.md)
+ [四、分布式](4.md)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# TensorFlow Rager 教程
> 来源:[madalinabuzau/tensorflow-eager-tutorials](https://github.com/madalinabuzau/tensorflow-eager-tutorials)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
+ [TensorFlow Rager 教程](README.md)
+ [一、如何使用 TensorFlow Eager 构建简单的神经网络](1.md)
+ [二、在 Eager 模式中使用指标](2.md)
+ [三、如何保存和恢复训练模型](3.md)
+ [四、文本序列到 TFRecords](4.md)
+ [五、如何将原始图片数据转换为 TFRecords](5.md)
+ [六、如何使用 TensorFlow Eager 从 TFRecords 批量读取数据](6.md)
+ [七、使用 TensorFlow Eager 构建用于情感识别的卷积神经网络(CNN)](7.md)
+ [八、用于 TensorFlow Eager 序列分类的动态循坏神经网络](8.md)
+ [九、用于 TensorFlow Eager 时间序列回归的递归神经网络](9.md)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册