深度学习实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-11-14 20:36:44

点击下载

作者:(美)杜威·奥辛格(Douwe Osinga)

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

深度学习实战

深度学习实战试读:

前言

深度学习简史

当前深度学习的热潮,其根源可令人惊讶地追溯到20世纪50年代。虽然“智能机器”的模糊概念可以进一步追溯到更早期的科幻小说和各类科学设想中,但是到了20世纪50年代和60年代,才真正出现了“人工神经网络”的最初理念,该理念基于有关生物神经元的一个极简模型。在这些模型中,由Frank Rosenblatt提出的感知机系统引起了大家的极大兴趣。通过连接到一个简单的“照相机”回路,它可以学会区分不同类型的物体。该系统的第一个版本以软件形式在IBM计算机上运行,但是其后续的版本都是用纯硬件来实现的。

对多层感知机(Multilayer Perceptron,MLP)模型的兴趣在20世纪60年代持续不断。到了1969年,Marvin Minksy和Seymour Papert出版了《感知机》(Perceptrons,MIT出版社)一书,彻底改变了这种形势。这本书证明了线性感知器不能对非线性函数(XOR)的行为进行分类。虽然该证明存在局限性(该书出版时,非线性感知器模型已被提出,作者也注意到了这一问题),但是该书的出版预示着神经网络模型研究基金的急剧减少。直到20世纪80年代,随着新一代研究人员的逐步崛起,相关研究才得以恢复。

随着计算能力的提高以及反向传播(back-propagation)技术的发展(反向传播技术自20世纪60年代被提出以来,有很多种不同的形式,但是直到80年代才开始普遍应用),人们对神经网络重新产生了兴趣。不仅计算机拥有了训练更大型网络的能力,我们也拥有了有效训练更深层网络的能力。最初的卷积神经网络将这些技术发展与哺乳动物大脑的视觉识别模型结合起来,首次产生了能够有效地识别诸如手写数字和人脸等复杂图像的网络。卷积网络通过将相同的“子网络”应用到图像的不同位置并将这些结果聚合到更高级的特征中来实现这一点。在本书第12章中,我们会详细介绍这方面的内容。

20世纪90年代和21世纪00年代初期,随着支持向量机(SVM)和决策树等更“易于理解”的模型变得流行,人们对神经网络的兴趣再次出现了下降。对于当时许多数据源来说,SVM被证明是非常优秀的分类器,特别是在与人工特征相结合时更是如此。在计算机视觉中,“特征工程”开始变得流行起来。该技术涉及为图片中的小元素构建特征检测器,并人工将其组合成能够识别更复杂形态的模型。后来科研人员发现,深度学习网络也能够学会识别类似的特征,并能够学会以非常相似的方式组合这些特征。在第12章中,我们将会探讨这些网络的内部工作机制,并对深度学习网络所学的内容进行可视化。

21世纪00年代后期,随着图形处理单元(GPU)通用编程的出现,神经网络架构在竞争中取得了长足的进步。GPU包含数千个微处理器,它们可以每秒并行执行数万亿次操作。GPU最初是为计算机游戏开发的,主要目的是实现复杂3D场景实时渲染,事实证明,GPU也能够用于并行训练神经网络,可以实现10倍或更高倍数的计算速度提升。

另外一件促使深度学习领域长足进步的事情,是互联网的发展为其提供了大量可用的训练数据。以往研究人员只能使用数千幅图像训练分类器,现在已经可以提供几千万甚至数以亿计的图像了。结合更大型的网络,神经网络技术现在迎来了绽放光芒的机会。这种优势仅在最近几年才开始持续出现,伴随着技术改进与现实应用,神经网络技术逐步应用到了图像识别之外的很多领域,包括机器翻译、语音识别和图像合成。为什么是现在

计算能力和改进技术的爆发式发展,使人们对于神经网络的兴趣逐步增加,同时我们也看到了神经网络技术在可用性上取得了巨大的进步。特别是像TensorFlow、Theano和Torch这样的深层学习框架使得非专业人士也能够构建出复杂的神经网络来解决自己的机器学习问题。这使得以往需要数月甚至数年手动编码和辛勤付出(编写高效的GPU内核真的十分困难!)的任务,转变成为任何一个人都可以在一下午(或者几天内)完成的任务。可用性的提升极大地增加了有能力开展深度学习问题研究的人员数量。正如本书在后续章节中将展示的那样,具有更高抽象级别的框架,比如Keras,使得任何具有Python和相关工具知识的人都能够开展一些有趣的实验。

回答“为什么是现在”这个问题的第二个要素是,每个人都可以使用大型的数据集。是的,Facebook和Google在访问数十亿图片、用户评论方面仍占据优势,但是你也可以通过各种数据源获得包含数百万条目的数据集。在第1章中,我们将会探讨各种可选的数据源,并且纵览全书,每章的示例代码通常都会在各章开头向你展示如何获得所需的训练数据。

与此同时,私营公司也开始生产和收集更大数量级的数据,这使得整个深度学习领域在商业上忽然变得越来越令人着迷。一个能辨别猫和狗的模型已经非常不错了,然而一个能够在使用所有历史销售数据的基础上将销售额提升15%的模型,对于一个公司来说则可能意味着生死存亡。你需要知道什么

如今,对于深度学习来说,有很多平台、技术和编程语言可供选择。在本书中,所有的例子都是用Python编写的,并且大部分代码的实现都依赖于优秀的Keras框架。本书的示例代码可以在GitHub上的Python notebook中找到,每章的代码存放在一个notebook中。因此,对读者来说,具备以下知识技能将有助于阅读本书:

Python

Python 3是首选版本,你也可以使用Python 2.7。我们会用到各类helper库,你可使用pip轻松安装它们。本书涉及的代码大都比较简单易懂,所以即使是一名新手也可以跟着本书进行实践。

Keras

机器学习的繁重工作几乎全部是由Keras完成的。Keras是对TensorFlow或Theano深度学习框架的抽象封装。Keras能够轻松地使用可读的方式定义神经网络。本书中所有代码均在TensorFlow框架下完成了测试,但也适用于Theano框架。

NumPy、SciPy、scikit-learn

在很多技巧中用到了这些很有用且广泛使用的代码库。大多数情况下,从上下文中应该能弄清楚其功能,但是对它们进行快速浏览也不会耽误阅读。

Jupyter notebook

notebook是一种很好的共享代码的方式,它允许在浏览器中将代码、代码的输出和注释混合在一起显示出来。

每章都包括了对应的notebook,其中包括了工作代码。本书中代码省略了如导入一类的细节,因此,最好的方法是从Git上下载代码,启动本地的notebook。首先,检查代码并进入新的目录:git clone https://github.com/DOsinga/deep_learning_cookbook.gitcd deep_learning_cookbook

然后,设置项目的虚拟环境:python3 -m venv venv3source venv3/bin/activate

安装依赖程序包:pip install -r requirements.txt

如果你有GPU并想使用它们,你需要卸载tensorflow,安装tensorflow-gpu,使用pip可以很容易地做到这一点:pip uninstall tensorflowpip install tensorflow-gpu

你还需要兼容GPU的函数库,这可能有点麻烦。

最后,启动IPython notebook服务器:jupyter notebook

如果一切顺利,这将自动打开一个带有notebook概况的浏览器,每章一个。你可以任意修改代码,如果你要回到原来的基线,可以使用Git很容易地撤销所做的任何修改:git checkout .ipynb

每章的第1节列出相关的notebook,由于已经按照章节顺序对notebook进行了编号,所以一般来说你很容易找到它们。在notebook文件夹,你还会发现其他3个目录:

Data

包含notebook所需的数据——大多数是开放数据集的例子或者那些太烦琐而无法自己生成的东西。

Generated

用于存储中间数据。

Zoo

包含与每章对应的子目录,每个子目录中是该章保存的模型。如果你没有时间实际训练模型,可以从这里载入这些模型直接运行。本书如何组织

第1章详细介绍了神经网络如何工作,从哪里获取数据,以及如何进行数据预处理以便于使用。第2章讨论面临的困境以及如何处理。众所周知,神经网络很难调试,而本章介绍一些如何让它们表现良好的诀窍,在阅读本书其余部分面向项目的技巧时将会派上用场。如果你没有耐心读完,你可以跳过这一章,等你陷入困境时再回来阅读。

第3~15章围绕各种媒体类型展开介绍,先是从文本处理开始,然后是图像处理,最后第15章是音乐处理。每章将一个项目分解为几个不同的技巧。通常,每章从数据获取技巧开始,接着是几个帮助你完成本章目标的技巧和一个数据可视化的技巧。

第16章介绍在实际生产系统中使用模型。在notebook上做实验很棒,但最终我们想和实际用户分享我们的结果,让模型在真实的服务器或移动设备上运行。这一章将完成这些内容。本书的一些约定

下面是本书在印刷方面的一些约定。

斜体字

表示URL、电子邮件地址、文件名和文件扩展名。

等宽字体

用于程序列表,以及在段落内的程序元素,如变量或函数名、数据库、数据类型、环境变量、语句和关键字。

斜体等宽字体

表示一些可被用户指定值替换的或者根据上下文而确定的文本。表示提示或建议。表示一般性的注释。本书代码

本书的每章都带有一个或多个Python notebook,其中包含了本章提到的示例代码。你可以在不运行代码的情况下阅读这些章节,但是在你阅读时运行这些代码会更加有趣。这些代码可以在https://github.com/DOsinga/deep_learning_cookbook中找到。

在shell中执行下面的命令,可以获得并执行技巧中的示例代码:git clone https://github.com/DOsinga/deep_learning_cookbook.gitcd deep_learning_cookbookpython3 -m venv venv3source venv3/bin/activatepip install -r requirements.txtjupyter notebook

本书是为了帮助你完成工作。所有notebook中附带代码获得了Apache License 2.0授权许可。

我们欢迎但不要求引用本书。引用信息通常包括标题、作者、出版商和ISBN号码。例如“Deep Learning Cookbook by Douwe Osinga(O’Reilly).Copyright 2018 Douwe Osinga,978-1-491-99584-6”。第1章工具与技术

本章我们看一看深度学习常用的工具和技术。通过阅读本章,你可以大致了解什么是深度神经网络,需要的时候也可以再回来翻阅。

我们首先从本书中讨论的各类神经网络的整体情况入手。后面的章节集中精力在如何完成具体工作上,仅会简要讨论如何构建深度神经网络。

然后我们讨论从哪里获得数据。像Facebook、Google这样的科技巨头可以获得海量的数据进行深度学习研究,但是我们也有足够的数据来源做感兴趣的东西。本书各技巧中所用的数据来源广泛。

接下来是数据预处理。这是一个经常被忽略的重要领域。即使你拥有正确的网络配置和完美的数据,仍需要以最好的方式将你的数据送给网络。你要使网络尽可能容易地学习到需要学的东西,而不被数据中其他不相关的东西干扰。1.1 神经网络的类型

在本书中,我们将研究网络和模型。网络是神经网络的简写,指的是一组堆叠互连的层。你在一侧输入数据,经过转换的数据在另一侧输出。每层对数据流进行数学操作,每层拥有一组可以被修改的变量,这些变量决定了每层的具体行为。这里的数据指的是张量,也就是一个多维向量(维度通常是2或3)。

全面讨论不同类型的层和这些操作背后的数学知识超出了本书的范围。最简单的层是全连接层,它将输入作为矩阵,与另外一个称为权重的矩阵相乘,并与第三个称为偏置的矩阵相加。每层的后面是一个激活函数,该函数将这一层的输出映射为下一层的输入。例如,一个简单的激活函数是ReLU,该函数对所有正数的输出保持不变,对负数的输出设置为0。

从技术上来讲,网络一词指的是结构,是不同层互相连接的方式,而模型指的是网络加上决定运行行为的全部变量。训练一个模型就是改变这些变量,使预测的输出更加符合预期。实际上,这两个词经常互换使用。

在实际中,“深度学习”和“神经网络”包含了种类繁多的模型。大多数网络会共享相同的元素(例如,几乎所有的分类网络会使用一种特定的损失函数)。尽管模型的空间各不相同,但我们可以把大多数模型分为几个大的类别。一些模型会使用来自多个类型的片段。例如,很多图像分类网络有一个全连接部分“顶部”来执行最终的分类。

1.1.1 全连接网络

全连接网络是要研究的第一种网络,直到20世纪80年代,它在研究兴趣中一直占主导地位。全连接网络的每个输出单元都是输入的加权和。“全连接”一词是由神经网络每个输出都与输入相连接这个特性而来的。可以写成下面的公式:

为简化表示,很多论文使用矩阵符号表示全连接网络。这里,我们用一个权重矩阵W与输入向量相乘得到输出向量:

y=Wx

因为矩阵相乘是一个线性操作,只包含矩阵乘法的神经网络将只能做线性映射学习。为了增强网络表现力,我们在矩阵乘法的后面加上一个非线性的激活函数。这个函数可以是任何可微的函数,但是常用的函数只有几个。直到最近,双曲正切函数tanh一直是常用的激活函数,仍可以在一些模型中看到它。

使用tanh函数的难点在于该函数在远离零点的区域非常平坦。这导致梯度很小,意味着网络需要很长时间才能改变行为。近来,其他一些激活函数流行起来。最常用的激活函数是修正线性单元函数ReLU:

最后,很多神经网络在最后一层使用sigmoid激活函数,该函数的输出总是处在0和1之间。可以将该输出视为概率值:

矩阵乘法和后面的激活函数一起构成了神经网络的一层。尽管全连接网络的层数比较少,但在一些网络中全部网络层数仍达到了100多层。如果我们正在解决一个分类问题(如“图片中的猫是哪种类型?”),那么网络的最后一层就是分类层。该层的输出数量与我们需要分类的数量相同。

处在网络中间的层称为隐层,隐层的输出有时称为隐单元。“隐层”一词来自于这样一个事实,即从模型外部看不到这些单元。这些层输出的数量取决于模型本身:

尽管有一些如何选择隐层数量和大小的经验法则,但是除了试验和排错外,还没有通用的原则来选择最佳的设置。

1.1.2 卷积网络

早期研究尽力尝试使用全连接网络解决各类问题。但是,当我们的输入是图像时,全连接网络则不是一个好的选择。图像非常巨大:一幅256×256(图像分类中常用的分辨率)像素的图像拥有256×256×3输入(每个像素3个颜色)。如果模型有一个包含1000个隐藏单元的隐藏层,该层将有2亿个参数(可学习的变量)!图像模型需要好几层去实现较好的分类效果,如果我们仅使用全连接层实现,参数将会达到几十亿个。

如此多的参数,模型会不可避免地出现过拟合(下一章将详细介绍过拟合,过拟合指的是网络无法泛化,仅记住了输出结果)。CNN(卷积神经网络)为我们提供了使用很少的参数来训练超过人类识别水平的图像分类器的方法。它们通过模仿动物和人类的视觉原理来实现这一点:

卷积是CNN的基础性操作。卷积不是将函数应用在整个图像上,而是一次扫描图像的一个小窗口。在每个位置上,卷积应用一个核函数(与全连接网络类似,通常是矩阵相乘之后加一个激活函数)。单个核函数通常被视为滤波器。整个图像应用核函数的结果是一个新的、更小的图像。例如,常用的滤波器形状是(3,3)。如果在输入图像上应用32个滤波器,我们需要3×3×3(输入的颜色)×32=864个参数,相对于全连接网络这是一种巨大的节约。

1.子采样

卷积操作减少了参数数量,但我们现在遇到了不同的问题:每层神经网络每次只能“看到”图像3×3的部分。如果是这种情况,我们如何识别占据整幅图像的对象呢?

为了处理这种情况,图像通过神经网络时,典型的卷积网络使用子采样来减少图像的尺寸。子采样有两个常用的机制:

跨步卷积

在跨步卷积,当在图像上滑动卷积滤波器时,我们简单地跳过1个或几个像素。操作得到的结果是尺寸更小的图像。例如,如果输入图像是256×256,跳过1个像素,得到的输出图像将是128×128(为简单起见,我们忽略图像边缘的填充问题)。这种跨步卷积下采样在生成网络中经常使用(详见1.1.4节)。

池化

很多神经网络在卷积过程中不是跳过一些像素,而是使用池化层缩小输入。池化层实际上是另外一种卷积,不是将输入与矩阵相乘,而是应用一个池化操作。典型的池化操作使用最大值或平均值操作符。最大值池化操作在扫描区域之中从每个通道(颜色)选取最大值。平均值池化操作使用平均值代替整个扫描的区域(这可以看作输入图像的简单模糊化)。

另一种思维方式是将子采样作为提高网络功能抽象水平的一种方法。在最低层上,卷积检测小的、局部的特征。很多特征的抽象层次并不是非常深。随着每个池化操作,我们提高了抽象水平。特征的数量减少了,但是特征的抽象层次的深度增加了。直到我们得到少量几个包含可用于预测的高层次抽象的特征,这个过程才结束。

2.预测

将多个卷积层和池化层连接在一起之后,CNN在网络的顶部使用一个或两个全连接层来输出预测结果。

1.1.3 循环网络

循环神经网络(RNN)与CNN在概念上相似,但在结构上却非常不同。循环神经网络常用于处理序列输入。这类输入在处理文本或语音时经常碰到。序列问题一次处理问题的一部分,而不是完整处理单个示例(就像用CNN处理图像一样)。例如,考虑建立一个神经网络为我们写莎士比亚的剧本。输入自然就是莎士比亚自已写的剧本:Lear. Attend the lords of France and Burgundy, Gloucester.Glou. I shall, my liege.

我们希望神经网络学习预测该剧本的下一个单词。为实现这个功能,神经网络需要“记住”到目前为止它所看到过的东西。循环网络提供了这样一种机制。这允许我们建立一个能够处理不同输入长度的模型(例如句子或语音片段)。最基本的RNN结构形式如下:

从概念上讲,你可以认为RNN是一个被我们“展开”了的非常深的全连接网络。在这个概念模型之中,每层神经网络有2个输入,而不是我们过去常用的1个输入:

回想一下,在原来的全连接网络中,我们有一个矩阵乘法运算:

y=Wx

在该操作中增加第二个输入最简单的方法就是将它与隐状态相连接:

hidden=W{hidden|x}ii-1

这里“|”表示连接。正如全连接网络一样,我们可以在矩阵乘法输出上使用激活函数,以获得新的状态:

hidden=f(W{hidden|x})ii-1

有了RNN的说明,我们可以很容易理解如何训练RNN:简单地将RNN作为未展开的全连接网络,并照常训练它。在文献中这称为基于时间的反向传播算法(BPTT)。如果我们的输入太长,通常需要将它们分割为较小尺寸的片段,并独立训练每个片段。然而,这种技术并不是对每个问题都奏效,只是比较稳妥和常用。

梯度消失和LSTM

很可惜的是,朴素RNN对长输入序列的效果比我们预期的差很多。这主要是因为它的结构更容易遇到“梯度消失问题”,造成梯度消失的主要原因是未展开的网络非常深。每次通过激活函数,都有一定概率导致较小的梯度通过(例如ReLU激活函数在输入小于0时,梯度等于0)。一旦某个单元出现这种情况,训练就无法通过此单元传递到网络。这导致训练信号不断下降。观察到的结果是学习极其缓慢或整个网络不再继续学习。

为解决此问题,研究人员开发了构建RNN的替代机制。在时间维度展开状态的基本模型得到了保留,我们不再使用简单的矩阵乘法以及后面的激活函数,而是使用一个更复杂的方式进行状态前向传播(下图来自维基百科):

长短期记忆网络(LSTM)使用四个矩阵乘法替换一个矩阵乘法,并引入了可以与向量相乘的门的概念。其关键是始终有一条从最终预测结果到任何层的路径,该路径可以保留梯度,这使得LSTM比普通的RNN能更加有效地学习。

阐述LSTM如何完成任务的详细过程超出了本章的范围,但是互联网上有几个不错的手册可供参考(http://colah.github.io/posts/2015-08-Understanding-LSTMs/)。

1.1.4 对抗网络与自动编码器

与我们上面讨论的网络类似,对抗网络和自动编码器没有引入新的网络结构。相反,它们使用更加适合特定问题的结构。例如,处理图像的对抗网络或自动编码器会使用卷积。不同的地方在于如何训练它们。最常用的网络训练方法是从输入(一个图像)预测输出(是否是一只猫):

训练后的自动编码器输出提供的图像:

我们为什么要这样做呢?如果神经网络中间的隐层包含输入图像的一个表示,这个表示包含了明显比输入图像少的信息,通过这个表示可以重构原始图像,这就产生一种压缩形式:我们可以使用神经网络中间层的一些值表示任何图像。从另外一个角度思考就是,对于原始图像,我们使用神经网络将它映射到抽象空间。该空间的每个点可以被转换回这个图像。

自动编码器已经成功应用在小图像上,但是其训练机制不能很好地扩展到更大的问题上。实际上,这个用于图像绘制的空间并不是足够“密集”,很多点事实上并没有表示相关的图像。

我们将在第13章看到一个自动编码器的例子。

对抗网络是更新一点的模型,可以用来生成逼真的图像。对抗网络把问题分为两部分进行处理:生成网络和判别网络。生成网络输入一个小的随机种子,产生一个图像(或文本)。判别网络尝试去判断输入的图像是否是“真的”或者是否是来自于生成网络。

当我们训练对抗模型时,两个网络同时训练:

我们从生成网络中采集一些图像送到判别网络。如果生成网络产生的图像可以欺骗判别网络,生成网络就会受到奖励。判别网络也不得不正确地识别真实的图像(它不能总说图像是假的)。通过两个网络的互相竞争,会产生一个可以生成高质量自然图像的生成网络。第14章介绍如何使用生成式对抗网络来产生图标。

1.1.5 小结

目前有很多构建神经网络的方法,选择哪种方法主要取决于网络的用途。在研究领域设计一个新型网络十分困难,即使重新实现一种论文中描述的网络也十分困难。实际工作之中,最简单的方法就是在你所要工作的方向上选择一个已实现某些功能的例子,一步一步地修改它,直到它按你的期望工作。1.2 数据获取

近几年来,深度学习能够飞速发展的原因之一就是数据的可用性大幅增加。20年前人们使用几千幅图像训练神经网络,现在像Facebook和Google这样的公司可以使用几十亿幅图像。

可以从用户处获取所有的信息是这些公司以及其他互联网巨头在深度学习领域的天然优势。然而,互联网上有很多易于获得的数据源,花费少量的精力即可满足诸多训练的需要。本节将讨论这些重要的数据源。对于每个数据源,我们将看看如何获取这些数据,有哪些流行的库可以帮助我们解析数据,以及典型的用例,同时还会为你提供这些数据源的使用方法。

1.2.1 维基百科

维基百科不仅有500多万篇英文文章,还支持上百种语言(https://en.wikipedia.org/wiki/List_of_Widipedias),尽管它们在深度和质量上有着很大的差异。维基百科的基本的思想仅仅是支持链接作为结构编码的一种方式,但是随着时间的推移,维基百科已经超越了这一点。

类目页链接到某一属性或主题页,因为这些页面又链接到类目页,这样我们可以有效地使用这些标签。类目可以非常简单,如“猫”,但是有时将信息编码到它们的名字之中,可以有效地将(键,值)对赋给一个页面,如“1758年记录的哺乳动物”。与维基百科上其他类目非常类似,这个类目的结构也非常特别。此外,递归类别只能沿着树进行追溯。

模板最初设计为维基百科标记的片段,其目的是要自动复制(“嵌入”)到一个页面中。你可以通过在{{双括号}}中放置模板名来添加它们。这使得不同页面的布局可以保持同步。例如,所有城市页面都有一个信息框,其属性如人口、位置和标志在页面上始终一致地呈现。

模板拥有一些参数(如人口),这些参数被视为将结构信息嵌入维基百科页面的一种方法。在第4章,我们将用模板提取一个电影集合,用它们来训练电影推荐系统。

1.2.2 维基数据

维基数据(https://www.wikidata.org/)是维基百科的结构化数据部分。相对于维基百科,维基数据知名度低一些,并且还不够完整,但其雄心勃勃。它计划在公开授权下为每个人提供常规数据源。这使它成为一个优秀的免费数据源。

所有的维基数据以(主语,谓语,宾语)三元组的形式存储。维基数据所有主语和谓语有自己的记录,它们列出该主语上所有的谓语。宾语可以是维基数据的记录或文字,如字符串、数字或日期。这个结构受到语义网络的早期想法的启发。

维基数据有自己的查询语言,该语言与SQL类似,并进行了扩展。例如:SELECT ?item ?item Label ?picWHERE{ ?item wdt:P31 wd:Q146 . OPTIONAL { ?item wdt:P18 ?pic } SERVICE wikibase:label { bd:service Param wikibase:language "[AUTO_LANGUAGE],en" }}

该查询将选择一系列猫和它们的照片。变量以问号开头。wdt:P31(或属性31)是“一个实例”的意思,wd:Q146是家猫的一个类型。因此,第4行在项中存储猫的一个实例。OPTIONAL{..}语句尝试查找上述项对应的图片,最后一行尝试使用自动语言特性为上述项查找标签,如果失败,则使用英语。

在第10章,我们将维基数据和维基百科结合起来获取标准的类别图像,作为反向图像搜索引擎的基础。

1.2.3 开放街区地图

开放街区地图(OSM,https://www.openstreetmap.org/)与维基百科相似,但内容是关于地图的。维基百科的思想是如果世界上每个人将自己知道的每件事都放在维基百科上,我们就有可能得到最好的百科全书。开放街区地图也是基于这样的思想,如果每个人将自己知道的道路信息放到维基上,我们就有可能得到做好的地图系统。

很显然,这两个思想都表现得非常好。

尽管OSM的覆盖面参差不齐,从几乎没有覆盖的区域到覆盖度可以比拟甚至超过Google地图的地方都在包含范围之内,数据的绝对数量以及免费提供的事实仍使它成为所有与地理相关的项目的巨大资源。

OSM的二进制格式文件和较大XML格式文件可以自由下载。整个世界文件有几十GB,但是如果我们想使用小一些的文件,互联网上有很多按国家或区域导出的文件。

二进制格式文件和XML格式文件有着相同的结构:地图由一系列带着经度和纬度信息的节点以及道路构成,这些道路结合前面已经定义好的节点形成了更大的结构。最后,这些关系结合了更多的前面看到的东西(节点、道路或关系)构成了超级结构。

节点用来表示地图上的点,包括一些独立的特征以及道路的形状。道路用来表示简单的形状,如建筑物和道路区段。最后,用关系来表示包含多个形状的或更大的事物,如海岸线或边界线。

本书的最后,我们将研究一个模型,该模型输入卫星图像和渲染后的地图,尝试学习自动识别道路。这个技巧所使用的实际数据并不局限于OSM,但这属于OSM应用于深度学习的范围。例如,“图像到OSM”项目(https://github.com/jremillard/images-to-osm)告诉我们怎样训练一个从卫星图像中提取体育场形状的神经网络来改进OSM。

1.2.4 Twitter

作为一个社交网络,Twitter很难与规模更大的Facebook进行竞争,但是就深度学习的文本来源来说,Twitter则是一个更好的选择。Twitter的API非常完善,允许各种应用程序使用。然而,对于刚起步的机器学习迷来说,流式API可能是最有趣的一个。

Twitter提供的所谓Firehose API直接将推文以流的形式送给客户端。正如所想象的那样,这是一个非常庞大的数据。除此之外,Twitter为此收取高额的费用。鲜为人知的是,Twitter免费API提供了一个采样版的Firehose API。这个API仅返回全部推文的1%,但这对于很多文本处理应用来说已经足够了。

推文的大小有限,但附带了一些有趣的元信息,例如作者、时间戳、位置信息标签、图像和URL地址。在第7章,我们会介绍如何使用这个API基于一小部分文本建立一个预测感情符号的分类器。我们开发流API,仅保留只包含一个感情符号的推文。耗费几个小时就可以得到一个完整的训练数据集,但是如果你的计算机有稳定的互联网连接,让它执行几天也没什么问题。

Twitter是情感分析实验中常见的数据源,针对语言检测、位置消歧和命名实体识别的模型都成功地在Twitter数据上进行了训练,预测表情符号则无疑是这些模型的一种演进。

1.2.5 古腾堡计划

早在Google图书项目很久之前,事实上早在Google甚至互联网出现之前,追溯到1971年,古腾堡计划就启动了将所有书籍都数字化的计划。这个计划包含5000多部书籍,不仅有小说、诗、短片故事和戏剧,还有食谱、著作和期刊。大多数文本属于公开领域,可以在网站上自由下载。

这里有大量格式便捷的文本,如果你不介意其中大多数文本有些老的话(因为他们不再享有版权),这里就是很好的文本处理实验数据源。在本书第5章,我们使用古腾堡计划获得莎士比亚作品集的一个副本,作为生成类似莎士比亚文本的基础。如果你有Python库,它只需要一行代码:shakespeare = strip_headers(load_etext(100))

尽管也有少量其他语言的书籍,古腾堡计划得到的大多数资料都是英文的。该项目开始主要是纯ASCII格式,但是随着不断的发展现已支持很多字符编码方式,因此如果下载非英语文本,你需要确保正确的编码方式——然而并不是世界上所有文本都是UTF-8编码。在第8章,我们从一些古腾堡计划的书籍中提取所有对话,然后训练聊天机器人模仿那些对话。

1.2.6 Flickr

Flickr(https://www.flickr.com)是一个图片分享网站,它从2004年开始运营。它最初是一个名为“游戏永不结束”的大型多人在线游戏的一个辅助项目。当这个游戏无法成为一门生意时,公司的创始人意识到公司的照片共享部分正在飞速发展,因此他们执行了所谓的核心转换,完全改变了公司的主要业务焦点。

一年后,Flickr卖给了雅虎。

在众多的照片分享网站中,Flickr成为一个深度学习实验图像的有效来源,有以下几个原因。

一是Flickr已经运营了很长时间,已经有上亿张的图像。尽管这与人们在Facebook上每个月上传的图像数量相比,会显得有些苍白,但是因为将图像传到Flickr的用户会对公开使用感到非常骄傲,Flickr的图像平均水平是高质量的,而且更加有趣。

第二个原因就是许可。Flickr上的用户为自己照片选择了许可证,许多人选择某种形式的创作共享许可(https://creativecommons.org/),允许在没有请求许可的情况下重复使用这些照片。虽然如果你通过最新最棒的算法来运行一组照片,并且只对最终结果感兴趣,那你通常不需要这个,但是如果你的项目最终需要重新发布原始或修改的图像,这一点就是非常重要的了。Flickr使这一点成为可能。

相对于其他竞争者,最后一个也可能是最重要的原因,就是Flickr的API。和Twitter API类似,它是一个考虑周详的、REST风格的API,这使得你可以在网站上自动地执行你想要的操作。像Twitter一样,Flickr的API与Python进行了很好的绑定,这使得开展实验非常容易。你所需要的就是正确的函数库和Flickr API密钥。

本书中涉及API的主要功能是搜索和获取图像。这个搜索模仿了一些主流网站大多数的搜索选项,功能非常丰富,尽管很不幸缺少一些高级的过滤器。这里可以获取各种尺寸的图像。从小尺寸图像快速地开始,后续再扩大图像尺寸,通常是一个有效的方法。

第9章,我们使用Flickr API函数获取两个图像集合,一个是狗的图像,一个是猫的图像,并且训练一个分类器来学习它们之间的差异。

1.2.7 互联网档案馆网站

互联网档案馆网站(Internet Archive,https://archie.rog//)的使命是提供“所有知识的普遍性获取”。该项目因它的互联网时光机而出名,这是一个允许用户查看过去Web页面的Web接口。互联网时光机包含了3000亿条追溯到2001年抓取的信息,该项目中调用了三维Web索引。

但是互联网档案馆网站远比互联网时光机大得多,其中包括了杂七杂八的文档、媒体,以及范围从过了版权期的书籍,到NASA的图像,再到艺术领域音频CD和视频资料的包罗万象的数据集。

这些东西都非常值得浏览,也经常激励新项目加入其中。

一个有趣的例子是一组直到2015年的所有的Reddit新闻网站评论,有超过5000万个条目。这件事情是这样作为项目开始的:一个Reddit新闻网站的用户非常有耐心地使用Reddit API下载所有评论,然后说明这些信息源自Reddit。当遇到在何处存储这些评论的问题时,互联网档案馆网站成了一个不错的选择(尽管同样的数据可以通过Google Big Query以更加即时分析的方式找到)。

本书中使用的是一个关于Stack Exchange网站(https://archive.org/details/stackexchange.)上问题的例子。Stack Exchange网站已经进行了长期创意共享许可授权,所以没有什么会妨碍我们自己下载这些数据集,但是通过互联网档案馆网站来获取这些数据会更加容易。在本书中,我们使用这个数据集训练一个匹配问题和答案的模型(详见第6章)。

1.2.8 爬虫

如果你的项目需要特定信息,但你所访问的数据可能无法通过公共API访问。即使有公共API,也可能因为限速而变得无法使用。你最喜欢的体育项目的历史数据很难得到。你本地的新闻报纸有在线文档,但是没有API或无法导出数据。Instagram拥有很好的API,但是最近该服务的变化导致获取大规模的训练数据集变得非常困难。

在这些情况下,你可以选择抓取,或者如果你想听起来更体面,就叫爬虫。最简单的情景就是你希望在本地复制一个网站,可你没有网站结构或URL结构的先验知识。在这种情况下,你仅需要从网站的根目录开始,读取网页的内容,从网页中提取出所有的链接,在所有链接上执行同样的操作,直到不再发现新的链接为止。Google在更大的范围上也是这样做的。Scrapy(https://scrapy.org)是一个处理此类工作非常有用的框架。

有时候网站有着明显的结构,例如一个旅游网站有国家、地区、这些地区中的城市,最后还有城市中的景点。这时,写一个具体的scraper更加有效,这个scraper不断地工作直到遍历完该结构的所有层,得到所有景点。

其他时候可以利用内部API。很多基于内容的网站会首先载入全部布局,然后使用JSON调用网页服务器来获得实际数据,插入到模板之中。这种情况支持无限的抓取和搜索。就像传递给服务器的参数一样,从服务器返回的JSON通常很容易理解。Chrome扩展请求程序(http://bit.ly/request-maker)会显示页面发出的所有请求,这是一个查看是否有任何有用的东西出现的好方法。

然而,很多网站并不希望被抓取。Google本来就是一个通过抓取建立的帝国,但是它的很多服务会明确地检测抓取信号,阻止你或从你的IP地址发出请求的任何人,直到你完成验证。你可以使用速率限制和用户代理,但在某些时候可能不得不求助于使用浏览器进行抓取。

Web Driver是一个通过调用浏览器进行网站测试的框架,在这些情况下会非常有用。网页的获取是通过选择的浏览器实现的,因此对于网页服务器来说更像一个真实的访问。你可以使用控制脚本来“点击”链接,监测执行结果。考虑将爬虫代码进行一些延时,使它像人一样浏览网站,你就可以很好地进行下去。

第10章的代码使用爬虫技术从维基百科中获取图像。一般情况下,有一个从维基百科ID到相应图像的URL方案,但它并不总能正常工作。这时我们可以获取包含图像的页面,并按照链接查找图,直到得到实际图像。

1.2.9 其他选择

有很多获取数据的方法。Programmable Web(http://www.programmableweb.com)列出了18000多个公共API(尽管一些已经失效)。有三个值得关注的方面:

Common Crawl

如果网站不是很大的话,爬一个网站是可行的。但是假如你想爬一遍互联网的所有主要页面呢?Common Crawl(http://commoncrawl.rog/)执行月度爬虫,每次以一种非常容易处理的格式获取20亿个页面。AWS将它作为一个公共数据集,因此你可以在这个平台执行,一般情况下非常容易以这种方式大规模地执行作业。

Facebook

经过多年的发展,Facebook的API已经渐渐地从一个实实在在构建基于Facebook数据的应用程序的有用资源,发展为一个构建让Facebook数据更好用的应用程序的资源。尽管从Facebook的角度来看可以理解,但作为数据探索者会经常为其仍能公开的数据感到好奇。不过,尤其是在OSM编辑过于不均匀的情况下,Facebook API仍是一个有用的资源。

美国政府

美国政府的各级部门都公布了大量的数据,这些数据都可以免费访问。例如,人口普查数据(https://www.census.gov)拥有详细的美国人口信息,Data.gov(https://www.data.gov/)网站门户上列出了各方面不同的数据集。最重要的是,各州和城市都有一些值得一看的资源。1.3 数据预处理

深度神经网络非常善于发现数据中的模式,这有助于学习预测数据的标签。这也意味着我们需要小心处理传递给神经网络的数据,数据中任何与我们问题不相关的模式可能会使神经网络学习到错误的东西。通过正确的数据预处理,我们可以使神经网络训练更加容易。

1.3.1 获得一个平衡的测试数据集

有这样一个真实性存疑的故事,讲述了美国陆军曾经如何训练神经网络来区分伪装的坦克和真实的森林,这是自动分析卫星数据时很有用的技巧。乍一看,他们做的一切都是对的。有一天,他们驾驶一架飞机飞过一片森林,里面藏有伪装的坦克,然后拍了照片,而另一天,他们按照同样的方法又照了照片,但是这次没有伪装的坦克,拍摄过程中确保两个场景相似但是不尽然相同。他们把数据分成训练集和测试集,让网络进行训练。

网络训练好之后,刚开始得到了一些不错的结果。但当研究人员将它送到野外进行测试时,人们认为该网络简直是一个笑话。预测结果似乎非常随机。经过一阵详查,结果发现是输入数据存在问题。包含坦克的照片都在晴朗的一天拍摄,而只有森林的照片都在阴天拍摄。因此,当研究人员认为他们的神经网络学习到了如何区分有没有坦克时,其实他们训练了一个观察天气的神经网络。

数据预处理本质就是确保神经网络提取我们希望它提取的信号,而不受其他无关事物的干扰。这里第一步就是确保我们确实有正确地输入数据。理想情况下,数据应该尽可能接近真实世界的情况。

确保数据中所包含的信号是我们试图学习的信号看起来是显而易见的,但很容易弄错。获取数据是困难的,每一个数据源都有它自己的特殊性。

当发现输入数据被污染时,我们可以做一些事情来补救。当然,最好的办法是重新平衡数据。因此,在坦克与森林的例子中,我们将试着在各类天气中获得两类场景的图片。(如果你仔细想想,会发现即使所有的原始照片都在晴朗的天气拍摄,训练集还是不够理想——一个平衡的训练集将包含各种天气条件。)

第二个选择是丢弃一些数据使集合更加平衡。也许在阴天里拍了一些坦克的照片,但是数量还不够,所以我们可以丢弃一些晴天的照片。显然,这削减了训练集的大小,而且在有些情况下并不适用。(在1.3.5节中讨论的数据增强可能会对此有所帮助。)

第三个选择是尝试修复输入数据,也就是使用图像滤镜让天气条件呈现得更加相似。这么做有些棘手,可能很容易引入其他的或更多的可能会被网络检测到的人为因素。

1.3.2 数据分批

神经网络以批处理方式使用数据(输入/输出对集合)。重要的是要确保这些批次数据适当地随机化。设想我们有一组图片,前半部分都描绘了猫,后半部分描绘了狗。如果没有对数据进行随机交叉,神经网络就不可能从这个数据集中学到任何东西:几乎所有批次要么只包含猫,要么只包含狗。如果我们使用Keras,而且数据完全存储在内存中,那么使用fit方法很容易实现这一点,因为它将执行随机交叉操作:char_cnn_model.fit(training_data, training_labels, epochs=20, batch_size=128)

fit将批大小取值128,随机从training_data和training_labels集合中创建批次。Keras负责进行随机化。只要我们将数据载入内存,它就会按通常的方式处理。在一些情况下,我们希望每个批次调用一次fit,这时我们的确需要确保正确的随机交叉。我们不得不让数据和标签一起小心地进行随机交叉,numpy.random.shuffle可以很好地做到这一点。

然而我们并不是总能将数据载入内存。有时数据可能太大或者需要动态处理,或者格式不理想。在这些情况下,我们使用fit_generator:char_cnn_model.fit_generator( data_generator(train_tweets, batch_size=BATCH_SIZE), epochs=20)

这里data_generator是一个产生批次数据的生成器。该生成器确保数据被适当随机化。如果数据是从文件中读入,则不能进行随机交叉。如果数据来自于SSD并且记录的大小相同,我们可以通过文件内部随机化实现随机交叉。如果不是这种情况,并且文件拥有某种排序,我们可以通过使同一个文件拥有多个文件句柄来增加随机性,这些文件句柄都在不同的位置。

当建立生成器批量动态生成批次数据时,我们还需要注意保持适当的随机性。例如,在第4章中我们在维基百科文章上进行训练,使用从电影页面到其他页面的链接作为训练数据,构建一个电影推荐系统。生成这些(FromPage,ToPage)对的最简单方法是随机选择一个FromPage,然后从FromPage上找到的所有链接中随机选择一个ToPage。

当然,这是可行的,但是它会在链接较少的页面中选择比应选数量更多的链接。在第一步,具有一个链接的FromPage与具有一百个链接的页面具有相同的被选中的机会。但是在第二步中肯定要选择一个链接,而页面上具有100个链接页面中的每个链接被选中的机会都很小。

1.3.3 训练、测试和验证数据

在建立干净的、归一化的数据之后,开始实际训练之前,我们需要将数据划分为训练集和测试集,以及尽可能划分一个验证集。正如许多事情一样,这样做的原因与过拟合有关。神经网络几乎总是记住一小部分训练数据,而不是学习泛化。通过将一小部分数据划分到一个训练中不使用的测试集,我们可以测量这种情况发生的程度。在每代(epoch)训练完成之后,我们测量训练集和测试集的准确率,只要这两个数字间没有太大的差异,训练得就不错。

如果我们将数据载入内存,使用sklearn的train_test_split将数据划分为训练集和测试集:data_train, data_test, label_train, label_test = train_test_split( data, labels, test_size=0.33, random_state=42)

这将创建一个包含33%数据的测试集。random_state作为随机种子,确保两次执行同样的程序可以得到相同的结果。

当我们使用生成器为神经网络送入数据时,需要自己划分数据。常用但不是非常高效的一个方法是使用类似下面的程序:def train_or_test(gen, train=True): for i, x in enumerate(gen): if (i % 4 == 0) != train: yield x

当train为False时,生成器gen的每第4个元素中生成一个数据。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载