深度学习的可视化方法-全-

深度学习的可视化方法(全)

原文:zh.annas-archive.org/md5/1d11bcd44219f6e6fdfcb80821618fa3

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

想象一下你正在摩擦一盏金灯。你说:“精灵,作为我的三个愿望,给我一个可以相爱的人、巨大的财富和长久健康的生活。”

现在想象一下,你正走进自己的家。你说:“房子,把车开过来,问一下莎拉她是否有空吃午餐,安排个理发,给我做一杯拿铁。哦,顺便放点塞隆纽斯·蒙克的音乐。”

在这两种情境下,你都在请求一个强大的、无形的存在听到你的声音、理解你的需求,并满足你的愿望。第一种情景是一种追溯千年的幻想。第二种情景是今天的普遍现实,这要归功于人工智能,或者说 AI。

我们是如何发明这些神奇的“精灵”的?今天正在改变世界的人工智能革命源自三项发展。

首先,计算机每年都在变得更大、更快,最初用于生成图像的专用芯片现在也在为 AI 技术提供动力。

其次,人们不断开发新的算法。令人惊讶的是,今天推动 AI 的某些算法已经存在了几十年。它们比任何人想象的都要强大,只是在等待足够的数据和计算能力来让它们大放异彩。更新且更强大的算法现在正以越来越快的速度推动这一领域的进展。今天使用的一些最强大的算法属于一种被称为深度学习的 AI 类别,而这些正是本书将重点介绍的技术。

第三,或许最关键的一点是,现在有了大量的数据库供这些算法学习。社交网络、流媒体服务、政府、信用卡机构,甚至超市都在衡量并保存它们能够收集到的每一丝信息。网络上的公开数据也是庞大的。到 2020 年底,估计网上已经有超过 40 亿小时的视频内容可以自由访问,超过 170 亿张图片,以及无数文本,涉及从体育历史到气候变化、从天气模式到市政记录等各种主题。

在实践中,这些数据库通常被视为任何新机器学习系统中最有价值的组成部分。毕竟,任何有钱的人都可以购买计算机,而它们运行的算法几乎都可以在研究期刊、书籍和开源库中找到。真正让组织垂涎的,是数据,它们把这些数据独占,或卖给出价最高的人。毫不夸张地说,数据库就是新石油,新黄金。

像所有革命性技术一样,人工智能(AI)也有其热情的支持者,他们预见到人类将从中获得巨大益处;也有悲观的预言者,他们看到的只有毁灭和摧毁我们世界中各种社会与文化的可能性。谁是对的?我们如何判断这项新技术的风险与回报?我们应该何时拥抱 AI,何时禁止它?

深入理解一项新技术是理智的应对方式。当我们了解它是如何工作的,以及它的优点和局限性,我们就能决定它应该在何处以及如何使用,以创造我们希望生活的未来。本书旨在帮助你理解深度学习是什么以及它是如何工作的。当你了解深度学习的优点和不足时,你将能更好地运用它到自己的工作中,并理解它对我们的文化和社会的实际与潜在影响。它还将帮助你识别当拥有权力的人和组织使用这些工具时,再决定它们是为你的利益而用,还是为了他们自己的利益。

本书适合谁阅读

我写这本书是为了任何对深度学习如何运作感兴趣的人。你不需要数学或编程经验。你不需要成为计算机高手。你根本不需要是技术专家!

本书适合任何有好奇心并希望深入了解新闻背后的内容的人。你可能会惊讶地发现,深度学习的大多数算法其实并不复杂或难以理解。它们通常简单而优雅,通过在庞大的数据库上反复执行百万次来获得强大力量。

除了满足纯粹的智力好奇心(我认为这是学习任何事物的一个很好理由)外,我写这本书是为了那些在自己工作中,或者与使用深度学习的其他人互动时,面对深度学习的人。毕竟,理解人工智能的一个最佳理由就是我们可以亲自使用它!我们现在可以构建帮助我们更好地工作、深入享受我们的爱好、并更全面理解周围世界的 AI 系统。

如果你想了解这些东西是如何工作的,你会觉得如鱼得水。

本书没有复杂的数学和代码

每个人都有自己的学习方式。我写这本书是因为我觉得有些人希望理解深度学习,但他们不想通过研究方程式或程序来达到这个目的。

因此,我并不通过方程式或程序列表来描述算法,而是使用文字和图像。这并不意味着描述很马虎或模糊。相反,我努力做到尽可能清晰,并在适当时做到精确。当你读完这本书,你将对一般原则有一个扎实的掌握。如果你之后决定想用数学语言重新框架你的理解,或者用某个特定的计算机语言或库编写程序,你会发现这比从零开始要容易得多。

如果你需要代码,它在这里

虽然阅读本书不需要编程技能,但如果你想自己构建和训练系统,将这些想法转化为实践是很重要的。所以,如果你有这个愿望,我已经提供了工具来满足它。

我在我的 GitHub 上提供了三个免费的附赠章节,链接是 github.com/blueberrymusic。其中一章讲解了如何使用 Python 的免费库 scikit-learn,另外两章则展示了如何构建我们在书中讨论的许多深度学习系统。通过实际运行的代码,你可以在此基础上修改并使用,进而为你自己的应用设计和训练自己的网络。代码以流行的 Jupyter notebooks 形式呈现,因此你可以立即开始调查、实验、适应和使用。

所有这些程序都在 MIT 许可证下共享,这意味着你几乎可以按照任何方式使用它们。

图示也可以获得!

同样在我的 GitHub 上 github.com/blueberrymusic,你会找到我为本书绘制的几乎所有图示,都是高质量 300 dpi 的 PNG 格式。这些图示和代码一样,也都在 MIT 许可证下提供,因此你可以自由地使用或修改它们,用于课堂、讲座、演示、论文,或任何其他你觉得这些图示能帮助你的地方。

勘误

任何一本这样规模的书籍都难免会有错误,从小的打字错误到大的失误。如果你发现有看起来不对的地方,请通过 errata@nostarch.com 告诉我们。我们会在 nostarch.com/deep-learning-visual-approach 上发布确认的修正列表。

关于本书

本书的第一部分涵盖了我们稍后需要的概率论、统计学和信息论的基础思想。不要让这些话题的名称吓到你!我们只会讲解最基本、最核心的概念,几乎不涉及数学符号。你可能会惊讶于当这些话题以通俗的语言和图示呈现时,它们竟然如此简单。

书的第二部分介绍了基本的机器学习概念,包括基本思想和一些经典的算法。

有了这些背景知识后,书的第三部分和第四部分是关键,我们将深入探讨深度学习本身。

这是每一章内容的简要概述。

第一部分:基础概念

  1. 第一章: 机器学习概述。我们将概览全局,并为理解机器学习的工作原理铺垫基础。

  2. 第二章: 基本统计学。深度学习的核心思想之一是从数据中发现模式。统计学的语言帮助我们识别和讨论这些模式。

  3. 第三章: 性能评估。当算法回答一个问题时,总是有一定的概率这个答案是错误的。通过仔细选择评估方法,我们可以讨论“错误”到底意味着什么。

  4. 第四章: 贝叶斯法则。我们可以通过考虑我们的预期和我们迄今看到的结果来讨论算法给出正确结果的可能性。贝叶斯法则是实现这一点的有力工具。

  5. 第五章: 曲线与曲面。当学习算法在数据中寻找模式时,它通常会利用抽象的曲线和表面,存在于想象的空间中。为了帮助我们在书中后续讨论这些算法,这里我们讨论这些曲线和曲面是什么样的。

  6. 第六章: 信息论。在机器学习中,一个强大的概念是我们在表示和修改信息。信息论的思想使我们能够量化和测量不同种类的信息。

第二部分:基础机器学习

  1. 第七章: 分类。我们通常希望计算机将一条数据分配到特定的类别或类别中。例如,照片中是什么动物,或者电话里正在说什么单词?我们将探讨解决这个问题的基本思路。

  2. 第八章: 训练与测试。为了构建一个可以在现实世界中使用的深度学习系统,我们首先需要训练它,教会它如何完成我们希望的任务,然后测试它的性能,确保它能够高效地完成工作。

  3. 第九章: 过拟合与欠拟合。训练深度学习系统时一个令人惊讶的结果是,它可能开始记住我们用来训练的数据。在一个明显的悖论中,这反而使得它在发布时处理新数据时变得更差。我们将探讨这个问题的来源以及如何减少其影响。

  4. 第十章: 数据准备。我们通过提供大量数据来训练深度学习系统,使其从中学习。我们将探讨如何准备这些数据,以便训练过程尽可能高效。

  5. 第十一章: 分类器。我们将研究用于分类数据的具体机器学习算法。这些算法通常是了解我们数据的好方法,在投入时间和精力训练深度学习系统之前,可以先用这些方法做一些了解。

  6. 第十二章: 集成方法。我们可以将许多非常简单的学习系统组合成一个更强大的复合系统。有时,多个小系统比单个大型系统返回答案的速度更快,准确性也更高。

第三部分:深度学习基础

  1. 第十三章: 神经网络。我们将研究人工神经元及其如何连接形成网络。这些网络构成了深度学习的基础。

  2. 第十四章: 反向传播。使神经网络得以实践的关键算法是训练它们的一种方式,使它们能够从数据中学习。我们将深入探讨构成学习过程的两个算法中的第一个。

  3. 第十五章: 优化器。训练深度网络的第二个算法实际上是修改构成网络的数值,从而提升其性能。我们将探讨多种有效的实现方法。

第四部分:超越基础

  1. 第十六章: 卷积神经网络。为了处理图像等空间数据,已经开发出了强大的算法。我们将了解这些算法及其使用方式。

  2. 第十七章: 卷积神经网络实践。在掌握了处理空间数据的技术之后,我们将更深入地探讨如何在实际中使用这些技术来识别物体。

  3. 第十八章: 自编码器。我们可以简化庞大的数据集,使其更小、更易于管理。我们还可以去除噪声,帮助我们清理受损的图像。

  4. 第十九章: 循环神经网络。当我们处理序列数据时,比如文本和音频片段,我们需要特殊的工具。在这里,我们将介绍一种流行的工具类别。

  5. 第二十章: 注意力机制与 Transformer。理解文本和语言尤为重要。我们将探讨最初设计用于解释和生成文本的算法,但它们也被证明在其他应用中非常有用。

  6. 第二十一章: 强化学习。有时我们不知道希望计算机提供的答案是什么,比如在安排涉及不可预测的群体活动时。我们将看看如何灵活地解决这类问题。

  7. 第二十二章: 生成对抗网络。我们经常想要创建或生成我们拥有的某些数据的新实例,例如从原始数据生成新闻故事,或者为人们在游戏中探索创造世界。我们将探讨一种强大的训练这种生成器的方法。

  8. 第二十三章: 创意应用。我们将在一些有趣的应用中总结,使用深度学习工具制作迷幻图像,为照片应用艺术家的签名风格,生成任何作者风格的新文本。

最后总结

本书包含了大量的内容!

这里的所有内容都是为了让你完成后真正掌握相关知识。你将能够与其他人讨论深度学习,分享你的见解和经验,并从他们的经验中学习。如果你有足够的动力,你还可以拿到许多免费的深度学习库,设计、训练、测试并部署自己的系统,用于任何你能想到的目的。

深度学习是一个迷人的领域,它将来自多个智力学科的思想结合在一起,构建出迫使我们提出有关智能和理解本质的基本问题的算法。它也充满了乐趣!

欢迎加入这段旅程,你将度过一段非常棒的时光!

第一部分

基础概念

第一章:机器学习概述

本书讲述的是深度学习,它是机器学习的一个子领域。机器学习这个词描述了一个不断发展的技术体系,这些技术有一个共同的目标:从数据中提取有意义的信息。在这里,数据指的是任何可以用数字表示的东西。数据可以是原始数字(例如连续几天的股票价格、不同星球的质量,或是参加县展览的人们的身高),但也可以是声音(某人说话时手机中的声音)、图片(花朵或猫的照片)、文字(报纸文章或小说的文本)、行为(某人喜欢什么活动)、偏好(某人喜欢的音乐或电影)等任何我们能够收集并用数字描述的东西。

我们的目标是发现有意义的信息,至于什么是有意义的,由我们自己决定。我们通常希望找到能够帮助我们理解数据的模式,或者用过去的测量值预测未来的事件。例如,我们可能想根据某人已经评分的电影来预测他们会喜欢什么电影,或者识别一个便条上的手写字,或者仅凭几句音符就能辨认出一首歌。

我们通常通过三个步骤找到我们需要的信息:我们确定想要寻找的信息,收集我们希望包含这些信息的数据,然后设计并运行算法,从这些数据中提取尽可能多的信息。

在本章中,我们将介绍机器学习中的一些主要发展。我们将首先讨论一种早期的机器学习方法——专家系统。接下来,我们将讨论三种主要的学习方法:监督学习、无监督学习和强化学习。最后,我们将讨论深度学习。

专家系统

在深度学习广泛应用之前,从数据中学习的一个流行方法是创建专家系统。这种方法至今仍在使用,它们是旨在封装人类专家(如医生、工程师甚至音乐家)思维过程的计算机程序。其思路是研究人类专家的工作,观察他们的行为和方式,并可能让他们描述自己的过程。我们通过一组规则来捕捉这种思维和行为。希望计算机能够仅仅通过遵循这些规则来完成专家的工作。

这种类型的系统一旦建立起来就能运行得很好,但它们的创建和维护非常困难。值得花点时间了解其中的原因。问题在于,产生规则的关键步骤,称为特征工程,可能需要不切实际的人工干预和创造力。深度学习成功的部分原因就是它通过算法方式创建规则,正好解决了这个问题。

让我们通过一个实际例子来说明专家系统面临的问题:识别数字。假设我们想教计算机识别数字 7。通过与人们交谈并提问,我们可能会提出三条小规则,帮助我们将 7 与其他数字区分开:首先,7 的顶部有一条大致水*的线;其次,它有一条大致的东北—西南方向的对角线;第三,这两条线在右上角相交。这些规则如图 1-1 所示。

直到我们遇到像图 1-2 中的 7,之前的规则可能才会有些效果。

f01001

图 1-1:顶部:手写的 7。底部:一组用于区分手写 7 与其他数字的规则。

f01002

图 1-2:由于多出的横线,规则无法识别为图 1-1 中的 7。

我们的规则集无法将这个识别为 7,因为我们最初没有考虑到有些人会在对角线的中间加上一条横线。所以现在我们需要为这种特殊情况添加一个新规则。在实际操作中,这种问题在任何开发专家系统的人身上都会反复发生。在任何复杂的问题中,找到一套好的完整规则常常是一个非常艰巨的任务。将人类的专业知识转化为一系列明确的指令,往往意味着要艰难地揭示人们在不自觉的情况下做出的推论和决策,将这些转化为大量的指令,然后调整和手动调优这些指令,以覆盖最初被忽视的所有情况,调试那些相互矛盾的规则,等等,这一系列任务似乎永无止境,并且处理的是一大堆复杂的规则。

找到完成任务的规则这一过程是艰苦的工作:人类专家遵循的规则往往不是明确的,正如我们所看到的,很容易忽视例外情况和特殊情形。试想要找到一套完整的规则,能够模仿放射科医生判断 MRI 图像上的污点是否良性的思维过程,或者空中交通管制员如何处理繁忙的航班调度,抑或一个人在极端天气条件下如何安全地驾驶汽车。更复杂的是,围绕人类活动的技术、法律和社会习惯不断变化,这要求我们不断地监控、更新并修复这些错综复杂的规则网络。

基于规则的专家系统在某些情况下是可以工作的,但设计一套合适的规则,确保它们能在各种数据下正常工作,并保持更新,这些困难使得它们作为一种通用解决方案变得不切实际。

如果我们能找到并管理这一系列规则,那么计算机就可以模仿某些形式的人类决策过程。这正是深度学习的核心所在。这些算法,在有足够的训练数据的情况下,可以自动发现决策规则。我们不需要明确告诉算法如何识别数字 2 或 7,因为系统自己会弄明白。它可以判断一个 MRI 的模糊区域是否是良性的,判断手机照片是否曝光适当,或者判断一段文字是否真的是某个历史人物所写。这些都是深度学习已经在为我们执行的许多应用之一。

计算机通过检查输入数据并提取模式来发现决策规则。系统从不“理解”它所做的事情,像人类那样。它没有常识、意识或理解力。它只是衡量训练数据中的模式,然后使用这些模式来评估新数据,基于它所训练的示例做出决策或结果。

一般来说,根据我们拥有的数据和希望计算机为我们提供的输出,我们会以三种不同的方式训练深度学习算法。让我们简要地了解一下这些方法。

监督学习

我们首先考虑监督学习。这里,supervised一词是“已标注”的同义词。在监督学习中,我们通常会给计算机提供一对对的值:一个来自数据集的项,以及我们为该项分配的标签。

例如,我们可能正在训练一个叫做图像分类器的系统,目标是让它告诉我们照片中最突出的是哪个物体。为了训练这个系统,我们会给它一组图像,并为每个图像附上一个标签,描述最显著的物体。所以,举个例子,我们可能会给计算机一张老虎的图片,并附上标签老虎

这一理念可以扩展到任何类型的输入。假设我们有一些做过的菜谱,记录了我们每道菜的喜好程度。在这种情况下,菜谱就是输入,而我们对它的评分则是该菜谱的标签。在对所有这些菜谱进行训练后,我们可以给我们的训练系统一个新的菜谱,它就能预测我们会多喜欢吃这个菜。一般来说,我们训练系统的能力越强(通常是通过提供更多的训练数据),它的预测结果就会越准确。

无论数据类型如何,通过给计算机提供大量的输入和标签对,针对任务设计的成功系统将逐渐从输入中发现足够的规则或模式,进而能够正确地预测每个提供的标签。也就是说,通过这种训练,系统学会了在每个输入中应该测量什么,以便能够识别它应该返回的学习标签。当它频繁地得到正确答案以满足我们的需求时,我们称系统已经被训练过。

请记住,计算机并不理解食谱是什么,或者食物的味道如何。它仅仅是利用输入中的数据,按照它在训练过程中学到的规则,找到最接*的匹配标签。

图 1-3 展示了将四张照片输入到训练过的图像分类器后的结果。

这些照片是从网络上找到的,系统以前从未见过它们。针对每张图片,分类器会告诉我们它训练时识别的 1,000 个标签中的每一个的可能性。这里我们展示了每张照片的前五个预测标签及其相关的概率。

图 1-3 左上角的图片是一串香蕉,因此理想情况下,我们希望返回一个像“香蕉串”这样的标签。但这个特定的分类器没有针对标注为“香蕉串”的任何图像进行训练。该算法只能返回它训练时使用的标签之一,就像我们只能用我们知道的词汇来识别物体一样。它从训练过的标签中找到的最接*的匹配是“香蕉”,所以它返回了这个标签。

f01003

图 1-3:四张图像及其预测标签和概率,来自深度学习分类器

在左上角,计算机对香蕉标签的信心非常高。在右下角,计算机对耳朵标签的信心大约是 60%。但它对玉米的信心大约是 40%。如果我们遵循常见的做法,每张图像只返回一个标签给用户,那么同时返回计算机对标签是否正确的信心值也会很有帮助。如果信心值不令人放心,比如耳朵的信心只有大约 60%,我们可能决定使用不同的算法重新尝试,或者甚至请求人工帮助。

无监督学习

当我们的数据没有标签时,我们使用一种被统称为无监督学习的技术。这些算法学习的是输入元素之间的关系,而不是每个输入与标签之间的关系。

无监督学习经常用于聚类或者将我们认为相关的数据片段分组。例如,假设我们在挖掘新房子的基础时,发现地面上布满了旧的陶罐和花瓶。我们打电话给一位考古学家朋友,他意识到我们发现了一堆混乱的古代陶器,显然来自许多不同的地方,甚至可能是不同的时期。

考古学家无法识别任何标记和装饰,因此她无法确切地说出每个标记来自哪里。有些标记看起来像是同一主题的变体,但其他标记则像是不同的符号。为了解决问题,她对标记进行拓印,然后试图将它们分成组。但她需要分类的标记太多了,而且由于所有研究生都在进行其他项目,她转向一个机器学习算法,以自动将标记合理地分组在一起。

图 1-4 展示了她捕捉到的标记以及算法可能产生的分组。

f01004

图 1-4:使用聚类算法来组织陶罐上的标记。左图:陶罐上的标记。右图:标记按相似簇分组。

因为这种技术将我们的数据分成相关的组(或),所以我们称这个过程为聚类,并且我们将这种算法称为聚类算法

无监督学习算法也可以用来提高测量数据的质量(例如,去除手机相机拍摄的照片中的斑点)或者压缩数据集以便在我们的磁盘上占用更少的空间,而不会丢失我们关心的任何特性(例如 MP3 和 JPG 编码器为声音和图像所做的)。

强化学习

有时我们想训练计算机学习如何执行一个任务,但我们甚至自己都不知道最佳方法。也许我们在玩一个复杂的游戏或写一些音乐。下一步该采取什么最佳动作或者下一个最佳音符该选择什么?通常并没有单一的最佳答案。但我们也许能够大致说一个答案比另一个更好。能够训练计算机通过让它尝试可能的方法来找到朝着良好结果的最佳步骤将是很好的,我们只需要以非常一般的方式对其进行排名,比如“可能不错”,或者“比上一个好”。

例如,假设我们负责设计新办公楼电梯的运行,就像图 1-5 中所示。我们的工作是决定电梯车在不需要时应该等待在哪里,以及哪辆车应该在有人按下呼叫按钮时响应。假设我们的目标是尽量减少所有乘客的*均等待时间。

f01005

图 1-5:我们希望找到这些电梯的最佳时间表。

我们该怎么做呢?我们能想到的任何解决方案的质量将完全依赖于人们想要出行的时间和地点的模式。也许在早上,每个人都在上班,所以我们应该总是将空车送到一楼,准备好迎接新到的人。但也许在午餐时间,每个人都想出去,所以我们应该把空闲的车停在高层,随时准备下来接人到一楼。但如果下雨了,也许大多数人反而会想去顶楼的餐厅。一天又一天,一小时又一小时,最好的策略是什么?

可能没有单一的最佳策略,因此计算机无法学会给我们提供它。我们能做的只是随着时间的推移尝试不同的方法,选择那个似乎能给我们带来最佳结果的策略。所以我们会让计算机发明一个策略,或者对现有的策略进行变更,看看它的表现如何。然后,我们根据乘客的*均等待时间给它打分。在尝试了许多变化后,我们可以选择那个得分最高的策略。随着时间的推移,随着模式的变化,我们可以尝试新的方法,不断搜索,但保持得分最好的时间表。

这是一个强化学习 (RL) 的例子。强化学习技术是最*击败人类大师的游戏算法的核心,例如围棋,甚至像星际争霸这样的在线战略游戏。

深度学习

深度学习这个词指的是使用一系列计算步骤或的机器学习算法(Bishop 2006;Goodfellow, Bengio, 和 Courville 2017)。我们可以以任何我们喜欢的方式在页面上绘制这些层,只要结构清晰。如果我们将这些层竖直绘制,我们可以想象从底部往上看,说系统很高,或者从顶部往下看,说它很深。如果我们将许多层水*绘制,我们可能会说系统是宽的。没有特别的原因,"深度"这个词最终流行了,并且赋予了整个领域“深度学习”的名称。

需要记住的是,这些系统之所以被称为“深度”,仅仅是因为它们在我们竖直堆叠时的外观。它们并不具备深刻的理解或透彻的洞察力。当一个深度学习系统在照片中为一张脸贴上名字时,它并不知道什么是脸,什么是人,甚至不知道人类的存在。计算机只是测量像素,并且利用它从训练数据中学到的模式,产生最可能的标签。

让我们跳过许多章节,快速看一下一个深度网络,如图 1-6 所示。在这个简单的网络中,我们从四个输入数字开始,图底部显示了这些数字。这些数字可能是一个 2x2 灰度图像中四个像素的值,四个连续天数的股票收盘价,或者是某段语音数据的四个样本。每个输入值只是一个浮动的数字,如-2.982 或 3.1142\。

f01006

图 1-6:一个简单的深度神经网络

这四个值在图中向上进入一个,或者叫做一组三个人工神经元。尽管它们的名字中有“神经元”这个词,且灵感源自于真实的神经元,但这些人工神经元极其简单。我们将在第十三章详细介绍它们,但最好把它们看作是执行小型计算的单元,远远不如真实神经元复杂。

在这个图示中,第一层的每个神经元接收四个起始数字中的每一个作为输入。请注意,图中每一条将输入值传入神经元的 12 条线都有一个小点。在这个图中,每个点代表了这样一个概念:沿着这条线传递的输入值在到达神经元之前,会被乘以另一个数字,称为权重。这些权重对于网络至关重要,我们稍后会详细讨论它们。

第一层中每个人工神经元的输出是一个新的数字。在图 1-6 中,这些输出被输入到第二层的每个神经元中,同样,每个值在传递过程中都会被乘以一个权重。最后,第二层的两个神经元产生的值就是网络的输出。我们可能将这些输出值解释为输入属于两个类别中的每一个的概率,或者说这段语音片段是由哪个人说的,或者是未来两天的股票预测价格。

每个表示人工神经元的大圆圈将其输入值转换为一个数字。这些计算是固定的:一旦我们设置好网络,每个神经元在给定输入下总是会计算出相同的输出。所以,一旦我们选择了人工神经元并将它们安排成图 1-6 中的网络,几乎所有东西都已经被指定好了。

在图 1-6 中,唯一能改变的东西是输入和权重。这是使得网络能够训练的关键洞察。权重一开始是随机数字。这意味着网络的输出最初会是无意义的,我们永远不会得到我们想要的结果(除非我们偶尔运气好)。

为了让网络可靠地生成我们想要的答案,我们在每次错误输出后,都会小心地调整权重,逐步改变,以使网络更有可能生成期望的输出值。如果我们这样小心操作,随着时间的推移,输出值将逐渐接*我们想要的结果。最终,如果我们完成得好,网络将能够对我们训练数据库中的几乎所有数据产生正确的答案,然后我们可以将网络发布到网络上,或将其作为产品或服务提供。

简而言之,训练这个网络,或者说是教它,实际上就是为权重找到值,使得每个输入都能产生期望的输出。令人惊讶的是,这就是全部!即使我们的网络发展到拥有数百层、许多种类、成千上万个人工神经元和数百万个权重,学习通常也只是逐渐改变权重,直到我们得到想要的答案。更复杂的网络可能还会学习其他一些值,但权重始终是至关重要的。

这个过程的一个美妙之处在于,它实现了特征工程的承诺。例如,考虑一个接受照片作为输入并告诉我们图片中是什么犬种的系统。当该系统的训练过程完成且权重稳定在最佳值时,这些权重的效果是将神经元转变为小的特征检测器。例如,早期层的一个神经元可能会在“看到”眼睛时产生一个较大的值,另一个则是在“看到”下垂的耳朵时产生一个值(我们将在第十六章中看到具体是如何实现的)。然后,后续的神经元可能会寻找这些特征的组合,比如蓬松的尾巴加上短腿,或者黑色的眼睛加上长鼻子和大身体,以帮助判断犬种。简而言之,神经元在寻找特征,尽管我们从未明确引导它们这样做。特征检测只是训练权重以产生正确答案的自然结果。

所以,虽然手动构建一个像放射科医生一样的专家系统几乎是不可能的任务,但创建一个复杂的深度网络并成功训练它,可以自动实现这个目标。系统会找到将每张图片的像素值组合成特征的方式,然后使用这些特征来判断该图片是否显示了健康的组织(Saba 等,2019)。

总结

在本章中,我们对深度学习有了一个总体的了解。我们从专家系统开始,这些系统在实践中需要过多的人工工作,难以成功。我们看到,训练一个深度学习系统通常遵循三种方法之一。监督学习意味着我们为每个数据提供一个标签,以便我们可以训练系统预测新数据的正确标签。无监督学习意味着我们只给系统提供数据,没有标签,因此我们训练系统将数据聚类成相似的组。强化学习意味着我们对计算机提出的各种建议进行评分,期望它最终能提出一个足够好的解决方案。

然后,我们看了一个真实但微小的深度学习系统。其基本结构将人工神经元组织成层级。每一层的神经元与前一层和后一层的神经元进行通信。正是这种结构的形状(当我们将其绘制成这样的层塔状)赋予了深度学习这个名字。

最后,我们看到了权重的重要性,或者说是那些在到达人工神经元输入之前,乘以每个数字的值。当我们教导一个系统,或者我们说它在学习时,我们通常做的就是调整这些权重。当权重找到足够好的值时,系统就能够完成我们要求的任务。

接下来的几章将深入探讨我们设计和构建深度学习系统所需的背景知识。

第二章:基础统计学

我们对数据的理解越深入,就能设计出更好的深度学习系统,从而最大化地利用这些数据。

通过研究和分析我们所开始的数据,我们可以选择最佳的算法来从中学习。让我们能够进行这种分析的思想和工具通常被归纳为统计学。统计学的概念和语言在机器学习中随处可见,从论文和源代码注释到库文档。

在本章中,我们将涵盖进行深度学习时所需的关键统计学概念,而不深入探讨数学或细节。这些概念大致分为两类。第一类涉及随机数,以及如何以对机器学习最有价值的方式描述它们。第二类涉及如何从一个集合中选择对象(如数字),以及如何衡量这些选择如何代表整个集合。我们在这里的目标是发展足够的理解和直觉,以帮助我们在进行机器学习时做出正确的决策。

如果你已经熟悉统计学和随机值,至少快速浏览一下本章。这样你将了解我们在本书中使用的语言,并且如果你想复习时可以知道哪里需要回头查看。

描述随机性

随机数在许多机器学习算法中起着重要作用。我们使用它们来初始化系统、控制学习过程中的步骤,有时甚至影响输出。正确选择随机数非常重要:这样做可能意味着一个系统能够从我们的数据中学习并产生有用的结果,或者一个系统顽固地拒绝学习任何东西。我们不会仅仅凭空挑选一些任意的数字,而是使用各种工具来控制我们想要使用哪些类型的数字,以及如何选择它们。

我们通常在给定的最小值和最大值之间选择一个随机数,比如当有人让我们“从 1 到 10 中选择一个数字”时。在这个例子中,意味着我们的选择限制在有限的选项范围内(从 1 到 10 的整数)。我们还经常使用实数,这些数字可能位于整数之间。从 1 到 10 之间有 10 个整数(包括端点),但是在这个范围内有无限多个实数。

当我们讨论数字集合时,无论是随机的还是其他类型的,我们通常还会谈到它们的*均值。这是一个快速描述数值集合的有用方法。有三种常见的计算*均值的方法,它们经常出现,因此我们将在这里标出它们。作为一个示例,我们使用五个数字的列表:1, 3, 4, 4, 13。

均值是我们在日常语言中所说的*均数。它是所有条目之和除以列表中的条目数量。在我们的例子中,将所有列表元素加起来得到 1 + 3 + 4 + 4 + 13 = 25。共有五个元素,所以均值是 25 / 5,或者 5。

众数是列表中出现次数最多的值。在我们的例子中,4 出现了两次,而其他三个值各出现一次,所以 4 是众数。如果没有任何值比其他值出现得更多,我们就说列表没有众数。

最后,中位数是当我们将列表中的值从小到大排序后,位于中间的那个数。在我们的列表中,已排序的列表为 1、3、4、4、13,1 和 3 构成左边部分,4 和 13 构成右边部分,另一个 4 位于中间。因此,4 是中位数。如果列表包含偶数个条目,那么中位数是两个中间条目的*均值。例如,对于列表 1、3、4、8,中位数是 3 和 4 的*均值,即 3.5。

*均值很有用,但它并不能告诉我们集合中数字的分布情况。例如,数字可能在整个范围内均匀分布,也可能分成一个或多个聚类。接下来,我们将探讨描述数字分布情况的技术。

随机变量和概率分布

在深入细节之前,让我们通过一个类比来培养直觉。假设我们是摄影师,被指派拍摄关于汽车废品场的文章,任务是拍摄大量的报废卡车和汽车。出于冒险的心态,我们去了一个有很多报废车辆的废品场。我们与老板交谈,达成一致,最好的拍摄方式是由我们付钱,让她把车一辆一辆地送来拍照。她通过在办公室里使用一个老式的嘉年华轮盘让过程更加有趣。轮盘上每个车位大小相等,正如图 2-1 所示。车位和车辆都从 1 开始编号。

F02001

图 2-1:废品场老板的嘉年华轮盘。每个大小相等的扇区代表她场地中的一辆车。

一旦我们付钱,她就会旋转轮盘。当轮盘停止时,她记录下顶部的数字,然后带着拖车出去,把对应编号的车辆拖回来。我们拍照后,她将车辆送回废品场。如果我们想拍摄另一辆车,就再次付费,她再旋转轮盘,过程就这样重复。

假设我们的任务是拍摄五种不同类型的车:轿车、皮卡、厢式车、SUV 和旅行车。对于每种车,我们希望知道她旋转轮盘时,我们得到该类型车辆的概率。为了计算这一点,假设我们进入车场,检查每辆车,并将其分配到这五个类别中的一个。我们的结果如图 2-2 所示。

F02002

图 2-2:我们的废品场中有五种不同类型的车。每个条形显示了我们拥有的该类型车的数量。

在她的车场上,几乎有 950 辆车,其中最大数量的是迷你面包车,其次是皮卡车、旅行车、轿车和 SUV,按此顺序排列。由于车场上的每辆车被选中的机会是均等的,因此在每次转动轮盘时,我们最有可能选中迷你面包车。

但具体来说,获得迷你面包车的可能性有多大?为了确定获得每种类型车辆的可能性,我们可以将图 2-2 中每个条形的高度除以车辆的总数。这个数值给出了我们获得某种类型汽车的概率,如图 2-3 所示。

F02003

图 2-3:获得废品场中每种类型汽车的概率

要将图 2-3 中的数值转换为百分比,我们将它们乘以 100。例如,迷你面包车条形的高度约为 0.34,因此我们说获得迷你面包车的概率是 34%。我们说每个条形的高度是我们获得那种类型车辆的概率。如果我们将所有五个条形的高度相加,我们会发现总和是 1.0。这说明了将任何数字列表转化为概率的规则:数值必须介于 0 和 1 之间,且总和为 1。

我们将图 2-3 称为概率分布,因为它将获得某种车辆的 100%概率分配到所有可选项中。我们有时也说图 2-3 是图 2-2 的归一化版本,这意味着所有数值相加的结果是 1。

我们可以使用我们的概率分布来绘制一个简化版的嘉年华轮盘,如图 2-4 所示。

F02004

图 2-4:一个简化版的嘉年华轮盘,告诉我们如果车主转动图 2-1 的大轮盘,我们会获得哪种类型的车辆

轮盘的指针停在某个区域的概率由该区域所占轮盘周长的比例决定,这个比例与图 2-3 中显示的比例相同。

大多数时候,在我们生成计算机上的随机数时并没有嘉年华轮盘。相反,我们依赖软件来模拟这一过程。例如,我们可能将一个数值列表(如图 2-3 中条形的高度)传递给一个库函数,并要求它返回一个值。我们期望大约 34%的时间会返回迷你面包车,大约 26%的时间会返回皮卡车,依此类推。

从一系列具有各自概率的选项中随机选一个值的工作需要一些操作。为了方便,我们将这个选择过程封装成一个概念化的过程,称为随机变量

这个术语可能会让程序员感到困惑,因为程序员通常将变量视为一个有名称的存储单元,可以用来存储数据。在这个上下文中,随机变量不是一个存储单元,而是一个函数(维基百科 2017b),它接受一个概率分布作为输入,并产生一个单一的值作为输出。从分布中选择一个值的过程叫做从随机变量中抽取一个值。

我们称图 2-3 为概率分布,但我们也可以将其视为一个函数。我们调用该函数,它会根据这些概率返回某一种类型的车辆。这个概念引出了分布的两个更正式的名称。当只有有限数量的可能返回值时,比如图 2-3 中的五个值,我们有时会使用斜体名称概率质量函数pmf(这个缩写通常写为小写)。pmf 有时也被称为离散概率分布(在这个术语末尾加上函数是可选的)。这些术语旨在提醒我们,只有固定数量的可能输出。

我们可以轻松创建连续的概率分布。当我们初始化神经网络中的值时,我们使用这些函数的*似值。做个类比,假设我们想知道废车场商人带给我们的每辆车剩下多少油。油量是一个连续变量,因为它可以取任何实数。图 2-5 展示了我们的油量测量的连续图表。这个图表告诉我们,不仅仅是几个特定值的概率,而是任何实数值的概率,这些值位于 0(空)和 1(满)之间。

F02005

图 2-5:连续范围值的概率分布

如图 2-5 所示的分布被称为连续概率分布(或cpd),或概率密度函数(或pdf)。有时,人们会随意使用概率密度函数或 pdf 来指代离散分布,而不是连续分布。通常情况下,上下文会清楚地表明是指哪种解释。

回想一下,对于离散情况,所有可能的返回值需要加起来为 1。在连续情况下,如图 2-5 所示,这意味着曲线下方的面积为 1。

大多数时候,我们通过选择一个分布,然后调用一个库函数来从该分布中生成一个值,从而获得随机数(即,从我们给定的分布中抽取一个随机变量)。当我们需要时,可以自己创建分布,但大多数库提供了一些已经被证明适用于大多数情况的分布。这样,我们只需在选择随机数时使用这些预构建的分布之一。让我们来看一下这些分布。

一些常见的分布

我们之前提到过,可以从一个分布中抽取一个随机变量。每次抽取随机变量时,它会根据分布的规律取一个数值:在分布中对应值较大的数字比对应值较小的数字更可能出现。这使得分布具有很大的实际价值,因为不同的算法会希望使用具有特定概率值的随机变量。为此,我们只需要选择一个合适的分布。

连续分布

以下大多数分布都是由主要库提供的内置例程,因此它们易于指定和使用。为了简便起见,我们将演示以下两个分布的连续形式。大多数库为我们提供了连续版本和离散版本的选择,或者它们可能提供一个通用例程,根据需要将任何连续分布转换为离散分布,反之亦然。我们将在后面的部分讨论一些离散分布。

均匀分布

图 2-6 显示了均匀分布。基本的均匀分布在 0 到 1 之间的取值为 1,其他地方的取值为 0。

在图 2-6 中,可能看起来在 0 和 1 处各有两个值,但实际上并没有。我们的约定是,开放圆圈(如下线所示)表示“该点不属于该线”,而闭合圆圈(如上线所示)表示“该点属于该线”。因此,在输入值为 0 和 1 时,图形的输出值为 1。这是一种常见的函数定义方式,但有些实现可能会将其中一个或两个输出值设为 0。因此,检查非常重要。

F02006

图 2-6:均匀分布的示例

这个分布有两个关键特征。首先,我们只能得到 0 到 1 之间的值,因为所有其他值的概率为 0。第二,在 0 到 1 的范围内,每个值的概率是相同的。我们得到 0.25、0.33 或 0.793718 的概率是一样的。我们说图 2-6 是均匀的,或常数的,或*坦的,在 0 到 1 的范围内,这三种说法都意味着该范围内的所有值的概率是相同的。我们还说它是有限的,意味着所有非零值都在某个特定的范围内(也就是说,我们可以确定 0 和 1 是它能返回的最小值和最大值)。

为我们创建均匀分布的库函数通常允许我们选择非零区域的起始点和结束点。除了默认的 0 到 1 范围外,最流行的选择可能是−1 到 1 的范围。库会处理诸如调整函数高度以确保面积始终为 1.0 等细节(回想一下,如果图形要表示一个概率分布,这是必需的)。

正态分布

另一个常用的分布是正态分布,也叫做高斯分布,或简单地称为钟形曲线。与均匀分布不同,它是*滑的,没有尖锐的角或突兀的跳跃。

图 2-7 展示了几个典型的正态分布。

图 2-7 中的所有四条曲线具有相同的基本形状。它们的形状之所以不同,是因为我们对曲线进行了缩放、水*移动,或两者同时进行。对于这些插图,我们没有对曲线进行缩放,使得曲线下面的面积之和为 1。

F02007

图 2-7:一些正态分布

图 2-8 展示了通过从每个分布中抽取值得到的一些代表性样本。它们在分布值高的地方聚集(即,在该值处抽到样本的概率较高),在分布值低的地方则较为稀疏(即,在该值处抽到样本的概率较低)。红点的垂直位置仅为了使样本更容易观察而进行了抖动,没有实际意义。

F02008

图 2-8:每个红色圆圈表示从其正态分布中抽取的一个样本的值。

正态分布在几乎所有地方都接* 0,除了它在某个区域*滑地上升形成凸起。尽管值在凸起两侧逐渐趋*于 0,但永远不会真正达到 0。因此我们说这个分布的宽度是无限的。实际上,我们通常将接* 0 的任何值视为 0,从而得到一个有限的分布。在讨论正态分布时,还会出现一些其他术语。由正态分布的随机变量产生的值有时称为正态偏差,并且说这些值是正态分布的。我们也说这些值符合或遵循正态分布。

每个正态分布由两个数字定义:均值标准差。均值告诉我们凸起中心的位置。图 2-9 展示了图 2-7 中的四个高斯分布及其均值。这是正态分布的许多优良性质之一:其均值也是中位数和众数。

F02009

图 2-9:正态分布的均值是凸起中心的位置,这里用红线表示。

标准差也是一个数字,通常用小写希腊字母σ(西格玛)表示,它告诉我们凸起的宽度。可以想象从凸起的中心开始,向外对称移动,直到包围了曲线下面约 68%的总面积。凸起中心到这些端点的距离叫做该曲线的一个标准差。图 2-10 展示了我们的四个高斯分布,其中一个标准差范围内的区域用绿色阴影表示。

我们可以使用标准差来表征一个凸起:当标准差较小时,意味着凸起较窄;当标准差增大时,凸起在水*方向上变得更加宽广。

F02010

图 2-10:一些正态分布,其中一个标准差内的区域用绿色阴影标出

如果我们从中心对称地再往外走一个标准差(也就是相同的距离),那么我们就涵盖了大约 95%的曲线下方的区域,如图 2-11 所示。再往外走一个标准差,我们就涵盖了大约 99.7%的曲线下方的区域,这也在图 2-11 中展示了。这一特性有时被称为三西格玛规则,因为标准差使用的是符号σ。它有时也被称为68-95-99.7 规则,这个名字听起来也很有吸引力。

F02011

图 2-11:三西格玛规则,或 68-95-99.7 规则

假设我们从任何正态分布中抽取 1000 个样本。我们会发现,大约 680 个样本的值距离该分布的均值不超过一个标准差,即在范围−σ到σ之间;大约 950 个样本的值在两个标准差范围内,即在−2σ到 2σ之间;大约 997 个样本的值在三个标准差范围内,即在−3σ到 3σ之间。

总结一下,均值告诉我们曲线的中心在哪里,而标准差告诉我们曲线的分布范围有多广。标准差越大,曲线就越宽,因为 68%的临界值离中心越远。

有时人们会使用一个不同但相关的值,称为方差,而不是标准差。方差就是标准差的*方(即标准差乘以自身)。这个值在计算中有时更为方便。尽管如此,它的一般解释是相同的:方差较大的曲线比方差较小的曲线更为分散。

正态分布在机器学习和其他领域中经常出现,因为它自然地描述了许多现实世界的观察结果。如果我们测量某一地区成年公马的身高,或是向日葵的大小,或者果蝇的寿命,我们会发现这些数据往往呈现出正态分布的形态。

离散分布

现在让我们看一下两个离散分布。

伯努利分布

一个有用但特殊的离散分布是伯努利分布(发音为 ber-noo′-lee)。该分布只返回两个可能的值:0 和 1\。伯努利分布的常见例子是掷硬币时获得正面或反面的概率。我们通常用字母 p 来描述得到 1(假设是正面)的概率。如果忽略硬币侧立等特殊情况,正反面的概率之和必须等于 1,这意味着得到 0(或反面)的概率是 1 − p。图 2-12 以图形方式展示了公*硬币和加权硬币的概率。由于我们只有两个值,所以我们可以将伯努利分布绘制为柱状图,而不是我们在连续分布中看到的线条和曲线。

使用分布语言来处理这么简单的情况可能看起来有些过度,但它的好处在于我们可以与从分布中生成值的库例程一起使用它。我们可以将一个均匀分布、正态分布或伯努利分布传递给我们的例程,它将根据分布的概率返回一个从该分布中抽取的值。这使得编程变得更加容易。

F02012

图 2-12:伯努利分布告诉我们抽取 0 或 1 的概率。左侧:公*硬币。右侧:不公*硬币。

多项伯努利分布

伯努利分布只返回两种可能的值。但假设我们正在进行一个实验,实验的结果可以是多个可能性中的任何一种。例如,除了抛硬币只会出现正面或反面,我们可能会掷一个 20 面的骰子,它可以显示 20 个不同的值中的任何一个。

为了模拟掷骰子的结果,我们的随机变量可以返回一个从 1 到 20 之间的整数。但在其他情况下,拥有一个可能值列表也很有用,其中除了我们抽取的条目,其余所有条目的概率值都是 0,而抽中的条目的概率是 1\。这样的列表在我们构建机器学习系统时非常有用,可以用来将输入分类为不同的类别——例如,描述照片中出现的是 10 种不同动物中的哪一种。

假设我们有一张鳄鱼的照片,并且这是我们列表中的第五个条目。如果我们的算法无法确定图像是什么,我们可能会得到类似于图 2-13 左侧的结果,其中识别出了三种动物作为可能性。我们希望系统能够输出右侧的结果,其中除了鳄鱼的值为 1 外,其他每个条目的值都为 0\。

以这种方式表示从列表中选择一个单一选项是训练具有两个以上类别的分类器的关键步骤。我们将在第六章中回到这个想法,它是一个名为交叉熵的概念的组成部分。

因为图 2-13 中的每个分布都是将二项分布的两个结果推广到多个结果的泛化,我们可以称之为“多项伯努利分布”,但我们将这些词组合在一起,创造了一个新词,称之为多项伯努利分布(有时也叫做较为*淡的分类分布)。

F02013

图 2-13:左:鳄鱼图片的可能预测概率。右:我们期望的概率。

随机值的集合

我们已经看到如何从分布中生成随机值。我们通过该分布的概率来抽取值,从而确定哪些返回值比其他值更可能被选中。

当我们从一个或多个随机变量中抽取了大量值时,通常需要对这些值进行描述,以便我们可以将其作为一个整体来讨论。让我们来看三个在机器学习中常出现的相关概念。

期望值

如果我们从任何一个概率分布中选取一个值,然后再选取另一个,再选取另一个,随着时间的推移,我们会积累一长串的值。

如果这些值是数字,那么它们的均值称为期望值。这对于许多应用来说是有用的信息。举个简单的例子,我们可能需要在-1 到 1 之间生成随机数,且正负值大致相等。如果随机变量的期望值为 0,那么我们就知道我们得到了一个*衡的值集。

请注意,期望值可能并不是从分布中抽取的值之一!例如,如果 1、3、5 和 7 是唯一的值,且它们的概率相等,那么我们用来从该列表中抽取值的随机变量的期望值为(1 + 3 + 5 + 7)/ 4 = 4,这个值在分布中是无法抽取到的。

依赖性

到目前为止,我们看到的随机变量彼此之间完全没有关联。当我们从某个分布中抽取一个值时,之前是否抽取过其他值并不重要。每次抽取新的随机变量时,都是一个全新的世界。

我们称这些为独立变量,因为它们之间没有任何依赖关系。这是最容易处理的随机变量类型,因为我们不需要担心如何管理两个或更多随机变量可能相互影响。

相比之下,存在依赖变量,它们之间相互依赖。例如,假设我们有几种动物的毛发长度分布:狗、猫、仓鼠等等。我们可能首先从动物列表中随机选择一种动物,然后使用该动物来选择适当的毛发长度分布。接着,我们从该分布中抽取一个值,以找到该动物的毛发长度。动物的选择不依赖于任何其他因素,因此它是一个独立变量。但毛发长度依赖于我们使用的分布,而该分布又取决于我们选择了哪种动物,因此毛发长度是一个依赖变量。

独立同分布变量

许多机器学习技术的数学和算法都是设计来处理从具有相同分布的随机变量中抽取的多个值,并且这些值彼此独立。也就是说,我们反复从同一个分布中抽取值,并且连续的值之间没有关系。实际上,一些算法要求我们以这种方式生成随机值,而其他算法则在这种方式下效果最好。

这一要求非常常见,以至于这些变量有一个专门的名称:i.i.d.,表示独立同分布(这个缩写不常见,因为通常会以小写字母书写,并且字母之间有点号)。例如,我们可能会看到某个库函数的参数这样描述:“确保连续的输入是 i.i.d.”。

同分布这个词只是简洁地表达“从相同的分布中选择”的意思。

抽样与替换

在机器学习中,通常会通过随机选择现有数据集中的一些元素来构建新的数据集。我们将在下一节中进行这种操作,寻找一组样本的均值。让我们看两种方法,如何从一个起始对象池中选择并创建一个选择列表。

带替换的选择

让我们先来看一种方法,其中我们对每个选中的项目进行复制,这样原始项目保持不变,如图 2-14 所示。我们称这种方法为带替换的选择,或者SWR,因为我们可以把它理解为移除物体,制作副本并替换原物。

f02014

图 2-14:带替换的选择

带替换选择的一个含义是我们可能会多次得到相同的物体。在极端情况下,我们的新数据集可能完全由相同物体的多个副本组成。第二个含义是我们可以创建一个比原始数据集小、新数据集与原始数据集相同大小,甚至更大的数据集。由于原始数据集从未被更改,我们可以继续选择元素,直到我们满意为止。

这一过程的统计含义是我们的选择是相互独立的。没有历史记录,所以我们的选择完全不受之前选择的影响,也不影响未来的选择。为了说明这一点,请注意,图 2-14 中的池(或起始数据集)始终包含八个物体,因此每个物体被选中的几率是 1/8。在图中,我们首先选择了元素 C。现在我们新数据集里包含了 C 元素,但在选择后我们将该元素重新放回了池中。当我们再次查看池时,所有八个项目依然存在,如果我们再次选择,每个项目依旧有 1/8 的机会被选中。

一个日常的放回抽样*似是去一个备货充足的咖啡店点单。如果我们点了一杯香草拿铁,它并不会从菜单中被移除,而是继续供下一个顾客选择。

不放回抽样

另一种随机选择新数据集的方法是从原始数据集中移除我们的选择,并将其放入新数据集中。我们不进行复制,因此原始数据集就失去了一个元素。这种方法叫做不放回抽样,或SWOR,如图 2-15 所示。

一个日常的非放回抽样例子是玩扑克牌等卡牌游戏。每次发牌时,卡片会被从牌堆中移除,不能再被发出(直到牌被收集并洗牌)。

F02015

图 2-15:不放回抽样

让我们将不放回抽样(SWOR)与放回抽样(SWR)的含义进行比较。首先,在不放回抽样中,任何物品不能被选中超过一次,因为我们将其从原始数据集中移除。其次,我们的新数据集可能比原始数据集小,或者大小相同,但绝不会更大。第三,我们的选择现在是相互依赖的。在图 2-15 中,每个元素最初有相同的 1/8 的机会被第一次选中。当我们选择了 C 项时,它并未被放回。当我们进行下一次选择时,只有七个元素可供选择,每个元素现在有 1/7 的机会被选中。选择其中任何一个元素的几率增加了,因为竞争选择的元素减少了。如果我们再选择一个项目,其余的元素每个有 1/6 的机会被选中,依此类推。在我们选择了七个项目后,最后一个项目在下一次选择中有 100%的概率被选中。

继续比较,假设我们想要创建一个比原始数据池更小的数据集。我们可以选择有放回抽样或无放回抽样。但是有放回抽样可以生成比无放回抽样更多的可能新集合。为了说明这一点,假设我们在原始数据池中只有三个物体(我们称它们为 A、B 和 C),并且我们想要一个包含两个物体的新集合。无放回抽样只能给我们三个可能的新集合:(A,B)、(A,C) 和 (B,C)。有放回抽样则给我们这三个集合,还包括(A,A)、(B,B) 和 (C,C)。一般来说,有放回抽样总是能给我们更多可能的新集合。

自助法

让我们来看一个刚才提到的无放回和有放回抽样算法的实用应用。

有时候我们想了解一些数据集的统计信息,而这些数据集太大,以至于我们无法在实践中处理。例如,假设我们想知道当前全球所有人的*均身高。实际上,根本没有办法测量每个人。通常,我们会通过提取数据集中的一个具有代表性的部分,然后进行测量来回答这类问题。我们可能会找出几千个人的身高,并希望这些测量值的*均值接*于我们如果能够测量每个人时得到的*均值。

假设我们把全世界的每个人都看作我们的总体。由于人数太多,无法直接操作,我们将收集一个合理规模的群体,期望它能够代表这个总体。我们称这个较小的群体为样本集。我们将在没有放回的情况下构建这个样本集,因此每次从总体中选择一个值(即一个人的身高)时,它会从总体中移除,放入样本集中,并且不能再被选择。

我们希望通过仔细构建我们的样本集,使其在我们想要衡量的属性上能够合理地代表整个总体。图 2-16 展示了一个包含 21 个圆圈数字的总体的构思。样本集包含了来自该总体的 12 个元素。

F02016

图 2-16:通过无放回抽样从总体中创建样本集

现在我们将测量样本集的*均值,并将其作为总体*均值的估计。在这个小例子中,我们可以计算出总体的*均值,约为 4.3。而我们的样本集的*均值约为 3.8。虽然这两者的匹配不是很完美,但也并不算非常偏差。

大多数时候我们无法测量总体(这也是我们首先要建立样本集的原因)。通过计算样本集的均值,我们得到了一个*似值,但这个值有多准确呢?我们应该依赖这个值作为对整个总体的良好估计吗?很难说。如果我们能将结果表示为置信区间,情况就会更好。这让我们能够做出类似“我们有 98%的把握,总体均值在 3.1 到 4.5 之间”的陈述。为了做出这样的陈述,我们需要知道范围的上下界(在这里是 3.1 和 4.5),并且知道我们有多大的信心认为值位于该范围内(在这里是 98%)。通常,我们会根据任务需求选择所需的置信度,然后从中找出相应范围的上下值。

我们希望能够对均值或我们感兴趣的任何统计量做出类似的陈述。我们可以通过自助法(Efron 和 Tibshirani 1993; Teknomo 2015)来实现这一点,该方法包括两个基本步骤。第一个步骤是我们在图 2-16 中看到的步骤,即使用不放回抽样从原始总体中创建一个样本集。第二个步骤是对样本集进行重抽样,生成新的样本集,这次使用放回抽样。每个新的样本集都叫做一个自助抽样。这些自助抽样是我们构建置信度声明的关键。

要创建一个自助抽样,我们首先决定从起始样本集中选取多少个元素。我们可以选择任何数量,最多为样本集中的元素数量,尽管我们通常只选取较少的元素。一旦我们确定了选取的数量,就会从样本集中随机抽取这么多元素,且允许放回抽样,因此有可能会多次抽取到相同的元素。这个过程在图 2-17 中有所示。

F02017

图 2-17:使用放回抽样创建三个自助抽样并求出它们的均值

总结一下,我们从一个总体开始。我们通过不放回抽样从总体中抽取一个样本集。接着,我们使用放回抽样从这个样本集生成自助抽样。最后一步需要使用放回抽样,因为我们可能想要生成与样本集大小相同的自助抽样。在我们的示例中,我们可能希望我们的自助抽样包含 12 个值。如果不使用放回抽样,那么每个自助抽样将与样本集完全相同。

如果我们真的希望找到全世界每个人的*均身高,我们需要比 21 个测量值多得多的样本。让我们增加样本量,并大幅缩小它们的范围。为了方便起见,我们将聚焦于两个月大婴儿的身高。它们通常约为 500 厘米长,因此我们创建了一个模拟的 5,000 个测量值的种群,长度从 0 到 1,000 毫米(即 1 米,约 3.2 英尺)。从这个种群中,我们随机抽取了 500 个测量值作为样本集,然后创建了 1,000 个自助法样本,每个样本包含 20 个元素。图 2-18 展示了每个大约 100 个不同区间中,每个区间内具有均值的自助法样本的数量,这些区间的范围从 0 到 1,000(几乎没有均值低于 200 或高于 800)。像这样的图形几乎总是呈现*似的钟形曲线,因为自助法的特性使得更多的样本均值靠*真实均值,而远离真实均值的样本较少。

F02018

图 2-18:直方图显示了有多少自助样本的均值为给定值。大约 490 的蓝色条是样本集的均值。大约 500 的红色条是种群的均值。

由于我们是创建数据的,因此我们知道种群的均值是 500。我们的样本集的均值接*这个值,大约为 490。自助法的目的是帮助我们确定应该多大程度上信任这个 490 的值。虽然不深入数学计算,自助样本均值的*似钟形曲线已经能告诉我们所有需要知道的信息。假设我们想找到我们有 80%信心包含种群均值的区间,那么我们只需要切掉自助法样本值中最低和最高的 10%,留下中间的 80%(Brownlee 2017)。图 2-19 显示了一个框,正是做了这件事,封闭了我们 80%信心认为包含真实值的区间,我们知道真实值是 500。从图表中读取,我们现在可以说:“我们有 80%的信心,原始种群的均值在大约 410 到 560 之间。”

F02019

图 2-19:我们有 80%的信心,这个框包含了种群的均值。

自助法具有吸引力,因为我们通常可以使用小型自助样本,可能每个样本只有 10 个或 20 个元素,即使在具有数百万测量值的大型种群中也适用。由于每个自助样本都很小,构建和处理通常比较快。为了弥补样本小的不足,我们通常会创建数千个自助样本。我们构建的自助样本越多,结果就越像一个高斯分布的峰值,我们对置信区间的精确度也可以越高。

协方差与相关性

有时候,变量之间可能会相互关联。例如,一个变量可能告诉我们外面的温度,另一个变量则是降雪的几率。当温度非常高时,降雪的几率为零,因此了解其中一个变量可以告诉我们关于另一个变量的信息。在这种情况下,关系是负相关的:温度上升时,降雪几率下降,反之亦然。另一方面,我们的第二个变量可能告诉我们预计在本地湖泊中游泳的人数。温度和游泳人数之间的关系是正相关的,因为在较暖的日子里我们会看到更多的游泳者,反之亦然。

能够发现这些关系并衡量它们的强度是很有用的。例如,假设我们计划教授一个算法来从数据集中提取信息。如果我们发现数据中的两个值密切相关(例如温度和降雪几率),我们可能能够从数据中删除其中一个变量,因为它是冗余的。这可以提高我们的训练速度,甚至改善我们的结果。

在这一部分,我们将介绍一个叫做协方差的度量,它由数学家们开发,用来让我们确定这些关系的强度。我们还将看到一种变体,叫做相关性,它通常更有用,因为它不依赖于涉及的数字大小。

协方差

假设我们有两个变量,并且注意到它们之间存在特定的数值模式。当任何一个变量的值增加时,另一个变量增加该量的固定倍数,反之亦然。举个例子,假设变量 A 增加了 3,变量 B 增加了 6。然后,B 增加了 4,A 增加了 2。接着 A 减少了 4,B 减少了 8。在每个例子中,B 的增减都等于 A 增减的两倍,因此我们的固定倍数是 2。

如果我们看到这样的关系(对于任何倍数,而不仅仅是 2),我们说这两个变量协变。我们用一个叫做协方差的数字来衡量两个变量之间的关系强度,或者它们协变的一致性。如果我们发现当一个值增加或减少时,另一个值按可预测的量做出相同的变化,那么协方差是一个正数,我们说这两个变量表现出正协方差

传统上,讲解协方差的方式是绘制二维图点,如图 2-20 所示。在这里我们看到两组不同的协变点。每个点都有 x 和 y 坐标,但这些坐标仅仅是我们想要比较的两个变量的代表。y 的变化越一致地跟随 x 的变化,协方差就越强。

F02020

图 2-20:每个图示展示了具有正协方差的不同点集。

在图 2-20 的左侧,水*相邻的每一对点之间的 y 值变化大致相同。这是正协方差。在右侧,y 值的变化在每对点之间稍有不同,表示较弱的正协方差。非常强的正协方差告诉我们,这两个变量是一起变化的,因此每当其中一个变量按给定量变化时,另一个变量会以一致且可预测的量变化。

如果一个值在另一个值增加时减少,我们称这两个变量具有负协方差。图 2-21 展示了两组具有负协方差的点。

F02021

图 2-21:每个图示展示了一组具有负协方差的点。

如果两个变量没有这种一致匹配的变化,那么协方差为零。图 2-22 展示了一些例子。

F02022

图 2-22:这些数据点的每一组都具有零协方差。

我们对协方差的理解只捕捉到变量之间在它们的变化是彼此倍数关系时的关系。图 2-22 右侧的图表显示数据中可以有明显的模式(这里点的形状构成了一个圆的一部分),但协方差仍然为零,因为它们的关系如此不一致。

相关性

协方差是一个有用的概念,但它有一个问题。由于它的数学定义方式,它没有考虑到两个变量单位之间的关系,这使得我们很难比较不同协方差的强度。例如,假设我们测量了一打描述吉他的变量:木材的厚度、琴颈的长度、音符的共鸣时间、弦的张力等等。我们可能会找到这些测量值之间的协方差,但无法有意义地比较协方差的大小,以找出哪些对变量之间的关系最强或最弱。甚至尺度也很重要:如果我们找到一对测量值的协方差单位是厘米,而另一对测量值的协方差单位是英寸,我们无法比较这两个值,以确定哪一对的协方差更强。

协方差的符号是我们唯一能够了解的信息:正值表示正相关,负值表示负相关,零表示无关。仅仅有符号是不够的,因为我们实际上想要比较不同变量集之间的关系。这样我们可以找出哪些变量之间正相关最强或负相关最强。然后,我们可以根据这些信息来缩小数据集的规模,例如,通过删除一个或多个强相关对中的一个测量值。

为了获得一个可以进行比较的度量,我们可以计算一个稍微不同的数值,叫做相关系数,或者简称相关性。这个值从协方差开始,但包括了一步额外的计算。结果是一个不依赖于所选变量单位的数字。我们可以把相关性看作协方差的缩放版本,总是给我们一个介于 −1 和 1 之间的值。+1 的值告诉我们存在完全正相关,而 −1 的值则告诉我们存在完全负相关

完全正相关很容易辨认:所有的点都落在沿东北-西南方向移动的直线上一,如图 2-23 所示。

F02023

图 2-23:显示完全正相关,或相关系数为 +1 的图形

什么样的点之间的关系会给我们一个正相关,但在 0 和 1 之间的某个范围内?这是一个 y 值随着 x 值持续增加,但比例不会保持恒定的情况。我们可能无法预测它变化的程度,但我们知道 x 增加会导致 y 增加,x 减少会导致 y 减少。图 2-24 显示了相关系数在 0 和 1 之间的正值的一些点图。点越接*落在一条直线上,相关值就越接* 1。我们说,如果值接*零,则相关性是(或),如果接* 0.5,则是中等,如果接* 1,则是(或)。

F02024

图 2-24:从左到右显示减少的正相关例子

现在让我们来看一下零相关值。零相关意味着一个变量的变化与另一个变量的变化之间没有关系。我们无法预测会发生什么。回想一下,相关性只是协方差的缩放版本,因此当协方差为零时,相关性也为零。图 2-25 展示了一些具有零相关的数据。

F02025

图 2-25:这些模式具有零相关性。

负相关就像正相关一样,只是变量朝相反的方向移动:当 x 增加时,y 减少。图 2-26 展示了一些负相关的例子。就像正相关一样,如果值接*零,则相关性是(或),如果接* −0.5,则是中等,如果接* −1,则是强(或)。

F02026

图 2-26:从左到右显示减少的负相关例子

最后,图 2-27 展示了完全负相关,或相关系数为 −1 的情况。

F02027

图 2-27:这些模式具有完全负相关,或相关系数为 −1。

还有一些其他术语值得一提,因为它们在文档和文献中时常出现。我们之前讨论的两个变量之间的关系通常称为简单相关。然而,我们可以研究多个变量之间的关系,这被称为多重相关。如果我们有一堆变量,但我们只研究其中两个变量如何相互影响,这叫做偏相关

当两个变量之间具有完美的正相关或负相关(即 +1 和 −1 的值)时,我们说这两个变量是线性相关的,因为(正如我们所看到的)这些点都在一条直线上。任何其他相关值描述的变量都被称为非线性相关

图 2-28 总结了线性相关性不同值的含义。

F02028

图 2-28:总结线性相关性不同值的含义

统计数据并不能告诉我们一切

本章中我们看到的统计数据确实能告诉我们关于数据集的很多信息,但我们不应假设统计数据能告诉我们一切。一个著名的例子,展示了统计数据如何欺骗我们,是由四组不同的二维点组成的。这些数据集看起来彼此毫不相似,但它们都具有相同的均值、方差、相关性和直线拟合。这些数据被称为安斯科姆四重奏,以发明这些数据值的数学家安斯科姆命名(Anscombe 1973)。这四个数据集的数值在网上广泛可见(Wikipedia 2017a)。

图 2-29 显示了这个四重奏中的四个数据集,以及最适合每个数据集的直线。

这四个不同的数据集的一个惊人之处在于,它们共享许多相同的统计量。每个数据集中的 x 值的均值是 9.0。每个数据集中的 y 值的均值是 7.5。每组 x 值的标准差是 3.16,每组 y 值的标准差是 1.94。每个数据集中 x 和 y 之间的相关性为 0.82。每个数据集的最佳直线通过 Y 轴截距为 3,斜率为 0.5。

换句话说,这七个统计量对于这四组点几乎都有相同的值(当我们进一步查看更多的数字时,某些统计量会有所不同)。如果我们仅仅根据这些统计量,就会认为这四个数据集是相同的。

F02029

图 2-29:安斯科姆四重奏中的四个数据集及其最佳拟合直线

图 2-30 将所有四组点及其最佳直线*似叠加在一起。四条线相同,所以在图中我们只看到一条线。

F02030

图 2-30:安斯科姆四重奏的四个数据集及其最佳直线拟合,叠加显示

这四组数据,虽然很著名,但并不特殊。如果我们想要制作更多具有相同(或*似相同)统计特征的不同数据集,我们可以随意制作多少组都可以(Matejka 和 Fitzmaurice 2017)。这个道理是,我们不应假设统计数据能告诉我们任何数据集的完整故事。

每当我们处理一组新的数据时,花时间了解它总是值得的。这可以包括计算统计数据,但调查过程通常还包括绘制图表和其他可视化内容。一般来说,理解数据越深,我们设计和训练算法从数据中学习的效果就会越好。

高维空间

让我们再探讨一个与数字相关的话题。它更多的是一个概念,而不是一个统计工具,但它影响我们在进行统计分析、机器学习,或是处理大数据集时对数据的思考方式。

在机器学习中,我们通常将多个数字组合成一个单一的样本,或者数据片段。例如,我们可能会通过水果的重量、颜色和大小来描述一个水果。我们称每个数字为样本的特征。一张照片则可以描述为一个样本,其特征是描述每个像素颜色的数字。

我们经常谈论每个样本是某个巨大空间中的一个点。如果一个样本有两个特征,我们可以通过将一个特征与 X 轴关联,另一个特征与 Y 轴关联,把样本绘制成*面上的一个点或圆点。如果样本有三个特征,我们可以在三维空间中放置一个点。但我们通常有更多特征的样本。例如,一张宽 1000 像素、高 1000 像素的灰度照片由 1000 × 1000 个像素值描述。这是一百万个数字。我们无法在一个拥有一百万维的空间中画出一个点的图像,甚至无法想象这样的空间可能是什么样子,但我们可以通过类比我们熟悉的二维和三维空间来推理它。这是处理实际数据时一个重要的思维工具,所以让我们感受一下由大量特征占据的样本空间。

一般来说,空间的每个维度或轴对应样本中的一个特征。将所有特征(即所有数字)看作是一个列表是很有帮助的。如果我们有一个只有一个特征的数据(比如温度),那么我们可以用一个仅包含一个数字的列表来表示该特征。在视觉上,我们只需要显示一条线的长度来表示这个测量值的大小,如图 2-31 所示。我们称这条线为一维空间,因为从线上的任何一点开始,我们只能在一个维度或方向上移动。在图 2-31 中,这个方向是水*方向的。

F02031

图 2-31:一个只有单个值的数据只需要一个坐标轴或维度来绘制它的值。左:X 轴。右:一些数据点,用 X 轴上的点或者不同长度的线段表示。

如果我们在样本中有两条信息,比如温度和风速,那么我们需要一个长度为两个项的列表。为了绘制它,我们需要两个维度,每个维度对应一个测量值。在图形上,我们通常使用两个垂直的坐标轴,如图 2-32 所示。一个点的位置是先沿 X 轴按第一个测量值的量移动,然后沿 Y 轴按第二个测量值的量移动。我们说这是一个二维空间

F02032

图 2-32:如果我们的数据有两个值,我们需要两个维度或坐标轴来绘制这些数据。

如果我们的样本中有三个值,那么我们使用一个包含三个值的列表。如同之前,每个值在我们将其绘制的空间中都有一个对应的维度。这三个维度可以通过三个坐标轴表示,如图 2-33 所示。我们称之为三维空间

F02033

图 2-33:当每个数据点有三个值时,我们需要三个维度或坐标轴来绘制它。

如果我们有四个测量值呢?尽管有一些勇敢的尝试,但目前没有一种公认的方式可以绘制四维空间,特别是在二维的页面上(Banchoff 1990; Norton 2014; ten Bosch 2020)。而一旦我们涉及到五维、十维或百万维,绘制空间图像几乎就成了不可能完成的任务。

这些高维空间可能看起来很深奥且罕见,但实际上它们是很常见的,我们每天都能见到。正如我们所看到的,一个边长为 1,000 像素的灰度图像有一百万个值,等同于一百万维的空间。一个相同大小的彩色图像有三百万个值,所以它是一个位于三百万维空间中的点(或点)。我们根本无法绘制出如此多维的图像。我们甚至无法在脑海中构想出这样一个图像。然而,我们的机器学习算法可以像处理二维或三维空间一样轻松地处理这样的空间。数学和算法并不关心空间的维度有多少。

需要记住的关键点是,每一条数据都可以解释为某个广阔空间中的一个单独的点。就像一个二维(2D)点用两个数字来告诉我们它在*面上的位置一样,一个 750,000 维的点用 750,000 个数字来告诉我们它在那个巨大的空间中的位置。我们通常会为空间命名,以便跟踪它们描述的内容,所以我们可能会说我们的图像由图像空间中的一个单一点表示。

我们称具有许多维度的空间为高维空间。关于“高”从何时开始,并没有正式的共识,但这个短语通常用于描述那些具有超过我们可以合理绘制的三维空间的空间。当然,对于大多数人来说,几十个或几百个维度就足以被认为是高维空间。

本书中我们将使用的算法的一个重要优势是,它们能够处理具有任意维度的数据。当涉及到更多数据时,计算需要更多时间,但理论上,我们可以以与处理二维数据相同的方式处理具有 2000 个维度的数据(实际上,我们通常会根据数据集的维度对算法和数据结构进行调优,以确保它们在处理数据时最为高效)。

我们将经常处理可以看作是抽象高维空间中点的数据。我们不会深入探讨数学内容,而是依赖于我们刚才看到的概念的直观推广,将我们的空间看作是巨大的(且无法视觉化的)类比,类似于我们的直线、正方形和立方体,每个数据点都由某个庞大抽象空间中的一个点表示,在这个空间中,每个方向或维度都对应样本中的一个单一值。然而,我们需要小心不要过度依赖我们的直觉。在第七章中,我们将看到,高维空间并不总是像我们习惯的二维或三维空间那样表现。

总结

我们经常需要描述一组数字。统计学领域致力于找到描述这些集合的有用方法。在本章中,我们了解了基本的统计度量,它们将在整本书中对我们有帮助。我们看到,控制我们在机器学习中需要的数字类型的一个方便方法是使用分布,并且我们了解了一些有用的分布。

我们看到,我们可以选择有放回或无放回地从总体中抽取元素,这样会得到不同种类的集合。我们可以使用许多这样的集合或自助法(bootstraps)的统计数据,来估计起始总体的统计数据。我们了解了协方差和相关性的概念,它们为我们提供了一种衡量一个变量变化如何预测另一个变量变化的方式。我们还看到,我们可以将数字列表看作是任何维度空间中的点。

在下一章,我们将转向概率的概念,在这一概念中,我们考虑随机事件,并试图描述它们发生的可能性,以及一个事件发生后另一个事件发生的可能性,或者与另一个事件同时发生的可能性。

第三章:性能度量

当我们构建系统来预测、分类以及在数据中发现模式时,我们需要某种方式来讨论它们的表现如何。为此,我们使用了各种数值度量工具,统称为性能度量标准。这些工具的设计旨在帮助我们仔细描述系统做得对的地方,更重要的是,当系统得出错误的答案时,具体是哪些地方错了。这些工具是解释任何系统结果的关键。

我们的度量标准基于概率,即我们看到不同类型结果的可能性。因此,我们将从轻松讨论概率开始,专注于最重要的概念。然后,我们将应用这些概念来构建我们的性能度量标准。

概率是一个庞大的主题,包含许多深奥的专业领域。由于我们关注的是如何明智地使用机器学习工具,我们只需要掌握一些基本的术语和主题:不同类型的概率、如何衡量正确性,以及一种叫做混淆矩阵的概率组织方式。掌握了这些基本概念后,我们将能够准备好数据,以便在后续使用工具时获得最佳性能。有关我们将讨论的所有主题的更广泛和深入的讨论,以及该领域的许多其他主题,可以在许多参考资料中找到(Jaynes 2003;Walpole 等 2011;Kunin 等 2020)。

概率的不同类型

概率有许多种类型。我们将在这里讨论其中的一些,从一个比喻开始。

掷飞镖

掷飞镖是讨论基础概率的经典比喻。基本思想是我们站在一个房间里,手里拿着一堆飞镖,面对着一面墙。我们没有挂一个软木靶,而是用不同颜色和大小的油漆斑块涂满了墙面。我们将飞镖投向墙面,并追踪每一支飞镖落在哪个颜色区域(背景也算作一个区域)。这个概念如图 3-1 所示。

C03f001

图 3-1:向墙上投掷飞镖。墙上覆盖着不同颜色的油漆斑块。

我们现在假设我们的飞镖总是会击中墙壁的某个地方(而不是落到地板或天花板上)。因此,每支飞镖击中墙壁某个地方的概率是 100%。我们将同时使用浮动点数(或实数)和百分比来表示概率,因此概率为 1.0 就是 100%,概率为 0.75 就是 75%,以此类推。

让我们更仔细地看看我们的投掷飞镖的场景。在现实世界中,我们更可能击中直接在我们面前的墙面部分,而不是击中侧面的一部分。但为了讨论的方便,我们假设在墙面上的任何一点被击中的概率都是相同的在任何地方都是如此。也就是说,墙面上的每一点被飞镖击中的机会相同。用第二章的术语来说,我们也可以说击中任意一个点的概率遵循均匀分布。

剩下的讨论将重点比较不同区域的面积,以及我们击中这些区域的概率。记住,背景也算作一个区域(在图 3-1 中,它是白色区域)。

这里有一个例子。图 3-2 显示了墙面上的一个红色方块。当我们投掷飞镖时,我们知道它会击中墙面上的某个位置,概率为 1\。

C03f002

图 3-2:我们保证会击中墙面。我们击中红色方块的概率是多少?

那么,击中红色方块的概率是多少呢?在这个图中,红色方块覆盖了墙面总面积的一半。由于我们的规则是墙面上的每一点被击中的概率相同,所以当我们投掷飞镖时,飞镖落入红色方块的概率是 50%,即 0.5。概率就是面积的比值。我们的方块越大,所覆盖的面积越多,因此我们落入方块的概率也就越大。

我们可以通过一个小图示来说明面积的比值。图 3-3 展示了我们方块与墙面之间的面积比。这个图示通过绘制一个形状在另一个形状上方的“分数”,为我们提供了一种直观的方式,帮助我们跟踪讨论的面积,并对它们的相对大小有一个直观的感受。

图 3-3 准确显示了相对面积,因此红色方块的面积实际上是其下方白色方框面积的一半。当其中一个形状远大于另一个形状时,使用全尺寸图形可能会导致图表不够紧凑,因此有时我们会缩小区域,以便让结果图更好地适应页面。这没关系,因为面积比值不会改变。记住,这些形状比值的目的是为了说明一个形状相对于另一个形状的面积。

C03f003

图 3-3:在图 3-2 中,击中红色方块的概率由红色方块的面积与墙面面积的比值表示,这里以符号分数的形式展示。

简单概率

当我们谈论某件事发生的概率时,我们将这件事称为事件。我们通常用大写字母表示事件,如 A、B、C 等等。短语“事件 A 发生的概率”只是指 A 发生的概率。为了节省空间,而不是写“事件 A 发生的概率”或更简洁地写“事件 A 的概率”,我们通常写成 P(A)(一些作者使用小写 p,写作 p(A))。

假设 A 是我们投掷飞镖并击中图 3-2 中的红色方块的事件。我们可以像之前一样用比率表示 P(A)。图 3-4 从图形上展示了这一点。

C03f004

图 3-4:我们假设用飞镖击中方形区域是事件 A。事件 A 发生的概率由图 3-3 中的面积比表示。我们将这个概率写作 P(A)。

这里,P(A) 是方形区域的面积除以墙壁的面积,因此 P(A) 为 1/2。这个比率是投掷飞镖时击中方形区域而不是墙壁其余部分的概率。我们称 P(A) 为简单概率

条件概率

现在让我们讨论涉及两个事件的概率。这两个事件中的任一个可能发生,或者两个都发生,或者都不发生。

例如,我们可能会问房子里有钢琴的概率和房子里有狗的概率。这两个特征(或事件)之间可能没有任何关系。我们称这些彼此无关的事件为独立事件

许多事件类型并非相互独立,而是至少有某种程度的联系。我们称这些为依赖事件。当事件是依赖的时,我们可能希望找出它们之间的关系。也就是说,当我们已经知道某个特定事件已经发生(或正在发生)时,我们想要知道另一个特定事件发生的概率。例如,假设我们路过一栋房子,听到里面有狗在叫。然后我们可能会问:“给定我们知道房子里有一只狗的情况下,房子里有狗咬玩具的概率是多少?”换句话说,我们知道一个事件已经发生,而我们想要知道另一个事件发生的概率。

让我们将这个问题变得更加抽象,讨论两个事件 A 和 B。假设我们知道 B 已经发生,或者等效地,B 为真。知道这一点后,我们可以问 A 也为真的概率是多少?我们将这个概率写作 P(A|B)。竖线表示“给定”,所以我们可以把它读作“在 B 为真的情况下,A 为真的概率”,或者更简单地说,“给定 B 的 A 的概率”。这就是 A 给定 B 的条件概率,因为它只适用于 B 为真的情形或条件。我们还可以讨论 P(B|A),即给定 A 为真时 B 为真的概率。

我们可以通过图示来说明这一点。图 3-5 左图展示了我们的墙面,上面有两个重叠的斑块,标记为 A 和 B。P(A|B) 是指在已知飞镖已经落在 B 区的情况下,它落在 A 区的概率。图 3-5 右边的符号比例中,顶部的形状是 A 区和 B 区的公共区域。也就是说,它是它们的重叠部分,或者是给定飞镖已经落在 B 区时,飞镖可能落在 A 区的区域。

C03f005

图 3-5:左图:墙上涂的两个斑块。右图:已知飞镖已经落在 B 区时,落在 A 区的概率是 A 区与 B 区重叠区域的面积与 B 区面积之比。

P(A|B) 是一个正数,我们可以通过使用飞镖来估算它。我们可以通过计数所有落在 A 区和 B 区重叠部分的飞镖,并将其数量除以落在 B 区任何部分的飞镖数量,来估算 P(A|B)。

让我们看看这个过程的实际应用。在图 3-6 中,我们已经将若干飞镖投向墙面,墙上有图 3-5 中所示的斑块。我们把飞镖投放的位置确保能够覆盖整个区域,且没有两个点距离过*。飞镖的尖端很难看到,所以我们用黑色圆圈表示每个飞镖的落点,圆圈的中心表示飞镖的撞击位置。

C03f006

图 3-6:向墙面投掷飞镖以找到 P(A|B)。 (a) 飞镖击中墙面。(b) 所有落在 A 区或 B 区的飞镖。(c) 仅落在 B 区的飞镖。(d) 落在 A 区和 B 区重叠部分的飞镖。

在图 3-6(a)中,我们展示了所有的飞镖。在图 3-6(b)中,我们仅仅展示了落在 A 区或 B 区的飞镖(记住,只有每个黑色圆圈的中心才是有效的)。在图 3-6(c)中,我们看到有 66 支飞镖落在 B 区,而在图 3-6(d)中,我们看到有 23 支飞镖落在 A 区和 B 区的重叠部分。23/66(约 0.35)的比例估算了落在 B 区的飞镖同时落在 A 区的概率。因此,P(A|B) 约为 0.35。也就是说,如果飞镖落在 B 区,那么大约 35% 的情况下,它也会落在 A 区。

请注意,这个过程并不依赖于颜色斑块的绝对面积,比如以*方英寸为单位的数字。它仅仅是一个区域相对于另一个区域的相对大小,这才是我们真正关心的唯一度量标准(如果墙面面积加倍,颜色区域也加倍,那么每个区域内落镖的概率并不会改变)。

A 区和 B 区的重叠越大,飞镖落在两者重叠部分的概率就越高。如果 A 区围绕着 B 区,就像在图 3-7 中那样,那么只要飞镖落在 B 区,我们一定也会落在 A 区。在这种情况下,A 区和 B 区的重叠部分(以灰色表示)就是 B 区本身。因此,重叠区域面积与 B 区面积之比为 100%,即 P(A|B) = 1。

C03f007

图 3-7:左:墙上两个新斑点。右:当我们处于 B 中时,落入 A 的概率为 1,因为 A 包围 B,因此它们的重叠与 B 相同。

另一方面,如果 A 和 B 没有任何重叠,就像 图 3-8 中一样,那么在 B 中落入 A 的概率为 0%,或者 P(A|B) = 0。

C03f008

图 3-8:左:墙上另外两个新斑点。右:当我们处于 B 中时,落入 A 的概率为 0(或等效地说,0%),因为 A 和 B 之间没有重叠。

在 图 3-8 中的符号比例显示,重叠区域的面积为 0,而 0 除以任何数仍然是 0。

作为一种娱乐,让我们换个角度来看这个问题,问问 P(B|A),或者我们在 A 中时落入 B 的概率。使用与 图 3-5 中相同的斑点,结果显示在 图 3-9 中。

C03f009

图 3-9:条件概率 P(B|A) 是我们在 A 中落入 B 的概率。

逻辑与以前相同。重叠区域的面积除以 A 的面积告诉我们 B 出现在 A 中的概率。它们重叠得越多,飞镖落入 A 中并落入 B 的可能性就越大。让我们给 P(B|A) 赋一个数值。参考 图 3-6,我们看到有 104 枚飞镖落入 A,23 枚落入 B,所以 P(B|A) 是 23/104,约为 0.22。

注意顺序很重要。从 图 3-5 和 图 3-9 可以看出,P(A|B) 的值与 P(B|A) 不同。考虑到 A、B 及其重叠的大小,落入 A 的概率在已经落入 B 的条件下要大于落入 B 的概率。也就是说,P(A|B) 约为 0.35,而 P(B|A) 约为 0.22。

联合概率

在上一节中,我们看到了一种表达一个事件发生概率的方式,前提是另一个事件已经发生。知道两件事同时发生的概率也会很有帮助。用我们的斑点语言来说,飞镖投向墙壁时,落入斑点 A 和斑点 B 的机会有多大?我们将同时发生 A 和 B 的概率写作 P(A,B),在这里逗号的意思是“和”。因此我们大声读出 P(A,B) 为“A 和 B 的概率”。

我们称 P(A,B) 为斑点 A 和斑点 B 的联合概率。利用我们的斑点,我们可以通过比较斑点 A 和斑点 B 的重叠区域与墙壁面积来找到这个联合概率 P(A,B)。毕竟,我们正在询问飞镖落入 A 和 B 的概率,即落入它们的重叠区域,相比于它可能落入墙壁的任何地方。参见 图 3-10。

C03f010

图 3-10:A 和 B 同时发生的概率称为它们的联合概率,记作 P(A,B)。

还有另一种看待联合概率的方法,它稍微复杂一些,但非常强大。它非常有用,将引领我们进入第四章的核心内容。这种联合概率的替代视角将简单概率与条件概率结合起来。

假设我们知道击中 B 的简单概率,或者 P(B)。假设我们还知道条件概率 P(A|B),即在已知击中 B 的情况下,击中 A 的概率。我们可以将这些结合起来形成一条推理链:在知道击中 B 的概率后,我们将其与已知击中 B 时击中 A 的概率结合,以得到同时击中 A 和 B 的概率。

让我们通过一个例子来看一下推理链。假设 B 区域覆盖了墙的一半,所以 P(B) = 1/2。进一步假设 A 区域覆盖了 B 的一三分之一,所以 P(A|B) = 1/3。那么我们投掷到墙上的一半飞镖将落在 B 区域,其中三分之一的飞镖将落在 A 区域。由于一半的飞镖落在 B 区域,而其中三分之一也将落在 A 区域,所以同时落在 B 和 A 的飞镖总数是 1/2 × 1/3,即 1/6。

这个例子向我们展示了一个通用规则:要找 P(A,B),我们将 P(A|B)与 P(B)相乘。这真的非常了不起:我们仅用条件概率 P(A|B)和简单概率 P(B)就找到了联合概率 P(A,B)!我们将其写作 P(A,B) = P(A|B) × P(B)。实际上,我们通常省略显式的乘法符号,直接写作 P(A,B) = P(A|B) P(B)。

图 3-11 展示了我们刚刚使用小面积图示所做的事情。

C03f011

图 3-11:另一种思考联合概率 P(A,B)的方法

考虑图 3-11 的右侧,并将这些小的符号比率看作实际的分数。然后,绿色的 B 区域相互抵消,剩下的就是覆盖在正方形上的灰色区域,这表明我们的小方程的左右两边确实是相等的。

我们也可以反过来做,使用事件 A 而不是 B。我们从 P(B|A)开始,学习在已知落在 A 的情况下落在 B 的概率,然后将其与落在 A 的概率 P(A)相乘。结果是 P(A,B) = P(B|A) P(A)。在图形上,这遵循与图 3-11 相同的模式,只是这次是 A 区域相互抵消。

用符号表示,P(B,A) = P(A,B),因为这两者都指的是同时落在 A B 的概率。与条件概率不同,在联合概率中,A 和 B 的顺序不重要。

这些概念可能有些难以理解,但掌握它们将在第四章派上用场。你可以尝试构造几个小情境并加以练习,想象不同的区域及它们的重叠,或者甚至把 A 和 B 看作实际的情境。例如,假设有一家冰激凌店,顾客可以购买不同口味的冰激凌,且可以选择用华夫饼干筒或杯子装。我们可以说如果某人点了香草冰激凌,那么 V 为真;如果某人选择了华夫饼干筒,那么 W 为真。这样,P(V)表示随机顾客点香草冰激凌的可能性,P(W)表示随机顾客选择华夫饼干筒的可能性。P(V|W)告诉我们在选择了华夫饼干筒的人中,有多大可能性点了香草口味,而 P(W|V)则告诉我们在选择了香草冰激凌的人中,有多大可能性选择了华夫饼干筒。而 P(V,W)告诉我们随机顾客点香草冰激凌并选择华夫饼干筒的可能性。

边际概率

另一个用于简单概率的术语是marginal probability(边际概率),理解这个术语的来源将帮助我们理解如何计算多个事件的简单概率。

让我们从词语marginal开始,它在这个背景下可能看起来有些陌生。毕竟,边缘和概率有什么关系呢?“边际”一词的由来是它源自于包含预计算概率表格的书籍。其背后的理念是我们(或印刷工)会将表格中每一行的总和算出,然后把这些总和写在页面的边缘(Glen 2014)。

让我们通过回到我们的冰激凌店来说明这个概念。在图 3-12 中,我们展示了顾客*期的购买情况。我们的店铺刚开张,出售香草和巧克力冰激凌,且只能选择华夫饼干筒或杯子装。根据昨天进店的 150 位顾客的购买情况,我们可以问一下顾客购买杯子装与华夫饼干筒的概率,或者香草与巧克力的概率。我们通过将每一行或每一列的数值加起来(得到边缘的数字),然后除以总顾客数,来计算这些概率。

C03f012

图 3-12:计算 150 名冰激凌店*期顾客的边际概率。绿色框中的数值(位于网格的边缘)是边际概率。

请注意,顾客购买杯子华夫饼干筒的概率之和为 1,因为每个顾客要么买杯子,要么买华夫饼干筒。同样,所有人都购买香草或巧克力冰激凌,所以这些概率的和也为 1。一般来说,任何事件的不同结果的概率之和总是为 1,因为总有一个选择是 100%发生的。

测量正确性

现在,让我们从概率转到第一个性能度量:给定一个不完美的算法,它产生正确答案的可能性有多大?这是机器学习中的关键问题,因为我们几乎总是会与那些不完全准确的系统打交道。所以,理解它们会犯哪些错误是非常重要的。

让我们考虑一个只有两个类别的简单分类器。我们可以询问它某个数据点属于某个特定类别的概率(这两个类别分别是在类别内不在类别内)。例如,我们可能会询问一张照片是狗的概率,或飓风登陆的概率,或我们的高科技围栏是否足够坚固以容纳我们的基因工程超级恐龙的概率(剧透:不太可能)。

自然地,我们希望我们的分类器做出准确的决策。诀窍在于定义我们所说的准确是什么。仅仅统计错误结果的数量是衡量准确度的最简单方法,但它并不十分有启发性。原因在于,错误的表现形式不止一种。如果我们希望通过错误来改进我们的表现,那么我们需要识别预测出错的不同方式,并考虑每种错误类型带来的困扰。这种分析不仅仅适用于机器学习。以下的思想可以帮助诊断和解决各种基于标签做出决策的问题。

在深入探讨之前,我们需要注意,像精度召回率准确性等术语在流行和非正式写作中使用得比较随意。但在技术讨论中(比如在本书中),这些词有精确的定义和特定的含义。不幸的是,并不是所有的作者对这些术语的定义都是一致的,这可能会引起各种混淆。在本书中,我们将坚持通常在讨论概率和机器学习时的用法,并将在本章后续部分详细定义它们。但请注意,这些术语在许多地方的含义不同,或只是作为模糊概念使用。当这些词语被过度加载时,虽然很遗憾,但它是常见的现象。

样本分类

让我们将语言集中于当前的任务。我们想知道给定的一个数据点或样本是否属于某个特定类别。现在,可以将其视为一个是/否问题:这个样本属于该类别吗?不允许有“可能”这样的回答。

如果答案是“是”,我们称该样本为正面。如果答案是“否”,我们称该样本为负面。我们将通过将分类器的答案与我们提前分配的真实或正确标签进行比较,来讨论准确性。我们手动分配给样本的正面或负面标签称为其真实值实际值。我们将分类器返回的值称为预测值。在理想情况下,预测值将始终与真实值匹配。在现实世界中,通常会发生错误,我们的目标是描述这些错误。

我们将通过二维(2D)数据来说明我们的讨论。也就是说,每个样本或数据点都有两个值。这些值可能是一个人的身高和体重,或者是天气的湿度和风速,或者是音乐音符的频率和音量。然后,我们可以将每个数据点绘制在一个二维坐标系中,X 轴对应一个测量值,Y 轴对应另一个测量值。

我们的样本将属于两个类别之一。我们称它们为正面负面。为了确定样本的正确分类或真实值,我们将使用颜色和形状提示,如图 3-13 所示。

C03f013

图 3-13:属于两个不同类别的二维数据

我们将通过绘制一条边界或曲线来展示预测结果,穿过一组数据点。边界可以是*滑的,也可以是弯曲的。我们可以将其视为分类器决策过程的一种总结。曲线一侧的所有点将被预测为一个类别,而另一侧的所有点则被预测为另一个类别。在图 3-13 的右侧图中,分类器已完美地预测了每个样本的真实情况。这是非常罕见的情况。

我们有时会说边界有正面和负面两侧。如果我们将分类器视为回答“这个样本属于该类别吗?”这个问题,这与我们课堂上所学的内容相符。如果答案是肯定的,那么预测就是“是”,否则预测就是“否”。通常,将边界两侧的区域上色,如我们在图 3-13 中所做的那样,会有助于清晰地看到哪一侧表示正面预测,哪一侧表示负面预测。

对于我们的数据集,我们将使用一组 20 个样本,如图 3-14 所示。具有正面真实值(或手动标签)的样本显示为绿色圆圈,而具有负面真实值(或手动标签)的样本则显示为红色方块。因此,每个样本的颜色和形状对应于其真实值,而背景颜色显示分类器分配的值。

C03f014

图 3-14:左侧:分类器的曲线在分类数据方面做得还可以,但它犯了一些错误。右侧:相同图表的示意图。曲线边界提醒我们,实际边界很少是直线。

分类器的任务是尽量找到一个边界,使得所有正样本都位于一侧,所有负样本都位于另一侧。为了查看分类器对每个样本的预测与实际情况的匹配程度,我们可以直接观察该样本是否落在分类器边界曲线的正确一侧。那条曲线将空间分为两个区域。我们使用浅绿色表示正区域,浅红色表示负区域,因此在浅绿色区域内的每个点都被预测或分类为正类,而在浅红色区域内的每个点都被分类为负类。

在理想的情况下,所有绿色圆圈(具有正实际标签的那些)都会位于边界曲线的绿色一侧(显示分类器将它们预测为正类),所有红色方块都会位于红色一侧。但正如我们在图中所看到的,这个分类器确实犯了一些错误。在图 3-14 的左侧,我们用每个数据的两个值以及描述分类器决策的边界曲线(和区域)绘制了每一条数据。但在这个讨论中,我们并不关心点的位置或曲线的形状。我们的兴趣在于有多少点被正确或错误地分类,因此落在了边界的正确或错误一侧。因此,在右侧的图中,我们简化了几何结构,以便一目了然地计算样本数量。

该图表示我们在真实数据集上运行分类器时通常发生的情况。一些数据被正确分类,而一些则没有。如果我们的分类器表现得不够好,我们需要采取一些措施——可能是修改分类器,甚至丢弃它并重新创建一个新的——因此,能够有效地描述其表现非常重要。

让我们找些方法来做到这一点。我们希望能够描述图 3-14 中的错误,以一种能够告诉我们分类器表现的性质,或者它的预测与给定标签的匹配程度的方式。我们希望了解的不仅仅是“对”或“错”——我们希望了解错误的性质,因为有些错误对我们来说可能非常重要,而另一些错误可能根本不重要。

混淆矩阵

为了描述分类器的预测结果,我们可以制作一个小表格,表格有两列,每一列代表一个预测类别,和两行,每一行代表一个实际类别,或者称为真实类别。这就形成了一个 2×2 的网格,称为混淆矩阵。这个名称反映了矩阵如何展示分类器在预测时出错或混淆的情况。分类器的输出在图 3-15 中得以展示,同时也展示了它的混淆矩阵。

C03f015

图 3-15:我们可以将图 3-14(此处左侧重复,带有标签)中的内容汇总到一个混淆矩阵中,这个矩阵告诉我们有多少样本落入了四个类别中的每一个。

正如图 3-15 所示,表格中的每一个单元格都有一个传统名称,描述了预测值和实际值的特定组合。六个绿色的正圆圈被正确预测为正,因此它们进入真正类。换句话说,它们被预测为正,实际上也确实是正,因此正的预测是正确的,或者说是真的。四个错误分类为负的绿色圆圈进入假负类,因为它们被错误地或虚假地标记为负。八个红色负方块被正确分类为负,因此它们都进入真负类。最后,两个被错误预测为正的红色方块进入假正类,因为它们被错误地或虚假地预测为正。

我们可以使用四个类别的两字母缩写和描述每个类别中有多少样本的数字来更简洁地表达这一点。图 3-16 展示了混淆矩阵通常的形式。

不幸的是,对于混淆矩阵图中的各个标签的位置,并没有统一的约定。一些作者将预测值放在左侧,将实际值放在顶部,而有些则将正负类别的位置与这里所示的相反。当我们遇到混淆矩阵时,重要的是查看标签,确保我们知道每个单元格代表什么。

C03f016

图 3-16:图 3-15 的混淆矩阵的数字表示形式

描述错误预测

我们之前提到过,有些错误可能对我们更重要。让我们来看看为什么会这样。

假设我们在一个公司工作,这家公司生产以流行电视角色为原型的玩具人物模型。我们的玩具现在非常受欢迎,所以生产线正处于满负荷运转。我们的工作是将制造出来的模型人物包装好,并送往零售商店。

突然有一天,我们被告知,公司失去了销售名为眼镜麦克眼镜脸的角色的权利。如果我们不小心发货了这些洋娃娃,我们会被起诉,因此确保这些娃娃不离开我们的工厂非常重要。不幸的是,机器仍在生产这些娃娃,如果我们停止生产线来更新机器,我们的订单会落后很多。我们决定更好的做法是继续生产这些禁止的洋娃娃,但在它们被制造出来之后,再把它们挑出来放进回收箱。所以我们的目标是识别每一个眼镜麦克眼镜脸并将其丢进回收箱,确保它们不会从工厂流出。

图 3-17 展示了这一情况。

我们需要快速工作,因此可能会犯一些错误。在图 3-17 中,我们看到一个被错误回收的洋娃娃。也就是说,在回答“这是眼镜麦克眼镜脸吗?”这个问题时,我们错误地说了“是的”。按照我们上一节的说法,这个娃娃是一个假阳性。这会是一个多大的问题呢?

在这种情况下,这并不是一个大问题(只要我们不要做得太频繁)。我们的目标是确保每个眼镜麦克眼镜脸都能被正确识别和移除。即使错过一个也会给我们带来很大的损失。但一个假阳性仅仅会给我们带来一点损失,因为我们会将塑料融化并重新使用。所以在这种情况下,假阳性虽然不理想,但可以容忍。

C03f017

图 3-17:眼镜麦克眼镜脸是第一排的第一个角色。我们想要移除所有可能是该角色的洋娃娃。我们的选择在第二排。

假设我们后来发现一些洋娃娃的眼睛没有画好。给孩子一个没有眼睛的玩具可能会造成创伤,因此我们绝对想要抓住所有这种情况。像之前一样,我们会检查每一个玩具,这次问的是:“眼睛在吗?”如果没有,就将洋娃娃丢进回收箱。图 3-18 展示了这个思路。

C03f018

图 3-18:一组新的玩具。现在我们在寻找眼睛画错的玩具。我们的选择在最下面一排。

在这里,我们遇到了一个假阴性:洋娃娃的眼睛已经画上了,但我们说它没有眼睛。在这种情况下,少数假阴性并不是什么大问题。只要我们确保移除所有没有眼睛的洋娃娃,即使移除了一些眼睛存在的娃娃也是可以接受的。

总结一下,真正的正例和真正的负例是容易理解的情况。我们如何应对假阳性和假阴性,取决于我们的情况和目标。了解我们的问题是什么,政策是什么,这样我们才能确定如何应对这些不同类型的错误。

衡量正确与错误

让我们回到关于真阳性、假阳性和假阴性的概述,正如混淆矩阵中总结的那样。查看混淆矩阵可能会令人困惑,因此人们创造了各种术语来帮助我们讨论我们的分类器性能如何。

我们将通过一个医疗诊断场景来说明这些术语,其中阳性表示某人患有特定的疾病,阴性表示他们健康。假设我们是公共卫生工作者,来到了一个正经历着一场可怕但完全虚构的疾病叫做拇指病MP)的爆发。任何患有 MP 的人都需要立刻接受拇指切除手术,否则疾病会在数小时内致命。因此,正确诊断所有患有 MP 的人至关重要。但我们绝对不希望做出错误诊断,导致切除任何人的拇指,特别是当他们的生命不在危险中时——拇指是很重要的!

假设我们有一种用于检测 MP 的实验室测试。这个实验室测试是完美无缺的,因此它总是能给出正确的答案:阳性诊断意味着此人有 MP,阴性诊断意味着没有。使用这个测试,我们已经检查了城镇中的每个人,现在知道他们是否患有 MP。但我们的实验室测试速度慢且费用高昂。我们担心未来的疫情,因此根据我们刚刚学到的情况,我们开发了一种快速、便宜且便于携带的现场测试,它可以立即预测某人是否患有 MP。

不幸的是,我们的现场测试并不完全可靠,有时会做出错误的诊断。尽管我们知道我们的现场测试有缺陷,但在爆发期间,它可能是我们唯一的工具。因此,我们希望了解现场测试的正确性以及错误的频率,并且在出现错误时,我们希望了解错误的具体方式。

为了计算这个,我们需要数据。我们刚刚听说另一个城镇有几个人报告了 MP。我们将用两种测试检查镇上的每个人:我们的完美(但慢且昂贵)实验室测试,和我们不完美(但快速且便宜)现场测试。换句话说,实验室测试为每个人提供了真实的结果,而现场测试则提供了预测。实验室测试太贵,不能总是对每个人都进行这两项测试,但我们这一次可以负担得起。

通过将现场测试的预测与实验室测试的标签进行比较,我们将知道现场测试的混淆矩阵的四个象限:

  1. 真阳性:此人有 MP,且我们的现场测试正确地诊断出他们有。

  2. 真阴性:此人没有 MP,且我们的现场测试一致。

  3. 假阳性:此人没有 MP,但我们的现场测试说他们有。

  4. 假阴性:此人有 MP,但我们的现场测试说他们没有。

真阳性和真阴性都是正确答案,而假阴性和假阳性是错误的。假阳性意味着我们会在没有理由的情况下操作,而假阴性则会让某人面临死亡风险。

如果我们通过为混淆矩阵的四个单元格附加数字来构建我们的实地测试,我们可以利用这些值来判断我们的实地测试表现如何。我们将能够通过一些常见的统计数据来描述其表现。准确率将告诉我们实地测试给出正确答案的频率,精度将告诉我们关于假阳性的一些信息,而召回率将告诉我们关于假阴性的一些信息。这些值是人们讨论类似测试质量的标准方式,所以让我们现在来看一下这些值。然后我们将回到实地测试的混淆矩阵,计算这些值,并看看它们如何帮助我们解读测试的预测。

准确率

我们在这一节中讨论的每个术语都来源于混淆矩阵中的四个值。为了更方便讨论,我们将使用常见的缩写:TP 表示真阳性,FP 表示假阳性,TN 表示真阴性,FN 表示假阴性。

我们用来描述分类器质量的第一个术语是准确率。对于任何样本集合,预测的准确率是一个从 0 到 1 的数值。它是正确分类的样本所占的百分比。因此,它就是两个“正确”值(真阳性 TP 和真阴性 TN)的总和,除以测量的样本总数。图 3-19 以图形方式展示了这个概念。在这个图中,和接下来的图一样,我们将展示用于任何给定计算的样本,且那些不参与该值计算的样本会被省略。

我们希望准确率是 1.0,但通常它会小于 1.0。在图 3-19 中,我们的准确率是 0.7,或者说是 70%,这并不是很高。准确率并不能告诉我们预测错误的方式,但它确实给了我们一个关于正确结果的广泛感知。准确率是一个粗略的度量。

现在让我们来看两个其他的衡量标准,它们可以更具体地描述我们的预测。

C03f019

图 3-19:准确率是一个从 0 到 1 的数值,告诉我们我们的预测有多准确。

精度

精度(也叫做正预测值)告诉我们,在我们标记为正类的所有样本中,实际上正确标记为正类的样本所占的百分比。从数值上讲,它是 TP 与 TP + FP 的比值。换句话说,精度告诉我们在所有正类预测中,有多少是正确的。

如果精确度为 1.0,那么每一个真实为正的样本都被正确预测为正。当精确度下降时,它也意味着我们对这些预测的信心降低。例如,如果精确度为 0.8,那么我们只能有 80% 的信心,认为任何标记为正的样本是正确的。图 3-20 以图形化的方式展示了这一概念。

C03f020

图 3-20:精确度的值是实际为正的正样本的总数,除以我们标记为正的样本总数。

当精确度小于 1.0 时,意味着我们错误地将一些样本标记为正。以之前的医疗案例为例,在我们假设的疾病中,精确度小于 1.0 意味着我们会进行一些不必要的手术。精确度的一个重要特性是,它并不告诉我们是否找到了所有的正样本,也就是说,它不关心那些真实为正的样本是否被完全找出。精确度只关注被标记为正的样本。

召回率

我们的第三个衡量指标是召回率(也叫敏感性命中率真正率)。它告诉我们正确预测为正的样本占所有真实为正样本的百分比。也就是说,它告诉我们我们正确预测的正样本所占的百分比。

当召回率为 1.0 时,意味着我们正确预测了每个正事件。召回率越低,错过的正事件就越多。图 3-21 以图形化的方式展示了这一概念。

C03f021

图 3-21:召回率的值是正确标记为正的样本的总数,除以应该标记为正的样本总数。

当召回率小于 1.0 时,意味着我们错过了一些正答案。在我们的医疗案例中,这意味着我们会误诊一些患有 MP 的人没有得病。结果是我们不会对这些人进行手术,即使他们已经感染且处于危险之中。

精确度-召回率权衡

当我们将数据分类为两类,且无法消除假阳性和假阴性时,精确度与召回率之间存在权衡:当一个增加时,另一个就会下降。这是因为,当我们减少假阳性的数量(因此增加精确度)时,必然会增加假阴性的数量(从而降低召回率),反之亦然。让我们看看这是如何发生的。

图 3-22 显示了 20 个数据点。它们最初位于最左侧时是负样本(红色方块),随着我们向右移动,逐渐变为正样本(绿色圆圈)。我们会在某个位置垂直画一条边界线,预测它左侧的所有数据为负,右侧的所有数据为正。我们希望所有的红色方块都被预测为负,所有的绿色圆圈都被预测为正。由于它们是混合在一起的,因此没有一条边界线能够完美地分开这两组数据。

C03f022

图 3-22:当我们将边界线向右移动,从上到下时,我们减少了假阳性(带有粗边框的红色方块)的数量,但增加了假阴性(带有粗边框的绿色圆圈)的数量。

在图 3-22 的顶行中,边界线靠*最左端。所有的绿色圆圈都被正确标记为正样本,但许多红色方块是假阳性(用粗线条显示)。随着我们将边界线向右移动到下方的行,我们减少了假阳性的数量,但增加了假阴性的数量,因为现在我们预测更多的绿色圆圈为负样本。

假设我们将数据集的大小增加到 5000 个元素。数据将类似于图 3-22,因此每个数据项的正样本概率由其距离最左侧的距离决定。图 3-23 最左侧的图显示了我们将决策边界从最左端移动到最右端时,真实正例和真实负例的数量。中间图显示了假阳性和假阴性的数量,最右侧的图则显示了最终的准确率。

C03f023

图 3-23:左图:我们移动边界时,真实正例和真实负例的数量。中图:假阳性和假阴性的数量。右图:准确率。

要计算精度和召回率,我们会将图 3-24 左侧图中的 TP 和 FP,以及中间图中的 TP 和 FN 收集在一起。右侧图显示了将这些对与 TP 结合后的结果,并根据之前的定义计算每个边界位置的精度和召回率。

C03f024

图 3-24:随着边界从最左端向最右端移动,TP 和 FP,TP 和 FN,以及精度和召回率。

请注意,随着精度的提高,我们的召回率会降低,反之亦然。这就是精度-召回率的权衡。

在这个例子中,精度呈直线变化,而召回率则呈曲线变化。为了理解其中的原因,考虑图 3-24 左侧曲线的 TP+FP 总和将是从左上到右下的对角线,而中间部分的 TP+FN 总和将是一个水*线。将 TP 曲线除以这两条方向不同的线,便得到了精度和召回率曲线的不同形状。

对于其他类型的数据集,这些曲线看起来会不同,但精度和召回率之间的权衡将保持不变:精度越好,召回率越差,反之亦然。

误导性度量

准确率是一个常见的度量标准,但在机器学习中,精度和召回率出现得更多,因为它们有助于表征分类器的性能并与其他分类器进行比较。但是,如果单独考虑精度和召回率,它们可能会具有误导性,因为极端条件可能会给我们带来这两个度量的高值,而总体性能却很差。

这些误导性的结果可能来自许多来源。也许最常见且最难发现的是,当我们对计算机应该为我们做什么不够小心时。例如,我们的组织可能希望我们创建一个具有极高精度或召回率的分类器。听起来可能很理想,但让我们来看看为什么这可能是一个错误。

要看清问题,可以考虑如果我们要求达到完美精度完美召回率这两个极端,可能会发生什么。我们将设计糟糕的边界曲线来展示这些问题,但请记住,这些问题可能自然地出现在一个被要求达到完美精度或召回率的算法中。

创建一条具有完美精度的边界曲线的一种方法是,查看所有样本,找出我们最确定的正例。然后我们画出这条曲线,使得我们选择的点是唯一的正样本,其他所有的都是负样本。图 3-25 展示了这个想法。

C03f025

图 3-25:左图:这条边界曲线给了我们完美的精度得分。右图:左图的示意版本。

这如何给我们完美的精度呢?记住,精度是正确正例的数量(这里只有 1 个)除以所有被标记为正例的点的总数(同样,只有 1 个)。所以我们得到的比值是 1/1,或者 1,这是一个完美的分数。但准确率和召回率都非常糟糕,因为我们也创建了大量的假负例,正如图 3-26 所示。

C03f026

图 3-26:这些图形都共享相同的边界曲线,该曲线将一个绿色圆圈标记为正例,其他所有的都标记为负例。

让我们用召回率做一个类似的操作。创建一条具有完美召回率的边界曲线甚至更容易。我们所要做的就是将所有样本都标记为正例。图 3-27 展示了这个想法。

C03f027

图 3-27:左图:这条边界曲线给了我们完美的召回率得分。右图:左图的示意版本。

我们从中得到了完美的召回率,因为召回率是正确标记的真实点的数量(这里是所有 10 个)除以真实点的总数(同样是 10)。所以 10/10 等于 1,或者召回率的完美分数。但当然,准确度和精确度都很差,因为每个负样本现在都是假阳性,正如 图 3-28 所示。

C03f028

图 3-28:所有这些图形共享相同的边界曲线。通过这条曲线,所有点都被预测为正类。我们获得了完美的召回率,因为每个正类点都被正确标记。不幸的是,准确度和精确度都得到了很低的分数。

图 3-26 和 3-28 的教训是,要求完美的精确度或完美的召回率不太可能带来我们真正想要的结果,即完美的正确性。我们希望准确度、精确度和召回率都接* 1,但如果不小心,我们可能会通过选择一个极端的解决方案来获得其中一个度量的高分,而这种解决方案在任何其他方式下查看结果时表现较差。

f1 分数

同时考虑精确度和召回率是有意义的,但它们可以通过一些数学方法结合成一个单一的度量,称为 f1 分数。这是一种特殊类型的“*均数”,叫做 调和均值。它使我们能够查看一个结合了精确度和召回率的单一数字(公式稍后出现在 图 3-30 和 图 3-32 的最后几行)。

图 3-29 直观地展示了 f1 分数。

一般来说,当精确度或召回率较低时,f1 分数会较低,当两者都接* 1 时,f1 分数也会接* 1。

C03f029

图 3-29:当精确度或召回率为 0 时,f1 分数为 0,当两者都为 1 时,f1 分数为 1。在两者之间,随着精确度和召回率的增加,f1 分数逐渐上升。

当一个系统表现良好时,有时人们会引用 f1 分数,作为展示精确度和召回率都很高的一种简便方式。

关于这些术语

准确度精确度召回率 这些术语可能看起来与它们所衡量的内容并不明显相关。让我们建立这些联系,这有助于我们记住这些术语的含义。

准确度 告诉我们我们正确预测的样本百分比。如果我们完全正确地预测了每个标签,准确度将为 1。当错误的百分比增加时,准确度会下降到接* 0。为了描述我们的错误,我们需要了解假阳性和假阴性的比率。这就是精确度和召回率的作用所在。

精确度揭示了我们假正例的百分比,或者说我们错误预测为正类的样本数量。所以这衡量了我们正类预测的特异性或精度。精确度的值越大,我们对正类预测准确性的信心就越强。在我们的医学例子中,如果我们的测试具有高精度,那么正向诊断很可能意味着那个人确实患有 MP。但精确度并不能告诉我们有多少感染者被错误地诊断为健康。

召回率揭示了我们假负例的百分比。如果我们将我们的系统视为仅从一组数据中寻找或召回正类样本,那么它告诉我们我们做得如何。召回率越好,我们就越有信心自己正确地检索到了所有正类样本。在我们的医学例子中,如果我们的测试具有高召回率,那么我们可以确信自己已经识别出所有患有 MP 的人。但召回率并不能告诉我们有多少健康人被错误地识别为患有 MP。

其他度量

我们已经看到了准确率、召回率、精确度和 f1 的度量。在概率和机器学习的讨论中,有很多其他术语有时也会被使用(维基百科 2020)。虽然我们在本书中不会遇到这些术语中的大部分,但我们在这里总结它们,提供一个方便的参考,集中了所有定义。

图 3-30 提供了这个总结。不要费心去记忆任何不熟悉的术语及其含义。这个表格的目的是提供一个方便的地方,在需要时查找这些内容。

C03f030

图 3-30:来自混淆矩阵的常见置信度术语

这个表格的信息量很大。我们提供了一个替代方案,通过图示展示这些术语,使用来自图 3-14 的样本分布,这里重复呈现为图 3-31。

C03f031

图 3-31:图 3-14 中的数据,标记了四个类别:真正例、假正例、假负例和真负例

从上到下阅读,我们有六个正类点被正确标记(TP = 6),两个负类点被错误标记(FP = 2),四个正类点被错误标记(FN = 4),以及八个负类点被正确标记(TN = 8)。

有了这些要点,我们可以通过将这四个数字或它们的图示以不同的方式组合起来,来说明图 3-30 中的度量。图 3-32 展示了我们如何仅使用相关数据片段来计算这些度量。

正确构造混淆矩阵

从统计度量的角度理解一个测试(或分类器)可能会很困难。信息量大,要保持条理清晰并组织好可能是一个挑战。接受这一挑战很重要,因为大多数现实世界的测试(各个领域的测试)都是不完美的,许多机器学习系统也是如此。一般来说,它们需要从统计性能的角度来理解。

混淆矩阵是一个简单但强大的工具,能够简化并总结我们的理解。但我们必须小心构建和解释它,否则很容易得出错误结论。为了总结本章内容,让我们更深入地看看如何正确构建和解释混淆矩阵。

我们的计划是回到假设的麻疹疫情,给我们的混淆矩阵附上一些数字,并提出一些关于我们快速但不准确的实地测试质量的问题。回想一下,我们之前说过,我们会用慢速且昂贵,但完全准确的实验室测试来测量一个城镇的每个人(这将给我们提供真相),同时也用更快、更便宜且不完美的实地测试(这将给我们提供预测)。

C03f032

图 3-32:我们使用图 3-29 的数据,将统计测量以可视化形式展示,并结合图 3-30 的数据。

假设这些测量结果显示,实地测试具有较高的真实阳性率:我们发现,99% 的情况下,麻疹患者被正确诊断。由于真实阳性率(TP)为 0.99,假阴性率(FN),即所有我们没有正确诊断的麻疹患者,便是 1 – 0.99 = 0.01。

对于那些没有感染麻疹的人,测试表现稍差。我们假设真实的阴性率(TN)为 0.98,即在 100 次预测没有感染的情况下,有 98 次预测是准确的。但这意味着假阳性(FP)率为 1 – 0.98 = 0.02,因此每 100 个没有麻疹的人中,会有 2 个被错误诊断为阳性。

假设我们刚刚听说一个新城镇(人口 10,000)的麻疹疫情爆发。从经验来看,考虑到已经过去的时间,我们预计有 1% 的人群已经被感染。这是关键信息。我们并不是盲目地测试人群,我们已经知道,一个人感染麻疹的几率只有 1/100。为了正确理解我们的实地测试结果,包含这些信息至关重要。

所以我们收拾好装备,以最快的速度赶往市区。

没有时间将我们的结果送往大型而缓慢的实验室,所以我们让大家来市政厅参加我们的实地测试。假设某人测试结果为阳性。他们应该怎么办?他们感染麻疹的可能性有多大?假设测试结果为阴性。那么这些人应该怎么办?他们没有感染麻疹的可能性有多大?

我们可以通过构建混淆矩阵来回答这些问题。如果我们直接进入,就可能通过将上面的值填入相应的框中来构建混淆矩阵,就像在 图 3-33 中那样。但这不是正确的方法!这个矩阵是不完整的,将导致我们得出错误的答案。

C03f033

图 3-33:这不是我们想要的混淆矩阵。左:使用我们测得的值的矩阵。右:将每个值乘以 100,显示为百分比。

问题在于我们忽略了一个关键的信息:现在只有 1% 的镇上居民会感染 MP。图表 图 3-33 没有包括这一信息,因此它没有告诉我们我们需要知道的内容。

在 图 3-34 中,我们通过考虑镇上 10,000 人的情况,并利用我们对感染率和测试性能的了解,来计算出正确的矩阵。

C03f034

图 3-34:根据我们的感染率和测试预期的群体分布

图 3-34 是正确过程的核心部分,让我们一起走一遍。我们从镇上的 10,000 人开始。我们最基本的起始信息是,根据以前的经验,我们已经知道 100 个人中就有 1 个人,或者说 1% 的人口会感染 MP。这个信息显示在上方路径中,我们显示了 10,000 中的 1%,即 100 人感染了 MP。我们的测试将正确地显示其中 99 个人为阳性,仅有 1 个人为阴性。回到我们最初的总人口,沿下方路径我们跟踪那 99% 的人,即 9,900 人,他们没有感染。我们的测试将正确地识别 98% 的人,即 9,702 个人为阴性。剩下的 2%(198 人)将获得错误的阳性结果。

图 3-34 告诉我们应当使用的值来填充混淆矩阵,因为它们融入了我们对 1% 感染率的知识。从我们的 10,000 次测试中,我们预期(*均而言)会有 99 个真正阳性、1 个假阴性、9,702 个真正阴性和 198 个假阳性。这些值给我们提供了 图 3-35 中的正确混淆矩阵。

c03f035

图 3-35:我们的 MP 测试的正确混淆矩阵,结合了我们对 1% 感染率的知识

与 图 3-33 相比,TN 率变化非常大!TN 值从 98 变为 9,702. 假阳性(FP)值也发生了巨大的变化,从 2 变为 198. 这是很重要的:198 名健康人将收到显示他们感染的结果。

现在我们有了正确的矩阵,我们准备回答我们的问题。假设某人得到了阳性测试结果。那么他们真正患有 MP 的概率是多少?用统计学的术语来说,就是给定测试显示为阳性,某人患有 MP 的条件概率是什么?更简单地说,返回的阳性结果中有多少比例是真阳性?这正是精确度衡量的内容。在这种情况下,精确度为 99 / (99 + 198),即 0.33,或者 33%。

等一下,似乎有什么地方不对。我们的测试有 99% 的概率正确诊断 MP,但在 2/3 的情况下,当它给出阳性结果时,那个病人并没有患有该病。我们的大部分阳性结果都是错误的!

这确实看起来很奇怪。

这就是我们为什么要通过这个例子的原因。理解概率可能是很棘手的。这里我们有一个 99% 正确诊断 MP 的测试,听起来相当不错。然而,大多数我们的阳性诊断是错误的。

这个令人惊讶的结果出现是因为尽管错过感染者的概率很小,但被测试的健康人数量巨大。因此,我们得到了大量这些罕见的错误阳性诊断,并且它们迅速累积。结果是,如果某人得到阳性结果,我们不应该立即进行手术。我们应该将此结果解读为信号,提示我们进行更昂贵且更准确的测试。

让我们使用我们的区域图来查看这些数字。我们需要扭曲图 3-36 中区域的大小,以便制作一个我们能够解读的图表。

C03f036

图 3-36:左侧:该人群包含 100 个患有 MP 的人,以及 9,900 个没有患病的人。中间和右侧:我们的测试结果。图形的大小不按比例。

我们之前看到,精确度告诉我们,被诊断为阳性的人真正患有 MP 的概率。这在图 3-37 的最左侧进行了说明。我们可以看到,现场测试错误地将没有 MP 的人标记为阳性,导致精确度为 0.33。这告诉我们应该对阳性结果保持怀疑,因为 1 – 0.33 ≈ 0.66,或者 66% 的阳性结果是错误的。

如果某人得到了阴性结果,情况如何?他们真的是清楚的吗?这就是正确阴性与总阴性数之比,即 TN / (TN + FN),这就是图 3-29 所定义的负预测值。在这种情况下,它是 9,702 / (9,702 + 1),大约为 0.999,或 99.9%。所以,如果某人得到了阴性结果,那么他们的测试结果错误的概率只有大约 1/10,000,他们确实患有 MP。我们可以告诉他们这一点,让他们决定是否需要更慢、更昂贵的测试。

C03f037

图 3-37:描述我们测试 MP 的四个统计数据,基于图 3-36 的结果。精准度:我们的阳性诊断中有多少百分比是准确的?召回率:我们找到所有阳性的百分比是多少?阴性预测值:我们的阴性诊断中有多少百分比是准确的?特异性:我们找到所有阴性的百分比是多少?如之前所述,区域大小并不按比例绘制。

我们发现,阳性结果意味着某人实际上确实患有 MP 的概率只有大约 33%。另一方面,阴性结果有 99.9%的可能性确实是阴性。

图 3-37 展示了其他几个测量值。召回率告诉我们被正确诊断为阳性的人群百分比。由于我们只漏掉了 100 人中的一个,因此该值为 99%。特异性告诉我们被正确诊断为阴性的人群百分比。由于我们错误地给出了 198 个阴性诊断,因此该结果略低于 1\。

总结来说,在这个城镇中,1%感染率的 1 万名居民中,我们的测试只会漏掉 1 例 MP。但我们将得到* 200 个错误的阳性诊断(即假阳性),这可能会不当地吓到和担心人们。有些人甚至可能会立刻进行手术,而不是等待更慢的测试。在我们希望正确找到每一个患有 MP 的人时,我们的测试可能在告知人们他们感染了病毒时过于激进。

如前所述,如果我们想要做一个永远不会漏掉任何患有 MP 的人的测试,我们可以简单地将每个人都标记为阳性,但那样并没有意义。在现实情况下,面对不完美的系统,目标是*衡假阴性和假阳性的比例,以便达到我们的目的,同时考虑到这些错误。

我们的 MP 示例是虚构的,但现实世界充满了人们基于错误的混淆矩阵或构造不当的问题做出重要决策的情况。那些决策中有些与真实且非常严重的健康问题相关。

例如,许多女性因其外科医生误解了乳腺检查的概率并给出了错误的建议,导致她们接受了不必要的乳房切除术(Levitin 2016)。推荐某人接受不必要的手术是一个危险的错误。男性也因没有必要的原因进行了手术,因为许多人根据医生误解了使用升高的 PSA 水*作为前列腺癌证据的统计学,得到了错误的建议(Kirby 2011)。

概率和统计学可能是微妙的。我们必须慢慢来,仔细思考,并确保我们正确地解读数据。

现在我们知道,听到某个测试“99%准确”或“正确识别了 99%的阳性病例”时,我们不应轻易被误导。在我们这个只有 1%人群被感染的小镇上,使用一个令人印象深刻的 99%真正阳性率的测试,任何被诊断为阳性的人很可能并没有真正患病。

这个道理是,任何情况下的统计声明,从广告到科学,都需要仔细审视并放入上下文中。通常,像“精确度”和“准确度”这样的术语被随意或口语化地使用,最好是使它们难以解读。即使这些术语在技术意义上使用,单纯的准确度和相关指标的声明也很容易引起误导,并可能导致错误的决策。

在涉及概率时,不要仅凭直觉。到处都有意想不到的惊讶和反直觉的结果。慢慢来,收集所有数据,仔细思考。

总结

我们在这一章中学到了很多!我们涵盖了一些概率中的最重要的概念。我们看到了一个术语,用来表示某个事件 A 发生的可能性,P(A);或者在某个事件 B 已经发生的条件下,事件 A 发生的可能性,P(A|B);又或者是事件 A 和 B 同时发生的可能性,P(A,B)。

然后我们看了一些性能指标,帮助我们表征一个测试在正确识别数据集中的正负样本方面的能力。我们看到,我们可以使用这些指标来帮助我们解释任何决策过程的结果。我们将这些术语组织成一个混淆矩阵,这有助于我们理解所有这些信息。

我们看到了统计数据可能会引起误导。如果我们不小心,我们可能会创建出一些测试(或分类器),它们根据一组度量看起来做得很好,但在其他方面却很糟糕。重要的是要慢慢来,考虑所有数据,仔细思考,并且在处理概率时小心使用语言。

在第四章中,我们将把这些概念应用到一种广泛用于机器学习中的推理方法。这将为我们提供另一个工具,帮助我们在设计学习算法时,能够有效地完成我们期望的任务。

第四章:贝叶斯法则

在第三章中,我们讨论了一些基于概率的性能度量。随着我们对概率及其在机器学习中的应用深入了解,我们发现关于如何处理这个问题,有两种根本不同的思维流派。

学校中最常教授的方法称为频率主义方法。另一种方法叫做贝叶斯方法,它以托马斯·贝叶斯命名,贝叶斯在 1700 年代首次提出了这一思想。尽管它不如频率主义方法广为人知,贝叶斯方法在机器学习中却非常流行。其原因有很多,但最重要的一点是,它为我们提供了一种明确识别并利用我们对所测量系统的预期的方式。

本章我们首先探讨频率主义和贝叶斯方法之间的差异。然后我们讨论贝叶斯概率的基础知识,涵盖足够的内容,以帮助我们理解基于贝叶斯思想的机器学习论文和文档。我们将重点介绍贝叶斯法则,也称为贝叶斯定理,它是贝叶斯统计的基石。即使是这条单一的法则,也是一个重要的主题,因此我们只会以最宽泛的方式讨论它(Kruschke 2014)。本章最后我们会再次比较贝叶斯方法和频率主义方法。

频率主义与贝叶斯概率

在数学中,几乎总是有不止一种思考问题甚至整个领域的方式。有时方法之间的差异微妙,而有时差异则非常显著。概率论无疑属于后者。概率有至少两种不同的哲学方法,每种方法都有其优点和缺点。频率主义方法与贝叶斯方法之间的差异有深刻的哲学根源,常常通过所使用的数学和逻辑中的细微差别来表现,这些数学和逻辑构成了它们各自的概率理论(VanderPlas 2014)。这使得在不深入大量细节的情况下讨论它们之间的差异变得困难。尽管这两种方法差异如此之大,但精确描述这两种概率方法之间的区别的挑战被称为“特别棘手”(Genovese 2004)。

我们在这里的做法是跳过复杂的论证,而是用一般性的术语描述这两种方法,这样我们就能感知它们在目标和过程上的不同,而无需深入细节。这有助于为我们接下来讨论贝叶斯法则奠定概念基础,贝叶斯法则是贝叶斯方法的核心。

频率主义方法

一般来说,频率学派的人是不信任任何特定的测量或观察,认为它仅仅是对一个真实、潜在值的*似。例如,如果我们是频率学派的支持者,想要知道一座山的高度,我们会假设每次测量的结果可能都比真实值稍大或稍小。这种态度的核心信念是,真实答案已经存在,任务是找到它。也就是说,这座山有一个精确、明确的高度,如果我们足够努力并进行足够的观察,我们就能发现这个值。

为了找到这个真实的值,我们结合大量的观察结果。尽管我们认为每次测量可能并不完全准确,但我们也期望每次测量都能接*真实值。如果我们进行大量测量,我们就会说出现频率最高的值是最可能的。对最常见值的关注正是频率学派名称的来源。通过结合大量测量结果,我们可以找到真实值,其中出现最频繁的值对结果影响最大(在某些情况下,我们可以直接取所有测量值的*均值)。

当在学校首次讨论概率时,通常会介绍频率学派的方法,因为它易于描述,且往往与常识相符。

贝叶斯方法

用同样的广泛概括,贝叶斯学派的人相信每次观察都是对某种东西的准确测量,尽管每次测量的对象可能略有不同。贝叶斯学派的态度是,没有“真实”值等待着被发现。在回到我们山的例子时,贝叶斯学派认为山的真实高度是一个没有意义的概念。相反,每次测量的高度都描述了从地面某个点到山顶附*某个点的距离,但每次这两个点都不完全相同。因此,尽管每次测量的结果不同,但每次测量都是对我们可以称之为山的高度的某种准确测量。每次细心的测量都和其他测量一样真实——并没有一个单一的、确定的值等待我们去发现。

相反,山的高度只有一个可能范围,每个范围都有一个概率。随着我们进行更多的观察,这个可能范围通常会变得更加狭窄,但永远不会收敛到一个单一的数值。我们永远无法以一个数值来表示山的高度,只能表示为一个范围,每个值都有其对应的概率。

频率学派 vs. 贝叶斯学派

这两种概率方法引发了一个有趣的社会现象。一些从事概率学的严肃人士认为,只有频率派的方法才有意义,而贝叶斯方法是一个无用的干扰。另一些严肃的人则恰恰持相反意见。还有许多人在这两种方法中持有较为温和但仍然强烈的观点,认为哪种方法应该被视为思考概率的正确方式。当然,也有很多人认为两种方法都提供了有用的工具,适用于不同的情境。当我们处理真实数据时,我们如何思考概率可以大大影响我们能够提出和回答的问题(Stark 和 Freedman 2016)。

贝叶斯方法的一个关键特点是我们在开始测量之前明确识别我们的期望。在我们的山脉示例中,我们会提前说明我们预计山的高度。一些频率派对此提出异议,认为你不应该带着预设的期望或偏见进入实验。贝叶斯派则回应说,偏见是不可避免的,因为它已经融入了每个实验的设计中,影响着我们选择测量的内容以及方式。他们认为最好明确地陈述这些期望,以便进行检验和辩论。频率派不同意并提出反论点,贝叶斯派再提出反驳,辩论持续进行。

让我们通过抛硬币并问它是否公*,即正反面出现的次数大致相同,还是它有偏向,某一面更频繁地出现,来观察这两种技术的实际应用。首先,我们来看频率派是如何解决这个问题的,然后我们将看看贝叶斯派是如何处理的。

频率派抛硬币

人们常常用抛硬币作为讨论概率的例子(Cthaeh 2016a;Cthaeh 2016b)。抛硬币之所以受欢迎,是因为它对每个人来说都很熟悉,并且每次抛掷只有两个可能的结果:正面或反面(我们忽略像硬币立在一边这样的特殊情况)。只有两个结果使得数学足够简单,我们常常可以手工计算出结果。尽管我们在这里不会做太复杂的数学运算,抛硬币仍然是一个很好的方式来展示潜在的概念,所以我们将其作为我们的示例。

我们说一个公*的硬币是指*均来说,它正面朝上的次数占一半,反面朝上的次数占另一半。一个不公*的硬币我们称之为作弊加权不公*的硬币。为了描述一个作弊的硬币,我们会提到它正面朝上的倾向,或者它的偏见。一个偏见为 0.1 的硬币大约有 10%的几率正面朝上,而偏见为 0.8 则意味着我们预计硬币 80%的几率是正面朝上。如果一个硬币的偏见为 0.5,它正反面朝上的几率大致相同,那么它与一个公*的硬币是无法区分的。实际上,我们可能会说一个偏见为 0.5,或者 1/2 的硬币就是公*硬币的定义。

所以,通过进行大量测量(翻转)并将它们的结果(正面或反面)结合起来,我们可以希望找到正确的答案(硬币的偏差)。图 4-1 展示了频率学派在寻找三种不同硬币偏差时的做法,每行表示一种硬币。

F04001

图 4-1:顶排:偏差为 0.2 的硬币。中排:偏差为 0.5 的硬币。底排:偏差为 0.8 的硬币。

在图 4-1 的每一行左侧,我们展示了该硬币连续翻转 100 次的结果。在右侧,我们展示了频率学派在每次翻转后的硬币偏差估计。这是通过将我们迄今为止找到的正面次数除以翻转的总次数来得出的。像往常一样,频率学派认为每一次测量(这里是硬币是否正面朝上)都只是对真相的一次小小*似。通过将所有这些*似(这里是简单的运行*均值)结合起来,我们可以收敛到表示硬币偏差的“真实”值。

贝叶斯硬币翻转

让我们考虑一下如何通过贝叶斯观点估算硬币的偏差。为此,我们将使用一个稍微复杂的情境,来突出展示贝叶斯如何提问并回答问题。

一个激励性示例

假设我们有一个朋友,她是一个深海海洋考古学家。她最*的发现是一个古老的沉船,其中有一件宝物是一个标记板和一袋看起来一模一样的两枚硬币。她认为这些硬币曾被用于一种赌博游戏,并且她和她的同事们甚至重建了部分规则。

关键因素是,两个看起来相同的硬币中,只有一个是公*的。另一个硬币是做过手脚的,它出现正面朝上的几率是三分之二(也就是说,它的偏差是 2/3)。1/2 和 2/3 的偏差差距并不大,但足以构建一个游戏。这个做手脚的硬币被巧妙地制造,以至于我们无法通过观察它们,甚至通过拿起硬币随便摸一摸,来分辨哪一个是哪个。游戏的内容是玩家翻转这些硬币,并尝试找出哪一枚硬币是哪一枚,同时过程中会涉及各种形式的诈唬和下注。游戏结束时,玩家通过将两枚硬币竖着旋转来辨别哪一枚是做手脚的,哪一枚是公*的。由于偏心的硬币重量不均,它比公*的硬币更早掉落。

我们的考古学家朋友希望进一步探索这个游戏,但她需要知道硬币的真实身份。她请求我们帮助理清楚。她给了我们两个信封,分别标注为“公*”和“做手脚”,我们的任务是把每一枚硬币放进适当的信封里。我们可以使用旋转测试来判断哪一枚是哪个,但让我们改用概率的方法,这样我们就可以有机会体验这种思维方式。

让我们从选择一个硬币开始,投掷一次,看看它是正面还是反面,然后看看我们能从这个信息中做出什么。第一步是选择一个硬币。由于我们不能通过观察区分两个硬币,我们有 50%的概率选择到公*硬币,另外 50%的概率选择到作弊硬币。这个选择引出了我们的大问题:我们选择的是公*硬币吗? 一旦我们知道手中的硬币是哪一枚,我们就可以将其放入相应的信封中,并将另一个硬币放入另一个信封里。让我们将问题重新表述为概率的问题:我们选择公*硬币的概率是多少? 如果我们能确定自己拿到的是公*硬币,或者确定自己拿到的不是,我们就能知道所有需要知道的事情。

所以,我们有了问题,也有了硬币。让我们投掷吧。正面!概率推理的一个伟大之处在于,仅凭这一次投掷,我们就已经能够就我们手中的硬币做出有效且量化的判断。

描述硬币的概率

为了绘制接下来讨论中涉及的各种概率,让我们回顾一下第三章中的概率图。假设我们有一面方形的墙,我们要向墙上投掷飞镖,每次飞镖落在墙上的每个点的概率是相等的。我们可以将墙的不同区域涂上不同的颜色,表示不同的结果。例如,如果某个结果的发生概率是 75%,而另一个结果的概率是 25%,我们可以将三分之四的墙面涂成蓝色,其余四分之一涂成粉色。如果我们投掷 100 次飞镖,预计大约 75 次会落在蓝色区域,剩下的会落在粉色区域。

我们的第一步是选择一个硬币。由于我们无法分辨两枚硬币,选择公*硬币的概率是 50:50。为了表示这一点,我们可以想象将墙面分成两个相等的区域。我们将公*区域涂成亚麻色(类似米色),将作弊区域涂成红色,如图 4-2 所示。当我们向墙面投掷飞镖时,飞镖落在公*区域的概率是 50:50,代表选择公*硬币的概率。

F04002

图 4-2:当我们随机选择两个硬币中的一个时,这就相当于向一面被涂成两个相等区域的墙投掷飞镖,一个区域代表公*硬币,一个区域代表作弊硬币。

让我们更有信息地涂色墙面,告诉我们第一次投掷时,正面或反面出现的概率有多大。我们知道公*硬币正面或反面的概率是 50:50,因此我们可以将公*区域分成两个相等的部分,一个表示正面,一个表示反面,如图 4-3 所示。

在图 4-3 中,我们还将作弊面分开。因为我们从朋友那里知道,作弊硬币出现正面的概率是三分之二,所以我们将三分之二的区域分配给正面,三分之一分配给反面。图 4-3 总结了我们对系统的所有了解。它告诉我们选择任一硬币的概率(对应于黄色或红色区域),以及在每种情况下出现正面或反面的概率(根据正面和反面区域的相对大小)。

F04003

图 4-3:我们可以根据我们已经知道的信息,将墙上的公*和作弊区域分成正面和反面区域,以了解它们在翻动时出现的可能性。

如果我们向像图 4-3 那样涂漆的墙壁投掷飞镖,我们的飞镖会落在与某个硬币和正面或反面相对应的区域。但由于我们已经翻过硬币并观察到正面,所以我们知道我们落在了公*正面或作弊正面区域。

记住我们的问题:我们选择公*硬币的概率是多少?我们可以通过使用得到正面这个信息来进一步明确这个问题。稍后我们会看到,提问的最佳方式是以一个模板的形式,询问:“在(某事 1)为真,给定(某事 2)为真时,(某事 1)为真的概率是多少?”在这种情况下,这个问题变成了:“我们看到正面后,选择公*硬币的概率是多少?”

我们可以用图示表示这一点。它是公*正面区域的面积与所有可能给我们正面的总面积之比,后者是公*正面和作弊正面面积之和。图 4-4 显示了这个比率。

F04004

图 4-4:如果硬币正面朝上,公*硬币的概率有多大?它是公*硬币给我们正面的区域大小与所有给我们正面的区域总面积的比值。

让我们稍微思考一下这张图。由于作弊正面区域比公*正面区域大,这使得我们的“正面”结果更可能来自于落在作弊区域。换句话说,现在我们已经看到硬币正面朝上,稍微更可能是作弊硬币,就像飞镖投掷到图 4-3 中那样,飞镖更可能落在作弊正面区域而不是公*正面区域。

后面我们将讨论“某件事可能发生的方式”,或“某件事可能发生的所有方式”。这意味着如果我们正在寻找某个属性为真的情况,我们需要考虑所有能导致该结果的可能事件。在这种情况下,图 4-4 的下半部分是我们得到正面的所有方式的总和。换句话说,我们可以通过公*硬币或作弊硬币得到正面,所以表示“得到正面所有方式”意味着将这两种可能性结合起来。

将投币表达为概率

让我们用概率术语重新表述图 4-4。得到公*硬币并且得到正面的概率是 P(H,F)(或等价地 P(F,H))。得到作弊硬币并且得到正面的概率是 P(H,R)。

现在我们可以将图 4-4 中的面积比率解释为一个概率陈述。该图显示了我们的硬币是公*硬币的概率,前提是我们知道它正面朝上。这就是 P(F|H),代表“在观察到正面后,我们有公*硬币的概率”。换句话说,这个条件概率就是我们问题的答案。

我们可以将这一切汇总到图 4-5 中。

F04005

图 4-5:将图 4-4 转化为概率语言

我们能否将数字代入这个图表并得出实际的概率?当然可以,在这种情况下我们可以,因为这个情形被设计得很简单。但通常情况下,我们无法知道这些联合概率,它们也不会容易找出。

不用担心。图 4-5 右侧的所有框都是联合概率,我们在第三章中看到,我们可以用两种不同且等价的方式来表示任何联合概率,每种方式都涉及一个简单的概率和一个条件概率。这些术语通常更容易为我们赋予数字。这两种方法在此作为图 4-6 和图 4-7 重复出现。

F04006

图 4-6:我们可以将两个事件 A 和 B 的联合概率表示为条件概率 P(A|B)乘以 B 的概率,即 P(B)。

F04007

图 4-7:我们可以将两个事件 A 和 B 的联合概率表示为条件概率 P(B|A)乘以 A 的概率,即 P(A)。

让我们在图 4-5 中写出 P(F|H)的表达式,不带有彩色框,然后将 P(H,F)替换为图 4-7 中的表达式,后者告诉我们,我们可以通过将从公*硬币得到正面的机会 P(H|F)与我们一开始就拥有公*硬币的概率 P(F)相乘,来计算 P(H,F),即正面朝上并且使用公*硬币的联合概率。这个变化如图 4-8 所示。

F04008

图 4-8:我们在图 4-5 中的比率代表 P(F|H),即在正面朝上的情况下,我们拥有公*硬币的概率。

对另外两个联合概率做同样的处理,替换它们为扩展版本(其中第一个值就是 P(H,F))。图 4-9 展示了结果。

F04009

图 4-9:我们也可以将图 4-8 中另外两个联合概率替换为它们的扩展版本。

由于我们通常可以为这个扩展版本中的所有符号表达式找到数字,因此这是一种有用的方式来计算 P(F|H)。

让我们用这个表达式来找出我们刚刚抛掷的是公*硬币的概率。我们需要为图 4-9 中的每个项分配一个数字。P(F) 是我们开始时选择公*硬币的概率。我们已经知道 P(F)=1/2。P(R) 是我们开始时选择不公*硬币的概率,也为 1/2。P(H|F) 是在选择公*硬币的情况下得到正面的概率。按定义,它是 1/2。P(H|R) 是从不公*硬币得到正面的概率。根据我们考古学家朋友的说法,它是 2/3。

现在我们已经有了所有需要的数字,可以计算出在我们刚刚抛掷硬币并得到正面时,硬币是公*的概率。图 4-10 展示了将这些数字代入并完成计算步骤(遵循数学惯例,我们先进行乘法运算,再进行加法运算——这让我们可以省略一些干扰性的括号)。

F04010

图 4-10:计算在我们刚刚看到正面时,硬币是公*的概率

结果是 3/7 或大约 0.43。这有点令人惊讶。它告诉我们,仅仅一次抛掷硬币后,我们就已经可以通过一种有原则的方式说出,公*硬币的概率只有 43%,因此不公*硬币的概率为 57%。这是一种 14% 的差异,仅凭一次抛掷!

顺便提一下,频率主义者在仅仅一次抛掷后是绝不会敢于判断硬币是否公*的,而这种贝叶斯方法已经用具体的概率描述了硬币的情况。

回到我们第一次抛掷的情况,假设这次我们得到了反面。那么我们现在想要找出 P(F|T),即在看到反面时,硬币是公*的概率。回忆一下,偏差是得到正面的概率,因此得到反面的概率是 1 – 偏差。对于公*的硬币,它得到反面的概率,或 P(T|F),是 (1 – (1/2)) = 1/2。对于不公*的硬币,我们从朋友那里得知,偏差是 2/3,因此 P(T|R) 是 (1 – (2/3)) = 1/3。选择公*和不公*硬币的概率,分别由 P(F) 和 P(R) 给出,都是 1/2,就像之前一样。我们将这些值代入,找出 P(F|T),即在得到反面时,我们选择了公*硬币的概率。图 4-11 展示了步骤。P(F|T) 的表达式与 P(F|H) 类似,只不过 H 和 T 交换了位置。

这是一个更加戏剧性的结果,它告诉我们,得到反面意味着我们正在抛掷公*硬币的概率为 60%(因此抛掷不公*硬币的概率为 40%)。这仅仅通过一次抛掷就大大提升了我们的信心!请注意,结果并不是对称的。如果我们得到正面,公*硬币的概率是 43%,但如果我们得到反面,公*硬币的概率是 60%。

我们已经看到,通过一次抛硬币可以获得很多信息,但即使是 60%的概率也远未确定。进行更多的抛硬币实验给了我们机会去找出更精确的概率,稍后在本章中我们将学习如何做到这一点。

F04011

图 4-11:如果我们抛硬币时得到了反面呢?

贝叶斯法则

让我们找另一种写 P(F|H)的方式。在图 4-5、图 4-8 和图 4-9 中,我们看到了几种不同的方式来表示给定看到正面后我们挑选到公*硬币的概率。

让我们回到图 4-8 中的版本(在图 4-12 顶部重复出现)。注意,比例的底部部分 P(H,F) + P(H,R)结合了我们可能获得正面的所有方式的概率(毕竟,正面必须来自公*硬币或作弊硬币)。如果我们处理的是例如 20 枚硬币,那么我们必须写出 20 个联合概率的和,这将使得表达式非常复杂。我们通常使用快捷方式,将这些联合概率简写为 P(H),即“得到正面的概率”。这隐含着所有可能获得正面的方式的和。如果我们有 20 枚不同的硬币,这将是每一枚硬币给我们正面的概率之和。图 4-12 展示了这种简化的符号。

F04012

图 4-12:来自图 4-8 的最后一行,但我们已将比例底部的部分替换为符号 P(H)

图 4-13 单独展示了这个最新的版本。这就是我们在本章前面提到的著名的贝叶斯法则贝叶斯定理。这里我们采用了数学家的约定,将两个并排的值相乘。

F04013

图 4-13:贝叶斯法则,或者通常书写的贝叶斯定理

用文字表达,我们想要找的是 P(F|H),即在我们刚刚抛硬币并看到正面时,硬币是公*的概率。为了确定这个,我们结合了三条信息。首先是 P(H|F),即如果我们确实有一枚公*的硬币,它出现正面的概率。我们将这个概率与 P(F)相乘,后者是我们拥有公*硬币的概率。正如我们所看到的,这个乘法只是评估 P(H,F)的一种更便捷方式,P(H,F)是我们硬币公*正面朝上的概率。最后,我们将所有结果除以 P(H),即硬币出现正面的概率,考虑到公*硬币和作弊硬币的情况。这就是我们使用任一硬币时,得到正面的可能性。

贝叶斯定理通常以图 4-13 的形式写出,因为它将事情分解成我们可以方便测量的部分(字母通常会根据讨论的内容进行更改)。我们只需用相应的数值替换每个术语,条件概率就会出来,表示在获得正面朝上的情况下,我们选中了公*硬币。记住,P(H)代表联合概率的总和,正如我们在图 4-12 中所看到的。

这就是为什么我们对贝叶斯定理提出的问题需要以条件概率的形式呈现:在(某个事件 2)为真的情况下,(某个事件 1)为真的概率是多少?因为贝叶斯定理正是提供了这种形式的答案。如果我们无法将问题以这种形式表达,那么贝叶斯定理就不是回答该问题的合适工具。

贝叶斯定理讨论

贝叶斯定理可能很难记住,因为有很多字母漂浮在周围,每个字母都必须放在正确的位置。但好在我们可以随时轻松地完美地重新推导出定理。

让我们以两种形式写出 F 和 H 的联合概率(即 P(F,H)和 P(H,F))。我们知道这两者是相同的:拥有一枚公*硬币并且正面朝上的概率。将它们替换为展开版本,如在图 4-8 中所做的,得到图 4-14 中的第二行。

要得到贝叶斯定理,只需将每一边除以 P(H),如第三行所示。结果就是最后一行,这就是贝叶斯定理。如果我们需要它而忘记了该如何推导,这是一种方便的方法。

F04014

图 4-14:如果我们忘记贝叶斯定理,如何重新推导,或者快速展示它为什么成立。

贝叶斯定理中的四个术语都有一个约定俗成的名称,总结见图 4-15。

F04015

图 4-15:贝叶斯定理中的四个术语及其名称。

在图 4-15 中,我们使用了传统的字母 A 和 B,它们代表任何类型的事件和观测。通过这些字母,P(A)是我们对是否拥有公*硬币的初步估计。因为它是我们在抛硬币之前用于“我们选择了公*硬币”的概率,所以我们称 P(A)为先验概率,或简称先验

P(B)告诉我们得到我们所得到结果的概率,在本例中即硬币正面朝上的概率。我们称 P(B)为证据。这个词可能会令人误解,因为有时它指的是类似犯罪现场的指纹。在我们的上下文中,证据是事件 B 通过任何方式发生的概率。记住,证据是我们可能选择的每一枚硬币朝正面出现的概率之和。

条件概率 P(B|A)告诉我们在假设我们有一枚公*硬币的情况下,获得正面朝上的概率。我们称 P(B|A)为似然

最终,贝叶斯定理的结果告诉我们,在观察到正面时,选择公正硬币的概率。因为 P(A|B)是计算结束时得到的结果,所以它被称为后验概率,简称后验

在本章早些时候,我们提到贝叶斯方法的一个优点是它使我们能够明确识别我们的先入为主的观点和预期。现在我们可以看到,我们通过选择先验 P(A)来实现这一点。一般来说,我们知道从实验设置中获得的似然性 P(B|A)和证据 P(B),但我们必须猜测先验 P(A)。如果我们只进行一次实验,这可能会成为问题,因为如果我们的先验估计是错误的,那么后验也会是错误的。稍后我们将看到,如果我们能进行多次实验(比如多次掷硬币),那么我们可以在每次掷硬币后使用贝叶斯定理来细化我们的初始先验,从而得到一个越来越好的 P(A)描述,这将使我们得到更准确的结果,即我们真正关心的后验 P(B|A)。

在我们的小硬币测试示例中,我们很容易得出了先验的值,但在更复杂的情况下,选择一个合适的先验可能会更加复杂。有时,这取决于经验、数据、知识,甚至仅仅是关于先验应该是什么的直觉。由于我们的选择中有一些主观性或个人性,所以独自选择先验被称为主观贝叶斯。另一方面,有时我们可以使用规则或算法来为我们选择先验。如果这样做,那就叫做自动贝叶斯(Genovese 2004)。

贝叶斯定理与混淆矩阵

在第三章中,我们探讨了使用混淆矩阵来帮助我们正确理解测试结果。让我们再次看看这个概念,不过这次使用贝叶斯定理。

与其创建一些人为的、做作的例子,不如使用一些现实且日常的情境。你是星舰忒修斯的舰长,正在执行一项深空任务,目标是寻找可以开采原材料的岩石类无人行星。你刚刚发现了一颗有潜力的岩石行星。开采它将会很棒,但你的命令是永远不能开采有生命的行星。那么,大问题就是:这颗行星上有生命吗?

根据你的经验,这些岩石星球上的大部分生命只是一些细菌或蘑菇,但生命就是生命。按照协议,你派出探测器进行调查。探测器着陆并报告“没有生命”。

因为没有探测器是完美的,我们现在必须问这样一个问题:“在探测器什么都没检测到的情况下,星球含有生命的概率是多少?”这个问题非常适合使用贝叶斯规则。一个条件(我们称之为 L)是“生命存在”,其中正值意味着星球上有生命,负值意味着星球上没有生命(这样我们可以开始开采)。另一个条件(我们称之为 D)是“检测到生命”,其中正值表示探测器检测到生命,负值表示没有检测到。

我们真正想避免的情况是,在一个有生命的星球上进行开采。这是一个假阴性:探测器报告为阴性,但其实不该这样。这将是非常糟糕的,因为我们不想干扰,更不想破坏任何形式的生命。假阳性则不那么令人担忧。假阳性是那些荒芜的星球,但探测器认为它发现了生命迹象。唯一的缺点是我们没有开采一个本可以开采的星球。虽然有经济损失,但仅此而已。

建造我们探测器的科学家也有这些相同的担忧,所以他们努力减少假阴性的发生。他们也试图减少假阳性,但那并不像假阴性那么关键。

实际上,一些有生命的星球可能并不是所有地方都有生命,因此探测器可能降落在一个有生命的星球的无生命区域,并且检测不到任何东西。为了简化起见,我们不考虑这种情况,假设所有的错误结果(即遗漏了本应存在的生命,或者说存在生命时却表示没有生命)都由探测器引起,而不是星球本身。

他们派出去的探测器性能如图 4-16 所示。为了得到这些数据,他们将探测器送到了 1,000 个我们希望开采的已知类型星球,其中 101 个已知含有生命。这些数值变成了我们的先验:每 1,000 颗星球中,我们预计有 101 颗含有生命。

F04016

图 4-16:我们探测器的性能

探测器正确地报告它发现了生命(即真正的阳性)100 次,出现在 1,000 次中。换句话说,在 101 个有生命的星球中,探测器只错过了 1 次生命迹象(假阴性)。

在 899 个空星球中,探测器正确地报告没有生命(真正的阴性)869 次。最后,它错误地报告在一个荒芜星球上发现生命(假阳性)30 次。总的来说,这些结果还不错,因为它们偏向于保护生命。

使用字母 D 表示“检测到生命”(探测器的结果),字母 L 表示“生命存在”(实际情况),我们可以在图 4-17 的混淆矩阵中总结这些结果。对于边际概率,我们写 not-D 表示探测器结果为“未检测到生命”(即探测器表示没有生命),写 not-L 表示“无生命存在”(即这个星球上真的没有生命)。

F04017

图 4-17:总结图 4-16 的混淆矩阵,展示了我们生命探测探测器的表现。四个边际概率显示在右侧和底部的边缘。

图 4-18 汇总了四个边际概率,以及我们将使用的两个条件概率。

F04018

图 4-18:根据图 4-17 中的数据,汇总了四个边际概率和两个条件概率。

为了计算 P(D|L),即探测器报告发现生命的概率,假设行星上确实存在生命,我们找到了探测器发现生命的次数(100 次),并将其除以发现生命的行星数量(101 颗)。也就是说,我们得到了 TP / (TP + FN),在第三章中我们看到这被称为召回率。100/101 的值约为 0.99。

为了计算 P(not-D|L),我们反向进行了计算。它在 101 颗行星中错过了一次发现生命的机会。我们得到了 FN / (TP + FN),在第三章中我们看到这被称为假阴性率。结果是 1/101,约为 0.01。 (为了更深入了解探测器的行为,我们还可以使用第三章中的定义来计算探测器的准确率为 969/1000,即 0.969,精确度为 100/130,约为 0.77。)

现在我们可以回答最初的问题了。假设我们的探测器表示没有生命,实际上有生命的概率是 P(L|not-D)。利用贝叶斯定理,我们将前一段或图 4-18 中的数据代入公式,得到了图 4-19。

F04019

图 4-19:计算在探测器报告未检测到生命的情况下,行星上有生命的概率。

这令人放心。在探测器表示没有生命的情况下,行星上确实有生命的概率大约是千分之一。这已经相当有信心了,但如果我们想更有把握,我们可以发射更多的探测器。稍后我们将看到,每增加一个探测器,如何进一步增强我们对于是否有生命的信心。

让我们换个角度,假设探测器返回了一个积极的报告,告诉我们它确实探测到了生命。那对我们来说是一个财务损失,因此我们需要确认这是否正确。那么,我们能有多大的信心,认为那颗行星上真的有生命呢?为了找出这个答案,我们再次使用贝叶斯定理,但这次我们要计算 P(L|D),即在探测器检测到生命的情况下,行星上有生命的概率。让我们通过图 4-20 中的数据来计算。

F04020

图 4-20:计算在探测器报告发现生命迹象的情况下,行星上有生命的概率。

哇。如果探测器说它发现了生命,那么仅凭这一颗探测器,我们可以有约 77%的信心相信真的有生命。这远远没有从负面报告中得到的信心高,但那是因为这个探测器的设计使得它报告假阳性的概率大于假阴性。由于我们总是希望在保护生命方面有所保留,总的来说这些结果是好的。

正如我们之前提到的,我们可以发送更多的探测器来增加对结果的信心,但无论如何,我们永远也无法达到绝对的确定性。在某个时刻,无论是第一个探测器、还是第 10 个,或第 10,000 个探测器,我们都需要做出是否开采行星的判断。

现在我们来看看发送更多探测器如何帮助我们提高信心。

重复贝叶斯规则

在前面的章节中,我们看到如何使用贝叶斯规则来回答类似“在(某个事情 2)为真时,(某个事情 1)为真”的概率是多少? 这样的问题。我们把这个问题当作一个一次性事件来处理,输入我们已知的系统信息,得到一个概率值。

一次事件或测量的结果并不足以得出结论。让我们回到我们之前的硬币游戏。回想一下,一个硬币是公*的,另一个硬币被改装成了正面朝上的概率大于 50%。我们从两个硬币中随机选择一个,抛了它,发现它是正面朝上,然后我们就得出了一个概率,认为我们选择的是公*的硬币。就这样结束了。

但我们可以继续推进。在本节中,让我们将贝叶斯规则放入循环的核心,每一条新数据都会给我们一个新的后验概率,然后我们将这个后验概率作为下一次观测的先验概率。随着时间的推移,如果数据是一致的,先验概率应当逐渐逼*我们所寻找的基本概率。

这里是基本的直觉,在我们进入细节之前。我们通常知道似然性 P(B|A)和证据 P(B),这些是通过我们的实验设置得出的,因此它们是已知的。但我们很少知道先验概率 P(A)。我们需要为此值做出一个估计,因此我们会考虑这个问题并做出最佳猜测。由于这就完成了贝叶斯规则所需的所有值,我们可以将它们代入并得到后验概率 P(A|B)。

现在进入有趣的部分。后验概率告诉我们,在事件 B 发生的情况下,A 的概率是多少,但我们知道事件 B 发生了。无论是硬币正面朝上,还是探测器发现行星上有生命,我们都知道 B 发生了,因为我们选择了计算 P(A|B),而不是计算 B 未发生时 A 的概率。既然我们知道 B已经发生,那么 P(A|B)就是 P(A)。

让我们用另一个例子来表达这一点,以便更好地理解。假设事件 B 是“今天很暖和”,事件 A 是“人们穿着凉鞋”。假设今天很暖和。那么,“人们穿着凉鞋,假设今天很暖和的情况下”的概率就等同于“人们穿着凉鞋”,因为我们已经观察到今天很暖和。

换句话说,后验 P(A|B)变成了 P(A),也就是我们的先验!这就是关键的洞察。当我们知道 B 发生了时,贝叶斯规则的输出给了我们 P(A)的新估计。因此,贝叶斯规则为我们提供了一种基于实验结果来改变和改进我们对系统的期望或信念的方法。

总结一下,我们猜测 P(A)。然后我们进行实验。关键是,我们根据是否观察到事件 B,选择计算 P(A|B)或 P(A|not-B),正如在图 4-19 和 4-20 中所看到的那样。选择评估贝叶斯规则的哪个版本是使整个循环运作的魔力。我们选择 P(A|B)或 P(A|not-B),这取决于我们实际观察到的结果,这会将新信息引入我们的过程中。这些新信息帮助我们细化我们对所学习系统的理解。因此,在做出这个选择后,我们将数据代入贝叶斯规则的适当形式,并得出一个后验概率。这成为新的先验。凭借这个新的 P(A),我们再次进行实验,再次使用贝叶斯规则,根据 B 是否发生来计算 P(A|B)或 P(A|not-B),并再次通过使用该结果作为下一次实验的新的 P(A)或先验来更新我们的期望,依此类推。随着时间的推移,我们对 A 的概率或 P(A)的信念或期望将从一个猜测逐渐精炼为一个实验支持的值。

让我们将这个描述包装成一个循环,从猜测先验 P(A)开始,然后通过进行更多实验来精炼。

后验-先验循环

在图 4-15 中,我们给贝叶斯规则的各个项命名。这些不是唯一的命名方式。我们也将贝叶斯规则中的事件(我们称之为 A 和 B)用假设和观察的术语来表示(有时缩写为 Hyp 和 Obs)。我们的假设陈述了我们想要发现其真相的某件事(例如,“我们有一枚公*的硬币”)。观察是实验结果(例如,“我们得到了正面”)。图 4-21 展示了带有这些标签的贝叶斯规则。

F04021

图 4-21:用描述性标签表示贝叶斯规则中的 A 和 B

在我们的抛硬币示例中,我们的假设是“我们选择了公*的硬币”。我们进行了实验并得到了一个观察结果,即“硬币正面朝上”。我们将先验概率 P(Hyp)与给定该假设的观察的似然概率 P(Obs|Hyp)结合,得到观察和假设同时为真的联合概率。然后,我们通过证据 P(Obs)进行缩放,即观察结果可能通过任何手段发生的概率。最终结果是后验概率 P(Hyp|Obs),它告诉我们在观察到的情况下,假设为真的概率。

如约所说,让我们将这个过程封装在一个循环中。我们计算后验概率,然后(因为我们知道观察已经发生)可以将其作为先验,重复实验。结果是一个新的后验,我们可以将其作为下次的先验,依此类推。每次循环,我们的先验在描述系统时会变得更加准确,因为每次实验的结果都会被纳入。

这个循环的示意图见图 4-22。

F04022

图 4-22:每当我们有新的观察结果时,我们将证据、观察的可能性和先验结合起来,计算后验概率。然后,这个后验概率将在评估新的观察结果时作为先验。

总结一下,我们从先验开始。这来自分析、经验、数据、算法或仅仅是猜测。然后我们进行实验(或进行观察)并启动循环。我们将证据、观察的可能性和先验结合起来,选择贝叶斯规则的两种形式之一,用它来计算后验概率。这个后验概率成为我们新的先验。现在,当另一个观察结果到来时,我们再次进入循环,使用我们的新先验。

这个想法是,在每次循环中,我们的先验会改进,从最初的猜测逐渐接*一系列高度可能的答案。先验之所以改进,是因为每次循环时,先验都包含了最新的观察结果,以及所有先前的观察结果。

让我们通过掷硬币的例子来看一下这个循环是如何运作的。

贝叶斯循环的实际应用

记得我们的考古学家朋友和她的双硬币问题吗?让我们将它概括一下,这样我们就可以尝试一些变化,探索如何使用图 4-22 中显示的贝叶斯规则循环来回答问题。

与其只有一个装有公*硬币和有偏硬币的袋子,不如假设她找到了许多这样的袋子,其中没有两个有偏硬币的偏差是相同的。每个袋子上标明了其有偏硬币的偏差(偏差通常用小写希腊字母θ [theta]表示)。

她认为,在玩家开始游戏之前,他们会达成协议,决定他们希望有偏硬币的偏差有多大。然后他们会选择相应的袋子,按常规操作,挑选出两枚硬币中的一枚,然后押注选中了哪一枚硬币。

像他们一样,我们首先选择一个袋子,然后从袋子里挑选一枚硬币。接着,我们将确定选中公*硬币的概率。我们可以通过多次掷硬币、记录正反面结果,并观察贝叶斯规则如何处理每次掷硬币的观察结果(即每次翻转后的结果),来使用贝叶斯规则的重复形式。

假设我们进行 30 次抛硬币实验。即便数据量这么小,我们也可能会看到一些不寻常的事件。例如,我们可能使用的是公*的硬币,但结果却是 25 次正面和 5 次反面。这虽然不太可能,但也是有可能的。更可能的是我们用的是一个偏向某一面的伪造硬币。让我们看看贝叶斯定理如何帮助我们根据多次抛掷来判断我们拿的是哪一枚硬币。

我们从选择一个袋子开始,里面有一枚公*硬币和一枚偏置为 0.2 的伪造硬币,这意味着我们期望它每 10 次抛掷中出现 2 次正面。假设我们将这枚硬币抛掷 30 次,其中只有 20%(即 6 次)是正面,其他 24 次都是反面。我们是拥有公*硬币,还是伪造硬币呢?因为从公*硬币的 30 次抛掷中,我们预期会得到 15 次正面,而伪造硬币则预期只有 6 次正面,所以 6 次正面似乎是我们拥有伪造硬币的一个有力证据。

图 4-23 显示了贝叶斯定理在每次抛掷后的结果。如之前所示,公*硬币的概率用亚麻色(或米色)表示,而伪造硬币的概率用红色表示。这两种概率总和为 1。

F04023

图 4-23:随着每次抛掷,公*硬币的概率用亚麻色(米色)表示。每根柱子下方的字母是导致该柱子的观察结果。

要理解这个图表传达的信息,首先看一下底部的字母。这些字母是“H”或“T”,表示该次抛掷的结果。在这种情况下,我们有 24 次反面,偶尔会有 6 次正面。接下来,考虑这些柱子,从左边开始。最左边的列显示我们在还没有进行任何抛掷之前认为我们拥有的硬币类型,因此概率都是 0.5。毕竟,选择任一枚硬币的机会是相等的,我们还没有抛掷硬币来获得任何数据。右边的柱子显示了贝叶斯定理的后验概率,观察到第一次抛掷为反面(T)后。因为公*硬币抛出反面的概率是 0.5,而伪造硬币抛出反面的概率是 0.8,所以抛出反面表明我们更可能拥有伪造硬币。继续往右看,大约 80%的抛掷结果是反面。这是我们从伪造硬币中预期的结果,因此它的概率迅速接* 1。注意,当我们连续出现几个正面时,伪造硬币的概率大约会下降到三分之二的位置,但随着每次新的反面抛掷,概率又会上升。

每个条形图中的浅色或米色块的高度表示 P(F)的值,即我们选择公正硬币的概率。在每次抛掷后,我们使用贝叶斯法则计算 P(F|H)或 P(F|T),以适应我们观察到的结果。这成为 P(F)的新值,即我们认为自己拥有公正硬币的概率。我们利用这个值计算下一次抛掷后的后验概率。如前所述,这个选择是使整个循环运作的关键步骤。在每次实验后,我们选择返回 P(F|H)或 P(F|T)的贝叶斯法则版本,取决于我们观察到的结果。这个选择使我们能够使用后验概率作为新的先验概率,因为它反映了实际发生的情况。

接*实验结束时,我们得到公正硬币的概率几乎是 0。它永远不会完全为 0,因为我们永远不能绝对确定这不是一枚具有极其不寻常抛掷模式的公正硬币,因此这个选项始终至少有一丝概率。

在这个例子中,我们得到的数据清楚地显示我们拥有作弊硬币。我们继续保留这枚硬币并进行另一轮实验。假设下一轮我们得到的正面更少,也许总共只有三次,这使得作弊硬币的可能性更强。将此数据带入循环得到的结果见图 4-24。

F04024

图 4-24:我们使用同一枚偏置为 0.2 的硬币,但这次我们在 30 次抛掷中恰好只得到了三次正面。

经过仅仅四次抛掷后,我们对自己拥有作弊硬币的信心达到了 90%。经过 30 次抛掷后,公正硬币的概率再次几乎为 0,但从未完全为 0。

假设我们再进行一次 30 次抛硬币的实验,这次我们恰好得到了 24 次正面。这两枚硬币的结果都与之不太匹配。我们预计公正硬币会得到 15 次正面,而作弊硬币则预计只有 6 次正面。仅从这两种选择来看,公正硬币似乎更有可能。图 4-25 展示了我们使用贝叶斯法则的结果。

F04025

图 4-25:我们使用同一枚偏置为 0.2 的硬币,但这次我们在 30 次抛掷中恰好得到了 24 次正面。

即使公正硬币应该大约一半的时间是正面,作弊硬币的正面概率只有 20%。这些正面结果对于任何一枚硬币来说都是不太可能的,但对于作弊硬币来说,它们的可能性要低,这增强了我们对拥有公正硬币的信心。

我们已经看到了这枚硬币的三种不同抛掷结果,从几乎全是反面到几乎全是正面。让我们通过抛掷 10 枚具有不同偏置的硬币来概括这些结果。我们为每枚硬币创造 10 种不同的抛掷模式,每种模式有不同的正反面比例。我们可以对每种硬币的每种模式使用贝叶斯法则,创建 100 种情景。结果见图 4-26,每个单元格都是一个小条形图,类似于图 4-23 到图 4-25 中的条形图。

让我们从左下角开始。在这个位置,我们的水*坐标值(标记为“作弊硬币偏差”)大约是 0.05。这意味着我们预计这枚硬币出现正面的概率约为 1/20。我们的垂直坐标值(标记为“翻转序列偏差”)也大约是 0.05。这意味着我们将创建一个人工的观察序列,像之前那样,每次观察到正面的概率为 1/20。在这种情况下,我们为该单元格创建的 30 次观察序列中的正面数量与我们期望从作弊硬币中得到的正面数量相匹配,因此我们对硬币作弊的信心(用红色表示)迅速增加。

让我们向上移动三个单元格。由于我们没有在水*方向上移动,我们的水*坐标值仍然是 0.05,所以我们抛掷的硬币应该有 1/20 的概率出现正面。但现在垂直坐标大约是 0.35,所以我们看到的是一个正面出现频率明显更高的模式。面对这些正面,似乎我们得到的硬币翻转序列是来自一枚公*硬币的可能性更大,而不是来自一枚非常不公*硬币的异常序列。随着翻转次数的增加,我们对硬币公*性的信心也在增强。

F04026

图 4-26:抛掷硬币 30 次,并使用贝叶斯规则的重复应用来判断我们正在翻转的是哪一枚硬币。每个方格报告我们对一次 30 次随机翻转的结果。每一行使用相同的正反面序列。

每个单元格可以以相同的方式理解。我们构造一个包含 30 次正反面的模式,其中正面出现的相对比例由垂直位置决定,我们要判断这个模式更可能来自一枚公*硬币,还是来自一枚其正面概率由水*位置决定的硬币。

在网格的中间部分,两个值都接* 0.5 时,几乎无法分辨。作弊硬币出现正面的频率几乎与公*硬币相同,正反面模式大致*分,所以我们可能正在翻转任一枚硬币。两者的概率都接* 0.5。然而,当我们制作正面较少的模式(图形下部)或正面较多的模式(图形上部)时,我们可以判断该模式是否与具有较低正面概率(左侧)或较高正面概率(右侧)的作弊硬币匹配。

一系列 30 次翻转是很有启发性的,但我们仍然可能遇到一些不寻常的结果(比如从一枚公*硬币中得到 25 次正面)。如果我们将每个图表中的翻转次数增加到 1,000 次,就像在图 4-27 中那样,这种不寻常的序列变得更少,模式也变得更清晰。

F04027

图 4-27:这个图表与图 4-26 的设置相同,但现在我们每枚硬币翻转 1,000 次。

在左下角和右上角,硬币翻转的模式与做手脚的硬币的偏差更为吻合,而贝叶斯定理将“我们选的是公*硬币”的先验推向 0。在左上角和右下角,翻转的模式更符合公*硬币的特点,先验则向 1 靠拢。

图 4-26 和图 4-27 的普遍结论是,我们做的观察越多,我们对假设是否正确的确定性就越强。每次观察都会增加或减少我们的信心。当观察结果与我们的先验(“我们有公*的硬币”)一致时,我们对这个先验的信心就会增加。当观察结果与先验相矛盾时,我们的信心就会下降,而由于在这种情况下只有一个其他选择(“我们有做手脚的硬币”),那个假设的概率就会增加。即使我们只有少量的观察结果,我们通常也能很早就获得很大的信心。

多重假设

我们已经看过如何利用贝叶斯定理通过将假设与观察结果结合起来(或许是反复地)来改进假设。但是没有什么限制我们只能测试一个单一的假设。实际上,我们一直在做多个假设。就在上一节中,我们明确看到了“这个硬币是公*的”和“这个硬币被做了手脚”这两个假设是同时更新的。由于我们知道这两个概率之和必须为 1,因此知道其中一个就能推知另一个,所以我们只需跟踪其中一个即可。

但是如果我们愿意的话,我们也可以明确地计算出两个概率。我们只需要使用两个贝叶斯定理的副本。假设一次翻转结果是正面。然后我们可以独立地计算出拥有公*硬币的条件概率 P(F|H)和拥有做手脚硬币的条件概率 P(R|H)。这在图 4-28 中展示了。

F04028

图 4-28:计算两个假设的概率。线(c)明确展示了如何在两种情况下计算 P(H)。

从图 4-28 中我们可以看到,公*硬币和做手脚硬币的概率只是硬币翻转为正面的两种方式的比例。如果我们想同时跟踪多个假设,我们可以使用多个贝叶斯定理副本,每次有新的观察结果时都更新它们。

我们可以利用这个能力再次帮助我们的考古学家朋友。她刚刚发现了一个箱子,里面有一款新游戏的零件,再次地,游戏使用袋子来存放成对的硬币。像之前一样,袋子里的做过手脚的硬币有不同的偏差。因为一个偏差极大的硬币(即正面出现的概率远大于反面,或者反之)会更容易被发现,她认为可能存在不同水*的玩家,从新手到老手。新手会玩一些偏差较大的硬币,但随着玩家技能的提高,他们会转向偏差越来越接* 0.5 的做过手脚的硬币。这些硬币更难被察觉,从而导致更长的游戏时间和更有风险、更加复杂的投注。

由于我们的朋友想了解她的发现,所以她将所有硬币倒入一个大箱子里,并要求我们找出每个硬币的偏差。暂时假设只有五个可能的偏差值——0、0.25、0.5、0.75 和 1(回想一下,偏差为 0.5 对应的是公*的硬币)——因此我们将创建五个假设。我们将它们编号为 0 到 4,分别对应不同的偏差值。假设 0 表示:“这是偏差为 0 的硬币”,假设 1 表示:“这是偏差为 0.25 的硬币”,以此类推,直到假设 4,它表示:“这是偏差为 1 的硬币。”

现在我们将从箱子中随机挑选一枚硬币,反复投掷,并尝试确定哪个假设最有可能。为了开始,我们需要为每个假设做一个先验估计。记住,每次循环时这些先验会被更新,因此我们只需要一个好的初始猜测。因为我们对选中的硬币一无所知,假设每个硬币被选中的概率是相同的,所以所有五个先验值都有 1/5 的概率是正确的,或者 1 / 5 = 0.2。

注意,如果我们想的话,可能会做得更复杂。每对硬币中都有一个是公*的,一个是做过手脚的。假设我们有 16 枚硬币。如果是这种情况,那么我们有 8 枚公*的硬币和 8 枚做过手脚的硬币(每个偏差值允许两个)。那么,选择一个公*硬币的概率是 8 / 16 = 0.5,而选择每个做过手脚的硬币的概率是 2 / 16 = 0.125。这可能是一个更好的先验,因为它利用了我们已知的更多信息。从一开始就使用一个更好的先验意味着我们的循环会更快地集中在高概率的解上。但贝叶斯方法的其中一个优点是,我们几乎可以从任何一个接*的先验开始,最终都能得到相同的结果。为了简便起见,我们使用第一个先验,即每个假设的值为 0.2。

唯一需要我们指定的是每个硬币的似然性。但我们已经知道这些,因为它们就是偏差。也就是说,如果硬币的偏差为 0.2,那么它正面朝上的概率就是 0.2。因此,反面朝上的概率就是 1 − 0.2 = 0.8。

假设 0,表示“我们有一枚偏向 0.0 的硬币”,其得到正面朝上的可能性为 0,反面朝上的可能性为 1。假设 1,表示“我们有一枚偏向 0.2 的硬币”,其得到正面朝上的可能性为 0.2(即 20%),得到反面朝上的可能性为 0.8(即 80%)。我们的可能性在图 4-29 中进行了绘制。

由于硬币本身在我们抛掷并收集观察结果时并不会改变,因此其可能性也不会变化。每次在获得新的观察结果后,我们都会重复使用这些相同的可能性来评估贝叶斯定理。

F04029

图 4-29:我们五个假设的正面或反面朝上的可能性

我们的目标是反复抛硬币,观察五个先验如何随着时间的推移而变化。为了展示每次抛硬币时发生的情况,我们将五个先验值用红色表示,五个后验值用蓝色表示。在图 4-30 中,我们展示了第一次抛硬币的结果,假设结果是正面朝上。五个红色的条形图代表每个假设的先验值,都是 0.2。由于硬币出现了正面朝上,我们将每个先验值与图 4-29 左侧对应的可能性相乘,得出新的可能性。然后,除以所有五个正面朝上的概率之和,得到后验值,即贝叶斯定理的输出,展示在蓝色条形图中。

F04030

图 4-30:我们正在测试五个假设,假设我们的硬币偏向分别为 0、0.25、0.5、0.75 和 1.0。每个假设的先验值都从 0.2(红色)开始。在抛掷一次硬币后,假设正面朝上,我们计算出后验值(蓝色)。

在图 4-30 中,每对条形图表示一个假设的先验值和后验值。很明显,我们已经排除了假设 0,因为它表示硬币永远不会出现正面,而我们刚才得到的是正面。表示这枚硬币总是正面朝上的假设是迄今为止最强的,因为我们刚刚得到了正面朝上的结果。

现在,我们进行一系列的抛硬币实验。我们将使用包含 30% 正面朝上的 100 次抛掷结果。也就是说,这些结果相当于一枚偏向 0.3 的硬币。虽然我们五个假设都不完全匹配这个偏向,但假设 1 最为接*,表示一枚偏向 0.25 的硬币。让我们看看贝叶斯定理如何表现。图 4-31 展示了前 10 次抛掷的结果,在顶部两行中显示,然后在底部一行中跳跃更大。

F04031

图 4-31:我们的先验(红色)和后验(蓝色)在一系列由偏向 0.3 的硬币生成的抛掷结果中如何变化。每个图形顶部显示的是刚刚评估过的抛掷次数。

如前两行所示,每次抛硬币后的后验分布(蓝色)成为下一次抛硬币的先验分布(红色)。我们还可以看到,在第一次抛硬币(正面朝上)后,最左边的假设 0 的可能性降为 0,因为该假设认为硬币永远不会出现正面。然后在第二次抛硬币时,恰好是反面朝上,假设 4 的可能性降为 0,因为它对应的是一个总是正面朝上的硬币。这样,剩下的只有三个假设。

我们可以看到剩下的三个选项如何随着每次抛硬币的结果调整其概率。随着更多的抛硬币进行,正面朝上的次数越来越接* 30%,假设 1 占据主导地位。当抛硬币次数达到 100 次时,系统基本上决定假设 1 是最优选择,这意味着我们的硬币更可能有 0.25 的偏差,而不是其他的选择。

如果我们能测试 5 个假设,我们就能测试 500 个。图 4-32 展示了 500 个假设,每个假设对应一个从 0 到 1 均匀分布的偏差。我们增加了第四行,显示更多的抛硬币结果。我们在这些图表中去除了竖直的条形,以便更清晰地看到所有 500 个假设的值。

F04032

图 4-32:与图 4-31 相同的情况,但现在我们正在评估 500 个同时进行的假设,每个假设基于一个稍有偏差的硬币。

在这个图(以及接下来的图中),我们重新使用了在图 4-31 中使用的相同的抛硬币结果。正如我们预期的那样,获胜的假设是预测偏差约为 0.3 的那个假设。但这里发生了另一件有趣的事情:后验分布呈现出高斯分布的形态。回忆一下第二章中提到的,高斯曲线就是著名的钟形曲线,除了对称的峰值外几乎是*坦的。这是贝叶斯规则的数学演变中先验分布的典型特征。统计学和概率论中,数据中常常出现高斯曲线,这只是其中许多地方之一。

如我们在章节开始时所说,贝叶斯推理并不是集中于一个正确答案。相反,它逐渐将更多的概率赋予一个较小范围的答案。其思路是,这个范围内的任何值都有可能是我们所寻找的答案。

如果贝叶斯规则似乎演化成先验呈现高斯形状,那如果我们从一开始就用形成高斯的先验,结果会怎样?我们就这样做,但为了增加系统的难度,我们将先验的峰值均值(也就是它的中心)设置在大约 0.8 处。这意味着我们认为我们测试的硬币最有可能偏向 0.8。这个值远离我们序列中设定的 0.3。描述我们硬币的 0.3 的概率一开始仅为 0.004,所以通过我们的先验,我们断定这枚硬币偏向 0.3 的概率只有 0.4%,即每千次中有 4 次。那么,系统如何应对一个如此错误的先验呢?正确答案的概率仅有这么微小的机会?

图 4-33 展示了结果。

F04033

图 4-33:这张图与图 4-32 的设置相同,只是现在我们从一个以 0.8 为中心的高斯峰值形式的先验开始。

很好。即使我们的先验选择得不理想,系统还是锁定了正确的偏差值 0.3。虽然花了点时间,但它最终得到了正确答案。

在图 4-34 中,我们展示了图 4-33 中的先验,在评估前 3,000 次抛掷的过程中,经过 10 步的演化,这些先验被重叠展示,而不是按顺序排列。

请注意,我们一开始用的是均值为 0.8 的广泛先验,但随着更多抛掷的进行和观察的积累,先验的均值逐渐向 0.3 移动。峰值的宽度也变窄了,这告诉我们系统正在判断离均值远的偏差值不太可能。每条曲线所对应的抛掷次数是手动选择的,以确保曲线之间的间距大致相等。请注意,随着系统的信心增长,产生了一个狭窄的先验,曲线变化变得更慢。换句话说,结果越确定,我们就需要更多的观察才能对后验做出重大改变。

F04034

图 4-34:来自图 4-33 的前 3,000 次抛掷的后验快照,重叠在一起。不同的颜色显示了已经经过了多少次抛掷,具体情况可以参考右上角的图例。我们可以看到,系统越来越多地将权重集中在接* 0.3 的先验上,同时减少其他地方的概率。高度变化是为了保持每条曲线下的面积为 1.0。

我们不打算深入细节,但通过一些数学运算,我们可以将可能的偏差数量(因此也包括假设数量)推向逻辑极限,将值的列表替换为连续曲线,就像图 4-34 所示那样。这样做的好处是,我们可以精确到任意程度,找到任何值的偏差,而不仅仅是列表中最接*的那个。

总结

概率领域中有两大派系:频率派和贝叶斯派。频率派认为我们选择测量的任何事物都有一个准确的或真实的值。因此,每次测量只是对该值的*似。贝叶斯派则认为没有单一的真实值,只有一系列可能的值,每个值都有其对应的概率。每次测量都是某种事物的准确度量,但可能并非我们想要测量的内容。

本章的大部分内容都在讨论贝叶斯方法。贝叶斯概率在深度学习中非常流行,因为它非常适合我们面临的各种问题和我们想要回答的各类问题。贝叶斯概率的语言出现在许多深度学习系统的论文、书籍和文档中。其核心思想是为我们提供一套描述测量的工具,而不是通过寻找一个单一的真实数值,而是通过找到该测量的可能值范围,每个值都有其对应的概率。

例如,如果一个深度学习系统帮助某人编写短信并提供下一个单词的快捷方式,它通常会显示几个高概率的猜测,而不是一个单一的最佳下一个单词。

在下一章中,我们将探讨曲线和表面的某些特性,借此我们可以理解我们的学习系统可能犯的错误类型(以及如何纠正这些错误)。

第五章:曲线和表面

在机器学习中,我们经常处理各种类型的曲线和表面。这些对象的两个最重要的性质分别被称为导数梯度。它们描述了曲线或表面的形状,从而决定了我们应该朝哪个方向移动,以便向上爬升或向下滑动。这些思想是深度学习系统如何工作的核心。了解导数和梯度对于理解反向传播(第十四章的主题)至关重要,因此了解如何构建和训练成功的网络。

如常,我们将跳过方程式,转而专注于构建对这两个术语描述的直观理解。关于我们在这里讨论的内容,您可以在大多数现代多变量微积分书籍中找到数学深度和严谨性,并且许多在线网站也有更加易懂的形式(Apostol 1991;Berkey 1992;3Blue 2020)。

函数的性质

如前所述,在机器学习中,我们经常处理各种类型的曲线。最常见的这些是数学函数的图形。我们通常从输入和输出的角度来看待函数。当我们处理二维(2D)曲线时,输入通过选择图表中的一个位置来表示。输出是该点正上方曲线的值。在这种情况下,我们提供一个数字作为输入,并得到一个数字作为输出。

当我们有两个输入时,我们进入了三维世界。在这里,我们的函数是一个表面,像在风中飘动的薄纸。我们的输入是位于薄纸下方地面上的一个点,输出是该点正上方的薄纸的高度。在这种情况下,我们提供两个数字作为输入(用于标识地面上的一个点),并再次得到一个单一的输出。

这些思想可以被推广,使得函数能够接受任意数量的输入值,这些值也叫做参数,并且可以提供多个输出值,有时称为返回值,或者简称返回。我们可以把函数看作一个将输入转换为输出的机器:一个或多个数字进入,且一个或多个数字出来。只要我们没有故意引入随机性,系统就是确定性的:每次我们给一个特定的函数相同的输入时,都会得到相同的输出。

在本书中,我们将通过几种方式使用曲线和表面。其中最重要的一种,也是本章的重点,是确定如何沿着它们移动以得到更大或更小的输出。我们用于这一过程的技术要求我们的函数满足一些条件。我们将通过曲线来说明这些条件,但这些思想同样适用于表面和更复杂的形状。

我们希望我们的曲线是连续的,这意味着我们可以用一笔画出它们,且永远不需要将笔从页面上抬起。我们还希望我们的曲线是光滑的,这样它们就没有尖角(称为尖点)。图 5-1 展示了一条具有这些禁止特征的曲线。

f05001

图 5-1:圆圈围住了一个尖点,虚线箭头表示不连续性,或跳跃。

我们还希望我们的曲线是单值的。在二维中,这意味着对于页面上的每一个水*位置,如果我们在该点画一条垂直线,这条线只与曲线交于一个点,因此该水*位置只对应一个值。换句话说,如果我们从左到右(或从右到左)跟随曲线,它永远不会自我反转。违反这一条件的曲线在图 5-2 中有所展示。

f05002

图 5-2:在紫色区域内,曲线在垂直方向上有多个值。

从现在开始,假设我们的所有曲线都满足这些规则(即它们是*滑的、连续的,并且是单值的)。这是一个安全的假设,因为我们通常会故意选择具有这些特性的曲线。

导数

曲线的一个最重要的方面叫做导数。导数告诉我们很多关于曲线在任何一点形状的信息。在本节中,我们将探讨一些核心思想,它们引导我们到导数的概念。

最大值和最小值

深度学习训练的一个关键部分是最小化系统的误差。我们通常通过将误差想象成一条曲线,然后寻找该曲线的最小值来实现这一目标。

更一般的问题是,在曲线的整个长度上找到曲线的最小值或最大值,如图 5-3 所示。如果这些是整个曲线的最大值和最小值(而不仅仅是我们偶然查看的部分),我们称这些点为全局最小值全局最大值

f05003

图 5-3:曲线的全局最大值(棕色圆圈)和全局最小值(橙色方块)

有时我们只关心这些最大值和最小值,但有时我们也希望知道这些点在曲线上的具体位置。找到这些值有时可能很困难。例如,如果曲线在两个方向上都无限延伸,我们如何确定找到的确实是最小值或最大值?或者如果曲线重复,如在图 5-4 中所示,应该选择哪些高(或低)点作为全局最大值最小值的位置?

f05004

图 5-4:当曲线无限重复时,我们可以有无限多个点可以用作最大值(棕色圆圈)或最小值(橙色方块)的位置。

为了解决这些问题,让我们考虑给定点附*的最大值和最小值。为了描述这一点,我们可以进行以下的小小思维实验。从曲线上的某个点开始,向左移动,直到曲线改变方向。如果值随着我们向左移动而增加,我们就继续移动,直到它们开始减少为止。若值在向左移动时开始减少,我们也继续移动,直到它们开始增加为止。我们再次进行同样的思维实验,这次从同一个点出发,但我们向右移动。这会给我们带来三个有趣的点:我们的起始点,以及向左和向右移动时停下来的两个点。

这三点中的最小值是我们起始点的局部最小值,而这三点中的最大值是我们起始点的局部最大值。图 5-5 展示了这一概念。

f05005

图 5-5:对于黑色的点,棕色圆圈和橙色方框分别表示该点的局部最大值和最小值。

在图 5-5 中,我们向左移动直到得到一个点,并用圆圈标记它,然后向右移动直到到达一个用方框标记的点。局部最大值是这三点中的最大值,在这种情况下是棕色圆圈的中心。局部最小值是这三点中的最小值,在这种情况下是橙色方框的中心。

如果曲线无限逼*正无穷或负无穷,情况就会变得更复杂。在本书中,我们总是假设我们可以为任何曲线上的任意点找到局部最小值和最大值。

请注意,对于任何给定的曲线,只有一个全局最大值和一个全局最小值,但对于任何给定的曲线或表面,可能会有多个局部最大值和最小值(有时称为最大值最小值),因为它们取决于我们所考虑的点。图 5-6 直观地展示了这一概念。

f05006

图 5-6:这些局部最大值和最小值的影响通过其对应的彩色区域来展示。

切线

我们通向导数的下一步涉及一个叫做切线的概念。为了说明这个概念,我们在图 5-7 中标出了一个二维曲线。

f05007

图 5-7:曲线上的一些点被标记为点。每个这些点的切线以黑色画出。

在曲线的每个点上,我们可以画一条线,其斜率由该点的曲线形状决定。这就是切线。我们可以把它想象成一条在该点仅仅与曲线接触的线。如果我们想象自己沿着曲线移动,切线就能告诉我们我们正在朝哪里看(就像如果我们有后脑眼睛一样,它也能告诉我们该朝哪里看)。切线对我们很有用,因为它在每个局部最大值和局部最小值处都是水*的。找到曲线的最大值和最小值的一种方法是找到曲线上切线水*的点(如图 5-7 所示,切线在曲线水**坦处也是水*的,但我们暂时忽略这一点)。

这是找到切线的一种方法。让我们选择一个点,称之为目标点。我们可以沿着曲线向目标点的左侧和右侧各移动相等的距离,在那里画出两个点,并画一条连接这两个点的直线,如图 5-8 所示。

f05008

图 5-8:为了找到给定点的切线,我们可以在曲线上选取距离该点相等的两个点,并画一条连接这两个点的直线。

现在,让我们以相同的速度将这两点拉向目标点,确保它们始终位于曲线上。在它们合并的最后一刻,穿过它们的直线就是切线。我们说这条线是切线,意思是它只是轻轻地接触了曲线。它是描述曲线在该点的最佳直线。古希腊人称切线为吻线

我们可以测量在图 5-8 中构造的切线的斜率。斜率就是一个数字,它告诉我们这条线与水*线之间的夹角。水*线的斜率为 0。若我们逆时针旋转这条线,斜率会变得越来越大。如果我们顺时针旋转这条线,斜率则会变得越来越小。当一条线变得完全垂直时,它的斜率被认为是无穷大。

现在我们来到了导数!它只是斜率的另一种叫法。曲线上的每个点都有自己的导数,因为每个点的切线都有自己的斜率。

图 5-9 显示了为什么我们之前制定了要求曲线连续、光滑和单值的规则。这些规则确保了我们始终能够找到切线,从而找到每个点的导数。

f05009

图 5-9:上排:有问题的曲线。下排:求导时的问题,以蓝色显示。

在图 5-9(a)中,曲线是不连续的,因此两个不同的曲线端点在我们选择的点(用方框标出)上有不同的导数。问题在于我们不知道该选择哪个导数,因此我们通过不允许存在不连续性来避免这个问题。在图 5-9(b)中,曲线不光滑,因此当我们从左侧和右侧到达拐点时,坡度是不同的。同样,我们不知道该选择哪个坡度,所以我们不会处理有拐点的曲线。在图 5-9(c)中,曲线不是单值的。曲线上有多个点可以选择,每个点都有自己的导数,再次,我们不知道该选择哪一个。图 5-9(d)显示了如果曲线变得完全垂直,这也违反了我们的单值规则。更糟糕的是,切线是完全垂直的,这意味着它的坡度是无限的。处理无限值会让简单的算法变得复杂和混乱。因此,我们回避了这个问题,就像回避其他问题一样,我们声明不会使用可能变得垂直的曲线,从而我们永远不需要担心无限导数。通过要求我们的曲线是连续的、光滑的和单值的,我们可以确保它们永远不会产生这种情况。

我们之前说过,曲线是函数的图形表示:我们提供一个输入值,通常沿着水*的 X 轴,然后向上(或向下)查找,以找到该 x 值对应的曲线上的 y 值。这个 y 值就是函数的输出,如图 5-10 所示。

f05010

图 5-10:二维曲线。随着我们向右移动,x 值增大,y 值随着我们向上移动而增大。

当我们从某一点向右移动(也就是 x 增大时),我们可以问曲线是否给出了增大的 y 值、减小的 y 值,还是 y 值没有变化。如果随着 x 增大 y 增大,我们说切线有正坡度。如果随着 x 增大 y 减小,我们说切线有负坡度。坡度越极端(也就是越接*垂直),它就越正或越负。这只是以另一种方式说明坡度角度相对于水*线的关系。图 5-11 展示了这个概念。

f05011

图 5-11:标记图 5-7 中切线的坡度,是否为正坡度(+)、负坡度(–)或*坦(0)

注意到在图 5-11 中有些点并不是山丘或山谷,但它们的坡度仍然是 0。我们只会在山丘的顶部、山谷的底部和像这样的高原上找到坡度为 0 的点。

使用导数寻找最小值和最大值

让我们看看如何使用导数来驱动一个算法,找出某一点的局部最小值或最大值。

给定曲线上的一个点,我们首先找到它的导数。如果我们想沿着曲线移动,以使 y 值增大,我们就沿着导数符号的方向移动。也就是说,如果导数是正的,那么沿 X 轴正方向(即向右移动)会带我们到更大的值。同样,如果导数是负的,那么为了找到更小的 y 值,我们就向左移动。图 5-12 展示了这个概念。

f05012

图 5-12:某一点的导数告诉我们该朝哪个方向移动,以找到曲线的更大或更小值。

我们可以把两种情况归纳起来,说为了找到某一点附*的局部最大值,我们首先找到该点的导数,然后沿 X 轴朝着导数符号的方向迈出小步。接着我们在该点找到导数并再迈出一步。我们重复这一过程,直到达到导数为 0 的点。图 5-13 展示了从最右边的点开始的这一过程。

f05013

图 5-13:使用导数找到某一点的局部最大值

在我们的起始点,即最右边,我们得到一个相对较大的负导数,因此我们向左走大一步。第二次导数稍小(也就是说,斜率依然为负,但小了一些),因此我们向左走较小的一步。第三步,较小的一步,把我们带到了局部最大值处,在这里切线是水*的,因此导数为 0。为了使这个算法更实际,我们需要解决一些细节问题,例如步长的大小和如何避免超过最大值,但现在我们只关注概念上的理解。

要找到局部最小值,我们做相同的操作,但我们沿着 X 轴朝着导数符号的相反方向移动,如图 5-14 所示。在这里,我们从最左边的点开始,发现它的导数为负值,因此我们继续向右移动,直到找到导数为 0 的点。

f05014

图 5-14:使用导数找到某一点的局部最小值

寻找局部最大值和最小值是机器学习中常用的核心数值技术,它依赖于我们能够在我们跟踪的每个曲线点上找到导数。我们选择*滑性、连续性和单值性这三个曲线条件,特别是为了确保我们在曲线上的每个点都能找到一个唯一的有限导数,这意味着我们可以依赖这种跟踪曲线的技术来找到局部最小值和最大值。

在机器学习中,我们的大多数曲线在大多数时候遵循这些规则。如果我们碰巧使用的曲线不遵循这些规则,并且无法计算某一点的切线或导数,通常(虽然不总是)有数学技术可以自动解决这个问题,使我们能够继续进行。

我们之前提到,当曲线*坦时,导数也为 0。这可能会让我们的算法误认为找到了一个极大值或极小值。在第十五章中,我们将看到一种叫做动量的技巧,可以帮助我们避免这种误判,继续寻找真正的极大值或极小值。

梯度

梯度是导数在三维、四维或任意维度空间中的推广。通过梯度,我们可以在这些更高维的空间中找到表面的最小值和最大值。让我们来看一下它是如何工作的。

水、重力与梯度

想象我们身处一个大房间中,房顶上方悬挂着一张波动的织物,它上下起伏,没有任何褶皱或撕裂,如图 5-15 所示。

f05015

图 5-15:没有褶皱或撕裂的光滑织物

这块织物的表面自然满足我们之前对曲线的要求:它是*滑且连续的,因为它是单一的织物片,且它是单值的,因为织物不会像海浪那样翻卷自己。换句话说,从下面的任何一个点来看,织物上方只有一片表面,我们可以测量它相对于地面的高度。

现在假设我们能在某一时刻将织物冻结。如果我们爬到织物上并在上面走动,感觉就像是在一片山脉、*台和山谷的地形中徒步。

假设织物足够密实,水无法通过它。当我们站在某个位置时,往我们的脚下倾倒一些水。水自然会流向低处。事实上,水会沿着最快速的方式向下流动,因为它受到重力的拉力。在每一个点上,它实际上会搜索周围的邻域,并沿着最能让它快速向下流动的方向前进,如图 5-16 所示。

f05016

图 5-16:左图:水滴落在表面。右图:一滴水在它的局部邻域(黄色)中探索多个点,以找到最下坡的那个点。

在所有的移动方式中,水总是沿着最陡峭的路线流向低处。水流所遵循的方向称为最大下降方向。相反的方向,即我们尽可能快地向上爬升的方向,称为最大上升方向

最大上升方向与梯度相同。如果我们想要下降,就沿着梯度的负方向,或称为负梯度。一位试图尽可能快速到达最高山顶的登山者会沿着梯度行进。而一股尽可能快速流下的溪水则沿着负梯度流动。

现在我们知道了最大上升方向,我们也可以找到它的大小,或者说强度、大小。那就是我们上坡的速度。如果我们在一个*缓的坡度上,升高的速度是一个小数字。如果我们在爬陡坡,它就是一个更大的数字。

使用梯度寻找最大值和最小值

我们可以利用梯度在三维空间(3D)中找到局部最大值,就像我们在二维空间(2D)中使用导数一样。换句话说,如果我们处在一个地形上,想要爬到周围的最高峰,我们只需要跟随梯度,始终朝着与我们脚下的点相关的梯度方向前进。

如果我们想要下降到周围的最低点,我们可以沿着负梯度前进,始终朝着与每个脚下点的梯度方向完全相反的方向走。基本上,我们就像一滴水,沿着最快的路径向下流。图 5-17 展示了这个一步步的过程。

f05017

图 5-17:为了下坡,我们可以反复找到负梯度,并朝着那个方向迈出小步。

假设我们站在一座山的顶部,就像图 5-18 中所示。这是一个局部最大值(也许是全局最大值)。在这里,没有上坡的方向。如果我们放大看山顶,发现附*的表面是*的。因为没有上升的空间,我们的最大上升速度是 0,梯度的大小也是 0。根本没有梯度!我们有时说梯度已经消失,或者我们有一个零梯度

f05018

图 5-18:在山顶的最顶部,没有上坡。左:山丘。右:我们位于山顶的地方。

当梯度消失时,就像在山顶一样,负梯度也会消失。

如果我们处于一个碗形的山谷底部,就像图 5-19 所示,这就是局部最小值(也许是全局最小值)。

f05019

图 5-19:在碗的最底部,我们的每一步都是上坡。左:一个碗。右:碗底的一个点。

在碗的最底部,每个方向似乎都是上坡。但如果我们放大看,会发现碗底是*的。再次,梯度已经消失。

如果我们不在山顶或山谷里,或者不在坡面上,而是处于一个*坦的*原或高原上,就像图 5-20 所示,情况会怎样呢?

f05020

图 5-20:一个*坦的表面、*原或高原。左:高原。右:*原上的一个点,基本上位于*面上。这个点没有梯度。

就像在山顶一样,没有地方可以上坡或下坡。当我们在高原上时,我们再次没有任何梯度。

马鞍点

到目前为止,我们看到了局部的最小值、最大值和*台区域,就像我们在二维中看到的那样。但在三维空间中,出现了一种全新的特征。在一个方向上,我们处于山谷的底部,而在另一个方向上,我们则处于山顶。在这个点的局部邻域中,表面看起来像是马术骑手使用的鞍形。显然,这种形状被称为鞍形。一个示例鞍形如图 5-21 所示。

f05021

图 5-21:鞍形在一个方向上向上,另一个方向上向下。左:一个鞍形。右:鞍形上的一个点。

如果我们处在鞍形的中间,如图 5-21 所示,那就像是同时处于山顶和山谷。就像那些地方一样,当地的邻域看起来像一个*台,因此没有梯度。但如果我们在某个方向上稍微移动一下,就会发现有一点曲率,然后梯度重新出现,告诉我们从这个点开始的最大上升方向。

当我们训练一个深度学习算法时,我们通常希望找到最小的误差。把误差看作一个表面时,最理想的情况是我们能够找到一个碗底。但如果我们发现自己处在山顶、鞍形点或者*台上时,我们就说我们被卡住了。我们知道自己没有在最小值处,但梯度消失了,所以我们不知道该往哪个方向走才能下坡。

幸运的是,现代算法提供了多种自动化技术来帮助我们脱困。但有时它们会失败,除非我们能做出重大改变,比如提供额外的训练数据,否则我们的算法会停滞不前,无法移动到表面更低的值。从实际角度来看,这意味着算法会停止学习,输出不再改进。

我们稍后会看到,我们可以通过测量误差来观察学习进度。如果误差在我们的结果尚不可接受之前停止改善,我们可以稍微改变算法,使它在学习时走一条不同的路径,避开那个零梯度的特定位置。

总结

在本章中,我们探讨了一些寻找曲线最小值和最大值的方法。当我们训练一个深度学习系统时,我们会调整它以最小化系统的整体误差。如果我们把误差看作是一个多维空间中的表面,那么我们就是在寻找这个表面上的最小值。为了找到这个最小误差,我们找到最陡峭的下坡方向,这个方向由负梯度给出。然后我们会改变网络,使得误差朝着这个方向移动。从本质上讲,梯度告诉我们如何改变网络,以便减少系统的整体误差。

在后续的章节中,我们将看到如何在实际中运用这个想法,来教我们的深度学习系统在工作中变得越来越优秀。

现在,让我们转向一些信息论的内容,这将帮助我们更好地理解错误的本质以及如何解读它们。

第六章:信息理论

在本章中,我们将探讨信息理论的基础知识。这是一个相对较新的研究领域,于 1948 年通过一篇开创性的论文向世界介绍,为现代计算机、卫星、手机和互联网等技术奠定了基础(Shannon 1948)。原始理论的目标是找到最有效的方式来电子化传递信息。但该理论的思想深刻、广泛且深远。它为我们提供了测量我们了解某事物的程度的工具,通过将信息转换为我们可以研究和操作的数字形式。

信息理论中的术语和概念构成了深度学习的基石。例如,信息理论提供的度量方法在评估深度网络的性能时非常有用。在本章中,我们将快速浏览信息理论的一些基本概念,同时避免使用抽象的数学符号。

我们从单词information(信息)开始,这是一个既有日常意义,又有专业科学意义的词。在这种情况下,这两种意义有很多概念上的重叠,但日常意义广泛且开放,容易受到个人理解的影响,而科学意义则是精确的,并且通过数学定义的。我们从逐步构建信息的科学定义开始,最终我们将上升到一个重要的度量标准,允许我们比较两个概率分布。

惊讶与语境

当我们收到任何形式的通信时,某些东西从一个地方移动到另一个地方,无论是电脉冲、一些光子,还是某人的声音。广义地说,我们可以说一个发送者通过某种方式将某种信息传递给接收者。接下来,我们将介绍一些更专业的词汇。

理解惊讶

在本章中,我们有时使用惊讶一词来表示发送者的通信对接收者来说有多么出乎意料。惊讶并不是一个正式术语。事实上,本章的目标之一是为惊讶找到更正式的名称,并附上具体的含义和度量标准。

假设我们是接收者,收到一条消息。我们想描述收到这条信息后我们有多惊讶。能够这样描述很有用,因为正如我们所看到的,惊讶越大,传递的信息量就越大。

假设我们收到了一条来自未知号码的意外短信。我们打开短信,第一句话是“Thanks”(谢谢)。我们有多惊讶呢?肯定至少有些惊讶,因为到目前为止,我们不知道这条短信是来自谁,也不知道它的内容是什么。不过,收到感谢我们的短信确实是有可能的,所以这并不是什么闻所未闻的事。

我们来设定一个假想的、完全主观的惊讶量表,其中 0 表示完全预期的事件,100 表示完全出乎意料的事件,就像在图 6-1 中所示。

f06001

图 6-1:惊讶度量表,表示从 0 到 100 的值

在这个量表上,"Thanks"(谢谢)出现在一条意外的文本消息开头时,可能排在 20 的位置。现在假设我们消息的第一个词不是"Thanks",而是"Hippopotamus"(河马)。除非我们在处理这些动物或与它们有其他关联,否则这可能是消息中的一个相当令人惊讶的词。我们将这个词的惊讶度定为 80,正如图 6-2 所示。

f06002

图 6-2:将消息放置在我们的惊讶度量表上

尽管"hippopotamus"(河马)可能在消息开头给人一种很大的惊讶感,但在后续的语境中它可能就不那么令人惊讶了。区别就在于语境。

拆解语境

对于我们的目的来说,我们可以将语境视为信息的环境。由于我们关注的是每条消息的意义,而不是它的传递方式,语境代表了发送者和接收者之间的共享知识,正是这些知识赋予了消息意义。

当信息是一段语言时,这种共享知识必须包括所使用的词汇,因为像 Kxnfq rnggw 这样的消息是没有意义的。我们可以将这种共享知识扩展到包括语法、表情符号和缩写的当前解释、共享的文化影响等等。这一切都被称为全球语境。它是我们在接收任何信息之前就已经带入的普遍知识。就像在第四章讨论的贝叶斯定理一样,部分全球语境被捕捉到我们的先验中,因为这就是我们表示对环境的理解以及我们期望从中学到什么的方式。

与全球语境相对的是局部语境。那是由消息本身的元素构成的环境。在一条文本消息中,任何给定单词的局部语境就是该消息中的其他单词。

假设我们第一次阅读这条消息,因此每个单词的局部语境仅由它前面的单词构成。我们可以利用语境来判断惊讶感。如果"Hippopotamus"是我们消息中的第一个词,那么此时没有局部语境,只有全球语境。如果我们不经常接触河马,这个词可能非常令人惊讶。但是,如果消息的开头是"Let's go down to the river area at the zoo and maybe see a big gray"(让我们去动物园的河边,或许能看到一只大灰色的动物),那么在这个语境下,"hippopotamus"(河马)就不再令人惊讶了。

我们可以通过为特定单词分配一个惊讶值,来描述在全球语境中该单词所携带的惊讶感,正如我们在图 6-1 中所做的那样。假设我们为字典中的每个单词分配一个惊讶值(这是一项繁琐的工作,但肯定是可行的)。如果我们将这些数字缩放,使它们的总和为 1,就创建了一个概率质量函数(或 pmf),正如我们在第二章中讨论的那样。这意味着我们可以从这个 pmf 中抽取一个随机变量来得到一个单词,其中最令人惊讶的单词比那些不太令人惊讶的单词更常出现。一种更常见的方法是建立一个 pmf 来表示单词的常见程度,这大致是惊讶感的对立面。在这种设置下,我们预计会更频繁地抽到那些不太令人惊讶或更常见的单词,而不是那些不常见的单词。

我们将在本章后面使用这个思想,设计一种高效的方案来传输信息的内容。

测量信息

在本章中,我们将谈论很多关于比特的内容。在日常语言中,比特通常被认为是一个小的数据包,通常被标记为 0 或 1。例如,当我们谈论“每秒比特数”的网络速度时,我们可能会把比特想象成河流中漂流的叶子,并在它们经过时进行计数。

这是一个方便的概念,但从技术角度来说,比特并不是像树叶一样的物体,而是像加仑或克那样的单位。也就是说,它不是一种物质,而是一种衡量我们所拥有的物质多少的方式。比特是一个容器,刚好能够存储我们当前认为的基本、不可分割的最小信息单元。

从这种方式谈论比特作为单位在技术上是正确的,但这并不方便。大多数时候,我们可以不加澄清地使用通俗的说法,就像我们说“我的网速是每秒 8,000 比特”而不是“我的网连接每秒能够传输 8,000 比特的信息”。我们在本书的大部分内容中将使用更通俗的语言,但了解技术定义是值得的,因为它确实会出现在一些论文和文档中,而在那些地方区分开来是很重要的。

我们可以通过一个公式来衡量文本信息的量,这个公式告诉我们需要多少比特来表示该信息。我们不会深入探讨数学内容,但会描述其中的原理。这个公式有两个输入。第一个是信息的文本内容。第二个是描述信息中每个单词所包含的惊讶感的概率质量函数(在本章中,我们将其称为概率分布)。当我们将信息的文本内容和概率分布结合起来时,就可以得出一个数字,告诉我们这条信息所携带的比特数。

该公式的设计是为了使它为每个单词(或者更一般地,每个事件)所生成的值具备四个关键属性。我们将在一个我们在办公室工作而不是在河流上的情境中说明每一个属性。

  1. 可能发生的事件信息量较少。订书机的信息量较少。

  2. 不太可能发生的事件包含更多信息。鳄鱼包含更多信息。

  3. 可能发生的事件比不太可能发生的事件包含的信息更少。订书机传递的信息比鳄鱼少。

  4. 最后,两个不相关事件的总信息量是它们各自信息值的和。

前三个属性将单个对象与其信息相关联。这个组中的特例是属性 4,所以我们需要更仔细地看一下它。

在正常的对话中,两个连续的词完全不相关是很少见的。但假设有人要求我们给出一个“金桔水仙花”。这些词几乎完全不相关,因此属性 4 说明我们可以通过将每个词独立传递的信息相加来找到该短语的信息。

在正常的对话中,引导到某个给定单词的词汇通常会缩小它可能是什么的范围。如果有人说:“今天我吃了一个大...”,那么接下来的“三明治”和“比萨”这类词比“浴缸”或“帆船”产生的惊讶感要少。当某些词被预期时,它们比没有预期时产生的惊讶感要少。相比之下,假设我们正在传送一个设备的序列号,它本质上是一个任意的字母序列,可能还包含数字,比如“C02NV91EFY14”。如果这些字符之间真的没有关联,那么通过加上每个字符所带来的惊讶感,我们就能得到整个序列号所代表的信息。

通过将两个不相关词汇的惊讶感合并为它们各自惊讶值的和,我们从衡量每个词汇中的惊讶感或信息,转向衡量它们组合中的惊讶感。我们可以这样不断将词汇组合成越来越大的组,直到我们考虑完整个信息。虽然我们没有涉及数学,但我们已经得出了信息的正式定义:它是一个通过公式生成的数字,该公式使用一个或多个事件(如单词)和一个概率分布,来描述每个事件对我们来说有多么令人惊讶。通过这两个输入,算法为每个事件提供一个数字,并确保这些数字满足我们刚才列出的四个属性。我们称每个单词的数字为其,它告诉我们传递该单词需要多少比特。

自适应编码

每个事件所携带的信息量受我们公式中所使用的概率函数大小的影响。换句话说,我们可能传递的词汇数量会影响每个词所携带的信息量。

假设我们要将一本书的内容从一个地方传送到另一个地方。我们可以列出书中所有独特的单词,然后为每个单词分配一个编号,可能从“the”对应 0,接着是“and”对应 1,依此类推。然后,如果接收方也有这份单词列表,我们可以仅通过发送每个单词的编号来传送这本书,从书中的第一个单词开始。Dr. Seuss 的《Green Eggs and Ham》这本书只有 50 个不同的单词(Seuss 1960)。为了表示 0 到 49 之间的一个数字,每个单词我们需要 6 位信息。相比之下,Robert Louis Stevenson 的《Treasure Island》这本书包含约 10,700 个独特的单词(Stevenson 1883)。我们必须为每个单词使用 14 位来唯一标识书中的每个单词。

尽管我们可以使用一个包含所有英语单词的大型单词列表来发送这些书籍,但更高效的做法是根据每本书的独特词汇量来定制我们的列表,仅包括我们实际需要的单词。换句话说,我们可以通过调整信息传输的方式,使其更符合传递的内容,从而提高效率。

让我们基于这个想法进一步探讨。

说摩尔斯电码

一个很好的适应性例子是摩尔斯电码。在摩尔斯电码中,每个字母字符都有一个由点和划线组成的模式,点和划线之间由空格分隔,如图 6-3 所示。

f06003

图 6-3:摩尔斯电码中的每个字符都有一个与之相关的点、划线和空格的模式。

摩尔斯电码通常通过使用电报键来发送,电报键用于启用或禁用清晰音调的传输。一个点是一个短促的音符。我们按下电报键发送一个点的时间长度由一个单位来表示,这个单位称为dit。一个划线持续三个 dit 的时间。符号之间留一个 dit 的静默,字母之间留三个 dit 的静默,单词之间留七个 dit 的静默。当然,这些是理想的标准。实际上,许多人可以辨认出每个朋友和同事个人的节奏,这种节奏叫做fist(Longden 1987)。

摩尔斯电码包含三种符号:点、划线和点大小的空格。假设我们要用摩尔斯电码发送“nice dog”这条信息。图 6-4 显示了短音(点)、长音(划线)和点大小空格的序列。

f06004

图 6-4:摩尔斯电码的三种符号:点(实心圆)、划线(实心方块)和静默空格(空心圆)。

我们通常严格地用点和划来讨论摩尔斯电码,这些被称为符号。分配给任何字母的符号集就是该字母的模式。发送信息所需的时间取决于组成信息内容的字母所分配的具体模式。例如,尽管字母 Q 和 H 都有四个符号,但发送 Q 需要 13 个点(每个 3 个划的 3 个点,1 个点,和每个 3 个空格的 1 个点),而发送字母 H 只需要 7 个点(4 个点,和每个 3 个空格的 1 个点)。

让我们比较不同字符的模式。当我们查看图 6-3 时,我们可能不清楚这些不同模式分配背后是否有任何原则。但一个美丽的思想正在等待被揭示。图 6-5 显示了 26 个罗马字母的列表,按它们在英语中的典型使用频率排序(Wikipedia 2020)。最常用的字母 E 排在列表的最前面。

f06005

图 6-5:按在英语中使用频率排序的罗马字母

现在回头看看图 6-3 中的模式。最常见的字母 E 只是一个点。下一个最常见的字母 T 只是一个划。这是唯一两个只有一个符号的模式,因此我们继续往下看两个符号。字母 A 是一个点后跟一个划。接下来是 O,它打破了这个模式,因为它太长:三个划。稍后再回来讨论。返回我们的列表,字母 I 是两个点,字母 N 是一个划和一个点。最后的两个字母模式是 M,由两个划组成,但它在我们到达的位置上已经很远了。为什么 O 太长而 M 太短?摩尔斯电码几乎遵循我们的字母频率表,但还不完全是。

解释从塞缪尔·摩尔斯开始,他在原始代码中仅定义了数字 0 到 9 的模式。字母和标点符号是由阿尔弗雷德·维尔添加到代码中的,他大约在 1844 年设计了这些模式(Bellizzi 11)。维尔没有一个简单的方法来查找字母的出现频率,但根据维尔的助手威廉·巴克斯特的说法,他知道自己应该遵循这些频率。巴克斯特说,

他的总体计划是采用最简单和最短的组合来表示最常出现的英语字母,将其余的字母分配给较不常见的字母。例如,他在调查后发现字母 e 的出现频率远高于其他字母,因此他为其分配了最短的符号,一个点(•)。另一方面,j 的出现频率较低,用划-点-划-点(– • – •)表示(Pope 1887)^(1)

Vail 认为,他可以通过访问新泽西州莫里斯敦的本地报社来估算英文文本中的字母频率表,当时他们仍在手动排版。那时,排版工人是逐个字母地构建页面。对于每个字母,他们会选择一个合适的铅字,即一根金属条的一端上压印有字母,然后将其放入一个大托盘中。Vail 推测,最常用的字符会有最多的铅字,因此他数了每个字母托盘中的铅字数量。这些流行度计数成为了他估算英语字母频率的替代指标(McEwen 1997)。尽管这个样本很小,他还是做得相当好,尽管存在一些不准确的地方,比如他显然认为 M 比 O 更常见。

为了查看我们的频率图(和摩尔斯电码)与一些实际文本的对比,图 6-6 展示了《宝岛》中各个字母的频率(史蒂文森 1883)。在这张图中,我们只统计了字母,并在计数前将它们转换为小写字母。我们还排除了数字、空格和标点符号。

f06006

图 6-6:每个字母在罗伯特·路易斯·史蒂文森的《宝岛》中出现的次数。大写字母被当作小写字母计算。

图 6-6 中的字符顺序与图 6-5 中的字母频率图不完全匹配,但差距不大。图 6-6 看起来像是从 A 到 Z 字母的概率分布。为了使其成为一个实际的概率分布,我们必须对其进行缩放,使得所有条目的总和为 1。结果显示在图 6-7 中。

f06007

图 6-7:《宝岛》中字符的概率分布函数(pdf)

现在让我们利用字母的概率分布,来提高通过摩尔斯电码发送《宝岛》的效率。

自定义摩尔斯电码

为了激励我们改进通过摩尔斯电码发送《宝岛》的方式,首先让我们退后一步,开始思考一个假设的摩尔斯电码版本,在这个版本中,Vail 先生并没有下到报社去。而是,假设他想为每个字符分配相同数量的点和划符号。使用四个符号,他只能标记 16 个字符,但使用五个符号,他可以标记 32 个字符。

图 6-8 展示了我们如何任意地为每个字符分配这样的五符号模式。为了简化起见,我们通过使用不同的音调来表示两个符号,使得每个点和划的时间都相同。所以每个点(这里显示为黑色圆点)是一个持续一拍的高音,而每个划(显示为红色方块)是一个持续一拍的低音。结果是,每个字符需要发送九拍的时间(五拍用于点和划,现在分别是高音和低音,四拍是它们之间的间隔)。这是一个定长编码的例子,也叫做固定长度编码

在图 6-8 中,我们没有为空格创建字符,这与原始的莫尔斯电码保持一致,因为它假定我们可以通过查看信息来判断空格的位置。遵循这一精神,我们将在接下来的讨论中忽略空格字符。

f06008

图 6-8:为每个字符分配五个符号得到固定长度编码。黑色圆圈表示高音调;红色方块表示低音调。它们的持续时间都是一个点。

《金银岛》文本中的前两个单词是名称“斯奎尔·特雷劳尼”。由于我们这种双音调版本的莫尔斯电码中的每个字符都需要 9 个点,因此这个包含 15 个字母的短语(记住我们忽略了空格)需要 9 × 15 = 135 个点的时间来发送。再加上字母之间的 14 个停顿,这些停顿需要 3 × 14 = 42 个比特,我们发现固定长度的信息需要 135 + 42 = 177 个点的时间,如图 6-9 所示。

f06009

图 6-9:使用我们固定长度编码的《金银岛》前两个单词

现在将此与实际的莫尔斯电码进行比较,后者大多数情况下,常见字母所需的符号比不常见字母少。图 6-10 展示了这一点。我们将继续使用不同音调的点和划线发送信息,每个音调的持续时间为一个点。

f06010

图 6-10:使用莫尔斯电码的《金银岛》前两个单词

如果我们计算出这些元素(记住现在点和划每个只需一个点),我们会发现图 6-10 版本只需要 101 个点,大约是固定长度编码时间的一半(101 / 177 ≈ 0.57)。这种节省来自于我们将编码调整为适应所发送的内容。我们将任何通过将短模式与高概率事件匹配来提高效率的编码称为可变比特率编码,或更简单地说,自适应编码。即便在这个简单的例子中,我们的自适应编码几乎是固定长度编码的两倍高效,几乎将通信时间减半。

让我们来看一下整个《金银岛》文本,它包含约 338,000 个字符(不包括空格、标点符号等)。自适应编码只需约固定长度编码所需时间的 42%。我们可以在不到非自适应编码时间的一半内发送这本书。

如果我们不使用标准的莫尔斯电码(莫尔斯电码通常适用于英语书写),而是将符号的分布调整为更接*我们发送的特定书籍文本中符号的实际百分比,我们的效率会更高。当然,我们必须与接收方共享我们的聪明编码,但如果我们正在发送一条长信息,那额外的沟通成本相比信息本身几乎可以忽略不计。让我们走出这一步,想象一个专门为《金银岛》定制的莫尔斯电码,它完全适应《金银岛》的内容。我们应该预期能够节省更多时间。

让我们用概率的语言重新表述这个问题。自适应编码为概率分布中的每个值创建一个模式。具有最高概率的值将获得尽可能最短的编码。然后,我们按从最高概率到最低概率的顺序逐个处理这些值,为每个值分配始终尽可能短的模式,并且不重复。这意味着每个新模式的长度至少和分配给前一个值的模式相同,或者更长。这正是费尔先生在 1844 年所做的,他根据当地报纸排字工的字母数量为指导。

现在,我们可以查看任何想要传达的信息,识别每个字符,并将其与告诉我们该字符最初出现概率的概率分布进行比较。这告诉我们每个字符承载的信息量,以比特为单位。得益于我们描述计算信息的公式中的第四个属性,表示消息所需的总比特数(暂时忽略上下文),正好是每个字符所需的比特数之和。

我们也可以在发送消息之前进行这个过程。这告诉我们我们即将传达给接收者的具体信息量。

我们已经讨论过惊讶,它指的是那些我们没有预料到的事情。一个相关的概念是不确定性,它指的是那些我们知道所有可能发生的事情,但不确定哪一个会实际发生的时刻。例如,当我们掷一个公正的六面骰子时,我们知道六个面出现的概率是相等的,但直到我们掷出并查看时,我们才不确定哪个面会朝上。这种不确定性的一个更正式的术语是

我们可以为结果的不确定性或熵赋予一个数值。这个数值通常取决于可能的结果数量。例如,抛硬币只有 2 种结果,而掷一个六面骰子有 6 种结果,从字母表中挑选一个字母则有 26 种结果。这三种结果的不确定性,或它们的熵,是一个数值,它的大小依次增加,从硬币到骰子,再到字母表,因为每种情况中的结果数量在增加。这使得每个具体结果的不可预测性增大。

在这三个例子中,每个结果的概率是相同的(硬币每一面是 1/2,骰子的每一面是 1/6,字母表中的每个字母是 1/26)。但如果结果的概率不同呢?计算熵的公式明确考虑了这些不同的概率。本质上,它考虑了分布中的所有可能结果,并为描述在我们从分布中抽样时实际产生哪个结果的不确定性赋予了一个数值。

事实证明,特定事件发生的不确定性与用一个完全适应的编码发送消息所需的比特数相同。从概念上讲,一条文本消息是从词汇表中抽取的一组单词,这与多次掷骰子的值没有什么不同。我们使用术语来表示这两种情况:事件的不确定性,或者传递该事件所需的比特数(从而消除不确定性)。

熵在机器学习中非常有用,因为它允许我们比较两个概率分布。这是学习中的关键步骤。例如,考虑一个分类器。我们可能有一张图片,我们手动判定它有 80%的可能性是狗,但有 10%的可能性是狼,3%的可能性是狐狸,还有其他一些动物的较小概率。我们希望系统的预测与这些标签相匹配。换句话说,我们希望比较我们的手动分布与系统的预测分布,并利用任何差异来改进我们的系统。我们可以发明许多比较分布的方法,但在理论和实践中最有效的方法是基于熵的。让我们从求一个单一分布的熵开始,逐步建立起这种比较方法。

考虑由单词组成的分布。如果我们的分布中只有一个单词,那么当我们从分布中抽取单词时就没有不确定性,因此熵为 0。如果有很多单词,但它们的概率都是 0,除了一个单词的概率是 1,那么仍然没有不确定性,所以熵再次为 0。当所有单词的概率相同,我们就有了最大的 uncertainty,因为没有任何选择比其他选择更可能。在这种情况下,我们的不确定性或熵达到了最大值。虽然可以方便地说最大熵应该是 1 或 100,但实际值是通过公式计算的。我们所知道的是,其他任何概率分布都不会给我们更大的熵。

在接下来的部分,我们将看到如何将熵应用于分布对的比较。

交叉熵

在训练深度学习系统时,我们通常希望有一个度量标准,告诉我们两个概率分布在多大程度上相同或不同。我们通常使用的值是一种叫做交叉熵的量,它也只是一个数字。回想一下,熵告诉我们,使用一个完美匹配消息的编码需要多少位。交叉熵则告诉我们,如果我们使用某种其他的、不太完美的编码,我们需要多少位。通常情况下,交叉熵比完美编码所需的位数要大(如果替代编码恰好与理想编码一样高效,则交叉熵的最小值为 0)。交叉熵是一个让我们数值化比较两个概率分布的测量工具。相同的分布具有 0 的交叉熵,而越来越不同的分布对的交叉熵值则会越来越大。

为了理解这个概念,让我们看两本小说,并为每本小说建立一个基于单词的自适应编码。虽然我们的目标是比较概率分布,而且我们现在讨论的是编码,但在概念上很容易来回切换。回想一下,通过构造,较小的编码对应于概率较高的单词,而较大的编码对应于概率较低的单词。

两种自适应编码

马克·吐温的小说《金银岛》和《哈克贝里·费恩历险记》几乎在同一时期用英语写成(斯蒂文森 1883 年;吐温 1885 年)。《金银岛》的词汇量更大,使用了大约 10,700 个独特的单词,而《哈克贝里·费恩历险记》则用了大约 7,400 个独特的单词。当然,这两本书使用的单词集完全不同,但也有很多重叠。让我们来看一下《金银岛》中最流行的 25 个单词,如图 6-11 所示。在计算单词时,我们首先将所有的大写字母转换为小写字母。因此,单字母代词“I”在图表中显示为小写字母“i”。

f06011

图 6-11:按出现频率排序的《金银岛》中最流行的 25 个单词

让我们将这些与《哈克贝里·费恩历险记》中最流行的 25 个单词进行比较,如图 6-12 所示。

也许并不令人惊讶,两本书中最流行的十二个单词几乎完全相同(尽管顺序不同),但随后就开始有所不同。

f06012

图 6-12:按出现频率排序的《哈克贝里·费恩历险记》中最流行的 25 个单词

假设我们想要逐字逐句地传输这两本书的文本。我们可以查阅英语词典,为每个单词分配一个编号,顺序为 1、2、3,依此类推。但我们从之前的摩尔斯电码示例中知道,通过使用一个适应传输内容的代码,我们可以更高效地传递信息。我们来创建这种类型的代码,单词出现得越频繁,其代码编号就越小。因此,像“the”和“and”这样的高频词可以用短代码传输,而稀有词则有更长的代码,需要传送更多的比特(在金银岛中大约有 2,780 个单词只出现了一次;在哈克贝利·费恩中大约有 2,280 个单词只出现了一次)。

两本书的词汇大部分是重合的,但每本书也有一些在另一册中没有出现的单词。例如,“yonder”这个词在哈克贝利·费恩中出现了 20 次,但在金银岛中一次也没有出现。而“schooner”这个词在金银岛中出现了 28 次,但在哈克贝利·费恩中完全找不到。

因为我们希望能够使用任意一种代码传输任何一本书,所以我们需要统一它们的词汇表。对于哈克贝利·费恩中在金银岛中没有出现的每个单词,当我们制作金银岛的代码时,我们会加入该单词的一次实例。我们对哈克贝利·费恩也做同样的事情。例如,在制作金银岛的代码时,我们会将“yonder”这一单词加到书的末尾,这样如果我们想要使用这个代码传输哈克贝利·费恩,就可以使用。

让我们从金银岛中的单词开始。我们将为这本书制作一个自适应代码,从为“and”分配一个小代码开始,逐渐增加代码长度,直到为像“wretchedness”这样仅出现一次的词分配较大的代码。现在,我们可以使用该代码传输整本书,相较于其他任何代码,这样可以节省时间。

现在我们将对哈克贝利·费恩进行相同的操作,并为这段文本创建一个专门的代码,给常见的词分配最短的代码,将像“危险”(令人震惊,但是真的:“危险”这个词在哈克贝利·费恩中只出现过一次!)这样的生僻词留给较大的代码。现在,哈克贝利·费恩的代码让我们比任何其他代码更快速地传输这本书的内容。

请注意,这两种代码是不同的。我们可以预期会有这种差异,因为两本书的词汇量不同,且涉及的主题也有显著差异。

使用这些代码

现在我们有了两种代码,每种代码都可以传输任意一本书。金银岛的代码针对每个单词在金银岛中的出现次数进行了调整,而哈克贝利·费恩的代码则针对哈克贝利·费恩进行了调整。

压缩比告诉我们使用自适应编码与固定长度编码相比,节省了多少数据。如果压缩比正好为 1,那么我们的自适应编码使用的位数与非自适应编码完全相同。如果压缩比为 0.75,那么自适应编码仅使用非自适应编码所需位数的 3/4。压缩比越小,节省的位数就越多(一些作者定义这个比率时,数字的顺序正好相反,所以压缩比越大,压缩效果越好)。

让我们尝试逐词发送这两本书。图 6-13 的顶部条形图显示了我们使用为《哈克贝里·芬》构建的代码发送这本书时得到的压缩比。我们使用了一种叫做霍夫曼编码的自适应编码,但对于大多数自适应编码,结果都会类似(霍夫曼 1952;费里尔 2020)。

f06013

图 6-13:上图:使用为《哈克贝里·芬》构建的代码发送《哈克贝里·芬》的压缩比。下图:使用为《宝岛》构建的代码发送《哈克贝里·芬》的压缩比。

这非常棒。自适应编码的压缩比略低于 0.5,这意味着使用这种编码发送《哈克贝里·芬》所需的位数将不到固定长度编码的一半。如果我们使用为《宝岛》构建的代码来发送《哈克贝里·芬》,我们可以预期压缩效果不会那么好,因为该代码中的数字与我们正在编码的词频不匹配。图 6-13 的下方显示了这一结果,压缩比大约为 0.54。这仍然非常不错,但不如前者高效。

让我们换个角度,看看使用为《宝岛》构建的代码和为《哈克贝里·芬》构建的代码分别有什么效果。结果见图 6-14。

f06014

图 6-14:上图:使用为《哈克贝里·芬》构建的代码发送《宝岛》的压缩比。下图:使用为《宝岛》构建的代码发送《宝岛》的压缩比。

这一次我们发现,《宝岛》的压缩效果比《哈克贝里·芬》更好,这很有道理,因为我们使用了与其词汇使用相匹配的代码。一般来说,发送任何信息的最快方法是使用专门为该信息内容构建的编码。没有其他代码能做得更好,大多数代码会做得更差。

我们已经看到,使用《宝岛》代码来发送《哈克贝里·芬》的压缩效果较差。换句话说,使用不适合该信息的编码来发送这本书需要更多的位数。这是因为每个代码都是基于其对应的概率分布,而这些分布是不同的。

我们用来衡量两个概率分布之间差异的量是交叉熵

注意,情况并非对称的。如果我们想用《金银岛》中的文字通过《哈克贝里·费恩》代码发送,交叉熵将与用《金银岛》代码发送《哈克贝里·费恩》时不同。我们有时会说交叉熵函数在其参数上是不对称的,意味着它们的顺序很重要。

一种概念化这种情况的方式是将我们的概率分布空间想象成海洋,海流在不同地方朝不同方向流动。从某个 A 点游到 B 点所需的努力,时而需要与海流抗争,时而被海流带着走,通常与从 B 点游到 A 点的努力不同。在这个比喻中,交叉熵是衡量工作的量,而不是两点之间的实际距离。但随着 A 和 B 之间的距离变得更*,游泳所需的工作量在任一方向上都会减少。

交叉熵在实践中

让我们来看一下交叉熵的实际应用。我们将像训练一个照片分类器时一样使用它,比较两个概率分布。第一个是我们手动创建的标签,用来描述照片中的内容。第二个是系统在我们展示给它照片时计算出的概率集。我们的目标是训练系统,使其输出与我们的标签匹配。为了做到这一点,我们需要知道系统出错的地方,并给出一个衡量错误大小的数字。这就是通过比较标签和预测得到的交叉熵。交叉熵越大,误差就越大。

在图 6-15 中,我们展示了一个虚拟分类器的输出,预测了一张狗的照片的概率。在大多数实际情况下,所有标签值都会是 0,只有狗的条目是 1。在这里,我们为六个标签分配了任意的概率,以更好地展示系统如何尝试匹配标签分布(我们可以想象这张照片模糊不清,因此我们自己也不确定它显示的是哪种动物)。

f06015

图 6-15:分类一张狗的照片。左:训练开始时。右:经过大量训练后。当匹配更好时,交叉熵更低。

左侧的图像来自训练开始时。系统的预测与我们的手动标签匹配得相当差。如果我们将这些数字带入交叉熵公式,我们得到大约 1.9 的交叉熵。在右侧,我们看到经过一些训练后的结果。现在这两个分布更为接*,交叉熵降到了大约 1.6。

大多数深度学习库提供了内置的例程,可以在一步中为我们计算交叉熵。在图 6-15 中,我们有六个类别。当只有两个类别时,我们可以使用专门为这种情况设计的例程。它通常被称为二元交叉熵函数。

库尔巴克-莱布勒散度

交叉熵是比较两个分布的一个很好的度量。通过最小化交叉熵,我们可以减少分类器输出与我们的标签之间的误差,从而提高系统性能。

我们可以通过再增加一步,概念上让事情变得稍微简单一点。让我们再次把词汇分布看作是编码。回想一下,熵告诉我们,在使用完美、调优的编码时,发送一条消息需要多少比特。而交叉熵则告诉我们,使用不完美的编码时,发送该消息需要多少比特。如果我们从交叉熵中减去熵,我们就得到了不完美编码所需的额外比特数。我们能够让这个数字尽可能小,就意味着我们需要的额外比特数越少,相应的概率分布就越相同。

不完美编码所需的额外比特数(即熵的增加)有许多令人畏惧的名称。最常见的名称是 Kullback–Leibler 散度,简称 KL 散度,这个名字来源于提出计算该值公式的科学家。较少见的名称还包括 判别信息信息散度有向散度相对熵KLIC(即 Kullback–Leibler 信息准则)。

像交叉熵一样,KL 散度是非对称的:参数的顺序很重要。用 Huckleberry Finn 编码发送 Treasure Island 的 KL 散度记作 KL(Treasure Island||Huckleberry Finn)。中间的两个竖线可以看作是一个分隔符,类似于我们更常见的逗号。我们可以将其理解为“使用…编码发送”的意思。如果我们通过数学计算,这个值大约是 0.287。我们可以把它理解为告诉我们,因为我们使用了错误的编码,所以每个词大约“多付出”了 0.3 个额外比特(Kurt 2017)。用 Treasure Island 编码发送 Huckleberry Finn 的 KL 散度,记作 KL(Huckleberry Finn||Treasure Island),则要高得多,大约是 0.5。

KL 散度告诉我们,为了使用不完美的编码发送消息,我们需要多少额外的比特。另一种理解方式是,KL 散度描述了我们需要多少更多的信息,将我们的不完美适配的编码转换为完美编码。我们可以将其想象为贝叶斯规则的一步,在这一步中,我们从一个*似的先验(不完美编码)转换为一个更好的后验(适配的编码)。在这种情况下,KL 散度告诉我们从理想化的贝叶斯规则步骤中,我们学到了多少东西(Thomas 2017)。

我们可以通过最小化 KL 散度或交叉熵来训练系统,选择哪个更方便都可以。KL 散度具有良好的数学性质,并且出现在许多数学和算法讨论中,甚至在深度学习文档中也会涉及。但是在实践中,计算交叉熵几乎总是更快的。由于最小化任意一个都能改善系统的效果,因此我们通常在技术讨论中看到 KL 散度,而在深度学习程序中看到交叉熵。

总结

在本章中,我们探讨了一些信息理论的基本思想,以及如何利用这些思想来训练深度学习系统。我们通过将代码转换为概率分布来在机器学习中运用这些思想。这意味着我们将代码中最小的代码元素视为最频繁的元素,而随着数字的增大,频率则会下降。从这个角度解读,我们可以通过比较分类器对输入产生的预测概率列表与我们手动分配的概率列表,来计算交叉熵。我们在训练中的目标是尽可能使这两个分布相似,我们也可以将其表述为尽量最小化交叉熵。

这标志着本书的第一部分结束。我们已经涵盖了一些具有深远价值的基本概念,这些概念远超深度学习的范畴。统计学、概率论、贝叶斯定理、曲线和信息理论都能帮助我们理解各种各样的问题,甚至是日常生活中遇到的事物。它们能帮助我们改善对世界中发生事件的推理,从而帮助我们理解过去并为未来做准备。

拥有了这些基本原理后,我们将转向机器学习的基本工具。

第二部分

基础机器学习

第七章:分类

机器学习的一个重要应用是观察一组输入,将每个输入与一组可能的类(或类别)进行比较,并将每个输入分配给其最可能的类。这个过程称为分类类别化,我们说它是由分类器执行的。我们可以使用类来处理各种任务,比如识别某人通过手机说出的词语,照片中可见的动物,或者水果是否成熟。

在本章中,我们将探讨分类背后的基本思想。我们在这里不考虑具体的分类算法,因为这些内容会在第十一章详细讨论。我们现在的目标只是熟悉这些原理。我们还将讨论聚类,它是一种自动将没有标签的样本分组的方式。最后,我们将探讨如何在四维及更高维度的空间中,我们的直觉往往会受到干扰,导致在训练深度学习系统时出现潜在问题。在超过三维的空间中,事物可能会以出乎意料且令人惊讶的方式运作。

二维二元分类

分类是一个庞大的话题。让我们从一个高层次的概述开始,然后再深入探讨一些具体细节。

一种流行的训练分类器的方法是使用监督学习。我们首先收集一组样本,或者说我们想要分类的数据。这些数据被称为训练集。我们还准备一份类别列表,或者说分类标签,例如照片中可能出现的动物,或者应该分配给音频样本的音乐类型。最后,我们手动考虑训练集中的每个示例,并确定应该将其分配到哪个类别。这就是该样本的标签

然后,我们将每个样本逐一提供给计算机,但不告诉它标签。计算机处理样本,并得出它认为应分配的类别的预测。现在,我们将计算机的预测与我们的标签进行比较。如果分类器的预测与我们的标签不匹配,我们会稍微调整分类器,使其在再次看到此样本时更有可能预测出正确的类别。我们称这个过程为训练,并说系统正在学习。我们会反复进行这一过程,通常是用成千上万甚至数百万个样本,一次又一次地重复。我们的目标是逐步改进算法,直到它的预测与我们的标签匹配的频率足够高,以至于我们认为它准备好投入实际应用,我们期望它能够正确分类之前从未见过的新样本。到那时,我们会用新数据测试它,看看它的效果如何,以及它是否准备好用于实际应用。

让我们更仔细地看看这个过程。

为了开始,让我们假设我们的输入数据仅属于两个不同的类别。仅使用两个类别简化了我们对分类的讨论,而不会遗漏任何关键点。因为每个输入只有两种可能的标签(或类别),我们称之为二分类

另一种简化方法是使用二维(2D)数据,因此每个输入样本由恰好两个数字表示。这恰到好处地既有趣又简单,因为我们可以将每个样本显示为*面上的一个点。在实际操作中,这意味着我们有一堆点,或称为小圆点,显示在页面上。我们可以使用颜色和形状编码来显示每个样本的标签和计算机的预测。我们的目标是开发一个算法,能够准确地预测每个标签。当它做到了这一点,我们就可以将该算法应用于没有标签的新数据,并依赖它告诉我们哪些输入属于哪个类别。

我们称这个为二维二分类系统,其中“二维”指的是数据点的两个维度,而“二分类”指的是两个类别。

我们将要看的第一组技术统称为边界方法。这些方法背后的思想是,我们可以查看绘制在*面上的输入样本,并找到一条将空间划分开来的线或曲线,使得所有带有一种标签的样本都位于曲线(或边界)的一侧,而所有带有另一标签的样本都位于另一侧。我们将看到,一些边界在预测未来数据时比其他边界更有效。

让我们通过鸡蛋来具体化这个问题。假设我们是农场主,拥有大量下蛋的母鸡。这些鸡蛋可能已经受精并正在孵化小鸡,或者没有受精。假设如果我们仔细测量每个蛋的一些特征(例如它的重量和长度),就可以判断它是否受精。(这完全是虚构的,因为蛋并不是这么工作的!但我们假装它们是。)我们将重量和长度这两个特征结合起来形成一个样本。然后我们将样本交给分类器,它会将其归类为“受精”或“未受精”。

因为我们用于训练的每个蛋都需要一个标签,或者一个已知的正确答案,我们使用一种叫做打光法的技术来判断蛋是否受精(内布拉斯加州 2017)。擅长打光法的人被称为candler。打光法涉及将蛋在强光源前举起。最初,打光员使用蜡烛,但现在他们使用任何强光源。通过解释蛋内内容物在蛋壳上投射出的模糊暗影,熟练的打光员能够判断蛋是否受精。我们的目标是让分类器给出与熟练打光员确定的标签相同的结果。

总结一下,我们希望我们的分类器(计算机)能够考虑每个样本(鸡蛋),并利用其特征(重量和长度)做出预测(受精或未受精)。让我们从一批训练数据开始,这些数据给出了某些鸡蛋的重量和长度。我们可以在一个网格上绘制这些数据,将重量作为一个轴,长度作为另一个轴。图 7-1 展示了我们的初始数据。受精的鸡蛋用红色圆圈表示,未受精的鸡蛋用蓝色方框表示。通过这些数据,我们可以在两个鸡蛋群体之间画一条直线。直线一侧的所有鸡蛋都是受精的,另一侧的则是未受精的。

F07001

图 7-1:鸡蛋分类。红色圆圈表示受精的,蓝色方块表示未受精的。每个鸡蛋被绘制为一个点,依据其重量和长度的两个维度。橙色线将两类分开。

我们的分类器完成了!当我们得到新的鸡蛋(没有已知标签时),只需要查看它们被绘制时位于哪一边。位于受精一侧的鸡蛋被归类为“受精”,位于未受精一侧的鸡蛋被归类为“未受精”。图 7-2 展示了这个概念。

F07002

图 7-2:鸡蛋分类。左:边界。第二个从左:显示由边界划分的两个区域或域。第三个从左:四个新的样本待分类。最右边:新样本被分配的类别。

假设这几季都进展顺利,然后我们购买了一大批新种类的鸡。为了防止它们的鸡蛋和我们之前的不同,我们手动检查了来自两种鸡的新鸡蛋,并记录是否受精,然后像之前一样绘制结果。图 7-3 展示了我们的新数据。

F07003

图 7-3:当我们将一些新的鸡种加入到我们的鸡群中时,仅根据重量和长度来判断鸡蛋是否受精可能会变得更加复杂。

这两个群体仍然很清晰可分,这很棒,但现在它们是通过一条弯曲的曲线而不是直线来分开的。这没问题,因为我们可以像之前一样使用这条曲线。当我们有额外的鸡蛋需要分类时,每个鸡蛋会被放置在这个图表上。如果它位于红色区域,就预测为受精;如果它位于蓝色区域,就预测为未受精。当我们能够这样清晰地将事物区分开时,我们称将*面划分成的区域为决策区域,它们之间的线或曲线称为决策边界

假设消息传开了,人们都喜欢我们农场的鸡蛋,于是第二年我们又买了一批第三种品种的鸡。像之前一样,我们手动检查了一些鸡蛋并绘制了数据,这次得到的图表如图 7-4 所示。

F07004

图 7-4:我们新购买的鸡使得区分受精蛋和未受精蛋变得更加困难。

我们仍然有一个主要是红色的区域和一个主要是蓝色的区域,但我们没有明确的办法来绘制一条线或曲线将它们分开。让我们采取一种更通用的方法,而不是以绝对确定性预测单一类别,而是为每个可能的类别分配一个概率

图 7-5 使用颜色来表示我们网格中一个点属于某个特定类别的概率。对于每个点,如果它是亮红色的,那么我们非常确定该蛋已被受精,而红色的强度逐渐减弱对应着受精概率的降低。对于未受精的蛋,蓝色的区域具有相同的解释。

F07005

图 7-5:根据最左侧显示的重叠结果,我们可以为每个点分配一个受精的概率,如中央所示,亮红色表示该蛋更可能被受精。最右侧的图像显示了该蛋未受精的概率。

一个落在深红色区域的蛋很可能是受精的,而落在深蓝色区域的蛋很可能是未受精的。在其他地方,正确的类别并不那么明确。我们的处理方式取决于农场的政策。我们可以利用第三章中的准确率、精确率和召回率的概念来制定这个政策,并告诉我们应该绘制什么样的曲线来分隔这些类别。例如,假设“受精”对应“正例”。如果我们想非常确定地捕捉到所有受精蛋并且不介意一些假阳性,我们可以像 图 7-6 中的中心一样绘制边界。

另一方面,如果我们想找出所有未受精的蛋,并且不介意假阴性,我们可以像 图 7-6 右侧那样绘制边界。

F07006

图 7-6:根据最左侧的结果,我们可以选择中央所示的策略,接受一些假阳性(未受精的蛋被分类为受精)。或者我们可以选择正确分类所有未受精的蛋,采用最右侧的策略。

当决策区域具有明显的边界且不重叠时,如 图 7-6 中的中央和右侧图像,每个样本的概率很容易计算:样本所在的类别是确定的,概率为 1,其他类别的概率为 0。但在更常见的情况下,当区域模糊或重叠时,如 图 7-5 中所示,两个类别可能都有非零概率。

在实际操作中,最终我们总是需要将概率转化为一个决策:这个蛋是受精的还是未受精的?我们的最终决策受到计算机预测的影响,但最终我们还需要考虑人为因素以及这个决策对我们的意义。

2D 多类别分类

我们的蛋卖得很好,但有个问题。我们只是在区分受精蛋和未受精蛋。随着我们对蛋了解更多,我们发现未受精蛋有两种不同的形成方式。未受精蛋如果从未受精,就叫做蛋黄蛋。这些蛋适合食用。我们可以卖给其他农民的受精蛋叫做优胜蛋。但在一些受精蛋中,胚胎由于某些原因停止了生长并死亡。这样的蛋叫做退出蛋(Arcuri 2016)。我们不想卖退出蛋,因为它们可能会突然爆裂并传播有害细菌。我们希望能够识别退出蛋并将其处理掉。

现在我们有了三类蛋:优胜蛋(可用的受精蛋)、蛋黄蛋(安全的未受精蛋)和退出蛋(不安全的受精蛋)。和之前一样,假设我们仅凭蛋的重量和长度就能区分这三种蛋。图 7-7 展示了一组测量过的蛋,以及我们通过照蛋(照明)手动分配给它们的类别。

F07007

图 7-7:左:三类蛋。红色圆圈表示受精蛋。蓝色方块表示未受精但可食用的蛋黄蛋。黄色三角形表示我们想从孵化器中移除的退出蛋。右:每个类别可能的边界和区域。

将新输入分配到这三类中的一种任务叫做多类分类。当我们有多个类别时,我们再次找到与不同类别相关的区域之间的边界。当一个训练好的多类分类器被发布到世界上并接收到一个新样本时,它会确定该样本属于哪个区域,然后将该样本分配给该区域对应的类别。

在我们的例子中,我们可能会向每个样本添加更多的特征(或维度),例如蛋的颜色、*均周长和蛋被下的时间。这将使我们每个蛋拥有五个数字。五维空间是一个难以理解的地方,我们显然不能画出有用的图像。但我们可以通过类比来推理我们可以想象的情况。在二维空间中,我们的数据点倾向于在各自的位置上聚集,这使我们能够在它们之间画出边界线(或曲线)。在更高维度中,大多数情况下也是如此(我们将在本章末尾进一步讨论这个想法)。就像我们把二维的正方形分割成几个小的二维形状,每个形状代表一个不同的类别,我们也可以把五维空间分割成多个更小的五维形状。这些五维区域也定义了不同的类别。

数学原理不关心我们有多少个维度,我们基于这些数学原理构建的算法也不在乎。这并不是说我们作为人类不在乎,因为通常情况下,维度数越多,算法的运行时间和内存消耗也会相应增加。我们将在本章末尾回到处理高维数据时涉及的一些问题。

多分类分类

二分类器通常比多分类器更简单、更快速。但在实际应用中,大多数数据都有多个类别。幸运的是,我们可以通过构建多个二分类器并结合它们的结果来生成一个多分类的答案,而无需构建一个复杂的多分类器。让我们看看两种流行的方法来实现这一技术。

一对其余

我们的第一种技术有几个名字:一对多OvR)、一对所有OvA)、一对其余OAA)或二元相关性方法。假设我们有五个类别的数据,分别用字母 A 到 E 命名。我们不是构建一个复杂的分类器来分配这五个标签,而是构建五个更简单的二分类器,并分别命名为 A 到 E,表示每个分类器专注于的类别。分类器 A 告诉我们一个给定数据是否属于类别 A。由于它是一个二分类器,它有一个决策边界,将空间划分为两个区域:类别 A 和其他类别。我们现在可以理解“一对其余”这个名称的来源了。在这个分类器中,类别 A 是“一个”,类别 B 到 E 是“其余”。

我们的第二个分类器,命名为分类器 B,是另一个二分类器。它告诉我们一个样本是否属于类别 B。以此类推,分类器 C 告诉我们一个样本是否属于类别 C,而分类器 D 和 E 则分别对类别 D 和 E 执行相同的操作。图 7-8 总结了这个概念。在这里,我们使用了一种算法,它在为每个分类器构建边界时考虑了所有数据。

f07008

图 7-8:一对多分类。上排:来自五个不同类别的样本。下排:五个不同二分类器的决策区域。从紫色到粉色的颜色显示了该点属于某类别的概率逐渐增加。

请注意,二维空间中的某些位置可能属于多个类别。例如,右上角的点来自类别 A、B 和 D,且它们的概率非零。

为了对一个样本进行分类,我们依次通过我们的五个二分类器,得到该点属于每个类别的概率。然后,我们找到概率最大的类别,这就是该点被分配到的类别。图 7-9 展示了这一过程的实际操作。

F07009

图 7-9:使用一对其余方法对样本进行分类。新样本是黑色的点。

在图 7-9 中,前四个分类器的返回概率都较低。类别 E 的分类器为该点分配了比其他分类器更大的概率,因此该点被预测为来自类别 E。

这种方法的吸引力在于它的概念简单且速度较快。缺点是我们需要训练五个分类器,而不是仅仅一个,并且随后我们必须对每一个输入样本进行五次分类,才能找出它属于哪个类别。当类别数较多且边界复杂时,运行大量二元分类器所需的时间会积累起来。另一方面,我们可以将所有五个分类器并行运行,因此如果我们有合适的设备,所有分类器的运行时间将与其中任何一个分类器的运行时间相同。对于任何应用,我们需要根据时间、预算和硬件限制来*衡这些权衡。

一对一

我们的第二种方法是使用二元分类器处理多类别问题,称为一对一OvO),它使用的二元分类器数量比一对多(OvR)还要多。一般来说,方法的核心思想是查看数据中每一对类别,并为这两类建立一个分类器。由于随着类别数的增加,可能的配对数迅速增加,因此这种方法中的分类器数量也会随着类别数的增加而迅速增长。为了让事情更容易管理,这次我们只考虑四个类别,如图 7-10 所示。

F07010

图 7-10:左侧:展示 OvO 分类的四个类别点。右侧:各簇的名称。

我们从一个二元分类器开始,该分类器仅使用来自类别 A 和 B 的数据进行训练。为了训练这个分类器,我们仅仅排除掉所有未标记为 A 或 B 的样本,就好像它们根本不存在一样。这个分类器找到一个边界,将类别 A 和 B 分开。现在,每次我们将一个新样本输入到这个分类器时,它会告诉我们该样本属于类别 A 还是 B。由于这是该分类器唯一可以选择的两个选项,因此它会将数据集中每个点都分类为 A 或 B,即使它其实不是这两个中的任何一个。我们很快就会明白为什么这样做是可以的。

接下来,我们建立一个仅使用类别 A 和 C 的数据进行训练的分类器,另一个则用于类别 A 和 D。图 7-11 的顶部行以图形方式展示了这一过程。接着,我们继续进行所有其他配对,构建仅使用类别 B 和 C 以及 B 和 D 的数据进行训练的二元分类器,正如图 7-11 第二行所示。最后,我们到达类别 C 和 D 的配对,这一过程展示在图 7-11 的底行。最终的结果是我们有了六个二元分类器,每个分类器告诉我们数据最可能属于哪个特定的类别。

为了对一个新示例进行分类,我们需要将其传入所有六个分类器,然后选择出现次数最多的标签。换句话说,每个分类器都会投票选出两个类中的一个,最终我们将出现最多票数的类别作为该样本的预测类。图 7-12 展示了 OvO 方法的实际应用。

F07011

图 7-11:构建用于执行 OvO 分类的六个二元分类器,分类四个类。顶部行:从左到右,分别是 A 和 B、A 和 C、A 和 D 的二元分类器。第二行:从左到右,分别是 B 和 C、B 和 D 的二元分类器。底部行:C 和 D 类的二元分类器。

在这个例子中,A 类得到了三票,B 类得到了四票,C 类得到了两票,而 D 类没有得到任何票数。最终的赢家是 A 类,因此它是该样本的预测类别。

One-versus-one 方法通常需要比 one-versus-rest 方法更多的分类器,但有时它更具吸引力,因为它可以提供关于样本如何在每一对可能的类别之间进行评估的更清晰理解。这可以使得系统更具透明性,或者在我们想了解系统如何得出最终答案时具有更好的可解释性。当多个类别之间存在大量混杂的重叠时,One-versus-one 方法能够帮助我们人类更容易理解最终结果。

这种清晰性的代价是巨大的。One-versus-one 方法需要的分类器数量随着类别数量的增加而迅速增长。我们已经看到,当类别数量为 4 时,我们需要 6 个分类器。更进一步,图 7-13 展示了随着类别数量增加,所需的二元分类器数量增长的速度有多快。对于 5 类,我们需要 10 个分类器,20 类时需要 190 个,30 类时需要 435 个分类器!当类别数量超过 46 时,我们需要超过 1,000 个分类器。

F07012

图 7-12:OvO 方法的实际应用,分类一个显示为黑点的新样本。顶部行:从左到右,投票结果为 A、A 和 A。第二行:从左到右,投票结果为 C 和 B。底部行:投票结果为 C 类。

F07013

图 7-13:随着类别数量的增加,我们需要的 OvO 二元分类器数量增长得非常快。

每个二元分类器都需要进行训练,之后我们需要将每个新样本传入每个分类器,这将消耗大量的计算机内存和时间。某些情况下,使用一个单一、复杂的多类分类器会更高效。

聚类

我们已经看到,一种对新样本进行分类的方法是将空间划分为不同的区域,然后测试每个点与每个区域的关系。另一种方法是将训练集数据本身分成,或者相似的块。假设我们的数据有相关的标签,我们如何利用这些标签来进行聚类?

在图 7-14 的左图中,我们有五个不同标签的数据,通过颜色显示。对于这些分离得很好的组,我们只需画出每组点的曲线,就能形成簇,如中间的图所示。如果我们将这些曲线向外延伸,直到它们互相交汇,让网格中的每个点都根据它最接*的簇被上色,我们就可以覆盖整个*面,如右图所示。

F07014

图 7-14:聚簇增长。左图:开始时的数据,包含五个类别。中图:识别出五个组。右图:将这些组向外扩展,直到每个点都被分配到一个类别。

这种方法要求我们的输入数据有标签。如果我们没有标签呢?如果我们能以某种方式自动将未标记的数据分成簇,我们仍然可以应用我们刚才描述的技术。

记住,涉及无标签数据的问题属于我们称之为 无监督学习 的学习类型。

当我们使用算法自动从未标记的数据中推导出簇时,我们必须告诉它我们希望它找到多少个簇。这个“簇的数量”值通常用字母 k 来表示(这是一个任意的字母,并没有特别的含义)。我们说 k 是一个 超参数,即我们在训练系统之前选择的一个值。我们选择的 k 值告诉算法应该构建多少个区域(即,将我们的数据分成多少个类)。由于该算法使用点群的几何均值或*均值来构建簇,因此该算法被称为 k-均值聚类

选择 k 值的自由是一个祝福也是一个诅咒。拥有这一选择的好处是,如果我们事先知道应该有多少个簇,我们可以直接告诉算法,算法就会产生我们想要的结果。请记住,计算机并不知道我们认为簇的边界应该在哪里,因此尽管它将数据分成了 k 个部分,但这些部分可能不是我们预期的样子。但是,如果我们的数据已经很好地分开了,样本聚集在一起且之间有较大的空隙,我们通常会得到我们期望的结果。簇的边界越模糊或重叠,事情就越有可能让我们感到意外。

提前指定 k 的缺点是我们可能不知道多少个簇最能描述我们的数据。如果我们选择的簇太少,那么我们就无法将数据分成最有用的不同类别。但是,如果我们选择的簇太多,那么我们最终可能会把相似的数据分到不同的类别中。

要查看这个过程,考虑一下图 7-15 中的数据。这里有 200 个未标记的点,故意将它们聚集成五组。

F07015

图 7-15:一组 200 个未标记的点。它们似乎在视觉上被分成了五组。

图 7-16 展示了k-均值聚类如何为不同的k值划分这组点。记住,我们在算法开始工作之前会提供k值作为参数。

F07016

图 7-16:自动对图 7-15 中的数据进行聚类,k值从 2 到 7

不足为奇的是,k = 5 在这组数据上表现得最好,但我们使用的是一个简单的示例,边界容易辨识。在更复杂的数据中,尤其是当数据维度超过两三个时,我们几乎不可能轻松地在事前识别出最有用的聚类数。

但并非一切都失去希望。一种常见的选择是多次训练我们的聚类模型,每次使用不同的k值。通过衡量结果的质量,这种超参数调优让我们能够自动搜索一个合适的k值,评估每个选择的预测结果,并报告表现最好的k值。当然,缺点是,这需要计算资源和时间。这就是为什么在聚类之前使用某种可视化工具来预览数据如此有用。如果我们能够立刻选出最佳的k值,或者甚至给出一个可能值的范围,就能节省评估那些无法提供良好结果的k值的时间和精力。

维度灾难

我们一直在使用具有两个特征的数据示例,因为二维数据很容易在页面上绘制。但实际上,我们的数据可以有任意数量的特征或维度。似乎特征越多,我们的分类器效果越好。因为更多的特征意味着分类器可以使用更多的信息来找到数据中的边界(或聚类),这似乎是合乎逻辑的。

这在某种程度上是正确的。超过这个点后,向数据中添加更多特征实际上会让情况变得更糟。在我们的鸡蛋分类示例中,我们可以为每个样本添加更多的特征,例如蛋下的温度、母鸡的年龄、巢中同时有的其他鸡蛋数量、湿度等。但正如我们将看到的,添加更多特征通常会使得系统更难,而不是更容易,准确地分类输入。

这个反直觉的观点出现得如此频繁,以至于它有了一个特别的名字:维度灾难(Bellman 1957)。这个词在不同领域有不同的含义。在这里我们使用它指的是机器学习中的含义(Hughes 1968)。让我们来看看这个“灾难”是如何产生的,以及它告诉我们关于训练的哪些信息。

维度与密度

形象地理解维度灾难的一种方法是思考分类器如何找到边界曲线或边界面。如果只有少数几个点,分类器可以发明大量的曲线或面来划分数据。为了选择一个在未来数据中表现最好的边界,我们需要更多的训练样本。这样分类器就可以选择出最能分离这些更密集样本的边界。图 7-17 直观地展示了这一思路。

F07017

图 7-17:为了找到最佳的边界曲线,我们需要样本的良好密度。左图:我们有很少的样本,因此可以构建许多不同的边界曲线。右图:通过更高密度的样本,我们可以找到一条好的边界曲线。

如图 7-17 所示,找到一条好的曲线需要密集的点集。但关键的洞察是:当我们为样本添加维度(或特征)时,为了在样本空间中保持合理的密度,我们所需的样本数量会爆炸性增加。如果我们跟不上这种需求,分类器会尽力而为,但它没有足够的信息来绘制出好的边界。它就像图 7-17 左侧的情况,正在猜测最佳的边界,而这可能会导致未来数据上的结果不佳。

让我们通过鸡蛋的例子来看一下密度丧失的问题。为了简化问题,我们假设所有可能测量的鸡蛋特征(如体积、长度等)都位于 0 到 1 的范围内。首先,我们用一个包含 10 个样本的数据集,每个样本有一个特征(鸡蛋的重量)。由于每个鸡蛋有一个维度描述,我们可以将它绘制在一个从 0 到 1 的单维线段上。为了查看我们的样本是否覆盖了线段的每一部分,我们将线段划分成几个部分,或者叫做区间,看看每个区间内有多少个样本。区间只是一个帮助我们估算密度的概念工具。图 7-18 显示了一个数据集如何分布在区间[0,1]上,且该区间被划分为 5 个区间。

f07018

图 7-18:我们的 10 个数据点每个都有一个维度。

选择 10 个样本和 5 个区间并没有什么特别之处。我们之所以选择它们,是因为这样画图更方便。如果我们选择 300 个鸡蛋或 1700 个区间,我们讨论的核心内容不会发生变化。

我们空间的密度是样本数除以箱子数。这为我们提供了一种粗略的方式来衡量我们的数据在多大程度上填充了可能值的空间。换句话说,我们是否有足够的示例来学习大多数输入值?如果我们有太多空的箱子,问题就开始显现出来。在这种情况下,密度是 10 / 5 = 2,这告诉我们每个箱子(*均)有 2 个样本。通过查看图 7-18,我们可以看到这是每个箱子中样本数的一个相当不错的估计。在一维数据中,密度为 2 让我们能够找到一个好的边界。

现在让我们将每个蛋的重量也包含进描述中。由于我们现在有了两个维度,我们可以将图 7-18 中的线段向上拉,形成一个二维正方形,如图 7-19 所示。

f07019

图 7-19:我们的 10 个样本现在每个都由两个测量值或维度来描述。

像之前一样,将每个边分成 5 个部分,我们得到了 25 个箱子在正方形内部。但我们仍然只有 10 个样本。这意味着大多数区域将没有数据。现在的密度是 10 / (5 × 5) = 10 / 25 = 0.4,远低于一维数据时的密度 2。因此,我们可以绘制许多不同的边界曲线来划分图 7-19 中的数据。

现在让我们添加第三个维度,比如蛋被产下时的温度(缩放到 0 到 1 之间的值)。为了表示这个第三个维度,我们将正方形推回页面,形成一个 3D 立方体,如图 7-20 所示。

F07020

图 7-20:现在我们的 10 个数据点由三个测量值表示,因此我们将它们绘制在 3D 空间中。

通过将每个轴分成 5 个部分,我们现在有了 125 个小立方体,但我们仍然只有 10 个样本。我们的密度已下降到 10 / (5 × 5 × 5) = 10 / 125 = 0.08。换句话说,*均每个单元格包含 0.08 个样本。我们的密度从 2 降到了 0.4,再降到 0.08。不管数据位于 3D 空间的哪个位置,绝大部分空间都是空的。

任何将这个 3D 数据分成两部分并设置边界面的分类器,都必须做出一个大猜测。问题不在于分隔数据困难,而在于太容易了。并不清楚如何最好地分隔数据,使我们的系统能够泛化,即正确分类我们未来得到的点。分类器将不得不将许多空的框分类为属于某一类,但它没有足够的信息以系统化的方式做到这一点。

换句话说,谁知道我们的系统部署后,新样本会最终到达哪里?目前,没人知道。图 7-21 显示了一个边界面猜测,但正如我们在图 7-17 中看到的那样,我们可以通过两个样本集之间的大开放空间拟合各种*面和弯曲的面。它们中的大多数可能不会很好地进行泛化。也许这个面离红色小球太*了,或者也许它离得还不够*,或者也许它应该是一个弯曲的表面而不是*面。

当我们用这个边界面预测新数据的类别时,期望的低质量并不是分类器的错。这个*面是一个完全合适的边界面,考虑到分类器可用的数据。问题在于,由于样本的密度如此低,分类器根本没有足够的信息来做好工作。随着每增加一个维度,密度像石头一样急剧下降,随着我们添加更多特征,密度继续暴跌。

F07021

图 7-21:通过我们的立方体传递一个边界面,分隔红色和蓝色的样本

在三维以上的空间中很难画出图形,但我们可以计算它们的密度。图 7-22 显示了随着维度数增加,我们的 10 个样本的空间密度变化图。每条曲线对应每个轴上不同的箱数。请注意,随着维度的增加,不管我们使用多少箱,密度都会下降到 0。

如果我们有更少的箱数,我们有更大的机会填满每个箱子,但很快,箱数的增加就变得无关紧要。随着维度数的增加,我们的密度总是趋向于 0。这意味着我们的分类器最终会在猜测边界应该在哪里。将更多特征与我们的蛋数据结合起来会在一段时间内改善分类器,因为边界能更好地跟随数据的位置。但最终,我们需要大量的数据来满足这些新特征所要求的密度需求。

有一些特殊情况,新特征所造成的密度缺失不会引发问题。如果我们的新特征是冗余的,那么现有的边界已经足够好,无需更改。或者如果理想的边界很简单,比如一个*面,那么增加维度的数量不会改变该边界的形状。但在一般情况下,新特征会为我们的边界增加精细化和细节。因为为适应这些特征而增加的新维度导致密度下降到接* 0,这使得边界变得更难以找到,因此计算机会本质上在猜测它的形状和位置。

F07022

图 7-22:这是随着维度数增加,固定数量样本的点密度如何下降的示意图。每一条彩色曲线显示了沿每个轴的不同数量的箱数。

维度灾难是一个严重的问题,它可能让所有的分类尝试都变得徒劳无功。毕竟,没有一个好的边界,分类器就无法做好分类。挽救局面的是非均匀性的祝福(Domingos 2012),我们更倾向于将其称为结构的祝福。这个术语描述了这样一个观察结果:在实际情况中,即使在非常高维的空间中,我们通常测量的特征也不会在样本的空间中均匀分布。也就是说,它们不会均匀分布在我们所看到的线、*面和立方体中,或者我们无法绘制的这些形状的高维版本中。相反,它们通常会聚集在小区域内,或者分布在更简单、更低维的表面上(例如一张起伏的纸面或一条曲线)。这意味着我们的训练数据和所有未来数据通常会落在相同的区域中。这些区域将是密集的,而其余大部分空间将是空的。这是个好消息,因为它表明,无论我们如何在那些大空区域中绘制边界面,都没关系,因为那里不会有数据出现。我们希望并通常能发现的,正是样本实际落入的地方的良好密度。

让我们看看这种结构化的实际应用。在我们展示的图 7-20 中,我们可能会发现每个类别的样本都位于同一水*面上,而不是在立方体中大致均匀地分布。这意味着,任何大致水*的边界面,分割这些群体的可能也能很好地适应新数据,只要这些新值也倾向于落在这些水*面上。图 7-23 展示了这个想法。

F07023

图 7-23:实际上,我们的数据在样本空间中通常有一些结构。左:每组样本大多位于立方体的同一水*切片中。右:一条边界面穿过了两组点之间。

图 7-23 中的大部分立方体是空的,因此密度较低,但我们关心的部分具有较高的密度,因此我们可以找到一个合理的边界。尽管维度灾难注定使得我们在整体空间中拥有低密度,即使有大量数据,但结构的祝福告诉我们,我们通常会在需要的地方获得合理的高密度。在图 7-23 的右侧,我们展示了一个穿过立方体中部的边界面。这可以完成分离类别的任务,但由于它们聚集得如此紧密,并且它们之间的空间是空的,几乎任何分割这些群体的边界面都可能在泛化时表现良好。

请注意,维度灾难和维度优势都是经验观察,而不是我们可以始终依赖的硬性事实。因此,这个重要实际问题的最佳解决方案通常是尽可能多地填充样本空间,获取尽可能多的数据。在第十章中,我们将看到一些方法来减少数据中的特征数,如果它似乎导致不好的边界。

维度灾难是机器学习系统在训练时需要大量数据的原因之一。如果样本具有许多特征(或维度),我们需要大量的样本才能获得足够的密度,从而做出准确的分类预测。

假设我们想要足够的点数来获得特定的密度,比如 0.1 或 0.5。随着维度的增加,我们需要多少点呢?图 7-24 显示了所需点数迅速激增的情况。

F07024

图 7-24:为了实现不同密度所需的点数,假设我们在每个轴上有五个划分

一般来说,如果维度较低,并且我们有大量的点,那么我们很可能已经有足够的密度,使得分类器可以找到一个有很大可能性能够很好地推广到新数据的表面。句子中的大量的具体值取决于我们使用的算法以及数据的特点。没有固定的规则来预测这些值;我们通常会做一个猜测,看看得到的性能如何,然后进行调整。一般来说,在训练数据方面,更多确实意味着更多。尽可能获取所有你道德上可以获得的数据。

高维空间的奇异性

由于现实世界中的数据样本通常具有许多特征,我们经常在具有多个维度的空间中工作。我们之前看到,如果数据具有局部结构,我们通常没问题:我们对大空旷区域边界的无知不会对我们产生负面影响,因为没有输入数据会落入那里。但如果我们的数据没有结构或没有聚类呢?

很容易看图像,比如图 7-19 和 7-20,凭直觉指导我们设计学习系统,认为多维空间就像我们习惯的空间,只是更大而已。但事实并非如此!对于高维空间的特征,有一个技术术语:奇怪。事物的发展往往出乎我们意料。让我们看两个关于高维几何奇异性的警示故事,以训练我们的直觉,避免从我们熟悉的低维空间跳跃到泛化的结论。这将帮助我们在设计学习系统时保持警觉。

一个球体在立方体中的体积

一个著名的高维空间怪异性例子涉及一个球体在立方体中的体积(Spruyt 2014)。设置很简单:取一个球体并放入一个立方体中,然后测量球体占据了立方体多少空间。让我们先在一维、二维和三维中做这个。然后我们继续向更高维度推进,跟踪随着维度增加,球体占据立方体的百分比。

图 7-25 帮助我们了解 1D、2D 和 3D 的情况。

F07025

图 7-25:立方体中的球体。左:一个 1D 的“立方体”是一个线段,而“球体”完全覆盖了它。中:一个 2D 的“立方体”是一个正方形,而“球体”是一个触及边缘的圆形。右:一个 3D 的立方体包围着一个球体,球体接触到每个面的中心。

在一维中,我们的“立方体”只是一个线段,而球体是一个覆盖整个线段的线段。球体内容与“立方体”内容的比率是 1:1。在二维中,我们的“立方体”是一个正方形,而球体是一个圆,正好触及四条边的中心。圆的面积与盒子面积的比大约是 0.8。在三维中,我们的立方体是一个正常的 3D 立方体,球体正好适应其中,接触到六个面的中心。球体体积与立方体体积的比约为 0.5。

球体相对于包围它的盒子所占的空间正在下降。如果我们进行数学计算,并计算更高维度下球体的体积与其立方体的体积(它们被称为 超球体超立方体),我们得到的结果如 图 7-26 所示。

F07026

图 7-26:不同维度下,最大球体在盒子中所占的体积比例

超球体所占的体积逐渐接* 0。到了 10 维时,我们能放入它包围盒中的最大球体几乎不占据盒子的体积!

这与我们在 3D 世界中的经验大相径庭。如果我们取一个超立方体(具有多个维度),并将其中能容纳的最大超球体(具有相同数量的维度)放入其中,那个超球体的体积几乎为 0\。

这没有什么技巧,也没有出错。当我们计算数学时,结果就是这样。我们在前三个维度中看到了这个模式,但我们无法真正画出其余的维度,所以很难想象这到底是怎么发生的。但它确实是这样发生的,因为更高的维度很奇怪。

将超球体装入超立方体

为了确保我们的直觉正确无误,让我们再来看一个来自将超球体装入超立方体的奇特结果。假设我们要运输一些特别精美的橙子,并确保它们在运输过程中不受任何损坏。每个橙子的形状接*球形,因此我们决定将它们装入立方体盒子中,并用充气气球进行保护。我们在盒子的每个角落放一个气球,使得每个气球都触碰到橙子和盒子的侧面。这些气球和橙子都是完美的球体。那么,我们能将一个多大的橙子放入给定尺寸的立方体盒子中呢?

我们想要回答这个问题,对于任何维度的立方体(以及气球和橙子),让我们从二维开始。我们的盒子是一个大小为 4×4 的二维正方形,四个气球是半径为 1 的圆形,放置在四个角落,如图 7-27 所示。

F07027

图 7-27:将一个圆形二维橙子装入一个方形盒子中,盒子的四个角落被圆形气球包围。左图:四个气球的半径均为 1,因此它们可以完美地装入一个边长为 4 的方形盒子中。右图:橙子被放置在气球之间。

我们在这个二维图中的橙子也是一个圆形。在图 7-27 中,我们展示了能够放入的最大橙子。通过一点几何学推算,我们得知这个橙子圆的半径大约是 0.4。

现在让我们转到三维空间,因此我们有一个立方体(每边仍然是 4 单位)。我们现在可以将八个半径为 1 的球形气球放入立方体的角落,如图 7-28 所示。与往常一样,橙子被放入气球之间的中央空间。

F07028

图 7-28:将一个球形橙子装入一个立方体盒子中,盒子的每个角落都被球形气球包围。左图:盒子的尺寸为 4×4×4,八个气球的半径均为 1。右图:橙子坐落在气球的中央。

另一个几何问题(这次是三维的)告诉我们,这个橙子的半径大约是 0.7。这比二维情况下的橙子半径要大,因为在三维空间中,橙子可以在球体之间的中央空隙中获得更多的空间。

让我们将这个情景扩展到四维、五维、六维甚至更多维度,在这些维度中我们有超立方体、超球体和超橙子。对于任何维度,我们的超立方体每一边的长度总是 4,超立方体的每个角落都有一个半径为 1 的超球体气球。

我们可以写出一个公式,告诉我们在任何维度下,能装入此情景中的最大超橙子的半径(Numberphile 2017)。图 7-29 绘制了不同维度下的半径。

我们可以从图 7-29 看出,在四维空间中,我们能够运输的最大超橙子的半径恰好是 1。这意味着它和周围的超气球一样大。这很难想象,但情况会更奇怪。

图 7-29 同样告诉我们,在九维空间中,超橙子的半径为 2,因此其直径为 4。这意味着超橙子的大小与盒子本身一样,就像图 7-25 中的三维球体一样。尽管它被 512 个半径为 1 的超球体包围,而这些超球体位于这个 9D 超立方体的 512 个角落,但超橙子的大小和盒子相同。那么,这些气球是如何保护任何东西的呢?

但事情变得更疯狂了。在 10 维及更高维度时,超橙子的半径大于 2。超橙子现在原本要保护它的超立方体还要。它似乎超出了立方体的边界,尽管我们构建它时是为了让它既能适应盒子,也能适应每个角落的保护气球。对于我们这些有三维大脑的人来说,很难想象 10 维(或更多)的空间,但方程式是成立的:橙子同时位于盒子内部,并且延伸出了盒子之外。

F07029

图 7-29:在一个边长为 4 的超立方体中,超橙子的半径为 2,周围有半径为 1 的超球体,每个角落都有一个。

这里的教训是,当我们进入多维空间时,我们的直觉可能会失效(Aggarwal 2001)。这一点很重要,因为我们经常处理具有几十个或几百个特征的数据。

每当我们处理具有超过三个特征的数据时,我们就进入了高维空间的世界,我们不应通过类比来推理,尤其是依据我们在二维和三维空间中的经验。我们需要睁大眼睛,依赖数学和逻辑,而不是直觉和类比。

实际上,这意味着在处理多维数据时,我们需要密切关注深度学习系统在训练过程中的表现,并时刻警惕它出现异常的行为。《第十章》中的技术可以减少我们数据中的特征数量,这可能会有所帮助。在本书中,我们将看到其他改善系统的方式,无论是因为高维度的奇异性还是其他原因导致系统学习不佳。

总结

在本章中,我们探讨了分类的机制。我们看到,分类器可以将数据空间划分为由边界分隔的领域。新的数据通过识别它落入哪个领域来进行分类。这些领域可能是模糊的,表示概率,因此分类器的结果是每个类别的概率。我们还探讨了聚类算法。最后,我们发现,当我们在超过三维的空间中工作时,我们的直觉往往会出错。事物经常不会按我们预期的方式运行。这些高维空间很奇异,充满了惊喜。我们了解到,在处理多维数据时,我们应该谨慎行事,并在系统学习的过程中进行监控。我们永远不应依赖基于我们在 3D 经验的猜测!

在下一章,我们将探讨如何高效地训练一个学习系统,即使我们没有大量数据。

第八章:训练与测试

在这一章中,我们将讨论训练,即将一个已用默认或随机值初始化的系统,逐渐改进,以便它能够针对我们想要理解的数据进行调整。训练完成后,我们可以估计系统在评估新数据时的表现,即它如何处理之前未见过的数据,这一过程称为测试

我们使用一个监督分类器来说明这一章中的概念,通过标注数据来教它。我们讨论的大部分技术都是通用的,可以应用于几乎所有类型的学习器。

训练

当我们使用监督学习训练一个分类器时,每个样本都有一个与之相关的标签,用来描述我们手动分配给它的类别。所有我们要学习的样本以及它们的标签集合被称为训练集。我们将把训练集中的每个样本逐一呈现给分类器。对于每个样本,我们提供样本的特征并要求系统预测它的类别。

如果预测正确(即与我们分配的标签一致),我们就进入下一个样本。如果预测错误,我们将分类器的输出和正确的标签反馈给分类器。通过我们将在后续章节中看到的算法,我们修改分类器的内部参数,使其更有可能在再次看到该样本时预测正确的标签。

图 8-1 直观地展示了这个概念。我们使用分类器得到一个预测,并将其与标签进行比较。如果它们不一致,我们会更新分类器。然后我们继续下一个样本。

f08001

图 8-1:训练分类器的框图

当我们的训练过程一次处理一个样本时,分类器的内部变量会逐渐调整到能够更好地预测标签的值。每当我们遍历整个训练集一次,我们就说我们训练了一个周期。通常,我们会让系统经历多个周期,让系统多次看到每个样本。通常情况下,我们会继续训练,直到系统不再学习并且在训练数据上的表现有所改善,但如果时间耗尽,或者遇到我们在本章及第九章后面讨论的问题时,我们可能会停止训练。

现在让我们看看如何衡量分类器预测正确标签的准确性。

测试性能

我们从一个参数初始化为随机数的系统开始。然后,我们通过训练数据中的样本来教它。一旦系统被发布,或部署到现实世界,它将遇到新的现实世界数据(或部署数据发布数据用户数据)。我们希望在系统发布之前,能够知道它在现实世界数据上的表现如何。我们可能不需要完美的准确性,但通常希望系统达到或超过我们心中的某个质量标准。在系统发布之前,我们如何估计其预测质量呢?

我们需要系统在训练数据上表现得很好,但如果仅仅根据这些数据来评估系统的准确性,通常会误导我们。这是实践中的一个重要原则,所以让我们更详细地看一下。

假设我们打算使用我们的监督分类器来处理狗的图片。对于每一张图片,它将分配一个标签来识别狗的品种。我们的目标是将系统放到网上,让人们可以将自己狗的图片拖到浏览器上,然后得到该狗的品种,或者一个笼统的“混合品种”。

为了训练我们的系统,让我们收集 1,000 张不同纯种狗的照片,每张照片都由专家标注。通过使用图 8-1,我们可以将所有 1,000 张图片展示给系统,然后反复展示这些图片,一遍又一遍,每一个周期接一个周期。在这样做时,我们通常会打乱每个周期中的图片顺序,确保它们不会总是按照相同的顺序出现。如果我们的系统设计得好,它会逐渐开始产生越来越准确的结果,直到它可能在 99%的训练图片中正确识别出狗的品种。

这并意味着我们的系统在我们把它放到网上时会有 99%的准确率。问题在于,系统可能在利用训练数据中微妙的关系,而这些关系在一般的数据中并不成立。例如,假设我们的贵宾犬图片像图 8-2 这样。

当我们组建训练集时,我们没有注意到所有的贵宾犬尾巴末端都有一个小的球形,而其他狗都没有。但系统注意到了。数据中的这个小特征为系统提供了一种轻松分类贵宾犬的方法:系统不需要查看狗腿的大小、鼻子的形状等特征,而只需检查尾巴末端的小球。使用这个规则,它将正确分类所有我们的贵宾犬训练图片。我们有时会说系统做到了我们要求的事(“识别贵宾犬”),但没有做我们想要的事(“根据图片中大部分特征判断是否是贵宾犬”)。我们常说系统学会了作弊,尽管这可能不公*。它学会了一个捷径,给我们带来了我们所要求的结果。

f08002

图 8-2:用于识别狗狗品种的训练数据。上排:我们的输入贵宾犬图片。下排:我们的系统学习到的将图片识别为贵宾犬的特征,用红色标出。

另一个例子是,假设我们训练数据中所有约克夏犬(或 Yorkies)的图片都是在狗狗坐在沙发上时拍摄的,如图 8-3 所示。我们之前没有注意到这一点,也没有注意到另一个重要事实:其他所有图片中都没有沙发。系统可能会学到,如果图像中有沙发,它就能立即将这张图像分类为约克夏犬的图片。这条规则对于我们的训练数据来说是完全有效的。

f08003

图 8-3:上排:三只约克夏犬坐在沙发上。下排:我们的系统已经学会识别沙发,并用红色标出。

假设我们将系统部署后,有人提交了一张大丹犬站在一串大白球装饰前的照片,或者他们的西伯利亚哈士奇坐在沙发上的照片,如图 8-4 所示。我们的系统看到了大丹犬尾巴末端的白色球,告诉我们那是一只贵宾犬,并看到了沙发,忽略了狗,报告说那只哈士奇是约克夏犬。

f08004

图 8-4:上排:一只大丹犬站在一串白色球形装饰品前面,一只西伯利亚哈士奇躺在沙发上。下排:系统看到了大丹犬尾巴末端的白色球,告诉我们那是贵宾犬,并注意到沙发,将躺在沙发上的狗分类为约克夏犬。

这不仅仅是一个理论问题。有一个著名的例子描述了 1960 年代的一次会议,会上一个演讲者展示了一个早期的机器学习系统(Muehlhauser 2011)。数据的具体细节不清楚,但似乎他们有一些树丛中有伪装坦克的照片,以及没有坦克的树丛照片。演讲者声称该系统能够毫无失败地识别出带有坦克的图像。对于那个时代来说,这将是一项令人难以置信的壮举。

在演讲结束时,一位观众站起来指出,带有坦克的照片都是在阳光明媚的日子拍摄的,而没有坦克的照片则是在阴天拍摄的。看起来系统只是区分了明亮的天空和阴暗的天空,因此那些令人印象深刻(且准确)的结果其实与坦克毫无关系。

这就是为什么仅仅查看训练数据上的表现不足以预测在现实世界中的表现。系统可能会学到一些训练数据中的奇怪特征,并将其作为规则来使用,结果却被没有这些特征的新数据所破坏。这种现象在正式上称为过拟合,但我们通常称其为作弊。我们将在第九章更详细地讨论过拟合问题。

我们已经看到,我们需要一种除了训练集上的性能之外的度量方式来预测我们部署系统后它的表现。如果有一种算法或公式可以拿我们的训练好的分类器并告诉我们它有多好,那该有多好,但事实并没有。我们无法知道系统的表现如何,除非通过尝试并观察。就像自然科学家必须通过实验来了解现实世界中的实际情况一样,我们也必须通过实验来看看我们的系统表现如何。

测试数据

到目前为止,任何人发现的最佳方法是给系统提供新的、看不见的数据,看看它的表现如何,这样我们就能知道系统在新数据上的表现。没有捷径可以走这种实验验证。

我们将这一组看不见的数据点或样本称为测试数据测试集。像训练数据一样,我们希望测试数据能够代表我们系统发布后将要遇到的数据。典型的过程是使用训练数据训练系统,直到系统表现达到我们认为的最佳水*。然后我们在测试数据上进行评估,这能告诉我们系统在现实世界中的表现如何。

如果系统在测试数据上的表现不够好,我们需要改进它。由于在更多数据上训练几乎总是提高性能的好方法,通常收集更多数据并重新训练是一个不错的主意。获取更多数据的另一个好处是可以使我们的训练集更加多样化。例如,我们可能会发现除贵宾犬以外的其他犬种尾巴上有小球,或者可能会发现除约克夏犬以外的其他犬种在沙发上。这样我们的分类器就必须找到其他方法来识别这些狗,我们也能避免由于过拟合而犯错。

训练和测试过程中的基本规则是我们永远不能从测试数据中学习。尽管将测试数据放入训练集,让系统有更多的示例来学习是很诱人的,但这么做会破坏测试数据作为衡量系统准确性的客观工具的价值。从测试数据中学习的问题在于它变成了训练集的一部分。这意味着我们又回到了最初的状态:系统很容易专注于测试数据中的独特特征。如果我们随后使用测试数据来看分类器的表现,它可能会正确预测每个样本的标签,但可能是在作弊。如果我们从测试数据中学习,它就失去了作为衡量系统在新数据上表现的特殊和宝贵的质量。

出于这个原因,我们在开始训练之前就将测试数据从训练数据中拆分出来,并将其保留下来。我们只有在训练结束后才会回到测试数据,然后只使用一次来评估系统的质量。如果系统在测试集上的表现不够好,我们不能只是继续训练然后再进行测试。可以将测试集看作是课堂上的期末考试题目:一旦被看到,它们就不能再使用。如果系统在测试数据上的表现不好,我们必须从头开始,使用一个初始化为随机值的系统。然后,我们可以使用更多的数据或训练更长的时间。当训练完成后,我们可以再次使用测试集,因为这个新训练的系统之前从未接触过它。如果它再次表现不够好,我们必须重新开始训练。

这点很重要,值得重复:我们绝不能在训练完成之前以任何方式让系统接触到测试数据。

无意中从测试数据中学习的问题有一个专门的名称:数据泄漏,也叫做数据污染污染数据。我们必须时刻警惕这个问题,因为随着我们的训练过程和分类器变得越来越复杂,数据泄漏可能会以不同(且难以察觉)的伪装悄然进入。通过执行数据卫生可以避免数据泄漏:始终确保测试数据被单独保存,并且只有在训练完成后才使用一次。

我们通常通过将原始数据集拆分为两个部分来创建测试数据:训练集和测试集。我们通常将约 75%的样本分配给训练集。样本通常是随机选择的,但更复杂的算法可以尝试确保每个集合都能较好地*似完整的输入数据。大多数机器学习库都提供了可以为我们执行这一拆分过程的功能。

图 8-5 展示了这一思想。

f08005

图 8-5:将输入示例拆分为训练集和测试集。通常的拆分比例为 75:25 或 70:30。

验证数据

在到目前为止的讨论中,我们训练了系统一段时间,然后停止并使用测试集评估其性能。如果性能不够好,我们就重新开始训练。

这个策略没有问题,唯一的问题是它是一种工作效率较慢的方法。在实际操作中,我们通常希望在进行过程中对系统的性能进行大致估计,这样当我们认为系统能够在测试集上达到我们想要的性能时,就可以停止训练。

为了做出这个估计,我们将输入数据分为三个数据集,而不是之前看到的两个数据集。我们将这个新数据集称为验证数据,或验证集。验证数据是另一块数据,它旨在成为我们在部署系统时将遇到的现实世界数据的良好代理。我们通常通过将大约 60% 的原始数据分配给训练集,20% 分配给验证集,剩余的 20% 分配给测试集来生成这三个数据集。图 8-6 展示了这一思路。

f08006

图 8-6:将我们的输入数据分为训练集、验证集和测试集

我们的新过程是训练系统一个周期,遍历整个训练集,然后通过要求它对验证集进行预测来估计其性能。我们在每个周期后都这样做,因此我们在重复使用验证集。这会导致数据泄漏,但我们只使用验证数据进行非正式的估计。我们使用系统在验证集上的表现来大致了解它随着时间的推移学习得怎么样。当我们认为系统表现足够好以进行部署时,我们使用一次性测试集来获得可靠的性能估计。

当我们使用自动化搜索技术尝试不同的超参数值时,验证集也非常有用。回想一下,超参数是我们在运行系统之前设置的变量,用来控制系统的操作方式,例如在出错后应更新多少内部值,或者我们分类器的复杂度应该有多大。对于每一个变化,我们在训练集上进行训练,并在验证集上评估系统的表现。正如我们所提到的,我们并不从验证集中学习,但我们确实会反复使用它。来自验证集的结果仅仅是我们系统表现的估计,这样我们就可以决定何时停止训练。当我们认为系统的表现足够好时,我们会拿出测试集,并仅使用一次,以获得系统准确性的可靠估计。

这为我们提供了一种方便的方式,可以反复尝试不同的超参数,然后根据它们在验证集上的表现选择最佳的参数。

这种尝试不同超参数集的方法是基于运行一个循环的。现在让我们看看这个循环的简化版本。

为了运行我们的循环,我们选择一组超参数,训练系统,然后用验证集评估其性能。这可以估计用这些超参数训练的系统在预测新数据时的表现。接下来,我们将该系统搁置一旁,创建一个新的系统,像往常一样初始化为随机值。我们应用下一组超参数,进行训练,并使用验证集评估这个系统的表现。我们一次次地重复这个过程,每次使用不同的超参数集。当所有超参数集都运行完毕后,我们选择那个表现最准确的系统,通过它运行测试集,并发现它的预测究竟有多准确。

图 8-7 以图形方式展示了整个过程。

f08007

图 8-7:当我们尝试不同的超参数集时,我们使用验证集。请注意,我们仍然保留一个单独的测试集,只有在部署前才使用它。

当循环结束时,我们可能会倾向于使用验证数据的结果作为系统的最终评估。毕竟,分类器并没有从那些数据中学习,因为它只是用来测试的。看起来我们可以省去制作一个单独的测试集的麻烦,然后将它运行在系统上,以获得性能估计。

但那样做相当于在使用泄漏数据,这会扭曲我们的结论。数据泄漏的来源有些狡猾和微妙,就像许多数据污染问题一样。问题在于,尽管分类器没有从验证数据中学习,我们的整个训练和评估系统却学到了,因为它用了这些数据来选择最佳的超参数。换句话说,即使分类器没有明确从验证数据中学习,这些数据也影响了我们选择分类器的过程。我们选择了在验证数据上表现最好的分类器,因此我们已经知道它会在验证数据上表现得很好。换句话说,我们对分类器在验证数据上表现的了解“泄漏”到了我们的选择过程中。

如果这看起来有点微妙或棘手,那是因为确实如此。这类问题很容易被忽视或漏掉,这也是为什么我们必须警惕数据污染。否则,我们就有可能错误地认为系统比实际情况更好,从而部署一个不适合预期用途的系统。要想准确估计我们的系统在从未见过的新数据上的表现,没有捷径可走:我们需要在从未见过的新数据上进行测试。这就是为什么我们总是将测试集留到最后使用的原因。

交叉验证

在上一节中,我们将几乎一半的训练数据预留出来用于验证和测试。这对于数据量非常大的时候是没问题的。但如果我们的样本集很小,而且我们无法获得更多数据呢?也许我们在处理的是新地*线号探测器 2015 年飞掠冥王星时拍摄的冥王星及其卫星的照片,我们希望构建一个分类器,可以安装在未来的航天器上,以识别它们所观察的地形。我们的数据集是有限的,并且不会变大:*期不会有新的冥王星*距离照片出现。我们所拥有的每一张图片都是宝贵的,我们希望从每张照片中学到尽可能多的东西。如果将一些图片预留出来,仅仅为了确定我们的分类器效果,那将是一个巨大的代价。

如果我们愿意接受系统性能的估计,而不是可靠的衡量标准,那么我们就不必专门预留一个测试集。我们实际上可以在每一份输入数据上进行训练,并仍然预测我们在新数据上的表现。问题是,我们只会得到系统准确性的估计,因此它不会像使用真实测试集那样可靠,但当样本非常珍贵时,这种权衡可能是值得的。

完成这个任务的技术叫做交叉验证旋转验证。有多种类型的交叉验证算法,但它们都有相同的基本结构(Schneider 1997)。我们将查看一种不需要我们创建专门测试集的版本。

核心思想是我们可以运行一个循环,反复从头训练相同的系统并进行测试。每次循环时,我们将整个输入数据分成一次性的训练集和一次性的验证集。关键在于每次循环时,我们都会以不同的方式构建这些数据集。这使我们能够使用所有的数据进行训练(尽管,正如我们将看到的,并不是同时使用所有数据)。

我们首先构建一个全新的分类器实例。我们将输入数据分成一个临时的训练集和临时的验证集。我们在临时训练集上训练系统,并使用临时测试集进行评估。这将给我们一个分类器性能的得分。接下来,我们再次进行循环,但这一次,我们将训练数据分成不同的临时训练集和测试集。当我们完成每次循环的迭代后,所有得分的*均值将作为我们对分类器整体性能的估计。

交叉验证的视觉摘要显示在图 8-8 中。

f08008

图 8-8:使用交叉验证来评估我们系统的性能

通过使用交叉验证,我们可以用所有的训练数据来训练(尽管每次循环中并不是所有数据都会参与训练),同时我们依然能够从留出的测试集获得系统质量的客观测量。这个算法没有数据泄漏的问题,因为每次通过循环时,我们都会创建一个新的分类器,而该分类器的临时测试集包含的是与该特定分类器全新且未见过的数据,因此可以公*地使用它来评估该分类器的性能。这种技术的缺点是,我们对系统准确性的最终估计不像从留出的测试集中得到的估计那么可靠。

有多种不同的算法可以用来构建临时的训练集和验证集。让我们来看一下一个流行的方法。

k-折交叉验证

也许最流行的构建交叉验证临时数据集的方法叫做k-fold 交叉验证。这里的字母k不是某个单词的首字母,而是表示一个整数(例如,我们可能会运行“2-fold 交叉验证”或“5-fold 交叉验证”)。通常,k的值是我们希望在图 8-8 中循环的次数。

该算法在交叉验证循环开始之前就已经启动。我们将训练数据分割成一系列等大小的组。每个样本仅被放入一个组中,所有组的大小相同(当然,如果不能将输入数据分成完全相等的部分,我们允许最后一个组稍微小一点)。

如果这些组分别被称为“组”或“等大小的块”,那会很好,但描述这个概念的词是fold。这个词在这里的使用是为了表示页面上折痕(或末端)之间的部分。为了更好地理解这个概念,可以想象将训练集中的所有样本写在一张长纸上,然后将其折叠成固定数量的等份。每次我们折叠纸张时,就形成一个折痕,而折痕之间的部分称为 fold。图 8-9 展示了这个概念。

f08009

图 8-9:为k-fold 交叉验证创建 folds。这里有四个折痕和五个 fold。

让我们从训练数据中构建等大小的 fold。我们可以将图 8-9 展开,创建出图 8-10 中更典型的五个 fold 的图像。

f08010

图 8-10:将我们的训练集拆分为五个大小相等的 fold,分别命名为 Fold 1 到 Fold 5

让我们使用这五个 fold 来看看循环是如何进行的。在第一次循环中,我们将 fold 2 到 fold 5 中的样本作为临时训练集,而将 fold 1 中的样本作为临时测试集。也就是说,我们使用 fold 2 到 fold 5 中的样本来训练分类器,然后用 fold 1 中的样本来评估它。

下一次循环时,从一个初始化为随机数的新分类器开始,我们使用第 1、3、4 和 5 折的样本作为临时训练集,使用第 2 折的样本作为临时测试集。我们照常用这两组数据进行训练和测试,并继续使用其余的折叠。图 8-11 直观地展示了这一过程。

f08011

图 8-11:在每次循环中,我们选择一个折叠进行测试(蓝色),其余的折叠(红色)用于训练。如果我们循环超过五次,我们将重复这一模式。

我们可以选择重复循环任意多次,只需重复折叠选择的周期(或者混合数据,使得每次集合的内容不同)。

在一个可选的最终步骤中,我们可以使用所有数据来训练一个新的分类器。这意味着我们无法得到其性能的估计。但如果我们仔细观察训练过程,并留意过拟合(将在下一章讨论),我们通常可以假设,使用所有数据训练的系统至少和我们从交叉验证中得到的最差表现一样好(并且我们希望它会稍微好一点)。

这使得交叉验证在数据有限时成为一个很好的选择。我们确实需要重复多次训练-测试周期,并且我们的最终性能度量只是一个估计,这两者都是缺点,但我们可以利用所有数据进行训练,最大化地提取输入集中的每一点信息,并将其用来提高分类器的表现。

我们讨论了带分类器的k折交叉验证,但该算法可以广泛应用于几乎任何类型的学习器。

总结

本章主要讲解了训练深度学习系统,并判断它是否足够好以进行部署。我们聚焦于分类器,但这种思路对任何此类系统都适用。我们看到我们将数据分为两部分:训练集和测试集。我们了解了过拟合和数据泄露的问题,并且还看到了如何使用验证集来大致了解系统在每个训练周期后的学习效果。最后,我们介绍了交叉验证,这是一种通常用于小数据集的技术,用于估计系统的性能。

在下一章,我们将更详细地探讨过拟合和欠拟合问题。

第九章:过拟合与欠拟合

无论是人还是计算机,从有限的例子中学习一个主题的通用规则都是一个艰难的挑战。如果我们不够重视例子中的细节,我们的规则将过于笼统,以至于在处理新数据时用处不大。另一方面,如果我们过于关注例子中的细节,我们的规则就会过于具体,同样在评估新数据时效果不好。

这些现象分别被称为欠拟合过拟合。其中更为常见且令人头疼的问题是过拟合,如果不加以控制,它可能会导致我们得到一个几乎无用的系统。我们通过被统称为正则化的技术来控制过拟合并加以遏制。

本章我们将探讨过拟合和欠拟合的原因,以及如何应对这些问题。最后,我们将通过使用贝叶斯方法将一条直线拟合到一组数据点上来总结本章内容。

寻找合适的匹配

当我们的系统从训练数据中学习得非常好,但在面对新数据时表现不佳时,我们称之为过拟合。当它没有从训练数据中学得足够好,在面对新数据时表现糟糕时,我们称之为欠拟合。由于过拟合通常是一个更难解决的问题,我们将首先讨论它。

过拟合

让我们通过一个比喻来讨论过拟合。假设我们被邀请参加一个大型的户外婚礼,而我们几乎不认识任何人。在整个下午的时间里,我们在聚会的客人中游走,进行介绍和寒暄。我们决定努力记住每个人的名字,因此每次遇到某人时,我们都会在他们的外貌和名字之间建立某种心理联想(Foer 2012;Proctor 1978)。我们遇到的其中一位叫沃尔特,他有着一副大海象胡须。我们将沃尔特想象成一只海象,并试图让这个形象在脑海中留下深刻印象。之后,我们遇到一位叫艾琳的人,我们注意到她戴着美丽的绿松石耳环。我们将她的耳环想象成一个在某个方向上被缩短的形状,所以耳环变成了艾琳。我们对每一个遇到的人都做类似的心理联想,当我们再次与一些同样的人相遇时,我们轻松地记住了他们的名字。系统运作得非常好。

那天晚上在接待会上,我们遇到了很多新的人。某个时刻,我们碰到了一位留着大海象胡子的男士。我们笑着说:“又见面了,沃尔特!”结果他露出了困惑的表情。原来这是鲍勃,我们以前从未见过他。类似的事情可能反复发生。我们可能被介绍给一个佩戴美丽耳环的人,但她是苏珊,而不是艾琳。问题在于我们的心理图像误导了我们。并不是我们没有正确地记住这些人的名字,因为我们记住了。只是我们以一种仅适用于原始小组中的人的方式来记住名字,当我们遇到更多的人时,无法做到泛化。

要将某人的外貌与他们的名字联系起来,我们需要在这两个概念之间建立某种联系。这个联系越牢固,我们就越能在新的环境中识别这个人,即使他们戴了帽子、眼镜或其他改变外貌的东西。以我们的婚礼为例,我们通过将人们的名字与某个独特的特征相联系来记住他们的名字。问题是,当我们在接待会上遇到另一个拥有相同特征的人时,我们无法判断他是新认识的人。

在婚前派对上,我们认为自己表现得很好,因为当我们用训练数据(婚礼上人的名字)评估自己的表现时,大部分结果都正确。如果我们关注成功的次数,我们会说自己达到了很高的训练准确度。但如果我们转而关注失败的次数,我们会说自己有较低的训练误差(或训练损失)。但是当我们去了接待会并需要评估新数据(我们遇到的其他人的名字)时,我们的泛化准确度很低,或者换句话说,我们的泛化误差(或泛化损失)很高。

我们在第八章中看到了同样问题的一个例子,当时我们错误地将沙发上的一只哈士奇识别为约克夏犬,因为我们仅使用沙发作为识别它品种的唯一线索。

我们在人和狗身上的错误都源于过拟合。换句话说,我们学会了如何分类眼前的数据,但我们只是使用了数据中的具体细节,而不是学习能够适用于新数据的一般规则。

机器学习系统非常擅长过拟合。有时我们会说它们擅长作弊。如果输入数据中有某种特征可以帮助系统得到正确的结果,它就会找到并利用这个特征,就像我们在第八章中的故事,系统本应解决在树木照片中找到伪装坦克的难题,但很可能选择了简单的方式,通过注意天空是晴天还是阴天来解决问题。

我们可以采取两种措施来控制过拟合。首先,我们可以在规则过于具体时停止学习过程。其次,通过使用正则化方法,我们可以通过鼓励系统尽可能长时间地学习一般规则来推迟过拟合的发生。稍后我们将分别讨论这两种方法。

欠拟合

过拟合的对立面是欠拟合。与过拟合不同,过拟合是由于使用了过于精确的规则,欠拟合描述的是当我们的规则过于模糊或通用时的情况。在婚礼聚会上,我们可能会通过创建一个规则来欠拟合:“穿裤子的人叫沃尔特”。虽然这对于某一特定数据是准确的,但这个规则并不能很好地推广!

与过拟合相比,欠拟合在实际中通常不是一个大问题。我们通常可以通过使用更多的训练数据来解决欠拟合问题。通过更多的示例,系统可以为每一条数据找到更好的理解规则。

检测和解决过拟合

我们怎么知道何时开始过拟合数据呢?假设我们使用验证集在每个训练周期后估计系统的泛化误差(像往常一样,当我们完成训练时,我们使用一次性测试集来获得更可靠的泛化误差)。系统对验证数据产生的误差称为验证误差。它是系统部署时将产生的误差的估计,这称为泛化误差。当验证误差趋于*稳或开始变得更糟,而训练误差在改善时,我们就出现了过拟合。这是我们停止学习的信号。图 9-1 形象地展示了这一点。

F09001

图 9-1:训练误差和验证误差在训练初期都稳步下降,但在某个节点之后,验证误差开始上升,而训练误差继续下降,标志着过拟合的发生。

在图 9-1 中,请注意,随着我们进入过拟合区域,训练误差继续下降,而验证误差开始上升。这是因为我们仍然在从训练数据中学习,但现在我们学到的是特定于该数据的信息,而不是一般规则。正是验证集上的表现让我们看到了这一点,因为我们的验证误差(估算我们的泛化误差)在变得更糟。我们这样训练的时间越长,系统在部署时的表现就会越差。

让我们看看这个实际应用。假设某家商店的店主订阅了一项背景音乐服务。该公司提供了多种不同节奏的音乐流,并且提供了一个控制器,允许店主随时选择音乐的节奏。她并非每天早上设定好节奏后就不再理会,而是发现自己在一天中频繁调整节奏,这变成了一项令人分心的琐事。于是,她聘请了我们为她建立一个系统,自动根据她的需求调整一天中的音乐。

第一步是收集数据。第二天早上,我们坐在控制器前观察。每次她调整节奏时,我们记录下时间和新的设置。我们收集的数据如图 9-2 所示。

F09002

图 9-2:我们记录的数据展示了店主在一天中每次调整节奏时所选择的节奏。

那天晚上,我们回到实验室,将曲线拟合到数据中,如图 9-3 所示。

F09003

图 9-3:一条适合图 9-2 数据的曲线

这条曲线非常弯曲,但我们可能会推测它是一个好的解决方案,因为它很好地匹配了她记录的选择。第二天早上,我们编程让系统按照这个模式进行。到下午中时,店主抱怨音乐的节奏变化得太频繁且太剧烈,分散了顾客的注意力。

这条曲线过度拟合了数据,因为它过于精确地匹配了观察到的数值。我们测量数据的那天,她的选择是基于当天播放的特定歌曲。由于服务并不是每天在同一时间播放相同的歌曲,我们不希望如此紧密地重现那一天的观测数据。通过适应每一个波动和曲线,我们过分关注了训练数据中的个性化特征。

如果我们能再观察她的选择几天,并利用所有这些数据制定一个更通用的计划,那就太好了,但她不希望我们再次占用她店里的空间。我们拥有的数据就是我们能得到的全部。我们希望制定一个变化较小的计划,所以第二天晚上我们减少了与数据匹配的精确度。我们的目标是做出一个不像之前那样波动太大的计划,得到如图 9-4 所示的*滑曲线。

F09004

图 9-4:用于匹配我们节奏数据的*滑曲线

我们第二天发现,客户仍然不满意,因为这条曲线太粗糙,忽略了重要的特征,比如她早上希望使用较慢的节奏,下午则希望使用更有活力的歌曲。这条曲线未能很好地拟合数据。

我们想要的是一个不试图精确匹配所有数据,而是能够很好地把握总体趋势的解决方案。我们希望这个解决方案既不会过于精确,也不会过于松散,而是“恰到好处”。第二天,我们根据图 9-5 中的曲线设置了系统。

F09005

图 9-5:一条足够匹配我们的节奏数据的曲线,但又不至于过度匹配

我们的客户对这条曲线和它选择的歌曲节奏感到满意。我们在欠拟合和过拟合之间找到了一个很好的折衷。在这个例子中,找到最佳曲线是个人品味的问题,但稍后我们将看到一些算法方法,帮助我们找到欠拟合和过拟合之间的最佳*衡点。

图 9-6 展示了另一个过拟合的例子,这次是在对二维(2D)点进行分类时出现的。

F09006

图 9-6:一个可能的过拟合情况。(灵感来源于 Bullinaria 2015。)

在图 9-6 的左侧,我们看到一个圆形点位于正方形区域的深处,导致了一个复杂的边界曲线。我们称这种孤立的点为异常点,并且自然会对它保持怀疑态度。也许这是由于测量或记录错误造成的,或者这只是一个非常不寻常但完全有效的数据点。获取更多的数据可以帮助我们更好地理解这种异常现象的情况,但如果我们只有这一组数据,我们需要决定如何处理它。通过绘制边界来适应这个数据点,我们有可能错误地将一些未来的数据点归类为蓝色圆形,尽管它们实际上位于棕色正方形区域内部,因为它们落在了这个奇怪的边界曲线的蓝色一侧。也许更好的做法是选择图 9-6 右侧的简单曲线,并接受这个点为一个错误。

现在我们已经看到了过拟合的样子,让我们看看如何防止它的发生。

早期停止

一般来说,当我们开始训练模型时,通常是欠拟合的。模型还没有看到足够的例子来弄清楚如何正确处理它们,因此它的规则是广泛和模糊的。

随着训练的进行,模型不断调整边界,训练误差和验证误差通常都会下降。为了方便讨论,我们在这里重复图 9-1,作为图 9-7。

F09007

图 9-7:为了方便起见,重复图 9-1

在某个时刻,我们会发现,虽然训练误差持续下降,但验证误差开始上升(可能首先会保持*稳一段时间)。现在我们进入了过拟合阶段。训练误差在下降,因为我们越来越多地正确地处理了细节。但我们现在过度调整了结果,以适应训练数据,导致泛化误差(或其估计值——验证误差)上升。

从这个分析中,我们可以得出一个好的指导原则:当我们开始过拟合时,停止训练。也就是说,当我们在图 9-7 中达到大约 28 个 epoch,并且发现验证误差开始上升,而训练误差仍在下降时,我们应该停止训练。这种在验证误差开始上升时结束训练的技巧叫做提前停止,因为我们在训练误差尚未降到零时就停止了训练过程。把这个想法称为最后时刻停止可能更有帮助,因为我们尽可能地训练,直到找到没有过拟合的最佳数据表示,然后才停止训练。

实际上,我们的误差测量通常不像图 9-7 中的理想曲线那样*滑。它们往往会有噪声,甚至可能在短时间内出现“错误”的变化方向,因此很难找到完全正确的停止时机。大多数用于提前停止的库函数都提供了几个变量,允许我们让它们隐式地*滑这些误差曲线,以便在验证误差真正上升时能检测到,而不是仅仅经历短暂的增加。

正则化

我们总是希望尽可能从训练数据中提取信息,直到接*过拟合为止。提前停止在验证误差开始上升时结束学习,但如果有办法延迟这一现象,让我们可以训练更久,并继续降低训练误差和验证误差呢?

类比来说,考虑在烤箱里烤火鸡。如果我们只是将火鸡放在一个锅里,并用高温烹饪,它的外部最终会烧焦。但假设我们想要把火鸡烤得更久,而又不想让它烧焦。一个方法是将其包裹在铝箔中。铝箔延缓了烧焦的发生,使我们可以烤得更久。

延迟过拟合发生的技术统称为正则化方法,或简称正则化。请记住,计算机并不知道它正在过拟合。当我们要求它从训练数据中学习时,它会尽可能地从数据中学习。它并不知道何时从“对输入数据的良好理解”跨越到“对这个特定输入数据的过度特定理解”,所以管理这个问题完全由我们来处理。

一种常见的正则化方法,或者说延迟过拟合开始的方法,是限制分类器所使用的参数的值。从概念上讲,为什么这种方法能够延迟过拟合的核心理由是,通过将所有参数的值保持在较小的数字范围内,我们防止了任何一个参数占主导地位(Domke 2008)。这使得分类器不容易依赖于特殊的、狭窄的特征。

为了理解这一点,回想一下我们记住人名的例子。当我们记住了沃尔特这个名字时,他有一副海象胡子,这个信息主导了我们记住的所有其他信息。我们还可以从他的外貌中学到其他的事实,比如他是个男性,身高接*六英尺,长着灰色的长发,有着大大的微笑和低沉的声音,穿着一件深红色的衬衫,扣子是棕色的,等等。但相反,我们专注于他的胡子,忽略了其他所有有用的线索。后来,当我们看到另一个拥有海象胡子的完全不同的人时,那一特征主导了其他所有特征,我们把那个人误认为是沃尔特。

如果我们强制要求我们注意到的所有特征都在大致相同的范围内,那么“有海象胡子”就不会有机会主导,其他特征在我们记住新人的名字时仍然会发挥作用。正则化技术确保没有任何一个参数,或者一小组参数,能够主导其他所有参数。

请注意,我们并不是试图将所有参数设置为相同的值,这样会使它们变得毫无用处。我们只是想确保它们都在大致相同的范围内。将参数推到较小的值可以让我们学习更长时间,并在发生过拟合之前从训练数据中提取更多信息。

应用正则化的最佳量因学习器和数据集的不同而有所变化,因此我们通常需要尝试几个值,看看哪一个最有效。我们通过一个超参数来指定应用的正则化量,传统上这个超参数写作小写希腊字母λ(lambda),不过有时也使用其他字母。通常来说,较大的λ值意味着更多的正则化。

保持参数值较小通常意味着分类器的边界曲线不会像它本来可能那样复杂和波动。我们可以使用正则化参数λ来选择我们希望边界有多复杂。较高的值给我们*滑的边界,而较低的值让边界更加精确地拟合它所看的数据。

在后面的章节中,我们将处理具有多层处理结构的学习架构。这些系统可以使用额外的、专门的正则化技术,如dropoutbatchnormlayer normweight regularization,这些技术有助于控制这些类型架构的过拟合。所有这些方法的设计目的是防止网络的任何元素主导最终结果。

偏差与方差

统计术语偏差方差与欠拟合和过拟合密切相关,通常在讨论这些话题时会涉及到。我们可以说,偏差衡量的是一个系统持续学习错误事物的倾向,而方差衡量的是它学习无关细节的倾向(Domingos 2015)。另一种理解方式是,大量的偏差意味着系统偏向于某种特定的结果,而大量的方差意味着系统返回的答案过于针对特定数据。

我们将通过讨论二维曲线的方式来图解这两个概念。这些曲线可能是回归问题的解,就像我们之前的任务一样:为商店的背景音乐设定节奏,随时间变化。或者这些曲线可能是*面上两个区域之间的边界曲线,如分类问题中的情况。偏差和方差的概念并不限于任何特定类型的算法或二维数据。但我们将坚持使用二维曲线,因为我们可以画出并解读它们。让我们专注于找到一个合适的拟合曲线,来描述如何通过偏差和方差的概念来说明我们的算法行为。

匹配基础数据

假设我们的一个大气研究员朋友来向我们寻求帮助。她在几个月内每天同一时间,在一座山顶的某个位置测量了风速。她测量的数据见图 9-8。

F09008

图 9-8:大气科学家测量的风速随时间变化的数据。在这些数据中,有一条明显的基础曲线,但也有很多噪音(基础曲线灵感来自 Macskassy 2008)。

她认为她所测得的数据是一个理想化曲线噪音的总和,理想化曲线每年都保持不变,而噪音则解释了日常波动的不可预测性。她测得的数据被称为噪声曲线,因为它是理想化曲线和噪音的叠加。图 9-9 展示了理想化曲线和噪音,二者叠加在一起形成图 9-8 中的数据。

F09009

图 9-9:将图 9-8 中的数据分成两部分。左侧:我们要寻找的基础“理想化”曲线。右侧:大自然添加到理想化曲线上的噪音,导致我们得到噪声的、测量的数据。请注意,这两个图的垂直尺度不同。

我们的大气研究员认为她有一个很好的模型来描述噪声(也许它遵循我们在第二章中看到的均匀分布或高斯分布)。但是她对噪声的描述是统计性的,因此她不能用它来修正她的日常测量。换句话说,如果她知道图 9-9 右侧噪声的确切值,她可以将其从图 9-8 中的测量值中减去,从而获得她的目标,即图 9-9 左侧的干净曲线。但她不知道这些噪声值。她有一个统计模型,可以生成很多像图 9-9 右侧那样的曲线,但她没有与她的数据对应的具体值。

这里有一种清理噪声数据的方法。我们可以回到图 9-8 中的噪声数据,并尝试将一条*滑曲线拟合到它上面(Bishop 2006)。通过选择曲线的复杂性,使其足够曲折以跟随数据,但又不至于曲折到精确匹配每个点,我们希望能得到一个相当好的匹配,反映曲线的大致形状,这是找到潜在*滑曲线的一个良好起点。

有很多方法可以将*滑曲线拟合到噪声数据上。图 9-10 展示了其中一条这样的曲线。右端的小波动是我们使用的曲线的典型特征,它在数据集的边缘附*往往会有一些波动。

看起来还不错。但我们能做得更好吗?

让我们将偏差和方差的概念应用于寻找理想曲线的问题。这个想法源于我们在第二章讨论的自助法(bootstrapping)方法,但我们实际上不会使用自助法技巧。

F09010

图 9-10:使用曲线拟合算法拟合我们的噪声数据

让我们制作原始噪声数据的 50 个版本,但每个版本仅包含 30 个随机选取的点,且不重复选择。这些缩小的数据集的前五个版本显示在图 9-11 中。

F09011

图 9-11:我们原始噪声数据的 50 个小版本中的五个。每个版本由从原始数据中随机选择的 30 个样本组成,且不重复选择。这些是前五个版本,所选点显示为绿色点,原始的噪声数据为灰色,以供参考。

让我们尝试用简单的曲线和复杂的曲线来匹配这些数据点集,并根据偏差和方差来比较结果。

高偏差,低方差

我们首先用简单的*滑曲线来拟合数据。由于我们提前选择了这些特征,我们预计所有的拟合曲线看起来都会差不多。拟合我们在图 9-11 中五个数据集的曲线如图 9-12 所示。

F09012

图 9-12:将简单曲线拟合到我们在图 9-11 中的前五个数据点集

如预期,这些曲线都非常简单且相似。由于这些曲线彼此非常相似,我们说这组曲线表现出高偏差。这里的偏差指的是对简单形状的预定偏好。因为这些曲线非常简单,所以每条曲线都缺乏通过多个点的灵活性,最多只能经过其中的一些点。

方差指的是曲线之间的差异或变化程度。为了查看这些高偏差曲线的方差,我们可以将所有 50 条曲线叠加在一起,如图 9-13 所示。

F09013

图 9-13:所有 50 个 30 点样本的曲线,叠加在一起

如预期,曲线非常相似。我们说它们展示了低方差。

总结来说,这组曲线具有高偏差,因为它们的形状差不多,而且具有低方差,因为个别曲线不受数据的太多影响。

低偏差,高方差

现在,让我们尝试减少曲线需要简单的约束条件。这使我们能够拟合复杂的曲线,使每条曲线更接*其绿色点。图 9-14 展示了这些曲线应用于我们前五组数据的情况。与图 9-12 相比,这些曲线更加弯曲,拥有多个山峰和谷底。尽管它们仍然不会直接经过太多点,但它们比之前更接*这些点。

F09014

图 9-14:我们从噪声数据的前五组点生成的复杂曲线

由于这些曲线的形状更复杂且更灵活,它们比任何初始假设更受数据的影响。因为我们对曲线形状施加的约束更少,所以我们说这组曲线具有低偏差。另一方面,这些曲线彼此差异很大。我们可以通过将所有 50 条曲线叠加在一起来看出这一点,如图 9-15 所示。由于曲线在开始和结束时剧烈偏离,因此我们还展示了覆盖这些大幅波动的扩展垂直坐标轴。

F09015

图 9-15:拟合到我们 50 组数据的复杂曲线。右侧的图显示了曲线的整个垂直尺度。

这些曲线并不完全遵循相同的形状,因此它们具有低偏差。此外,它们彼此之间差异很大,每条曲线都受到其数据的强烈影响,因此这组曲线具有高方差

比较曲线

让我们回顾一下迄今为止的曲线拟合实验。

我们的气象学家要求我们提供一条符合她数据中理想化曲线的曲线。我们从她原始的噪声数据中随机提取了 50 个小点集。当我们为这些点集拟合简单光滑的曲线时,曲线始终未能准确匹配大多数数据点。这组曲线具有高偏差,或者说是对特定结果(光滑且简单)的偏向。曲线受它们要匹配的数据影响较小,因此这组曲线具有低方差。

另一方面,当我们为这些点集拟合复杂且波动的曲线时,曲线能够适应数据,并且与大多数点更接*。由于它们更受数据的影响,而非对特定形状的偏向,这组曲线具有低偏差。但曲线的适应性意味着它们彼此之间差异很大。换句话说,这组曲线具有高方差。

所以,第一个集合具有高偏差和低方差,而第二个集合具有低偏差和高方差。

理想情况下,我们希望得到具有低偏差(这样我们不会对其可能的形状加上先入为主的想法)和低方差(这样我们不同的曲线都能大致匹配原始噪声数据)的曲线。不幸的是,在大多数实际情况中,随着其中一个指标的下降,另一个指标会上升。这意味着我们需要找到每种特定情况的最佳偏差-方差权衡。我们稍后会回到这个问题。

请注意,偏差和方差是一系列曲线或曲线集合的属性。讨论单一曲线的偏差和方差是没有意义的。偏差和方差在机器学习讨论中常常用来描述模型或算法的复杂性或能力。

现在我们可以看到,偏差和方差如何帮助我们描述欠拟合和过拟合。在训练开始时,系统试图找到表示训练数据的正确方式,它会生成一般规则,或者说是欠拟合。如果这些规则是数据类之间的边界,它们呈曲线形状。如果我们在多个相似但不同的数据集上进行训练,我们会看到形状简单且相似的曲线。也就是说,它们具有高偏差和低方差。

在训练的后期,每个数据集的曲线变得更加复杂。它们的形状没有太多的前提条件,因此具有较低的偏差,并且可以与训练数据紧密匹配,因此具有较高的方差。当我们让系统训练得过长时,高方差的曲线开始过度跟随输入数据,导致过拟合。

图 9-16 展示了偏差和方差的权衡关系图。

F09016

图 9-16:最上排:我们希望匹配的四条曲线。中排:使用高偏差和低方差的曲线。底排:使用低偏差和高方差的曲线。底部两行最右侧的图像显示了四条曲线的叠加。

在中排,高偏差给我们带来简单的曲线(可以避免过拟合),但它们的低方差意味着它们不能很好地匹配数据。在底排,低偏差让曲线更好地匹配数据,但它们的高方差意味着曲线可能过度匹配(从而有过拟合的风险)。

一般来说,偏差和方差没有谁天生优于谁,因此我们不应该总是试图找到例如最低偏差或方差的解决方案。在某些应用中,高偏差或高方差可能是可以接受的。例如,如果我们知道我们的训练集完全代表了所有未来的数据,那么我们不在乎方差,而是追求最低的偏差,因为完美匹配训练集正是我们所需要的。另一方面,如果我们知道我们的训练集并不能很好地代表未来的数据(但这是我们目前最好的选择),我们可能不关心偏差,因为匹配这个糟糕的数据集并不重要,但我们希望得到最低的方差,以便在未来数据上至少能做出一些合理的预测。

一般来说,我们需要在这两个度量之间找到合适的*衡,使其最好地服务于特定项目的目标,考虑到我们正在使用的具体算法和数据。

使用贝叶斯法则拟合直线

偏差和方差是描述一组曲线如何拟合数据的有用方式。回顾我们在第四章讨论的频率主义和贝叶斯哲学,我们可以说偏差和方差本质上是频率主义的概念。这是因为偏差和方差的概念依赖于从数据源中提取多个值。我们不依赖于任何单一曲线,而是通过对所有曲线进行*均,来找到每条曲线所逼*的“真实”答案。这些观点非常适合频率主义方法。

相比之下,贝叶斯方法拟合数据的方式主张,结果只能用概率的方式来描述。我们列出所有我们认为可能的匹配数据的方式,并为每种方式附加一个概率。随着我们收集更多的数据,我们逐渐淘汰一些描述,从而使剩余的描述变得更加可能,但我们永远无法得到一个唯一的、绝对的答案。

让我们在实际操作中看看这个方法。我们的讨论基于《模式识别与机器学习》(Bishop 2006)中的一个可视化图示。我们将使用贝叶斯定理来找到对我们在图 9-8 中看到的带噪声大气数据的良好*似。我们并不打算为数据拟合一条复杂的曲线,而是限制自己使用直线。这仅仅是因为这样做可以让我们用二维图形和图表展示所有内容。事实上,我们坚持使用大部分水*的直线。再说一次,这只是为了方便绘制不需要高维度的漂亮图示。

使用贝叶斯定理进行曲线拟合的方法可以使用复杂的曲线、空间中的*面,甚至具有数百个维度的形状。我们仅使用大部分水*的直线,只因为这种选择让图示保持简单。

我们将同时处理多行,因此找到一种紧凑的方式来表示不同组的线条而不需要绘制它们所有的内容会非常有帮助。

诀窍是用两个数字来描述每条直线。第一个数字告诉我们直线与水*线的倾斜程度,这样可以得到任何方向的直线。第二个数字告诉我们该如何上下移动直线。

第一个数字是斜率。水*线的斜率为 0。当直线顺时针旋转时,如图 9-17 所示,斜率增加。当直线逆时针旋转时,斜率减少。

当直线完全对角时,斜率为 1 或–1。当它旋转到更陡峭的角度时,斜率迅速增加,直到对于完全垂直的直线达到无穷大。我们可以采取措施来避免这个问题,但这只会让讨论变得更复杂。因此,为了简化起见,我们将只关注斜率在–2 到 2 之间的直线,这些直线位于图 9-17 中的绿色区域。

f09017

图 9-17:水*线的斜率为 0。当直线逆时针旋转时,斜率增加。当直线顺时针旋转时,斜率减少。我们只会使用斜率位于浅绿色区域的直线。

描述一条直线的第二个数字是Y 轴截距。它只是将整条直线上下*移。这个数字告诉我们当 X 为零时直线的值。换句话说,它是直线与 Y 轴交点的值。图 9-18 展示了这一概念。为了简化起见,我们将重点关注 Y 截距在[–2, 2]范围内的直线,这些直线在图 9-18 中被绿色标出。

f09018

图 9-18:Y 截距告诉我们直线与 Y 轴交点时的 Y 值,无论其斜率如何。我们将只使用 Y 截距在–2 到 2 之间的直线。

给定任意一条直线,我们可以测量其方向以得到斜率的值,并观察它与 Y 轴的交点以得到 Y 截距的值。这就是描述直线所需的所有信息。我们可以将其显示为一个新二维网格中的点,其中坐标轴标记为斜率Y 截距。我们将此称为SI图,表示斜率-截距。普通的图将是一个 XY 图。我们还可以说,SI 图绘制的是SI 空间中的直线,而 XY 图则显示的是XY 空间(也叫笛卡尔空间)中的直线。

图 9-19 显示了 XY 和 SI 图中的几条线。

数学家称这两种看待同一事物的方式为对偶表示,这样的事物还有很多可以讨论的地方。我们只会讨论与使用贝叶斯定理拟合直线相关的内容。

f09019

图 9-19:左:XY 空间中的三条直线。右:每条直线作为一个点绘制在 SI 空间中。

当我们在 SI 空间中将一组点沿一条直线排列时,发生了一些有趣的事情。当我们在 XY 空间中绘制它们的对应直线时,它们都会在同一个 XY 点交汇。图 9-20 展示了这一现象。这在任何时候,如果我们的 SI 点位于一条直线上时,都是成立的。

f09020

图 9-20:在 SI 空间中沿直线放置数据点的两个示例。它们对应的直线将在 XY 空间中的单一点交汇。

当我们寻找最适合数据的最佳直线时,我们知道它可能无法通过所有数据点。但我们希望它尽可能接*。因此,我们不在 SI 空间中放置数据点,而是给每一条可能的直线分配一个从 0 到 1 的概率,表示它成为我们所需直线的可能性。图 9-21 展示了这个概念(在图 9-21 及随后的图中,我们根据需要放大概率值,以便更容易阅读)。

f09021

图 9-21:左:XY 空间中的一个点。中:SI 图中的每个点都分配一个从 0 到 1 的概率(从蓝到紫),告诉我们该直线与点的接*程度。我们展示了一些代表性的直线,使用黑点表示。右:中间图中的黑点作为它们在 XY 空间中的直线绘制。注意,它们都通过我们原始的红点,或者接*它。

现在让我们回到我们要解决的问题:为一个带噪声的数据集找到最佳的直线*似。我们从 SI 空间中的一个任意、宽泛的高斯凸起作为先验开始,如图 9-22 左上角所示。这意味着任何一条直线都可能是我们的答案,但处于亮紫色区域的直线最为可能。我们根据这些点的概率选择了几个点,并将它们绘制在右上角。我们得到了很多非常不同的直线,这确认了我们在选择直线时非常模糊的先验。

f09022

图 9-22:左上:在 SI 空间中的先验,以及从该概率分布中选择的一些点。右上:这些点在 XY 空间中绘制为直线。第二行和第三行:与第一行类似,但先验变得更小。

我们可以在图 9-22 中看到,随着先验变得更小,我们得到的直线选择变得更加精细。因此,我们希望贝叶斯法则能够跟随这种变化,给我们一个较小的后验(或先验),从而得到一小组能够拟合我们数据的直线。

现在我们准备好使用贝叶斯法则来匹配我们的数据了!图 9-23 展示了这一过程。

f09023

图 9-23:使用贝叶斯法则拟合我们的数据的直线(图形灵感来自 Bishop 2006)

让我们逐行查看图 9-23 中发生的情况。在第 1 行中,我们展示了我们的先验,或者说是我们对将拟合我们数据的直线分布的初步猜测。我们任意选择了一个中心在中间的高斯分布。这个先验意味着我们猜测我们的数据最可能被一条水*的直线拟合,其 Y 截距为 0,即它就是 X 轴本身。但高斯分布延伸到边缘(它在图中任何地方都没有完全达到 0),因此我们考虑的任何直线都是可能的。我们可以查看数据并选择一个更好的初始先验,但这个先验简单,而且因为它对每一条我们考虑作为候选的直线都有一定的概率,所以它是一个可以接受的起点。

第 1 行右侧的图像显示了从该先验中随机选取的 20 条直线,其中更有可能的直线被选中的概率更高。

第 2 行左侧的图像显示了我们的噪声数据集,以及随机选取的一个点,标记为红色。所有通过(或接*)该点的直线的似然图显示在其右侧。现在我们应用贝叶斯法则,将第 1 行中的先验与第 2 行中的似然相乘。

结果是第 3 行左侧的图像。这是后验,或者说是将先验中的每个点(我们认为该直线有多可能)与新点的似然(每条直线与该数据点拟合的可能性)相乘的结果。我们没有展示贝叶斯法则中除以证据的步骤,因为我们正在缩放图片以覆盖整个颜色范围,所以这实际上是后验的一个缩放版本。为了简化,我们还是称之为后验。

注意,第 3 行的后验是一个新的二维分布,由一个新的“云”表示。在其右侧,我们看到从该分布中随机绘制的另外 20 条直线。我们可以看到图像顶部的一个大空白区域,这在第 1 行中并不存在。系统从贝叶斯法则的这一单步学习到,没有任何穿过该空白区域的直线能够与我们看到的数据匹配。第 3 行的后验成为下一步数据点的新的先验。

在第 4 行,我们从输入中选择一个新的数据点,再次显示为红色。其右侧是该点对应的直线的似然性。在第 5 行,我们再次应用贝叶斯定理,并将我们的先验(来自第 3 行的后验)与第 4 行的似然性相乘,以得到新的后验。注意,后验的大小变小了,这告诉我们,适合两个点的直线集合比仅适合第一个点的集合要小。右侧显示了从这个分布中绘制的直线。注意它们如何聚集在与我们刚刚学习的两个点相似的方向上。

我们再次使用第 6 行中的新点和似然性进行处理,并在第 7 行得到新的后验和一组新的直线。来自该后验的直线非常相似,趋势似乎正趋向于与我们的数据良好拟合。通过使用越来越多的点,我们得到了一个越来越有限的可能直线范围。

从这个例子中,我们可以看出贝叶斯定理在训练学习系统中的重要性。可以把我们的训练数据看作曲线上的点,把不断演变的先验看作系统的输出。当我们向系统提供更多的样本(在这种情况下是点)时,系统能够自我调整,以提供我们所期望的响应。

看着图 9-21 右下角的这些直线,可能会让人想将偏差和方差的概念应用于它们,但那并不是贝叶斯思维方式。在贝叶斯框架中,这些不是一组*似某个真实答案的直线,我们也不能通过使用各种形式的*均来发现这个真实答案。相反,贝叶斯认为所有这些直线都是准确和正确的,只是具有不同的概率。计算从这个集合中绘制出的直线的偏差和方差是可能的,但在贝叶斯意义上并没有实际意义。

频率派方法和贝叶斯方法都允许我们将直线(或曲线)拟合到数据上。它们采用了非常不同的态度和机制,给我们提供了两种不同的方式来找到问题的好答案。

摘要

在这一章中,我们探讨了学习系统无法进行良好泛化的几种方式。当学习系统由于曲线与数据不匹配而表现不佳时,我们称之为欠拟合。当学习系统在新数据上表现不佳,但在训练数据上表现出色时,我们称之为过拟合:系统已经学会了训练数据中的许多独特之处和特殊性。我们看到如何通过观察训练和验证的表现并使用正则化方法来防止过拟合。我们在本章的结尾讨论了偏差和方差与过拟合的关系,并通过贝叶斯定理看到了如何为噪声数据拟合一条直线。

在下一章中,我们将探讨数据以及如何为我们的学习系统正确地准备数据。

第十章:数据准备

机器学习算法的效果只能与它们所训练的数据一样好。在现实世界中,我们的数据可能来自噪声传感器、带有漏洞的计算机程序,甚至是纸质记录的不完整或不准确的转录。我们始终需要查看我们的数据,并在使用之前修复任何问题。

为了完成这项任务,已经开发了一系列丰富的方法。它们被称为数据准备技术或数据清洗技术。其目的是在从数据中学习之前对数据进行处理,以便我们的学习系统能够最有效地使用这些数据。

我们还需要确保数据本身适合机器学习,这可能意味着需要调整它,例如通过对数字进行缩放,或将类别合并。此工作至关重要,因为数据的具体结构方式以及它所涵盖的数值范围,可能会对算法从中提取的信息产生强烈影响。

本章的目标是看看我们如何调整所给的数据,在不改变其含义的情况下,获得最有效和高效的学习过程。我们从确认数据已清洗并准备好进行训练的技术开始。接着,我们将考虑检查数据本身的方法,并确保我们已经将其整理成最适合机器学习的形式。这可能包括做一些简单的事情,比如将字符串替换为数字,或采取更有趣的操作,如对数据进行缩放。最后,我们看看如何减少训练数据的大小。这可以让我们的算法运行和学习得更快。

基本数据清洗

让我们首先考虑一些简单的方法,确保我们的数据已经清洗干净。其基本思想是确保我们开始使用的数据没有空白、错误条目或其他错误。

如果我们的数据是文本形式的,那么我们需要确保没有印刷错误、拼写错误、嵌入的不可打印字符或其他类似的破损。例如,如果我们有一组动物照片,并且有一个文本文件描述这些照片,而我们的系统区分大小写,那么我们需要确保每只长颈鹿都标注为 giraffe,而不是 girafe 或 Giraffe,我们还要避免其他拼写错误或变体,如 beautiful giraffe 或 giraffe-extra tall。每次提到长颈鹿时,都需要使用相同的字符串。

我们还应该寻找其他常识性的事项。我们要去除训练数据中的任何重复项,因为它们会扭曲我们对所用数据的理解。如果我们不小心将某一数据项重复多次,我们的学习器会将其视为多个不同的样本,它们恰好具有相同的值,因此该样本可能会产生比应有的更大影响。

我们还需要确保没有任何拼写错误,比如漏掉小数点导致我们指定了 1,000 而不是 1.000,或者在数字前放了两个减号而不是一个。在一些手工编写的数据库中,发现空白或问号的情况并不罕见,表示人们没有数据可输入。一些计算机生成的数据库可能会包含像NaN(不是一个数字)这样的代码,这是一个占位符,表示计算机本来想输出一个数字,但没有有效的数字可显示。更麻烦的是,有时候当人们在填写数值字段时缺少数据时,他们会输入类似 0 或–1 的值。我们必须在开始从数据中学习之前找到并修复所有这些问题。

我们还需要确保数据的格式能够被我们提供的计算软件正确解读。例如,我们可以使用一种称为科学计数法的格式来表示非常大或非常小的数字。问题在于,这种表示法没有一个正式的格式。不同的程序使用略有不同的方式来输出这种格式,而其他读取这些数据的程序(比如我们在深度学习中经常使用的库函数)可能会误解它们无法预料的格式。例如,在科学计数法中,值 0.007 通常会被表示为7e-37E-3。当我们提供7e-3作为输入时,程序可能会将其解释为(7 × e)– 3,其中e是欧拉常数,值约为 2.7。结果是计算机认为7e-3意味着我们要求它先将 7 和e的值相乘,然后减去 3,得出大约 16,而不是 0.007。我们需要捕捉到这些问题,以确保我们的程序能正确解读它们的输入。

我们还需要查找缺失的数据。如果一个样本缺失了一个或多个特征的数据,我们可能能够手动或通过算法填补这些空缺,但有时候直接删除这个样本可能更好。这是一个主观的决策,通常需要根据具体情况逐个判断。

最后,我们需要识别出任何与其他数据明显不同的部分。这些异常值可能只是一些拼写错误,比如漏掉了小数点。也有可能是人为错误,比如错误的复制粘贴,或者某人忘记从电子表格中删除一条记录。当我们不确定某个异常值是有效数据还是某种错误时,我们必须运用我们的判断力来决定是否保留它,或者手动将其删除。这是一个主观的决策,完全依赖于我们的数据代表什么,我们对其的理解程度,以及我们想如何处理它。

虽然这些步骤看起来可能很简单,但在实践中,根据我们的数据的大小和复杂性以及我们首次获取时的混乱程度,执行起来可能是一项重大工作。有许多工具可帮助我们清理数据。有些是独立的,而其他的内置在机器学习库中。商业服务也会收费清理数据。

记住这句经典的计算格言非常有用:垃圾进,垃圾出。换句话说,我们的结果只能和我们的起始数据一样好,所以从最好的可用数据开始非常重要,这意味着我们必须努力使其尽可能清洁。

现在我们已经处理了基本的细枝末节,让我们把注意力转向使数据适合学习。

一致性的重要性

准备学习的数字意味着对它们应用变换,而不改变我们关心的它们之间的关系。我们在本章后面涵盖了几种这样的变换,例如,我们可能将所有数字缩放到给定范围内或者消除一些多余的数据,这样学习者的工作就少了。当我们做这些事情时,我们必须始终遵守一个重要原则:任何时候我们以某种方式修改我们的训练数据,我们也必须以同样的方式修改所有未来的数据

让我们看看为什么这么重要。当我们对训练数据进行任何更改时,我们通常修改或组合值的方式,旨在提高计算机的学习效率或准确性。图 10-1 用视觉方式展示了这个想法。

F10001

图 10-1:用于训练和评估预处理流程的流程图

正如图所示,我们通常通过查看整个训练集来确定所需的所有变换。我们将数据转换为训练我们的学习者,并且在我们释放系统到世界之后,我们也使用相同的转换来处理所有新数据。关键点在于,我们必须为我们的算法提供的所有新数据应用相同的修改,只要我们的系统在使用中。这一步绝不能被忽略。

我们需要在机器学习中再次强调,每当我们评估数据时,我们都需要重新使用相同的转换方式。让我们首先用一个视觉示例来以一种一般的方式看待这个问题。

假设我们想教一个分类器如何区分牛的图片和斑马的图片。我们可以收集大量两种动物的照片作为训练数据。最明显区分这两种动物图片的是它们不同的黑白斑纹。为了确保我们的学习者关注这些元素,我们可能决定裁剪每张照片以隔离动物的皮毛,然后用这些孤立的纹理块来训练。学习者只看到这些裁剪过的照片。图 10-2 展示了几个样本。

F10002

图 10-2:左:一块牛的纹理。右:一块斑马的纹理。

假设我们已经训练并部署了我们的系统,但我们忘记告知用户这一图像预处理步骤——将每张图片裁剪到仅保留纹理的部分。如果没有了解这个关键信息,典型的用户可能会提供完整的牛和斑马照片,像图 10-3 中那样,并要求系统识别每张图片中的动物。

F10003

图 10-3:左:一张牛的照片。右:一张斑马的照片。如果我们在图 10-2 中训练我们的系统,系统可能会被照片中的额外细节误导。

人类可以从这些照片中识别出皮肤纹理模式。另一方面,计算机可能会被腿部、头部、地面和其他细节误导,从而降低其提供良好结果的能力。图 10-2 中的准备数据和图 10-3 中的未准备数据之间的差异可能会导致系统在训练数据上表现出色,但在实际应用中给出糟糕的结果。为了避免这种情况,所有新数据(如图 10-3 中的数据)必须裁剪成与图 10-2 中的训练数据完全相同的输入。

忘记以与我们转换训练数据相同的方式转换新数据是一个容易犯的错误,但通常会导致我们的算法表现不佳,有时甚至变得毫无用处。记住这个规则:我们首先确定如何修改训练数据,然后修改它,接着我们记住我们是如何修改它的。每当我们处理更多数据时,必须首先以与训练数据相同的方式修改这些数据。我们稍后会回到这个想法,看看它是如何在实践中使用的。

数据类型

典型的数据库包含不同类型的数据:浮动小数、字符串、整数(用于类别标识)等。我们会根据不同的数据类型采用不同的处理方式,因此区分这些数据并为每种类型命名是很有用的。最常见的命名系统是基于数据是否可以排序的标准。尽管我们在深度学习中很少显式进行排序,但这个命名系统仍然很方便并被广泛使用。

回想一下,每个样本是一组值的列表,每个值被称为特征。每个样本中的特征可以是两种基本类型之一:数值型类别型

数值数据就是一个数字,可以是浮动小数或整数。我们也称之为定量数据。数值数据或定量数据可以通过其值进行排序。

类别数据指的是其他类型的数据,通常它是描述标签的字符串,比如牛或斑马。这两种类别数据分别对应可自然排序的数据和不可排序的数据。

有序数据是具有已知顺序的分类数据(因此得名),因此我们可以对其进行排序。字符串可以按字母顺序排序,也可以按含义排序。例如,我们可以将彩虹的颜色视为有序数据,因为它们在彩虹中有一个自然的排序,从红色到橙色,再到紫色。为了按彩虹顺序排序颜色名称,我们需要使用一个理解彩虹颜色顺序的程序。另一个有序数据的例子是描述一个人不同年龄段的字符串,如婴儿、青少年和老年人。这些字符串也有一个自然的顺序,因此我们也可以对它们进行排序,再次通过某种自定义的程序。

名义数据是没有自然顺序的分类数据。例如,桌面物品列表,如回形针、订书机和削笔刀,并没有自然的排序,衣物图片的集合,比如袜子、衬衫、手套和圆顶礼帽,也没有自然顺序。我们可以通过定义一个顺序并遵循它,将名义数据转化为有序数据。例如,我们可以声明衣物的顺序应该从头到脚,因此我们之前的例子中的顺序应为圆顶礼帽、衬衫、手套和袜子,从而将这些图片转化为有序数据。我们为名义数据创建的顺序不必有特别的逻辑,只要它被定义并且始终如一地使用即可。

机器学习算法需要数字作为输入,因此我们在学习之前将字符串数据(和任何其他非数字数据)转化为数字。以字符串为例,我们可以列出所有训练数据中的字符串,并为每个字符串分配一个唯一的数字,从 0 开始。许多库提供了内置的程序来创建并应用这种转换。

一位编码(One-Hot Encoding)

有时,将整数转化为列表是有用的。例如,我们可能有一个有 10 个类别的分类器,其中类别 3 可能是烤面包机,类别 7 可能是圆珠笔,依此类推。当我们手动为这些物体之一的照片分配标签时,我们查阅这个列表并给它正确的数字。当系统做出预测时,它会返回一个包含 10 个数字的列表。每个数字代表系统认为输入属于相应类别的置信度。

这意味着我们将标签(一个整数)与分类器的输出(一个列表)进行比较。当我们构建分类器时,将列表与列表进行比较是有意义的,因此我们需要一种方法将标签转化为列表。

这很容易做到。我们标签的列表形式就是我们从输出中想要的列表。假设我们正在标注一个烤面包机的图片。我们希望系统的输出是一个包含十个值的列表,其中第 3 类的槽位为 1,表示我们完全确定这是一个烤面包机,而其他所有槽位都是 0,表示我们完全确定图像不是其他任何东西。所以,我们的标签的列表形式就是这个:十个数字,全部为 0,除了第 3 个槽位为 1。

将像 3 或 7 这样的标签转换为这种列表的过程称为独热编码,即列表中只有一个条目是“热的”或已标记的。这个列表本身有时被称为虚拟变量。当我们在训练过程中向系统提供类别标签时,通常提供的是这个独热编码列表或虚拟变量,而不是单一的整数。

让我们看看实际应用中的情况。图 10-4(a) 显示了 1903 年原版 Crayola Crayons 盒子中的八种颜色(Crayola 2016)。假设这些颜色在我们的数据中以字符串形式出现。我们提供给系统的独热标签显示在最右列。

F10004

图 10-4:1903 年原始八种 Crayola 颜色的独热编码。(a)原始的八个字符串。(b)每个字符串都分配一个从 0 到 7 的值。(c)每次该字符串出现在我们的数据中时,我们将其替换为一个包含八个数字的列表,所有数字为 0,除了对应该字符串值的位置为 1。

到目前为止,我们已经将数据从一种形式转换成了另一种形式。现在,让我们来看一些实际上改变我们数据值的变换。

标准化和规范化

我们经常处理特征跨越不同数值范围的样本。例如,假设我们收集了关于一群非洲草原象的数据。我们的数据用四个值描述每只大象:

  1. 年龄(小时)(0, 420,000)

  2. 重量(吨)(0, 7)

  3. 尾巴长度(厘米)(120, 155)

  4. 相对于历史*均年龄的年龄,单位小时(−210,000, 210,000)

这些是显著不同的数字范围。一般来说,由于我们使用的算法是数值型的,较大的数字可能比较小的数字对学习程序的影响更大。特征 4 中的值不仅很大,而且还可能是负数。

为了获得最佳的学习效果,我们希望所有数据在某种程度上具有可比性,或者大致适合相同的数字范围。

规范化

转换数据的一个常见第一步是对每个特征进行归一化。在日常生活中,normal这个词通常意味着“典型的”,但在不同领域它也有专门的技术含义。在这个上下文中,我们使用的是统计学上的含义。我们说,当我们将数据缩放到某个特定范围时,数据就被归一化了。归一化的最常见范围是[−1,1]和[0,1],具体取决于数据及其含义(例如,谈论负的苹果或年龄就没有意义)。每个机器学习库都提供了执行此操作的常规方法,但我们必须记得调用它。

图 10-5 展示了我们将用于演示的二维数据集。我们选择了吉他形状,因为它有助于我们看到数据点在移动时发生的变化。我们还添加了颜色,仅仅作为视觉辅助工具,帮助我们看到数据点的移动。颜色没有其他意义。

F10005

图 10-5:由 232 个数据点构成的吉他形状

通常,这些数据点是测量结果,例如某些人的年龄和体重,或者一首歌的节奏和音量。为了保持通用性,我们将这两个特征称为 x 和 y。

图 10-6 显示了将我们吉他形状数据中的每个特征归一化到范围[−1,1]后的结果。

F10006

图 10-6:对图 10-5 中的数据进行归一化处理后,数据范围被调整到[−1,1]。形状的倾斜是由于在 y 轴方向上拉伸较多,而 x 轴方向的拉伸较少。

在图 10-6 中,x 值的范围从−1 到 1 进行缩放,而 y 值则独立地从−1 到 1 进行缩放。通过这种操作得到的吉他形状稍微发生了倾斜,因为它在垂直方向上被拉伸得比水*方向更长。每当起始数据的不同维度跨越不同范围时,就会发生这种情况。在我们的例子中,x 数据最初的范围大约是[–1, 0],而 y 数据的范围大约是[–0.5, 0.2]。当我们调整这些值时,我们必须比 x 值更大地拉伸 y 值,导致了在图 10-6 中看到的倾斜。

标准化

另一个常见操作是对每个特征进行标准化。这是一个两步过程。首先,我们为每个特征的所有数据添加(或减去)一个固定值,使得每个特征的均值为 0(这个步骤也叫做均值归一化均值减法)。在我们的二维数据中,这会将整个数据集左右和上下移动,使得均值恰好位于(0,0)的位置。然后,我们不是将每个特征归一化或缩放到−1 到 1 之间,而是将其缩放到标准差为 1(这个步骤也叫做方差归一化)。回想第二章,这意味着该特征中大约 68%的值位于−1 到 1 的范围内。

在我们的二维示例中,x 值会被水*拉伸或压缩,直到大约 68%的数据在 X 轴的−1 和 1 之间,然后 y 值会被垂直拉伸或压缩,直到 Y 轴上也满足相同的条件。这必然意味着点会落在每个轴的[−1,1]范围之外,因此我们的结果与归一化所得到的不同。图 10-7 展示了标准化应用于我们在图 10-5 中的原始数据。

F10007

图 10-7:标准化后图 10-5 的数据

在这里我们再次看到,当原始形状不符合正态分布时,像标准化这样的转换可能会扭曲或以其他方式改变原始数据的形状。大多数库提供了一个函数,可以一次性对我们的所有特征进行归一化或标准化处理。这使得满足某些要求输入数据归一化或标准化的算法变得更加方便。

记住转换

归一化和标准化过程都由参数控制,这些参数告诉它们如何执行任务。大多数库函数会分析数据以找到这些参数,然后利用它们来应用转换。由于使用相同操作转换未来数据至关重要,这些库调用总是提供一种方法来保存这些参数,以便我们以后可以再次应用相同的转换。

换句话说,当我们稍后收到一批新数据以进行评估时,无论是为了评估系统的准确性,还是为了在实际应用中做出预测,我们不会分析这些数据以寻找新的归一化或标准化转换。相反,我们会应用为训练数据确定的相同归一化或标准化步骤。

这一过程的结果是,新转换后的数据几乎从未真正归一化或标准化。也就是说,数据的范围不会在两个轴上都在[−1,1]之间,或者它的*均值不会在(0,0)处,并且其 68%的数据不会在每个轴上的[−1,1]范围内。这没关系。重要的是我们使用了相同的转换。如果新数据没有完全归一化或标准化,那也无妨。

转换类型

一些转换是单变量的,这意味着它们一次只处理一个特征,每个特征彼此独立(该名称来源于将uni(表示“一个”)与variate(表示“变量”或“特征”)结合)。其他的则是多变量的,意味着它们同时处理多个特征。

让我们考虑一下标准化。这通常作为一个单变量变换器来实现,它将每个特征当作一个独立的数据集来处理。也就是说,如果它是将二维点缩放到[0,1]范围,它会首先将所有的 x 值缩放到这个范围,然后独立地将所有的 y 值缩放到这个范围。两个特征集之间没有任何交互,因此 X 轴如何被缩放与 y 值无关,反之亦然。图 10-8 直观地展示了这一理想情况,展示了对具有三个特征的数据应用的标准化过程。

F10008

图 10-8:当我们应用单变量变换时,每个特征是独立地变换的。在这里,我们将三个特征标准化到[0,1]的范围内。(a) 三个特征的起始范围。(b) 每个特征的范围独立地被移动和拉伸到[0,1]的范围内。

相比之下,多变量算法同时考虑多个特征,并将它们作为一个整体来处理。这个过程的最极端(也是最常见)版本是同时处理所有特征。如果我们以多变量方式缩放这三根彩条,我们将它们作为一个整体进行移动和拉伸,直到它们共同覆盖[0,1]的范围,正如图 10-9 所示。

我们可以采用单变量或多变量方式进行多种变换。我们根据数据和应用场景来选择。例如,当我们缩放 x 和 y 样本时,单变量版本在图 10-6 中是合理的,因为它们本质上是独立的。但假设我们的特征是不同时间、不同日期的温度测量值呢?我们可能希望将所有特征一起缩放,这样作为一个整体,它们就覆盖了我们所使用的温度范围。

F10009

图 10-9:当我们应用多变量变换时,我们同时处理多个特征。在这里,我们再次将范围标准化到[0,1]。(a) 三个特征的起始范围。(b) 条形图作为一个整体被移动和拉伸,使得它们的最小值和最大值共同覆盖[0,1]的范围。

切片处理

给定一个数据集,我们需要思考如何选择我们要转换的数据。根据我们是否按样本、按特征或按元素提取数据,通常有三种方法。这些方法分别称为样本级特征级元素级处理。

让我们按这个顺序来看看它们。为了讨论的方便,假设我们数据集中的每个样本都是一组数字列表。我们可以将整个数据集安排成一个二维网格,其中每一行包含一个样本,每一行中的每个元素是一个特征。图 10-10 展示了这种设置。

F10010

图 10-10:我们接下来讨论的数据集是一个二维网格。每一行是一个样本,包含多个特征,这些特征构成了列。

样本级处理

样本逐一处理方法适用于所有特征都是同一事物的不同方面的情况。例如,假设我们的输入数据包含一些音频片段,例如某人通过手机讲话。那么每个样本中的特征就是音频在连续时刻的振幅,如图 10-11 所示。

如果我们想将这些数据缩放到[0,1]范围内,那么按单个样本缩放所有特征是有意义的,这样最响亮的部分就设为 1,最安静的部分设为 0。因此,我们一次处理一个样本,独立于其他样本。

F10011

图 10-11:每个样本由一段短音频波形的测量数据组成。每个特征给出了该时刻声音的音量的瞬时测量值。

按特征处理

按特征处理方法适用于我们的样本代表本质上不同的事物。

假设我们每天晚上都进行各种天气测量,记录温度、降水量、风速和湿度。这为每个样本提供了四个特征,如图 10-12 所示。

F10012

图 10-12:当我们按特征处理数据时,我们独立分析每一列。前三行:原始数据。中间一行:范围。底部一行:缩放后的数据。

按样本逐一缩放这些数据没有意义,因为单位和测量方式不兼容。我们不能在同等条件下比较风速和湿度。但我们可以将所有湿度值一起分析,温度、降水量和湿度的所有值也可以以同样的方式处理。换句话说,我们依次修改每个特征。

当我们按特征处理数据时,每一列特征值有时被称为纤维

元素逐一处理

元素逐一处理方法将图 10-10 网格中的每个元素视为独立实体,并对网格中的每个元素独立应用相同的变换。例如,当我们的所有数据代表相同类型的事物,但我们想改变其单位时,这种方法很有用。例如,假设每个样本对应一个有八个成员的家庭,并包含这八个人的身高。我们的测量团队报告了他们的身高(单位为英寸),但我们想要将其转换为毫米。

我们只需要将网格中的每个条目乘以 25.4,将英寸转换为毫米。无论我们是沿着行还是列进行操作都没有关系,因为每个元素都以相同的方式处理。

我们在处理图像时经常这样做。图像数据通常每个像素的值在[0,255]范围内。我们通常应用元素逐一缩放操作,将整个输入图像的每个像素值除以 255,得到的结果是一个 0 到 1 之间的数据。

大多数库允许我们使用这些解释中的任何一种应用变换。

逆变换

我们一直在研究可以应用于数据的不同转换。但是,有时我们希望撤销或反转这些步骤,这样我们就可以更容易地将结果与原始数据进行比较。

例如,假设我们为一个有一条主要高速公路的城市交通部门工作。我们的城市位于北方,所以温度常常降到冰点以下。城市管理者注意到,交通密度似乎与温度有关,最冷的日子里更多的人待在家里。为了规划道路施工和其他建设,管理者希望了解他们可以根据温度预测每天早晨高峰时段的车辆数量。由于需要一些时间来测量和处理数据,我们决定每晚午夜测量一次温度,然后预测第二天早晨 7 点到 8 点之间路上的车辆数量。我们将在冬季中期开始使用系统,因此我们预期温度会在冰点以上和以下(0°C)波动。

几个月来,我们每晚午夜测量温度,并在第二天早晨 7 点到 8 点之间统计通过特定路标的总车辆数。原始数据如图 10-13 所示。

我们希望将这些数据提供给一个机器学习系统,该系统将学习温度与交通密度之间的关系。部署后,我们输入一个样本,包含一个特征,描述温度(以度数表示),然后返回一个实数,告诉我们预测的路上车辆数量。

F10013

图 10-13:每个午夜我们测量一次温度,然后在第二天早晨 7 点到 8 点之间测量路上的车辆数量。

假设我们使用的回归算法在输入数据缩放到[0,1]范围内时效果最好。我们可以像图 10-14 那样,在两个坐标轴上将数据归一化到[0,1]范围。

F10014

图 10-14:将两个范围都归一化到[0,1]使数据更适合训练。

这看起来就像是图 10-13,只是现在我们的两个尺度(和数据)都从 0 到 1。

我们强调记住这种转换的重要性,这样我们才能将其应用于未来的数据。我们将分三步来介绍这些机制。为了方便,我们采用面向对象的哲学,其中我们的转换由对象执行,而对象会记住它们自己的参数。

我们的三步中的第一步是为每个坐标轴创建一个转换器对象。这个对象能够执行这种转换(也叫做映射)。

其次,让我们将输入数据提供给该对象进行分析。它找到最小值和最大值,并利用它们创建转换,将我们的输入数据移位并缩放到[0,1]范围内。我们将温度数据提供给第一个转换器,将车辆计数数据提供给第二个转换器。

到目前为止,我们只创建了转换器,但还没有应用它们。我们的数据没有发生任何变化。

图 10-15 展示了这个过程。

f10015

图 10-15:构建转换对象。左侧:温度数据输入到一个转换对象中,表示为一个蓝色矩形。右侧:我们还为汽车计数构建了一个黄色的转换器。

第三步是将我们的数据再次交给转换对象,这次我们告诉它们应用它们已经计算出的转换。结果是一个新的数据集,已经转换到[0,1]的范围内。图 10-16 展示了这个过程。

现在我们准备好进行学习了。我们将转换后的数据提供给学习算法,让它分析输入与输出之间的关系,如图 10-17 所示。

假设我们已经训练好了系统,并且它能很好地根据温度数据预测汽车数量。

第二天,我们将系统部署到城市管理者的网页上。在第一个晚上,值班经理测得午夜时温度为−10°摄氏度。她打开我们的应用程序,找到温度输入框,输入−10 并点击“大型预测交通”按钮。

F10016

图 10-16:每个特征都被我们之前计算出的转换所修改。转换后的输出进入我们的学习系统。

F10017

图 10-17:从我们转换后的特征和目标中学习的示意图

哎呀,出现了问题。我们不能直接将−10 输入到我们训练过的系统中,因为正如图 10-17 所示,它期望输入的是一个范围在 0 到 1 之间的数字。我们需要以某种方式对数据进行转换。唯一合理的做法是应用我们在训练系统时对温度所做的相同转换。例如,如果在原始数据集中−10 变成了 0.29,那么如果今晚的温度是−10,我们应该输入 0.29,而不是−10\。

这时我们就能看到将转换保存为对象的价值。我们可以简单地告诉这个对象,使用它在训练数据上应用的相同转换,现在将其应用到这组新的数据上。如果−10 在训练中变成了 0.29,那么任何新的−10 输入,在部署时也会变成 0.29。

假设我们正确地将温度 0.29 提供给系统,它输出的交通密度是 0.32\。这对应于通过我们对汽车转换所得到的某个汽车数量的值。但是这个值是在 0 和 1 之间,因为这是我们训练时代表汽车数量的数据的范围。我们如何撤销这个转换,把它转换回汽车数量呢?

在任何机器学习库中,每个转换对象都有一个逆向例程,用来撤销其转换,提供一个逆向转换。在这种情况下,它会反转之前应用的标准化转换。如果该对象将 39 辆车转换为标准化值 0.32,那么逆向转换会将标准化值 0.32 转回为 39 辆车。这就是我们打印给城市经理的值。图 10-18 展示了这些步骤。

F10018

图 10-18:当我们将新的温度输入系统时,我们使用我们为温度数据计算的转换,将其转换为一个 0 到 1 之间的数字。出来的值接着会通过我们为汽车数据计算的逆向转换,将其从缩放的数字转换为汽车的数量。

这里可能出现的一个问题是,如果我们获得了超出原始输入范围的新样本。假设我们在某个夜晚测得了一个令人吃惊的低温 −50° 摄氏度,这个温度远低于我们原始数据中的最小值。结果是,经过转换后的值是一个负数,超出了我们的[0,1]范围。如果我们遇到一个非常热的夜晚,测得一个正温度,经过转换后得到的值将大于 1,这同样超出了[0,1]范围。

两种情况都是可以的。我们希望将输入缩放到[0,1]范围内,是为了让训练尽可能高效,并且控制数值问题。一旦系统训练完毕,我们可以给它任何我们想要的输入,它会计算出相应的输出。当然,我们仍然需要注意我们的数据。如果系统预测明天的车数为负数,我们是不能基于这个数字来制定计划的。

交叉验证中的信息泄漏

我们已经看到如何从训练集构建转换,然后保持这个转换,并将其不变地应用于所有附加数据。如果我们不仔细遵循这个策略,就可能会发生信息泄漏,即本不应包含在转换中的信息意外地渗入(或泄漏)到转换中,从而影响转换。这意味着我们没有按照预期的方式转换数据。更糟糕的是,这种泄漏可能导致系统在评估我们的测试数据时获得不公*的优势,从而给我们一个虚高的准确度衡量值。我们可能会得出系统足够好,可以部署的结论,结果却会在实际使用时表现得更差。

信息泄漏是一个具有挑战性的问题,因为它的许多原因可能是微妙的。举个例子,让我们看看信息泄漏是如何影响交叉验证过程的,这一点我们在第八章讨论过。现代库提供了方便的例程,提供快速且正确的交叉验证实现,因此我们不必自己编写代码来实现它。但我们还是来看看底层的实现。我们将看到一个看似合理的方法是如何导致信息泄漏的,然后我们将看看如何修复它。看到实际操作中的这些情况将帮助我们更好地预防、发现和修复我们自己系统和算法中的信息泄漏。

回顾一下在交叉验证中,我们将初始训练集中的一个折叠(或部分)留出作为临时验证集。然后我们建立一个新的学习器,并用其余数据进行训练。当训练完成后,我们使用保存的折叠作为验证集来评估学习器。这意味着每次进入循环时,我们都有一个新的训练集(即去除了选定折叠样本的初始数据)。如果我们要对数据进行转换,我们需要仅基于作为该学习器训练集的数据来构建转换。然后,我们将这个转换应用到当前的训练集,并将相同的转换应用到当前的验证集。需要记住的关键点是,因为在交叉验证中我们每次通过循环都会创建一个新的训练集和验证集,所以我们每次通过循环时也需要构建一个新的转换。

让我们看看如果我们做错了会发生什么。图 10-19 显示了我们开始的样本集在左侧。它们被分析以生成一个转换(由红圈表示),然后通过转换例程(标记为 T)应用该转换。为了展示变化,我们将转换后的样本标为红色,像转换一样。

f10019

图 10-19:一种错误的交叉验证方法:基于所有原始训练数据构建一个转换

然后我们进入交叉验证过程。这里循环是“展开”的,因此我们展示了多个训练会话的实例,每个实例与不同的折叠相关联。每次进入循环时,我们移除一个折叠,使用剩余的样本进行训练,然后用验证折叠进行测试并创建得分。

这里的问题是,当我们分析输入数据以构建转换时,我们将每个折叠中的数据都包含在了分析中。为了看看为什么这是一个问题,让我们更仔细地观察一下在一个更简单、更具体的场景中发生了什么。

假设我们想要应用的转换是将训练集中的所有特征按组缩放到 0 到 1 的范围内。也就是说,我们将进行逐样本的多变量标准化。假设在第一个折叠中,最小和最大特征值分别为 0.01 和 0.99。在其他折叠中,最大和最小值位于较小的范围内。图 10-20 展示了每个折叠中数据的范围。我们将分析所有折叠中的数据,并从中构建我们的转换。

在图 10-20 中,左侧显示了我们的数据集被分成五个折叠。在每个框内,我们展示了该折叠的值范围,左侧为 0,右侧为 1。顶部的折叠包含从 0.01 到 0.99 的特征。其他折叠中的值位于此范围内。当我们将所有折叠的数据作为一个整体进行分析时,第一个折叠的范围占主导地位,因此我们只会稍微拉伸整个数据集。

F10020

图 10-20:一种错误的数据转换方式是在循环之前一次性转换所有数据。

现在,让我们继续进行交叉验证循环。我们的输入数据是图 10-20 右侧的五个转换后的折叠数据堆叠。首先,我们提取第一个折叠并将其放置一旁;然后我们可以使用剩余的数据进行训练和验证。但我们在这里犯了一个错误,因为我们的训练数据转换受到了验证数据的影响。这违反了我们的基本原则,即仅使用训练数据中的值来创建转换。然而,我们在计算转换时使用了现在作为验证数据的部分数据。我们称之为信息泄露,因为验证数据的内容进入了不该存在的转换参数中。

正确的构建训练数据转换的方法是,从样本中移除验证数据,然后从剩余数据中构建转换,再将该转换应用于训练数据和验证数据。图 10-21 直观地展示了这一过程。

f10021

图 10-21:交叉验证时正确的数据转换方式是,首先移除折叠样本,然后从剩余数据中计算转换。

现在我们可以将该转换应用于训练集和验证数据。请注意,这里验证数据最终超出了[0,1]范围,这没有问题,因为这些数据确实比训练集更为极端。

为了修正我们的交叉验证过程,我们需要在循环中使用这一方案,并为每个训练集计算新的转换。图 10-22 展示了正确的处理方法。

F10022

图 10-22:交叉验证的正确方法

对于我们想要作为验证集的每一个折叠,我们会分析移除该折叠的起始样本,然后将得到的变换应用到训练集和验证集。不同的颜色表明每次循环时,我们构建并应用的是不同的变换。

我们在交叉验证的背景下讨论了信息泄漏,因为它是这个棘手话题的一个很好的例子。幸运的是,现代库中的交叉验证程序都能正确处理,所以我们在使用库的例程时不必担心这个问题。但这并不意味着我们在编写自己代码时就可以不管信息泄漏。信息泄漏往往很微妙,它可能以意想不到的方式潜入我们的程序。我们在构建和应用变换时,始终要仔细考虑可能的信息泄漏源,这一点非常重要。

缩小数据集

我们一直在研究如何调整数据中的数字,以及如何选择进入每个变换的数字。现在,让我们来看一种不同的变换方式,这种方式不仅仅是操作数据,而是实际压缩数据。我们将字面上创建一个比原始训练集更小的新数据集,通常是通过删除或合并每个样本中的特征。

这有两个好处:提高训练时的速度和准确性。可以推理出,训练时我们处理的数据越少,训练的速度就越快。提到速度,我们是指在给定的时间内,我们能够完成更多的学习,从而使得系统更为准确。

让我们来看几种缩小数据集的方法。

特征选择

如果我们在数据中收集了冗余、不相关或其他无用的特征,那么我们应该将它们剔除,以免浪费时间。这一过程叫做特征选择,有时也叫做特征筛选

让我们考虑一个例子,其中一些数据实际上是多余的。假设我们正在手动标注大象的图像,输入它们的大小、物种以及其他特征到数据库中。不知为何,大家记不清楚,我们还设有一个字段用于记录头的数量。大象只有一个头,所以这个字段的值将始终是 1。因此,这个数据不仅没用,还会拖慢我们的速度。我们应该从数据中删除这个字段。

我们可以将这个想法推广到去除那些几乎无用的特征,这些特征贡献很小,或者根本没有对得到正确答案做出太大贡献。继续以我们收集的大象图像为例。我们为每只大象记录了身高、体重、最后已知的纬度和经度、象鼻长度、耳朵大小等等。但对于这个(假设的)物种,象鼻长度和耳朵大小可能是高度相关的。如果是这样,我们可以删除(或筛选掉)其中一个特征,依然可以获得它们各自代表的信息的好处。

许多库提供了可以估算从数据库中删除每个字段的影响的工具。我们可以利用这些信息来指导我们简化数据库,加速学习,同时不牺牲我们愿意放弃的准确性。因为删除特征是一个转换过程,因此从训练集中删除的任何特征,也必须从未来的所有数据中删除。

维度减少

降低数据集大小的另一种方法是组合特征,使一个特征能够完成两个或更多特征的工作。这被称为维度减少,其中维度指的是特征的数量。

这里的直觉是,我们的数据中某些特征可能密切相关,但并不完全冗余。如果关系很强,我们可能能够将这两个特征合并成一个新的特征。一个日常的例子是身体质量指数(BMI)。它是一个结合了身高和体重的单一数值。一些健康测量可以仅通过 BMI 来计算。例如,帮助人们决定是否需要减肥的图表,可以通过年龄和 BMI 便捷地索引(CDC 2017)。

让我们来看一个工具,它可以自动确定如何选择和组合特征,以对我们的结果影响最小。

主成分分析

主成分分析(PCA)是一种用于减少数据维度的数学技术。让我们通过观察它对吉他数据的处理,直观地了解 PCA。

图 10-23 再次展示了我们起始的吉他数据。如前所述,点的颜色只是为了在后续图形中跟踪它们,数据操作时不会有其他意义。

F10023

图 10-23:我们讨论 PCA 的起始数据

我们的目标是将这二维数据压缩成一维数据。也就是说,我们将每一组成对的 x 和 y 值合并成一个新的数值,正如 BMI 是一个结合了身高和体重的单一数值。

让我们从标准化数据开始。图 10-24 展示了这个过程,设定每个维度的均值为 0,标准差为 1,正如我们之前看到的那样。

我们已经知道我们要将这二维数据减少到一维。在实际应用之前,先让我们通过一个关键步骤缺失的过程来感受这个概念,然后再把这个步骤加回去。

要开始,我们在 X 轴上画一条水*线。我们将这条线称为投影线。然后我们将投影或移动每个数据点到它在投影线上的最*位置。由于我们的线是水*的,我们只需上下移动数据点,找到它们在投影线上的最*点。

F10024

图 10-24:标准化后的输入数据

将图 10-24 中的数据投影到水*投影线上的结果如图 10-25 所示。

F10025

图 10-25:我们通过将吉他数据集的每个数据点移动到其最*的投影直线点上来进行投影。为了清晰起见,我们只展示了大约 25% 的点的移动路径。

所有点处理后的结果如图 10-26 所示。

F10026

图 10-26:在所有点移动到投影直线后的图 10-25 结果。每个点现在仅通过其 x 坐标进行描述,形成了一维数据集。

这就是我们所追求的一维数据集,因为这些点仅通过其 x 值不同(y 值始终为 0,因此无关紧要)。但这并不是一个好的特征组合方法,因为我们所做的只是丢弃了 y 值。这就像通过仅使用体重而忽略身高来计算 BMI。

为了改进这种情况,我们可以加入我们之前跳过的步骤。我们不再使用水*投影线,而是旋转该线,直到它通过最大方差的方向。可以将其视为在投影后具有最大点范围的那条线。任何实现 PCA 的库函数都会自动为我们找到这条线。图 10-27 展示了我们的吉他数据的这一线。

F10027

图 10-27:粗黑线是通过原始数据的最大方差线。这就是我们的投影线。

现在我们继续像之前一样操作。我们将每个点投影到这条投影线上,方法是将其移动到与线最*的点。像之前一样,我们通过垂直于这条线的方向移动,直到与它相交。图 10-28 展示了这一过程。

F10028

图 10-28:我们通过将吉他数据投影到投影线上的每个点,移动每个点到线上的最*点来进行投影。为了清晰起见,我们只展示了大约 25% 的点的移动路径。

投影后的点如图 10-29 所示。注意,它们都位于我们在图 10-27 中找到的最大方差直线上。

f10029

图 10-29:吉他数据集的各点投影到最大方差直线上

为了方便,我们可以将这条点线旋转到与 X 轴*行的位置,如图 10-30 所示。现在,y 坐标不再重要,我们得到了包含每个点的原始 x 和 y 值信息的一维数据。

F10030

图 10-30:将图 10-29 中的点旋转到水*位置

尽管图 10-30 中的直线与图 10-26 中的点大致相似,但它们是不同的,因为这些点在 X 轴上的分布不同。换句话说,它们的数值不同,因为它们是通过投影到一条倾斜的直线,而不是水*直线计算出来的。图 10-31 展示了这两种投影的对比。PCA 的结果不仅更长,而且点的分布也有所不同。

F10031

图 10-31:比较通过投影到 y = 0 的图 10-26(顶部)和 PCA 算法的图 10-30(底部)所创建的点

我们刚才讨论的所有步骤,在调用机器学习库的 PCA 函数时都会自动执行。

通过这个投影步骤创建的一维数据的美妙之处在于,每个点的单一值(其 x 值)是它最初的二维数据的组合。我们将数据集的维度减少了一个维度,但我们尽可能保留了最多的信息。现在,我们的学习算法只需要处理一个特征而不是两个,因此它们的运行速度会更快。当然,我们丢弃了一些信息,因此准确性可能会受到影响。有效使用 PCA 的关键是选择可以组合的维度,同时仍保持项目的性能目标。

如果我们有三维数据,可以想象将一个*面放置在样本云的中央,并将数据投影到该*面上。库的任务是找到该*面的最佳朝向。这样,我们的数据就从三维变成了二维。如果我们想进一步降维到一维,可以想象将数据投影到通过点云的直线上。在实际应用中,我们可以在任何维度数的问题中使用这种技术,并将数据的维度减少十个或更多。

这种算法的关键问题包括:我们应该压缩多少维度?应该组合哪些维度?它们应如何组合?我们通常使用字母k来表示 PCA 处理后剩余的维度数。因此,在我们的吉他示例中,k为 1。我们可以把k称为算法的一个参数,尽管通常我们将其称为整个学习系统的超参数。正如我们所看到的,字母k在机器学习中的许多不同算法中都有使用,这是不幸的;因此,在看到k时,我们必须注意上下文。

压缩得太少意味着我们的训练和评估步骤将低效,但压缩得太多则意味着我们有可能丢失应当保留的重要信息。为了选择超参数k的最佳值,我们通常会尝试几个不同的值,看它们的效果如何,然后选择效果最好的一项。我们可以利用许多库提供的超参数搜索技术来自动化这一过程。

一如既往,无论我们使用什么 PCA 变换来压缩训练数据,都必须以相同的方式应用于所有未来的数据。

简单图像的 PCA

图像是重要且特殊的数据类型。让我们对一组简单的图像应用 PCA。

图 10-32 显示了一组六张图像,可能来自一个包含数万个此类图像的大型数据集。如果这些灰度图像的每一张边长为 1,000 像素,每张图像包含 1,000 × 1,000,或 100 万个像素。是否有比使用每张图像 100 万个数字的表示方法更好的方式?

F10032

图 10-32:我们希望表示的六张图像

让我们从观察图 10-32 中的每张图像可以通过按不同的比例缩放图 10-33 中的三张图像并将它们相加来重新创建开始。

F10033

图 10-33:我们可以通过以不同的比例缩放这三张图像并将结果相加,来创建图 10-32 中的所有六张图像。

例如,我们可以通过将圆形的 20%、竖直框的 70%和水*框的 40%相加来重建图 10-32 中的第一张图像。我们通常将这些缩放因子称为权重。图 10-34 显示了六张起始图像的权重。

F10034

图 10-34:当按比例缩放图 10-33 中的三张图像以恢复图 10-32 中的图像时,使用的权重。

图 10-35 展示了通过缩放和组合这些成分来恢复第一张原始图像的过程。

一般来说,我们可以用三种成分图像的权重来表示任何此类图像,这些权重能够产生最佳匹配。为了重建任何输入图像,我们需要这三张简单图像(每张 100 万个值)以及该特定图像的三个数字。如果我们有 1,000 张图像,存储每张图像将需要总计 1,000 兆字节。但使用这种压缩形式,我们只需要总共 3.001 兆字节。

F10035

图 10-35:通过缩放图 10-33 中的图像来恢复图 10-32 中的第一张图像。

对于这些简单的图像,找到三个几何形状作为成分是很容易的。但当我们处理更现实的图像时,这通常是不可能的。

好消息是,我们可以使用前面讨论的投影技术。我们可以通过投影一组图像来生成一张新图像,而不是像之前那样将一组点投影到一条线上,生成一个新的点集。这是一个比我们在吉他点上操作时更抽象的过程,但概念是相同的。让我们通过跳过细节,专注于结果,来感受一下 PCA 如何处理图像。

再次考虑图 10-32 中的六个起始图像。请记住,这些是灰度图像,而非矢量图。现在让我们要求 PCA 找到一张灰度图像,它最接*代表所有图像的方式,就像我们的对角线最接*代表吉他中的所有点一样。然后,我们可以将每个起始图像表示为该图像的一个加权和,适当缩放后再加上剩余部分。图 10-36 展示了这一过程。

F10036

图 10-36:对图 10-32 中的图像运行 PCA

来自 10-32 的起始图像显示在图 10-36 顶部。

现在我们要求 PCA 找到与图 10-28 中的那条线相对应的图像。也就是说,找到一张从某种意义上讲,能够捕捉所有输入内容的图像。假设它找到了图 10-36 左侧的图片,显示的是两条条形图和一个圆圈,全部是黑色并叠加在一起。我们将其称为共享图像,这不是一个正式术语,但在这里很有用。

现在,让我们将图 10-36 顶部的每个图像表示为共享图像的缩放版与其他图像的组合。为此,我们找到每个输入图像中最亮的像素,并将共享图像缩放到该亮度。这个缩放因子显示在中间行的公共图像副本顶部,在这些副本中,公共图像的强度被按该因子缩放。如果我们将每个缩放过的公共图像从上方的源图像中减去,我们就得到了它们的差异。我们可以将其写为“源图像 - 公共图像 = 差异”,或者等价地,“源图像 = 公共图像 + 差异”,如图所示。

然后我们可以再次运行 PCA,这次对图 10-36 的底部一行进行处理。同样,它将这些六个图像进行投影,生成一个新的图像,该图像最能匹配所有图像。和之前一样,我们可以将每张图片表示为共享部分的一个缩放版加上剩余部分。图 10-37 展示了这一思想。在这个示例中,我们假设 PCA 生成了一个包含两个重叠矩形的图像作为最佳匹配图像。

F10037

图 10-37:对图 10-36 底部一行运行 PCA

在底部行的两张图像中发生了一些有趣的事情。我们来看一下从左数的第二列。我们想要匹配的顶部图像是一个圆形和一个水*框,但我们试图用一对缩放后的交叉框来匹配它。为了匹配顶部图像,我们需要添加一些圆形的部分,但要减去我们刚刚引入的竖直框。这意味着将底部图像中相应的数据设置为负值。这些是完全有效的数据,尽管如果我们直接显示这张图像时需要小心。如果我们通过将下面的两张图像加起来来重建顶部图像,底部行红色区域的负值将抵消中间行竖直框的正值,因此这些图像的和与顶部的圆形和水*框相匹配。右侧列中的水*框也适用相同的推理。

图 10-38 总结了我们到目前为止看到的两步。

我们这里只进行了两步,但我们可以重复这个过程几十次甚至上百次。

为了表示每个起始图像,我们只需要一组公共图像以及我们分配给每个图像的权重。由于这些公共图像是所有图像共享的,我们可以将它们视为共享资源。然后,每个图像都可以通过对这个共享资源的引用以及应用于公共图像的权重列表来完全描述。

F10038

图 10-38:将每个起始图像(第一行)表示为两个缩放后的分量图像(第二行和第三行)的和,再加上剩下的部分(底部行)

每个公共图像都称为分量,我们在每个步骤中创建的那个是主分量(因为它是所有可能分量中最优的,就像图 10-28 中的线是最优的那样)。我们通过分析输入图像来找到这些主分量。因此,这个方法被称为主成分分析

我们包含的分量越多,每个重建的图像与其原始图像的匹配度就越高。我们通常的目标是产生足够的分量,使得每个重建的图像都能保留其原始图像的所有关键特征。

在本讨论中,大多数权重是正数,但我们也看到了一些应该被减去而不是加上的分量图像,因此它们产生了负值的权重。这样,最终的像素在所有分量相加后,会得到期望的值。

仅使用两个分量图像我们做得如何?图 10-39 显示了原始的六个图像和我们通过将图 10-38 中间两行的和来重建的图像。

匹配结果并不完美,但它们是一个不错的起点,特别是在只有两个主成分的情况下。因此,我们有一个包含两张图像的公共池(每张图像需要 100 万个数字),然后每张图像本身只用两个数字来描述。这个方案的美妙之处在于,我们的算法不需要看到这些公共图像。我们只需要它们来找到描述每个输入图像的权重(如果需要,还可以重建图像)。就学习算法而言,每张图像仅由两个数字描述。这意味着我们的算法消耗更少的内存,运行得更快。

F10039

图 10-39:我们的六张起始图像(上),以及从图 10-35c 重建的图像(下)

我们在这个例子中跳过了一个步骤:通常在使用 PCA 时,我们首先会对所有图像进行标准化。这一点将在我们的下一个例子中介绍。

真实图像的 PCA

上一节中的图像是为了简化而设计的。现在我们将对真实图片应用 PCA。

让我们从图 10-40 中展示的六张哈士奇图片开始。为了便于观察处理过程,这些图像的尺寸仅为 45 × 64 像素。它们是手动对齐的,以确保每张图像中的眼睛和鼻子大致位于相同的位置。这样,每张图像中的每个像素都有很大的机会代表狗的相同部位,就像其他图像中的相应像素一样。例如,位于中心下方的像素很可能属于鼻子,靠*上角的像素很可能属于耳朵,等等。

f10040

图 10-40:我们的起始哈士奇图像集

六只狗的数据库并不算大量的训练数据,因此让我们通过数据增强的方式来扩大我们的数据库,这是放大扩展数据集的常见策略。在这种情况下,我们将反复随机遍历我们的六张图像。每次遍历时,我们会选择一张图像,制作一份副本,随机地将其在水*和垂直方向上分别*移最多 10%的距离,旋转最多五度,顺时针或逆时针,并可能将其左右翻转。然后,我们将该变换后的图像添加到我们的训练集。图 10-41 显示了我们第一次和第二次遍历六只狗图像后的结果。我们使用这种创建变体的技术,构建了一个包含 4,000 张狗图像的训练集。

F10041

图 10-41:每一行展示了通过*移、旋转和可能的水*翻转所生成的新图像集。

由于我们希望对这些图像进行 PCA 处理,我们的第一步是对它们进行标准化。这意味着我们会分析在 4,000 张图像中每个像素的位置,并调整这些图像的集合,使其具有零均值和单位方差(Turk 和 Pentland 1991)。我们生成的六只狗的标准化版本见图 10-42。

F10042

图 10-42:标准化后的六只哈士奇

由于 12 张图像适合放在一个图中,让我们首先随便要求 PCA 找出 12 张图像,这些图像在加上适当的权重后,能够最好地重建输入图像。PCA 找到的每个投影(或成分)在技术上被称为特征向量,来自德语词“eigen”意为“自身”(或大致“自我”),以及“vector”是这种数学对象的名称。当我们创建特定类型的事物的特征向量时,通常会通过将前缀“eigen”与我们正在处理的对象结合,来创造一个有趣的名称。因此,图 10-43 展示了我们的 12 个特征狗

F10043

图 10-43:PCA 生成的 12 个特征狗。

看这些特征狗告诉我们很多关于 PCA 如何分析我们的图像的信息。第一个特征狗是一个大模糊区域,颜色较深,恰好是大多数狗出现在图像中的地方。这是唯一一张最接**似每个输入图像的图像。第二个特征狗在第一个特征狗的基础上进行了改进,捕捉到一些左右阴影的差异。

接下来的特征狗提供了更多的细节,依此类推,通过所有 12 个特征狗来实现。因此,第一个特征狗捕捉到了最广泛、最常见的特征,而每增加一个特征狗,就能让我们恢复更多的细节。

PCA 不仅能够创建图 10-43 中的特征狗,还能接收任何图片作为输入,并告诉我们应该对每个特征狗图像应用多少权重,这样当加权图像加在一起时,我们能得到最接*输入图像的最佳*似。

让我们通过将这 12 个特征狗与它们对应的权重结合起来,看看我们能多好地恢复原始图像。图 10-44 展示了 PCA 为每个输入图像找到的权重。我们通过将图 10-43 中的每个特征狗图像按其对应权重缩放,然后将结果加在一起,来创建重建的狗图像。

f10044

图 10-44:通过 12 个特征狗的集合重建我们的原始输入。顶部:重建的狗图像。底部:应用于图 10-43 中特征狗的权重,以构建上面直接显示的图像。请注意,底部一行的垂直尺度并不完全相同。

图 10-44 中的恢复图像不太好。我们要求 PCA 用仅仅 12 张图片表示我们训练集中的所有 4000 张图像。它尽力了,但这些结果还是相当模糊。不过,它们似乎走在了正确的轨道上。

让我们尝试使用 100 个特征狗。前 12 个特征狗图像看起来和图 10-43 中的一样,但接下来它们变得更复杂和细致。我们用 100 个特征狗重建我们前 6 个狗的结果如图 10-45 所示。

F10045

图 10-45:通过 100 个特征狗的集合重建我们的原始输入。

好一点了!它们开始像狗了。但似乎 100 个特征狗还是不够。

让我们将特征狗的数量增加到 500,再试一次。图 10-46 展示了结果。

F10046

图 10-46:从一组 500 个特征狗重建我们的原始输入

这些看起来非常不错。它们都能轻松识别为图 10-42 中的 6 种标准化狗。它们并不完美,但考虑到我们正在将 500 张共享图像的不同数量加在一起,我们在匹配原始图像方面做得很不错。这些前 6 张图像没有什么特别的地方。如果我们查看数据库中任何一张 4,000 张图像,它们看起来都一样好。我们可以继续增加特征狗的数量,结果会继续改进,图像会变得更加清晰,噪点也会减少。

在每个权重图中,获得最多权重的特征狗图像通常是开始的那些,它们捕捉了大的结构。随着我们向下滚动列表,每个新的特征狗通常被赋予的权重比前一个稍微少一些,因此它对最终结果的贡献较小。

PCA 的价值并不在于我们能生成与初始图像集完全相同的图像,而在于我们可以利用特征狗的表示来减少深度学习系统需要处理的数据量。这一点在图 10-47 中得到了说明。我们的输入狗集进入 PCA,PCA 生成一组特征狗。然后,我们想要分类的每只狗再次进入 PCA,PCA 给出该图像的权重。这些权重值最终进入分类器。

如我们之前提到的,我们可以选择仅使用每张图像的 100 个或 500 个权重来训练我们的分类器,而不是使用每张图像的所有像素。分类器从未看到过包含百万个像素的完整图像。它甚至从未见过特征狗。它只得到每张图像的权重列表,这些权重数据是它在训练期间用于分析和预测的。当我们想要分类一张新图像时,我们只提供它的权重,计算机会返回一个类别。这可以节省大量的计算,这意味着节省了时间,并可能提高最终结果的质量。

F10047

图 10-47:在第一行中,我们首先使用 PCA 构建一组特征狗,然后在第二行中,我们为分类器的每个输入找到权重,分类器仅使用这些权重来找到输入的类别。

总结来说,我们提供给分类器的数据不是每一张输入图像,而是它的权重。然后,分类器根据这些权重来推断它所看到的是什么品种的狗。通常,我们只需要几百个权重,就能表示拥有成千上万、甚至数百万个特征的输入样本。

总结

在本章中,我们讨论了数据准备的方法。我们看到,在对数据进行任何处理之前,检查数据并确保其清洁是非常重要的。一旦数据干净,我们可以通过多种方式对其进行转换,以更好地适应我们的学习算法。这些转换仅基于训练数据构建。需要记住的是,我们对训练数据应用的任何转换,必须同样应用于每个额外的样本,无论是验证数据、测试数据,还是来自真实用户的部署数据。

在下一章,我们将深入探讨分类器,并调查一些最重要的算法。

第十一章:分类器

在本章中,我们介绍了四种重要的分类算法,基于我们在第七章中涵盖的分类基础知识。我们通常使用这些算法来帮助我们研究和理解数据。在某些情况下,我们甚至可以根据这些算法构建最终的分类系统。在其他情况下,我们可以利用这些方法获得的理解来设计深度学习分类器,如后续章节中所讨论的那样。

我们通常会使用二维数据并且通常只有两个类别来说明我们的分类器,因为这样便于绘制和理解,但现代分类器能够处理任意维度(或特征)和大量类别的数据。现代库使我们可以用少量代码将这些算法应用于我们自己的数据。

分类器的类型

在深入探讨具体算法之前,我们先将分类器的世界分为两种主要方法:参数方法非参数方法

在参数方法中,我们通常认为算法以对其处理的数据的预先设定描述开始,然后搜索该描述的最佳参数以使其适配。例如,如果我们认为我们的数据符合正态分布,我们可以寻找最适合它的均值和标准差。

在非参数方法中,我们让数据引导我们,在分析数据后,我们才尝试找到某种方式来表示它。例如,我们可能会查看所有数据,试图找到一个边界,将其分成两个或多个类别。

实际上,这两种方法更像是概念性的,而不是严格的。例如,我们可以认为仅仅选择一种特定类型的学习算法就意味着我们在对数据做出假设。而我们也可以认为我们总是在通过处理数据来学习数据本身。但这些是有用的概括,我们将用它们来组织我们的讨论。

让我们从两个非参数分类器开始。

k-最*邻

我们从一种非参数算法开始,叫做k-最*邻,或称kNN。像往常一样,字母k代表的不是一个词,而是一个数字。我们可以选择任何大于或等于 1 的整数。因为在算法运行之前设置了这个值,所以它是一个超参数。

在第七章中,我们看到了一个叫做k-均值聚类的算法。尽管名字相似,但该算法和k-最*邻是不同的技术。一个关键的区别是,k-均值聚类从未标记的数据中学习,而 kNN 则处理带标签的数据。换句话说,k-均值聚类和 kNN 分别属于无监督学习和监督学习。

kNN 训练速度很快,因为它所做的就是将每个传入的样本复制到数据库中。真正有趣的部分发生在训练完成后,当一个新样本到来需要分类时。kNN 分类新样本的核心思想是几何上的,这一点在图 11-1 中得到了很好的展示。

F11001

图 11-1:为了找到新样本(以星号表示)的类别,我们寻找其k个邻居中最常见的类别。

在图 11-1(a)中,我们有一个样本点(一个星号),它位于代表三类(圆形、方形和三角形)的其他样本之间。为了确定新样本的类别,我们查看k个最*的样本(或邻居),并统计它们的类别。类别最多的那个类将成为新样本的类别。我们通过一条线连接每个k个最*样本,来显示在不同k值下考虑的样本。在图 11-1(b)中,我们将k设置为 1,这意味着我们希望使用最*样本的类别。在这种情况下,它是一个红色的圆形,因此新样本被分类为圆形。在图 11-1(c)中,我们将k设置为 9,因此我们查看 9 个最*的点。在这里,我们找到 3 个圆形,4 个方形和 2 个三角形。因为方形的数量多于其他类别,所以星号被分类为方形。在图 11-1(d)中,我们将k设置为 25。现在我们有 6 个圆形,13 个方形和 6 个三角形,因此星号再次被分类为方形。

总结来说,kNN 接受一个新的样本进行评估,并提供一个k值。然后,它会找到与新样本最*的k个样本,并将新样本分配给在找到的k个样本中,代表性最多的类别。处理*局和特殊情况的方式有很多种,但这就是基本思想。

请注意,kNN 算法不会在各个点群体之间创建明确的边界。这里没有样本所属区域或区域的概念。我们称 kNN 为按需懒惰算法,因为它在学习阶段不会对样本进行任何处理。学习时,kNN 只是将样本存储在其内部内存中,然后结束。

kNN 之所以具有吸引力,是因为它简单,并且训练速度通常非常快。另一方面,kNN 可能需要大量内存,因为(在其基本形式下)它保存所有输入样本。使用大量内存可能会导致算法变慢。另一个问题是,新的数据点的分类通常很慢(与我们接下来看到的其他算法相比),这是由于寻找邻居的成本。每次我们想要对一个新数据进行分类时,都必须找到它的k个最*邻,这需要一定的计算。当然,有许多方法可以增强算法以加快这一过程,但它仍然是一个相对较慢的分类方法。对于分类速度至关重要的应用,例如实时系统和网站,kNN 每次产生答案所需的时间可能会使其无法使用。

这种技术的另一个问题是,它依赖于附*有大量邻居(毕竟,如果最*的邻居都非常远,那么它们就无法为我们尝试分类的其他相似样本提供很好的代理)。这意味着我们需要大量的训练数据。如果我们有很多特征(也就是我们的数据有很多维度),那么 kNN 很快就会遭遇维度灾难,这在第七章中我们已经讨论过。随着空间的维度增加,如果我们没有显著增加训练样本的数量,那么任何局部邻域中的样本数都会减少,这使得 kNN 很难得到一个好的邻*点集合。

让我们来测试一下 kNN。在图 11-2 中,我们展示了一个“微笑”数据集,该数据集属于两类。在此图以及接下来类似的图中,数据由点组成。由于点难以观察,我们在每个点周围绘制一个实心圆圈作为视觉辅助。

F11002

图 11-2:一个二维点的微笑数据集。共有两类,蓝色和橙色。

使用不同k值的 kNN 给出了图 11-3 中的结果。

F11003

图 11-3:使用不同k值的 kNN 对图 11-2 中的点进行分类

尽管 kNN 不会生成明确的分类边界,但我们可以看到,当k较小时,我们只与少数邻居进行比较时,空间被分割成一些具有相当粗糙边界的区域。随着k增大,我们使用更多的邻居时,那些边界会变得*滑,因为我们对新样本周围的环境有了更全面的了解。

为了让事情变得更有趣,我们向数据中添加一些噪声,这样边界就不容易找到。图 11-4 展示了图 11-2 的一个噪声版本。

F11004

图 11-4:来自图 11-2 的噪声版本的微笑数据集

不同k值的结果显示在图 11-5 中。

F11005

图 11-5:使用 kNN 和 图 11-4 将类别分配给*面中的点

我们可以看到,在存在噪声的情况下,较小的 k 值会导致边界不规则。在这个例子中,我们需要将 k 增加到 50 才能看到相对*滑的边界。还需要注意的是,随着 k 增加,微笑形状会收缩,因为较多的背景点侵蚀了边缘。

由于 kNN 并没有明确表示类别之间的边界,它可以处理任何类型的边界或任何类别的分布。为了说明这一点,我们可以在微笑上添加一些眼睛,创建三个不相连的相同类别的数据集。生成的带噪声数据见 图 11-6。

F11006

图 11-6:带噪声的数据集,包含一个微笑和两只眼睛

不同 k 值的结果分类见 图 11-7。

在这个例子中,约 20 的 k 值看起来最好。过小的 k 值可能导致边缘不规则和结果噪声,但过大的 k 值可能会开始侵蚀特征。像往常一样,找到最佳的超参数对于任何给定数据集而言,都是通过反复实验来完成的。我们可以使用交叉验证来自动评分每个结果的质量,这在维度很多的情况下特别有用。

kNN 是一个很棒的非参数算法:它容易理解和编程,而且当数据集不太大时,训练非常快速,分类新数据也不会太慢。但当数据集变大时,kNN 就不那么吸引人了:内存需求增加,因为每个样本都会被保留,分类变得更慢,因为搜索变得更慢。这些问题是大多数非参数算法共有的。

F11007

图 11-7:kNN 不会在样本簇之间创建边界,因此即使一个类别被拆分成几个部分,它也能正常工作。

决策树

让我们考虑另一种非参数分类方法,称为 决策树。该算法从样本集中的点构建一个数据结构,然后用这个结构来分类新点。让我们首先看看这个结构,然后再看看如何构建它。

树的介绍

我们可以通过一个熟悉的娱乐游戏“20 个问题”来说明决策树的基本思想。在这个游戏中,一名玩家(选择者)想一个特定的目标物体,通常是一个人、地方或事物。另一名玩家(猜测者)接着问一系列是/否问题。如果猜测者能在 20 个问题内正确识别目标物体,他们就获胜。这个游戏经久不衰的原因之一是,它很有趣,通过如此少量的简单问题就能把庞大的可能对象范围缩小到一个特定实例(或许令人惊讶的是,20 个是/否问题我们只能区分出超过一百万个不同的目标)。

我们可以像图 11-8 那样以图形形式绘制一个典型的 20 个问题游戏。

F11008

图 11-8:用于玩 20 个问题的树形图。请注意,每次决策后都有两个选择,一个是“是”,另一个是“否”。

我们称像图 11-8 这样的结构为,因为它看起来像一棵倒立的树。这种树有许多相关的术语,值得了解。我们说,树中的每个分裂点是一个节点,连接节点的每一条线是链接分支。沿着树的类比,位于顶部的节点是根节点,位于底部的节点是叶节点,或称终端节点。位于根节点和叶节点之间的节点称为内部节点决策节点

如果一棵树具有完美对称的形状,我们称这棵树为*衡的,否则它就是不*衡的。实际上,几乎所有树在创建时都是不*衡的,但我们可以运行算法使它们更接**衡,如果某个特定应用偏好这种情况的话。我们还可以说,每个节点都有一个深度,它是一个数字,表示到达根节点所需通过的最小节点数。根节点的深度为 0,紧接在其下方的节点深度为 1,以此类推。

图 11-9 显示了一个带有这些标签的树形图。

F11009

图 11-9:树的一些术语

通常也使用与家族树相关的术语,尽管这些抽象的树形结构不需要两个节点结合来产生子节点。每个节点(根节点除外)都有一个位于其上方的节点。我们称这个节点为该节点的父节点。紧接在父节点下方的节点是其子节点。我们有时会区分直接子节点(直接与父节点相连的节点)和远程子节点(与父节点处于相同深度,但通过一系列其他节点连接到父节点的节点)。如果我们将注意力集中在一个特定的节点上,那么该节点及其所有子节点一起称为子树。共享相同父节点的节点称为兄弟节点

图 11-10 以图形方式展示了这些概念的一部分。

F11010

图 11-10:使用树的熟悉术语。绿色节点的父节点在其上方,而其子节点在下方。

记住这些词汇后,让我们回到我们的 20 个问题游戏。

使用决策树

在图 11-8 中展示的 20 问题树的一个有趣特点是它是二叉的;每个父节点恰好有两个子节点:一个代表“是”,一个代表“否”。二叉树是某些算法特别容易处理的一种树结构。如果某些节点有超过两个子节点,我们称整个树为灌木状。如果需要,我们总是可以将一个灌木状的树转换为二叉树。一个尝试猜测某人生日月份的灌木状树示例显示在图 11-11 的左侧,右侧则是相应的二叉树。因为我们可以轻松地在两者之间转换,所以通常会根据讨论的需要,绘制最清晰简洁的树形。

我们可以使用树来分类数据。当树用于这种方式时,称为决策树。这种方法的全称是分类变量决策树。这是为了区分我们在使用决策树处理连续变量时的情况,如回归问题。那种情况下的决策树被称为连续变量决策树

F11011

图 11-11:将一个灌木状树(左)转换为二叉树(右)

在这里,我们继续使用分类版本。为了简化,从现在开始,我们只将其称为决策树,或者简称为树。一个用于将输入分类到不同类别的树的示例如图 11-12 所示。

在图 11-12 中,我们从一个包含不同类别样本的根节点开始,这些样本通过形状(和颜色)来区分。为了构建树,我们在每个节点将样本分成两组,使用某种测试方式,得到一个决策。例如,在根节点应用的测试可能是:“这个形状是矩形吗?”左子节点的测试可能是:“这个形状比宽度高吗?”我们很快就会看到如何提出这些测试。这个示例的目标是不断拆分每个节点,直到只剩下属于一个类别的样本为止。此时,我们声明该节点为叶子节点,并停止拆分。在图 11-12 中,我们已将起始数据分成了五个类别。记住我们在每个节点应用的测试非常重要。现在,当一个新样本到来时,我们可以从根节点开始,应用根节点的测试,然后是相应子节点的测试,依此类推。最终,当我们到达一个叶子节点时,就确定了该样本的类别。

F11012

图 11-12:一个分类决策树。每个类别都有不同的形状和颜色。

决策树并不是一开始就完全构建好的。相反,我们根据训练集中的样本来构建树。当我们在训练过程中到达一个叶节点时,我们会测试新的训练样本是否与该叶节点中所有其他样本属于同一类别。如果是,我们将该样本添加到叶节点中,并完成操作。否则,我们基于一些特征制定决策标准,这些特征能帮助我们区分这个样本和节点中的其他样本。然后我们使用这个测试来拆分节点。我们制定的测试会与节点一起保存,至少创建两个子节点,并将每个样本分配到相应的子节点,就像在图 11-13 中所示。

当我们完成训练后,评估新样本变得很容易。我们只需从根节点开始,沿着每个节点根据该节点的测试结果,结合样本的特征,沿着合适的分支向下遍历。当我们到达叶节点时,我们报告该样本属于该叶节点中的对象类别。

F11013

图 11-13:通过对节点内容应用测试来拆分节点

这是一个理想化的过程。在实际应用中,我们的测试可能并不完美,如果出于效率或内存考虑,我们选择不再拆分某些叶节点,那么这些叶节点中可能包含不同类别的对象的混合。例如,如果我们到达一个节点,该节点包含 80%来自 A 类和 20%来自 B 类的样本,我们可能会报告新样本有 80%的概率属于 A 类,20%的概率属于 B 类。如果我们只能报告一个类别,我们可能会报告 A 类占 80%的概率,而 B 类占 20%。

这种决策树的构建过程一次只处理一个样本。在这个技术的最简单版本中,我们不会一次性考虑整个训练集并尝试找到最小的或最*衡的树来对样本进行分类。相反,我们一次只考虑一个样本,并根据需要拆分树中的节点来处理该样本。然后我们对下一个样本进行相同的操作,以此类推,实时做出决策,而不关心任何还未出现的其他数据。这使得训练更加高效。

这个算法只根据它以前见过的数据和当前考虑的数据做出决策。它不会基于之前所见的内容为未来做出计划或策略。我们称这种算法为贪婪算法,因为它专注于最大化其即时的短期收益。

决策树有时在实践中优于其他分类器,因为它们的结果是可解释的。当算法为一个样本分配类别时,我们无需解开复杂的数学或算法过程。相反,我们可以通过识别每个决策过程来完全解释最终结果。这在现实生活中也可能非常重要。

例如,假设我们向银行申请贷款,但被拒绝。当我们询问原因时,银行可以展示在整个过程中所做的每一个测试。我们称之为算法的透明性。需要注意的是,这并不意味着它们是公*或合理的。银行可能制定了一些对某些社会群体有偏见的测试,或者依赖于看似无关的标准。仅仅因为他们可以解释为什么做出选择,并不意味着这一过程或结果是令人满意的。立法者尤其偏好那些强制执行透明性的法律,这些法律容易证明,而公*则更加难以实现。透明性是一个可取的特性,但它并不意味着一个系统的行为是我们期望的那样。

决策树可能会做出糟糕的决策,因为它们特别容易出现过拟合。让我们看看为什么。

过拟合树

让我们通过考虑几个示例,开始讨论在构建树的过程中如何出现过拟合。

图 11-14 中的数据展示了一个干净分离的两类数据集。一个大致遵循这种几何结构的数据集通常被称为两个月亮数据集,可能是因为这些半圆形状让人联想到新月。

F11014

图 11-14:我们构建决策树的 600 个起始数据点,排列成两个半月形结构。这些二维数据点代表了两个类别,蓝色和橙色。

构建树的每一步可能涉及拆分一个叶子节点,从而将其替换为一个节点和两个叶子节点,导致树中一个内部节点和一个叶子节点的净增加。我们通常通过树中叶子节点的数量来衡量树的大小。

图 11-15 显示了为这些数据构建决策树的过程。

F11015

图 11-15:为 图 11-14 中的数据构建决策树。请注意,树是如何从大块区域开始,并逐步将它们细化为更小、更精确的区域。

在此图中,我们在每个图像上绘制了数据集,仅供参考。在这个示例中,每个节点对应一个盒子。我们在这里没有显示,但树的起始部分仅由一个根节点组成,对应一个覆盖整个区域的蓝色盒子。然后,训练过程接收到一个靠*橙色曲线顶部的橙色点。这不属于蓝色类别,因此我们通过水*切割将根节点分成两个盒子,如左上方所示。接下来的点是一个靠*蓝色曲线左侧的蓝色点。这落入了橙色盒子,因此我们通过垂直切割将其分成两个盒子,如所示,最终得到了三个叶子节点。其余部分显示了随着更多样本到来,树形逐渐演变的过程。

请注意,当树形随着更多训练数据的到来而生长时,区域是如何逐渐细化的。

这棵树只需要 12 个叶子就能正确分类每个训练样本。图 11-16 中同时展示了最终树和原始数据。该树完美地拟合了数据。

注意这两个水*的细长矩形。它们将两个橙色样本包围在弧线左侧的顶部,并设法穿插在蓝色点之间(回想一下,样本是每个圆圈中心的点)。这是过拟合,因为尽管它们几乎完全位于蓝色区域内,但任何落入这些矩形的未来点都会被分类为橙色。

F11016

图 11-16:我们的最终决策树,包含 12 个叶子

由于决策树对每个输入样本都非常敏感,它们有一种强烈的过拟合倾向。实际上,决策树几乎总是会过拟合,因为每个训练样本都可能影响树的形状。要验证这一点,请看看 图 11-17。在这里,我们对 图 11-16 使用相同的算法进行了两次运行,但每次我们都使用了不同的、随机选择的 70% 输入数据。

F11017

图 11-17:决策树对其输入非常敏感。(左)我们从 图 11-14 随机选择了 70% 的样本并拟合了一棵树。(右)同样的过程,但对于从原始样本中随机选择的 70% 样本。

这两棵决策树相似,但显然不是完全相同。当数据不容易分离时,决策树过拟合的倾向会更加明显。现在我们来看一个这样的例子。

图 11-18 展示了另一对弯月形状的样本,但这次我们在样本分配类别后加入了大量噪声。两个类别不再有清晰的边界。

F11018

图 11-18:构建决策树的 600 个噪声样本集

将树拟合到这些数据从大区域开始,但随着算法不断拆分节点来适应噪声数据,它很快变成了一组复杂的微小矩形框。图 11-19 显示了结果。在这种情况下,树需要 100 个叶子来正确分类这些点。

F11019

图 11-19:构建决策树的过程。注意第二行使用了大量的叶子。

图 11-20 展示了最终树和原始数据的特写。

F11020

图 11-20:我们的噪声数据拟合了一棵包含 100 个叶子的树。注意,多少小矩形被用来捕捉偶尔的样本。

这里有很多过拟合。尽管我们预计右下角的大部分样本应该是橙色,而左上角的大部分样本应该是蓝色,但这棵树根据这个特定的数据集划出了许多例外。未来落入这些小矩形框的样本可能会被错误分类。

让我们重复构建树的过程,使用不同的、随机选择的 70%数据,在图 11-18 中展示。结果见图 11-21。

F11021

图 11-21:使用图 11-18 中不同 70%的样本集构建的几棵树

这些树有相似之处,但它们差异显著,许多小部分仅用于分类少量样本。这就是过拟合的表现。

尽管这看起来对于决策树方法来说可能不太理想,但在第十二章中我们将看到,通过将多个简单的决策树结合成一个群体,或称为集成,我们可以创建出稳健、高效的分类器,从而减少过拟合的影响。

还有一些其他方法可以控制过拟合。

如我们在图 11-15 和图 11-19 中看到的,树的生长初期通常会生成较大、较为通用的形状。只有当树变得非常深时,才会出现那些典型的过拟合现象——小的盒子。减少过拟合的一个常见策略是限制深度:我们在树构建过程中简单地限制树的深度。如果一个节点离根节点超过了设定的步数,我们就将其直接声明为叶子节点,不再进行分裂。另一种策略是设置最小样本要求,确保我们在分裂一个节点时,该节点至少包含一定数量的样本,无论这些样本有多么混杂。

另一种减少过拟合的方法是在树构建完成后,通过一种叫做剪枝的过程来减小树的大小。其原理是通过移除或修剪叶子节点来实现。我们检查每个叶子节点,并判断如果移除该叶子节点,树的总误差会发生什么变化。如果误差仍在可接受范围内,我们就直接移除该叶子节点。如果移除一个节点的所有子节点,那么该节点就会变成一个叶子节点,并成为进一步剪枝的候选对象。剪枝树可以使树变得更浅,这还带来了一个额外的好处,那就是在对新数据进行分类时,它的速度也会更快。

限制深度、设置每个节点的最小样本要求以及剪枝,都能简化树的结构,但由于它们是以不同的方式实现的,因此通常会给出不同的结果。

节点分裂

在我们离开决策树之前,简要回顾一下节点分裂过程,因为许多机器学习库提供了不同的分裂算法供我们选择。当考虑一个节点时,有两个问题需要问:首先,是否需要分裂?其次,我们应该如何分裂它?我们按顺序来讨论这两个问题。

当我们问一个节点是否需要拆分时,通常会考虑节点中所有样本属于同一类别的程度。我们用一个叫做该节点纯度的数字来描述节点内容的均匀性。如果所有样本都属于同一类别,则该节点是完全纯净的。我们拥有其他类别样本的数量越多,纯度的值就越小。为了测试节点是否需要拆分,我们可以将纯度与阈值进行比较。如果节点的纯度过于不纯,即纯度低于阈值,则我们进行拆分。

现在我们可以看一下如何拆分节点。如果我们的样本具有多个特征,我们可以设计许多不同的拆分测试。我们可以只测试一个特征的值,忽略其他特征。我们可以查看特征组,并基于它们的一些聚合值进行测试。我们可以自由地根据不同特征在每个节点上选择完全不同的测试方法。这为我们提供了大量可能的测试方案。

图 11-22 展示了一个包含不同大小和颜色的圆圈的混合节点。让我们尝试将所有红色物体放入一个子节点,将所有蓝色物体放入另一个子节点。当我们仅查看数据时(通常是处理任何新数据库时的最佳第一步),似乎红色圆圈是最大的。让我们尝试使用基于圆圈半径的测试。图中展示了使用三个不同半径值进行拆分的结果。

F11022

图 11-22:根据圆圈的半径值拆分节点

在这个例子中,半径值为 70 的拆分产生了最纯净的结果,所有蓝色物体都在一个子节点中,所有红色物体都在另一个子节点中。如果我们使用这个测试来拆分该节点,我们将记住我们使用的特征(半径)以及要测试的值(70)。

由于我们可能基于样本的任何特征使用测试来拆分节点,因此我们需要某种方式来评估结果,以便选择最佳的测试方法。让我们来看看两种常见的结果测试方法。

回想一下第六章中提到的,它是复杂性的度量,或者说传达某些信息所需的比特数。信息增益(IG)度量通过比较节点的熵与每个候选测试所生成子节点的熵来使用这一概念。

为了评估一个测试,IG 将通过该测试生成的所有新子节点的熵加在一起,并将结果与父节点的熵进行比较。一个单元格越纯净,它的熵就越低,因此如果一个测试使得单元格变得纯净,则它们的熵总和会小于父节点的熵。经过不同的拆分测试后,我们选择那个能带来最大熵减少(或最大信息增益)的拆分方法。

另一种评估分裂测试流行的方法是 基尼不纯度。该技术使用的数学方法旨在最小化错误分类样本的概率。例如,假设某个叶子节点包含 10 个 A 类样本和 90 个 B 类样本。如果一个新样本进入该叶子节点,并且我们报告它属于 B 类,那么我们错误的概率为 10%。基尼不纯度衡量每个叶子节点中多个候选分裂值的错误情况。然后,它选择错误分类机会最小的分裂。

一些库提供了其他用于评估潜在分裂质量的度量。像许多其他选择一样,我们通常会尝试几种选项,并选择最适合我们正在处理的特定数据的那个。

支持向量机

让我们考虑第一个参数化算法:支持向量机(或 SVM)。我们将使用二维数据并仅用两个类别进行说明(VanderPlas 2016),但像大多数机器学习算法一样,这些思想很容易应用于具有任意维度和类别的数据。

基本算法

让我们从两个点簇开始,每个簇对应两个类别,如图 11-23 所示。

F11023

图 11-23:我们的起始数据集由两个二维样本簇组成。

我们希望找到这两个簇之间的边界。为了简化问题,假设我们使用一条直线。但应该选择哪一条呢?有许多条直线可以分开这两个组。图 11-24 显示了三条候选线。

F11024

图 11-24:可以分隔我们两个样本簇的三条无限多的线之一。

我们应该选择哪一条线?一种思考方法是想象可能会有新的数据进来。一般来说,我们希望将任何新样本分类为与其最接*的样本所属的类别。

为了评估任何给定的边界线如何实现这一目标,让我们找出它到任一类别最*样本的距离。我们可以利用这个距离在这条线周围画一个对称的边界。图 11-25 展示了几条不同线的想法。

F11025

图 11-25:我们可以通过找到该线与最*数据点之间的距离,为每条线分配一个质量值。

在图 11-25 中,我们在最接*该线的样本周围画了一个圆圈。在最左侧的图中,许多新点会被错误地归类为左上簇的一部分,而它们实际上更接*右下簇。在最右侧的图中,情况相同。在中间,线条明显更好,但它现在略微偏向右下簇。由于我们希望每个新点都被分配到它最接*的样本的类别,因此我们希望我们的线能够恰好穿过两个簇的中间。

寻找这条线的算法叫做支持向量机,或SVM(Steinwart 2008)。SVM 找到的线是离两个簇中所有点最远的线。在这种情况下,支持可以理解为“最*”,向量是“样本”的同义词,而机器是“算法”的同义词。因此,我们可以将 SVM 描述为“最*样本算法”。

SVM 计算出的图 11-23 中我们两个簇的最佳线,如图 11-26 所示。

让我们看看 SVM 是如何找到这条线的。

在图 11-26 中,圈起来的样本是最*的样本,或称为支持向量。算法的第一步是定位这些圈中的点。一旦找到这些点,算法接着找到图形中心附*的实线。在所有分开这两个点集的线中,这条线离每个集中的每个样本最远,因为它与其支持向量的距离最大。图 11-26 中的虚线,就像圈住支持向量的圆圈一样,是视觉辅助工具,帮助我们看到 SVM 找到的实线是如何尽可能远离所有样本的。实线到穿过支持向量的虚线之间的距离被称为间隔。我们可以重新表述这个概念,SVM 算法找到的是具有最大间隔的那条线。

F11026

图 11-26:SVM 算法找到的线,与所有样本的距离最大。

如果数据存在噪声,且数据块重叠,如图 11-27 所示,该怎么办?现在我们无法创建一个被空白区域包围的线。什么样的线是穿过这些重叠样本集的最佳选择呢?

F11027

图 11-27:一个数据集,其中数据块发生重叠

SVM 算法让我们可以控制一个名为C的参数。这个参数控制算法在允许点进入边界之间的区域时的严格程度。C值越大,算法对线周围的空白区域要求越严格。C值越小,更多的点可以出现在线周围的区域。我们通常需要通过反复试验来搜索最佳的C值。在实际操作中,这通常意味着尝试多个值,并通过交叉验证来评估它们。

图 11-28 显示了我们重叠数据的C值为 100,000 时的情况。

F11028

图 11-28:C值告诉 SVM 对线周围区域内可以进入的点的敏感程度。这里,C为 100,000。

让我们将C的值大幅降至 0.01。图 11-29 显示,较小的C值让更少的点进入线周围的区域。

F11029

图 11-29:将C降低到 0.01,能让比图 11-28 中更少的点进入。

图 11-28 和图 11-29 中的直线是不同的。我们更倾向于选择哪条线,取决于我们对分类器的需求。如果我们认为最佳的边界来自于点重叠区域附*的细节,我们希望选择较小的C值,这样我们只关注那些接*边界的点。如果我们认为两组点的整体形状更能描述该边界,我们希望选择较大的C值,以便包括更多距离较远的点。

SVM 核技巧

参数化算法的限制在于它能够找到的形状。例如,SVM 只能找到线性形状,比如直线和*面。如果我们有无法通过这种形状明显分开的数据,可能会觉得 SVM 并不适用。但有一个巧妙的技巧,能够让我们在最初看似只有曲线才能分开的地方,使用线性边界。

假设我们有图 11-30 中的数据,其中一个类别的样本形成一个块状区域,另一类别的样本围绕着它形成一个环。我们无法通过画一条直线将这两组数据分开。

F11030

图 11-30:两个类别的数据集

这里就涉及到巧妙的部分。假设我们通过提升每个点的高度,按照该点距离方形区域中心的距离添加一个新的维度。图 11-31 展示了这个概念。

F11031

图 11-31:如果我们将图 11-30 中的每个点按照其距离粉色区域中心的距离向上推移一定的量,我们将得到两个不同的点云,并且我们可以用一个*面将它们分开。

正如我们在图 11-31 中看到的那样,我们现在可以在这两组数据之间画一条*面(直线的二维版本)。事实上,我们可以像之前一样使用支持向量和边界的概念来找到这个*面。图 11-32 突出显示了两组点之间*面的支持向量。

F11032

图 11-32:*面的支持向量

现在,所有在*面上方的点都可以归为一类,所有在*面下方的点则归为另一类。

如果我们在原始的二维图中突出显示从图 11-32 找到的支持向量,我们得到图 11-33。

F11033

图 11-33:从上方观察图 11-32

如果我们包括由*面创建的边界,我们得到图 11-34。

在这种情况下,我们通过观察数据并设计一个合适的三维转换方法,成功找到了分离数据的正确方式。但是,当数据具有很多维度时,我们可能无法很好地可视化数据,甚至无法猜测一个合适的转换方法。幸运的是,我们不需要手动寻找这些转换。

找到一个好的变换方法的一种方式是尝试大量的变换,然后选择效果最好的一个。由于所需的计算量,这种方法往往过于缓慢,不具备实用性,但幸运的是,我们可以通过巧妙的方式加速这一过程。这个想法集中在一种叫做的数学概念上,它是 SVM 算法的核心。数学家们有时会用技巧这个词来形容一个特别巧妙或简洁的想法。在这种情况下,重写 SVM 数学公式被称为核技巧(Bishop 2006)。核技巧使得算法可以在不实际变换数据点的情况下计算变换后点之间的距离,这是一个巨大的效率提升。所有主要库都会自动使用核技巧,因此我们甚至不需要特别请求它(Raschka 2015)。

F11034

图 11-34:来自图 11-30 的数据,带有支持向量、显示边界的虚线以及由*面所创建的边界

朴素贝叶斯

让我们来看一个参数化分类器,这种分类器通常在我们需要快速得到结果时使用,即使这些结果不是最准确的。

这个分类器之所以快速,是因为它首先对数据做出假设。它基于贝叶斯定理,我们在第四章中已经讲过这个定理。

回想一下,贝叶斯定理是从先验开始的,或者说是对结果可能是什么的预先设想。通常,当我们使用贝叶斯定理时,我们通过评估新的证据来精化先验,创建一个后验,然后这个后验就成了我们的新先验。那么,如果我们提前就决定好先验,然后看看它会带我们走向何方呢?

朴素贝叶斯分类器采用了这种方法。之所以叫做朴素,是因为我们在先验中做出的假设并不是基于数据的内容。也就是说,我们对数据做出了一个未经信息验证的假设,或者说是一个“朴素”的假设。我们只是假设数据有某种结构。如果我们的假设是正确的,那就很好,我们能得到不错的结果。如果数据和这个假设不太匹配,结果就会变差。朴素贝叶斯之所以受欢迎,是因为这个假设经常是正确的,或者至少接*正确,因此值得一试。有趣的是,我们从来不检查我们的假设是否成立。我们只是继续前进,好像我们确信无疑一样。

在朴素贝叶斯的一个常见形式中,我们假设每个样本的特征都遵循高斯分布。回想第二章中的内容,这就是著名的钟形曲线:一种*滑对称的形状,中央有一个峰值。这就是我们的先验。当我们查看所有样本中的某个特定特征时,我们只是尽可能地将其与高斯曲线进行匹配。

如果我们的特征确实遵循高斯分布,那么这个假设会产生良好的拟合效果。朴素贝叶斯的一个优点是,这个假设似乎比我们预期的要有效得多。

让我们看看它是如何工作的,从满足先验假设的数据开始。图 11-35 展示了一个通过从两个高斯分布中抽样生成的数据集。

F11035

图 11-35:用于朴素贝叶斯训练的一组二维数据。共有两个类别:红色和蓝色。

当我们将这些数据输入到朴素贝叶斯分类器时,它假设每组特征来自一个高斯分布。也就是说,它假设红色点的 x 坐标符合高斯分布,红色点的 y 坐标也符合高斯分布。它对蓝色点的 x 和 y 特征做同样的假设。然后,它尝试为这些数据拟合出最佳的四个高斯分布,生成两个二维的高山。结果如图 11-36 所示。

F11036

图 11-36:朴素贝叶斯为每个类别的 x 和 y 特征拟合高斯分布。左:红色类别的高斯分布。右:蓝色类别的高斯分布。

如果我们将高斯分布的“斑点”和数据点重叠,并从上方直接观察,如图 11-37 所示,我们可以看到它们形成了非常接*的匹配。这个结果并不令人意外,因为我们生成数据时使用的分布正是朴素贝叶斯分类器所期望的分布。

F11037

图 11-37:我们整个训练集与图 11-36 中的高斯分布重叠

为了看看分类器在实际中的表现如何,让我们将训练数据拆分,随机选择 70% 的点作为训练集,其余作为测试集。我们用这个新的训练集进行训练,然后将测试集绘制在高斯分布上,得到图 11-38。

在图 11-38 中,我们将所有被分类为第一类的点绘制在左侧,所有第二类的点绘制在右侧,并保持它们原来的颜色。我们可以看到所有的测试样本都被正确分类。

F11038

图 11-38:使用我们起始数据的 70% 进行训练后的测试数据

现在,让我们尝试一个不满足所有样本特征遵循高斯分布先验假设的例子。图 11-39 显示了我们新的起始数据,包含两个噪声弯月形的数据。

F11039

图 11-39:一些噪声弯月形数据

当我们将这些样本输入到朴素贝叶斯分类器时,它假设(和往常一样)红色 x 值、红色 y 值、蓝色 x 值和蓝色 y 值都来自高斯分布。它找到它能找到的最佳高斯分布,如图 11-40 所示。

F11040

图 11-40:将高斯分布拟合到图 11-39 中的弯月形数据

当然,这些高斯分布与我们的数据并不匹配,因为它们不满足假设。将数据覆盖在图 11-41 中的高斯分布上,可以看到匹配并不糟糕,但也相差甚远。

F11041

图 11-41:我们的训练数据来自图 11-39,并叠加在图 11-40 的高斯分布上

和之前一样,接下来我们将新月形的数据分为训练集和测试集,在 70%的数据上进行训练,并查看预测结果。在图 11-42 的左侧图像中,我们可以看到所有分配给红色类别的点。正如我们所希望的那样,这些大多数是红色点,但来自左上方红色新月的一些点没有被分类为红色,来自右下方蓝色新月的一些点被错误地分类为红色,因为它们来自这个高斯分布的值高于来自另一个的值。

在图 11-42 的右侧,我们可以看到另一种高斯分布的相反情况。换句话说,我们正确分类了许多点,但也在每个类别中都出现了误分类。

我们不应对误分类感到太惊讶,因为我们的数据并没有遵循朴素贝叶斯先验假设。令人惊讶的是分类器的表现如此出色。一般来说,朴素贝叶斯在各种数据上通常表现良好。这可能是因为许多现实世界的数据来源于可以很好地用高斯分布描述的过程。

F11042

图 11-42:我们在图 11-39 的数据上训练的朴素贝叶斯分类器的测试数据预测结果

由于朴素贝叶斯非常快速,因此我们通常在试图了解数据时应用它。如果它表现出色,我们可能不需要再考虑更复杂的算法。

分类器比较

本章中我们介绍了四种流行的分类算法。大多数机器学习库都提供了这些算法以及许多其他算法。简要地说,让我们看看这四种分类器的优缺点,从非参数算法开始。

kNN 方法非常灵活。它不显式地表示边界,因此可以处理训练数据中由类别样本形成的任何复杂结构。它的训练速度很快,因为通常只是保存每个训练样本。另一方面,预测速度较慢,因为算法必须为我们要分类的每个样本搜索最*的邻居(虽然有很多提高搜索效率的方法,但仍然需要时间)。并且因为它保存了每个训练样本,算法可能会消耗大量内存。如果训练集的大小超过了可用内存,操作系统通常需要开始将数据保存在硬盘(或其他外部存储)上,这可能会显著减慢算法的速度。

决策树训练速度快,在做出预测时也很快。它们可以处理类之间的奇怪边界,尽管这可能需要更深的树。由于它们容易过拟合,决策树有一个巨大的缺点(不过正如我们所提到的,我们稍后会通过使用多个小树来解决这个问题,所以并非全是坏消息)。决策树在实践中非常有吸引力,因为它们容易解释。有时候即使结果不如其他分类器,人们仍然会使用决策树,因为它们的决策是透明的,或者说容易理解。需要注意的是,这并不意味着选择是公*或正确的,仅仅是它们对人类来说是可以理解的。

支持向量机是参数化算法,可以快速做出预测。一旦训练好,它们不需要太多内存,因为它们只存储样本区域之间的边界。而且它们可以使用核技巧来找到看起来比直线(以及*面、甚至更高维度的*面)复杂得多的分类边界。另一方面,训练时间随着训练集大小的增加而增长。结果的质量对参数C非常敏感,C指定了允许多少样本靠*边界。我们可以使用交叉验证来尝试不同的C值并选出最佳的。

朴素贝叶斯训练迅速,预测也很快,而且它的结果也不难解释(尽管比起决策树或 kNN 结果来说,稍显抽象)。该方法没有需要调整的参数。如果我们处理的类分布较为分离,那么朴素贝叶斯的先验通常能产生很好的结果。当我们的数据呈高斯分布时,该算法效果尤其好。当数据有许多特征时,它也能很好地工作,因为这类数据的类别通常以能发挥朴素贝叶斯优势的方式分离(VanderPlas 2016)。在实践中,我们通常会在了解数据集的初期尝试朴素贝叶斯,因为它训练和预测速度快,能让我们对数据的结构有一个初步的感觉。如果预测质量较差,我们可以转向更昂贵的分类器(即需要更多时间或内存的分类器)。

我们在这里看到的算法在实践中经常被使用,尤其是在我们初步了解数据时,因为它们通常很容易应用和可视化。

总结

在这一章中,我们介绍了两种类型的分类器。当一个分类器没有预设的数据结构假设时,我们称之为非参数化。k-最*邻算法属于这一类,它根据样本最常见的邻居来为样本分配类别。决策树也是非参数化的,它根据从训练数据中学习到的一系列决策来分配类别。

另一方面,参数化分类器对数据的结构有一个先入为主的假设。一个基本的支持向量机寻找线性形状,如直线或*面,将训练数据按类别分开。朴素贝叶斯分类器假设数据具有固定的分布,通常是高斯分布,然后尽力将该分布拟合到数据的每个特征上。

在下一章中,我们将看到如何将多个分类器结合起来,产生比单个组件更强大的集成分类器。

第十二章:集成体

任何人都可能犯错误,算法也是如此。有时候,我们可能非常确信我们的算法给出了正确的答案,但由于各种原因,我们可能还是会有一些疑虑。我们如何增加对计算机告诉我们的结果的信任呢?

这并不是一个新问题。1960 年代和 70 年代的阿波罗航天器在指令舱中使用了一种类型的计算机,该指令舱绕月球飞行,而在登月舱中则使用了另一种类型的计算机。计算机在几乎每一个操作中都起着至关重要的作用,因此宇航员必须能够信任它们的输出。这些计算机采用集成电路,而集成电路在当时相对较新。宇航员把他们的生命交给了这些软件和硬件,但始终存在疑虑。他们如何防范可能导致任务失败甚至致命的错误或故障呢?

这些计算机的设计者通过冗余来解决这个问题:每块电路板都被复制了,不止一次,而是两次,共产生了三份副本。三套系统始终保持同步运行,这种技术称为三模冗余。这些计算机接受相同的输入并计算出各自独立的结果。集体的输出由多数投票决定(Ceruzzi 2015)。这样,如果其中任何一个系统发生故障,正确的答案仍然能够出现。

我们可以在机器学习中采用并扩展这个思路。就像阿波罗工程师一样,我们可以创建多个学习器,并同时使用它们。在机器学习中,类似的学习器群体被称为集成体。就像阿波罗计算机一样,集成体的输出是其成员中最受欢迎的结果。但与阿波罗的相同软件和硬件不同,我们使每个学习器具有独特性,通常通过在略有不同的数据上进行训练。这样,一个学习器所犯的错误不太可能以完全相同的方式被其他学习器重复。这种方式下,多数投票帮助我们排除错误的决策。

在第十一章中,我们看到决策树容易对训练数据过拟合,这会导致系统部署后出现错误。在本章中,我们将看到如何将多棵决策树结合成一个集成体。最终结果是一种算法,它继承了决策树的简单性和透明性,但大大减少了它们的问题。我们首先简要讨论一下集成体如何确定最终结果。

投票

对计算机和人类来说,做决策都很困难。在一些人类社会中,我们通过汇聚许多人的意见来处理个体在决策上的不完美。法律由参议院通过,财务决策由董事会做出,个人领导人通过民众投票选举产生。这些情况中的思维方式是,如果我们使用多个独立选民的共识,而不是依赖单一个体的判断,就可以避免那些仅属于某一个人的判断错误。尽管这并不能保证做出正确的决策,但它有时能帮助避免由任何一个人的特异性、偏见或错误判断引发的问题。

机器也有偏见。当我们使用学习算法做决策时,它们的预测是基于它们所训练的数据。如果这些数据包含了偏见、遗漏、代表性不足、过度代表或任何其他形式的系统性错误,这些错误也会被“烙印”在学习者中。这在现实世界中可能产生深远的影响。例如,当我们使用机器学习来评估房贷或商业贷款、决定大学录取、或预筛选求职者时,训练数据中的任何不公*或偏见都会导致系统决策中出现类似的不公*和偏见,过去的错误决策会在现在被重复,并且传播到未来。

减少这些问题影响的一种方法是创建多个使用不同数据集训练的学习者。例如,我们可以用来自不同来源的不同训练集来训练每个系统。由于这种数据在实践中往往难以获得,通常我们会使用从一个公共训练数据池中抽取的不同子集进行训练。

当我们在这些不同的数据集上训练了一堆学习者时,我们通常会让每个学习者评估每一个新的输入。然后,我们让学习者投票以决定最终结果。

实现这一点的典型方式是使用多数投票(RangeVoting.org 2020)。简单来说,每个学习者对其预测投下一个票,获得最多票数的预测就是获胜者(如果出现*局,计算机可以随机选择其中一个*局项,或者再进行一轮投票)。尽管多数投票并不完美,并且存在一些有用的替代方法,但它简单、快速,通常能在机器学习中产生可接受的结果(NCSL 2020)。

一种流行的多数投票变体是加权多数投票。在这种投票中,每一票都有一个特定的权重,即一个数字,表示该票对结果的影响力。另一种变体是要求每个选民表明他们对自己决策的信心,因此更有信心的选民可以比那些不太确定的选民对结果产生更大影响。

在这些术语确定之后,我们现在来深入探讨如何创建一个决策树的集成。

决策树集成

一种建立决策树优势同时减少其缺点的好方法是将它们组合成集成。为了让接下来的讨论更具体,我们集中讨论使用决策树进行分类。

让我们看看三种常见的决策树集成技术,它们能够显著超越各自的单一组件。

Bagging

被称为bagging的集成技术是bootstrap aggregating的合成词。正如名字所示,这种技术基于我们在第二章中看到的引导法思想。在那里,我们看到了如何使用引导法来估计某些统计度量的质量,通过评估从起始数据中抽取的小子集。在这里,我们再次创建许多由训练集构建的小集合,但这次我们将使用它们来训练一组决策树。

从我们原始的训练集样本开始,我们可以通过从原始数据中抽取样本来构建多个新的集合,或引导集,并使用有放回的抽样。这意味着可能会多次抽到同一样本。图 12-1 展示了这个概念。请记住,每个样本都有其指定的类别(通过颜色显示),所以我们可以用它进行训练。

在图 12-1 的中心,我们有一组由八个样本组成的初始集合,这些样本属于五个类别。通过从这个集合中选择样本,我们可以创建许多新的集合,在本例中,每个集合包含四个样本。这是 bagging 的第一步。由于我们是有放回地抽样,因此任何给定的样本可能会出现多次。

现在让我们为每个引导集创建一棵决策树,并用该数据进行训练。我们将这些树的集合称为集成

f12001

图 12-1:从一组样本(中间)创建三个引导集(上面和下面)

当训练完成,我们正在评估一个新样本时,我们将其交给集成中的所有决策树。每棵树都会产生一个类别预测。我们将预测的类别视为在多数选举中的投票,从而产生一个赢家或*局。假设我们有一个只有五棵树的小型集成。图 12-2 展示了在部署后评估新样本的过程,将其分配给四个字母类中的一个。

f12002

图 12-2:使用集成来预测一个样本的类别

看一下图的左侧。最上面,一个未知类别的新样本到达我们的集成。这个样本会被交给我们每一棵决策树(显示为三角形),每棵树都会产生一个预测类别,标记为 A 到 D。由于每棵树都是在不同的引导集上训练的,因此它与其他树有所不同。在图的右侧,我们使用这些预测类别进行多数投票选举。在这个例子中,最流行的类别是 B。这个类别获胜,因此成为集成的输出。

我们只需要指定两个参数来创建这个集成模型:每个自助采样(bootstrap)中应该使用多少样本,以及我们想要构建多少棵树。分析显示,增加更多的分类器会使集成的预测效果更好,但在某个点之后,增加更多的分类器只会让速度变慢,结果不再改善。这被称为集成构建中的收益递减法则。一个好的经验法则是使用与数据类别数量大致相同的分类器数量(Bonab 2016),尽管我们可以使用交叉验证来搜索给定数据集的最佳树木数量。

在讨论袋装方法之前,让我们考虑一些建立在基本思想之上的技术。每种技术的核心思想是在训练过程中为我们的树增加额外的随机性。

随机森林

正如我们在第十一章看到的,当需要将决策树的一个节点分裂成两个时,我们可以选择任何特征(或特征集)来创建测试,将元素引导到一个子节点或另一个子节点。如果我们选择基于单一特征进行分裂,那么我们需要选择想要使用的特征以及该特征的测试值。为了比较不同的测试,我们可以使用第十一章中提到的度量方法,如信息增益或基尼不纯度。

在构建决策树时,我们通常通过考虑每个特征来寻找最佳测试。但我们也可以使用一种叫做特征袋装的技术。在寻找节点的最佳测试之前,我们首先选择该节点样本的特征的一个随机子集,采用无放回选择。现在我们准备好只根据这些特征来寻找最佳测试。我们甚至不考虑基于我们忽略的特征进行的分裂。

后来,当我们决定再次分裂一个节点时,我们再次选择一个全新的特征子集,并仅使用这些特征来确定新的分裂。这个思想在图 12-3 中有所展示。

F12003

图 12-3:通过特征袋装方法确定在分裂节点时使用哪个特征(f1 到 f5),该方法展示了两个节点,每个节点有五个样本

在图 12-3 的左侧,我们从五个可用特征中随机选择三个特征,并在这些特征上搜索最佳特征以进行分裂。在右侧,我们再次选择三种不同的随机特征集,从而获得不同的测试。这里的思路是,通过随机选择少数特征,我们可以避免在每棵训练的树中对该节点做出相同的选择,从而增加决策的多样性。

当我们以这种方式构建集成时,我们称结果为随机森林。名字中的随机部分指的是我们在每个节点上随机选择特征,而森林一词则指的是生成的决策树集合。

为了创建一个随机森林,我们需要提供与袋装法相同的两个参数:每个自助采样的大小和要构建的树的数量。我们还必须指定在每个节点考虑哪些特征的比例。我们可以将其表示为节点中特征数量的百分比。或者,许多库提供了各种算法,能够为我们自动选择该百分比。

极端随机树

让我们来看一下构建集成树时,第二种随机化构建树的方法。通常,当我们分割一个节点时,我们会考虑它包含的每个特征(或者如果我们在构建随机森林,则考虑它们的随机子集),并找到最能将该节点样本分成两个子节点的特征值。正如我们之前提到的,我们通过信息增益等度量来比较不同的可能测试。

我们可以不去找每个特征的最佳切分点,而是基于节点中已有的值随机选择切分点。这一变化的结果是一个叫做极端随机树Extra Trees)的集成方法。

虽然看起来这注定会给我们的决策树带来更差的结果,但请记住,决策树容易过拟合。通过这种随机选择切分点的方法,我们可以在牺牲一些准确度的同时,减少过拟合。

提升法

我们刚刚讨论的技巧都是特定于决策树的。现在,让我们来看一种适用于任何类型学习器的集成构建方法。这种方法叫做提升法(Schapire 2012)。

提升法是一种流行的算法,因为它让我们可以将大量小而快速、不太准确的学习器结合成一个准确的学习器。为了让讨论更加具体,我们继续使用决策树作为例子学习器。为了简化问题,我们专注于二元分类器,这种分类器将每个样本分配到两个类别中的一个。我们将从一些几乎没有用的分类器开始,构建我们的集成。为了启动这个过程,让我们先从一个完全无用的分类器开始进行思考实验,然后稍微改进它。

假设有一个数据集,其中样本来自两个类别。再假设有一个完全随机的二元分类器。无论样本的特征如何,分类器都会任意地将样本分配到这两个类别中的一个。如果训练集中的样本均匀分布,我们有 50:50 的概率正确标记任何一个样本。我们称之为随机标记,因为得到正确答案的概率完全取决于运气。

假设我们可以稍微调整我们的二元分类器,使它比随机预测稍微好一点。例如,图 12-4 展示了一个由两个类别组成的数据集,一个与随机预测一样的二元分类器,以及一个稍微比随机预测好一点的二元分类器。

图 12-4(b)中的学习器与随机分类器没有区别,两个类别中的一半都被错误分类。这是一个无用的分类器。部分(c)中的学习器比部分(b)中的分类器稍微好一些,因为边界线的轻微倾斜使得它的表现比无用的分类器稍微好一些。

F12004

图 12-4:(a)我们的训练数据。(b)一个随机分类器。(c)一个糟糕的,但不完全随机的分类器。

我们把图 12-4(c)中的分类器称为弱学习器。在这种情况下,弱学习器是指任何即使只有一点点准确性的分类器。也就是说,它在超过 50% 的时间里分配正确的类别,尽管可能只是稍微超过。提升的魅力在于,我们可以将这个弱学习器作为一个集成的一部分,从而产生非常好的结果。

事实上,即使一个弱二元分类器的表现比随机情况还要,它对我们来说依然非常有用。这是因为我们只有两类。如果一个分类器的表现低于随机水*(即它分配错误类别的频率高于正确类别),我们可以通过交换输出的类别,使它的表现变得比随机情况还要好,而不是更差。结论是,只要一个二元分类器不是完全随机的,我们就能够利用它。

弱分类器很容易构造。或许最常见的弱分类器是一个深度仅为 1 的决策树。也就是说,整个树由一个根节点和它的两个子节点组成。这个极小的决策树通常被称为决策树桩。因为它几乎总是比随机分配类别要做得好,所以它是弱分类器的一个典型例子。它小,快,而且比随机分类稍微好一些。

与弱学习器相对的是强学习器,它大多数时候都能正确地标记类别。学习器越强,它正确分类的百分比就越高。

提升算法的核心思想是将多个弱分类器组合成一个集成分类器,从而表现得像一个强分类器。请注意,我们的弱分类器条件只是一个最低标准。尽管我们可以组合许多强分类器,但使用弱分类器更为常见,因为它们通常更快。

让我们通过一个例子来看一下提升算法是如何工作的。图 12-5 展示了一组属于两种不同类别的训练样本。

那么,哪些可能是适合这种数据的优秀分类器呢?一个快速且简单的分类器只需在二维数据集上画一条直线。我们可以看到,任何直线都无法分开这些数据,因为圆形样本在三侧围绕着方形样本。

F12005

图 12-5:我们将通过提升算法分类的样本集合

尽管没有一条直线可以分开这些数据,但我们将看到,多个直线是可以做到的,因此我们将使用直线作为我们的弱分类器。图 12-6 展示了一个这样的分类器的边界线。我们将使用 A 作为分类器和它所定义的边界线的名称。

F12006

图 12-6:将一条名为 A 的直线放入我们的样本中,切割开两个大的聚类。

在这个分类器中,箭头指向的 A 侧的所有内容被分类为正方形,另一侧的所有内容被分类为圆形。使用我们在第三章中定义的准确度度量,我们发现这个学习者的准确度为 (TP + TN) / (TP + TN + FP + FN) = (12 + 8) / (12 + 8 + 12 + 2) = 20 / 34,约为 59%。这是一个弱学习者的不错例子:它比随机猜测(50%)要好,但也好不了多少。

为了使用提升方法,我们需要添加更多的直线(即额外的弱学习者),以便最终每个由直线形成的区域都包含单一类别的样本。添加了另外两条这样的直线分类器后,我们得到了图 12-7。

直线 B 的准确度约为 73%。直线 C 非常糟糕,准确度仅约为 12%。但正如我们之前所提到的,这没关系,因为如果我们仅仅交换 C 所分配的标签(即,只需将箭头指向另一方向),它的准确度约为 88%!

图 12-7 中的三条直线或边界共同创建了七个不重叠的区域。图中还显示,每个区域只包含一个类别的样本。通过观察这些区域,我们可以看到只通过三个分类器的输出就能确定一个样本的类别。

F12007

图 12-7:在图 12-6 中添加了两条直线

让我们将这三条边界线一起画出来。我们将用学习者的名称标记每个区域。结果如图 12-8 所示。

F12008

图 12-8:左侧展示了名为 A、B 和 C 的三条直线。右侧,每个区域都标记有使该区域位于各自直线正面侧的学习者的名称。

当我们的分类器接收到一个新样本时,通常它们每个都会返回一个类别。相反,让我们设置它们,当样本位于分类器边界的正面时返回 1(即图 12-8 中箭头指向的那一侧),否则返回 0。

现在我们可以在每个单元格中加总来自三个学习者的所有贡献。

例如,考虑图 12-8 中标记为 C 的区域。它位于学习器 C 的正侧,因此得分为 1。它位于 A 和 B 的负侧,因此 A 和 B 都贡献 0,因此三者输出的总和为 1。底部标记为 AB 的区域,A 和 B 各贡献 1,C 贡献 0,总和为 2。这些得分与其他区域一起显示在图 12-9 中。

F12009

图 12-9:每个区域的合成得分。图 12-8 中的每个字母为该区域带来 1 分。

我们几乎已经创建好了新的分类器。接下来有两个步骤。首先,我们将每个分类器输出中我们随意分配的 1 替换为一个更有用的值。其次,我们找到一个阈值,将每个区域的总值转换为一个类别。

回想一下我们之前在本章中讨论的加权多数投票。如果某个区域位于某条线的正侧,那么该线正在为该区域投票。我们可以为每个分类器分配一个投票权重,而不仅仅是从每个分类器那里加上 1。例如,如果分类器 A、B 和 C 的投票权重分别为 2、3 和-4,而一个点位于 A 和 C 的正侧,但不在 B 的正侧,那么 A 贡献 2,B 贡献 0(因为该点位于 B 线的负侧),C 贡献-4,总和为 2 + 0 + -4 = -2。

每个分类器的投票权重是由提升算法为我们计算的。我们不打算详细讨论这些机制,而是通过可视化一组特定的权重结果来观察它们对数据集的影响。

在图 12-10 中,我们展示了每个学习器的得分所影响的区域。深色区域获得该学习器的值,而浅色区域则没有(因此浅色区域内的学习器值为 0)。在这里,我们为 A、B 和 C 使用了权重 1.0、1.5 和-2。回想一下,C 线指向的是“错误”的方向。给 C 的权重赋负值的效果是颠倒了分类器 C 的决策。

F12010

图 12-10:我们为每个被每个学习器分类为正的区域分配一个数值。每条线的深色区域会得到与该线相关联的权重。

所有这些得分的总和显示在图 12-11 中。蓝色区域的总和为正,红色区域的总和为负。这些正好对应于我们数据集中圆形和方形的分布。任何位于正区域的样本都是方形的,任何位于负区域的样本都是圆形的。

F12011

图 12-11:汇总图 12-10 中的得分

当一个样本到来时,我们将其发送到每个分类器(即,我们将其与相应的线进行测试)。对于每个分类器,如果该分类器认为样本位于其线的正侧,我们就将其投票权重贡献给一个累加和。在加总所有分类器的输出后,我们确定样本是正类还是负类,这样我们就能知道样本属于哪个类别。我们已经正确地对数据进行了分类!

让我们看一个新的例子。图 12-12 展示了一组新的数据。

F12012

图 12-12:我们希望使用提升法分类的一组数据

对于这个数据,我们尝试使用四个学习器。图 12-13 展示了一个提升算法可能会找到的四个弱学习器,用于对该数据进行划分。

F12013

图 12-13:四条线让我们能够对图 12-12 的数据进行分类

如前所述,算法还会为这些学习器分配权重。我们通过为学习器 A、B、C 和 D 分别赋予权重 −8、2、3 和 4 来展示结果。图 12-14 展示了哪些区域的总体得分增加了这些权重。浅色区域隐式地接收到 0 的值。

F12014

图 12-14:与每个学习器对应的区域

图 12-15 展示了每个区域贡献的总和。同样,正总和或负总和区分了这两种类型的区域。我们已经找到了一个方法,通过组合四个弱学习器来正确分类图 12-13 中的点。

提升的美妙之处在于,它将简单且快速但效果差的分类器,通过为它们找到权重,转化为一个单一的强大分类器。

F12015

图 12-15:来自图 12-14 的每个区域得分的总和。正区域用蓝色显示,它们正确地对图 12-13 中的点进行了分类。

我们需要提供的唯一超参数是我们想要多少个分类器。在提升法中,像在袋装法中一样,一个好的经验法则是从与类别数量大致相等的分类器开始(Bonab 2016)。这意味着我们之前的例子使用了三个或四个分类器来分类两个类别,因此起步时较高。但正如在许多机器学习任务中一样,最佳值是通过试验和错误找到的。

提升法首次出现是在一个名为Adaboost的算法中(Freund 1997;Schapire 2013)。尽管它可以与任何学习算法配合使用,但提升法在决策树中尤为流行。事实上,它在我们之前提到的决策树桩上表现得非常好(这些树只有一个根节点和两个直接子节点)。我们可以把我们在图 12-7 和 12-13 中使用的线看作决策树桩,因为它们只有一个测试:样本是否在箭头指向的线的正侧?还是不在?

值得注意的是,boosting 并不是提高所有分类算法的万灵药。boosting 的理论仅适用于二分类问题,如我们之前的示例所示(Fumera 2008;Kak 2016)。这也是为什么 boosting 在决策树分类器中如此流行和成功的部分原因。

总结

集成方法是多种多样的学习器的集合。基本思路是,我们收集多个类型相似但训练数据不同的学习器,让它们一起评估输入。然后,让它们投票决定各自认为的类别,获得最多票数的类别将作为该输入的最终分类。其背后的思考是,个别学习器的错误会被其他学习器达成一致的分类结果“投票”消除。

Boosting 是一种将多个弱学习器组合成一个强学习器的方式。

本讨论总结了我们关于机器学习技术的讨论。从第十三章开始,我们将讨论深度学习算法背后的神经网络。我们会看到,我们在这里介绍的方法对深度学习有帮助,因为它们帮助我们理解数据,并做出最佳的算法和网络选择,以处理这些数据,并产生对我们有用的结果。

第三部分

深度学习基础

第十三章:神经网络

深度学习算法基于建立一个连接的计算单元网络。这些网络的基本单元是一个小型计算捆绑体,称为人工神经元,尽管它通常简称为神经元。人工神经元的灵感来自于人类神经元,神经元是构成我们大脑和中枢神经系统的神经细胞,主要负责我们的认知能力。

本章中,我们将展示人工神经元的样子,并讨论如何将它们安排成网络。接着,我们将它们分组为层,从而形成深度学习网络。我们还会探讨如何配置这些人工神经元的输出,以便它们产生最有用的结果。

真实神经元

在生物学中,神经元一词适用于分布在人类身体各处的多种复杂细胞。这些细胞都具有相似的结构和行为,但它们被专门化以执行许多不同的任务。神经元是复杂的生物体,利用化学、物理、电学、时间、接*性等多种方式来执行其行为并相互通信(Julien 2011;Khanna 2018;Lodish 等人 2000;Purves 等人 2001)。图 13-1 显示了神经元的一个高度简化的示意图。

F13001

图 13-1:一个高度简化的生物神经元示意图(红色),标出了几个主要结构。该神经元的输出信号传递给另一个神经元(蓝色),后者仅部分显示(改编自 Wikipedia 2020b)。

神经元是信息处理机器。信息的一种类型是通过名为神经递质的化学物质传递,这些化学物质暂时结合或附着在神经元上的受体位点(Goldberg 2015)。让我们概述一下接下来会发生什么。

结合到受体位点的化学物质会导致电信号进入神经元的主体。每个信号可能是正电或负电。在短时间内到达神经元主体的所有电信号会被加在一起,然后与阈值进行比较。如果总和超过该阈值,就会沿着轴突发送一个新信号到神经元的另一部分,导致一定量的神经递质释放到环境中。这些分子随后会与其他神经元结合,过程重复进行。

以这种方式,信息在大脑和中枢神经系统中通过神经元的密集连接网络传播并被修改。如果两个神经元之间足够接*,以至于一个可以接收到另一个释放的神经递质,我们就说这些神经元是连接的,即使它们可能并没有实际接触。有一些证据表明,神经元之间的特定连接模式对认知和身份的形成至关重要,甚至和神经元本身一样重要(Sporns、Tononi 和 Kötter 2005;Seung 2013)。一个个体神经元连接的地图被称为连接组。连接组和指纹或虹膜图案一样独特。

尽管真实的神经元及其周围环境极其复杂和微妙,但这里描述的基本机制却具有一种迷人的优雅。对此,一些科学家尝试通过创建大量简化的神经元及其环境,使用硬件或软件来模仿或复制大脑,希望能出现有趣的行为(Furber 2012;Timmer 2014)。到目前为止,这还没有产生大多数人认为是智能的结果。

但是,我们可以以特定的方式连接简化的神经元,从而在广泛的问题上产生显著的结果。这些结构将是本章和本书其余部分的重点。

人工神经元

我们在机器学习中使用的“神经元”受真实神经元的启发,就像木偶画是受人类身体启发一样。它们有相似之处,但仅仅是最一般的层面。几乎所有的细节在这个过程中都丢失了,最终我们得到的更多的是对原始对象的提醒,而不是一个简化的复制品。

这导致了一些混淆,特别是在大众媒体中,“神经网络”有时被用作“电子大脑”的同义词,从而只需一步之遥就能联想到通用智能、意识、情感,甚至可能是世界统治和消灭人类生命。实际上,我们使用的神经元与真实神经元的抽象和简化程度如此之大,以至于许多人更愿意将它们称为更通用的单元。但无论好坏,神经元这个词、神经网络这个短语以及所有相关的语言显然将会存在,因此我们在本书中也使用它们。

感知机

人工神经元的历史可以追溯到 1943 年,当时发表了一篇论文,呈现了神经元基本功能的一个大幅简化的数学抽象形式,并描述了如何将该对象的多个实例连接成一个网络。这篇论文的重要贡献在于,它从数学上证明了这样的网络可以实现任何在数学逻辑语言中表达的想法(McCulloch 和 Pitts 1943)。由于数学逻辑是机器计算的基础,这意味着神经元可以执行数学运算。这是一个重大突破,因为它为数学、逻辑、计算机学和神经生物学这几个领域搭建了桥梁。

在此基础上,1957 年提出了感知器作为神经元的简化数学模型(Rosenblatt 1962)。图 13-2 是一个具有四个输入的单个感知器的框图。

f13002

图 13-2:一个四输入感知器

每个输入到感知器的数据都由一个浮动的浮点数表示。每个输入都会乘以一个对应的浮动浮点数,称为权重。这些乘积的结果都会加在一起。最后,我们将结果与一个阈值进行比较。如果求和结果大于 0,感知器输出 +1,否则输出 −1(在某些版本中,输出为 1 和 0,而不是 +1 和 −1)。

尽管感知器是一个大幅简化的真实神经元版本,但它已经证明是深度学习系统的一个出色构建块。

感知器的历史是机器学习文化中的一个有趣部分,我们来看看其中的几个关键事件;更完整的版本可以在线找到(Estebon 1997;Wikipedia 2020a)。

在感知器原理通过软件验证后,1958 年康奈尔大学建造了一个基于感知器的计算机。它是一个大小如冰箱的线圈板架,名为 Mark I Perceptron(Wikipedia 2020c)。该设备被设计用来处理图像,使用一个 400 个光电池的网格,能够以 20×20 像素的分辨率对图像进行数字化(当时还没有“像素”这个词)。施加在感知器每个输入上的权重是通过转动一个控制电气元件——电位计的旋钮来设置的。为了自动化学习过程,电动机被连接到电位计上,这样设备就可以通过自转旋钮调整权重,从而改变其计算,进而改变其输出。理论上保证,使用正确的数据,系统能够学习将两类不同的输入分开,这些输入能够通过一条直线来划分。

不幸的是,并不是很多有趣的问题涉及被一条直线分隔的数据集,而且将这一技术推广到更复杂的数据排列中也证明很困难。在几年的停滞不前之后,一本书证明了原始感知机技术在理论上存在局限性(Minsky 和 Papert 1969)。它显示出进展停滞并不是因为缺乏想象力,而是感知机结构中固有的理论限制。大多数有趣的问题,甚至一些非常简单的问题,都证明超出了感知机的解决能力。

这一结果似乎标志着许多人对感知机的看法的结束,并形成了一个流行的共识:感知机方法是一条死胡同。热情、兴趣和资金都枯竭,大多数人将他们的研究方向转向了其他问题。这个时期大约从 1970 年代到 1990 年代,被称为人工智能寒冬

尽管普遍的解释认为感知机的书籍已经为感知机的研究关上了大门,但事实上,它只显示了到那时为止感知机使用的局限性。有些人认为放弃整个想法是过度反应,或许通过不同的方式应用感知机仍然可以是一种有用的工具。这一观点最终在十几年后结出了果实,当时研究人员将感知机组合成更大的结构,并展示了如何训练它们(Rumelhart, Hinton, 和 Williams 1986)。这些组合轻松超越了任何单一单元的局限性。随后一系列论文显示,通过精心安排多个感知机,并增加一些小的改动,可以解决复杂且有趣的问题。

这一发现重新激发了人们对该领域的兴趣,很快感知机的研究再次成为热门话题,产生了一系列有趣的成果,这些成果最终发展成我们今天使用的深度学习系统。感知机仍然是许多现代深度学习系统的核心组成部分。

现代人工神经元

我们在现代神经网络中使用的神经元仅在原始感知机的基础上稍作了推广。这里有两个变化:一个发生在输入端,另一个发生在输出端。这些修改后的结构仍然有时被称为感知机,但通常不会产生混淆,因为新的版本几乎专门使用。更常见的是,它们被称为神经元。让我们来看看这两个变化。

对图 13-2 中感知机的第一个改动是为每个神经元提供一个额外的输入,我们称之为偏置。这是一个不来自前一个神经元输出的数值。相反,它是直接加到所有加权输入总和中的数值。每个神经元都有自己的偏置。图 13-3 展示了我们原始的感知机,但包括了偏置项。

F13003

图 13-3:图 13-2 中的感知器,但现在加入了偏置项。

我们对图 13-2 中的感知器进行了第二次修改,修改发生在输出部分。该图中的感知器将总和与阈值 0 进行比较,然后输出−1 或 1(或 0 或 1)。我们通过用一个数学函数替换测试步骤来进行概括,该函数将总和(包括偏置)作为输入,并返回一个新的浮动点值作为输出。由于真实神经元的输出被称为其激活,因此我们将这个计算人工神经元输出的函数称为激活函数。在图 13-2 中显示的小测试是一个激活函数,但现在已经很少使用了。稍后在本章中,我们将回顾一些在实践中证明既受欢迎又实用的激活函数。

绘制神经元

让我们定义一个大多数人工神经元图示所使用的约定。在图 13-3 中,我们显式地显示了权重,并且还包括了乘法步骤,以展示权重是如何乘以输入的。这在页面上占据了大量空间。当我们绘制包含大量神经元的图时,所有这些细节会使得图形显得拥挤和密集。因此,在几乎所有的神经网络图示中,权重及其乘法步骤都是隐含的。

这一点非常重要,需要重复强调:在神经网络图示中,权重及其乘法步骤不会被绘制出来。相反,我们应该知道它们存在并在心里将其包含在图示中。如果我们显示权重,通常会标注从输入到权重的线。图 13-4 展示了图 13-3 这种风格的绘制方式。

F13004

图 13-4:神经元通常会在箭头上标出权重。

在图 13-4 中,我们还将末尾的阈值测试改为一个小图形。这是一个名为阶跃的函数的图示,目的是给我们一个视觉提示,任何激活函数都可以放在这个位置。基本上,一个数字进入这个阶跃,新的数字输出,具体由我们为此任务选择的函数决定。

我们通常会再次简化问题。这一次,我们通过假装偏置是其中一个输入来省略它。这不仅使得图示更简洁,同时也让数学更简单,而在这种情况下,也能导致更高效的算法。这个简化方法被称为偏置技巧("技巧"这个词来源于数学,通常用于形容一些巧妙的简化问题的方法)。我们并不改变偏置的值,而是将偏置的值固定为 1,并在与其他输入求和之前,改变应用到它的权重。图 13-5 展示了这一标签的变化。虽然偏置项的值始终为 1,且只有它的权重会发生变化,但我们通常忽略这一区分,仅讨论偏置的值。

F13005

图 13-5:偏置技巧的实际应用。与图 13-4 中显式显示偏置项不同,我们假装偏置是另一个输入,并为其赋予自己的权重。

我们希望我们的人工神经元图示尽可能简洁,因为当我们开始构建网络时,我们会同时展示大量的神经元,因此大多数图示都会进行两个额外的简化步骤。首先,它们完全不显示偏置。我们应该记得偏置是包含在内的(以及它的权重),但它不会显示出来。其次,权重通常也会被省略,就像在图 13-6 中那样。这有点遗憾,因为权重是我们在神经元中最重要的部分。之所以如此,是因为它们是我们在训练过程中唯一能够改变的部分。尽管在大多数图示中没有显示出来,但它们非常关键,我们再次强调这一核心观点:即使我们没有显式地显示权重,权重始终存在。

F13006

图 13-6:典型的人工神经元图示。虽然偏置项和权重未显示,但它们显然是存在的。

像真实神经元一样,人工神经元可以被连接成网络,其中每个输入来自另一个神经元的输出。当我们将神经元连接成网络时,我们画上“连接线”将一个神经元的输出连接到一个或多个其他神经元的输入。图 13-7 直观地展示了这一概念。

F13007

图 13-7:一个更大人工神经元网络的一部分。每个神经元的输入来自其他神经元的输出。虚线表示与该小群体之外的连接。

这就是神经网络。通常,像图 13-7 这样的网络的目标是产生一个或多个输出值。稍后我们将看到如何以有意义的方式解读输出的数字。

即使我们已经说过通常不绘制权重,但在讨论中,有时提到单个权重是有用的。让我们来看一下一个常见的权重命名约定。图 13-8 显示了六个神经元。为了方便,我们用字母标记了每个神经元。每个权重对应的是一个特定神经元的输出在传递到另一个神经元时的变化。图中每一条连接线表示这种联系。为了命名一个权重,我们将输出神经元的名字和输入神经元的名字组合起来。例如,乘以 A 的输出并由 D 使用的权重叫做 AD。

F13008

图 13-8:权重是通过组合输出神经元和输入神经元的名称来命名的。

从结构的角度来看,无论我们将权重绘制在每个神经元内部,还是绘制在传递值到神经元的电线之上,其实并没有区别。如果为了便于讨论,某些作者可能会选择其中一种方式,但如果需要,我们也可以选择另一种视角。

在图 13-8 中,我们将从神经元 A 到神经元 D 的权重命名为 AD。有些作者会将其反过来写成 DA,因为这种方式更直接地对应我们通常写方程的方式。在查看类似的图示时,花一点时间确认使用的顺序总是值得的。

前馈网络

图 13-7 展示了一个没有明显结构的神经网络。深度学习的一个关键特点是我们将神经元排列成。通常,每一层的神经元仅从上一层获取输入,并且只将输出传递给下一层,且神经元之间不与同一层的其他神经元进行通信(当然,像往常一样,这些规则也有例外)。

这种组织方式允许我们分阶段地处理数据,每一层的神经元都基于前一阶段完成的工作来进行处理。通过类比,可以考虑一栋有多层的办公楼。任何一层的人只会从楼下那一层的人那里获得工作,并且只将工作交给楼上那一层的人。在这个类比中,每一层就是一个层级,楼层上的人就是该层的神经元。

我们说这种类型的结构处理数据是分层的。有一些证据表明,人类大脑在处理某些任务时是分层组织的,包括处理感官数据如视觉和听觉(Meunier 等,2009;Serre,2014)。但在这里,我们的计算机模型和真实生物学的关系更多的是灵感上的借鉴,而不是模仿。

很难相信,将神经元连接成一系列层次能够产生有用的东西。正如我们之前看到的,一个单一的人工神经元几乎做不了什么。它接受一堆数字输入,对其加权,求和,然后通过一个小函数传递结果。这个过程能够识别出一条将几堆数据分开的直线,仅此而已。但如果我们将成千上万的这些小单元组合成层,并使用一些巧妙的想法来训练它们,那么它们在一起工作时就能识别语音、识别人脸,甚至在逻辑和技能游戏中击败人类。

关键在于组织。随着时间的推移,人们开发了多种方式来组织神经元的层级,形成了常见的层次结构。最常见的网络结构将神经元排列成仅允许信息朝一个方向流动的方式。我们称这种结构为前馈网络,因为数据是向前流动的,较早的神经元将值传递给较晚的神经元。设计深度学习系统的艺术在于选择合适的层级顺序和超参数,以构建基本架构。为了为任何给定的应用构建一个有用的架构,我们需要理解神经元之间是如何相互关联的。接下来,让我们看看神经元集合是如何进行通信的,以及如何在学习开始之前设置初始权重。

神经网络图

我们通常将神经网络表示为。图的研究非常广泛,以至于它被认为是一个独立的数学领域,叫做图论(Trudeau 1994)。在这里,我们将坚持图的基本概念,因为这就是我们组织神经网络所需的全部内容。尽管我们知道通常会处理层级,但让我们先从一些一般性的图开始,例如在图 13-9 中所示的那些。

一个图由节点(也叫顶点元素)组成,这里用圆圈表示。在本书中,节点通常是神经元,并且在整本书中,我们偶尔会将像这样的网络中的一个或多个神经元称为节点。节点之间通过箭头连接,箭头被称为(也叫电缆,或者简单地叫线)。当图中的信息流方向一致时,箭头的箭头头通常省略,这通常是从左到右或从下到上的方向。信息沿着边缘流动,将一个节点的输出传递给其他节点的输入。由于每条边上的信息流动仅有一个方向,我们有时将这种图称为有向图

一般来说,我们是通过将数据放入输入节点或节点群开始的,然后它通过边缘流动,访问那些在其中被转换或改变的节点,直到它到达输出节点或节点群。一旦数据离开一个节点,就再也不会返回到那个节点。换句话说,信息只会向前流动,并且没有环路或循环。这种图形就像一个小工厂。原材料从一端进入,经过机器的处理和组合,最终在另一端产生一个或多个成品。

F13009

图 13-9:两个作为图形绘制的神经网络。数据沿着边缘从节点流向节点,跟随箭头。当边缘没有标注箭头时,数据通常是从左到右或从下到上流动的。(a)主要是从左到右的流动。(b)主要是从下到上的流动。

我们说在图 13-9(a)中,靠*输入的节点是靠*输出的节点之前的,后者在它之后。在图 13-9(b)中,我们会说,靠*输入的节点是靠*输出的节点下方,后者它的上方。有时即使图形是从左到右绘制的,这种下/上的说法也会使用,这可能会造成困惑。可以将下方理解为“离输入更*”,上方理解为“离输出更*”。

我们有时也会说,如果数据从一个节点流向另一个节点(假设它从 A 流向 B),那么节点 A 是 B 的祖先父节点,而节点 B 是 A 的后代子节点

神经网络中的一个常见规则是没有环路。这意味着从一个节点出来的数据永远不能回到该节点,无论它走的路径有多么曲折。此类图形的正式名称是有向无环图(或DAG,发音与“drag”押韵)。这里的有向意味着边缘有箭头(如我们之前提到的,箭头可能只是隐含的)。无环意味着没有循环或环路。像往常一样,规则也有例外,但它们很少见。当我们在第十九章讨论循环神经网络(RNNs)时,我们将看到一个这样的例外。

DAG 在许多领域都很受欢迎,包括机器学习,因为它们比包含环路的任意图形更容易理解、分析和设计。包括环路可能会引入反馈,即一个节点的输出被返回到它的输入。任何将活麦克风移得太靠*扬声器的人都知道反馈如何迅速失控。DAG 的无环特性自然避免了反馈问题,这让我们避免了处理这个复杂问题。

回想一下,数据仅从输入流向输出的图或网络被称为 前馈。在第十四章中,我们将看到训练神经网络的关键步骤之一是暂时反转箭头,将一种特定类型的信息从输出节点传回输入节点。尽管数据的正常流动仍然是前馈的,但当我们将数据反向推送时,通常我们称其为 反馈反向流动反向前馈 算法。我们将“反馈”这个词保留给图中的环路,环路可以使一个节点将其自身的输出作为输入。正如我们所说的,我们通常避免在神经网络中使用反馈。

解读像图 13-9 中的图表通常意味着想象信息沿着边缘流动,从一个节点流向下一个节点。但这个图像只有在我们做出一些常规假设的前提下才有意义。现在让我们来看看这些假设。

尽管我们在描述数据如何在图中流动时经常使用“流动”这个词的各种形式,但这与水流通过管道不同。水流通过管道是一个 连续 的过程:水分子在每一时刻都会通过管道流动。我们所使用的图(以及它们代表的神经网络)是 离散 的:信息是逐块到达的,就像文本消息一样。

回想一下图 13-5,我们可以通过在每条边上放置一个权重(而不是在神经元内部)来绘制神经网络。我们将这种风格的网络称为 加权图。正如我们在图 13-6 中看到的,我们很少显式地绘制权重,但它们是隐含的。无论如何,在任何神经网络图中,即使没有显式显示权重,我们也应该理解,每条边上都有一个唯一的权重,并且当一个值沿着这条边从一个神经元传到另一个神经元时,该值会被权重乘以。

初始化权重

教授神经网络涉及逐步改进权重。这个过程从我们为权重分配初始值开始。我们应该如何选择这些起始值?事实证明,在实践中,初始化权重的方式可能会对我们网络学习的速度产生很大的影响。

研究人员已经开发出了一些理论,提出了选择权重初始值的有效方法,且已被证明最有用的各种算法都以描述这些算法的论文中主要作者的名字命名。LeCun UniformGlorot Uniform(或 Xavier Uniform)和 He Uniform 算法都是基于从均匀分布中选择初始值(LeCun 等,1998;Glorot 和 Bengio,2010;He 等,2015)。可能并不令人惊讶的是,命名相似的 LeCun NormalGlorot Normal(或 Xavier Normal)和 He Normal 初始化方法则是从正态分布中选择它们的值。

我们不需要深入了解这些算法背后的数学原理。幸运的是,现代深度学习库提供了这些方案及其变种。通常,库默认使用的技术已经很好地工作,因此我们很少需要显式地选择如何初始化权重。

深度网络

在组织神经元的多种方式中,将它们放置在一系列层中已被证明既灵活又极具威力。通常,一个层内的神经元彼此之间不直接连接。它们的输入来自前一层,输出则传递给下一层。

实际上,深度学习这个术语正来源于这种结构。如果我们将许多层并排绘制,我们可能会称这个网络为“宽”。如果它们是垂直排列的,而我们站在底部向上看,我们可能会称之为“高”。如果我们站在顶部往下看,我们可能会称之为“深”。这就是深度学习的全部含义:一个由一系列层构成的网络,我们通常会将其垂直绘制。

将神经元按层组织的结果是,我们可以分层次地分析数据。早期的层处理原始输入数据,而每个后续的层能够利用前一层神经元的信息来处理更大块的数据。例如,考虑一张照片,第一层通常查看单个像素。下一层查看像素的组合,再下一层查看那些组合的组合,依此类推。早期的层可能会注意到某些像素比其他像素更暗,而后来的层可能会注意到一簇像素像一个眼睛,甚至更远的层可能会注意到形状的集合,从而揭示整个图像展示了一只老虎。

图 13-10 展示了一个使用三层的深度学习架构示例。

F13010

图 13-10:深度学习网络

当我们将层垂直绘制时,如同在图 13-10 中那样,输入几乎总是绘制在底部,而我们收集结果的输出则几乎总是绘制在顶部。

在图 13-10 中,所有三个层都包含神经元。在实际系统中,我们通常会使用许多其他类型的层,通常将这些层归类为支撑层。在后续章节中,我们将看到许多这样的层。当我们计算一个网络中的层数时,通常不会计算这些支撑层。图 13-10 会被描述为一个由三层构成的深度网络。

最顶层包含神经元的层(在图 13-10 中的第 3 层)被称为输出层

我们可能会认为图 13-10 中的第 1 层应该被称为输入层,但事实并非如此。在术语上的一个小巧妙用法中,输入层是指网络的底部,即图 13-10 中标为“输入”的部分。在这个“层”中并没有进行任何处理。它只是存放输入值的内存。输入层是一个支持层的例子,因为它没有神经元,因此在计算网络层数时不会被包括在内。我们计算的层数被称为网络的深度

如果我们想象站在图 13-10 的顶部往下看,我们只能看到输出层。如果我们想象站在底部往上看,我们只能看到输入层。介于两者之间的层对我们来说是不可见的。每一层输入和输出之间的层被称为隐藏层

有时堆叠图是从左到右绘制的,如图 13-11 所示。

F13011

图 13-11:图 13-10 的相同深度网络,但数据从左到右流动。

即使以这种方式绘制,我们仍然使用指代垂直方向的术语。作者可能会说第二层“在”第一层“上方”,而且“在”第三层“下方”。无论图示如何绘制,只要我们将“上方”或“更高”理解为接*输出层的层,将“下方”或“更低”理解为接*输入层的层,我们总能保持清晰。

全连接层

全连接层(也称为FC线性层密集层)是一组神经元,每个神经元都接收来自上一层每个神经元的输入。例如,如果在一个密集层中有三个神经元,而前一层有四个神经元,那么密集层中的每个神经元都有四个输入,每个来自前一层的神经元,总共有 3 × 4 = 12 个连接,每个连接都有一个相关的权重。

图 13-12(a)展示了一个有三个神经元的全连接层图示,它位于一个有四个神经元的层之后。

F13012

图 13-12:全连接层。(a) 彩色神经元组成了全连接层。这一层中的每个神经元都接收来自前一层每个神经元的输入。(b) 我们为全连接层设计的简化符号。

图 13-12(b)展示了我们将用于全连接层的简化符号。这个符号的思想是,两个神经元位于符号的顶部和底部,垂直和对角线表示它们之间的四个连接。在符号旁边,我们标识出该层中有多少个神经元,就像这里的数字 3 一样。当它与层的激活函数相关时,我们也会在这里标识。如果一个层只由密集层组成,它有时被称为全连接网络,或者回溯到早期的术语,称为多层感知机MLP)。

在后面的章节中,我们将看到许多其他类型的层,它们帮助我们以有用的方式组织神经元。例如,卷积层池化层已被证明对于图像处理任务非常有用,我们将给予它们很多关注。

张量

我们已经看到,深度学习系统是由一系列层构成的。尽管任何神经元的输出是一个单一的数字,但我们通常想要一次性讨论整个层的输出。表征这一组输出数字的关键概念是其形状。让我们看看这意味着什么。

如果某一层包含一个神经元,那么该层的输出就是一个单一的数字。我们可以将其描述为一个包含一个元素的数组或列表。从数学上讲,我们可以称其为零维数组。数组的维度数量告诉我们需要多少个索引来识别一个元素。由于单个数字不需要索引,因此该数组是零维的。

如果该层有多个神经元,那么我们可以将它们的集合输出描述为一个包含所有值的列表。由于我们需要一个索引来识别该列表中特定的输出值,因此这是一个一维(1D)数组。图 13-13(a)展示了包含 12 个元素的这种数组。

F13013

图 13-13:三个张量,每个包含 12 个元素。(a) 一维张量是一个列表。(b) 二维张量是一个网格。(c) 三维张量是一个体积。在所有情况下,以及更高维度的情况中,都没有空洞,元素也没有突出于块之外。

我们经常将数据组织成其他类似盒子的形状。例如,如果系统的输入是黑白图像,它可以表示为二维数组,如图 13-13(b)所示,按 x 和 y 位置进行索引。如果是彩色图像,则可以表示为三维数组,按 x 位置、y 位置和颜色通道进行索引。图 13-13(c)展示了一个三维形状。

我们经常将一维形状称为数组列表向量。要描述二维形状,我们通常使用网格矩阵这两个术语,而我们可以将三维形状描述为体积。我们经常使用更高维的数组。为了避免创建大量新的术语,我们使用一个术语来表示任何以盒子形状排列的、具有任意维度的数字集合:张量(发音:ten′-sir)。

张量只是一个具有给定维度和每个维度大小的数字块。它没有空洞,也没有部分突出。张量这个术语在某些数学和物理学领域有更复杂的含义,但在机器学习中,我们用这个词来表示一个组织成多维块的数字集合。综合来看,维度的数量和每个维度的大小提供了张量的形状

我们通常称网络的输入张量(指所有输入值)和输出张量(指所有输出值)。内部(或隐藏)层的输出没有特定名称,因此我们通常会说类似“第 3 层产生的张量”来指代从第 3 层神经元输出的多维数组。

防止网络坍缩

之前我们承诺要回到激活函数的问题。现在让我们来看一下它们。

每个激活函数,尽管是整体结构中的一个小部分,但对于成功的神经网络至关重要。如果没有激活函数,网络中的神经元会结合或坍缩成一个等效的单一神经元。正如我们之前看到的,一个神经元的计算能力非常有限。

让我们来看一下没有激活函数的网络是如何坍缩的。图 13-14 展示了一个包含两个输入(A 和 B)和五个神经元(E 到 G)的简单网络,共三层。每个神经元接收来自前一层每个神经元的输入,每个连接都有一个权重,总共有十个权重,如红色所示。

F13014

图 13-14:一个包含两个输入、五个神经元和十个权重的小网络

假设暂时这些神经元没有激活函数。那么我们可以将每个神经元的输出写成其输入的加权和,如图 13-15 所示。在这个图中,我们采用了数学惯例,在可能的情况下省略乘号,因此 2A 代表 2 × A。

F13015

图 13-15:每个神经元都标出了其输出的值。

C 和 D 的输出仅依赖于 A 和 B。同样,E 和 F 的输出仅依赖于 C 和 D 的输出,这意味着它们最终也仅依赖于 A 和 B。G 的情况也是如此。如果我们从 G 的表达式开始,代入 E 和 F 的值,再代入 C 和 D 的值,我们得到一个关于 A 和 B 的大表达式。经过简化后,我们发现 G 的输出是 78A + 86B。我们可以将其写成一个带有两个新权重的单一神经元,如图 13-16 所示。

G 在图 13-16 中的输出与 G 在图 13-14 中的输出完全相同。我们的整个网络已经坍缩成了一个单一的神经元!

无论我们的神经网络多么庞大或复杂,如果它没有激活函数,那么它始终等同于一个单一的神经元。如果我们希望网络能做的事情超过一个神经元的能力,这可不是好消息。

F13016

图 13-16:该网络的输出与图 13-14 中的输出完全相同。

在数学语言中,我们说我们的全连接网络崩溃了,因为它只使用了加法和乘法,这些操作属于线性函数。线性函数可以像我们刚才看到的那样组合,但非线性函数从根本上不同,并且不会以这种方式组合。通过设计激活函数使用非线性操作,我们可以防止这种崩溃。我们有时将激活函数称为非线性

激活函数有很多不同的类型,每种函数都会产生不同的结果。一般来说,这种多样性存在是因为在某些情况下,一些函数可能会遇到数值问题,使得训练速度变慢,甚至完全停止。如果发生这种情况,我们可以替换成一种避免这个问题的激活函数(当然,这种替代函数也有自己的弱点)。

实际上,我们通常只使用少数几种激活函数。当阅读文献并查看其他人的网络时,我们有时会看到一些较少见的激活函数。让我们先调查一下大多数主要库通常提供的函数,然后汇集最常用的函数。

激活函数

激活函数(有时也叫做传递函数,或非线性)接受一个浮动点数作为输入,并返回一个新的浮动点数作为输出。我们可以通过画图来定义这些函数,而不需要任何方程或代码。横坐标(X 轴)是输入值,纵坐标(Y 轴)是输出值。要找到任意输入的输出,我们只需要沿 X 轴找到输入位置,然后直接向上移动,直到碰到曲线。那就是输出值。

理论上,我们可以为网络中的每个神经元应用不同的激活函数,但实际上,我们通常会为每一层中的所有神经元分配相同的激活函数。

直线函数

让我们首先看看由一条或多条直线组成的激活函数。图 13-17 展示了一些仅由直线组成的“曲线”。

我们来看一下图 13-17 中的最左边的例子。如果我们在 X 轴上选择任何一点,并垂直向上直到碰到这条线,那么该交点在 Y 轴上的值与 X 轴上的值相同。这个曲线的输出值,或 y 值,总是与输入值,或 x 值相同。我们称之为恒等函数

F13017

图 13-17:直线函数。最左边的函数叫做恒等函数。

图 13-17 中的其他曲线也是直线,但它们的斜率不同。我们称任何仅由一条直线组成的曲线为线性函数,或者更稍微令人困惑的是,称其为线性曲线

这些激活函数并不会阻止网络崩溃。当激活函数是一条直线时,数学上它只是做了乘法和加法,这意味着它是一个线性函数,网络就会崩溃。这些直线激活函数通常只会出现在两种特定的情况下。

第一个应用是在网络的输出神经元上。由于输出后没有神经元,因此不存在崩溃的风险。图 13-18 的顶部展示了这个思想。

F13018

图 13-18:使用恒等函数作为激活函数。顶部:输出神经元上的恒等函数。底部:使用恒等函数在求和步骤和非线性激活函数之间插入一个处理步骤。

使用直线激活函数的第二种情况是,当我们想在神经元的求和步骤和其激活函数之间插入一些处理时。在这种情况下,我们对神经元应用恒等函数,执行处理步骤,然后再执行非线性激活函数,正如图 13-18 底部所示。

由于我们通常希望使用非线性激活函数,因此我们需要摆脱单一的直线。以下所有的激活函数都是非线性的,并能防止网络崩溃。

阶梯函数

我们不想要一条直线,但也不能随便选任何曲线。我们的曲线需要是单值的。正如我们在第五章讨论的那样,这意味着如果我们从 X 轴上的任何一个 x 值向上看,那么上方只有一个 y 值。线性函数的一个简单变种是从一条直线开始,然后将其分成几段。它们甚至不需要连接。在第五章的语言中,这意味着它们不需要是连续的。

图 13-19 展示了这种方法的一个例子。我们称之为阶梯函数。在这个例子中,如果输入值从 0 到不到 0.2,它的输出值为 0;如果输入值从 0.2 到不到 0.4,输出值则为 0.2,以此类推。这些突变并不违反我们的规则,即每个输入 x 值对应一个 y 输出值。

F13019

图 13-19:这条曲线由多条直线组成。实心圆告诉我们该点的 y 值是有效的,而空心圆则表示该点没有曲线。

最简单的阶梯函数只有一个步骤。这是一个常见的特例,因此它有了自己的名称:阶跃函数。图 13-2 中的原始感知器使用阶跃函数作为其激活函数。阶跃函数通常画成图 13-20(a)的样子。它在某个阈值之前有一个值,之后则有另一个值。

不同的人对输入恰好等于阈值时的情况有不同的偏好。在图 13-20(a)中,我们展示了阈值处的值是阶跃右侧的值,如实心点所示。

F13020

图 13-20:阶跃函数有两个固定值,分别位于阈值 x 的左右两侧。

作者们通常对输入值恰好处于过渡区时的情况比较随意,并且绘制如图 13-20(b)所示的图形,强调函数的“阶跃”性质。这是一种模糊的画法,因为我们不知道当输入恰好处于阈值时所使用的值是什么,但这是一种常见的绘图方式(通常我们不在乎阈值处使用的是哪个值,所以可以选择我们喜欢的任何值)。

一些流行的步骤函数版本有各自的名称。单位阶跃在阈值左侧的值为 0,右侧的值为 1。图 13-21 展示了这个函数。

如果单位阶跃的阈值为 0,我们称之为更具体的名称 Heaviside 步骤函数,如图 13-21 所示。

F13021

图 13-21:左:单位阶跃函数在阈值左侧的值为 0,右侧的值为 1。右:Heaviside 步骤函数是一种单位阶跃函数,其阈值为 0。

最后,如果我们有一个 Heaviside 步骤函数(即阈值为 0),但是左侧的值是 −1 而不是 0,我们将其称为符号函数,如图 13-22 所示。符号函数的一个常见变体是当输入值恰好为 0 时,输出值也为 0。两种变体通常都被称为“符号函数”,因此,当差异重要时,值得留意以确认指的是哪一种。

F13022

图 13-22:符号函数的两种版本。左:小于 0 的值输出 −1,其他值输出 1。右:与左图相同,唯一不同的是输入值恰好为 0 时,输出值为 0。

分段线性函数

如果一个函数由多个部分组成,每部分都是一条直线,那么我们称之为 分段线性。只要这些部分加在一起不形成一条直线,它仍然是一个非线性函数。

可能最流行的激活函数是一种分段线性函数,称为整流器,或整流线性单元,缩写为 ReLU(请注意,字母 e 是小写的)。这个名字来自于一种叫做整流器的电子元件,它可以防止负电压从电路的一部分传递到另一部分(Kuphaldt 2017)。当电压变为负值时,物理整流器将其限制为 0,我们的整流线性单元对输入的数字执行相同的操作。

ReLU 的图形如图 13-23 所示。它由两条直线组成,但由于有了折点或弯曲,这并不是一个线性函数。如果输入小于 0,则输出为 0。否则,输出与输入相同。

F13023

图 13-23:ReLU,或称整流线性单元。它对所有负输入输出 0,其他情况输出与输入相同。

ReLU 激活函数之所以受欢迎,是因为它是将非线性引入人工神经元末端的一种简单而快速的方式。但也存在潜在的问题。正如我们在第十四章中将看到的那样,如果输入的变化没有导致输出的变化,网络就会停止学习。而且 ReLU 对于每个负值的输出都是 0。如果我们的输入从比如 –3 变化到 –2,那么 ReLU 的输出仍然是 0。解决这个问题促使了后续 ReLU 变体的开发。

尽管存在这个问题,ReLU(或者接下来我们会看到的泄漏 ReLU)在实践中通常表现良好,而且人们在构建新网络时通常将其作为默认选择,特别是在全连接层中。除了这些激活函数在实践中表现良好的事实外,还有很好的数学理由希望使用 ReLU(Limmer 和 Stanczak 2017),尽管我们在这里不会深入探讨。

泄漏 ReLU 改变了对负值的响应。它不会对任何负值输出 0,而是输出输入值,按 10 倍缩小。图 13-24 显示了该函数。

F13024

图 13-24:泄漏 ReLU 类似于 ReLU,但当 x 为负时,它会返回一个缩小的 x 值。

当然,没必要总是将负值缩小 10 倍。参数化 ReLU 允许我们选择负值缩放的比例,如图 13-25 所示。

使用参数化 ReLU 时,关键是永远不要选择 1.0 的比例,因为那样我们会失去“折点”,函数会变成直线,应用此函数的神经元会与其后立即跟随的神经元发生崩溃。

F13025

图 13-25:参数化 ReLU 类似于泄漏 ReLU,但可以指定对于小于 0 的 x 值的斜率。

基本 ReLU 的另一种变体是偏移 ReLU,它只是将弯点向下和向左移动。图 13-26 展示了一个示例。

F13026

图 13-26:偏移 ReLU 将 ReLU 函数中的弯点向下和向左移动。

我们可以通过一种叫做maxout的激活函数(Goodfellow 等人 2013)来概括各种 ReLU 变体。Maxout 允许我们定义一组直线。在每个点,函数的输出是所有直线在该点的最大值。图 13-27 展示了只有两条线的 maxout,形成一个 ReLU,以及另外两个示例,它们使用更多的线条创建更复杂的形状。

F13027

图 13-27:maxout 函数让我们能够通过多条直线构建函数。重的红线是 maxout 在每一组直线上的输出。

基本 ReLU 的另一种变体是在输入前加上一个小的随机值,然后再通过标准的 ReLU 进行处理。这个函数叫做noisy ReLU

*滑函数

正如我们将在第十四章看到的,训练神经网络的一个关键步骤是计算神经元输出的导数,而这必然涉及到它们的激活函数。

我们在上一节看到的激活函数(除了线性函数)通过使用多条直线来创造它们的非线性特征,这些直线中至少有一条存在拐角。数学上,在一对直线之间的拐角处没有导数,因此该函数不是线性的。

如果这些拐角妨碍了导数的计算,而导数是训练神经网络所必需的,那么像 ReLU 这样的函数为什么还会有用,甚至如此流行呢?事实证明,标准的数学工具可以巧妙地处理像 ReLU 中的尖角这样的部分,并且仍然能够计算出导数(Oppenheim 和 Nawab 1996)。这些技巧并不是对所有函数都适用,但我们之前看到的函数的开发原则之一就是它们允许使用这些方法。

使用多条直线然后修补问题的替代方案是使用天生在每个地方都有导数的*滑函数。也就是说,它们在所有地方都是*滑的。让我们来看几个流行的*滑激活函数。

softplus函数仅仅是对 ReLU 进行了*滑处理,如图 13-28 所示。

F13028

图 13-28:softplus 函数是 ReLU 的*滑版本。

我们也可以*滑化偏移的 ReLU。这被称为指数型 ReLU,或ELU(Clevert, Unterthiner, 和 Hochreiter 2016)。它在图 13-29 中展示。

F13029

图 13-29:指数型 ReLU,或 ELU

*滑 ReLU 的另一种方法叫做swish(Ramachandran, Zoph, 和 Le 2017)。图 13-30 展示了它的样子。实际上,它是一个 ReLU,但在 0 点左侧有一个小的*滑波动,然后逐渐变*。

F13030

图 13-30:swish 激活函数

另一种流行的*滑激活函数是sigmoid,也称为logistic 函数logistic 曲线。这是 Heaviside 阶跃函数的*滑版本。sigmoid这个名字来源于该曲线与 S 形状的相似,而其他名称则指代其数学解释。图 13-31 展示了这个函数。

与 Sigmoid 密切相关的另一个数学函数叫做双曲正切。它与 Sigmoid 非常相似,唯一的区别是负值被映射到−1,而不是 0。这个名字来自于它在三角学中的曲线起源。因为它名字较长,所以通常简写为tanh。这在图 13-32 中展示了出来。

我们说 Sigmoid 和 tanh 函数都会将它们的整个输入范围从负无穷到正无穷压缩到一个小范围的输出值。Sigmoid 将所有输入压缩到[0, 1]范围,而 tanh 将它们压缩到[−1, 1]。

F13031

图 13-31:S 型 Sigmoid 函数也叫做逻辑函数或逻辑曲线。对于非常负的输入,它的值为 0,对于非常正的输入,它的值为 1。对于大约在−6 到 6 之间的输入,它*滑地在两者之间过渡。

F13032

图 13-32:双曲正切函数,写作 tanh,像图 13-31 中的 Sigmoid 一样呈 S 形。主要的区别在于,它对非常负的输入返回−1,并且过渡区略微狭窄。

这两者在图 13-33 中并排显示。

F13033

图 13-33:Sigmoid 函数(橙色)和 tanh 函数(青色),两者在范围−8 到 8 之间的图像。

另一个*滑的激活函数使用正弦波,如图 13-34 所示(Sitzmann 2020)。它像 tanh 一样将输出压缩到[–1, 1]范围,但对于远离 0 的输入,它不会饱和(或停止变化)。

F13034n

图 13-34:正弦波激活函数

激活函数画廊

图 13-35 总结了我们讨论过的激活函数。

F13035n

图 13-35:流行激活函数的画廊

比较激活函数

ReLU 曾是最流行的激活函数,但*年来,leaky ReLU 的受欢迎程度逐渐上升。这是实践的结果:使用 leaky ReLU 的网络通常学习得更快。

原因在于 ReLU 存在一个问题,我们之前提到过。当 ReLU 的输入为负时,它的输出为 0。如果输入是一个大的负数,那么即使改变它一个很小的量,输入仍然是负数,ReLU 的输出仍然是 0。这意味着导数也是 0。正如我们将在第十四章看到的,当一个神经元的导数为零时,不仅它自己停止学习,而且它前面的神经元也更有可能停止学习。因为一个输出永远不变化的神经元不再参与学习,所以我们有时会使用比较极端的说法,称这个神经元死亡。leaky ReLU 因其输出对于每一个负输入都不相同,导数不为 0,因此不会“死亡”,而且越来越受到欢迎。正弦波函数几乎在每个地方都有非零的导数(除了每个波的顶部和底部)。

在 ReLU 和 leaky ReLU 之后,sigmoid 和 tanh 可能是最受欢迎的函数。它们的吸引力在于它们*滑,且输出范围被限定在[0, 1]或[–1, 1]之间。经验表明,当网络中流动的所有值都处于有限范围时,网络的学习效率最高。

没有确凿的理论可以告诉我们在特定网络的特定层中,哪种激活函数最有效。我们通常从那些在其他类似网络中有效的选择开始,然后如果学习过程过于缓慢,我们会尝试替代方案。

一些经验法则为我们提供了许多情况下的良好起点。一般来说,我们通常对隐藏层的大多数神经元,特别是全连接层应用 ReLU 或 leaky ReLU。对于回归网络,我们通常对最后一层不使用激活函数(如果必须使用一个,我们使用线性激活函数,这等同于没有激活函数),因为我们关心的是特定的输出值。当我们进行二分类时,只有一个输出值。在这种情况下,我们通常会应用 sigmoid 函数,将输出清晰地推向某一类别。对于具有多个类别的分类网络,我们几乎总是使用一种略有不同的激活函数,我们接下来将讨论它。

Softmax

有一种操作我们通常只应用于分类神经网络的输出神经元,而且即便如此,只有在输出神经元有两个或更多时才使用。它不是我们通常所说的激活函数,因为它同时接受所有输出神经元的输出作为输入。它将这些输出一起处理,然后为每个神经元生成一个新的输出值。尽管它不完全是一个激活函数,但它在概念上与激活函数非常相似,因此值得在此讨论中提及。

这个技术称为softmax。softmax 的目的是将分类网络输出的原始数字转化为类别概率。

需要注意的是,softmax 取代了我们通常会应用于这些输出神经元的任何激活函数。也就是说,我们不给它们应用激活函数(或者等效地,应用线性函数),然后将这些输出传递给 softmax。

这一过程的机制涉及网络如何计算其预测的数学原理,因此我们不会在这里深入探讨这些细节。一般来说,如图 13-36 所示:分数输入,概率输出。

F13036n

图 13-36:softmax 函数同时修改所有网络输出。结果是将分数转化为概率。

每个输出神经元呈现一个值或分数,表示网络认为输入属于该类别的程度。在图 13-36 中,我们假设数据中有三个类别,分别命名为 A、B 和 C,因此每个输出神经元给出一个该类别的分数。分数越大,系统越确定输入属于该类别。

如果某个类别的分数比其他类别大,意味着网络认为该类别更有可能。这是有用的。但是这些分数并没有设计成以其他方便的方式进行比较。例如,如果 A 的分数是 B 的两倍,这并不意味着 A 的概率是 B 的两倍,它只是意味着 A 更有可能。因为像“概率是两倍”的比较非常有用,我们使用 softmax 将输出的分数转化为概率。现在,如果 A 的 softmax 输出是 B 的两倍,那么 A 的概率确实是 B 的两倍。这种看待网络输出的方式非常有用,因此我们几乎总是在分类网络的最后使用 softmax。

我们希望作为概率处理的任何一组数字必须满足两个条件:所有值都在 0 和 1 之间,并且它们的总和为 1。如果我们只是独立地修改每个网络输出,我们无法知道其他值,因此不能确保它们的总和符合任何特定要求。当我们将所有输出传递给 softmax 时,它可以同时调整所有值,使其总和为 1。

让我们来看一下 softmax 的实际应用。请参见图 13-37 的左上角图表。

F13037

图 13-37:softmax 函数同时修改所有网络输出。结果是将分数转化为概率。上排:分类器的分数。下排:将上排分数通过 softmax 处理后的结果。请注意,上排图表使用了不同的纵坐标刻度。

图 13-37 的左上方显示了一个具有六个输出神经元的分类器的输出,我们将这些输出标记为 A 到 F。在这个示例中,这六个值都位于 0 和 1 之间。从这个图中,我们可以看到 B 类的值是 0.1,而 C 类的值是 0.8。如我们所讨论的那样,得出输入属于 C 类的可能性是 B 类的 8 倍是错误的,因为这些是得分而不是概率。我们可以说 C 类比 B 类更可能,但任何更细致的比较需要一些数学计算。为了有意义地比较这些输出,我们可以应用 Softmax 来进行计算,并将它们转换为概率。

我们在左下图中展示了 Softmax 的输出。这些是输入属于六个类中的每一个的概率。值得注意的是,大的值,如 C 和 F,被大幅度缩小,而小的值,如 B,几乎没有缩小。这是得分在 0 到 1 之间转换为概率时的自然结果。但是,条形图的排序仍然与得分时一样(C 最大,其次是 F,再是 D,依此类推)。从 Softmax 生成的概率图中,我们可以看到 C 类的概率大约是 0.25,而 B 类的概率大约是 0.15。我们可以得出结论,输入属于 C 类的可能性是 B 类的 1.5 倍多一点。

图 13-37 的中间和右侧列显示了另两个假设的网络和输入的输出,分别是在应用 Softmax 前后的结果。这三个示例显示了 Softmax 的输出取决于输入是否都小于 1。图 13-37 中的输入范围,从左到右依次是[0, 0.8]、[0, 8]和[0, 3]。Softmax 始终保留输入的排序(也就是说,如果我们按从大到小排序输入,输出的排序也会相似)。但是当一些输入值大于 1 时,最大的值往往更突出。我们说 Softmax 放大了具有最大值的输出的影响。有时我们也说 Softmax 压缩了其他值,使得最大值更加明显地主导了其他值。

图 13-37 显示了输入范围在 Softmax 输出中产生了很大差异。Softmax 还表现出有趣的行为,取决于输入值是否都小于 1、都大于 1 或混合在一起。

在图 13-37 的最左侧,所有的输入值都小于 1,位于范围[0, 0.8]内。

在中间的列中,输入值都大于 1,位于范围[0, 8]内。注意到在输出中,D 的值(对应 8)明显主导了其他所有值。Softmax 放大了输出之间的差异,使得更容易将 D 选为最大值。

在图 13-37 的最右端,我们有一些小于 1 和大于 1 的数值,范围为[0, 3]。这里,夸张效应介于左列(所有输入都小于 1)和中间列(所有输入都大于 1)之间。

然而,在所有情况下,softmax 都会返回一个概率值,每个值都介于 0 和 1 之间,并且总和为 1。输入的顺序始终被保留,因此从最大到最小输入的顺序也对应于从最大到最小的输出。

概述

真实的生物神经元是复杂的神经细胞,通过一种极其复杂的化学、电气和机械过程来处理信息。尽管计算机版本和其生物学原型之间存在巨大的差距,它们依然启发了我们创造出一种简单的计算方式——人工神经元。人工神经元将每个输入值与相应的权重相乘,求和结果,然后通过激活函数处理。我们可以将人工神经元组合成网络。通常,这些网络是有向无环图(DAG):它们是有向的(信息仅沿一个方向流动),它们是无环的(没有神经元会将自己的输出作为输入),而且它们是图形的(神经元之间是相互连接的)。输入数据从一端进入,网络的结果出现在另一端。

我们看到,如果在构建网络时不小心,整个网络可能会崩溃成一个单一的神经元。我们通过使用激活函数来防止这种情况,激活函数是一个小函数,它接收每个神经元的输出并将其转换为一个新的数字。这些函数是非线性的,这意味着它们不能仅通过加法和乘法等操作来描述。正是这种非线性使得网络不等同于单一神经元。我们通过查看一些更常见的激活函数,以及 softmax 如何将神经网络输出的数字转化为类别概率,来结束本章。

未经过训练的深度学习系统与已经训练好并准备部署的系统之间唯一的区别在于权重的数值。训练或学习的目标是找到一组权重值,使得网络的输出尽可能正确地适应尽可能多的样本。由于权重一开始是随机数值,我们需要一些有原则的方法来找到这些新的、有用的数值。在第十四章和第十五章中,我们将通过查看两个关键算法来了解神经网络是如何学习的,这些算法逐步改进初始权重,将网络的输出转变为准确且有用的结果。

第十四章:反向传播

正如我们所见,神经网络只是由一组神经元组成,每个神经元进行自己的小计算,然后将结果传递给其他神经元。我们如何训练这样一个系统,让它产生我们想要的结果呢?我们又如何高效地完成这项任务?

答案是反向传播,简称backprop。没有反向传播,今天深度学习的广泛应用是不可能的,因为我们无法在合理的时间内训练大型网络。每个现代深度学习库都提供了稳定高效的反向传播实现。尽管大多数人永远不会实现反向传播,但理解这个算法很重要,因为深度学习中有很多内容都依赖于它。

大多数反向传播的介绍都以数学形式呈现,作为一系列方程和相关讨论(Fullér 2010)。和往常一样,我们这里跳过数学内容,专注于概念。章节的中间部分是本书中讨论反向传播核心的最详细部分。第一次阅读时,你可能想轻松读一下,以便大致了解发生了什么以及各部分是如何结合的。然后,如果你愿意,可以返回来慢慢阅读,跟随每一个步骤。

训练的高级概述

神经网络通过最小化错误来学习。这个过程从一个被称为成本损失惩罚的数值开始,用来衡量每一个错误。在训练过程中,网络减少成本,输出结果会越来越接*我们想要的目标。

惩罚错误

假设我们有一个分类器,可以将每个输入识别为五个类别中的一个,类别编号为 1 到 5。具有最大值的类别是网络对每个输入类别的预测。我们的分类器是全新的,尚未训练,因此所有的权重都有小的随机值。图 14-1 展示了网络对其第一个输入样本的分类。

F14001

图 14-1:一个神经网络处理一个样本并将其分配到类别 1。我们希望它被分配到类别 3。

在这个例子中,网络认为该样本属于类别 1,因为最大输出 0.35 来自输出 1(假设我们在网络的最后有一个 softmax 层,所以输出加起来等于 1)。不幸的是,样本被标记为属于类别 3。我们本不应期待正确答案。网络可能有成千上万,甚至数百万个权重,而它们目前都只有初始的随机值。因此,输出也只是随机值。如果网络预测了该样本为类别 3,那也只是纯粹的运气。

当预测与该样本的标签不匹配时,我们可以得出一个单一的数字,告诉我们这个答案错得有多远。例如,如果类别 3 几乎得到了最大得分,我们就说网络相较于将类别 3 赋予最小得分时,会更正确(或者更不错误)。我们称这个描述标签与预测之间不匹配的数字为误差分数,或误差,有时也叫惩罚,或损失(如果损失这个词似乎是“误差”的一个奇怪同义词,可以考虑将其看作描述由于错误答案“丧失”的信息量)。

误差(或损失)是一个浮动的数值,它可以取任何值,尽管我们通常会设置使其始终为正数。误差越大,说明网络对于该输入的标签预测越“错误”。零误差意味着网络正确预测了样本的标签。在一个理想的世界里,我们希望将每个训练样本的误差降到零。实际上,我们通常会尽可能接*零。

虽然在本章中我们主要关注减少特定样本(或样本组)的误差,但我们的总体目标是最小化整个训练集的总误差,通常这只是各个个体误差的总和。

我们选择确定误差的方式赋予了我们极大的灵活性来引导网络的学习过程。然而,这种思维方式看起来可能有些逆向,因为误差告诉网络应该不做什么。这就像雕刻的大名鼎鼎的引语:要雕刻一头大象,你只需从一块石头开始,剔除所有不看起来像大象的部分(引言调查员,2020 年)。

在我们的例子中,我们从一个初始化的网络开始,然后利用误差项去消除我们不希望的行为。换句话说,我们并不是在教网络找出正确的答案。相反,我们通过赋予错误答案一个正的误差值来惩罚它们。网络减少总体误差的唯一方法就是避免错误答案,因此它学会了这样做。这是一个强大的理念:为了获得我们想要的行为,我们惩罚我们不希望的行为。

如果我们想要同时惩罚多个因素,我们会为每个因素计算一个,或者,然后将它们加起来得到总误差。例如,我们可能希望我们的分类器预测正确的类别赋予它一个得分,且这个得分至少是下一个最接*类别得分的两倍。我们可以计算代表这两个愿望的数值,并将它们的和作为我们的误差项。网络唯一降低误差到零(或者尽可能接*零)的方式是改变其权重,以实现这两个目标。

一个常见的误差项来源于观察到,当网络中的权重都处于一个小范围内时,学习效率通常是最高的,比如[–1, 1]。为了强制实现这一点,我们可以加入一个误差项,当权重偏离这个范围太远时,它会产生较大的值。这叫做正则化。为了最小化误差,网络会学会保持权重较小。

所有这些都引出了一个自然的问题,那就是网络是如何实现最小化误差的目标的。这就是本章的重点。

为了简化问题,我们将使用一个仅包含一个项的误差度量,惩罚网络的预测与标签之间的不匹配。在本章的其余部分,所有的内容在误差项更多时的表现是相同的。我们为网络教学设计的第一个算法只是一个思想实验,因为在今天的计算机上它会非常缓慢。但这个实验产生的思想为我们在本章后面讨论的更高效的技术奠定了概念基础。

一种缓慢的学习方式

让我们继续使用一个通过监督学习训练的分类器作为例子。我们将给网络一个样本,并将系统的预测与样本的标签进行比较。如果网络预测正确并且预测了正确的标签,我们就不做任何改变,直接进入下一个样本(正如俗话所说:“如果它没坏,就不要修理”[Seung 2005])。但如果某个特定样本的结果是错误的,我们将尝试改进。

让我们用一种简单的方法来进行改进。我们将从整个网络中随机选择一个权重,并冻结其他所有值,使它们无法改变。我们已经知道与该权重当前值相关的误差,所以我们创建一个以零为中心的小随机值,称之为m,将其加到该权重上,并重新评估相同的样本。这对一个权重的改变会通过整个网络产生连锁反应,因为每个依赖于该神经元输出进行计算的神经元也会发生变化。结果是一个新的预测集,因此该样本的误差也会发生变化。

如果新的误差小于之前的误差,那么我们就改进了,保持这个变化。如果结果没有变好,那么我们需要撤销这个变化。现在我们随机选择另一个权重,按另一个随机值修改它,重新评估网络,看看是否要保持这个变化,再选择另一个权重,修改它,依此类推,一遍又一遍。

我们可以继续微调权重,直到结果改进到某个程度,或者我们决定已经尝试了足够多的次数,或者我们出于其他任何原因决定停止。此时,我们选择下一个样本,再次调整许多权重。当我们用完训练集中的所有样本后,我们就再次遍历这些样本(可能顺序不同),一遍又一遍。这个想法是,每一次小的改进都会让我们更接*一个能够准确预测每个样本标签的网络。

使用这个技术,我们预计网络会慢慢改进,尽管过程中可能会有挫折。例如,后来的样本可能会导致某些变化,破坏我们刚刚为早期样本所做的改进。

如果有足够的时间和资源,我们预计网络最终会改进到能够尽可能好地预测每个样本的程度。那句话中的关键词是最终。就像“水最终会沸腾”或“仙女座星系最终会与我们的银河系碰撞”一样(NASA 2012)。虽然这些概念是对的,但这个技术显然不切实际。现代网络可能有数百万个权重。用这个算法去寻找所有权重的最佳值根本不现实。

本章其余部分的目标是将这个粗略的想法重新结构化为一个更为实用的算法。

在我们继续之前,值得注意的是,由于我们专注于权重调整,我们实际上通过第十三章中看到的偏置技巧自动调整了每个神经元偏置的影响。这意味着我们不需要考虑偏置项,这使得一切变得更简单。

现在让我们考虑如何改进我们极其缓慢的权重变化算法。

梯度下降

上一节的算法改进了我们的网络,但速度极其缓慢。一个效率低下的主要原因是我们对权重的调整有一半是错误的方向:我们应该减去的值却加了上去,反之亦然。这就是为什么当误差增大时,我们不得不撤销更改的原因。另一个问题是我们一个个地调整每个权重,这需要评估大量的样本。让我们解决这些问题。

如果我们事先知道每个权重应朝正方向还是负方向微调,我们就可以将训练速度加倍。我们可以通过该权重的误差梯度精确获得这些信息。回想一下我们在第五章中接触到的梯度,它告诉我们随着每个参数的变化,表面高度如何变化。让我们将这个概念缩小到当前的情况。

和之前一样,我们将冻结整个网络,除了一个权重。如果我们将该权重的值绘制在横轴上,我们可以将该权重的网络误差在纵轴上绘制出来。这些误差合在一起,形成一条曲线,称为误差曲线。在这种情况下,我们可以通过找到该权重上方误差曲线的斜率来找到该权重处误差的梯度(或导数)。

如果该权重上方的梯度是正的(即,线条向右移动时上升),那么增大该权重的值(向右移动)会导致误差增加。类似地,对我们更有用的是,减小该权重的值(向左移动)会导致误差减少。如果误差的斜率是负的,情况则相反。

图 14-2 展示了两个示例。

F14002

图 14-2:梯度告诉我们,如果我们增大或减小某个权重,误差(黑色曲线)会发生什么变化,对于两条不同的误差曲线。每个图展示了两个权重处的梯度。

每个权重在网络中的误差曲线都是不同的,因为每个权重对最终误差的影响不同。但是,如果我们能找到某个特定权重的梯度,我们就解决了是否需要增加或减少该权重以减少误差的问题。如果我们能找到所有权重的梯度,我们可以一次性调整它们,而不是一个一个地调整。如果我们能同时调整每个权重,使用它自己特定的梯度来告诉我们是增大还是减小,那么我们就有了一种有效的改进网络的方法。

这正是我们所做的。因为我们使用梯度来移动每个权重,以产生误差曲线上的更低值,所以我们称该算法为梯度下降

在深入讨论梯度下降之前,注意到该算法假设,在我们评估一个错误的样本后,独立且同时调整所有权重会导致误差减少,不仅是对该样本,而且是对整个训练集,并且在网络发布后,所有看到的数据也会受益。这是一个大胆的假设,因为我们已经注意到,改变一个权重可能会通过网络的其余部分引起涟漪效应。改变一个神经元的输出会改变所有使用该值的神经元的输入和输出,从而改变它们的梯度。如果我们运气不好,一些原本有正梯度的权重可能会变成负梯度,反之亦然。这意味着如果我们坚持使用我们计算出来的梯度,改变这些权重会让误差变大,而不是变小。为了控制这个问题,我们通常会对每个权重进行小幅调整,希望这些错误不会淹没我们的改进。

入门

让我们通过两步调整网络的权重来减少整体误差。在第一步中,称为反向传播反传,我们访问每个神经元,在那里计算并存储一个与网络误差相关的数值。一旦我们为每个神经元获得这个值,我们就用它来更新进入该神经元的每个权重。第二步称为更新 步骤,或优化步骤。它通常不被视为反向传播的一部分,但有时人们会随便将这两个步骤合并在一起,称整个过程为反向传播。本章专注于第一步,第十五章专注于优化。

在本讨论中,我们将忽略激活函数。激活函数的非线性特性对神经网络的正常工作至关重要,但这种特性引入了许多与理解反向传播本质无关的细节。尽管为了更清晰的讨论而进行简化,但在任何反向传播的实现中,激活函数肯定是被考虑在内的。

进行此简化后,我们可以得出一个重要的观察:当我们网络中的任何神经元输出发生变化时,最终的输出误差也会按比例发生变化。

让我们来分析一下。我们在神经网络中只关心两种类型的值:权重(我们可以随意设置和更改)和神经元输出(这些是自动计算的,超出了我们直接控制的范围)。除了第一层之外,每个神经元的输入值都是前一个神经元的输出与该输出经过的边的权重的乘积。每个神经元的输出只是所有这些加权输入的总和。如果没有激活函数,每个神经元输出的变化与其输入或这些输入的权重的变化成比例。如果输入本身保持不变,神经元输出变化(从而影响最终误差)唯一的方式是输入上的权重发生变化。

想象一下,我们正在查看一个刚刚发生输出变化的神经元。网络的误差会因此发生什么变化呢?没有激活函数时,网络中的唯一操作是乘法和加法。如果我们写下数学公式(我们这里不写),结果表明,最终误差的变化总是与神经元输出的变化成比例。

任何神经元输出变化与最终误差变化之间的关系就是神经元的变化乘以一个数值。这个数值有各种名称,但最流行的是小写希腊字母δ(德尔塔),有时也使用大写版本Δ。数学家们常用德尔塔字符表示某种“变化”,因此这是一个自然(虽然简洁)的名称选择。

每个神经元都有一个与之相关的 delta,或称δ,这是通过使用当前样本评估当前网络得到的结果。这是一个实数,可能很大或很小,正数或负数。假设网络的输入没有变化,且其余网络被冻结,如果一个神经元的输出发生了特定的变化,我们可以将这个变化乘以神经元的 delta,看看整个网络的输出将如何变化。

为了说明这个概念,我们暂时只关注一个神经元的输出。我们在其输出值产生之前,添加一些任意的数字。图 14-3 图示了这一概念,其中我们使用字母m(代表“修改”)表示这个额外的值。

F14003

图 14-3:计算由于神经元输出变化导致的网络最终误差变化

因为输出会发生m的变化,我们知道最终误差的变化是m乘以神经元的δ

在图 14-3 中,我们通过将值m直接放入神经元来改变输出。或者,我们可以通过改变输入中的一个值来引起输出的变化。让我们改变来自其他神经元的输入值。与图 14-3 中的逻辑相同,这在图 14-4 中也有展示。如果我们愿意,也可以将m添加到来自神经元 A 或 C 的输入值;关键是 D 的输出发生了m的变化。由于我们仍然只是通过m改变输出,我们通过将其与图 14-3 中相同的δ值相乘来找到最终误差的变化。

F14004

图 14-4:是图 14-3 的变体,我们在 B 的输出中添加了m(在乘以权重 BD 之后)。

图 14-3 和图 14-4 说明了网络最终输出的变化可以通过任何神经元输出或网络中的任何权重的变化来预测。

我们可以利用与每个神经元相关的 delta,来告诉我们每个输入权重应该朝着正向还是负向调整。

让我们通过一个例子来说明。

现在是时候进入细节了。基本的概念是,误差会为每个权重提供一个梯度,然后我们可以使用这个梯度稍微调整每个权重,以减少整体误差。这个过程的机制并不复杂,但有一些新的概念、新的术语和很多细节需要理清。如果这部分内容让你觉得信息量太大,你可以在第一次阅读时略过这一部分(例如,跳过“在更大网络中的反向传播”部分),之后再回来深入理解这个过程。

小型神经网络的反向传播

为了更好地理解反向传播,我们将使用一个小型网络,将 2D 点分类为两类,我们称之为类 1 和类 2。如果这些点可以通过一条直线分开,那么我们只需一个神经元就能完成这个任务,但我们还是使用一个小网络,因为它让我们能看到一般的原理。让我们从查看网络并给每个我们关心的部分加上标签开始。这将使后续的讨论更加简洁易懂。图 14-5 显示了我们的这个小网络,并为它的八个权重命名。为了简化,我们省略了神经元 C 和 D 之后的常规 softmax 步骤。

F14005

图 14-5:一个简单的四神经元神经网络

最后,我们想要引用每个神经元的输出和 delta 值。为此,我们通过将神经元的名称与我们想要引用的值结合,来创建简短的两字母名称。所以AoBo是神经元 A 和 B 的输出名称,而是这两个神经元的 delta 值。

图 14-6 显示了这些值与它们的神经元一起存储。

F14006

图 14-6:我们简单的网络,每个神经元的输出和 delta 值

我们可以观察当神经元输出发生变化时,如何导致误差的变化。假设神经元 A 的输出因变化而变化的量为m,则将其标记为Am,网络的最终误差为E,而导致的误差变化为Em

现在我们可以更精确地描述当神经元输出发生变化时,误差会发生什么变化。如果神经元 A 的输出发生变化Am,那么将该变化与相乘得到误差的变化。即,变化EmAm × 给出。我们认为的作用是将神经元 A 输出的变化进行乘法操作或缩放,从而得到对应的误差变化。图 14-7 展示了我们在本章中用于可视化神经元输出的变化如何通过其 delta 值进行缩放,以产生对误差的变化的示意图。

F14007

图 14-7:我们的示意图,用于可视化神经元输出的变化如何改变网络的误差

在图 14-7 的左侧,我们从神经元 A 开始。我们看到 A 的初始输出,或称Ao,输出变化Am,以及其新的输出Ao + Am。框内的箭头表示Am的变化是正向的。这个变化与相乘得到Em,即误差的变化。我们用楔形图示来表示这个操作,说明Am的放大过程。将Em加到之前的误差值E上,就得到了新的误差值E + Em。在这种情况下,Am都是正的,因此误差变化Am × 也是正的,导致误差增加。当Am中的任意一个(但不是两个)为负时,误差会减小。

现在我们已经标记了所有内容,终于可以开始查看反向传播算法了。

找到输出神经元的增量

反向传播的核心就是找到每个神经元的增量值。为此,我们首先找到网络末端的误差梯度,然后将这些梯度反向传播,即向网络的起始位置移动。因此,我们从末端开始:输出层。

计算网络误差

我们小型网络中的神经元 C 和 D 的输出给出了输入属于类 1 或类 2 的概率。在理想情况下,一个属于类 1 的样本将产生P1 = 1.0 和P2 = 0.0 的值,这意味着系统确信它属于类 1,同时确信它不属于类 2。如果系统的不确定性稍大,我们可能会得到P1 = 0.8 和P2 = 0.2,告诉我们样本更可能属于类 1。

我们想要得到一个单一的数字来表示网络的误差。为此,我们将P1P2的值与该样本的标签进行比较。最简单的比较方式是,如果标签采用了独热编码,如我们在第十章中所看到的那样。回想一下,独热编码将零列表示为类别数的长度,只有对应正确类别的位置上是 1。在我们的案例中,我们只有两个类别,因此类 1 的样本标签为(1, 0),类 2 的样本标签为(0, 1)。有时这种标签形式也称为目标

让我们将预测值P1P2也放入一个列表中:(P1, P2)。现在我们可以直接比较这两个列表。我们几乎总是使用交叉熵来进行此比较,如第六章中所讨论的那样。图 14-8 展示了这一思想。

F14008

图 14-8:从样本中找出误差

每个深度学习库都提供了一个内置的交叉熵函数,帮助我们计算分类器中的误差,如本例所示。除了计算网络的误差外,函数还提供了梯度,告诉我们如果增加四个输入中的任何一个,误差将如何变化。

使用误差梯度,我们可以查看输出层中每个神经元的值,并确定是否希望该值变得更正或更负。稍后,我们会将每个神经元朝着使误差减少的方向进行调整。

绘制我们的误差

让我们来看一条误差曲线。我们还将绘制与网络中某一特定输出或权重相关的梯度。记住,这只是该点的误差的斜率。

让我们看看误差如何随着预测值P1的变化而变化,参见图 14-9。假设P1的值为-1。

在图 14-9 中,我们用橙色圆点标记了P1 = −1 的值,并且用绿色线条在该P1值上方的曲线上绘制了导数。这个导数(或梯度)告诉我们,如果我们使P1变得更正(即从−1 向右移动),网络中的误差将减少。

F14009

图 14-9:误差如何依赖于P1的不同值

如果我们知道代表误差的黑色曲线,我们就不需要梯度了,因为我们只需找到曲线的最小值。不幸的是,数学并没有给我们黑色曲线(我们在这里仅为参考绘制它)。但幸运的是,数学提供了足够的信息,使我们能够在任何位置找到曲线的导数。

图 14-9 中的导数告诉我们,如果我们稍微增加或减少P1,误差会发生什么变化。在我们改变P1之后,我们可以在新的位置找到导数并重复操作。导数或梯度能够准确预测每次改变P1后的新误差,只要我们保持每次变化较小。变化越大,预测的准确性就越差。

我们可以在图 14-9 中看到这一特点。假设我们将P1从−1 向右移动一个单位。根据导数,我们现在预计误差为 0。然而在P1 = 0 时,误差(黑色曲线的值)实际上约为 1。我们把P1移动得太远了。为了确保图形清晰且易于阅读,我们有时会做出较大的移动,但实际上,我们改变权重时通常是小幅度的。

让我们用导数来预测由于P1变化而导致的误差数值变化。图 14-9 中绿色线的斜率是多少?左端大约在(−2, 8),右端大约在(0, 0)。因此,这条线每向右移动一个单位,下降大约四个单位,斜率为−4/1,即−4。如果P1变化了 0.5(即从−1 变化到−0.5),我们预测误差将下降 0.5 × −4 = −2。

记住我们的目标是找到。我们刚刚做到了!在这个讨论中,P1只是Co,神经元 C 输出的另一种名称。我们发现当 P1 = –1 时,Co(或P1)变化 1 会导致误差变化−4。正如我们所讨论的,在P1发生如此大的变化后,我们不应该对这个预测过于自信。但对于小幅度的移动,比例是正确的。例如,如果我们将P1增加 0.01,那么我们预计误差会变化−4 × 0.01 = −0.04,对于如此小的P1变化,预测的误差变化应该非常准确。如果我们将P1增加 0.02,那么我们预计误差会变化−4 × 0.02 = −0.08。

相同的思路也适用于减少P1的值,或者将其向左移动。如果P1从−1 变化到−1.1,我们预计误差会变化−0.1 × −4 = 0.4,因此误差将增加 0.4。

我们发现,对于Co的任何变化量,我们可以通过将Co乘以−4 来预测误差的变化。这正是我们一直在寻找的!的值是−4。请注意,一旦P1的值因任何原因发生变化,误差曲线也会发生变化,的值必须重新计算。

我们刚刚找到了第一个增量值,它告诉我们如果 C 的输出发生变化,误差将变化多少。这只是误差函数在P1(或Co)处的导数。图 14-10 通过我们的误差图形象地展示了这一切。

F14010

图 14-10:我们的误差图,展示了神经元 C 输出的小幅增加 Cm 引起的误差变化。

原始输出是图 14-10 最左边的绿色条形。我们假设由于其中一个输入权重的变化,C 的输出增加了一个量Cm。这通过乘以来放大,得到误差的变化量Em。即,Em = Cm × 。这里Cm的值大约是 1/4(Cm框中的上箭头告诉我们变化是正的),而的值是−4(该框中的箭头告诉我们值是负的)。因此,Em = −4 × 1/4 = −1。新的误差值在最右侧,是前一个误差加上Em,即 4 + (−1) = 3。

请记住,在这个阶段,我们还没有对这个增量值做任何处理。我们现在的目标只是找出我们神经元的增量。稍后我们将使用它们来改变权重。

寻找 Dδ

让我们对P2重复整个过程,以得到的值,或者神经元 D 的增量。

让我们从* Cδ的回顾开始。在图 14-11 的左侧,我们展示了P1的误差曲线。由于将所有其他权重调整到更好的值,P1*的误差曲线现在有一个约为 2 的最小值。

F14011

图 14-11:左侧:不同P1值的误差;右侧:不同P2值的误差。

如果我们使用新的P1值和误差曲线,似乎P1的约 0.5 的变化将导致误差变化约−1.5,因此大约是−1.5 / 0.5 = −3。如果不改变P1,如果我们改变P2呢?

看一下图 14-11 右侧的图表。约−0.5 的变化(这次向左移动,朝着碗的最小值)会导致误差变化约−1.25,因此大约是 1.25 / −0.5 = 2.5。这里的正值告诉我们,移动P2到右侧会导致误差上升,所以我们希望将P2向左移动。

这里有一些有趣的事情值得观察。首先,尽管这两条曲线都是碗状的,但碗底的位置在不同的权重值上。其次,因为P1P2的当前值位于各自碗底的对立面,它们的导数符号相反(一个为正,另一个为负)。

最重要的观察是,我们目前无法将误差降至 0。在这个例子中,曲线的最低点约为 2。这是因为每条曲线仅考虑变化一个值,而另一个值保持不变。因此,即使P1达到了 1 的值,它的曲线达到了最小值,结果仍然会有误差,因为P2没有达到理想值 0,反之亦然。这意味着,如果我们只改变这两个值中的一个,我们无法将误差降至 0。得到 0 的误差是理想的,但更一般地说,我们的目标是逐步调整每个权重,直到将误差降到尽可能小的值。对于某些网络来说,我们可能永远无法达到 0 的误差。

请注意,即使我们能够将误差降至 0,我们也可能不希望这样做。正如我们在第九章中看到的,当网络过拟合时,训练误差会继续下降,但它处理新数据的能力却变得更差。我们真正希望的是尽可能地将误差最小化,而不发生过拟合。在日常讨论中,我们通常说我们希望将误差降至零,理解是最好在有些误差的情况下停止训练,而不是继续训练并发生过拟合。

我们稍后将看到,只要我们采取非常小的步伐,就可以同时改进网络中的所有权重。然后,我们需要重新评估误差,找到新的曲线,再计算新的导数和 delta 值,然后再进行调整。与每次样本后采取多次步骤不同,我们通常只调整一次权重,然后评估另一个样本,再次调整权重,如此循环。

测量误差

我们之前提到过,我们通常通过交叉熵计算分类器的误差。为了本次讨论,我们使用一个更简单的公式,使得每个输出神经元的 delta 值容易计算。这个误差度量被称为二次代价函数,或者均方误差 (MSE)(Nielsen 2015)。像往常一样,我们不会深入探讨这个方程的数学原理。我们选择它是因为它让我们能够通过神经元的值与相应标签值之间的差来计算输出神经元的 delta 值(Seung 2005)。图 14-12 直观地展示了这一思想。

F14012

图 14-12:当我们使用二次代价函数时,任何输出神经元的 delta 值仅为标签中的值减去该神经元的输出值。如图中红色部分所示,我们将该 delta 值与其神经元一起保存。

记住,CoP1是同一个值的两个名字,DoP2也是如此。

让我们考虑Co(或P1),当第一个标签为 1 时。如果Co = 1,那么的值为 1 – Co = 0,因此Co的任何变化都会乘以 0,导致输出误差不发生变化。

现在假设Co = 2。那么它们的差值是 = 1 – Co = −1,告诉我们改变Co会导致误差变化相同的量,但符号相反。如果Co更大,比如Co = 5,那么 1 – Co = −4,这意味着任何对Co的变化都会使误差的变化被放大一个-4 倍。我们使用了较大的数字是为了方便,但请记住,导数只准确预测在我们进行非常小的步长时会发生什么。

同样的思路也适用于神经元 D 及其输出Do(或P2)。

我们现在已经完成了反向传播的第一步:我们找到了输出层所有神经元的δ值。从图 14-12 中我们知道,输出神经元的δ值取决于标签中的值和神经元的输出。当我们改变输入该神经元的权重值时,它的δ值也会发生变化。δ值是一个临时值,它会随着每次网络或其输入的变化而变化。这也是我们每次只对每个样本调整一次权重的原因。由于每次更新后我们必须重新计算所有的δ值,不如先评估一个新的样本,并利用它提供的额外信息。

记住,我们的大目标是找到权重的变化。当我们知道一层中所有神经元的δ值时,就可以更新所有输入该层的权重。让我们来看一下如何操作。

使用δ值来改变权重

我们已经知道如何为输出层的每个神经元找到δ值。我们知道,神经元输出的变化必须来自输入的变化,而输入的变化又可以来自前一个神经元输出的变化,或者是连接该输出和当前神经元的权重变化。让我们来看看这些情况。

为了方便起见,我们假设一个神经元的输出或一个权重的变化值为 1。图 14-13 显示了权重 AC 每变化 1,神经元 A 的输出与神经元 C 的输出相乘后,网络中的误差会发生相应的变化,变化值为Ao × 。将该值减去后,误差会发生变化,变化值为–Ao × 。因此,如果我们想通过从网络误差中减去Ao × 来减少网络的误差,我们可以通过将权重 AC 的值减少 1 来实现这一点。

F14013

图 14-13:当AC变化 1 时,网络误差变化Ao ×

我们可以通过为我们的图示添加一个额外的约定来形象化地总结这一过程。我们一直将神经元的输出画成从圆形右侧出来的箭头。现在,让我们将δ值画成从圆形左侧出来的箭头,如图 14-14 所示。

F14014

图 14-14:神经元 C 有一个输出Co,画成指向右的箭头,并且有一个δ值,画成指向左的箭头。

使用这个约定,找到更新后的权重AC值或AC – (Ao × )的整个过程在图 14-15 中做了总结。像这样在图中显示减法是困难的,因为如果我们有一个“减号”节点,并且有两条输入箭头,那么不清楚哪个值从另一个值中被减去(也就是说,如果输入是xy,我们是计算x* − y 还是 yx?)。为了避免这个问题,我们通过先计算Ao × ,将其乘以−1,然后将结果加到AC上,来计算AC − (Ao × )。

F14015

图 14-15:将权重 AC 的值更新为新值AC –Ao × Cδ**)

让我们一起分析一下这个图。我们从神经元 A 的输出Ao和输出神经元 C 的增量开始,然后将它们相乘(在图的顶部)。我们想从当前的AC值中减去这个结果。为了在图中清楚地表示这一点,我们将乘积乘以−1,然后将其加到权重AC上。绿色箭头表示更新步骤,在这一过程中,结果成为AC的新值。

图 14-15 是一个大新闻!我们已经找到了如何改变进入输出神经元的权重,以减少网络的误差。我们可以将这一方法应用于进入输出神经元的所有四个权重(即ACBCADBD)。通过改进它的四个权重,我们已经稍微训练了一下我们的神经网络。

继续关注输出层,如果我们将输出神经元 C 和 D 的权重都改变,从每个神经元那里减少 1 的误差,我们预计误差会下降−2。我们可以预测到这一点,因为同一层的神经元不依赖彼此的输出。由于 C 和 D 都位于输出层,C 不依赖Do,D 也不依赖Co。它们确实依赖于前一层神经元的输出,但现在我们只关注改变 C 和 D 的权重所带来的影响。

我们知道如何调整进入输出层的边缘权重,这很棒,但其他所有权重怎么办呢?我们接下来的目标是计算所有前一层神经元的增量。一旦我们为网络中的每个神经元都计算出增量,就可以使用图 14-15 来调整网络中的每个权重,以减少误差。

这将引出反向传播的神奇技巧:我们可以利用一层的神经元增量来找到其前一层的神经元增量。让我们看看是怎么做到的。

其他神经元增量

现在我们有了输出神经元的增量值,我们可以用它们来计算输出层前一层神经元的增量。在我们的简单模型中,这一层是包含神经元 A 和 B 的隐藏层。现在我们暂时专注于神经元 A 及其与神经元 C 的连接。

如果Ao,即 A 的输出,因某种原因发生变化,会发生什么呢?假设它增加了Am。图 14-16 通过使用AC的任意值,跟踪这一系列操作。

F14016

图 14-16:如果我们改变神经元 A 的输出,结果如何

如果我们从左到右读取图 14-16 中的图表,A 的变化(表示为Am)乘以权重AC,然后加到神经元 C 累积的值上。这使得 C 的输出增加了Cm。正如我们所知道的,C 的这一变化可以乘以来找到网络误差的变化。

所以现在我们有了从神经元 A 到神经元 C,再到误差的操作链。链条的第一步表明,如果我们将Ao(即Am)的变化乘以权重AC,得到Am × AC,我们就得到了Cm,C 的输出变化。我们之前知道,如果将Cm乘以,得到Cm × ,就能得到误差的变化。

所以,将这些全部合并,我们发现由于 A 的输出变化Am而引起的误差变化为Am × AC × 。我们刚刚找到了 A 的 delta!它就是 = AC ×

图 14-17 直观地展示了这一点。

F14017

图 14-17:我们可以将图 14-16 中的操作合并成一个更简洁的图。

这真是令人惊讶。神经元 C 消失了。它在图 14-17 中简直不见了。我们所需要的只是它的 delta,,通过它我们可以找到,A 的 delta。现在我们知道了,就可以更新所有输入到神经元 A 的权重,然后……等等,稍等一下。

我们实际上还没有得到。我们只得到了它的一部分。

在本讨论开始时,我们说过我们将关注神经元 A 和 C,这没问题。但如果现在我们回想一下图 14-8 中的其余网络,我们可以看到神经元 D 也使用了 A 的输出。如果Ao由于Am的变化而发生变化,那么 D 的输出也会变化,这也会影响误差。

要找到由于神经元 D 的变化引起的误差变化,我们可以重复我们刚才的分析,只需要将神经元 C 替换为神经元 D。如果Ao发生了Am的变化,其他不变,D 引起的误差变化由AD × 给出。

图 14-18 同时展示了 A 的这两个输出。这个图的设置与我们之前看到的同类图稍有不同。在这里,A 的变化对由于 C 变化引起的误差的影响通过从图表中心向右的路径展示。A 的变化对由于 D 变化引起的误差的影响则通过从图表中心向左的路径展示。

F14018

图 14-18:神经元 A 的输出被神经元 C 和神经元 D 共同使用。

图 14-18 展示了对误差的两个独立变化。由于神经元 C 和 D 互不影响,它们对误差的作用是独立的。为了找到误差的总变化,我们只需将这两者的变化相加。图 14-19 展示了通过神经元 C 和神经元 D 的误差变化结果。

F14019

图 14-19:当神经元 A 的输出同时被神经元 C 和神经元 D 使用时,误差的变化相加。

现在我们已经处理了从 A 到输出的所有路径,我们终于可以写出的值了。由于误差是累加的,就像在图 14-19 中一样,我们只需加总那些影响Am的因子。如果我们将其写出来,就是 = (AC × ) + (AD × )。

现在我们已经找到了神经元 A 的增量值,我们可以对神经元 B 重复这个过程,找到它的增量。

我们刚刚做的其实远比仅仅找到神经元 A 和 B 的增量要好得多。我们已经学会了如何获取任何神经网络中每个神经元的增量值,无论它有多少层或多少个神经元!因为我们所做的所有工作仅仅涉及一个神经元、所有在下一层中使用它的值作为输入的神经元的增量值,以及连接它们的权重。仅凭这些值,我们就能找到神经元变化对网络误差的影响,即使输出层可能相隔数十层之远。

为了更直观地总结这一点,让我们扩展一下我们画输出和增量箭头的惯例,加入权重,如图 14-20 所示。假设一个连接上的权重乘以向右移动的输出值,或者乘以向左移动的增量值,这取决于我们考虑的步骤。

F14020

图 14-20:绘制与神经元 A 相关的值。(a) 输出Ao是从神经元右侧出来的箭头,而增量是从左侧出来的箭头。(b) Ao在传递到 C 的过程中被AC乘以。(c) 在传递到 A 的过程中被AC乘以。

这里有一种理解图 14-20 的方式。神经元 A 和 C 之间有一条连接,连接上有一个权重。如果箭头指向右边,那么权重会乘以A0,也就是 A 的输出值,传递到神经元 C。如果箭头指向左边,那么权重会乘以,也就是 C 的增量值,传递到神经元 A。

当我们评估一个样本时,我们使用从左到右的前馈流动方式,其中来自神经元 A 到神经元 C 的输出值通过一个权重为AC的连接传递。结果是,值Ao × AC到达神经元 C,并与其他传入值相加,如图 14-20(b)所示。

当我们稍后想计算 时,我们从右向左跟踪流动。然后,离开神经元 C 的增量通过一个带有权重 AC 的连接传递。结果是,值 × AC 到达神经元 A,并与其他输入值一起加总,如图 14-20(c)所示。

现在,我们可以总结一下对一个样本输入的处理过程,以及计算一个任意神经元(命名为 H)增量的过程(记住,我们忽略了激活函数),如图 14-21 所示。

F14021

图 14-21:左图:为了计算 Ho,我们将每个前一神经元的输出按连接的权重进行缩放,并将结果加总。右图:为了计算 ,我们将每个后续神经元的增量按连接的权重进行缩放,并将结果加总。和往常一样,我们忽略了激活函数。

这一点令人感到对称和愉悦。它还揭示了一个重要的实际结果:计算增量(delta)通常和计算输出值一样高效。即使输入连接的数量和输出连接的数量不同,涉及的工作量在两个方向上仍然接*。

请注意,图 14-21 并不要求神经元 H 做任何特殊的事情,除了它需要从邻接层接收输入,这些输入通过带有权重和增量的连接传递过来。我们可以应用图 14-21 的左半部分,并在前一层的输出可用时立即计算神经元 H 的输出。我们可以应用图 14-21 的右半部分,并在下一层的增量可用时立即计算神经元 H 的增量。

对后续神经元增量的依赖,解释了为何我们必须将输出层的神经元视为特殊情况:因为没有“下一层”的增量可供使用。

在整个讨论中,我们省略了激活函数。事实上,我们可以将它们纳入到图 14-21 中,而不改变基本方法。尽管这个过程在概念上非常直接,但其机制涉及很多细节,所以我们在这里不会深入探讨。

这一过程是为网络中每个神经元寻找增量的核心部分,它是反向传播算法的关键。让我们来了解一下反向传播在一个更大网络中的工作方式。

更大的网络中的反向传播

在上一节中,我们看到了反向传播算法,它允许我们计算出网络中每个神经元的增量。由于这个计算依赖于后续神经元的增量,并且输出神经元没有这些增量,且输出神经元的变化直接由损失函数驱动,因此我们将输出神经元视为特殊情况。一旦找到了任何一层(包括输出层)中所有神经元的增量,我们就可以向后逐层(朝着输入层方向)推进,找到该层所有神经元的增量。然后我们再次向后推进,计算所有增量,再次向后推进,依此类推,直到我们到达输入层。一旦我们为每个神经元找到了增量,就可以调整进入该神经元的权重值,从而训练我们的网络。

让我们通过反向传播的过程,逐步找到稍大网络中所有神经元的增量。

在图 14-22 中,我们展示了一个具有四层的网络。仍然有两个输入和两个输出,但现在我们有三个隐藏层,分别包含两个、四个和三个神经元。

F14022

图 14-22:一个新的分类器网络,具有两个输入、两个输出和三个隐藏层

我们通过评估一个样本来开始。我们将其 X 和 Y 特征的值提供给输入,最终网络会产生输出预测值P1P2

现在我们可以通过找到第一个输出神经元的误差,开始进行反向传播,如图 14-23 的上部所示。

我们任意从上面的神经元开始,它给出了我们标记为P1的预测值(即样本属于类别 1 的概率)。根据P1P2的值以及标签,我们可以计算出网络输出中的误差。假设网络没有完美预测这个样本,那么误差值就大于零。

利用误差、标签和P1P2的值,我们可以计算出该神经元的增量值。如果我们使用的是二次代价函数,这个增量只是标签值减去神经元的值,就像我们在图 14-12 中看到的那样。但如果我们使用其他函数,计算可能会更复杂,所以我们将讨论一般情况。

我们保存这个增量与其对应的神经元,然后对输出层中的所有其他神经元重复此过程(此处只有一个神经元),如图 14-23 的下部所示。这样就完成了输出层,因为我们现在为输出层中的每个神经元都找到了增量。

到此为止,我们可以开始调整输入层到输出层的权重,但通常我们会先找到所有神经元的增量,然后再调整所有的权重。让我们按照这个典型的顺序来操作。

F14023

图 14-23:总结为两个输出神经元计算增量的步骤

我们退后一步到第三隐藏层(有三个神经元)。让我们考虑如何计算这三个神经元中最上面一个的增量,如图 14-24 的左侧图所示。

F14024

图 14-24:使用反向传播计算倒数第二层神经元的增量

为了找到这个神经元的增量,我们按照图 14-18 中的步骤计算个别贡献,然后按照图 14-19 中的步骤将它们加在一起,得到该神经元的增量。

现在我们逐层处理,应用相同的过程到每个神经元。当我们完成这一层的所有神经元后,我们退后一步,开始处理前一层的四个神经元。这就是过程真正变得美妙的地方。为了找到这一层中每个神经元的增量,我们只需要该神经元的输出所连接的每个神经元的权重,以及这些神经元的增量,这些我们刚刚计算出来。

其他层就不相关了。我们现在不再关心输出层。

图 14-25 展示了如何计算第二隐藏层四个神经元的增量。

F14025

图 14-25:使用反向传播计算第二隐藏层的增量值

当四个神经元的增量都被分配完后,该层就完成了,我们再退后一步。现在我们来到第一隐藏层,这一层有两个神经元。每个神经元都与下一层的四个神经元相连。我们关心的只是下一层的增量和连接两层的权重。对于每个神经元,我们找到所有消费该神经元输出的神经元的增量,将这些增量与权重相乘,并将结果加总,如图 14-26 所示。

F14026

图 14-26:使用反向传播计算第一隐藏层的增量(delta)

当图 14-26 完成后,我们就找到了网络中每个神经元的增量。

现在让我们调整权重。我们可以遍历神经元之间的连接,并使用我们在图 14-15 中看到的技术来更新每个权重,使其达到新的、更好的值。

图 14-23 到图 14-26 展示了为什么这个算法叫做反向传播。我们把任何一层的增量信息传播,即一层一层地将其向后移动,并在这个过程中进行修改。正如我们所见,计算每个增量值的速度非常快。即使我们将激活函数步骤加进去,计算成本也不会增加太多。

当我们使用并行硬件如 GPU 时,反向传播变得非常高效,因为我们可以利用 GPU 同时为整个层计算所有的 delta 和权重。这种并行化带来的巨大效率提升是反向传播使得巨大的神经网络学习变得可行的关键原因。

现在我们已经得到了所有的 delta,并且可以更新权重了。这就是训练神经网络的核心过程。

然而,在我们结束讨论之前,让我们回到该如何调整每个权重的问题。

学习率

正如我们提到的,在单步中改变权重过多通常是麻烦的根源。导数只在输入值的变化非常小的情况下准确预测曲线的形状。如果我们改变权重过多,我们可能会跳过错误的最小值,甚至可能会导致错误增加。

另一方面,如果我们将权重的调整幅度设置得太小,我们可能只能看到极少的学习进展,这会让我们花费比实际需要更多的时间来学习。然而,这种低效率通常比一个总是过度反应的系统要好。

实际上,我们通过一个超参数叫做学习率来控制每次更新时权重的变化量,通常用小写希腊字母η(eta)表示。这个数字介于 0 和 1 之间,它告诉权重在更新时应使用每个神经元新计算出的变化量的多少。

当我们将学习率设置为 0 时,权重完全不会发生变化。我们的系统永远不会改变,也永远不会学习。如果我们将学习率设置为 1,系统会对权重进行大幅度的变化,可能会导致错误增加,而不是减少。如果这种情况频繁发生,网络可能会不断地过度调整,然后进行补偿,导致权重一直在波动,无法稳定在最佳值。因此,我们通常将学习率设置在这两个极端值之间。实际上,我们通常将其设置为稍微大于 0。

图 14-27 展示了学习率的应用。我们从图 14-15 开始,插入一个额外的步骤,在将−(Ao × )加回AC之前,先通过η缩放它的值。

F14027

图 14-27:学习率帮助我们控制网络学习的速度,通过控制每次更新时权重变化的幅度来实现。

学习率的最佳值取决于我们构建的特定网络和我们正在训练的数据。找到一个合适的学习率对网络的学习至关重要。一旦系统开始学习,改变学习率的值可能会影响学习的快慢。通常我们需要通过反复试验来寻找最合适的η值。幸运的是,一些算法可以自动搜索适合的学习率初始值,而其他算法则在学习过程中对学习率进行微调。作为一个通用的经验法则,如果其他选择没有特别指示学习率,我们通常会从 0.001 左右的值开始训练网络,观察其学习效果。然后我们根据情况调整这个值,再次训练,反复进行,寻找学习最有效的学习率。我们将在第十五章更详细地讨论如何控制学习率的技术。

让我们看看学习率的选择如何影响反向传播的性能,从而影响学习效果。

构建二分类器

让我们构建一个分类器,找出两个月牙形区域之间的边界。我们将使用大约 1,500 个训练数据点,如图 14-28 所示。

F14028

图 14-28:约 1,500 个点被分配到两个类别

因为我们只有两个类别,所以只需要一个二分类器。这使我们可以跳过标签的独热编码和处理多个输出,转而只使用一个输出神经元。如果输出接* 0,说明输入属于一个类别;如果输出接* 1,说明输入属于另一个类别。

我们的分类器只有两层隐藏层,每层包含四个神经元。这些都是我们为了讨论而做出的基本任意选择,足够复杂以满足我们的讨论需求。如图 14-29 所示,两层之间是全连接的。

这个网络对隐藏层的神经元使用 ReLU 激活函数,对输出神经元使用 sigmoid 激活函数。

F14029

图 14-29:我们的二分类器,具有两个输入、每个隐藏层四个神经元和一个输出神经元

我们的网络中有多少个权重?每个输入有四个权重,然后在两层之间有四乘四个权重,再加上四个连接到输出神经元的权重。总数是(2 × 4) + (4 × 4) + 4 = 28。每个神经元还具有一个偏置项,所以我们的网络共有 28 + 9 = 37 个权重。所有权重都初始化为小的随机数。我们的目标是通过反向传播调整这 37 个权重,使最终神经元的输出始终与该样本的标签匹配。

正如我们之前讨论的那样,我们评估一个样本,计算误差,如果误差不为零,我们通过反向传播计算增量,然后使用学习率更新权重。然后我们继续处理下一个样本。请注意,如果误差为 0,我们就不做任何更改,因为网络已经给出了我们想要的答案。每次处理完训练集中的所有样本后,我们就说完成了一个训练周期

成功运行反向传播依赖于对权重做出小的变化。这有两个原因。第一个,我们已经讨论过,是因为梯度仅在我们评估的点附*是准确的。如果我们走得太远,可能会发现自己在增加误差,而不是减少误差。

第二个小步前进的原因是,网络开始部分的权重变化会导致后续层中神经元的输出发生变化,从而改变它们的增量。为了防止一切变成相互冲突的混乱,我们只对权重做小的调整。

但是,“小”是什么意思?对于每个网络和数据集,我们必须进行实验来找出答案。正如我们之前看到的,步长的大小由学习率或 eta(η)控制。这个值越大,每个权重朝着新值移动的步伐就越大。

选择学习率

让我们从一个不寻常的大学习率 0.5 开始。图 14-30 展示了我们网络为测试数据计算的边界,每个类别使用不同的背景颜色。

F14030

图 14-30:学习率为 0.5 时,我们的网络计算的边界

这太糟糕了:似乎根本没有任何边界!所有的点都被分配到一个类别,显示为浅橙色背景。如果我们查看每个训练周期后的准确率和误差(或损失),我们会得到图 14-31 的图形。

F14031

图 14-31:学习率为 0.5 时,我们的半月数据的准确率和损失

情况看起来很糟糕。正如我们预期的那样,准确率大约为 0.5,这意味着有一半的点被错误分类了。这是有道理的,因为红色和蓝色点大致*均分布。如果我们将它们都分配到一个类别,就像我们现在做的那样,那么一半的分配将是错误的。损失或误差开始时很高,并且没有下降。如果我们让网络运行几百个周期,它将继续这样运行,永远不会有所改善。

权重在做什么?图 14-32 展示了训练过程中所有 37 个权重的值。

F14032

图 14-32:使用学习率为 0.5 时,我们网络的权重。一个权重不断变化并超出目标,而其他权重的变化太小,无法在图表上显示出来。

图表中由一个权重主导,它跳跃得很厉害。这个权重是连接到输出神经元的权重之一,试图调整输出以匹配标签。这个权重先上升,然后下降,然后再次上升,几乎每次都跳得太远,然后过度修正,再过度修正,如此反复。其他神经元也在变化,但变化幅度太小,在图中看不出来。

这些结果令人失望,但并不令人震惊,因为学习率 0.5 是较大的。这就是为什么图 14-32 中的权重会发生剧烈波动的原因。

让我们将训练率减少一个数量级,设为一个更合理(尽管仍然较大)的值 0.05。我们将保持网络和数据的其他一切不变,甚至重新使用相同的伪随机数序列来初始化权重。新的边界如图 14-33 所示。

F14033

图 14-33:使用学习率为 0.05 时的决策边界

这要好得多!查看图 14-34 中的图表可以发现,在大约 16 个周期后,我们已经在训练集和测试集上都达到了 100%的准确率。使用更小的学习率给我们带来了巨大的改进。

F14034

图 14-34:使用学习率为 0.05 时网络的准确率和损失

这向我们展示了为每个新的网络和数据组合调整学习率的重要性。如果一个网络无法学习,我们有时可以通过简单地降低学习率来改善情况。

现在权重在做什么?图 14-35 展示了它们的变化历史。

F14035

图 14-35:使用学习率为 0.05 时网络权重的变化

总体而言,这要好得多,因为许多权重正在变化。它们变得相当大,而这本身可能会抑制或减缓学习。我们通常希望权重在一个较小的范围内,通常是[–1, 1]。我们将在第十五章讨论正则化时看到一些控制权重值的方法。

图 14-33 和图 14-34 是成功的象征。我们的网络已经学会了完美地分类数据,并且只用了 16 个周期,速度很快(事实上,图表显示它只用了 10 个周期)。在一台没有 GPU 支持的 2014 年末款 iMac 上,整个 16 个周期的训练过程不到 10 秒钟。

一个更小的学习率

如果我们将学习率降低到 0.01 会怎样?现在权重变化得更慢了。这会带来更好的结果吗?

图 14-36 展示了这些微小步骤所产生的决策边界。这个边界看起来比图 14-33 中的边界更简单,但两个边界都完美地分开了各自的集合。

F14036

图 14-36:学习率为 0.01 时的决策边界

图 14-37 展示了我们的准确度和损失图。由于我们的学习率非常慢,网络大约需要 170 个周期才能达到 100% 的准确度,而不是像在图 14-35 中那样仅需 16 个周期。

F14037

图 14-37:使用学习率 0.01 时我们网络的准确度和学习率

这些图展示了一种有趣的学习行为。经过最初的跃升后,训练和测试准确度都达到了约 90%,并在那里形成了*台。同时,损失也达到了*台状态。然后,大约在第 170 个周期左右,情况再次迅速改善,准确度攀升至 100%,错误降至零。

这种交替改善和*台区的模式并不罕见,我们甚至可以在图 14-34 中看到在第 3 到第 8 个周期之间的一个*台。这个*台是因为权重处于误差面上几乎*坦的区域,导致梯度接*零,因此它们的更新非常小。

尽管我们的权重可能陷入了局部最小值,但它们更常见的是停滞在鞍点的*坦区域,就像我们在第五章中看到的那样(Dauphin 等,2014)。有时,某些权重需要很长时间才能进入梯度足够大的区域,从而获得足够的推动力。当一个权重开始变化时,通常可以看到其他权重也开始变化,这是因为该权重的变化对网络其他部分产生了级联效应。

权重的值随时间变化几乎遵循相同的模式,如图 14-38 所示。值得注意的是,在我们的训练过程中,至少部分权重并非处于*坦或*台区,而是有变化的,尽管这些变化较慢。系统在不断改进,但进展非常缓慢,直到大约第 170 个训练周期,性能图中的变化才变得明显。

F14038

图 14-38:使用学习率 0.01 时我们权重的变化历史

那么,降低学习率到 0.01 是否有好处呢?在这种情况下,并没有太大帮助。即使是 0.05,分类结果在训练数据和测试数据上已经非常完美。对于这个网络和这个数据,较小的学习率只是意味着网络学习的时间更长。这项调查向我们展示了网络对学习率选择的敏感性。我们需要找到一个既不太大也不太小,而是恰到好处的值(Pyle 1918)。

我们通常在开发几乎每个深度学习网络时都会进行这类学习率的实验。我们需要找到一个在特定网络和数据上表现最好的值。幸运的是,在第十五章中,我们将看到一些算法,可以通过复杂的方式自动调整学习率。

总结

本章讲解了反向传播。我们看到,我们可以预测网络的误差如何随着每个权重的变化而变化。如果我们能确定每个权重应该增加还是减少,我们就能减少误差。

为了找出如何调整每个权重,我们从给每个神经元分配一个 delta 值开始。这个值告诉我们权重值变化和最终误差变化之间的关系。这使我们能够确定如何调整每个权重,以减少误差。

这些 delta 的计算从最后一层开始,逐层向前传播。因为计算每个神经元的 delta 所需的梯度信息是逐层向后传播的,所以我们得名反向传播。反向传播可以在 GPU 上实现,在那里我们可以同时为多个神经元进行计算。

需要记住的是,反向传播是传播误差的梯度,梯度是告诉我们当权重变化时,误差如何变化的信息。一些作者随意地将反向传播称为传播误差,但这是一种误导性的简化。我们传播的是梯度,它告诉我们如何调整权重,以改善网络的输出。

现在我们知道每个权重是应该增加还是减少,我们需要决定实际的调整幅度。这正是我们将在下一章中解决的问题。

第十五章:优化器

训练神经网络通常是一个耗时的过程。任何能够加速这一过程的方法,都是我们工具箱中的一个受欢迎的补充。本章介绍了一类旨在通过提高梯度下降效率来加速学习的工具。其目标是让梯度下降运行得更快,并避免一些可能导致其陷入困境的问题。这些工具还自动化了寻找最佳学习率的部分工作,包括能够随着时间推移自动调整学习率的算法。这些算法统称为优化器。每个优化器都有其优缺点,因此熟悉它们是值得的,这样我们在训练神经网络时才能做出明智的选择。

让我们先画一些图,帮助我们可视化误差以及它在学习过程中如何变化。这些图将帮助我们为接下来要介绍的算法建立一些直觉。

误差作为二维曲线

将系统中的误差从几何学的角度进行思考,通常会非常有帮助。我们经常将误差绘制为二维曲线。

为了熟悉这种二维误差,我们考虑将表示为线上的点的两个类别样本进行划分的任务。负值处的点属于一个类别,零及以上的点属于另一个类别,如图 15-1 所示。

F15001

图 15-1:线上的两个类别的点。位于 0 左侧的点属于类别 0,显示为蓝色,其他点属于类别 1,显示为米色。

让我们为这些样本构建一个分类器。在这个例子中,边界只由一个数字构成。所有位于该数字左侧的样本都被分配到类别 0,所有位于右侧的样本都被分配到类别 1。如果我们想象将这个划分点沿着线移动,我们可以统计被误分类的样本数量,并将其作为我们的误差。我们可以将结果总结为一个图表,其中 X 轴展示了每个潜在的分割点,而与该点相关的误差则作为一个点绘制在其上。图 15-2 展示了这一结果。

F15002

图 15-2:绘制简单分类器的误差函数

我们可以将图 15-2 中的误差曲线*滑化,如图 15-3 所示。

F15003

图 15-3:*滑后的图 15-2 版本

对于这组特定的随机数据,我们看到当我们处于 0 或者稍微偏左时,误差为 0。这告诉我们,无论从哪里开始,我们都希望最后将分隔线放在 0 的左侧。

我们的目标是找到一种方法,定位任何误差曲线的最小值。当我们能够做到这一点时,我们就可以将这一技术应用于神经网络的所有权重,从而减少整个网络的误差。

调整学习率

当我们使用梯度下降法来训练系统时,关键参数是学习率,通常用希腊字母 η(eta)表示。它通常的取值范围是 0.01 到 0.0001。较大的值会导致更快的学习,但也可能会直接跳过深谷而错过它们。较小的 η 值(接* 0,但始终为正数)会导致学习较慢,并能够找到较窄的谷底,但也可能会停滞在较浅的谷底,即使附*存在更深的谷底。图 15-4 通过图形方式回顾了这些现象。

许多优化器共享一个重要的理念,即通过在学习过程中逐渐调整学习率来提高学习效果。这个思路类似于使用金属探测器在海滩上寻找埋藏的金属物品。我们开始时大步走过海滩,但当探测器响起时,我们会逐步缩小步伐,以精确定位金属物品的位置。以此类推,在学习过程的早期,我们通常会在误差曲线中大步前进,寻找谷底。随着时间的推移,我们希望找到了这个谷底,此时我们可以逐渐减小步伐,朝着最低点前进。

F15004

图 15-4:学习率 η 的影响。 (a) 当 η 太大时,我们可能会跳过一个深谷而错过它。 (b) 当 η 太小时,我们可能会慢慢下降到局部最小值,从而错过更深的谷底。

我们可以用一个简单的误差曲线来说明我们的优化器,该曲线包含一个孤立的谷底,其形状为负高斯分布,如图 15-5 所示。

F15005

图 15-5:我们查看优化器时的误差曲线

该误差曲线的一些梯度在图 15-6 中展示(实际上我们展示的是负梯度)。

F15006

图 15-6:我们的误差曲线及其在某些位置的负梯度(缩小了 0.25 倍)

图 15-6 中的梯度已被缩小至实际长度的 25%,以便清晰显示。我们可以看到,对于这条曲线,输入值小于 0 时梯度为负,输入值大于 0 时梯度为正。当输入为 0 时,我们处在碗底,所以此时梯度为 0,表示为一个点。

常量大小的更新

让我们通过观察使用常量学习率时会发生什么,来开始研究学习率的影响。换句话说,我们始终使用一个固定的 η 值来调整梯度,该值在整个训练过程中保持不变。

图 15-7 展示了使用固定 η 进行更新的基本步骤。

F15007

图 15-7:寻找基本梯度下降的步长

假设我们正在查看神经网络中的一个特定权重。假设该权重初始值为 w1,我们更新了一次它,现在它的值变为 w2,如图 15-7(a)所示。其对应的误差是误差曲线上直接位于其上方的点,标记为 B。我们希望再次更新该权重,得到一个新的、更好的值,称为 w3。

为了更新权重,我们找到误差面上 B 点的梯度,表示为箭头g。我们通过学习率η来缩放该梯度,得到一个新箭头,标记为ηg。因为η介于 0 和 1 之间,ηg是一个与g方向相同但大小要么相同要么更小的新箭头。

在图 15-7 中,我们显示的梯度箭头g实际上是梯度的相反方向,或负梯度。正梯度和负梯度沿同一条线指向相反的方向,因此当正负梯度的选择能够从上下文中理解时,人们通常直接称之为梯度。我们将在本章中遵循这一约定。

为了找到 w3,即权重的新值,我们将缩放后的梯度加到 w2 上。用图示表示,这意味着我们将箭头ηg的尾部放置在 B 点,如图 15-7(b)所示。该箭头尖端的水*位置就是权重的新值 w3,而它的值,直接位于其上方的误差面上,标记为 C。在这种情况下,我们走得有点太远,导致误差略微增加。

让我们通过一个具有单一谷底的误差曲线来看这个技巧的实际应用。图 15-8 展示了左上角的起始点。这里的梯度很小,所以我们向右移动了一小步。此时新点的误差比我们开始时的误差稍微小一些。

对于这些图示,我们选择了η = 1/8,即 0.125。这是一个非常大的η值,通常在恒定步长的梯度下降法中,我们常使用 1/100 或更小的值。我们选择这个较大的值是因为它能使图示更清晰。较小的值也能以类似的方式工作,只是速度更慢。我们没有在这些图表的坐标轴上显示数值,以避免视觉上的混乱,因为我们更关注发生的现象,而不是具体的数字。

F15008

图 15-8:使用恒定学习率的学习过程

我们并不是通过整个梯度移动到第一个点,而是只移动了梯度长度的 1/8。这一移动使我们到达了曲线的一个更陡峭部分,那里梯度较大,因此下一个更新会移动得更远。每一步学习都会用一种新的颜色显示,我们用这种颜色绘制上一个位置的梯度,然后是新点。

我们在图 15-9 中展示了六步的特写,从图 15-8 中的第一步开始。我们还显示了每个点的误差。

F15009

图 15-9:左:接*图 15-8 中的最终图像。右:与这六个点相关的误差。

这个过程会最终到达碗底并将误差降至 0 吗?图 15-10 展示了该过程的前 15 步。

F15010

图 15-10:左:使用恒定学习率的前 15 步。右:这些 15 个点的误差。

我们接*底部,然后沿右侧的山坡向上走。但这没关系,因为这里的梯度指向下方和左侧,所以我们又往下走进谷底,直到再次超过底部,最终出现在左侧,然后我们掉头再度超过底部,最终出现在右侧,如此反复。我们正在在碗底反弹

看起来我们永远也无法到达 0。这个问题在对称的谷底特别严重,因为误差在最小值的左右两侧反复跳动。但是,当我们使用恒定学习率时,这种行为很常见。反弹现象的发生是因为我们靠*谷底时希望采取小步伐,但由于学习率是恒定的,我们采取的步伐过大。

我们可能会想,图 15-10 中的反弹问题是否是由学习率过大引起的。图 15-11 显示了对于某些较小的η值,前 15 步的情况。

F15011

图 15-11:用小学习率进行 15 步。上排:学习率分别为 0.025(左列)、0.05(中列)和 0.1(右列)。下排:上排对应点的误差。

从图 15-11 中我们可以看到,采取较小的步伐并不能解决反弹问题,尽管反弹幅度变小了。另一方面,增加学习率会使反弹问题更加严重,正如图 15-12 所示。

f15012

图 15-12:上排:学习率分别为 0.5(左列)、0.75(中列)和 1.0(右列)。下排:上排对应点的误差。

较大的学习率也可能导致我们跳出一个具有较低最小值的好谷底。在图 15-13 中,从绿色点出发,我们直接跳过了当前所在的谷底(并希望停留其中),进入了一个具有较大最小值的新谷底。

F15013

图 15-13:大步伐超过谷底,最终进入一个具有更高最小值的不同谷底。

有时像这样的较大跳跃可以帮助我们从一个较浅的山谷移动到更深的山谷,但对于如此大的学习率,我们可能会在山谷之间反复跳动,永远找不到最小值。找到一个既能以合理速度移动,又不会过度跳跃或困在底部反弹的学习率似乎是一个挑战。一个不错的替代方法是随着训练的进行逐步调整学习率。

随着时间变化调整学习率

我们可以在学习初期使用较大的 η,以避免进展缓慢,而在学习后期使用较小的 η,以避免在碗底部反复跳动。

一种简单的开始时较大并逐渐变小的方法是每次更新后将学习率乘以一个接* 1 的数值。我们可以使用 0.99 作为乘数,假设初始学习率为 0.1。然后在第一步之后,它将变为 0.1 × 0.99 = 0.099。在下一步,它将变为 0.099 × 0.99 = 0.09801。图 15-14 展示了当我们在多个步骤中使用不同的乘数时,η 会发生什么变化。

编写这些曲线方程最简单的方法是使用指数,因此这种曲线被称为 指数衰减 曲线。我们在每一步乘以的值称为 衰减参数。这个值通常是一个接* 1 的数值。

F15014

图 15-14:从学习率 η = 1 开始,各种曲线显示了在每次更新后,学习率如何在乘以给定的值后下降。

让我们将这种学习率逐渐减少的方法应用于梯度下降中的误差曲线。我们再次从学习率 1/8 开始。为了使衰减参数的效果更加明显,我们将它设置为不寻常的低值 0.8。这意味着每一步将只有前一步的 80%。图 15-15 展示了前 15 步的结果。

F15015

图 15-15:使用递减学习率的前 15 步

让我们将这个与使用常数步长时的“反弹”结果进行比较。图 15-16 展示了常数步长和递减步长的结果对比,进行 15 步更新。

F15016

图 15-16:左侧是图 15-10 中的常数步长,右侧是图 15-15 中的递减步长。注意到递减学习率帮助我们有效地在山谷的最小值处稳定下来。

递减步长成功地帮助我们稳定地达到碗底并保持在那里。

衰减计划

衰减技术很有吸引力,但它带来了一些新挑战。首先,我们需要选择一个衰减参数的值。其次,我们可能不想在每次更新后都应用衰减。为了解决这些问题,我们可以尝试其他一些减少学习率的策略。

改变学习率随时间的任何方法称为衰减计划(Bengio 2012)。

衰减计划通常以时期来表达,而不是样本。我们在训练集中的所有样本上进行训练,然后考虑在再次训练所有样本之前改变学习率。

最简单的衰减计划是在每个时期后始终应用学习率衰减,正如我们刚刚看到的那样。 图 15-17(a) 显示了这个计划。

另一种常见的调度方法是暂时不施加任何衰减,这样我们的权重有机会摆脱它们的起始随机值,并进入可能接*找到最小值的某种状态。然后我们应用我们选择的任何计划。 图 15-17(b) 显示了这种延迟指数衰减方法,将图 15-17(a)的指数衰减调度推迟了几个时期。

另一个选择是仅偶尔应用衰减。 图 15-17(c) 中显示的间隔衰减方法,也称为固定步长衰减,在每隔固定数量的时期后减少学习率,例如每 4 个或 10 个时期。 这样我们就不会过快地变得太小。

F15017

图 15-17:随时间减小学习率的衰减计划。(a)指数衰减,在每个时期后减少学习率。(b)延迟指数衰减。(c)间隔衰减,在每个固定数量的时期后减少学习率(这里是 4 个)。 (d)基于错误的衰减,在误差停止下降时减少学习率。

另一个选项是监视网络的误差。只要误差在下降,我们就坚持使用现有的学习率。当网络停止学习时,我们应用衰减,以便它可以采取较小的步骤,希望能够进入错误景观的更深处。这种基于误差的衰减显示在图 15-17(d)中。

我们可以很容易地设计出许多替代方案,例如仅在误差减少一定量或百分比时应用衰减,或者仅通过减去一个小值而不是乘以接* 1 的数来更新学习率(只要我们在某个正值停止—如果学习率降至 0,系统将停止学习,如果学习率变为负数,系统将增加错误,而不是减少错误)。

如果我们愿意,甚至可以随着时间的推移增加学习率。强力驱动方法会查看每个训练周期后总损失的变化(Orr 1999a; Orr 1999b)。如果误差在下降,我们就增加学习率一点,比如从 1%增加到 5%。其思路是,如果一切进展顺利,且误差在下降,我们就可以采取大步伐。但如果误差上升得超过一点,我们就大幅降低学习率,将其减半。这样,我们可以立即停止任何增幅,以免它们把我们带离之前下降的误差。

学习率调度的缺点是我们必须提前选择它们的参数(Darken, Chang, 和 Moody 1992)。我们将这些参数视为超参数,就像学习率本身一样。大多数深度学习库提供了自动搜索值范围的例程,帮助我们找到一个或多个超参数的最佳值。

一般来说,调整学习率的简单策略通常效果很好,而且大多数机器学习库让我们可以轻松选择其中之一(Karpathy 2016)。

大多数机器学习系统都具备某种形式的学习率衰减。我们希望在早期阶段快速学习,大步走过整个数据景观,寻找能找到的最低点。然后我们减少学习率,使得我们能够逐渐采取更小的步伐,最终停留在我们找到的山谷的最低处。

自然会有人想知道是否有一种方法,可以控制学习率,而不依赖于我们在训练开始前设置的调度。我们一定能以某种方式检测到我们接*最小值、处于一个盆地中,或在四处跳动,并自动调整学习率以作出响应。

更有趣的问题是,或许我们并不希望对所有权重应用相同的学习率调整。能够调整更新,让每个权重以最适合它的学习速率进行学习,应该是件不错的事。

让我们看看一些针对梯度下降的变体,来解决这些问题。

更新策略

在接下来的章节中,我们将比较三种不同方式来增强梯度下降的表现。在这些例子中,我们使用一个小的,但真实的二分类问题。

图 15-18 展示了我们熟悉的两个模糊的新月形数据集。这些点的类别通过颜色显示。300 个样本是我们本章其余部分的参考数据。

为了比较不同的网络,我们需要训练它们直到误差达到最小值,或者似乎不再改善。我们可以通过绘制每个训练周期后的误差图表来展示训练结果。由于算法之间的差异,这些图表中的周期数变化范围较大。

F15018

图 15-18:我们在本章其余部分使用的数据。这 300 个点分为两个类别,每个类别 150 个点。

为了对我们的数据点进行分类,我们将使用一个具有三个全连接隐藏层(分别有 12、13 和 13 个节点)以及一个包含 2 个节点的输出层的神经网络,这样可以给出每个类别的概率。我们将在每个隐藏层使用 ReLU 激活函数,并在最后使用 softmax 函数。输出层概率较大的类别将作为我们的网络预测结果。为了保持一致性,当我们需要一个固定的学习率时,我们使用η = 0.01。该网络在图 15-19 中展示。

F15019

图 15-19:我们包含四个全连接层的网络

批量梯度下降

我们首先更新每个周期(epoch)中的权重一次,在评估完所有样本之后。这就是批量梯度下降(也叫做周期梯度下降)。在这种方法中,我们将整个训练集输入到系统中,累积所有的误差。然后我们使用所有样本的合成信息,更新所有权重一次。

图 15-20 展示了使用批量梯度下降进行典型训练时的误差。

F15020

图 15-20:使用批量梯度下降的训练运行误差

整体特征令人放心。一开始,误差大幅下降,表明网络正在开始进入误差面上的一个陡峭区域。随后曲线变得*缓。此时的误差面可能是一个几乎*坦的浅鞍点区域,或者是一个几乎是*台的区域,只是稍微有点倾斜,因为误差虽然逐渐变小,但仍在继续下降。最终,算法找到另一个陡峭的区域,并将误差一直降到 0。

批量梯度下降看起来非常*滑,但要将这个网络和数据的误差降到接* 0,需要大约 20,000 个周期,这可能需要很长时间。让我们通过放大前 400 个周期,仔细看看从一个周期到下一个周期发生了什么,具体内容见图 15-21。

看起来批量梯度下降的过程确实非常*滑。这是有道理的,因为它在每次更新时都使用了所有样本的误差。

批量梯度下降通常会产生*滑的误差曲线,但在实际应用中存在一些问题。如果我们的样本超过了计算机内存的容量,那么分页(即从较慢的存储介质中读取数据)成本会变得相当高,从而使训练过程变得非常缓慢。在处理包含数百万样本的庞大数据集时,这可能是一个问题。因为从较慢的内存(甚至硬盘)中一次次读取样本可能会耗费大量时间。解决这个问题有办法,但通常需要做很多工作。

F15021

图 15-21:在图 15-20 中展示的批量梯度下降前 400 个周期的特写

与此相关的内存问题是,我们必须保留所有样本并使其随时可用,以便每个周期都能循环使用它们。我们有时会说批量梯度下降是一个离线算法,意味着它严格依赖于其存储和可访问的信息。我们可以想象将计算机与所有网络断开连接,它仍然可以从所有训练数据中继续学习。

随机梯度下降

让我们转向另一个极端,每个样本处理后就更新一次权重。这就是所谓的随机梯度下降,或更常见的简称SGD。回想一下,随机一词大致相当于随机,它的使用是因为我们将训练样本以随机顺序呈现给网络,因此无法预测权重如何从一个样本变到下一个样本。

由于我们每处理一个样本就更新一次权重,我们的数据集有 300 个样本,这就意味着每个周期内我们需要更新 300 次权重。这会导致误差剧烈波动,因为每个样本都会推动权重发生一次变化,接着又会发生反向变化。由于我们仅按每个周期绘制误差,我们看不到这种小范围的波动。但我们仍然能看到每个周期之间存在较大的变化。

图 15-22 展示了我们使用 SGD 从该数据中学习的网络误差。

F15022

图 15-22:随机梯度下降,或 SGD

这张图与图 15-20 中批量梯度下降的图形大致相同,这是有道理的,因为两个训练过程使用了相同的网络和数据。

在大约第 225 个周期时,出现了一个巨大的峰值,显示了 SGD 的不确定性。样本的顺序以及网络权重更新的方式导致了误差从接* 0 飙升至接* 1。换句话说,网络从几乎正确分类每个样本,到几乎错误地分类每个样本,再到恢复正确(尽管这个恢复花了几个周期,正如峰值右侧的小曲线所示)。如果我们在学习过程中观察误差,可能会在这个峰值时倾向于停止训练。如果我们使用自动算法来观察误差,它也可能在这里停止。然而,就在这个峰值之后的几个周期内,系统已经恢复过来,我们又回到了接* 0 的误差。这个算法的确展现了“随机”这一特性。

从图中我们可以看到,SGD 在仅仅 400 个周期内就将误差降到了接* 0。之后我们切掉了图 15-22,因为从那时起曲线一直保持在 0。与此相比,图 15-20 中的批量梯度下降大约需要 20,000 个周期。这种相较于批量梯度下降的效率提升是典型的(Ruder 2017)。

但是让我们做一个公*的比较。每个算法更新权重的次数是多少?批量梯度下降在每个批次之后更新权重,所以 20,000 个时期意味着它进行了 20,000 次更新。SGD 在每一个样本后都进行更新。因此,在 400 个时期中,它执行了 300 × 400 = 120,000 次更新,是批量梯度下降的六倍。关键在于,实际等待结果的时间并不完全由时期数决定,因为每个时期所需的时间可能会有很大差异。

我们称 SGD 为在线算法,因为它不需要样本存储,也不需要样本在每个时期之间保持一致。它只处理每个到达的样本,并立即更新网络。

SGD 产生的结果噪声较大,正如我们在图 15-22 中看到的那样。这既是优点也是缺点。其优点是 SGD 可以在寻找最小值的过程中,从误差面的一一区域跳跃到另一一区域。但缺点是,SGD 可能会越过一个深度最小值,然后花费时间在一个误差更大的谷底中徘徊。随着时间的推移,降低学习率确实有助于解决跳跃问题,但进展通常仍然是噪声较大的。

误差曲线中的噪声可能是一个问题,因为它使得我们很难判断系统何时正在学习,何时开始过拟合。我们可以观察多个时期的滑动窗口,但我们可能直到很久以后才知道自己已经越过了最小误差。

小批量梯度下降

我们可以在批量梯度下降(每个时期更新一次)和随机梯度下降(每个样本后更新一次)这两种极端之间找到一个不错的折衷。这个折衷方法称为小批量梯度下降,有时也叫小批量 SGD。在这里,我们在评估了一定数量的样本后更新权重。这个数量通常远小于批量大小(即训练集中的样本数)。我们称这个较小的数量为小批量大小,从训练集中抽取的这些样本组成了一个小批量

小批量的大小通常是 2 的幂,介于大约 32 到 256 之间,通常选择这个大小是为了充分利用我们 GPU 的并行计算能力(如果我们有 GPU 的话)。但这只是出于效率考虑。实际上,我们可以选择任何我们喜欢的小批量大小。

图 15-23 显示了使用 32 个样本的小批量的结果。

这确实是两种算法的一个很好的结合。曲线*滑,像批量梯度下降一样,但并不是完美的。它大约在 5,000 个时期后下降到 0,介于 SGD 需要的 400 个时期和批量梯度下降的 20,000 之间。图 15-24 展示了前 400 步的详细情况。

F15023

图 15-23:小批量梯度下降

F15024

图 15-24:显示图 15-23 的前 400 次训练周期的特写,展示了训练初期的深度下降

mini-batch SGD 进行了多少次更新?我们有 300 个样本,且使用了 32 的 mini-batch 大小,因此每个 epoch 有 10 个 mini-batch。(理想情况下,我们希望 mini-batches 能精确地划分输入数据的大小,但在实际中,我们无法控制数据集的大小,这通常导致最后一个 mini-batch 是不完整的。)所以,每个 epoch 更新 10 次,乘以 5,000 次 epoch,总共是 50,000 次更新。这也恰好位于批量梯度下降的 20,000 次更新和 SGD 的 120,000 次更新之间。

Mini-batch 梯度下降比 SGD 更少噪声,这使它在跟踪误差时具有吸引力。该算法通过使用 GPU 并行计算来利用巨大的效率提升,能够在 mini-batch 中评估所有样本。它比批量梯度下降更快,并且在实践中比 SGD 更具吸引力。

因为这些原因,mini-batch SGD 在实践中非常流行,而“普通”SGD 和批量梯度下降则相对较少使用。事实上,在文献中,当提到 SGD 这个术语,甚至是 梯度下降 时,通常理解为作者指的是 mini-batch SGD(Ruder 2017)。为了让事情变得更复杂,batch 这个术语通常用来代替 mini-batch。由于基于 epoch 的梯度下降如今使用得非常少,关于批量梯度下降和批次的引用几乎总是指 mini-batch 梯度下降和 mini-batches。

梯度下降变体

Mini-batch 梯度下降是一个重要的算法,但它并不完美。让我们回顾一下 mini-batch 梯度下降的一些挑战,以及解决这些问题的一些方法。按照惯例,从这里开始我们将 mini-batch 梯度下降称为 SGD。(本节的结构灵感来自 Ruder 2017。)

我们面临的第一个挑战是指定我们希望使用的学习率 η 的值,这通常是一个难以提前确定的问题。如我们所见,过小的学习率可能导致学习时间过长,且陷入浅层局部最小值,而过大的学习率则可能导致我们越过深层局部最小值,然后在找到最小值后在其中反复跳动。如果我们通过使用衰减计划来改变 η 来避免这个问题,我们仍然需要选择 η 的初始值,以及计划的超参数。

我们还需要选择 mini-batch 的大小。这通常不是问题,因为我们通常选择与我们的 GPU 或其他硬件结构最匹配的计算值。

让我们考虑一些改进。现在,我们是通过一个统一的更新速率来更新所有权重。我们可以为系统中的每个权重找到一个独特的学习速率,这样我们不仅是在朝最佳方向移动它,还在以最佳的幅度进行移动。我们将在接下来的页面中看到这方面的例子。

另一个改进是基于这样一个认识:有时当误差面形成鞍点时,表面在所有方向上可能都很浅,所以在局部范围内,它几乎是一个(但不完全是)*原。这会让我们的进展变得非常缓慢。研究表明,深度学习系统的误差面中往往有很多鞍点(Dauphin 等,2014)。如果有办法在这些情况下不再被卡住,或者更好的是,避免在一开始就陷入这种困境,那就太好了。同样,*原也应该避免:我们希望避免进入那些梯度下降到 0 的*坦区域,当然,除非是我们要寻找的最小值。

让我们来看看一些解决这些问题的梯度下降变种。

动量

让我们同时考虑两个权重。我们可以在 XY *面上绘制它们的值,并在它们上方展示通过这些权重值训练系统所产生的误差。让我们把误差面看作是一片地形。现在我们可以将最小化误差的任务想象成跟随一滴水,寻找最低点的过程。

图 15-25 重复了第五章中的一张图,展示了这种思考训练过程的方式。

F15025

图 15-25:一滴水沿误差面滚动。这是第五章中的一张重复图。

我们可以将水替换为一个小球,想象它沿着误差面滚动。我们知道从物理世界的角度来看,一个真正的球沿着山坡滚动时,会有一些惯性,它描述了物体对其运动变化的抵抗力。如果它沿某个方向以一定速度滚动,除非有外力干扰,否则它会继续保持该方向的运动。

一个相关的概念是小球的动量,从物理角度来看,这个概念稍微抽象一些。虽然它们是不同的概念,但有时深度学习的讨论中会随意地将惯性称为动量,而我们接下来要看的算法也使用了这一术语。

这个概念使得图 15-25 中的小球在从山顶滚下来并经过图中间的鞍点后,能够继续沿着*台移动。如果小球的运动仅仅由梯度决定,那么当它到达图中间的*原时,它会停下(或者如果它接**原,小球会慢慢停止)。但小球的动量(或更准确地说,是惯性)让它继续滚动。

假设我们靠* 图 15-26 左侧。随着我们沿着山坡向下滚动,我们在约 -0.5 处到达*台期。

F15026

图 15-26:带有*台期的错误曲线,介于山峰和山谷之间

使用常规梯度下降时,我们会在*台期停止,因为梯度为零,如 图 15-27 左图所示。但如果我们加入一些动量,如右图所示,球体会继续滚动一段时间。虽然它会逐渐减慢,但如果幸运的话,它会继续滚动足够远,找到下一个山谷。

动量梯度下降(Qian 1999)技术基于这个思想。对于每一步,一旦我们计算出每个权重的变化量,我们会在其中加入上一阶段的少量变化。如果在某一步的变化为 0,或者接* 0,但在上一阶段我们有较大的变化,我们就会利用之前的动量,帮助我们继续前进,跨越*台期。

F15027

图 15-27:在 图 15-26 错误曲线上的梯度下降。左:带衰减的梯度下降。右:带衰减和动量的梯度下降。

图 15-28 直观地展示了这一概念。

F15028

图 15-28:带动量的梯度下降步长的寻找

假设某个权重的误差为 A。我们将该权重更新为值 w2,并且误差为 B。现在,我们想找到下一个权重值 w3,它将有误差 C。为了找到 C,我们需要找出对点 A 所做的变化。也就是说,我们需要找到施加在 A 上的先前变化。这就是动量,用 m 表示,如 图 15-28(a) 所示。

我们将动量 m 乘以一个缩放因子,通常用小写希腊字母 γ(gamma)表示。有时这被称为 动量缩放因子,其值介于 0 到 1 之间。将 m 乘以这个值后,我们得到一个新的箭头 γm,它的方向与 m 相同,但长度相同或更短。然后,我们像之前一样,在 B 点找到缩放后的梯度 ηg,如 图 15-28(b) 所示。现在,我们得到了所有需要的信息。我们将缩放后的动量 γm 和缩放后的梯度 ηg 相加到 B 上,图示化地表现为将 γm 的尾部放置在 ηg 的头部,如 图 15-28(c) 所示。

让我们应用这个规则,看看权重和误差如何随时间变化。图 15-29 展示了我们之前的对称山谷,以及训练的连续步骤。在这个图中,我们同时使用了指数衰减计划和动量。这就像我们在 图 15-15 中的序列一样,但现在每个步骤施加的变化还包括动量,或者说是上一阶段变化的缩放版本。我们可以通过观察从每个点出发的两条线(一个是梯度,另一个是动量)来看到这一点。然后,这个总和就成为了新的变化。

F15029

图 15-29:同时使用指数衰减学习率和动量进行学习

在每一步中,我们首先找到梯度,并按当前的学习率η进行缩放,像以前一样。然后我们找到上一步的变化,按γ进行缩放,将这两项变化加到当前权重的位置。这个组合给出了这一步的变化。

图 15-30 展示了网格中第六步的特写,并给出了沿途每个点的误差。

这里发生了一件有趣的事:当球滚到山谷的右侧时,尽管梯度指向下方,它依然继续往上滚。这正是我们对一个真实的球的预期。我们可以看到它逐渐减速,然后最终重新滚下坡,超过了底部,但超过的程度比之前少,然后又减速并重新滚下,依此类推。

F15030

图 15-30:图 15-29 中的最后一步,以及每个点的误差

如果我们使用过多的动量,球可能会直接飞到另一边,完全离开了碗的范围;但如果我们使用过少的动量,球可能无法越过沿途遇到的*坦区。图 15-31 展示了我们从图 15-26 中得到的误差曲线。这里我们通过反复试验找到一个γ值来调整动量,以使我们的球能够越过*坦区,但仍然能够顺利停留在碗底的最小值处。

F15031

图 15-31:使用足够的动量跨越*坦区,但又不至于使球无法顺利地停在最小值的底部

找到合适的动量是另一个任务,在这方面我们需要利用经验和直觉,并结合反复试验,来帮助我们理解特定网络和数据的行为。我们也可以通过超参数搜索算法来寻找这个值。

将这些内容综合起来,我们找到梯度,用当前的学习率η对其进行缩放,加入按γ缩放的上一步变化,这就给出了我们的位置。如果我们将γ设为 0,那么就不加入上一步的任何变化,这就是“普通”(或称“原始”)的梯度下降。如果将γ设为 1,那么就会加入上一步的全部变化。通常我们使用大约 0.9 的值。在图 15-29 和图 15-31 中,我们将γ设为 0.7 以更好地说明这一过程。

图 15-32 展示了使用学习率衰减和动量进行 15 步学习的结果。球从左侧开始,滚下来,然后沿右侧滚得很远,再次滚下来并沿左侧滚上,依此类推,每次爬升的高度逐渐减少。

F15032

图 15-32:使用动量和衰减学习率的 15 步学习

动量帮助我们跨越*坦的*原并逃离鞍点的浅处。它还有一个额外的好处:帮助我们迅速下坡,即使学习率较小,我们也能提高效率。

图 15-33 显示了在我们由图 15-18 构成的两弯月数据集上训练时的误差。

F15033

图 15-33:使用带动量的迷你批量梯度下降训练我们的两弯月数据集的误差曲线。我们在 600 多个周期内就达到了零误差。

在这里,我们使用带有动量的迷你批量梯度下降(或 SGD)。与图 15-23 中的迷你批量曲线相比,它的噪声更大,因为动量有时会将我们推过目标位置,导致误差出现尖峰。使用仅迷你批量 SGD 时,图 15-23 中的误差需要大约 5,000 个周期才能达到接*零的误差。而使用动量,我们在 600 多个周期内就能达到目标。不错吧!

动量显然帮助我们更快地学习,这是一个好事。但是动量带来了一个新问题:选择动量值γ。正如我们所提到的,我们可以通过经验和直觉来选择这个值,或者使用超参数搜索来寻找给我们最佳结果的值。

Nesterov 动量

动量让我们能够从过去获取信息来帮助训练。现在让我们从未来获取信息。关键思想是,不仅仅使用我们当前所在位置的梯度,还使用我们预计将要到达位置的梯度。然后,我们可以利用一些“来自未来的梯度”来帮助我们。

因为我们无法真正预测未来,我们估算下一步的位置,并在那里使用梯度。这样做的思路是,如果误差面比较*滑,并且我们的估计相当准确,那么在我们估计的下一位置上找到的梯度与如果我们仅使用标准梯度下降(无论是否有动量)移动时实际到达的位置的梯度是接*的。

为什么使用来自未来的梯度有用呢?假设我们正从山谷的一侧滚下来,接*底部。在下一步中,我们越过了底部,最终到达了另一侧的墙上。正如我们之前看到的,动量会带着我们沿着墙上几步,随着动量的减弱逐渐减慢,直到我们转过身来重新下降。但如果我们能够预测到自己将会在远侧,我们就可以在现在的计算中包含那时的梯度。因此,来自未来的推力让我们不再像之前那样向右上方移动过远,而是使我们移动的距离稍微小一点,这样我们就不会过度越过底部,最终接*山谷的底部。

换句话说,如果我们下一步的移动方向与上一步相同,我们现在就采取更大的步伐。如果下一步的移动会使我们后退,我们则采取更小的步伐。

让我们将其分解为几个步骤,以免混淆估计和现实。图 15-34 展示了这一过程。

如之前所述,我们假设从位置 A 开始,经过最*的更新后,最终到达位置 B,如图 15-34(a)所示。与动量类似,我们找到作用在 A 点的变化使我们到达 B(箭头 m),然后我们将其缩放为γ

现在进入新的部分,从图 15-34(b)开始。我们不再直接在 B 点找到梯度,而是首先将缩放后的动量加到 B 点,以得到“预测”误差 P。这是我们对下一步后将在误差面上最终位置的预测。如图 15-34(c)所示,我们在 P 点找到梯度 g,并按常规缩放得到 ηg。然后,我们通过将缩放后的动量 γm 和缩放后的梯度 ηg 加到 B 点,找到新的点 C,如图 15-34(d)所示。

F15034

图 15-34:带有 Nesterov 动量的梯度下降

请注意,我们根本没有使用 B 点的梯度。我们只是将到达 B 点的动量缩放版本和我们预测点 P 的梯度缩放版本结合起来。

还要注意,图 15-34(d)中的点 C 比我们用常规动量时会到达的点 P 更接*碗底。通过向未来看,我们发现自己将位于山谷的另一侧,从而能够利用指向左侧的梯度,防止我们滚得太远,翻到另一侧。

为了向开发此方法的研究人员致敬,它被称为Nesterov 动量,或Nesterov 加速梯度(Nesterov 1983)。它本质上是我们之前看到的动量技术的升级版。虽然我们仍然需要为γ选择一个值,但无需选择新的参数。这是一个很好的例子,展示了一个算法如何在不需要我们额外工作的情况下提高性能。

图 15-35 显示了 Nesterov 动量运行 15 步的结果。

F15035

图 15-35:运行 Nesterov 动量 15 步。它大约在七步内找到山谷底部,并保持在那里。

图 15-36 显示了使用 Nesterov 动量的标准测试案例的误差曲线。该图使用与图 15-33 中仅使用动量的结果相同的模型和参数,但其噪声较少,效率更高,在大约 425 个 epoch 时误差降到 0,而常规动量仅需要大约 600 个 epoch。

f15036

图 15-36:使用 Nesterov 动量的迷你批次 SGD 的误差。系统在约 600 个 epoch 时达到零误差。图表显示了 1,000 个 epoch。

每当我们使用动量时,都值得考虑使用 Nesterov 动量。它不需要我们提供额外的参数,但通常能更快地学习并且噪声更小。

Adagrad

我们已经看到两种动量方法,它们帮助我们突破*台期并减少过度波动。我们在更新神经网络中所有权重时使用相同的学习率。在本章早些时候,我们提到过使用一个针对每个权重量身定制的学习率 η 的想法。

一些相关算法使用了这个思想。它们的名字都以 Ada 开头,代表“自适应(adaptive)”。

让我们从一个名为 Adagrad 的算法开始,它是 自适应梯度学习(Duchi、Hazan 和 Singer 2011)的缩写。顾名思义,该算法会根据每个权重自适应地调整梯度的大小。换句话说,Adagrad 给了我们一种按权重逐个进行学习率衰减的方法。对于每个权重,Adagrad 会使用该更新步骤中的梯度,将其*方并加入该权重的累加和中。然后,梯度会被这个累加和衍生出的值除,以得到用于更新的值。

由于每个步骤的梯度在加入之前都会被*方,因此加入累加和的值始终是正数。因此,这个运行累加和会随着时间的推移变得越来越大。为了防止它失控,我们将每次变化除以这个逐渐增大的累加和,从而使每个权重的变化随着时间推移变得越来越小。

这听起来很像学习率衰减。随着时间的推移,权重的变化变得越来越小。这里的不同之处在于,学习的减速是基于每个权重的历史独特地计算的。

由于 Adagrad 实际上是自动为每个权重计算学习率,因此我们用来启动的学习率不像早期算法那样重要。这是一个巨大的好处,因为它解放了我们调节错误率的任务。我们通常将学习率 η 设置为像 0.01 这样的小值,然后让 Adagrad 从那里开始处理。

图 15-37 显示了 Adagrad 在我们测试数据上的表现。

它与我们其他曲线的总体形状相同,但需要很长时间才能接* 0。由于梯度的累加和随时间增大,最终我们会发现将每个新梯度除以与该累加和相关的值,会得到接* 0 的梯度。越来越小的更新是 Adagrad 错误曲线下降如此缓慢的原因,因为它试图消除最后剩余的误差。

我们可以通过不太复杂的工作来解决这个问题。

F15037

图 15-37:Adagrad 在我们测试设置中的表现

Adadelta 和 RMSprop

Adagrad 的问题在于我们应用于每个权重的梯度在更新步骤中不断变得越来越小。这是因为运行的累加和一直在增大。

不同于从训练开始时就将所有*方梯度求和,我们假设保持这些梯度的衰减和。我们可以将其视为每个权重的最新梯度的一个运行列表。每次更新权重时,我们会将新梯度添加到列表的末尾,并删除列表开头的最旧的一个。为了找到用来除以新梯度的值,我们将列表中的所有值相加,但首先将它们乘以一个与其在列表中的位置相关的数值。最*的值会乘以较大的数值,而最旧的值会乘以非常小的数值。通过这种方式,我们的累加和主要由最*的梯度决定,尽管它也会受到较老梯度的较小影响(Ruder 2017)。

这样,梯度的累加和(以及我们用来除以新梯度的值)可以根据我们最*应用的梯度而上下波动。

这个算法被称为Adadelta(Zeiler 2012)。这个名字来源于“adaptive”(自适应),类似于 Adagrad,而delta指的是希腊字母δ(delta),数学家通常用它来表示变化。这个算法自适应地根据每个梯度的加权累加和来改变每一步更新权重的量。

由于 Adadelta 会单独调整每个权重的学习率,任何在陡峭斜坡上的权重会减慢更新速度,避免过快变化,而当该权重处于较*坦的部分时,它将允许较大的步长更新。

与 Adagrad 一样,我们通常将学习率设置为大约 0.01 的值,然后让算法从那时起进行调整。

图 15-38 展示了在我们的测试设置上使用 Adadelta 的结果。

F15038

图 15-38:在我们的测试数据上使用 Adadelta 训练的结果

与图 15-37 中 Adagrad 的表现相比,这种方法表现更好。它*滑且在大约 2500 个 epoch 时达到 0,远远早于 Adagrad 的 8000 个 epoch。

Adadelta 的缺点是需要另一个参数,这个参数也叫做 gamma(γ)。它大致与动量算法中使用的γ参数相关,但它们足够不同,因此最好将它们视为两个不同的概念,恰好有相同的名称。这里的γ值告诉我们如何随着时间的推移缩小历史梯度列表中的梯度。较大的γ值“记住”较远的历史值,而较小的γ值则只关注最*的梯度。通常我们将这个γ设置为大约 0.9。

实际上,Adadelta 中还有另一个参数,希腊字母ε(epsilon)表示。这个细节用于保持计算的数值稳定性。大多数库会将其设置为一个由程序员精心选择的默认值,以使系统尽可能地正常工作,因此除非有特定需求,否则不应更改此值。

与 Adadelta 非常相似的算法,但使用了稍微不同的数学方法,叫做RMSprop(Hinton、Srivastava 和 Swersky 2015)。这个名字来源于它使用了均方根操作,通常缩写为 RMS,用来确定加入(或传播,因此名称中有“prop”)到梯度中的调整量。

RMSprop 和 Adadelta 大约在同一时间被发明,并且工作方式相似。RMSprop 也使用一个参数来控制它“记住”多少信息,这个参数也被命名为γ。同样,好的起始值大约是 0.9。

Adam

之前的算法共享一个思想,即为每个权重保存一个*方梯度的列表。然后它们通过将这个列表中的值加起来(可能在缩放之后)来创建一个缩放因子。每次更新步骤中的梯度都被这个总数除以。Adagrad 在构建缩放因子时给列表中的所有元素相等的权重,而 Adadelta 和 RMSprop 则认为较旧的元素不那么重要,因此它们对总体总数的贡献较小。

在将梯度放入列表之前对其*方在数学上是有用的,但当我们对一个数字进行*方时,结果总是正数。这意味着我们无法知道列表中的梯度是正还是负,而这是一个很有用的信息。为了避免丢失这些信息,我们可以保持一个不对梯度进行*方的第二个列表。然后我们可以利用这两个列表来推导出我们的缩放因子。

这就是一种名为自适应矩估计的算法方法,或更常见的叫法是Adam(Kingma 和 Ba 2015)。

图 15-39 展示了 Adam 的表现。

F15039

图 15-39:Adam 算法在我们的测试集上的表现

输出表现很好,只有略微的噪声,在大约 900 个训练周期时达到了接* 0 的误差,远远快于 Adagrad 或 Adadelta。缺点是 Adam 有两个参数,我们必须在学习开始时设置。参数是以希腊字母β(贝塔)命名的,分别称为“beta 1”和“beta 2”,写作β1 和β2。Adam 论文的作者建议将β1 设置为 0.9,将β2 设置为 0.999,这些值确实通常表现良好。

选择优化器

这并不是所有已提出并研究过的优化器的完整列表。还有许多其他优化器,并且新优化器不断出现,每个优化器都有其优缺点。我们的目标是提供一些最流行技术的概述,并理解它们是如何加速的。

图 15-40 总结了我们关于使用 Nesterov 动量的 SGD 和三个自适应算法(Adagrad、Adadelta 和 Adam)的两个月亮实验结果。

F15040

图 15-40:四种刚才介绍的算法在时间轴上的损失或误差。这张图只显示了前 4,000 个训练周期。

在这个简单的测试案例中,带有 Nesterov 动量的迷你批量 SGD 显然是最优选择,Adam 紧随其后。在更复杂的情况下,自适应算法通常表现更好。

在各种数据集和网络上,我们讨论的最后三种自适应算法(Adadelta、RMSprop 和 Adam)通常表现非常相似(Ruder 2017)。研究发现,在某些情况下,Adam 的表现略优于其他算法,因此通常是一个不错的起点(Kingma 和 Ba 2015)。

为什么会有这么多优化器?难道不应该找到最佳的一个并一直使用它吗?事实证明,不仅我们不知道什么是“最佳”优化器,而且也不可能有一个适用于所有情况的最佳优化器 无论我们提出哪个优化器作为“最佳”,我们都可以证明,总是有可能找到某种情况,其中另一个优化器会表现更好。这个结果以其充满色彩的名字著称,即无免费午餐定理(Wolpert 1996;Wolpert 和 Macready 1997)。这保证了没有任何优化器会永远优于其他优化器。

请注意,“无免费午餐定理”并不意味着所有优化器是相同的。正如我们在本章的测试中所看到的,不同的优化器确实有不同的表现。该定理只告诉我们,没有一个优化器会永远优于其他优化器。

尽管没有一个优化器适用于所有可能的训练情况,但我们可以为任何特定的网络和数据组合找到最佳的优化器。大多数深度学习库提供了自动化搜索的例程,可以尝试多个优化器,并为每个优化器运行多个参数选择。无论我们是自己选择优化器及其参数,还是通过搜索的结果得到它们,我们都需要记住,最佳选择可能会因网络和数据集的不同而有所不同。一旦我们对网络或数据集做出重大更改,就应考虑检查是否有更好的优化器能提供更高效的训练。作为一个实际指南,许多人开始时使用 Adam,并采用其默认参数。

正则化

无论我们选择什么优化器,我们的网络都可能会遭遇过拟合。正如我们在第九章中讨论的,过拟合是训练过长时间的自然结果。问题在于,网络学得太好,以至于它只适应训练数据,并且在新数据发布后表现不佳。

延迟过拟合出现的技术称为正则化方法。它们允许我们在过拟合对性能影响过大之前进行更多轮次的训练,这意味着我们的网络有更多的训练时间来提高其性能。

Dropout

一种流行的正则化方法叫做 dropout。它通常以 dropout 层 的形式应用于深度网络(Srivastava 等人,2014)。dropout 层被称为 附加层补充层,因为它本身不进行任何计算。我们称之为层,并将其绘制为层,因为在概念上这样方便,并且让我们可以在网络图中包括 dropout。但我们并不认为它是一个真正的层(无论是隐藏层还是其他层),在描述一个特定网络由多少层组成时,我们也不将其计算在内。

Dropout 是一个占位符,用来告诉网络在前一层运行一个算法。它仅在训练期间启用。当网络部署时,dropout 层会被禁用或移除。

dropout 层的作用是暂时断开前一层中一些神经元的连接。我们给它一个参数,描述应该影响的神经元的百分比,在每个批次开始时,它会随机选择前一层中该百分比的神经元,并暂时断开它们的输入和输出连接。由于这些神经元被断开,它们不会参与任何前向计算,也不包含在反向传播中,并且进入它们的权重不会被优化器更新。当批次完成并且其他权重已经更新时,选择的神经元及其所有连接会被恢复。

在下一个批次开始时,该层再次选择一组新的随机神经元并暂时移除它们,重复这个过程直到每个训练周期结束。图 15-41 以图形方式展示了这个概念。

F15041

图 15-41:Dropout。 (a) 在评估批次之前,中间层(灰色)中的 50% 的四个神经元被选中断开连接。 (b) 我们对单个 dropout 层的示意图是一个对角斜线。右侧表示选择断开连接的神经元比例。由于 dropout 应用在前一层,因此在这个例子中,我们将它应用于三个全连接层中的中间层。

Dropout 通过防止神经元过度专注和主导,延缓了过拟合的发生。假设在一个图像分类系统中,一个神经元高度专门化于检测猫眼。这对于识别猫脸的图片很有用,但对于系统可能需要分类的其他所有照片来说就没什么用。如果网络中的所有神经元都专注于训练数据中仅仅一两种特征,那么它们在该数据上可能表现得非常好,因为它们能够发现它们训练时专门定位的特征。但整体系统在遇到缺少这些特征的新数据时,表现就会很差。

Dropout 帮助我们避免这种专门化。当一个神经元被断开时,其余的神经元必须进行调整以弥补空缺。因此,被断开的神经元可以自由执行更具普遍性任务,我们也就延迟了过拟合的发生。Dropout 通过在所有神经元中分散学习,帮助我们推迟过拟合。

Batchnorm

另一种正则化技术叫做批量归一化,通常简称为batchnorm(Ioffe 和 Szegedy 2015)。与 dropout 类似,batchnorm 可以作为没有神经元的层来实现。不同于 dropout,batchnorm 实际上会执行一些计算,尽管我们不需要指定任何参数。

Batchnorm 修改了从某一层输出的值。这个可能看起来有些奇怪,因为训练的主要目的是让神经元产生输出值,从而取得好的结果。我们为什么要修改这些输出呢?

回想一下,我们的许多激活函数,如 leaky ReLU 和 tanh,在接* 0 时效果最强。为了从这些函数中获得最大的收益,我们需要输入到它们的数字处于一个围绕 0 的小范围内。这正是 batchnorm 通过对所有层输出进行缩放和*移所做的。因为 batchnorm 将神经元输出移到接* 0 的小范围内,所以我们更不容易看到某个神经元学习到一个特定的细节并产生一个巨大的输出,从而压倒了其他神经元,进而延迟了过拟合的出现。Batchnorm 会在整个小批量的过程中,按这种方式对从前一层输出的所有值进行缩放和*移。它与网络中的权重一起学习这些缩放和*移的参数,使其取到最有用的值。

我们在激活函数之前应用 batchnorm,以便修改后的值能落在激活函数最受影响的区域。在实践中,这意味着我们不在输入 batchnorm 的神经元上使用激活函数(或者如果必须指定一个函数,那就是线性激活函数,它没有任何效果)。这些值进入 batchnorm 后,再被送入我们想要应用的激活函数。

该过程在图 15-42 中进行了说明。我们用来表示像 batchnorm 这样的正则化步骤的图标是一个黑色圆盘位于圆形内部,表示圆内的值被转换到一个更小的区域。在后续章节中,我们将看到其他类似的正则化步骤,我们会使用相同的图标。文本(或附*的标签)会标明应用的是哪种正则化方法。

F15042

图 15-42:应用一个 batchnorm 层。顶部:一个神经元后面接着 leaky ReLU 激活函数。底部:同一个神经元加上 batchnorm。激活函数被替换成线性函数,接着是 batchnorm(用一个黑色圆盘表示)和 leaky ReLU。

像 dropout 一样,batchnorm 推迟了过拟合的发生,使我们能够训练更长时间。

总结

优化是调整权重的过程,以便让我们的网络进行学习。核心思想从每个权重的梯度开始。我们沿着这个梯度前进,引导我们到达误差表面上的低点,因此称为梯度下降。这个过程中的最重要值是学习率。一种常见的技术是根据衰减计划随着时间的推移减少学习率。

我们讨论了几种高效的优化技术。我们可以在每个周期后调整权重(批量梯度下降),在每个样本后调整权重(随机梯度下降,或 SGD),或者在小批量样本后调整权重(小批量梯度下降或小批量 SGD)。目前,小批量梯度下降是最常用的技术,业内的惯例是简单地将其称为 SGD。我们可以通过使用动量来提高每种梯度下降的效率。我们还可以通过使用像 Adam 这样的算法,根据时间为每个权重计算一个自适应学习率,从而改善学习。最后,为了防止过拟合,我们可以使用像 dropout 或 batchnorm 这样的正则化技术。

由全连接层构成的深度网络能够做一些令人惊叹的事情。但如果我们通过不同的方式构建神经元层并添加一些支持计算,它们的能力会显著提升。在接下来的几章中,我们将探讨这些新层以及它们如何用于分类、预测,甚至生成图像、声音等。

第四部分

超越基础

第十六章:卷积神经网络

本章主要介绍一种深度学习技术,叫做卷积。卷积的应用之一是它已成为分类、处理和生成图像的标准方法。卷积在深度学习中易于使用,因为它可以很方便地封装在一个卷积层(也叫卷积神经层)中。在本章中,我们将探讨卷积背后的关键思想以及我们在实际操作中使用的相关技术。我们将看到如何排列一系列这些操作,创建一个操作层次结构,将一系列简单的操作转化为强大的工具。

为了保持具体性,本章我们将卷积的讨论集中在图像处理上。使用卷积的模型在这个领域取得了惊人的成功。例如,它们在基本分类任务中表现优异,如判断一张图片是豹子还是猎豹,或者是行星还是大理石。我们可以识别照片中的人物(Sun, Wang, and Tang 2014);检测并分类不同类型的皮肤癌(Esteva et al. 2017);修复图像损坏,如灰尘、划痕和模糊(Mao, Shen, and Yang 2016);并从照片中分类人的年龄和性别(Levi and Hassner 2015)。基于卷积的网络在许多其他应用中也很有用,例如自然语言处理(Britz 2015),我们可以分析句子的结构(Kalchbrenner, Grefenstette, and Blunsom 2014)或将句子分类到不同类别中(Kim 2014)。

引入卷积

在深度学习中,图像是三维张量,具有高度、宽度和通道数,即每个像素的值。灰度图像每个像素只有一个值,因此只有一个通道。以 RGB 存储的彩色图像有三个通道(分别对应红色、绿色和蓝色的值)。有时人们使用深度光纤大小来指代张量中的通道数。不幸的是,深度也用来指代深度网络中的层数,而光纤大小并没有广泛应用。为了避免混淆,我们总是将图像(三维张量的相关内容)的三个维度称为高度、宽度和通道数。按照我们的深度学习术语,每个提供给网络处理的图像都是一个样本。图像中的每个像素都是一个特征。

当一个张量通过一系列卷积层时,它通常会在宽度、高度和通道数上发生变化。如果一个张量恰好有 1 个或 3 个通道,我们可以将其视为一张图像。但如果一个张量有 14 个或 512 个通道,最好就不再将其看作图像了。这意味着我们不应该将张量的单个元素称为像素,因为这是一个以图像为中心的术语。相反,我们称它们为元素。图 16-1 直观地展示了这些术语。

F16001

图 16-1:左侧:当我们的张量具有一个或三个通道时,我们可以说它是由像素构成的。右侧:对于具有任意数量通道的张量,我们称每个通道的切片为一个元素。

在卷积层起核心作用的网络通常被称为卷积神经网络convnet,或CNN。有时人们也会说CNN 网络(这就是“冗余首字母缩略症综合症”[Memmott 2015]的一个例子)。

检测黄色

为了开始讨论卷积,让我们考虑处理彩色图像。每个像素包含三个数字:分别表示红色、绿色和蓝色。假设我们想创建一个灰度输出,它的高度和宽度与我们的彩色图像相同,但每个像素的白色量与其输入像素中的黄色量对应。

为了简化起见,假设我们的 RGB 值是从 0 到 1 的数字。那么一个纯黄色的像素具有红色和绿色的值为 1,蓝色的值为 0。当红色和绿色的值减少,或者蓝色的值增加时,像素的颜色将偏离黄色。

我们希望将每个输入像素的 RGB 值合并为一个从 0 到 1 的单一数字,表示“黄色度”,这是输出像素的值。图 16-2 展示了实现这一目标的一种方式。

F16002

图 16-2:将我们的黄色检测器表示为一个简单的神经元

这看起来很熟悉。它与人工神经元的结构相同。当我们将图 16-2 解释为一个神经元时,+1、+1 和−1 是三个权重,与颜色值相关的数字是三个输入。图 16-3 显示了如何将这个神经元应用于图像中的任何像素。

F16003

图 16-3:将图 16-2 中的神经元应用于图像中的一个像素

我们可以将这个操作应用于输入中的每个像素,为每个像素创建一个单独的输出值。结果是一个新的张量,宽度和高度与输入相同,但只有一个通道,如图 16-4 所示。

F16004

图 16-4:将图 16-3 中的神经元应用于输入中的每个像素,产生一个宽度和高度相同但只有一个通道的输出张量。

我们通常设想将神经元应用于左上角的像素,然后一次向右移动,直到到达行的末尾,然后对下一行重复这个过程,直到到达右下角的像素。我们说我们正在对输入进行扫描,或扫描输入。

图 16-5 显示了此过程在一只黄色青蛙图像上的结果。正如我们预期的那样,输入像素中黄色的含量越多,输出中对应的白色也越多。我们说神经元正在识别检测输入中的黄色。

F16005

图 16-5:我们的黄色检测操作的应用。右侧的图像根据左侧图像中对应源像素的黄色程度,从黑到白变化。

当然,黄色并没有什么特别之处。我们可以构建一个小神经元来检测任何颜色。当我们以这种方式使用神经元时,我们通常说它在对输入进行过滤。在这种情况下,权重有时被统称为过滤值,或者简称过滤器。继承自其数学根源的语言,权重也被称为过滤核,或简称。通常也会把整个神经元称作过滤器。是否将过滤器一词指代神经元,或者特指其权重,通常可以从上下文中得知。

这种将过滤器扫过输入图像的操作对应于一种数学操作,称为卷积(Oppenheim 和 Nawab 1996)。我们说图 16-5 的右侧是对彩色图像和黄色检测过滤器进行卷积的结果。我们还可以说我们将图像与过滤器卷积。有时我们将这些术语合并,称一个过滤器(无论是整个神经元,还是仅其权重)为卷积过滤器

权重共享

在上一节中,我们设想了将我们的神经元扫过输入图像,在每个像素上执行完全相同的操作。如果我们想加速这一过程,可以创建一个巨大的相同神经元网格,并同时应用于所有像素。换句话说,我们并行处理这些像素。

在这种方法中,每个神经元具有相同的权重。我们不需要在每个神经元的独立内存中重复相同的权重,而是可以想象这些权重存储在某个共享的内存中,如图 16-6 所示。我们说神经元是共享权重的。

F16006

图 16-6:我们可以将我们的神经元同时应用于输入中的每个像素。每个神经元使用相同的权重,这些权重存储在共享内存中。

这使我们能够节省内存。在我们的黄色检测器示例中,共享权重还使得更换我们要检测的颜色变得容易。我们无需改变成千上万个神经元(或更多)的权重,只需更改共享内存中的那一组权重。

我们实际上可以在 GPU 上实现这个方案,GPU 能够同时执行许多相同的操作序列。共享权重使我们能够节省宝贵的 GPU 内存,从而将其释放用于其他用途。

更大的过滤器

到目前为止,我们一直在将神经元扫过图像(或使用共享权重并行应用它),一次处理一个像素,仅使用该像素的值作为输入。在许多情况下,查看我们正在处理的像素周围的像素也是有用的。通常我们会考虑一个像素的八个直接邻居。也就是说,我们使用一个以该像素为中心的三乘三的小框中的值。

图 16-7 展示了我们可以通过这种方式使用三乘三数字块进行的三种不同操作:模糊处理、检测水*边缘和检测垂直边缘。

为了计算每个图像,我们将权重块依次放置在每个像素上,并将其下方的九个值与相应的权重相乘。然后将结果加起来,并将它们的和作为该像素的输出值。让我们看看如何用神经元来实现这个过程。

F16007

图 16-7:通过将三乘三数字模板在图像上移动来处理灰度图像中的青蛙(见图 16-5)。从左到右,我们对图像进行模糊处理,找到水*边缘,并找到垂直边缘。

为了简化起见,我们暂时保持灰度输入。我们可以将图 16-7 中的数字块视为权重或滤波器核。在这种情况下,我们有一个九个权重的网格,将其放置在九个像素值的网格上。每个像素值都与其相应的权重相乘,结果相加并通过激活函数,我们就得到了输出。图 16-8 展示了这个概念。

F16008

图 16-8:使用三乘三滤波器(蓝色)处理灰度输入(红色)

这张图展示了如何处理单个像素(显示为深红色)。我们将滤波器集中在目标像素上,并将输入中的每个九个值与其相应的滤波器值相乘。我们将这九个结果相加,并将总和通过激活函数。

在这个方案中,构成神经元输入的像素形状被称为该神经元的局部感受野,或者更简单地称为它的足迹。在图 16-8 中,神经元的足迹是一个正方形,边长为三个像素。在我们的黄色探测器中,足迹是一个单独的像素。当滤波器的足迹大于单个像素时,我们有时会通过称之为空间滤波器来强调这一特性。

请注意,图 16-8 中的神经元就像其他任何神经元一样。它接收九个数字作为输入,将每个数字与相应的权重相乘,将结果加在一起,并通过激活函数传递这个数字。它并不在乎这九个数字来自输入的一个正方形区域,甚至不在乎它们来自于一张图像。

我们通过与图像进行卷积来应用这个三乘三滤波器,就像之前一样,依次将其扫描过每个像素。对于每个输入像素,我们想象将三乘三的权重网格放置在该像素上,应用神经元并创建一个单一的输出值,如图 16-9 所示。我们称我们正在将滤波器集中在其上的像素为锚点(或参考点零点)。

F16009

图 16-9:将三乘三滤波器(中心)应用于灰度图像(左),生成新的单通道图像(右)

我们可以设计任何大小和形状的滤波器。实际上,小尺寸的滤波器最为常见,因为它们比大尺寸的滤波器更快速地进行计算。我们通常使用每边像素数为奇数的小方块(通常是 1 到 9 之间)。这样的方块使我们能够将锚点放在滤波器中心,这样可以保持对称,且更易于理解。

让我们把这个理论应用到实践中。图 16-10 显示了将一个七乘七的输入图像与一个三乘三的滤波器进行卷积的结果。请注意,如果我们将滤波器置于输入图像的角落或边缘,滤波器的覆盖区域会超出输入图像,神经元将需要一些不存在的输入值。我们稍后会处理这个问题。现在,我们先限制讨论那些滤波器完全覆盖图像位置的情况。这样,输出图像的大小就是五乘五。

我们通过观察空间滤波器来引出讨论,这些滤波器可以实现像模糊图像或检测边缘这样的功能。那么,这些功能为什么对深度学习有用呢?为了回答这个问题,让我们更仔细地看一下滤波器。

F16010

图 16-10:为了将滤波器与图像进行卷积,我们将滤波器在图像上移动,并在每个位置应用它。我们在这个图中略去了角落和边缘。

滤波器与特征

一些研究蟾蜍的生物学家认为,动物视觉系统中的某些细胞对特定类型的视觉模式敏感(Ewert 等,1985 年)。这个理论认为,蟾蜍在寻找与它喜欢吃的生物相关的特定形状,以及这些动物所做的某些动作。人们曾认为,蟾蜍的眼睛会吸收所有照射到它们的光,将这些信息传送到大脑,然后由大脑从结果中筛选寻找食物。而新的假设认为,眼睛中的细胞在这个检测过程中会自行完成一些早期步骤(如寻找边缘),只有当它们“认为”看到的是猎物时,才会发射信号并将信息传递给大脑。

该理论已扩展到人类视觉系统,进而提出了一个令人惊讶的假设:某些单一的神经元被精确地调节,只对特定人的图片作出反应。导致这一建议的最初研究展示了 87 张不同的图像,其中包括人类、动物和地标。在一位志愿者身上,他们发现了一个只在志愿者看到女演员詹妮弗·安妮斯顿的照片时才会激活的神经元(Quiroga 2005 年)。更有趣的是,这个神经元只会在安妮斯顿单独出现时激活,而在她与其他人(包括著名演员)合影时并不激活。

我们的神经元是精确的模式匹配设备这一观点并未得到普遍接受,但我们这里并不是在进行真正的神经科学和生物学研究。我们只是寻找灵感。而让神经元执行检测工作的这一想法,似乎是一个相当棒的灵感。

与卷积的关联在于,我们可以使用滤波器来模拟蟾蜍眼睛中的细胞。我们的滤波器同样能够挑选出特定的模式,并将其发现传递给后续滤波器,后者寻找更大的模式。我们在这个过程中使用的一些术语,回响了我们之前见过的术语。具体来说,我们一直在使用特征这个词来指代样本中的一个值。但在这个上下文中,特征一词也指输入中的一个特定结构,滤波器试图检测到它,比如边缘、羽毛或鳞片皮肤。我们说一个滤波器在寻找条纹特征、眼镜或跑车。延续这一用法,滤波器本身有时被称为特征检测器。当特征检测器扫描完整个输入后,我们称其输出为特征图(此处的一词来源于数学语言)。特征图告诉我们,逐像素地,图像中该像素周围的内容与滤波器所寻找的匹配程度。

让我们看看特征检测是如何工作的。在图 16-11 中,我们展示了使用滤波器在二值图像中找到短的、孤立的垂直白色条纹的过程。

F16011

图 16-11:使用卷积进行二维模式匹配。(a)滤波器。(b)输入。(c)特征图,已缩放至[0, 1]以便显示。(d)特征图中值为 3 的条目。(e)(d)中白色像素周围的(b)邻域。

图 16-11(a) 显示了一个三乘三的滤波器,值为−1(黑色)和 1(白色)。图 16-11(b) 显示了一个噪声输入图像,仅包含黑白像素。图 16-11(c) 显示了将滤波器应用于输入图像中每个像素的结果(外部边框除外)。这里的值从−6 到+3,我们将其缩放至[0, 1]以便显示。图像中值越大,滤波器与像素(及其邻域)之间的匹配度越好。值为+3 表示该像素与滤波器完美匹配。

图 16-11(d) 显示了图 16-11(c)的阈值版本,其中值为+3 的像素显示为白色,其他所有像素为黑色。最后,图 16-11(e) 显示了图 16-11(b)的噪声图像,并突出显示了图 16-11(d)中白色像素周围的三乘三像素网格。我们可以看到,滤波器找到了图像中与滤波器模式匹配的那些位置。

让我们看看为什么这样有效。在图 16-12 的顶部行中,我们展示了滤波器和图像的三乘三块区域,以及逐像素的结果。

F16012

图 16-12:将滤波器应用于两个图像片段。从左到右,每一行显示滤波器、输入和结果。最后一个数字是右侧三乘三网格的总和。

考虑顶部行中间显示的像素。黑色像素(这里显示为灰色),值为 0,不对输出产生影响。白色像素(这里显示为浅黄色),值为 1,根据滤波器的值乘以 1 或-1。在顶部行的像素中,只有一个白色像素(顶部中央)与滤波器中的 1 匹配。这给出了结果 1 × 1 = 1。其他三个白色像素与-1 匹配,给出了三个结果-1 × 1 = -1。将这些加在一起,我们得到-3 + 1 = -2。

在下方这一行中,我们的图像与滤波器匹配。滤波器上三个权重为 1 的部分正好位于白色像素上,输入图像中没有其他白色像素。结果是一个 3 的分数,表示完全匹配。

图 16-13 展示了另一个滤波器,这次是寻找对角线。让我们在同一图像上运行它。这个由三个白色像素组成的对角线被黑色像素包围,它在两个地方出现。

F16013

图 16-13:另一个滤波器及其在随机图像上的结果。(a) 滤波器。(b) 输入。(c) 特征图。(d) 特征图中值为 3 的条目。(e) (d)中白色像素周围的邻域。

通过在图像上滑动滤波器并计算每个像素的输出值,我们可以寻找许多不同的简单模式。实际上,我们的滤波器和像素值都是实数(而不仅仅是 0 和 1),因此我们可以制作出更复杂的模式,找到更复杂的特征(Snavely 2013)。

如果我们将一组滤波器的输出传递给另一组滤波器,我们可以寻找模式中的模式。如果我们将第二组输出传递给第三组滤波器,我们可以寻找模式中的模式中的模式。这个过程让我们能够从一组边缘开始,构建一组形状,如椭圆和矩形,最终匹配某个特定物体的模式,如吉他或自行车。

通过这种方式应用连续的滤波器组,再结合我们很快将讨论的另一种技术——池化,极大地扩展了我们能够检测的模式种类。原因在于,滤波器以层级方式工作,其中每个滤波器的模式都是前一个滤波器找到的模式的组合。这样的层级结构让我们能够寻找复杂度较高的特征,如朋友的面孔、篮球的纹理,或是孔雀羽毛末端的眼睛。

如果我们必须手动计算这些滤波器,图像分类将变得不切实际。在一连串八个滤波器中,如何确定合适的权重来告诉我们一张图片是小猫还是飞机?我们又该如何去解决这个问题呢?我们又怎么知道何时找到了最好的滤波器?在第一章中,我们讨论了专家系统,人们曾试图通过手动进行这种特征工程。对于简单问题来说,这是一个艰巨的任务,且复杂性增长非常迅速,真正有趣的问题,比如区分猫和飞机,似乎完全无法解决。

卷积神经网络(CNN)的美妙之处在于,它们实现了专家系统的目标,但我们无需手动计算滤波器的值。我们在前几章中看到的学习过程,包括测量误差、反向传播梯度以及改进权重,教会了 CNN 自行找到所需的滤波器。学习过程修改了每个滤波器的核心(即每个神经元中的权重),直到网络产生符合我们目标的结果。换句话说,训练过程调节滤波器中的值,直到它们找到能够帮助网络为图像中的物体分类的特征。这一过程可以在数百个甚至数千个滤波器中同时发生。

这看起来像是魔法。系统从随机数开始,学习需要寻找的模式,以区分钢琴、杏子和大象,然后学习如何将数字放入滤波器内核,以便找到这些模式。

这一过程在某些情况下能够接*完成,实属不易。它在广泛的应用中经常产生高度准确的结果,这是深度学习领域的伟大发现之一。

填充

之前,我们承诺会回到卷积滤波器位于输入张量的角落或边缘时会发生什么问题。现在让我们来看一下。

假设我们想对一个 10x10 的输入应用一个 5x5 的滤波器。如果我们位于张量的中间位置,如图 16-14 所示,那么我们的工作就很简单。我们从输入中提取出 25 个值,并将它们应用到卷积滤波器上。

F16014

图 16-14:一个位于张量中部的 5x5 滤波器。鲜红色的像素是锚点,而较浅的像素组成了感受野。

但是如果我们位于边缘上,或者接*边缘,如图 16-15 所示,会怎么样呢?

F16015

图 16-15:在边缘附*,滤波器的感受野可能会超出输入的边界。我们该如何处理这些缺失的元素呢?

滤波器的足迹悬挂在输入的边缘。那里没有输入元素。那我们如何计算滤波器的输出值,当它缺少一些输入时呢?

我们有几种选择。一种是禁止这种情况,只能将足迹放置在完全位于输入图像内部的位置。结果是输出的高度和宽度会变小。图 16-16 展示了这一想法。

尽管简单,但这是一个糟糕的解决方案。我们曾说过,我们通常会依次应用多个滤波器。如果每次都牺牲一个或多个元素环,那么我们在每一步通过网络时都会丧失信息。

F16016

图 16-16:我们可以通过不让滤波器跑到那么远来避免“掉出边缘”问题。使用 5x5 的滤波器时,我们只能将滤波器集中在这里标记为蓝色的元素上,将 10x10 的输入图像缩小为 6x6 的输出图像。

一个流行的替代方法是使用一种称为填充的技术,这可以让我们创建一个与输入具有相同宽度和高度的输出图像。其思想是,在输入的外侧添加一个额外元素的边框,如图 16-17 所示。所有这些元素都有相同的值。如果我们在所有新元素中放置零,这种技术被称为零填充。在实践中,我们几乎总是使用零,因此人们通常将零填充称为填充,理解为如果他们打算使用零以外的任何值,他们会明确说明。

F16017

图 16-17:解决“掉出边缘”问题的更好方法是添加填充或额外的元素(浅蓝色),围绕输入的边界。

边界的厚度取决于滤波器的大小。我们通常只使用足够的填充,以便滤波器可以集中在输入的每个元素上。如果我们不希望从两侧丢失信息,每个滤波器都需要对其输入进行填充。

大多数深度学习库会自动计算所需的填充量,以确保我们的输出与输入具有相同的宽度和高度,并将其作为默认设置应用。

多维卷积

到目前为止,在本章中,我们主要考虑的是只有一个通道颜色信息的灰度图像。我们知道,大多数彩色图像有三个通道,表示每个像素的红、绿、蓝分量。让我们看看如何处理这些图像。一旦我们能够处理具有三个通道的图像,就能处理任何通道数的张量。

为了处理具有多个通道的输入,我们的滤波器(其足迹可以是任何形状)需要具有相同数量的通道。这是因为输入中的每个值都需要在滤波器中有一个相应的值。对于 RGB 图像,一个滤波器需要三个通道。因此,一个三乘三的滤波器需要三个通道,共 27 个数字,如图 16-18 所示。

F16018

图 16-18:一个具有三行三列足迹的三通道滤波器。我们已经对数值进行了着色,以显示它们将与哪个输入通道的数值相乘。

为了将这个卷积核应用于三通道的彩色图像,我们像之前一样进行操作,但现在我们以块(或三维张量)的形式来思考。

让我们以图 16-18 中的滤波器为例,它有一个三乘三的覆盖面积和三个通道,并用它来处理一个具有三个颜色通道的 RGB 图像。对于每个输入像素,我们像之前一样将滤波器的覆盖面积放置在该像素上,并将图像中的每个 27 个数值与滤波器中的 27 个数值相匹配,如图 16-19 所示。

F16019

图 16-19:使用三乘三乘三的卷积核对 RGB 图像进行卷积。我们可以想象,每个通道都被其在滤波器中的对应通道滤波。

在图 16-19 中,我们的输入有三个通道,所以我们的滤波器也有三个通道。可以帮助理解的是,可以将红色、绿色和蓝色通道分别与滤波器中对应的通道进行滤波,如图 16-19 所示。在实际操作中,我们将输入和滤波器视为三乘三乘三的块,每个 27 个输入值与其对应的滤波器值相乘。

这个概念可以推广到任意数量的通道。为了确保每个输入值都有一个对应的滤波器值,我们可以将这个必要的特性表述为一个规则:每个滤波器必须具有与其滤波的张量相同数量的通道。

多个滤波器

我们一直在一次应用一个滤波器,但在实际中这种情况很少见。通常我们将十个或上百个滤波器捆绑成一个卷积层,并同时(且独立地)将它们应用到该层的输入。

为了看清整体情况,假设我们得到了一张黑白图像,我们想要在像素中寻找几个低级特征,例如垂直条纹、水*条纹、孤立的点和加号。我们可以为每个特征创建一个滤波器,并独立地将每个滤波器应用到输入上。每个滤波器会产生一个包含一个通道的输出图像。将四个输出合并后,我们得到一个包含四个通道的张量。图 16-20 展示了这个概念。

F16020

图 16-20:我们可以将多个滤波器(以彩色显示)应用于相同的输入(以灰色显示)。每个滤波器在输出中创建自己的通道。然后它们被合并,形成一个包含四个通道的输出张量的单个元素。

现在,我们有一个包含四个通道的输出张量,而不是一个具有一个通道的灰度图像或一个具有三个通道的彩色图像。如果我们使用了七个滤波器,那么输出将是一个具有七个通道的新图像。这里需要注意的关键是,输出张量有一个通道对应每个应用的滤波器。

一般来说,我们的滤波器可以具有任何覆盖面积,我们可以将任意数量的滤波器应用于任何输入图像。图 16-21 展示了这个概念。

F16021

图 16-21:当我们将过滤器与输入进行卷积时,每个过滤器的通道数必须与输入的通道数相同。输出张量的通道数与过滤器的数量相同。

最左边的输入张量有七个通道。我们应用四个不同的过滤器,每个过滤器的尺寸是 3x3,因此每个过滤器的张量大小是 3x3x7。每个过滤器的输出是一个单通道的特征图。输出张量是通过堆叠这四个特征图得到的,因此它有四个通道。

虽然原则上我们应用的每个过滤器可以有不同的尺寸,但实际上,我们几乎总是为任何给定的卷积层使用相同的过滤器尺寸。例如,在图 16-21 中,所有的过滤器的尺寸都是 3x3。

让我们把上一节和这一节中的两个数值规则汇总起来。首先,卷积层中的每个过滤器必须与该层的输入张量具有相同的通道数。其次,卷积层的输出张量将具有与该层中过滤器数量相等的通道数。

卷积层

让我们更仔细地看看卷积层的机制。卷积层实际上是将多个过滤器组合在一起。它们独立地应用于输入张量,如图 16-21 所示,然后它们的输出被组合起来,生成一个新的输出张量。输入在这个过程中没有发生变化。

当我们在代码中创建一个卷积层时,我们通常会告诉库我们需要多少个过滤器、它们的尺寸应该是多少,以及其他可选的细节,比如是否使用填充以及我们希望使用什么激活函数——其余的由库来处理。最重要的是,训练过程中会改善每个过滤器的核值,使过滤器学习到能够产生最佳结果的值。

当我们绘制深度学习模型的图示时,通常会标注卷积层使用了多少个过滤器、它们的尺寸以及它们的激活函数。由于在输入的周围常常使用相同的填充,我们通常只提供一个值,而不是两个,默认它适用于宽度和高度。

就像全连接层中的权重一样,卷积层中过滤器的值最初是随机的,并通过训练得到改善。同样,像全连接层一样,如果我们在选择这些随机初始值时小心一些,训练通常会更快。大多数库提供多种初始化方法。一般来说,内置的默认值通常能很好地工作,我们很少需要明确选择初始化算法。

如果我们确实想选择一种方法,He 算法是一个不错的首选(He et al. 2015; Karpathy 2016)。如果该方法不可用,或者在特定情况下效果不好,Glorot 算法是一个不错的第二选择(Glorot 和 Bengio 2010)。

让我们来看几种有自己名字的特殊卷积类型。

一维卷积

一个有趣的特殊情况是对输入进行滤波的过程,称为1D 卷积。在这里,我们像往常一样对输入进行扫描,但只沿高度或宽度方向,而不在另一个方向上进行扫描(Snavely 2013)。当处理文本时,这是一种常见的技术,文本可以表示为一个网格,每个元素包含一个字母,行包含完整的单词(或固定数量的字母)(Britz 2015)。

基本思路见图 16-22。

F16022

图 16-22:1D 卷积示例。滤波器只向下移动。

在这里,我们创建了一个宽度与输入相同且高度为两行的滤波器。滤波器的第一次应用处理前两行的所有内容。然后,我们将滤波器向下移动,处理接下来的两行。我们不会水*移动滤波器。1D 卷积 这个名字来源于这种单一方向或维度的移动方式。

和往常一样,我们可以让多个滤波器在网格上滑动。我们可以对任何维度的输入张量执行 1D 卷积,只要滤波器本身仅在一个维度上移动。1D 卷积没有其他特别之处:它只是一个仅在一个方向上移动的滤波器。这个技巧有自己的名字,目的是强调滤波器的有限移动性。

1D 卷积的名称几乎与另一种完全不同的技术名称相同。让我们现在来看看这个技术。

1×1 卷积

有时我们希望在张量通过网络时减少通道的数量。通常这是因为我们认为某些通道包含冗余信息。这并不罕见。例如,假设我们有一个分类器,它识别照片中的主导物体。分类器可能有十几个滤波器,用于寻找不同种类的眼睛:人类眼睛、猫眼、鱼眼等等。如果我们的分类器最终会将所有生物合并成一个名为“生物”的类别,那么就没有必要关心我们发现的是哪种眼睛。只要知道输入图像中的某个区域有眼睛就足够了。

假设我们有一个层,其中包含检测 12 种不同类型眼睛的滤波器。那么该层的输出张量将至少有 12 个通道,每个滤波器一个通道。如果我们只关心是否找到眼睛,那么将这个张量通过合并或压缩这 12 个通道为一个表示每个位置是否有眼睛的通道会很有用。

这不需要任何新东西。我们希望一次处理一个输入元素,因此我们创建了一个尺寸为 1x1 的滤波器,正如我们在图 16-6 中看到的那样。我们确保滤波器的数量至少比输入通道数少 11 个。结果是一个与输入相同宽度和高度的张量,但多个眼睛通道被压缩成一个通道。

我们不需要做任何明确的操作来实现这一点。网络会学习滤波器的权重,使得网络能够为每个输入生成正确的输出。如果这意味着将所有通道组合在一起用于眼部识别,那么网络就会学会这样做。

图 16-23 展示了如何使用这些滤波器将一个拥有 300 个通道的张量压缩成一个宽度和高度相同,但只有 175 个通道的新张量。

F16023

图 16-23:应用 1×1 卷积进行特征降维

使用一对一滤波器的技术已经有了自己的名称。我们称之为应用一对一滤波器,通常写作1×1 滤波器,并使用它执行1×1 卷积(Lin, Chen, 和 Yan 2014)。

在第十章中,我们讨论了预处理输入数据的价值,以节省处理时间和内存。与其在数据进入系统之前一次性进行这种处理,不如让 1×1 卷积在网络内部动态地应用数据压缩和重构。如果我们的网络产生了可以压缩或完全移除的信息,那么 1×1 卷积可以找到这些数据并进行压缩或移除。我们可以在任何地方做到这一点,甚至在网络的中间。

当通道之间存在相关性时,1×1 卷积特别有效(Canziani, Paszke, 和 Culurciello 2016; Culurciello 2017)。这意味着前面层的滤波器已经生成了相互同步的结果,因此当一个通道值上升时,我们可以预测其他通道的上升或下降幅度。相关性越强,我们就越可能去除一些通道而几乎不丧失任何信息。1×1 滤波器非常适合这项工作。

1×1 卷积这个术语与我们在上一节中讨论的1D 卷积非常相似。但这些术语指的是完全不同的技术。当遇到这些术语时,值得花一点时间确保我们有正确的理解。

改变输出大小

我们刚刚看到如何通过使用 1×1 卷积改变张量中的通道数。我们还可以改变宽度和高度,这对于至少两个原因非常有用。第一个是,如果我们能使流经网络的数据变得更小,我们就可以使用更简单的网络,从而节省时间、计算资源和能源。第二个是,减少宽度和高度可以使某些操作,如分类,更高效,甚至更加准确。我们来看看为什么会这样。

池化

在前面的章节中,我们将每个滤波器应用于一个像素或一块像素区域。如果基础像素与滤波器的值匹配,滤波器就能找到它所寻找的特征。但是如果特征的某些元素稍微偏离了正确的位置呢?那么滤波器就无法匹配。如果图案中一个或多个部分存在,但稍微错位,滤波器就无法找到匹配的地方。如果我们不解决这个问题,那将是一个真正的麻烦。例如,假设我们在一页文本中寻找一个大写字母 T。由于印刷过程中发生了轻微的机械故障,一列像素被向下错位了一个像素。

我们仍然希望找到字母 T。这个情况在图 16-24 中有说明。

F16024

图 16-24:从左到右:一个五乘五的滤波器正在寻找字母 T,一个印刷错误的 T,滤波器覆盖在图像上方,以及滤波器的结果值。该滤波器无法报告找到字母 T 的匹配。

我们从一个五乘五的滤波器开始,寻找位于中心的 T 字形。我们使用蓝色表示 1,黄色表示 0 来进行说明。

我们将这个称为“完美滤波器”,稍后它的名称会更清晰。在其右侧是我们将要检查的印刷错误文本,标记为“完美图像”。再往右,我们将滤波器叠加到图像上。最右侧是结果。只有当滤波器和输入都是蓝色时,输出才会是蓝色。由于滤波器的右上角元素没有找到它预期的蓝色像素,因此整个滤波器报告了没有匹配,或者是一个较弱的匹配。

如果滤波器的右上角元素能够环顾四周并注意到它正下方的蓝色像素,它就能够匹配输入。实现这一点的一种方法是让每个滤波器元素“看到”更多的输入。最方便的数学方法是让滤波器稍微模糊一些。

在图 16-25 的上排中,我们选取了滤波器的一个元素并使其模糊。如果滤波器在这个更大、更模糊的区域中找到了蓝色像素,它就会报告找到了蓝色。如果我们对滤波器中的所有条目都进行这样的操作,就会创建一个“模糊滤波器”。由于这个扩展的范围,右上角的蓝色滤波器元素现在覆盖了两个蓝色像素,并且由于其他蓝色元素也覆盖了蓝色像素,滤波器现在报告了一个匹配。

F16025

图 16-25:上排:将一个滤波器元素替换为更大、更模糊的版本。下排:将模糊应用于每个滤波器元素,从而得到一个模糊滤波器。将其应用于图像时,可以匹配到印刷错误的 T 字形。

不幸的是,我们不能像这样模糊滤波器。如果我们通过模糊滤波器来修改它们的值,我们的训练过程将会出错,因为我们会改变那些我们试图学习的值。但是没有什么能阻止我们模糊输入!如果输入是图片,这一点尤其容易理解,但我们也可以模糊任何张量。所以,与其对完美的输入应用模糊滤波器,不如颠倒过来,对模糊的输入应用完美的滤波器。

图 16-26 的顶行显示了一个来自印刷错误 T 的单个像素,以及该像素在模糊处理后的版本。在我们将这种模糊应用到所有像素后,我们可以对这个模糊的图像应用完美的滤波器。现在,滤波器中的每个蓝色点下方都可以看到蓝色。成功了!

F16026

图 16-26:顶行:对输入中的一个像素进行模糊处理的效果。底行:我们将完美的滤波器应用于模糊版本的图像。这与印刷错误的 T 相匹配。

以此为灵感,我们可以提出一种模糊张量的技术。我们将这种方法称为池化,或者下采样。让我们通过一个具有单通道的小张量来看池化如何在数值上运作。假设我们从一个宽度和高度为四的张量开始,如图 16-27(a)所示。

F16027

图 16-27:池化,或下采样,一个张量。(a) 我们的输入张量。(b) 将(a)划分为 2x2 块。(c) *均池化的结果。(d) 最大池化的结果。(e) 我们的池化层图标。

让我们将这个张量的宽度和高度划分为 2x2 块,如图 16-27(b)所示。要模糊输入张量,请回顾图 16-7。我们看到,通过与一个内容全为 1 的滤波器进行卷积,图像变得模糊。这样的滤波器称为低通滤波器,或者更具体地说,称为框滤波器

要对张量应用框滤波器,我们可以使用一个 2x2 的滤波器,其中每个权重都是 1。应用这个滤波器仅仅意味着将每个 2x2 块中的四个数字相加。因为我们不希望数字无限增长,我们将结果除以 4,以得到该块的*均值。由于这个*均值现在代表整个块,我们只需保存它一次。我们对其他三个块也做相同的处理。结果是一个大小为 2x2 的新张量,如图 16-27(c)所示。这种技术称为*均池化

这个方法有一个变体:我们不计算*均值,而是直接使用每个块中的最大值。这称为最大池化(或更常见的,简称为max pooling),如图 16-27(d)所示。通常我们将这些池化操作视为由一个小的辅助层执行。在图 16-27(e)中,我们展示了这样的池化层的图标。经验表明,使用最大池化的网络学习速度比使用*均池化的网络更快,因此当人们提到池化而没有其他限定时,他们通常指的是最大池化。

池化的强大之处在于我们连续应用多个卷积层时。就像在滤波器和模糊输入的情况一样,如果第一个滤波器的值不在预期的位置,池化帮助第二层的滤波器仍然能够找到它们。例如,假设我们有两个连续的层,第二层的滤波器正在寻找第一层的强匹配,直接位于约一半值的匹配之上(也许这是某种动物的颜色特征)。在图 16-27(a)中的原始四乘四张量没有符合该模式的内容。存在一个 20 对 2,但 2 并不是 20 的一半。而且有一个 6 对 3,但 6 不是一个非常强的输出。所以第二层的滤波器会找不到它想要的匹配。那太遗憾了,因为确实有一个 20,接*位于 9 上方,这正是滤波器想要找到的匹配。问题是 20 和 9 不是完全垂直的邻居。

但是,最大池化版本中有 20 在 9 之上。池化操作正在向第二层传达,在右上角的两乘二块中有一个强匹配 20,并且在 20 正下方的块中有一个匹配 9。这就是我们想要的模式,滤波器会告诉我们它找到了一个匹配。

我们讨论了单通道的池化。当我们的张量具有多个通道时,我们会对每个通道应用相同的过程。图 16-28 展示了这个概念。

我们从一个高度和宽度为 6,通道数为 1 的输入张量开始,并用零环进行填充。卷积层应用三个滤波器,每个滤波器产生一个六乘六的特征图。卷积层的输出是一个尺寸为六乘六乘三的张量。然后,池化层在概念上考虑该张量的每个通道,并对其应用最大池化操作,将每个特征图缩小为三乘三。这些特征图随后像之前一样组合,生成一个宽度和高度为 3、具有三个通道的输出张量。

F16028

图 16-28:使用多个滤波器的池化或下采样

我们一直在使用二值图像和滤波器作为示例。这意味着,跨越单元边界的特征可能会被忽略,或者在池化后的张量中出现在错误的位置。当我们使用实值输入和滤波核时,这个问题会大大减少。

池化是一种强大的操作,它使得滤波器不再要求其输入位置必须完全正确。数学家称这种位置变化为*移偏移,如果某个操作对某种变化不敏感,就称其为对该操作不变。结合这些,我们有时会说池化使我们的卷积操作具备*移不变性,或偏移不变性(Zhang 2019)。

池化操作还具有一个额外的好处,就是减少了通过网络流动的张量的大小,这减少了内存需求和执行时间。

跨步

我们已经看到池化在卷积网络中的重要性。尽管池化层很常见,但我们可以通过将池化步骤直接集成到卷积过程中来节省时间。这个结合操作比两个独立的层更快。通过这两种过程得到的张量通常包含不同的值,但经验表明,快速的结合操作通常能产生与慢速的顺序操作一样有用的结果。

如我们所见,在卷积过程中,我们可以假设滤器从输入图像的左上角像素开始(假设我们有填充)。滤器产生一个输出,然后向右移动一步,产生另一个输出,再向右移动一步,依此类推,直到到达该行的右边缘。然后它向下一行移动,返回到左侧,过程重复进行。

但我们不必每次都按单步移动。假设我们在扫过滤器时,向右移动或跨步超过一个像素,或者向下移动超过一个像素,那么我们的输出将会比输入更小。我们通常只有在每个维度的步长大于一时,才会使用跨步(以及相关的跨步操作)这个词。

为了可视化跨步操作,让我们看看滤器从左上角开始的移动过程。当滤器从左到右移动时,它会生成一系列输出,这些输出会依次排列在输出中,同样是从左到右。当滤器向下移动时,新的输出会放置在输出的新一行单元格中。

现在假设我们不是每次水*移动滤器一个元素,而是每次向右移动三个元素。也许每次垂直移动时,我们会向下移动两行,而不是一行。我们仍然为每个输出增长一个元素。这个概念如图 16-29 所示。

F16029

图 16-29:我们的输入扫描在移动时可以跳过输入元素。这里我们在每个水*步长上移动三个元素,每个垂直步长上移动两个元素。

在图 16-29 中,我们在水*方向上使用了步幅三,在垂直方向上使用了步幅二。更常见的是我们会为两个轴指定一个单一的步幅值。在两个轴上都使用步幅二可以看作是每隔一个像素进行评估,无论是水*还是垂直。这将导致输出的尺寸是输入尺寸的一半,这意味着输出的尺寸与先使用步幅为一然后进行二乘二块池化操作后的尺寸相同。图 16-30 显示了滤波器在输入中对于不同步幅对的位置。

F16030

图 16-30:步幅示例。(a)在两个方向上使用步幅二意味着将滤波器集中在每隔一个像素的位置,既在水*方向也在垂直方向。(b)在两个方向上使用步幅三意味着将滤波器集中在每隔三个像素的位置。

当我们每一步移动一个元素时,一个三乘三的滤波器会多次处理相同的输入元素。当我们使用较大的步幅时,我们的滤波器仍然可以多次处理某些元素,如图 16-31 所示。

F16031

图 16-31:这个三乘三的滤波器在每个维度上以步幅为二进行移动,从左到右,从上到下读取。灰色的元素表示已经处理过的部分。绿色的元素是那些已经在之前的评估中被滤波器使用过,但这次会再次使用的部分。

重复使用一个输入值没有问题,但如果我们想节省时间,可能希望尽量减少计算量。这时,我们可以使用步幅来防止任何输入元素被重复使用。例如,如果我们在图像上移动一个三乘三的滤波器,我们可能会在两个方向上都使用步幅为三,这样就不会有任何像素被重复使用,正如图 16-32 所示。

F16032

图 16-32:与图 16-31 类似,只是现在我们在每个维度上都使用了步幅三。每个输入元素都只处理一次。

图 16-32 中的步幅操作生成的输出张量在高度和宽度上分别为输入张量高度和宽度的三分之一。考虑到在图 16-32 中,我们通过仅进行六次滤波器评估就处理了一个九乘六的输入元素块。通过这种方式,我们创建了一个三乘二的输出块,且没有明确的池化操作。如果我们不进行步幅操作然后再进行池化,我们需要更多的滤波器评估来覆盖相同的区域,之后还需要在滤波器的输出上执行池化操作。

与没有步幅的卷积加池化操作相比,步幅卷积有两个原因更快。首先,我们评估滤波器的次数更少,其次,我们没有一个明确的池化步骤需要计算。像填充一样,步幅可以(并且通常会)应用于任何卷积层,而不仅仅是第一个卷积层。

从步长学习到的滤波器通常与从没有步长的卷积后接池化学习到的滤波器不同。这意味着我们不能直接将训练好的网络中的卷积和池化对替换为步长卷积(或反之),然后期望它们仍然能正常工作。如果我们想改变网络的架构,就必须重新训练它。

大多数时候,使用步长卷积进行训练会给我们带来与卷积加池化相似的最终结果,而且所需时间更短。但有时,对于特定的数据集和架构,较慢的组合方法反而效果更好。

转置卷积

我们已经看到如何通过池化或步长来减小输入的大小,或者说下采样它。我们也可以增加输入的大小,或者说上采样它。与下采样一样,当我们上采样一个张量时,我们增加它的宽度和高度,但不会改变通道数。

与下采样一样,我们可以通过单独的层进行上采样,或者将其构建到卷积层中。一个独立的上采样层通常只是将输入张量的值重复我们要求的次数。例如,如果我们在宽度和高度上都将张量上采样两倍,每个输入元素就变成一个小的 2x2 正方形。图 16-33 展示了这一概念。

F16033

图 16-33:在每个方向上将张量上采样两倍。左图:输入张量。这个张量的每个元素都在垂直和水*方向上重复了两次。右图:输出张量。通道数不变。

我们已经看到,可以通过使用步长将下采样与卷积结合起来。我们也可以将上采样与卷积结合。这个结合步骤称为转置卷积分数步长膨胀卷积孔洞卷积转置一词来源于数学中的转置运算,我们可以用它来写出这个操作的方程式。孔洞atrous)是法语中“带孔”的意思。稍后我们将看到这些术语的来源。需要注意的是,一些作者将上采样和卷积的结合称为反卷积,但最好避免使用这个术语,因为它已经被用于不同的概念(Zeiler 等人 2010)。按照当前的做法,我们将使用转置卷积这一术语。

让我们来看一下转置卷积是如何工作的,以扩大一个张量的尺寸(Dumoulin 和 Visin 2016)。假设我们有一个宽度和高度都是 3x3 的初始图像(记住,通道数不会改变),并且我们希望使用一个 3x3 的滤波器处理它,但希望最终得到一个 5x5 的图像。一个方法是用两圈零进行填充,如图 16-34 所示。

F16034

图 16-34:我们的原始三乘三输入在外部网格中以白色显示,四周用两圈零填充。三乘三滤波器现在产生一个五乘五的结果,显示在中心。

如果我们在输入中添加更多的零环,我们会得到更大的输出,但它们会在中央五乘五的核心周围产生零环。这并不是特别有用。

另一种放大输入的方法是在卷积之前通过在输入元素之间和周围插入填充来扩展它。让我们试一下这个方法。我们在起始的三乘三图像中的每个元素之间插入一行一列零,并且像之前一样在外围用两圈零进行填充。结果是,我们的三乘三输入现在变成了九乘九,尽管其中有很多条目是零。当我们用三乘三的滤波器扫描这个网格时,我们得到一个七乘七的输出,如图 16-35 所示。

我们的原始三乘三图像显示在外部网格中,白色像素表示。我们在每个像素之间插入了一行和一列零(蓝色),然后用两圈零将整个图像包围。当我们将三乘三的滤波器(红色)与这个网格进行卷积时,我们得到了一个七乘七的结果,显示在中心。

F16035

图 16-35:转置卷积,将一个三乘三的滤波器卷积到七乘七的结果中

图 16-35 显示了 atrous(法语意思是“带孔”)卷积扩张卷积 这一命名的来源。通过在每个原始输入元素之间插入另一个行列,我们可以使输出变得更大,如 图 16-36 所示。现在我们的三乘三输入变成了十一乘十一的输入,输出则变成九乘九。

F16036

图 16-36:与图 16-35 相同的设置,只不过现在我们在原始输入像素之间插入了两行两列,产生了中心的九乘九结果

如果没有在输出中产生零的行列,我们无法再进一步推展这一技术。零的两行或两列限制是由于我们的滤波器具有三乘三的占地面积。如果滤波器是五乘五的,我们就可以使用最多四行或列的零。插入零的技术可能会在输出张量中产生一些类似棋盘的伪影。但库函数通常可以通过仔细处理卷积和上采样来避免这些问题(Odena、Dumoulin 和 Olah 2018;Aitken 等人 2017)。

反卷积与步幅之间存在联系。通过一些想象,我们可以将图 16-36 中的反卷积过程描述为每个维度上使用三分之一的步幅。我们并不是说我们字面上移动三分之一的元素,而是指我们需要在 11×11 的网格中走三步,才能相当于在原始 3×3 的输入中走一步。这种视角解释了为什么这种方法有时被称为分数步幅

就像步幅结合卷积与下采样(或池化)步骤一样,反卷积(或分数步幅)结合了卷积与上采样步骤。这带来了更快的执行时间,这总是令人愉快的。一个问题是我们可以增加输入尺寸的限制。在实践中,我们通常将输入维度加倍,并使用三乘三的滤波器,反卷积支持这种组合而不会在输出中引入多余的零。

与步幅一样,反卷积的输出与上采样加标准卷积的输出不同,因此,如果我们得到一个使用上采样加卷积的训练网络,我们不能仅仅将这两层替换为一个反卷积层并使用相同的滤波器。

反卷积因其效率更高且与结果相似(Springenberg 等,2015),而比上采样加卷积更为常见。

我们已经涵盖了很多基本工具,从不同类型的卷积到填充和改变输出大小。在下一节中,我们将把这些工具结合起来,创建一个完整但简单的卷积网络。

滤波器的层次结构

许多真实的视觉系统似乎是层次化的(Serre 2014)。从广义上讲,许多生物学家认为视觉系统的处理是在一系列层次中进行的,每一层都比前一层处理更高层次的抽象。

本章中我们已经从生物学中汲取了灵感,现在我们可以再次这样做。

让我们回到讨论蟾蜍视觉系统的话题。接收光的第一层细胞可能在寻找“虫子颜色的斑点”,下一层可能在寻找“来自前一层的斑点组合,形成类似虫子的形状”,接下来的层次可能在寻找“前一层形成的虫子形状组合,看起来像是有翅膀的胸部”,依此类推,直到最上层,它在寻找“苍蝇”(这些特征完全是虚构的,只是为了说明这个想法)。

这种方法在概念上很不错,因为它让我们能够按照图像特征的层次结构和寻找这些特征的滤波器来构建对图像的分析。它在实现上也很有优势,因为这是分析图像的一种灵活且高效的方式。

简化假设

为了说明层次结构的使用,让我们用卷积网络来解决一个识别问题。为了将讨论重点放在概念上,我们将使用一些简化。这些简化并不会改变我们要展示的原理;它们只是让图像更容易绘制和解读。

首先,我们将自己限制在二进制图像上:只有黑白,没有灰色阴影(虽然为了清晰起见,我们分别用米色和绿色表示 0 和 1)。在实际应用中,我们输入图像中的每个通道通常是一个范围在 [0, 255] 之间的整数,或者更常见的是一个范围在 [0, 1] 之间的实数。

其次,我们的滤波器也是二进制的,并且寻找输入中的精确匹配。在实际的网络中,我们的滤波器使用实数,并且它们将输入与不同程度的匹配,输出为不同的实数。

第三,我们手动创建所有的滤波器。换句话说,我们自己做特征工程。当我们讨论专家系统时,我们提到它们的最大问题是需要人工构建特征,而我们现在就是在做这件事!不过,这只是为了本次讨论。实际上,我们的滤波器值是通过训练学习得来的。由于我们目前不关注训练步骤,所以我们将使用手工制作的滤波器(我们可以将其视为通过训练得到的滤波器)。

第四,我们不会使用填充。这也是为了保持简单。

最后,我们的示例使用的是只有 12 像素边长的小输入图像。这个大小足以展示这些概念,但又足够小,可以让我们在页面上清晰地绘制所有内容。

在这些简化措施到位后,我们准备好开始了。

查找面具图

假设我们在一个博物馆工作,博物馆收到了大量的艺术品,我们的任务是将其整理好。我们的其中一项任务是找到所有与图 16-37 中的简单掩码相*的网格化面具画作。

F16037

图 16-37:在一个 12x12 网格上的简单二进制掩码

假设我们得到了中间的这个新掩码,如图 16-37 所示。我们将其称为候选图。我们想要确定它是否大致与原始掩码“相同”,即我们称之为参考图。我们可以将这两个掩码叠加在一起,看看它们是否匹配,如图 16-38 右侧所示。

F16038

图 16-38:测试相似性。左边是我们的原始掩码或参考图。中间是一个新的掩码或候选图。为了检查它们是否接*,我们可以将它们叠加在右边。

在这种情况下,它是一个完美匹配,很容易检测出来。但是如果一个候选图与参考图略有不同,像图 16-39 所示呢?这里其中一只眼睛向下移动了一个像素。

F16039

图 16-39:与图 16-38 类似,只不过候选者的左眼向下移动了一个像素。覆盖图现在不完美。

假设我们仍然希望接受这个候选者,因为它具有与参考面具相同的所有特征,并且这些特征大部分都在正确的位置。但覆盖图显示它们并不完全相同,因此简单的逐像素比较无法完成任务。

在这个简单的例子中,我们可以想出很多方法来检测相似的匹配,但让我们使用卷积来确定像图 16-39 中的候选者是否“像”参考面具。如前所述,我们将手动设计我们的滤波器。为了描述我们的层次结构,最简单的方法是从最后一步卷积开始,倒推到第一步。

让我们从描述参考面具开始。然后,我们可以判断候选者是否具有相同的特征。假设我们的参考面具的特征是:每个上角都有一只眼睛,鼻子在中间,嘴巴在鼻子下方。这个描述适用于我们在图 16-38 和图 16-39 中看到的所有面具。

我们可以用一个三乘三的滤波器来形式化这个描述,如图 16-40 左上角的网格所示。这将是我们最后几个滤波器之一:如果我们通过一系列卷积运算处理一个候选者,最终得到一个三乘三的张量(稍后我们将看到如何得到这个张量),那么如果这个张量与这个滤波器匹配,我们就找到了一个成功的匹配和一个可接受的候选者。带有×的单元格表示“不关心”。例如,假设候选者的一个面颊上有一个纹身,落在鼻子右侧的×区域内。这不会影响我们的判断,因此我们明确表示不关心该单元格中的内容。

F16040

图 16-40:面具识别的滤波器。上下行:寻找正面或侧面的面具。左列:特征化参考面具。中间:左侧网格所描述的张量的爆炸版。右侧:滤波器的 X 光视图(见正文)。

由于我们的滤波器只包含 1(绿色)和 0(米色)这两种值,我们无法直接做出像图 16-40 左上方示意图那样的滤波器。相反,由于它需要寻找三种不同类型的特征,我们需要将其重新绘制为一个具有三通道的滤波器,并将其应用于具有三通道的输入张量。一个输入通道告诉我们眼睛在输入中的所有位置,下一个告诉我们鼻子的所有位置,最后一个告诉我们嘴巴的所有位置。因此,我们的左上方示意图对应一个三乘三乘三的张量,如上中图所示,我们已将通道错开,以便可以分别读取每个通道。

我们画出了错开的版本,因为如果我们将该张量画成一个实心块,我们将无法看到 N(鼻子)和 M(嘴巴)通道上的大部分值。错开的版本很有用,但在我们开始比较张量的后续讨论时会变得非常复杂。相反,让我们画出张量的“X 射线视图”,如右上角所示。我们假设我们正在透视张量的各个通道,并在每个单元格中标记所有在该单元格中有 1 的通道名称。

由于这个过滤器用于寻找面向前方的面具,我们将其标记为 F。为了有趣,我们还可以制作一个寻找侧面面具的过滤器,我们称之为 P。我们不会查看任何由 P 匹配的候选图像,但我们在这里包括它,是为了展示这一过程的普遍性。接下来的层将在图 16-40 中的过滤器之前操作,它们将告诉我们在哪里找到了眼睛、鼻子和嘴巴。我们利用这些信息,在图 16-40 中通过使用不同的过滤器来识别这些面部特征的不同排列。

寻找眼睛、鼻子和嘴巴

让我们看看如何将一个 12×12 的候选图像转换为图 16-40 过滤器所要求的 3×3 网格。我们可以通过一系列卷积操作来实现,每个卷积后跟随一个池化步骤。由于图 16-40 中的过滤器是用来匹配眼睛、鼻子和嘴巴的,我们知道,在这些过滤器之前的卷积层必须产生这些特征。所以,让我们设计用于搜索这些特征的过滤器。

在图 16-41 中,我们展示了三个过滤器,每个过滤器的尺寸为 4×4。它们分别标记为 E4、N4 和 M4。它们分别用于检测眼睛、鼻子和嘴巴。稍后,为什么每个名字后面加上“4”的原因将会变得清晰。

F16041

图 16-41:三个用于检测眼睛、鼻子和嘴巴的过滤器

我们可以直接开始,将这三个过滤器应用于任何候选图像。由于图像的尺寸为 12×12,且我们不进行填充,输出的尺寸将为 10×10。如果我们将这些输出池化为 3×3,我们就可以将图 16-40 中的过滤器应用于图 16-41 中过滤器的输出,从而确定该候选图像是否是面向前方的面具、侧面的面具,或者都不是。

但应用 4×4 的过滤器需要大量的计算。更糟糕的是,如果我们想要寻找另一个特征(比如眨眼),我们必须构建另一个 4×4 的过滤器,并将其应用于整个图像。我们可以通过在此之前引入另一层卷积,使我们的系统更加灵活,同时也更快。

我们的 E4、N4 和 M4 滤波器在 图 16-41 中可以由哪些特征组成?如果我们将每个 4x4 滤波器看作是由 2x2 块组成的网格,那么我们只需要四种 2x2 块就可以组成所有三个滤波器。图 16-42 的顶行显示了这四个小块,下面的行则展示了它们如何组合成我们的眼睛、鼻子和嘴巴滤波器。我们分别将它们命名为 T、Q、L 和 R,代表上方、四分之一、左下角和右下角。

F16042

图 16-42:顶行:2x2 滤波器 T、Q、L 和 R。第二行,从左到右:滤波器 E4,将其分解为四个小块及其张量形式。最右边显示了 2x2x4 滤波器 E 的 X 射线视图。第三和第四行:滤波器 N4 和 M4。

从眼睛滤波器 E4 开始,我们将 4x4 滤波器分解为四个 2x2 块。E4 行中的第三个图显示了我们期望作为输入的四个通道,每个通道分别对应 T、Q、L 和 R,绘制为一个单一的张量,其中我们错开了通道。为了更方便地绘制该张量,我们使用了在 图 16-40 中看到的 X 射线约定。这样我们就得到了一个新的滤波器,大小为 2x2x4。这就是我们真正想用来检测眼睛的滤波器,所以我们去掉了“4”,直接称其为 E。

N 和 M 滤波器是通过从 T、Q、L 和 R 进行细分和组装的相同过程创建的。

现在想象将小的 T、Q、L 和 R 滤波器应用于候选图像。它们在寻找像素的模式。接着,E、N 和 M 滤波器寻找 T、Q、L 和 R 模式的特定排列。然后 F 和 P 滤波器寻找 E、N 和 M 模式的特定排列。因此,我们有一系列卷积层,每个输出都作为下一层的输入。图 16-43 以图形方式展示了这一过程。

F16043

图 16-43:使用三层卷积分析输入候选图像

现在我们已经有了滤波器,可以从底部开始处理输入。在此过程中,我们将看到应该放置池化层的位置。

应用我们的滤波器

让我们从 图 16-43 的底部开始,应用第一层的滤波器。 图 16-44 显示了将 T 滤波器应用于 12x12 候选图像的结果。由于 T 是 2x2 的,它没有中心,因此我们将其锚点任意放置在左上角。因为我们没有填充,且滤波器为 2x2,所以输出将是 11x11。在 图 16-44 中,T 找到完全匹配的每个位置都标记为浅绿色;否则,标记为粉红色。我们将此输出称为 T 图。

现在我们想确保,即使 T 匹配的位置与参考掩码中的位置不完全相符,E、N 和 M 滤波器依然能够成功找到 T 匹配。正如我们在上一节中所看到的,使滤波器对输入中的小位移具有鲁棒性的方法是使用池化。让我们使用最常见的池化形式:二乘二块的最大池化。

F16044

图 16-44:将 12 乘 12 的输入图像与 2 乘 2 的 T 滤波器进行卷积,产生 11 乘 11 的输出,或称为特征图,我们称之为 T 图。

图 16-45 显示了对 T 图进行最大池化的应用。对于每个二乘二的块,如果该块中至少有一个绿色值,则输出为绿色(回想一下,绿色元素的值为 1,红色元素的值为 0)。当池化块落在输入的右侧和底部时,我们只需忽略缺失的条目,并对实际存在的值进行池化。我们将池化的结果称为 T-pool 张量。

F16045

图 16-45:对 T 图应用二乘二最大池化,生成 T-pool 张量。绿色代表 1,粉色代表 0。

T-pool 的左上角元素告诉我们,当 T 滤波器放置在输入图像的左上角任意四个像素上时,是否匹配。在这种情况下,它确实匹配,因此该元素被标记为绿色(即,赋值为 1)。

让我们对其他三个第一层滤波器(Q、L 和 R)重复这个过程。结果显示在 图 16-46 的左侧部分。

四个滤波器 T、Q、L 和 R 一起生成一个包含四个特征图的结果,每个特征图在池化后为六乘六。回想一下 图 16-40,E、N 和 M 滤波器期望输入一个四通道的张量。为了将这些单独的输出合并成一个张量,我们可以像 图 16-46 中间那样将它们堆叠起来。和往常一样,我们随后使用 X 射线视图的约定将其绘制为二维网格。这为我们提供了一个四通道的张量,正是第二层所期望的输入。

F16046

图 16-46:左:对候选图像应用所有四个第一层滤波器并池化后的结果。中:将输出堆叠成一个单一张量。右:以 X 射线视图绘制六乘六乘四的张量。

现在我们可以转到第二层的滤波器。我们从 E 滤波器开始,见 图 16-47。

F16047

图 16-47:应用 E 滤波器。如同之前,从左到右,我们依次看到输入张量、E 滤波器(都以 X 射线视图显示)、应用该滤波器的结果、池化网格和池化后的结果。

图 16-47 展示了我们的输入张量(第一层的输出)和 E 滤波器,都是 X 光视图。在它们的右侧,我们可以看到应用 E 滤波器后的 E 图,接着是对 E 图应用 2x2 池化操作的过程,最后是 E 池特征图。我们已经可以看到,池化过程使得下一个滤波器能够匹配眼睛的位置,即使一个眼睛并没有出现在参考掩膜中的原始位置。

我们可以对 N 和 M 滤波器应用相同的处理过程,生成第二层的输出张量,如图 16-48 所示。

现在我们有一个三乘三的张量,包含三个通道,正好适合我们在图 16-40 中为 F 和 P 创建的滤波器。我们准备好进入下一层,即第三层。

F16048

图 16-48:计算 E、N 和 M 滤波器的输出,然后将它们堆叠成一个具有三个通道的张量

最后一步很简单:我们只需将 F 和 P 滤波器应用到整个输入上,因为它们的尺寸相同(也就是说,不需要对图像进行滤波器扫描)。结果是一个形状为一乘一乘二的张量。如果这个张量中第一通道的元素是绿色,那么 F 匹配,候选图像应该被接受为与我们的参考图像匹配。如果是米色,那么候选图像不匹配。

F16049

图 16-49:将 F 和 P 滤波器应用于第二层的输出张量。在这一层中,每个滤波器与输入的大小相同,因此该层生成的输出张量的大小为一乘一乘二。

完成了!我们使用了三层卷积来将候选图像与参考图像进行比较,判断它们是否相似或不相似。我们发现,候选图像中一个眼睛下移了一个像素,但仍足够接*参考图像,因此我们应该接受它。

我们通过创建一个层级结构来解决这个问题,而不仅仅是一个卷积序列。每一层卷积都使用了前一层的结果。第一层寻找像素中的模式,第二层寻找这些模式的模式,而第三层则寻找更大的模式,代表着正面或侧面的脸部。池化使得网络能够识别出一个候选图像,即使其中一个重要的像素块稍微发生了偏移。

图 16-50 展示了我们的整个网络。由于只有卷积层包含神经元,因此我们称之为全卷积网络

F16050

图 16-50:我们的全卷积网络用于评估掩膜。我们还展示了输入、输出和中间张量。带有嵌套框的图标表示卷积层,梯形图标表示池化层。

在图 16-50 中,带有框中框图标表示卷积层,梯形图标表示池化层。

如果我们想要匹配更多类型的面部特征,只需在最终层添加更多的过滤器。这样,我们就可以匹配任何我们想要的眼睛、鼻子和嘴巴的模式,且几乎没有额外的成本。通过减少网络中张量的大小,池化操作减少了我们需要进行的计算量。这意味着,使用池化的网络不仅比没有池化的版本更加稳健,还消耗更少的内存并且运行更快。

有一种感觉是,当我们逐步向上工作时,我们的过滤器变得越来越强大。例如,我们的眼睛过滤器 E 处理的是一个 4×4 的区域,尽管它本身只有 2×2,因为它的每个张量元素都是由一个 2×2 的卷积产生的。通过这种方式,层次结构中更高层次的过滤器能够寻找大型和复杂的特征,即使它们只使用小的(因此更快的)过滤器。

更高层次的网络能够以多种方式组合低层次的结果。假设我们要在照片中分类不同种类的鸟类。低层的过滤器可能会寻找羽毛或喙,而高层的过滤器则能够结合不同类型的羽毛或喙来识别不同种类的鸟类,所有这些都在一次通过照片的过程中完成。我们有时会说,使用卷积和池化技术来分析输入是应用了一个层次化的尺度

总结

本章讲述的内容完全是关于卷积的:即将一个过滤器或内核(也就是一组权重的神经元)应用到输入数据上的方法。每次我们将过滤器应用到输入时,会生成一个单一的输出值。过滤器可能只使用一个输入元素来进行计算,也可能有一个更大的区域并使用多个输入元素的值。如果一个过滤器的大小大于 1×1,那么在输入的某些位置,过滤器会“溢出”到边缘,这时会需要没有的数据。如果我们不将过滤器放置在这些地方,输出的宽度或高度(或者两者)会比输入的小。为了避免这种情况,我们通常通过在输入周围加上一圈零来进行填充,使得过滤器可以覆盖每个输入元素。

我们可以将许多过滤器打包成一个卷积层。在这样的层中,通常每个过滤器都有相同的大小和激活函数。每个过滤器会生成一个通道。该层的输出会有每个过滤器对应的通道。

如果我们想改变张量的宽度和高度,我们可以进行降采样(减少一个或两个维度)或升采样(增加一个或两个维度)。为了降采样,我们可以使用池化层,它会在输入块中找到*均值或最大值。为了升采样,我们可以使用升采样层,它会复制输入元素。以上任一技术都可以与卷积步骤结合使用。为了降采样,我们使用步幅,其中滤波器会在水*方向、垂直方向或两者上移动超过一个步骤。为了升采样,我们使用分数步幅或转置卷积,在此过程中我们在输入元素之间插入零行和/或零列。

我们看到,通过在一系列层中应用卷积并进行降采样,我们能够创建一个在不同尺度上工作的滤波器层次结构。这也意味着该系统具备*移不变性,即使模式的位置不完全符合预期,它仍能找到所需的模式。

在下一章中,我们将检查实际的卷积神经网络,并查看它们的滤波器,以了解它们是如何完成工作的。

第十七章:实际中的卷积神经网络(ConvNets)

在上一章中,我们讨论了卷积,并且通过一个简化的卷积网络(convnet)示例做了总结。

本章中,我们将介绍两个用于图像分类的实际卷积神经网络。第一个用于识别灰度手写数字,第二个用于识别彩色照片中占主导地位的物体,从 1,000 个不同类别中进行选择。

手写数字分类

手写数字分类是机器学习中的一个著名问题(LeCun 等,1989),这要感谢一个免费提供的数据集,称为 MNIST(发音为 em´-nist)。它包含 60,000 个手绘数字,从 0 到 9,每个数字都是白色的灰度图像,背景是 28 x 28 的黑色,标签标识数字。图像是由人口普查员和学生收集的。我们的任务是识别每张图像中的数字。

我们将使用一个为此任务设计的简单卷积神经网络(convnet),它包含在 Keras 机器学习库中(Chollet 2017)。图 17-1 展示了我们示意图中的架构以及传统的框和标签形式。

F17001

图 17-1:用于分类 MNIST 数字的卷积神经网络。输入图像是 28 x 28 x 1 的通道。两个卷积层后跟池化、丢弃法、扁*化,然后是一个密集层(或全连接层),另一个丢弃法层,最后是一个包含 10 个输出的密集层,后跟 softmax。上图:我们的示意图。下图:传统的框和标签形式。

网络的输入是 MNIST 图像,作为形状为 28 x 28 x 1 的 3D 张量提供(1 表示单一的灰度通道)。尽管在最后有两个全连接层,以及各种辅助层(如丢弃法、扁*化和池化),但我们仍然称其为卷积神经网络(convnet),因为卷积层主导了分类工作。第一个卷积层使用 32 个大小为 3 x 3 的滤波器对输入进行操作。每个滤波器的输出都经过 ReLU 激活函数处理后才离开该层。

由于未指定步幅,滤波器将在每个方向上移动一个元素。我们也没有应用任何填充。正如我们在图 16-10 中看到的,这意味着每次卷积后我们会丢失一圈元素。这样做在本例中是可以接受的,因为所有 MNIST 图像应该在数字周围有一个四个黑色像素的边框(并非所有图像都有这个边框,但大多数都有)。

第一层的输入张量是 28x28x1,因此第一卷积层中的每个滤波器深度为一个通道。由于我们有 32 个滤波器,并且输入没有填充,且滤波器的尺寸为 3x3,第一个卷积层的输出为 26x26x32。第二个卷积层包含 64 个滤波器,尺寸为 3x3。系统知道输入有 32 个通道(因为上一层有 32 个滤波器),因此每个滤波器创建为一个形状为 3x3x32 的张量。由于我们仍然没有使用填充,输入的外围再次丢失一圈,生成的输出张量是 24x24x64。

我们本可以使用步幅(striding)来减少输出的大小,但在这里我们使用了一个显式的最大池化层,池化块的大小为 2x2。这意味着,对于输入中每个不重叠的 2x2 块,层输出一个值,该值包含该块中的最大值。因此,这一层的输出是一个 12x12x64 的张量(池化层不会改变通道的数量)。

接下来,我们介绍一个 dropout 层,用斜线表示。正如我们在第十五章中看到的,dropout 层本身并不执行任何处理。相反,它指示系统对最*的包含神经元的前置层应用 dropout。dropout 前最*的层是池化层,但池化层没有神经元。当我们继续向后工作时,发现一个卷积层,它确实包含神经元。在训练过程中,dropout 算法会应用到这个卷积层(请记住,dropout 仅在训练期间应用,其他时候会被忽略)。在每个训练的周期前,卷积层中四分之一的神经元会被暂时禁用。这有助于防止过拟合。根据惯例,我们通常将 dropout 视为一个层,即使它不执行任何计算。请注意,由于 dropout 层会向后查找最*的具有神经元的层,我们本可以将其放置在池化层的左侧,网络的行为不会改变。根据惯例,当我们在卷积后进行池化时,我们通常将这两个层放在一起。

现在我们离开网络的卷积部分,为输出做准备。我们通常会在分类卷积神经网络的末端找到这些步骤,或者类似的步骤。第二个卷积层的输出是一个 3D 张量,但我们希望将其输入到一个全连接层,该层期望接收一个列表(或 1D 张量)。一个flatten层,如图所示,将任何维度的输入张量重组织成一个 1D 张量,通过将所有元素按顺序放在一起。这个列表从张量的第一行开始。我们取出第一个元素,并将它的 64 个值放在列表的开头。然后我们移到第二个元素,并将它的 64 个值放在列表的末尾。我们继续对每个元素执行此操作,然后对下一行重复此过程,以此类推。图 17-2 展示了这个过程。在这个重排过程中,张量中的值不会丢失。

F17002

图 17-2:flatten 层的作用。顶部:输入张量。中间:将每个通道转换为一个列表。底部:将这些列表一个接一个地放在一起,形成一个大的列表。

返回到图 17-1,flatten 层生成一个 12 × 12 × 64 = 9,216 个数字的列表。这个列表进入一个包含 128 个神经元的全连接层。这个层受到 dropout 的影响,在训练的每个批次开始时,四分之一的神经元会被暂时断开。

该层的 128 个输出进入一个最终的全连接层,该层有 10 个神经元。这一层的 10 个输出进入一个 softmax 步骤,将它们转换为概率。最后一层输出的 10 个数字给出了网络对输入图像属于 0 到 9 这 10 个可能类别的概率预测。

我们使用标准的 MNIST 训练数据训练了该网络 12 个周期。其在训练集和验证集上的准确率如图 17-3 所示。

曲线显示我们在训练和验证数据集上都达到了约 99%的准确率。由于曲线没有发散,我们成功避免了过拟合。

f17002

图 17-3:我们在图 17-2 中的卷积神经网络的训练表现。我们训练了 12 个周期,由于训练和验证曲线没有发散,我们成功避免了过拟合,并且在两个数据集上达到了约 99%的准确率。

让我们来看一些预测。图 17-4 显示了来自 MNIST 验证集的一些图像,标记了网络给出的最大概率对应的数字。在这个小的例子集中,网络做得非常完美。

f17004

图 17-4:这些是从 MNIST 验证集随机选择的 24 张图像。每张图像都标注了网络的输出,显示了具有最高概率的数字。网络正确地分类了这 24 个数字。

仅仅两个卷积层就赋予了这个系统足够的能力,达到了 99%的准确率。

VGG16

让我们来看看一个更大、更强大的卷积神经网络,叫做VGG16。它被训练用来分析彩色照片,并通过为 1,000 个不同类别分配概率来识别每张照片中的主要物体。

VGG16 是在一个著名的数据集上训练的,该数据集曾作为比赛的一部分使用。ILSVRC2014 竞赛是 2014 年的一个公开挑战赛,目标是构建一个神经网络,用于分类提供的图像数据库中的照片(Russakovsky 等,2015)。ILSVRC 是 ImageNet 大型视觉识别挑战赛(ImageNet Large Scale Visual Recognition Challenge)的缩写,因此这个图像数据库通常被称为 ImageNet 数据库。ImageNet 照片数据库可以在网上自由获取,至今仍被广泛用于训练和测试新的网络(更新、更大的 ImageNet 版本也可以使用[Fei-Fei 等,2020])。

原始的 ImageNet 数据库包含 120 万张图像,每张图像都手动标注了 1,000 个标签中的一个,描述照片中最突出的物体。挑战赛实际上包括了多个子挑战,每个子挑战都有自己的获胜者(ImageNet 2020)。其中一个分类任务的获胜者是 VGG16(Simonyan 和 Zisserman,2014)。VGG 是 Visual Geometry Group 的缩写,他们开发了这个系统。16 指的是该网络的 16 个计算层(还有一些辅助层,如 dropout 和 flatten,它们不进行计算)。

VGG16 在赢得比赛时打破了准确率的记录,尽管几年已经过去,但它依然流行。这主要是因为它在图像分类上仍然表现非常好(即使与更新、更复杂的系统相比),并且它具有简单的结构,易于修改和实验。作者已经发布了所有的权重,并说明了他们如何预处理训练数据。更棒的是,每个深度学习库都使得我们能够在自己的代码中轻松创建一个完全训练好的 VGG16 实例。由于这些优点,VGG16 通常成为涉及图像分类的项目的起点。

让我们来看一下 VGG16 的架构。大部分工作由一系列卷积层完成。辅助层沿途出现,最后会出现一些 flatten 层和全连接层,正如在图 17-1 中所示。

在将任何数据输入到我们的模型之前,我们必须以与作者处理训练数据相同的方式预处理数据。这涉及到确保每个通道通过从所有像素中减去特定值进行调整(Simonyan 和 Zisserman 2014)。为了更好地讨论网络中流动的张量形状,假设每张输入图片的高度和宽度为 224,以匹配网络训练时使用的 Imagenet 数据的维度,并且其颜色已正确预处理。完成这些步骤后,我们就可以将图像输入到网络中。

我们将 VGG16 架构呈现为六组层的系列。这些组是严格概念性的,仅仅是将相关层聚集在一起进行讨论的一种方式。前几组具有相同的结构:两层或三层卷积,后跟一个池化层。

第 1 组如图 17-5 所示。

f17005

图 17-5:VGG16 的第 1 组。我们使用 64 个大小为 3x3 的滤波器对输入张量进行卷积。然后,我们使用 64 个新的滤波器再次进行卷积。最后,我们使用最大池化将输出张量的高度和宽度减半。

卷积层对其输入应用了零填充,因此不会丢失宽度或高度。最大池化步骤使用大小为 2x2 的不重叠块。

VGG16 中的所有卷积层都使用默认的 ReLU 激活函数。

我们已经看到,池化对于帮助我们的滤波器识别即使被位移的模式是多么有用。出于与第十六章中匹配掩模时相同的原因,我们在这里也应用了池化。

图 17-5 中的组输出的是一个尺寸为 112x112x64 的张量。112 的数值来自于输入的 224x224 尺寸经过减半后的结果,而 64 来自于第二个卷积层中的 64 个滤波器。

第 2 组与第一组类似,只不过现在我们在每个卷积层中应用 128 个滤波器。图 17-6 展示了各层。该组的输出尺寸为 56x56x128。

f17006

图 17-6:VGG16 的第 2 组就像图 17-5 中的第一个块,只不过我们在每个卷积层中使用 128 个滤波器,而不是 64 个。

第 3 组继续每个卷积层中滤波器数量翻倍的模式,但它将卷积步骤重复三次,而不是两次。图 17-7 展示了第 3 组。最大池化步骤后的张量尺寸为 28x28x256。

f17007

图 17-7:VGG16 的第 3 组再次将滤波器的数量翻倍,达到 256 个,并且将卷积步骤重复三次,而不是像之前那样重复两次。

网络的第 4 组和第 5 组是相同的。每组由三次 512 个滤波器的卷积操作组成,后跟一个最大池化层。这些层的结构见于图 17-8。来自第 4 组的张量尺寸为 28 x 28 x 512,第 5 组最大池化层后的张量尺寸为 14 x 14 x 512。

f17008

图 17-8:VGG16 的第 4 组和第 5 组是相同的。每组包含三个卷积层,后跟一个 2x2 的最大池化层。

这结束了网络的卷积部分,现在进入总结部分。与我们在图 17-1 中看到的 MNIST 分类器相同,我们首先将来自第 5 组的张量展*。然后将其通过两个拥有 4,096 个神经元的全连接层,每个使用 ReLU,并且都经过 50% 设置的 dropout。最后,输出进入一个拥有 1,000 个神经元的全连接层。结果送入 softmax,产生 1,000 个概率值,分别对应 VGG16 被训练识别的每个类别。这些最终步骤,典型于这种风格的分类网络,见于图 17-9。

f17009

图 17-9:VGG16 的最终处理步骤。我们将图像展*,然后通过两个使用 ReLU 的全连接层,接着是 dropout,再通过一个带有 softmax 的全连接层。

图 17-10 展示了整个架构。

f17010

图 17-10:VGG16 架构汇总

该网络效果非常好。图 17-11 展示了四张使用手机相机在西雅图拍摄的图片。

f17011

图 17-11:四张在晴天时在西雅图拍摄的照片。图 17-10 中的卷积网络很好地识别了每张图像。

该卷积网络从未见过这些图像,但它处理得非常好。即使是右上角那个模糊的圆形物体,也被分配了合理的标签。

让我们通过查看 VGG16 的滤波器,来更仔细地了解其内部工作原理。

滤波器可视化,第一部分

VGG16 在分类中的成功归功于它的卷积层所学习到的滤波器。尽管很想看一下滤波器,并了解它们学到了什么,但滤波器本身是由大量数字组成的块,难以解读。我们不需要试图直接理解这些数字块,而是可以通过创建触发它们的图像来间接可视化滤波器。换句话说,一旦选择了一个要可视化的滤波器,我们可以找到一张图像,使该滤波器输出其最大值。那张图像向我们展示了这个滤波器在寻找什么。

我们可以通过一点基于梯度下降的小技巧来实现这一点,梯度下降算法是我们在第十四章中作为反向传播的一部分看到的。我们将梯度下降颠倒过来,创建梯度上升,用于沿着梯度上升并增加系统的误差。请记住,在第十四章的训练过程中,我们使用系统的误差创建梯度,通过反向传播将其向网络推送,从而使我们能够改变权重以减少该误差。对于滤波器可视化,我们将忽略网络的输出及其误差。我们唯一关心的输出是特定滤波器(或神经元)生成的特征图。我们知道,当滤波器看到它正在寻找的信息时,它会产生一个大的输出,因此,如果我们将该滤波器对给定输入图像的所有输出值相加起来,就可以知道该图像中滤波器所寻找的信息有多少。我们可以使用特征图中所有值的总和来替代网络的误差。

图 17-12 展示了这个概念。

f17012

图 17-12:可视化滤波器。特征图中所有值的总和作为网络的误差。

我们使用 VGG16,但在此可视化过程中,我们跳过最后卷积层之后的所有层。我们输入一个随机数网格,并提取我们要可视化的滤波器的滤波器图。这成为我们的误差。现在来到棘手的部分:我们使用这个误差来计算梯度,但我们根本不调整权重。网络本身和所有权重都是冻结的。我们只是不断计算梯度并将其推回,直到达到输入层,该层保存输入图像的像素值。到达这一层的梯度告诉我们如何更改这些像素值以减少我们知道的滤波器输出的误差。因为我们希望尽可能地激活神经元,所以我们希望“误差”尽可能大,因此我们改变像素值以增加而不是减少这个误差。这使得图片比之前更多地激活我们选择的神经元。

反复进行此操作后,我们将调整我们最初的随机像素值,使它们使滤波器输出尽可能大的值。当我们查看经过这种方式修改后的输入时,我们会看到一幅图片,使得该神经元产生巨大的输出,因此图片展示了滤波器正在寻找的内容(或至少给了我们一个大致的想法)(Zeiler 和 Fergus 2013)。我们将在第二十三章再次使用这个可视化过程,当我们看深度梦境算法时。

由于我们在输入图像中从随机值开始,每次运行此算法时都会得到不同的最终图像。但我们制作的每幅图像大致相似,因为它们都是基于最大化同一滤波器输出而生成的。

接下来我们看看使用这种方法生成的图像。图 17-13 展示了 VGG16 第一个块或组中的第二个卷积层的 64 个滤波器生成的图像(我们用标签block1_conv2表示这一层,其他层也有类似的名称)。在图 17-13 及之后的类似图像中,我们增强了色彩饱和度,使得结果更容易解读。

f17013

图 17-13:VGG16 中block1_conv2层的每 64 个滤波器产生最大响应的图像

看起来很多这些层正在寻找不同方向的边缘。有些值太微妙,难以让我们轻易解读。

接下来我们来看第 3 块,观察该块第一个卷积层的前 64 个滤波器。图 17-14 展示了最能激发这些滤波器的图像。

f17014

图 17-14:VGG16 中block3_conv1层的前 64 个滤波器产生最大响应的图像

现在开始有趣了!正如我们预期的那样,这里的滤波器正在寻找更复杂的纹理,结合了之前层所发现的简单模式。接着我们继续,看看第 4 块的第一个卷积层中的前 64 个滤波器,如图 17-15 所示。

f17015

图 17-15:VGG16 中block4_conv1层的前 64 个滤波器产生最大响应的图像

这些是对 VGG16 学习过程的迷人一瞥。我们可以看到它在分类图像中的物体时发现的一些有用结构。这些滤波器似乎在寻找包含多种不同类型流动和交织纹理的模式,就像我们在动物和我们周围世界的其他表面上看到的那样。

我们在这里真正看到了卷积层级的价值。每一层卷积都在寻找前一层输出中的模式,帮助我们从低级别的细节(如条纹和边缘)逐渐过渡到复杂且丰富的几何结构。

为了好玩,我们来看一下其中几个滤波器的特写。图 17-16 展示了从前几层选取的九种模式的更大视图。

f17016

图 17-16:VGG16 前几层中触发最大滤波器响应的一些手动选取图像的特写

图 17-17 展示了触发最后几层滤波器产生强烈响应的模式。

这些模式令人兴奋而美丽。它们也有一种有机的感觉,可能是因为 ImageNet 数据库包含了许多动物的图像。

f17017

图 17-17:VGG16 最后几层中触发最大滤波器响应的一些手动选取图像的特写

可视化滤波器,第二部分

另一种可视化过滤器的方法是通过 VGG16 运行图像,查看该过滤器生成的特征图。也就是说,我们将图像输入 VGG16 并让其通过网络,但像之前一样,我们忽略网络的输出。相反,我们提取我们感兴趣的过滤器的特征图,并将其像图像一样绘制出来。这是可能的,因为每个特征图总是只有一个通道,因此我们可以将其绘制为灰度图。

让我们试试看。图 17-18 展示了我们的输入图像,一只雄鸭。这是我们本节所有可视化的起始图像。

f17018

图 17-18 我们用来可视化过滤器输出的雄鸭图像

为了更好地理解,图 17-19 展示了网络第一层卷积层中第一个过滤器的响应。由于过滤器的输出只有一个通道,我们可以用灰度图来表示它。我们选择使用从黑色到红色再到黄色的热图来展示。

f17019

图 17-19:VGG16 中block1_conv1层第 0 个过滤器对图 17-18 中雄鸭图像的响应

这个过滤器在寻找边缘。考虑一下右下角的尾部。一个上面浅色下面深色的边缘会产生很大的输出,而另一个方向的边缘则输出非常低。变化较小的区域产生较小的输出,而颜色恒定的区域输出中等。

图 17-20 展示了第一个卷积层中前 32 个过滤器的响应。

f17020

图 17-20:VGG 卷积层block1_conv1中前 32 个过滤器的响应

这些过滤器中的很多似乎在寻找边缘,但也有些似乎在寻找图像的特定特征。我们来看一下从这层 64 个过滤器中手动选出的 8 个过滤器的特写,见图 17-21。

f17021

图 17-21:VGG16 第一个卷积层block1_conv1中八个手动选择的过滤器响应特写

顶排中的第三张图似乎在寻找鸭子的脚,或者它可能只对明亮的橙色物体感兴趣。底排最左边的图像看起来像是在寻找鸭子背后的波浪和沙滩,而其右边的图像似乎最强烈地响应蓝色波浪。通过对其他输入进行更多实验,我们可以更准确地确认这些解释,但看到我们从一张图像中能猜测出这么多内容,确实很有趣。

让我们进一步深入网络,进入第三个卷积层块。这里的输出比第一个块的输出小了四倍,因为它们已经通过了两层池化层。我们预计它们正在寻找特征的聚类。图 17-22 展示了第三块中第一个卷积层的响应。

f17022

图 17-22:VGG 卷积层block3_conv1中前 32 个滤波器的响应

很有趣的是,很多边缘检测似乎仍在进行中。这表明,强边缘对于 VGG16 来说是一个重要的线索,它在尝试识别图像内容时,即便是在第三组卷积中,边缘依然发挥着作用。但其他许多区域也在变亮。

让我们跳到最后一个块。图 17-23 展示了第五块卷积层中第一个卷积层的前 32 个滤波器的响应。

f17023

图 17-23:VGG 卷积层block5_conv1中前 32 个滤波器的响应

正如我们所预期的,这些图像变得更小,因为它们经过了两层池化层,每层都会将大小缩小一半。此时,鸭子几乎看不见,因为系统正在结合前面层的特征。一些滤波器几乎没有响应。它们可能负责寻找在鸭子图像中没有出现的高层次特征。

在第二十三章,我们将探讨一些利用卷积神经网络(convnet)滤波器响应的创意应用。

对抗样本

尽管 VGG16 在预测许多图像的正确标签时表现非常好,我们仍然可以通过一些微小到人眼无法察觉的方式修改图像,进而欺骗分类器,使其分配错误的标签。事实上,这个过程可以干扰任何基于卷积的分类器的结果。

欺骗卷积神经网络的技巧是创建一个被称为对抗样本的新图像。这个图像是通过在原始图像上添加对抗扰动(或更简单地说,扰动)来创建的。扰动是另一张图像,大小与我们要分类的图像相同,通常具有非常小的值。如果我们将扰动加到原始图像中,变化通常非常小,以至于大多数人无法察觉任何不同,哪怕是在最细微的细节上。但是,如果我们让 VGG16 去分类这个扰动后的图像,它会给出错误的答案。有时,我们可以找到一个单一的扰动,使得每张图像在输入到特定分类器时都会产生错误的结果,这种扰动我们称之为通用扰动(Moosavi-Dezfooli 等,2016 年)。

让我们看看这个过程的实际表现。在图 17-24 的左侧,我们看到一张老虎的图像。图像中的所有像素值都在 0 到 255 之间。系统正确地将其分类为老虎,置信度约为 80%,对于相关动物如虎猫和美洲豹的置信度较小。

f17024

图 17-24:对图像的对抗性攻击。左侧:输入图像及 VGG16 的前五个分类结果。中间:对抗性图像,其中像素值的范围大约为 [–2, 2],但此处为了便于观察,已将其缩放到 [0, 255] 范围。右侧:将图像与原始(未缩放)对抗性图像相加后的结果,以及新的前五个分类结果。

在 图 17-24 中间,我们展示了一张通过旨在寻找对抗样本的算法计算得出的图像。图像中的所有值大约都在 [–2, 2] 范围内,但为了便于观察,我们将这些值缩放到了 [0, 255] 范围。在 图 17-23 的右上角,我们展示了将老虎和对抗样本相加后的结果,因此原始老虎的每个像素都被改变了一个 [–2, 2] 范围内的值。对我们来说,老虎似乎没有变化,连细小的胡须看起来也没有改变。在该图像下方是 VGG16 对这张新图像的前五个预测结果。系统给出的预测完全不同,其中没有任何一个接*正确的类别。除了低概率的脑珊瑚类,系统甚至认为这张图像不是动物。

图 17-24 中的扰动图像可能对我们眼睛看起来是随机的,但其实并非如此。这张图像是专门计算出来的,目的是扰乱 VGG16 对老虎图像的预测。

计算对抗性图像有很多不同的方法(Rauber、Brendel 和 Bethge 2018)。这些方法为给定图像创建的扰动值的范围可能会有显著差异,因此为了找到最小的扰动,通常值得尝试几种不同的方法,这些方法也被称为 攻击。我们可以计算对抗样本以实现不同的目标(Rauber 和 Brendel 2017b)。例如,我们可以要求产生一个扰动,简单地使输入被错误分类。另一种选择是要求产生一个扰动,使得输入被分类为特定的目标类别。为了生成 图 17-24,我们使用了一种旨在使分类器的前七个预测结果不太可能出现的算法。也就是说,它接收起始图像和分类器的前七个预测结果,并生成一个对抗样本。当我们将对抗样本与输入图像相加,并交给分类器时,分类器的新前七个预测中不包含任何先前的前七个预测。

我们必须小心构建对抗扰动,这表明它们正在利用我们卷积神经网络中的某些微妙之处。

我们可能会找到构建抗攻击的卷积神经网络(convnets)的方法,但卷积神经网络可能本质上容易受到这些微妙图像操作的影响(Gilmer 等,2018)。对抗样本的存在表明,卷积神经网络仍然充满未知,我们不应将其视为万无一失的。关于卷积神经网络内部发生了什么,仍有许多值得探索的内容。

总结

在这一章中,我们看了几个实际的卷积神经网络:一个小型的用于分类手写 MNIST 数字的网络和一个较大的 VGG16 网络,用于分类照片。尽管我们的 MNIST 网络相当小,但它能够以约 99%的准确率对数字进行分类。

我们查看了 VGG16 的结构,接着看了两种不同类型的其滤波器可视化。我们发现该网络中的滤波器首先寻找简单的结构,如边缘,然后逐渐构建出复杂且美丽的有机图案。

最后,我们看到作为图像分类器使用的卷积神经网络容易受到欺骗,通过微小的像素值调整,这些调整对人类观察者是难以察觉的。

在下一章中,我们将探讨如何构建能够将输入压缩成更小表示的网络,然后再将其扩展以生成接*原始输入的结果。

第十八章:自编码器

本章介绍了一种特定类型的学习架构,叫做自编码器。可以把标准的自编码器看作是一种压缩输入的机制,这样它占用更少的磁盘空间,并能更快地进行传输,就像 MP3 编码器压缩音乐,或者 JPG 编码器压缩图像一样。自编码器得名于它通过训练自动学习如何最佳地编码或表示输入数据的理念。实际上,我们通常使用自编码器来完成两种任务:从数据集中去除噪声和减少数据集的维度。

我们将从研究如何在保留我们关心的信息的同时压缩数据开始。掌握了这些信息后,我们将查看一个小型自编码器。我们将使用它来了解基本概念,并讨论这些系统如何工作,以及它们如何通过数据表示让我们能够以有意义的方式操作数据。接下来,我们将构建一个更大的自编码器,进一步深入研究其数据表示。我们会发现,编码后的数据具有出乎意料的自然结构。这使得我们能够将自编码器的第二部分作为一个独立的生成器使用。我们可以为生成器输入随机数据,然后得到看起来像训练数据的新数据,但实际上是完全新的数据。

然后,我们通过包含卷积层来扩展网络的功能,使我们能够直接处理图像和其他二维数据。我们将训练一个基于卷积的自编码器来去噪颗粒状图像,从而恢复干净的输入。我们在本章结束时将讨论变分 自编码器,它能够创建一个更为整齐的编码数据表示。这使得将自编码器的第二部分作为生成器使用变得更加容易,因为我们能够更好地控制它将生成何种数据。

编码简介

压缩文件在计算中非常有用。许多人听着以 MP3 格式保存的音乐,这种格式能够大幅压缩音频文件,同时仍然保持与原音接*的音质(维基百科 2020b)。我们常常使用 JPG 格式查看图像,这种格式能够将图像文件压缩到原始大小的 20 倍,同时仍然看起来与原图接*(维基百科 2020a)。在这两种情况下,压缩后的文件只是原始文件的*似值。我们压缩文件越多(即保存的信息越少),就越容易检测到原始文件和压缩版本之间的差异。

我们将压缩数据或减少存储数据所需内存的行为称为编码。编码器是日常计算机使用的一部分。我们说 MP3 和 JPG 都会接受输入编码它;然后我们解码解压这个版本,恢复重建原始的某个版本。一般来说,压缩文件越小,恢复后的版本与原始版本的匹配度越差。

MP3 和 JPG 的编码器完全不同,但它们都是有损编码的例子。让我们来看一下这是什么意思。

无损与有损编码

在之前的章节中,我们使用损失一词作为错误的同义词,因此我们网络的误差函数也被称为其损失函数。在这一节中,我们使用这个词时有稍微不同的含义,指的是压缩后再解压时数据的退化程度。原始数据与解压后数据之间的差距越大,损失就越大。

损失的概念,或者输入的退化,和将输入变小的概念是不同的。例如,在第六章中,我们看到了如何使用摩尔斯电码传递信息。字母到摩尔斯电码符号的转换没有损失,因为我们可以从摩尔斯版本精确地重建原始信息。我们说将我们的信息转换或编码成摩尔斯电码是无损转换。我们只是改变了格式,就像改变一本书的字体或字体颜色一样。

为了看出损失是如何发生的,假设我们正在山中露营。在附*的山上,我们的朋友 Sara 正在庆祝她的生日。我们没有收音机或电话,但两组人都有镜子,我们发现可以通过反射阳光来进行摩尔斯电码通信。假设我们想发送消息:“HAPPY BIRTHDAY SARA BEST WISHES FROM DIANA”(为简便起见,我们省略了标点符号)。如果算上空格,这有 42 个字符。那是很多镜子摆动。于是我们决定省略元音,只发送“HPP BRTHD SR BST WSHS FRM DN”。这只有 28 个字母,所以我们可以在完整消息的三分之二的时间内发送它。

我们的新消息通过这种方式被压缩,丢失了一些信息(元音)。我们说这是一种有损的压缩方法。

我们不能简单地说丢失一些信息是否可以接受。若发生损失,我们能容忍的损失量取决于消息及其上下文。例如,假设我们的朋友 Sara 和她的朋友 Suri 正在露营,而且恰好她们是同一天生日。在这种情况下,“HPP BRTHD SR”就有歧义,因为她们无法知道我们在称呼谁。

测试一个转换是否有损或无损的简单方法是考虑它是否可以被反转,或者倒回去,以恢复原始数据。在标准的摩尔斯电码中,我们可以将字母转化为点划模式,然后再转换回字母,在这个过程中没有丢失任何信息。但是,当我们删除了消息中的元音字母时,这些字母就永远丢失了。我们通常可以猜测它们,但我们只是猜测,可能会猜错。去除元音字母会生成一个不可逆的压缩版本。

MP3 和 JPG 都是有损数据压缩系统。事实上,它们的有损程度非常高。但这两种压缩标准都是经过精心设计的,旨在丢弃“正确”的信息,以便在大多数日常情况下,我们无法分辨压缩版本和原始版本的差异。

这是通过仔细研究每种数据类型的特性以及它们是如何被感知的实现的。例如,MP3 标准不仅基于声音的一般特性,还基于音乐和人类听觉系统的特性。同样,JPG 算法不仅专注于图像中数据的结构,还依赖于描述人类视觉系统的科学。

在一个完美但不可能的世界里,压缩文件非常小,而且解压后的版本与其原始文件完全匹配。在现实世界中,我们为了文件大小而牺牲了保真度,即解压图像的准确性。一般来说,文件越大,解压后的文件与原始文件越匹配。这在信息理论上是有道理的:较小的文件比较大的文件包含的信息少。当原始文件存在冗余时,我们可以利用这一点,通过无损压缩来生成更小的文件(例如,当我们使用 ZIP 格式压缩文本文件时)。但一般而言,压缩通常意味着某种丢失。

有损压缩算法的设计师们努力工作,选择性地丢弃那些对我们来说在特定文件类型中最不重要的信息。通常,“什么重要”这个问题是一个争议问题,这也导致了多种不同的有损编码器(例如 FLAC 和 AAC 用于音频,JPEG 和 JPEG 2000 用于图像)。

表示的融合

在本章后面,我们将找到多个输入的数值表示,然后融合这些数据,以创建具有每个输入特征的新数据。数据融合有两种常见方法。我们可以将第一种方法描述为内容融合。这就是将两组数据的内容相互融合。例如,将牛和斑马的图像进行内容融合,就会得到类似图 18-1 的效果。

F18001

图 18-1:混合牛和斑马的图像内容。将每张图像缩放 50%,然后将结果加在一起,我们得到的是两张图像的叠加,而不是一个半牛半斑马的单一动物。

结果是两个图像的组合,而不是一个介于牛和斑马之间的混合动物。要得到一个混合动物,我们将使用第二种方法,称为参数混合,或表示混合。在这里,我们使用描述我们感兴趣事物的参数。通过混合两组参数,根据参数的性质和我们用来创建对象的算法,我们可以创建出混合了事物固有特征的结果。

例如,假设我们有两个圆形,每个圆形由一个中心、半径和颜色来描述,如图 18-2 所示。

F18002

图 18-2:我们想要混合的两个圆

如果我们混合这些参数(即,我们将表示圆形中心的 x 分量的两个值相互混合,将 y 的两个值相互混合,同样地,半径和颜色也如此),那么我们得到一个介于两个圆之间的圆形,如图 18-3 所示。

F18003

图 18-3:对两个圆进行参数混合意味着混合它们的参数(中心、半径和颜色)。

这种方法对于未压缩的对象效果很好。但如果我们尝试对压缩过的对象进行混合,通常无法得到合理的中间结果。问题在于,压缩后的形式可能与我们需要用来有意义地混合对象的内部结构相去甚远。例如,假设我们将“cherry”和“orange”这两个词的声音作为我们的对象进行混合。我们可以通过让两个人同时说出这两个词来混合这些声音,创造出图 18-1 中牛和斑马的音频版本。

我们可以将这些声音转化为书面语言看作是一种压缩方式。如果说出“cherry”这个词需要半秒钟,那么如果我们使用 MP3 格式,并选择流行的 128 Kbps 压缩设置,我们大约需要 8,000 字节(AudioMountain 2020)。如果我们使用 Unicode UTF-32 标准(每个字母需要 4 个字节),书面形式只需要 24 个字节,这比 8,000 小得多。由于字母来自字母表,且字母表有固定的顺序,我们可以通过在字母表中混合字母来混合这些表示形式。这种方法对字母不起作用,但让我们按照这个过程走下去,因为稍后某种形式的这个方法将对我们有用。

“cherry”和“orange”这两个词的首字母分别是 C 和 O。在字母表中,这两个字母所跨越的区域是 CDEFGHIJKLMNO。正中间是字母 I,因此 I 是我们混合的首字母。当第一个字母在字母表中的位置晚于第二个字母时,如 E 到 A,我们会倒数计算。当跨度中的字母数量是偶数时,我们选择较早的字母。如图 18-4 所示,这种混合方式给我们带来了 IMCPMO 这个字母序列。

F18004

图 18-4:通过找到字母表中每个字母的中点来混合书写的“cherry”和“orange”这两个词

我们想要的是一种压缩后,当解压时,声音听起来像是樱桃橙子的声音混合在一起。大声说出imcpmo这个词显然无法达到这个目标。更重要的是,它只是一个没有任何意义的字母串,既不代表任何水果,也不代表英语中的任何单词。

在这种情况下,混合压缩后的表示并不能给我们带来类似混合物体的效果。我们将看到,自编码器,尤其是本章末尾提到的变分自编码器的一个显著特点是,它们确实允许我们混合压缩版本,并且(在一定程度上)恢复出混合后的原始数据版本。

最简单的自编码器

我们可以构建一个深度学习系统来为我们想要的数据找出压缩方案。关键思想是在网络中创建一个地方,在这个地方,整个数据集必须用比输入数字少的数字来表示。毕竟,这就是压缩的核心。

例如,假设我们的输入由动物的灰度图像组成,分辨率为 100×100。每张图片有 100 × 100 = 10,000 个像素,所以我们的输入层有 10,000 个数字。假设我们随意选择,想要用仅仅 20 个数字来表示这些图像的最佳方式。

一种实现方式是构建一个如图 18-5 所示的网络。它只有一层!

F18005

图 18-5:我们的第一个编码器是一个单一的密集层,或者说全连接层,将 10,000 个数字转换为 20 个数字。

我们的输入是 10,000 个元素,进入一个只有 20 个神经元的全连接层。对于任何给定的输入,这些神经元的输出就是我们压缩后的图像版本。换句话说,通过这一层,我们构建了一个编码器。

现在的真正难题是,能够从这 20 个数字中恢复出原始的 10,000 个像素值,或者至少恢复接*它们的值。为此,我们在编码器后面加上一个解码器,如图 18-6 所示。在这种情况下,我们只需创建一个包含 10,000 个神经元的全连接层,每个神经元对应一个输出像素。

F18006

图 18-6:一个编码器(蓝色)将我们的 10,000 个输入转换为 20 个变量,然后一个解码器(米色)将它们转换回 10,000 个值。

因为数据量最开始是 10,000 个元素,中间是 20 个,最后又是 10,000 个,所以我们说我们已经创建了一个瓶颈。图 18-7 展示了这一理念。

F18007

图 18-7:我们说像图 18-6 所示的网络中间部分是瓶颈,因为它的形状像一个瓶子,顶部狭窄,像瓶颈。

现在我们可以开始训练我们的系统。每个输入图像也是输出目标。这个小型自编码器试图找到最佳方法,将输入压缩成仅有 20 个数字,然后再将其解压缩回原始目标,即输入本身。瓶颈处的压缩表示被称为编码,或潜在变量潜在意味着这些值本质上存在于输入数据中,只是等待我们去发现它们)。通常我们通过深度网络中间的一个小层来构建瓶颈,正如在图 18-6 中所示。这个层通常被称为潜在层瓶颈层。这个层上神经元的输出就是潜在变量。这个想法是,这些值以某种方式表示了图像。

这个网络没有类别标签(像分类器那样)或目标(像回归模型那样)。除了我们希望它进行压缩然后解压缩的输入外,系统没有其他信息。我们说自编码器是半监督学习的一个例子。它某种程度上是监督学习,因为我们给系统提供了明确的目标数据(输出应该与输入相同),而它又某种程度上不是监督学习,因为我们没有任何手动确定的标签或目标来标注输入。

让我们训练我们的小型自编码器,使用图 18-6 中的老虎图像,看看它的表现如何。我们会反复输入这张老虎图像,鼓励系统输出一张完整的老虎图像,尽管在瓶颈部分压缩到了仅仅 20 个数字。损失函数比较原始老虎图像的像素与自编码器输出的像素,并计算差异,因此像素差异越大,损失也越大。我们训练到它不再改进为止。图 18-8 显示了结果。右侧显示的每个误差值是原始像素值减去对应的输出像素值(像素已缩放到[0,1]的范围内)。

F18008

图 18-8:训练我们的小型自编码器(见图 18-6)来处理老虎图像。左侧:原始输入的老虎图像。中间:输出图像。右侧:原始与输出老虎图像之间的逐像素差异(像素值范围为[0,1])。自编码器似乎做得非常好,尽管瓶颈部分只有 20 个数字。

这太棒了!我们的系统将由 10,000 个像素值组成的图像压缩成了 20 个数字,现在它似乎又恢复了整张图像,甚至细致到那些细长、飘逸的胡须。任何像素的最大误差大约是 100 分之一。看起来我们找到了一个非常棒的压缩方法!

但是等一下。这不合理。没有办法仅凭 20 个数字重建那张老虎图像,除非做些“偷偷摸摸”的事情。在这种情况下,这种“偷偷摸摸”的事情就是网络完全过拟合并记住了图像。它简单地将所有 10,000 个输出神经元设置为接收这 20 个输入数字,并重建原始的 10,000 个输入值。更直白地说,网络仅仅记住了老虎。我们实际上并没有压缩任何东西。每个 10,000 个输入都传递到瓶颈层的 20 个神经元中,需要 20 × 10,000 = 200,000 个权重,然后这 20 个瓶颈结果都传递到输出层的 10,000 个神经元中,需要另外 200,000 个权重,这样就生成了老虎图像。我们基本上找到了一种方法,只使用 400,000 个数字就能存储 10,000 个数字。耶?

实际上,很多数字是无关紧要的。记住,每个神经元都有一个偏置,它与输入的加权值一起相加。输出神经元主要依赖它们的偏置值,而不是输入值。为了测试这一点,图 18-9 展示了给自编码器输入一张楼梯的图片的结果。它压缩和解压楼梯的表现并不差。相反,它主要忽略了楼梯,给我们返回了记住的老虎。输出图像并不完全是输入的老虎,如最右边的图像所示,但如果我们只看输出,几乎看不出楼梯的任何痕迹。

F18009

图 18-9:左:我们展示了一个仅针对老虎训练的小型自编码器,并输入了一张楼梯图像。中:输出是老虎!右:输出图像与原始老虎之间的差异。

图 18-9 右侧的误差条显示,我们的误差远大于图 18-8 中的误差,但老虎看起来仍然与原始图像相似。

让我们对“网络主要依赖偏置值”这一观点进行真正的压力测试。我们可以给自编码器输入一张到处都是零的图像。这样它就没有输入值可供处理,只有偏置值参与输出。图 18-10 展示了结果。

F18010

图 18-10:当我们给我们的小型自编码器输入一张纯黑的图像时,它使用偏置值给我们返回一张低质量但可识别的老虎图像。左:黑色输入。中:输出。右:输出与原始老虎之间的差异。注意,差异的范围从 0 到几乎 1,不同于图 18-9,其中差异的范围从大约 −0.4 到 0。

无论我们给这个网络什么输入,我们总是会得到某种版本的老虎作为输出。自编码器已经训练自己每次都生成老虎。

这个自编码器的真正考验是教它一堆图片,并观察它压缩这些图片的效果。让我们用一组 25 张照片再试一次,见图 18-11。

F18011

图 18-11:除了老虎,我们还使用了这 25 张照片来训练我们的微型自编码器。在训练过程中,每张图片都旋转了 90 度、180 度和 270 度。

我们通过不仅训练每张图像,还训练每张图像分别旋转 90 度、180 度和 270 度来扩展数据库。我们的训练集包括老虎(及其三种旋转版本)和图 18-11 中的 100 张带有旋转的图片,共计 104 张图像。

现在,系统必须记住如何用仅 20 个数字来表示这 104 张图片,结果它做得并不好也不奇怪。图 18-12 展示了当我们要求它压缩和解压老虎图像时,这个自编码器的表现。

F18012

图 18-12:我们用图 18-6 中的自编码器训练了图 18-11 中的 100 张图片(每张图片及其旋转版本),以及老虎的四种旋转。通过这个训练,我们给了它左边的老虎图片,它生成了中间的输出。

现在,由于系统不能作弊,结果完全不像老虎,一切都变得有道理了。我们可以看到,结果中有一些四向旋转对称性,这是由于我们训练了旋转过的输入图像版本。通过增加瓶颈层(或潜在层)中的神经元数量,我们可以做得更好。但由于我们希望尽可能压缩输入,增加瓶颈的值应作为最后的手段。我们更愿意尽可能少地使用值来完成最佳的工作。

让我们尝试通过考虑比目前使用的两个全连接层更复杂的架构来提高性能。

更好的自编码器

在本节中,我们将探索多种自编码器架构。为了进行比较,我们将使用第十七章中看到的 MNIST 数据库。回顾一下,这是一个大型的免费数据库,包含从 0 到 9 的手绘灰度数字,分辨率为 28×28 像素。图 18-13 展示了一些来自 MNIST 数据集的典型数字图像。

F18013

图 18-13:MNIST 数据集中的手写数字样本

要在这些数据上运行我们的简单自编码器,我们需要改变图 18-6 中输入和输出的大小以适应 MNIST 数据。每张图像有 28 × 28 = 784 个像素。因此,我们的输入和输出层现在需要 784 个元素,而不是 10,000 个。让我们在将图像输入网络之前,将二维图像展*为一个大的列表,并将瓶颈保持在 20 个潜在变量。图 18-14 显示了新的自编码器。在这个图示以及接下来的图示中,我们不会绘制开始时的展*层,或结束时的重塑层,这些层“还原”了展*操作,将 784 个数字重新转化为 28×28 的网格。

F18014

图 18-14:我们的两层自编码器用于 MNIST 数据

让我们训练 50 个周期(也就是说,我们将 60,000 个训练样本跑 50 遍)。一些结果显示在图 18-15 中。

F18015

图 18-15:通过我们训练过的自编码器(见图 18-14)处理 MNIST 数据集中的五个数字,使用 20 个潜在变量。上排:五个输入数据。下排:重构后的图像。

图 18-15 相当惊人。我们的两层网络学会了如何将每个 784 像素的输入压缩成仅 20 个数字,然后再把它扩展回 784 个像素。生成的数字虽然模糊,但仍然可以识别。

让我们尝试将潜在变量的数量减少到 10 个。我们预期结果会变得更糟。图 18-16 显示了情况确实更糟。

F18016

图 18-16:上排:原始的 MNIST 图像。下排:使用 10 个潜在变量的自编码器输出。

结果变得相当糟糕。数字 2 似乎变成了一个咬了一口的 3,而数字 4 似乎变成了 9。不过,这就是我们将这些图像压缩到 10 个数字时的结果。这远不足以让系统有效地表示输入。

这个教训是,我们的自编码器需要有足够的计算能力(也就是说,足够的神经元和权重)来弄清楚如何编码数据,同时需要足够的潜在变量来找到输入的有用压缩表示。

让我们看看更深的模型表现如何。我们可以使用任何类型的层来构建编码器和解码器。根据我们的数据,我们可以构建有很多层的深度自编码器,也可以构建只有少数层的浅层自编码器。现在,我们继续使用全连接层,但添加更多层来创建一个更深的自编码器。我们将从几层逐渐变小的隐藏层构建编码器阶段,直到达到瓶颈,然后再从几层逐渐变大的隐藏层构建解码器,直到它们的大小与输入相同。

图 18-17 展示了这种方法,现在我们有了三层编码和三层解码。

F18017

图 18-17:一个由全连接(或密集)层构建的深度自动编码器。蓝色图标:一个三层编码器。米色图标:一个三层解码器。

我们通常构建这些全连接层,使它们的神经元数目按 2 的倍数减少(然后再增加),就像在 512 和 256 之间切换一样。这个选择通常效果很好,但没有强制要求。

让我们像训练其他模型一样,训练这个自动编码器,训练 50 个 epoch。图 18-18 展示了结果。

F18018

图 18-18:我们深度自动编码器对图 18-17 的预测。上排:来自 MNIST 测试集的图像。下排:当我们向训练好的自动编码器提供测试数字时输出的结果。

结果稍微有些模糊,但与原图匹配得非常明确。将这些结果与图 18-15 进行对比,后者也使用了 20 个潜在变量,这些图像更清晰。通过提供额外的计算能力来寻找这些变量(在编码器中),以及在将它们转回图像时提供额外的计算能力(在解码器中),我们从 20 个潜在变量中获得了更好的结果。

探索自动编码器

让我们更仔细地看看图 18-17 中自动编码器网络生成的结果。

更深入地观察潜在变量

我们已经看到潜在变量是输入的压缩形式,但我们还没有看过潜在变量本身。图 18-19 展示了网络在图 18-17 中为我们的五张测试图像生成的 20 个潜在变量的图表,以及解码器从中构建的图像。

F18019

图 18-19:上排:网络在图 18-17 中为我们的五张图像生成的 20 个潜在变量。下排:从上述潜在变量解压出来的图像。

在图 18-19 中显示的潜在变量是典型的,因为潜在变量很少与它们所产生的输入数据有明显的关联。网络找到了自己专有的、高度压缩的形式来表示输入,而这种形式通常对我们来说毫无意义。例如,我们可以看到图中有几个一致的空洞(位于第 4、5 和 14 个位置),但从这一组图像中看不出为什么这些输入的值为 0(或接* 0)。查看更多数据肯定会有所帮助,但通常来说,解释的问题仍然存在。

潜在变量的神秘性是可以接受的,因为我们很少关心直接解释这些值。稍后,我们会通过混合和*均潜在值来玩这些变量,但我们并不关心这些数字代表什么。它们只是网络在训练过程中创建的一个私人代码,让它能够尽可能好地压缩和解压每个输入。

参数空间

尽管我们通常不关心潜在变量中的数值,但了解相似和不同输入所产生的潜在变量仍然是有用的。例如,如果我们输入两张几乎相同的七的图像,图像是否会被分配到几乎相同的潜在变量?或者它们可能会相隔非常远?

为了回答这些问题,让我们继续使用图 18-17 中的简单深度自编码器。但我们将编码器的最后一层从一个包含 20 个神经元的全连接层缩减为仅仅两个神经元,这样我们就只有两个潜在变量了。这样做的目的是,我们可以将这两个变量作为(x,y)对绘制在页面上。当然,如果我们仅用两个潜在变量生成图像,这些图像将会非常模糊,但这个练习是值得的,因为它让我们能够看到这些简单潜在变量的结构。

在图 18-20 中,我们对 10,000 个 MNIST 图像进行了编码,找出了每个图像的两个潜在变量,然后将它们作为点进行绘制。每个点根据其所属图像的标签进行了颜色编码。我们称像图 18-20 这样的图像是潜在变量空间的可视化,或者更简单地说是潜在空间

F18020

图 18-20:在只使用两个潜在变量训练深度自编码器后,我们展示了每个 10,000 个 MNIST 图像分配的潜在变量。

这里有很多结构!潜在变量并不是完全随机地被赋予数值。相反,相似的图像被分配了相似的潜在变量。1、3 和 0 似乎落在它们各自的区域里。许多其他数字似乎在图的左下方被打乱,分配了相似的值。图 18-21 展示了该区域的特写视图。

F18021

图 18-21:是图 18-20 左下角的特写图。

它并不是完全杂乱的。0 的区域有自己的带状区,而其他数字虽然有些混杂,但我们可以看到它们似乎都落入了明确的区域。

尽管我们预期这些图像会模糊,但我们还是用这些二维潜在值生成图像。从图 18-20 中我们可以看到,第一个潜在变量(我们在 X 轴上绘制的)取值范围从 0 到大约 40,第二个潜在变量(我们在 Y 轴上绘制的)取值范围从 0 到接* 70。

让我们按照图 18-22 中的方法,制作一个解码图像的方形网格。我们可以制作一个沿每个轴从 0 到 55 的框(Y 轴略短,但 X 轴略长)。然后我们可以选择这个网格内的(x,y)点,将这两个数输入解码器,生成一张图片。然后我们可以将这张图片绘制在对应网格的(x,y)位置。我们发现,在每个轴上取 23 个步骤生成的图像既密集又不会过于拥挤。

图 18-23 显示了结果。

F18022

图 18-22:通过解码来自图 18-20(以及稍微超出)中的(x,y)对来生成图像网格。左侧,我们选择一个位于大约(22,8)的(x,y)对。然后,我们将这两个数字传递给解码器,创建右侧的微小输出图像。

F18023

图 18-23:从图 18-22 中的潜在变量范围生成的图像

正如预期的那样,1 的喷洒位于顶部。令人惊讶的是,7 占据了右侧的主导地位。像以前一样,让我们在图 18-24 中看一下左下角的图像的特写。

F18024

图 18-24:从图 18-23 中的潜在变量特写范围生成的图像

数字经常模糊,而且它们没有落入清晰的区域。这不仅仅是因为我们使用了一个非常简单的编码器,而是因为我们将输入编码为仅两个潜在变量。使用更多的潜在变量时,事物变得更加分离和独特,但我们无法画出这些高维空间的简单图像。尽管如此,这仍然向我们展示了,即使是将潜在变量压缩到仅两个变量的极限,系统也以某种方式将这些值分配,形成了相似数字的聚类。

让我们更仔细地观察这个空间。图 18-25 显示了通过沿着四条线从图中提取(x,y)值并将其输入解码器生成的图像。

这证实了编码器将相似的潜在变量分配给相似的图像,并似乎构建了不同图像的簇,每种图像的变化都位于自己的区域。这是非常强的结构。随着我们将潜在变量的数量从这个极小的值(仅为两个)增加,编码器继续生成聚类区域,但它们变得更加清晰,重叠较少。

F18025

图 18-25:对于每个箭头,我们从起点到终点进行了八个等间距的步骤,生成了八个(x,y)对。这些对的解码图像显示在相应的行中。

混合潜在变量

现在我们已经看到了潜在变量中固有的结构,我们可以加以利用。特别是,让我们将一些潜在变量对混合在一起,看看是否能得到一张中间图像。换句话说,让我们对图像进行参数化混合,正如我们之前讨论的那样,其中潜在变量就是参数。

实际上,我们在图 18-25 中已经做了这个操作,通过将箭头一端到另一端的两个潜在变量混合。但在这里,我们使用的是只有两个潜在变量的自动编码器,因此它无法很好地表示图像,结果大多模糊。让我们使用更多的潜在变量,这样我们就能更好地理解这种混合或插值在更复杂模型中的表现。

让我们回到我们深度自编码器的六层版本,如图 18-17 所示,它有 20 个潜在变量。我们可以挑选出一对图像,找出每个图像的潜在变量,然后简单地*均每对潜在变量。也就是说,我们有一组包含 20 个数字的列表,表示第一张图像的潜在变量,和另一组 20 个数字的列表,表示第二张图像的潜在变量。我们将每个列表中的第一个数字合成,然后是第二个数字,以此类推,直到我们得到一个新的 20 个数字的列表。这就是我们传递给解码器的新潜在变量集合,解码器将生成一张图像。

图 18-26 展示了通过这种方式合成的五对图像。

正如我们所预期的那样,系统并不仅仅是将图像进行内容混合(就像我们在图 18-1 中对牛和斑马所做的那样)。相反,自编码器正在生成具有两个输入特征的中间图像。

F18026

图 18-26:在我们的深度自编码器中混合潜在变量的示例。第一行:来自 MNIST 数据集的五张图像。第二行:另外五张图像。第三行:通过直接*均上面两张图像的潜在变量,然后解码得到的图像。

这些结果并不荒谬。例如,在第二列,2 和 4 之间的混合看起来像是一个部分的 8。这是有道理的。图 18-23 告诉我们,数字 2、4 和 8 在只有 2 个潜在变量的图中相距很*,因此它们在具有 20 个潜在变量的 20 维图中仍然可能靠得很*。

让我们更仔细地看看这种潜在变量的混合。图 18-27 展示了三个新的数字对,并且通过六个等间距的插值步骤进行混合。

F18027

图 18-27:混合潜在变量。对于每一行,我们在最左侧和最右侧的潜在变量集之间进行混合。

每一行的最左边和最右边是来自 MNIST 数据的图像。我们为每个端点找到了 20 个潜在变量,创建了六个均匀间隔的潜在变量混合体,然后将这些混合的潜在变量传递给解码器。系统试图从一幅图像转换到另一幅,但它并没有生成非常合理的中间数字。即使是在中间行中从 5 转换到 5,过渡值几乎在两者之间分裂成两个独立的部分,然后才重新合并。顶部和底部行中靠*中间的一些混合图像根本不像任何数字。尽管两端可以识别,但这些混合图像迅速崩溃。自编码器中的潜在参数混合*滑地将图像从一个数字转换为另一个,但过渡图像只是奇怪的形状,而不是某种混合的数字。我们已经看到,这有时是因为经过密集区域,其中相似的潜在变量编码了不同的数字。一个更大的问题是概念上的。这些例子可能甚至不算错,因为如果我们能制造出一个部分为 0、部分为 1 的数字,它应该是什么样子还不清楚。也许 0 应该变得更瘦?也许 1 应该卷成一个圆圈?所以尽管这些混合图像不像数字,但它们是合理的结果。

这些插值的潜在值可能会落入潜在空间中的区域,这些区域没有附*的数据。换句话说,我们要求解码器从没有任何邻*数据的潜在变量值中重建图像。解码器正在生成某种东西,而且这个输出具有附*区域的一些特性,但解码器本质上是在猜测。

从新输入进行预测

让我们尝试使用在 MNIST 数据上训练的深度自编码器来压缩然后解压我们的老虎图像。我们将把老虎图像缩小到 28×28 像素,以匹配网络的输入大小,因此它看起来会非常模糊。

这只老虎是网络从未见过的,所以它完全没有能力处理这些数据。它尝试在图像中“看”一个数字并生成相应的输出。图 18-28 展示了结果。

F18028

图 18-28:使用我们训练在 MNIST 手写数字数据集上的 20 个潜在变量的深度自编码器,编码然后解码我们老虎图像的 28×28 版本,见图 18-8

看起来算法尝试找出一个位置,将几个不同的数字组合起来。中间的污点与老虎并不匹配,但也没有理由它应该匹配。

使用从数字中学到的信息来压缩和解压缩一只老虎,就像试图用削笔器的零件来建造一把吉他一样。即使我们尽最大努力,结果也不太可能是把好吉他。自编码器只能有意义地编码和解码它所训练过的数据类型,因为它只为潜在变量创建了表示该数据的意义。当我们用完全不同的东西来“惊讶”它时,它尽力而为,但效果不会很好。

基本的自编码器概念有几个变种。由于我们处理的是图像数据,而卷积是处理此类数据的自然方法,所以让我们使用卷积层来构建一个自编码器。

卷积自编码器

我们之前提到过,编码和解码阶段可以包含我们想要的任何类型的层。由于我们的示例使用的是图像数据,让我们使用卷积层。换句话说,我们来构建一个卷积自编码器

我们将设计一个编码器,使用多个卷积层逐步缩小原始 28 × 28 MNIST 图像,直到它变成 7 × 7。我们所有的卷积都会使用 3 × 3 的滤波器,并且会进行零填充。如图 18-29 所示,我们从一个包含 16 个滤波器的卷积层开始,然后跟一个 2 × 2 单元的最大池化层,得到一个形状为 14 × 14 × 16 的张量(我们本可以在卷积时使用步幅,但为了清晰起见,这里将步骤分开)。然后我们应用另一个卷积层,这次使用 8 个滤波器,并跟随一个池化层,生成一个形状为 7 × 7 × 8 的张量。最终的编码器层使用三个滤波器,生成一个形状为 7 × 7 × 3 的张量作为瓶颈。这样,我们的瓶颈用 7 × 7 × 3 = 147 个潜在变量表示了 768 个输入。

F18029

图 18-29:我们卷积自编码器的架构。在编码阶段(蓝色),我们有三层卷积层。前两层每层后面跟一个池化层,因此到第三层卷积层结束时,我们得到了一个形状为 7 × 7 × 3 的中间张量。解码器(米色)使用卷积和上采样将瓶颈张量恢复为 28 × 28 × 1 的输出。

我们的解码器反向执行这个过程。第一个上采样层生成一个形状为 14 × 14 × 3 的张量。接下来的卷积和上采样给我们一个形状为 28 × 28 × 16 的张量,最后的卷积产生一个形状为 28 × 28 × 1 的张量。如前所述,我们省略了开始时的扁*化步骤和结束时的重塑步骤。

由于我们有 147 个潜在变量,加上卷积层的强大功能,我们应该期望比之前仅有 20 个潜在变量的自编码器得到更好的结果。我们像之前一样训练了这个网络 50 个周期。在那个时候,模型仍在改进,但为了与之前的模型进行比较,我们在 50 个周期时停止了训练。

图 18-30 展示了来自测试集的五个例子,以及它们经过我们的卷积自编码器处理后解压的版本。

F18030

图 18-30:顶部行:来自 MNIST 测试集的五个元素。底部行:通过我们的卷积自编码器生成的图像,输入为上面一行的图像。

这些结果相当不错。图像并不完全相同,但非常接*。

只是为了好玩,尝试给解码器输入纯噪声。由于我们的潜在变量是一个 7×7×3 的张量,因此我们的噪声值需要是一个形状相同的 3D 体积。我们不打算画出这些数字块,而是只展示这个块的最上面一层 7×7 切片。图 18-31 展示了结果。

F18031

图 18-31:通过将随机值输入到我们的卷积神经网络解码阶段生成的图像

这只产生了随机的斑点图像,对于随机输入来说,这是一个合理的输出。

混合潜在变量

让我们在卷积自编码器中混合潜在变量,看看效果如何。图 18-32 展示了我们的网格,使用的图像与图 18-26 中的相同。我们找到顶部两行每张图像的潜在变量,将它们等量混合,然后解码插值变量,生成底部行。

结果有些模糊,尽管某些图像看起来像是上面几行图像的混合。然而,我们不应该太惊讶,因为我们并不清楚比如说,7 和 3 之间的数字应该是什么样子。

F18032

图 18-32:在卷积自编码器中混合潜在变量。顶部两行:来自 MNIST 数据集的样本。底部行:每张图像潜在变量等量混合后的结果。

让我们来看一下之前在图 18-27 中使用的三种混合方式的多个步骤。结果如图 18-33 所示。

F18033

图 18-33:将两张 MNIST 测试图像的潜在变量进行混合,然后解码

每行的左右两端是通过编码和解码 MNIST 图像生成的图像。中间部分是通过混合它们的潜在变量并解码得到的结果。这看起来比我们的简单自编码器好不了多少。所以,仅仅因为我们有更多的潜在变量,当我们尝试用那些与系统训练样本差异较大的输入进行重建时,依然会遇到问题。例如,在顶部一行中,我们并没有训练任何在某种意义上“介于”4 和 3 之间的输入图像,因此系统没有关于如何从代表这种情况的潜在值生成图像的有用信息。

从新输入中进行预测

让我们通过给卷积神经网络输入低分辨率的老虎图像,重复我们这个完全不公*的测试。结果如图 18-34 所示。

如果我们眯起眼睛看,它看起来像是眼睛周围、嘴巴两侧和鼻子周围的主要暗区被保留下来了。也许吧。或者这也可能只是想象。

和我们之前用全连接层构建的自动编码器一样,我们的卷积自动编码器试图在数字的潜在空间中找到某个地方的老虎。我们不应该期待它做得很好。

F18034

图 18-34:我们应用于卷积自动编码器的低分辨率老虎图像,以及结果。看起来不像老虎。

去噪

自动编码器的一个流行用途是去除样本中的噪声。一种特别有趣的应用是去除计算机生成图像中有时会出现的斑点(Bako et al. 2017; Chaitanya 2017)。这些明亮和暗淡的点,看起来像静态噪声或雪花,通常是在我们快速生成图像时出现的,未对所有结果进行细化。

让我们看看如何使用自动编码器去除图像中的亮点和暗点。我们将再次使用 MNIST 数据集,但这次,我们会在图像中添加一些随机噪声。在每个像素点,我们从均值为 0 的高斯分布中选择一个值,因此我们得到正值和负值,将它们加进来,然后将结果值截断到 0 到 1 的范围内。图 18-35 展示了应用了这种随机噪声的一些 MNIST 训练图像。

F18035

图 18-35:上:MNIST 训练数字。下:相同的数字,但加了随机噪声。

我们的目标是将训练过的自动编码器输入图 18-35 底部行的带噪数字版本,让它返回清理后的版本,就像图 18-35 顶部行中的数字一样。我们的希望是潜在变量不会编码噪声,所以我们只会得到数字。

我们将使用与图 18-29 中相同的总体结构的自动编码器,尽管它有不同数量的滤波器(Chollet 2017)。图 18-36 显示了架构。

F18036

图 18-36:去噪自动编码器

为了训练我们的自动编码器,我们将给它带噪的图像输入和它们相应的干净、无噪声的版本作为我们希望它生成的目标。我们将使用所有 60,000 张图像进行 100 轮训练。

在图 18-35 解码步骤的最后(也就是第三个卷积之后),张量的大小为 7 x 7 x 32,共计 1,568 个数字。所以这个模型中的“瓶颈”是输入大小的两倍。如果我们的目标是压缩,这将是糟糕的,但在这里我们是要去除噪声,所以最小化潜在变量的数量并不是特别关注的问题。

它表现如何?图 18-37 展示了一些噪声输入和自动编码器的输出。它很好地清理了像素,给我们带来了很棒的结果。

F18037

图 18-37:上排:添加噪声的数字。下排:通过我们模型(见图 18-36)去噪后的相同数字。

在第十六章中,我们讨论了显式的上采样和下采样层正在逐渐被淘汰,取而代之的是步幅和转置卷积。让我们遵循这一趋势,简化图 18-36 中的模型,得到图 18-38,该模型现在仅由五个卷积层组成。前两个卷积层使用步幅代替显式的下采样层,最后两个层使用重复代替显式的上采样层。请记住,我们假设在每个卷积层中都使用了零填充。

F18038

图 18-38:图 18-36 的自编码器,但在卷积层中使用了下采样和上采样,如卷积图标附带的楔形所示

图 18-39 显示了结果。

F18039

图 18-39:我们去噪模型的结果,图 18-38 所示

输出非常接*,尽管存在一些小的差异(例如,看看 0 的左下角)。第一个模型,图 18-36,具有显式的上采样和下采样层,在 2014 年底的 iMac 上每个训练周期大约需要 300 秒,并且没有 GPU 支持。较简单的模型,图 18-38,每个训练周期只需约 200 秒,因此节省了大约三分之一的训练时间。

需要更小心的问题陈述、测试和结果审查,以决定这些模型中的任何一个是否比另一个在此任务中产生更好的结果。

变分自编码器

到目前为止,我们看到的自编码器都试图找到最有效的方式来压缩输入,以便之后能够重新创建它。变分自编码器(VAE)与这些网络具有相同的基本架构,但在聚集潜在变量和填充潜在空间方面做得更好。

变分自编码器(VAE)与我们之前的自编码器有所不同,因为它们具有一些不可预测性。我们之前的自编码器是确定性的。也就是说,给定相同的输入,它们总是产生相同的潜在变量,而这些潜在变量总是会产生相同的输出。但 VAE 在编码阶段使用了概率思想(即随机数);如果我们多次将相同的输入通过系统,得到的输出每次都会略有不同。我们称 VAE 为非确定性的。

在我们讨论 VAE 时,为了具体化,我们继续以图像(和像素)为例进行讨论,但和我们其他的机器学习算法一样,VAE 可以应用于任何类型的数据:声音、天气、电影偏好,或任何我们可以用数字表示的东西。

潜在变量的分布

在我们之前的自编码器中,我们没有对潜在变量的结构施加任何条件。在图 18-20 中,我们看到一个全连接编码器似乎自然地将潜在变量分组,形成从共同起点 (0,0) 向右上方辐射的团块。这种结构并不是设计目标,而是由我们构建的网络的性质自然而然产生的。图 18-38 中的卷积网络在我们将瓶颈减少到两个潜在变量时,产生了类似的结果,如图 18-40 所示。

F18040

图 18-40:由图 18-38 中的卷积自编码器产生的潜在变量,瓶颈部分包含两个潜在变量。这些样本来源于密集混合和稀疏区域。

图 18-40 显示了从密集和稀疏区域中选择的潜在变量生成的解码图像。

在图 18-22 中,我们看到可以选择任意一对潜在变量,并通过解码器将这些值运行生成图像。图 18-40 显示了,如果我们选择密集区域或空白区域的点,我们通常得到的图像并不像数字。如果我们能找到一种方法,使得任何一对输入总是(或几乎总是)生成一个好看的数字,那将是很棒的。

理想情况下,最好是每个数字都有自己的区域,区域之间不重叠,也没有大的空白区域。对于填补空白区域,我们无能为力,因为那些地方是我们没有输入数据的地方。但我们可以尝试将混合区域拆开,使每个数字占据潜在空间的独立区域。

让我们看看变分自编码器是如何很好地实现这些目标的。

变分自编码器结构

正如许多好主意一样,VAE(变分自编码器)至少被两个不同的团队同时但独立地发明(Kingma 和 Welling 2014;Rezende, Mohamed 和 Wierstra 2014)。要详细理解这一技术,需要通过一些数学推导(Dürr 2016),因此我们将采用一种*似的概念性方法。因为我们的目的是捕捉方法的要点,而不是其精确的机制,所以我们将跳过一些细节,略过其他内容。

我们的目标是创建一个生成器,能够接受随机的潜在变量并生成新的输出,这些输出应该合理地类似于那些具有相似潜在值的输入。回想一下,潜在变量的分布是在训练过程中由编码器和解码器共同创建的。在这个过程中,除了确保潜在变量能够重建输入外,我们还希望潜在变量遵循三个特性。

首先,所有潜在变量应该集中在潜在空间的一个区域,以便我们知道随机值的范围应该是多少。其次,由相似输入(即显示相同数字的图像)生成的潜在变量应该聚集在一起。第三,我们希望最小化潜在空间中的空白区域。

为了满足这些标准,我们可以使用一个更复杂的误差项,当系统生成不符合规则的潜在样本时,给予惩罚。由于学习的整个目的是最小化误差,系统将学会如何创建结构符合我们要求的潜在值。架构和误差项是协同设计的。让我们看看这个误差项是什么样的。

聚类潜在变量

首先,我们来处理将所有潜在变量集中在一个地方的想法。我们可以通过强加一个规则或约束来实现这一点,并将其构建到误差项中。

我们的约束是,每个潜在变量的值在绘制时,接*形成单位高斯分布。回想一下第二章,高斯分布是著名的钟形曲线,如图 18-41 所示。

F18041

图 18-41:高斯曲线

当我们将两个高斯分布彼此垂直放置时,我们得到一个位于*面上的凸起,如图 18-42 所示。

F18042

图 18-42:在 3D 空间中,我们可以将两个高斯分布放置在垂直方向上。它们一起形成了一个位于*面上的凸起。

图 18-42 展示了二维分布的三维可视化。我们可以通过在 Z 轴上加入另一个高斯分布来创建一个实际的三维分布。如果我们把结果的凸起看作密度,那么这个三维高斯分布就像一朵蒲公英的花絮,中心密集,但向外扩展时变得更加稀疏。

F18043

图 18-43:一个高斯分布由其均值(即其中心位置)和标准差(即对称的距离,约 68%的面积包含在此范围内)来描述。这里的均值为 0,标准差为 1。

类比地,我们可以想象一个任意维度的高斯分布,只需要假设每个维度的密度沿其轴线遵循高斯曲线。这就是我们在这里做的。我们告诉 VAE 学习潜在变量的值,使得当我们查看大量训练样本的潜在变量并统计每个值出现的次数时,每个变量的计数形成类似高斯分布的分布,其均值(或中心)为 0,标准差(即其扩展范围)为 1,如图 18-43 所示。回想一下第二章,这意味着大约 68%的潜在变量值位于−1 到 1 之间。

当我们完成训练后,我们知道我们的潜在变量将按照这种模式分布。如果我们选择新的值并将其输入解码器,并且从这个分布中选择这些值(我们更有可能选择靠*中心和分布主要部分的值,而不是选择边缘的值),那么我们很可能生成与训练集中学到的值相*的潜在变量集,从而生成与训练集相似的输出。这也自然地将样本保持在同一区域,因为它们都试图匹配以 0 为中心的高斯分布。

如图 18-43 所示,将潜在变量落在单位高斯分布内是一个理想状态,但我们很少能做到这一点。变量与高斯分布的匹配度和系统在重建输入时的准确度之间存在权衡(Frans 2016)。系统在训练过程中自动学习这种权衡,努力保持潜在变量的高斯形态,同时也能很好地重建输入。

将数字聚集在一起

我们的下一个目标是让所有相同数字的图像的潜在值聚集在一起。为此,让我们使用一个巧妙的技巧,涉及一些随机性。这有点微妙。

假设我们已经实现了这个目标,让我们从一个特定的角度来看这个结果,这将告诉我们如何真正实现它。例如,我们假设每个图像的潜在变量集(例如数字 2 的图像)与所有其他数字 2 图像的潜在变量集相*。不过,我们可以做得更好。一些 2 在左下角有一个环。因此,除了将所有 2 聚集在一起外,我们还可以将所有有环的 2 聚在一起,将所有没有环的 2 聚在一起,而这两个聚集区域之间的区域则由那些大致有环的 2 的潜在变量填充,正如在图 18-44 所示。

现在让我们把这个想法推向极限。无论每个标记为 2 的图像的形状、风格、线条粗细、倾斜度等如何,我们都会为这些图像分配潜在变量,这些变量与其他标记为 2 的图像相*,而这些图像呈现相似的形状和风格。我们可以用一个循环将所有标记为 2 的图像聚集在一起,再将那些没有标记为 2 的图像分开,将所有用直线绘制的图像和所有用曲线绘制的图像、所有线条粗的图像和所有线条细的图像、所有高的 2 等聚在一起。这就是使用大量潜在变量的主要价值:它们让我们能够将这些特征的所有不同组合聚集在一起,而这在二维空间中是无法做到的。在一个区域,我们有细直线的无环 2,另一个区域则有粗曲线的无环 2,以此类推,涵盖了每一种组合。

F18044

图 18-44:一个 2 的网格被组织成使得相邻的 2 看起来相似。我们希望这些 2 的潜在变量大致遵循这样的结构。

如果我们必须自己识别所有这些特征,这种方案就不太实用了。但 VAE 不仅学习不同的特征,还会在学习过程中自动为我们创建所有不同的分组。像往常一样,我们只需输入图像,系统就会完成其余的工作。

这个“接*度”标准是在潜在空间中衡量的,其中每个潜在变量都有一个维度。在二维空间中,每组潜在变量在*面上形成一个点,它们之间的距离(或“接*度”)是连接它们的直线的长度。我们可以将这个概念推广到任意维度,因此即使每组潜在变量有 30 或甚至 300 个值,我们也可以始终找到它们之间的距离:我们只需测量连接它们的直线长度。

我们希望系统将相似的输入聚集在一起。但请记住,我们也希望每个潜在变量形成高斯分布。这两个标准可能会发生冲突。通过引入一些随机性,我们可以告诉系统“通常”将相似的输入潜在变量聚集在一起,并“通常”沿高斯曲线分布这些变量。让我们看看随机性如何让这一目标得以实现。

引入随机性

假设我们的系统输入的是数字 2 的图像,并且像往常一样,编码器为其找到潜在变量。在将这些潜在变量传递给解码器生成输出图像之前,让我们在每个潜在变量上添加一点随机性,并将这些修改后的值传递给解码器,如图 18-45 所示。

F18045

图 18-45:向编码器输出添加随机性的一个方法是,在将每个潜在变量传递给解码器之前,向其添加一个随机值。

因为我们假设相同风格的所有示例都被聚集在一起,所以我们从扰动后的潜在变量生成的输出图像将与输入图像相似(但有所不同),因此,衡量图像之间差异的误差也会较低。然后,我们只需通过向同一组潜在值添加不同的小随机数,就可以生成许多类似于输入 2 的新 2。

一旦聚类完成,这就是它的工作原理。为了首先完成聚类,我们只需在训练过程中,当这个扰动后的输出与输入差距较大时,给予网络一个大的误差分数。因为系统希望最小化误差,它会学习到与输入原始潜在值接*的潜在值应该生成接*输入图像的图像。结果,相似输入的潜在值会聚集在一起,正如我们所期望的那样。

但我们刚才走了一条在实践中不能继续的捷径。如果我们像在图 18-45 中那样直接加上随机数,我们就无法使用第十四章中看到的反向传播算法来训练模型。问题出在反向传播需要计算网络中流动的梯度,但像图 18-45 这样的操作的数学方法无法让我们以需要的方式计算梯度。如果没有反向传播,我们整个学习过程就会像消失在一阵烟雾中一样。

变分自编码器(VAE)采用了一种巧妙的想法来解决这个问题,它将添加随机值的过程替换为一个相似的概念,完成相似的工作,但让我们可以计算梯度。这是一种小小的数学替代方法,使得反向传播算法可以再次起作用。这被称为重参数化技巧。(正如我们之前看到的,数学家们有时会用技巧这个词来赞扬一个聪明的想法。)

了解这个技巧很有价值,因为在阅读变分自编码器(VAE)相关内容时,通常会遇到这个技巧(还有其他数学技巧涉及其中,但我们不会深入探讨)。这个技巧是这样的:我们不再像在图 18-45 中那样直接为每个潜在变量从空中选取一个随机数并加进去,而是从概率分布中抽取一个随机变量。这个值现在成为我们的潜在变量(Doersch 2016)。换句话说,我们不是从一个潜在值开始,再加上一个随机偏移来创建一个新的潜在值,而是利用潜在值来控制一个随机数生成过程,那个过程的结果就是新的潜在值。

回顾第二章,概率分布可以为我们提供随机数,其中一些数值比其他数值更可能出现。在这种情况下,我们使用高斯分布。这意味着,当我们请求一个随机值时,我们最有可能得到一个靠*“峰值”高的位置的数值,而我们越远离“峰值”的中心,得到这些数值的可能性就越小。

由于每个高斯分布需要一个中心(均值)和一个扩展(标准差),编码器为每个潜在变量生成这对数值。如果我们的系统有八个潜在变量,那么编码器将生成八对数值:每个潜在变量对应一个高斯分布的中心和扩展。一旦得到了这些数值,对于每一对值,我们从它们定义的分布中选择一个随机数,这个值就是我们随后提供给解码器的潜在变量值。换句话说,我们为每个潜在变量创建一个新的值,这个值与原来的非常接*,但内含一定的随机性。扰动过程的重构使我们能够将反向传播应用于网络。

图 18-46 展示了这个想法。

我们的自动编码器结构,如图 18-46 所示,要求网络在计算潜在值后进行分裂。分裂是我们深度学习架构中新采用的技术:它仅仅是对一个张量进行复制,并将两个副本发送到两个不同的后续层。在分裂之后,我们使用一层来计算高斯分布的中心,另一层计算其扩展。我们从这个高斯分布中进行采样,得到我们的新潜在值。

f18046

图 18-46:我们使用计算得到的潜在值来获得高斯波峰的中心和扩展。我们从这个波峰中选择一个数字,这个数字成为我们的新潜在值。

为了应用我们在图 18-46 中的采样思想,我们为每个潜在变量创建一个高斯分布并进行采样。然后,我们将所有的新潜在值输入到一个合并组合层中,该层只是简单地将所有输入依次放在一起形成一个单一的列表(实际上,我们通常将采样和合并步骤合并成一个层)。图 18-47 展示了如何处理一个具有三个值的潜在向量。

f18047

图 18-47:展示了具有三个潜在变量的 VAE 的分裂与合并采样步骤

在图 18-47 中,编码器以三个潜在变量结束,对于每一个,我们都会计算一个中心和扩展。这三个不同的高斯波峰随后会被随机采样,所选的值会被合并或组合,形成为该输入计算出的最终潜在变量。这些变量是编码器部分的输出。

在学习过程中,网络会学习每个高斯分布的中心和扩展应该是什么。

这个操作就是我们之前所说的,每次将一个样本输入经过训练的 VAE(也就是说,学习完成后),我们得到的结果都会略有不同。编码器在分裂之前是确定性的。但之后,系统从每个潜在变量的高斯分布中随机选择一个值,而这些值每次都会不同。

探索 VAE

图 18-48 展示了一个完全连接的 VAE 的架构。它就像我们从完全连接的层构建的深度自动编码器(参见图 18-17),但有两个变化(我们选择完全连接的层而不是卷积层,以简化模型)。

f18048

图 18-48:我们用于 MNIST 数据的 VAE 架构。共有 20 个潜在值。

第一个变化是现在我们在编码器的末端采用了分裂-选择-合并过程。第二个变化是我们使用了新的损失函数,或误差函数。

我们会给新的损失函数分配的另一个任务是测量编码和解码阶段完全连接层之间的相似性。毕竟,无论编码阶段做了什么,我们希望解码阶段能将其还原。

测量这个的最佳方式是使用我们在第六章看到的 Kullback-Leibler(或 KL)散度。回想一下,这度量了使用不同于最优编码的编码方式压缩信息时的误差。在这种情况下,我们认为最优编码器是解码器的反向操作,反之亦然。总的来说,随着网络试图减小误差,它也在减少编码器和解码器之间的差异,使它们越来越接*彼此镜像(Altosaar 2020)。

处理 MNIST 样本

让我们看看这个 VAE 对我们的一些 MNIST 样本会输出什么。图 18-49 展示了结果。

F18049

图 18-49: 我们的 VAE 对 图 18-48 的预测。顶行:输入的 MNIST 数据。底行:变分自编码器的输出。

这些匹配得相当不错并不奇怪。我们的网络使用了大量的计算资源来生成这些图像!但是正如我们从其架构中看到的,VAE 每次看到相同图像时都会产生不同的输出。让我们拿出这组测试集中的两个图像,并将其通过 VAE 运行八次。结果见图 18-50。

F18050

图 18-50: VAE 每次看到相同输入时都会产生不同的结果。顶行:输入图像。中行:VAE 处理输入八次后的输出。底行:输入和每个输出之间的逐像素差异。红色越深表示正差异越大,蓝色越深表示负差异越大。

这八个 VAE 结果彼此相似,但我们可以看到明显的差异。

让我们回到图 18-49 中的八个图像,但向从编码器输出的潜在变量中添加额外的噪声。也就是说,在解码器阶段之前,我们向潜在变量中添加了一些噪声。这为我们提供了一个很好的测试,来查看训练图像在潜在空间中是如何紧密聚集的。

让我们尝试向每个潜在变量的值中添加最多 10%的随机值。图 18-51 展示了在潜在变量中加入这种适量噪声后的结果。

F18051

图 18-51: 向从 VAE 编码器输出的潜在变量中添加 10% 的噪声。顶行:来自 MNIST 的输入图像。底行:向编码器输出的潜在变量添加噪声后的解码器输出。

添加噪声似乎并不会显著改变图像。这很好,因为这告诉我们这些噪声值仍然“接*”原始输入。让我们增加噪声,加入一个随机值,最大为潜在变量值的 30%。图 18-52 展示了结果。

F18052

图 18-52: 对潜在变量进行最多 30% 的扰动。顶行:MNIST 输入图像。底行:来自 VAE 解码器的结果。

即使有很多噪声,这些图像仍然看起来像数字。例如,7 的变化很大,但它变成了一个弯曲的 7,而不是一个随机的斑点。

让我们尝试混合我们数字的参数,看看效果如何。图 18-53 显示了我们之前看到的五对数字的等比例混合结果。

F18053

图 18-53:VAE 中的潜在变量混合。顶部和中间的行:MNIST 输入图像。底部行:每个图像的潜在变量的等比例混合,解码后的结果。

这里有趣的地方在于,这些图像大致看起来像数字(最左边的图像在作为数字的表现上最差,但它仍然是一个一致的形状)。这是因为潜在空间中没有那么多空白区域,所以中间值不太可能落入远离其他数据的区域(从而产生一个奇怪的、非数字的图像)。

让我们看一些线性混合。图 18-54 显示了我们之前见过的三对数字的中间步骤。

F18054

图 18-54:VAE 中潜在变量的线性插值。每行的最左边和最右边的图像是 VAE 对一个 MNIST 样本的输出。中间的图像是混合潜在变量的解码版本。

这个 5 看起来很不错,正从一个版本移动到另一个版本的 5 空间中。顶部和底部的行有很多不是数字的图像。它们可能正在通过潜在空间中的空白区域,但正如我们之前提到的,并不清楚这些在任何意义上是错误的。毕竟,一个介于四和三之间的图像应该是什么样子呢?

让我们为了好玩将老虎图像输入系统。记住,这是一个完全不公*的操作,我们不应该期望得到任何有意义的结果。图 18-55 显示了结果。

F18055

图 18-55:将我们低分辨率的老虎图像输入 VAE 模型

VAE 创建了一些具有一致结构的东西,但它与数字不像。

使用两个潜在变量

为了与其他自编码器进行对比,我们使用了仅有两个潜在变量的 VAE 进行训练(而不是我们一直在使用的 20 个),并在图 18-56 中绘制了它们。

F18056

图 18-56:我们训练的 VAE 模型中,使用两个潜在变量对 10,000 个 MNIST 图像的潜在变量位置进行展示。

这是一个很好的结果。潜在变量试图保持的高斯峰值的标准差在这里由一个黑色圆圈表示,并且似乎分布得相当好。各种数字通常被很好地聚集在一起。中间有些混乱,但请记住,这张图像仅使用了两个潜在变量。有趣的是,2 似乎形成了两个簇。为了了解发生了什么,让我们创建一个对应于我们两个潜在变量的图像网格,使用 图 18-22 中显示的公式,但使用 图 18-56 中的潜在变量。让我们将网格上每个点的 x 和 y 值输入到解码器中,假设它们是潜在变量。我们的范围是每个轴从 −3 到 3,就像 图 18-56 中的圆圈一样。结果是 图 18-57。

F18057

图 18-57:VAE 输出,将 x 和 y 坐标作为两个潜在变量处理。每个轴的范围从 −3 到 3。

没有环的 2 紧密地聚集在中下部,而带环的 2 则聚集在中左部。系统认为这些数字差异如此之大,以至于它们不需要靠*彼此。

浏览这张图像,我们可以看到数字是如何紧密聚集在一起的。这比我们在 图 18-23 中看到的结构更有组织,也更加均匀。在一些地方,图像有些模糊,但即使只有两个潜在变量,大部分图像看起来仍像数字。

生成新的输入

在 图 18-58 中,我们已经将 VAE 的解码器部分隔离出来,作为生成器使用。暂时,我们将继续使用一个版本,其中将 20 个潜在值减少到仅 2 个。

F18058

图 18-58:我们的 VAE 解码器阶段

由于我们只有两个潜在变量,我们从以 (0,0) 为中心,半径为 3 的圆内随机选择了 80 对随机 (x, y) 值,输入到解码器中,并将生成的图像汇集在一起,形成 图 18-59。

F18059

图 18-59:VAE 解码器在输入两个随机潜在变量时生成的图像

大部分图像看起来相当不错。有些图像不太清晰,但总体上,大多数图像都是可辨识的数字。许多模糊的形状似乎来自于 8 和 1 之间的边界,导致了狭窄且纤细的 8。

这些数字大多是模糊的,因为我们仅使用了两个潜在变量。让我们通过训练并使用一个具有更多潜在变量的更深层 VAE 来使结果更加清晰。图 18-60 展示了我们的新 VAE。这个架构基于 Caffe 机器学习库中的 MLP 自编码器(Jia 和 Shelhamer 2020;Donahue 2015)。(回忆一下,MLP 代表多层感知器,即一个完全由全连接层构成的网络。)

f18060

图 18-60:更深层 VAE 的架构

我们使用 50 个潜在变量训练了该系统 25 个周期,然后生成了另一个随机图像网格。与之前一样,我们仅使用了解码器阶段,见图 18-61。

F18061

图 18-61:我们仅使用深层 VAE 的解码器阶段,通过输入 50 个随机数来生成新图像。

结果见图 18-62。

F18062

图 18-62:当提供随机潜在变量时,我们更大的 VAE 生成的图像

这些图像的边缘比图 18-59 中的图像明显更清晰。在大多数情况下,我们已经从完全随机的潜在变量中生成了完全可识别且合理的数字,尽管像往常一样,还是会出现一些不太像数字的奇怪图像。这些图像可能来自数字之间的空白区域,或者不同数字相邻的区域,导致形状出现奇怪的混合。

一旦我们的 VAE 训练完成,如果我们想生成更多类似数字的数据,我们可以忽略编码器并保存解码器。现在它成为了一个生成器,我们可以用它来创建任意多的新数字图像。如果我们将 VAE 训练在拖拉机、鸣禽或河流的图像上,我们也可以生成更多这些类型的图像。

总结

在本章中,我们看到自编码器如何学会使用潜在变量表示一组输入。通常这些潜在变量的数量比输入值的数量要少,因此我们说自编码器通过强制数据通过瓶颈来压缩输入。由于在过程中有一些信息丢失,这是一种有损压缩。

通过将我们自己选择的潜在值直接输入训练好的自编码器的后半部分,我们可以将这组层视为生成器,能够生成新的输出数据,类似于输入,但完全是全新的。

自编码器可以使用多种层结构来构建。在本章中,我们看到了使用全连接层和卷积层构建的网络示例。我们观察了由训练过的全连接层自编码器生成的 2D 潜在变量结构,发现它具有出乎意料的组织性和结构性。从这些潜在变量的一个密集区域选择新的潜在变量对并传递给生成器,通常会生成模糊的输出(因为我们只有两个潜在变量值),但大致像输入数据。然后我们看了卷积自编码器,主要(或完全)由卷积层构成。

我们发现可以混合潜在变量,本质上是在端点之间创建一系列中间潜在变量。我们在瓶颈中使用的潜在变量越多,这些插值输出看起来就越好。接着,我们发现可以训练自编码器去噪输入,只需告诉它生成噪声输入的干净值。最后,我们研究了变分自编码器,它能够更好地聚集相似的输入,并填充潜在空间的区域,但代价是引入了某种随机化过程。

自编码器常用于去噪和简化数据集,但人们已经找到了创造性的方法,将其用于许多不同的任务,例如创作音乐和修改输入数据,以帮助网络更好、更快地学习(Raffel 2019)。

第十九章:递归神经网络

在本书的大多数内容中,我们将每个样本视为一个孤立的实体,与任何其他样本无关。这对于像照片这样的东西是有道理的。如果我们在对一张图像进行分类并决定它是一只猫,那之前或之后的图像是狗、松鼠还是飞机都无关紧要。图像彼此独立。但如果图像是电影中的一帧,那么将其放在其他图像的上下文中进行观察就变得有帮助。例如,我们可以追踪那些可能暂时被遮挡的物体。

当我们处理多个顺序重要的样本时,我们称之为序列。任何人类语言中单词的流动都是一种重要的序列类型,并且将是本章的重点。

能够理解和处理序列的算法还有一个额外的好处:它们通常能够生成,或创造,新的序列。经过训练的系统可以生成故事(Deutsch 2016a)或电视剧本(Deutsch 2016b),爱尔兰民间舞曲(Sturm 2015b),复调旋律(LISA Lab 2018),以及复杂的歌曲(Johnson 2015;O’Brien 和 Román 2017)。我们可以为流行音乐(Krishan 2016)、民谣音乐(Sturm 2015a)、说唱(Barrat 2018)或乡村音乐(Moocarme 2020)创作歌词。我们可以将语音转换为文本(Geitgey 2016;Graves、Mohamed 和 Hinton 2013),并为图像和视频写字幕(Karpathy 和 Li 2013;Mao 等,2015)。

在本章中,我们将介绍一种基于记住每个元素特征的序列处理方法。我们所构建的模型被称为递归神经网络(RNN)。

当我们处理序列时,每个输入元素称为token。一个 token 代表一个单词、一个单词的片段、一项测量,或任何其他可以用数字表示的东西。在本章中,我们使用语言作为最常见的数据来源,并专注于整个单词,因此我们将wordtoken互换使用。

语言的应用

研究自然语言的整体领域称为自然语言理解,或NLU。今天的大多数算法并不关心它们所处理的语言的实际理解。相反,它们从数据中提取统计信息,并以这些统计信息为基础进行诸如回答问题或生成文本等任务。这些技术通常被称为自然语言处理,或NLP

在第十六章和第十七章中,我们看到卷积神经网络(CNN)可以识别照片中的物体,而无需真正理解照片的内容。它们只处理像素的统计数据。同样,NLP 系统并不理解它们所处理的语言。相反,它们为单词分配数字,并找到这些数字之间有用的统计关系。

从根本上说,这些系统并不知道语言的存在,或它们操作的对象具有语义含义。像往常一样,系统使用统计学来生成我们在特定情境下认为可接受的输出,甚至没有一点理解它在做什么或这些输出对人类意味着什么。

常见的自然语言处理任务

自然语言算法的应用通常被称为任务。以下是一些流行的任务:

  1. 情感分析: 给定带有观点的文本,如电影评论,判断整体情感是积极的还是消极的。

  2. 翻译: 将文本转换成另一种语言。

  3. 回答问题: 回答关于文本的问题,比如谁是英雄,或者发生了什么行动。

  4. 总结或改写: 提供文本的简短概述,强调主要观点。

  5. 生成新文本: 给定一些起始文本,编写更多似乎与其相关的文本。

  6. 逻辑流: 如果一个句子首先提出一个前提,接下来的句子根据该前提提出一个结论,判断结论是否从前提中合乎逻辑地得出。

在本章及下一章中,我们主要关注两个任务:翻译和文本生成。其他任务与这两者有许多相似之处(Rajpurkar, Jia, and Liang 2018; Roberts, Raffel, and Shazeer, 2020)。特别是,逻辑流的处理非常困难,需要借助人机合作(Full Fact 2020)。

翻译至少需要我们提供要翻译的文本,以及源语言和目标语言。我们可能还希望了解一些上下文信息,以帮助我们理解习语和其他随时间或地点变化的语言特征。

文本生成通常从一个种子提示开始。算法将其作为文本的起点,并从那里构建。通常,它一次生成一个词。给定一个提示,它会预测下一个词。该词被添加到提示的末尾,系统使用新的、更长的提示来预测下一个词。我们可以无限重复这个过程来生成一个句子、文章或书籍。我们称这种技术为自回归,因为我们通过自动将之前的输出连接在一起并将其作为输入来预测或回归序列中的下一个词。自回归系统被称为自回归器。更一般地说,按算法创建文本被称为自然语言生成,或NLG

翻译和文本生成都使用了一个叫做语言模型的概念。这是一种计算方法,它将一系列单词作为输入,并告诉我们该序列形成一个完整句子的可能性有多大。请注意,它并不会告诉我们该句子是否写得特别好,甚至是否有意义或是否真实。通常,我们会将训练好的神经网络本身称为语言模型(Jurafsky 2020)。

将文本转化为数字

为了构建可以帮助我们进行翻译和文本生成的系统,我们必须首先将文本转化为计算机可用的形式。像往常一样,我们将一切转化为数字。这里有两种常见的方法来做到这一点。

第一种方法是基于字符的,我们为文本中可能出现的所有符号编号。人类语言中最广泛的书写字符表叫做 Unicode。最新版本 Unicode 13.0.0 包含 154 种人类书写语言,识别了 143,859 个不同的字符(Unicode Consortium 2020)。我们可以为这些书写系统中的每个符号分配一个从 0 到大约 144,000 的唯一编号。在本章中,我们保持简洁,展示一些使用 89 个最常见的英语字符的文本生成示例。

第二种方法是基于词语的,我们为文本中可能出现的所有单词编号。统计世界上所有语言中的所有单词将是一项艰巨的任务。在本书中,我们坚持使用英语,但即便如此,我们也没有明确统计过单词的数量。大多数现代英语词典大约有 300,000 个词条(Dictionary.com 2020)。试想,逐字翻阅词典并为每个条目分配一个从 0 开始的唯一编号。这些单词及其对应的数字便构成了我们的词汇表。本章中的大多数示例都采用基于词语的方法。

现在我们可以为任何句子创建一个计算机友好的数字表示。我们可以通过将这组数字传递给一个经过训练的自回归网络来生成更多的文本。网络预测下一个词的数字,然后将这个词附加到作为输入的词汇上,网络接着预测下一个词,这个词再次附加到输入的词汇中,依此类推。为了让我们看到与之对应的文本,我们可以将每个数字转回其对应的单词。在接下来的讨论中,我们将这些数字转换过程视为理所当然,并将输入和输出表示为单词,而不是数字。稍后我们将看到,虽然单个数字是可行的,但有一种更丰富的方式来表示单词,包含它们的上下文以及它们在句子中的使用方式。

微调与下游网络

训练一个系统时,通常会先在一个通用数据库上进行训练,然后再进行专业化。例如,我们可以将一个通用的图像分类器增强为一个能够识别叶子形状并告诉我们它们来自哪种树的分类器。这个过程被称为迁移学习。在分类器中使用时,通常涉及冻结现有的网络,在分类部分的末尾添加一些新的层,并训练这些新层。这样,新层就可以利用现有网络从每个图像中提取到的所有信息。

在自然语言处理(NLP)中,我们说一个从通用数据库中学习的系统是预训练的。然后,当我们想学习一种新的专业语言时,比如法律、诗歌或工程领域的语言,我们会用新的数据对网络进行微调。与迁移学习不同,当我们进行微调时,通常会修改系统中的所有权重。

如果我们不想重新训练系统,我们可以创建第二个模型,接受语言系统的输出并将其转换为对我们更有用的内容,这与迁移学习的精神相*。这里语言模型被冻结,其输出被传递给新模型。我们将这个第二个模型称为下游网络,它执行下游任务。一些语言模型旨在创建其输入文本的丰富、密集的摘要,从而可以用于推动各种下游任务。

微调和下游训练这两种方法是有用的概念区分,但在实践中,许多系统将两种技术结合使用。

完全连接预测

正如我们所讨论的,我们将语言视为一系列数字。为了更好地理解如何处理这种序列,让我们暂时放下语言,专注于数字。我们将构建一个微型网络,该网络学习从序列中获取一些数字,并生成下一个数字。我们将以最简单的方式来做:一个完全连接的层,只有五个神经元,接着是一个完全连接的层,只有一个神经元,如图 19-1 所示。我们将在第一层使用一个斜率为 0.1 的泄漏 ReLU 激活函数,在输出层不使用激活函数。

F19001

图 19-1:用于序列预测的微型网络

测试我们的网络

为了尝试这个微型网络,让我们使用一个合成数据集,该数据集是通过将一堆正弦波相加生成的。前 500 个样本如图 19-2 所示。

F19002

图 19-2:合成训练数据

为了训练我们的系统,我们将从数据集中取出前五个值,并让我们的微型网络生成第六个值。然后,我们将取出数据集中的第 2 到第 6 个值,并让它预测第七个值。我们说我们使用滑动窗口来选择每一组输入,如图 19-3 所示。

F19003

图 19-3:使用滑动窗口从训练数据的 5 元素序列中创建训练样本,蓝色表示训练样本。我们希望为每个样本预测的值用红色表示。

从我们开始的 500 个值中,我们可以以这种方式生成 495 个样本。我们用这些样本训练了我们的微型网络 50 个时期。当我们再次运行训练数据并请求预测时,我们得到图 19-4 左侧的结果,图中显示了原始训练数据(蓝色)和预测值(橙色)。还不错!

F19004

图 19-4:左侧:训练数据和预测结果。右侧:测试数据和预测结果。

现在,让我们在后续曲线的 250 个测试数据点上运行这个模型。数据和预测结果显示在图 19-4 的右侧。虽然预测结果并不完美,但考虑到我们的网络非常小,预测已经相当不错。

然而,这些数据比较简单,因为它们变化*缓。让我们尝试一个更现实的数据集,数据来源于 1749 到 2018 年间每月记录的太阳黑子*均数量(Kaggle 2020)。图 19-5 显示了使用与图 19-4 相同排列方式的输入和输出。数据中的波峰波谷对应于大约 11 年的太阳周期。虽然它并没有完全达到数据的极端值,但我们的小型回归模型似乎能很好地跟踪数据的整体波动。

F19005

图 19-5:左侧:训练太阳黑子数据和预测结果。右侧:测试数据和预测结果。

不幸的是,这个小型网络无法生成令人愉快的小说。为了理解原因,让我们将数据改为编号的单词。我们将使用查尔斯·狄更斯小说《双城记》(Dickens 1859)的前六章作为文本。为了便于处理,我们去除了所有标点符号并将所有内容转换为小写字母。

由于我们将按单词级别工作,我们需要为每个将要使用的单词分配一个编号。为整个词典编号显得过于繁琐,并且会错过文本中的所有人名和地名。相反,让我们从书本本身建立词汇表。我们将为书中的第一个单词分配编号 0,然后逐个单词向前推进。每次遇到一个之前未见过的单词时,我们就为它分配下一个可用的编号。这部分小说包含 17,267 个单词,但只有 3,458 个独特的单词,所以我们的单词编号从 0 到 3,457。

现在,由于小说中的每个单词都有了编号,我们将数据库分为训练集和测试集。在训练数据的末尾,我们只看到了大约 3,000 个独特的单词。为了避免网络预测它没有训练过的单词编号,我们移除了测试集中任何包含超过此值的单词编号(或目标)的所有序列。也就是说,测试数据只包含使用训练数据中已出现的单词的序列。

我们重复了前面的实验,向图 19-1 中的小网络输入了五个连续的单词编号窗口,从输出中收集了它对下一个单词的预测。我们让它训练了 50 个 epoch,但误差很快停止改进,并且早期停止在 8 个 epoch 后结束了训练,给出了图 19-6 中的结果。正如我们在左侧的训练数据中看到的,随着我们深入到书中,单词编号逐渐增加。橙色线条是系统根据每组五个输入预测的单词编号。

F19006

图 19-6:左:来自《双城记》前六章大约 12,000 个单词的训练和预测。右:大约 2,000 个更多单词的测试数据和预测。

结果一点也不好。预测结果显然与训练数据或测试数据都不匹配。从图 19-7 中的特写可以更容易地看到测试数据和预测的结构。

F19007

图 19-7:来自图 19-6 的 500 个测试数据和预测结果的特写

预测结果似乎模糊地跟随目标,但它们完全偏离了。

为什么我们的网络失败了

让我们把图 19-7 中的数字转回成单词。以下是一个典型的摘录:

pricked hollows mud crosses argument ripples loud want joints upon harness followed side three intensely atop fired wrote pretence

这不算是伟大的文学作品,即使我们加上了标点符号。这里有不少问题。首先,这个小网络显然没有足够的能力来完成这项任务。我们可能需要更多的神经元,也许需要更多的层,才能接*可读的文本。

即使是更大的全连接网络也会在这项任务上遇到困难,因为它们无法捕捉文本的结构,也就是所谓的语义。语言的结构与我们之前看到的曲线和太阳黑子数据有本质的不同。考虑这个五个单词的字符串:Just yesterday, I saw a。这个片段可以通过任何名词来完成。根据一项估计,英语中的名词数量至少有成千上万(McCrae 2018)。任何网络怎么可能猜出我们想要的那个名词呢?一个答案是将窗口增大,这样网络就有了更多的前置单词,可能能够做出更有根据的选择。例如,给定输入:I’ve been spending my time watching tigers very closely. Just yesterday, I saw a,大多数英语名词现在可以合理地排除为不太可能。

我们来试试这个。我们将图 19-1 中的小网络扩大,第一层有 20 个神经元。我们一次给它 20 个元素,并要求它预测第 21 个。曲线数据的结果显示在图 19-8 中。

尽管训练数据仍然相当不错,但测试结果却差得多。为了处理来自这个更大窗口的所有信息,我们需要一个更大的网络。增大窗口意味着我们需要一个更大的网络,这也意味着它需要更多的训练数据、更大的内存、更强的计算能力、更高的电力消耗和更多的训练时间。

F19008

图 19-8:一个放大的网络,使用 20 个元素的窗口预测正弦波数据

但还有一个更大的问题,仅仅通过使用更大的网络是无法改善的。问题在于,即使是预测中的一个微小错误,也会导致无法理解的文本。为了看清这一点,我们可以随便查看赋值为 1,003 和 1,004 的单词。这些数字分别对应单词 keep 和 flint。这两个单词似乎完全无关,但在搜索文本时,我们会找到书的开头附*有这样一段话:he had only to shut himself up inside, keep the flint and steel sparks well off the straw。单词 the 已经出现在书中的第三个位置,所以由于 keep 和 flint 之前都没有出现,当我们对书中的单词进行编号时,keep 和 flint 被赋予了相邻的编号。

假设我们的网络在响应某些输入时,预测下一个单词为 1,003.49。我们需要将其转换为整数,以便查找相应的单词。最接*的整数是 1,003,给我们的是 keep。但如果系统预测略大的值 1,003.51,最接*的整数是 1,004,给我们的是 flint。这两个词完全不相关。这表明,即使是预测中的微小数值差异,也会产生荒谬的输出。

回顾我们在网络图中的预测,我们可以看到很多错误,这些错误在曲线和太阳黑子数据中似乎不那么严重,但对于语言数据来说会造成严重的混乱。增加计算能力可以减少这些错误,但我们对精确度的需求不会消失。

我们在图 19-1 中的小型网络隐藏了另一个缺陷:它无法跟踪输入中单词的位置。假设我们给出句子“Bob 告诉 John 他饿了”,并且我们想知道代词 he 指的是谁。答案是 Bob。但单词顺序很重要,因为如果我们改为给出句子“John 告诉 Bob 他饿了”,那么 he 就会指代 John。对准确性的需求会促使我们通过更多的全连接层来扩展网络,但当单词进入第一层时,我们就会失去它们的隐含顺序。后续的层将无法判断哪个单词对应 he。

为了解决这些问题以及许多其他问题,我们需要比全连接层和由单一数字表示的单词更复杂的东西。我们可能会尝试使用 CNN,且已经有一些关于使用 CNN 处理序列数据的研究(Chen and Wu 2017;van den Oord 等,2016),但这些工具仍在发展中。相反,让我们看看一些专门设计来处理序列的东西。

循环神经网络

处理语言的更好方法是构建一个明确设计来管理单词顺序的网络。这样的一种网络,本章的重点,是循环神经网络(RNN)。这种网络建立在我们之前没有探讨过的一些概念之上,因此让我们现在考虑它们,然后用它们来构建一个 RNN。

引入状态

RNN 利用了一个叫做状态的概念。这只是描述系统(如神经网络)在任何给定时刻的状态。例如,想象一下预热烤箱。在这个过程中,烤箱会经历三种独特的状态:关闭;预热中;以及达到所需温度。状态也可以包含其他信息。例如,随着烤箱加热,我们可以将三条信息打包到烤箱的状态中:它当前的状态(例如,正在预热);它目前的温度;以及它的目标温度。因此,状态可以表示系统的当前状态,以及任何其他便于记忆的信息。

由于状态非常重要,让我们通过另一个例子来看看它的一些微妙之处。

假设你在一家冰淇淋店工作,正在学习如何制作一个简单的巧克力酱圣代。在这个故事中,你扮演的是系统的角色,而你脑海中逐步建立的配方就是你的状态。

在得到任何指示之前,你的起始状态初始状态将是“一个空杯子”。所以,假设你有一个空杯子。你的起始状态如图 19-9 所示,在最左侧。

F19009

图 19-9:你在学习做甜点时,逐步变化的状态或配方

你的经理说第一步是放一些香草冰淇淋。所以,你更新了内部配方或状态为“一个空杯子,里面有三勺香草冰淇淋。”你将三勺冰淇淋放入杯中。

你的经理说这样太多了,你应该去掉一勺冰淇淋。你照做了,并将状态更新为“一个空杯子,里面有两勺香草冰淇淋。”

现在你的经理说要倒入足够的巧克力糖浆,覆盖住冰淇淋。你这么做,并将状态更新为“一个空杯子,里面有两勺香草冰淇淋,覆盖着巧克力糖浆。”但这让你想起了你的朋友 Marty,因为这是他最喜欢的甜点。所以,你通过抛弃之前的状态来简化你的状态,现在只记得“马蒂最喜欢的”。

最后,你的经理说你应该在上面放一颗樱桃。所以,你更新了你的状态为“加樱桃的 Marty 最爱”。恭喜,你的圣代完成了!

从这个故事和状态的概念中,我们有几个关键要点需要记住。

首先,你的状态不仅仅是当前情况的快照或你所获得信息的列表。它捕捉了这两者的概念,可能是压缩或修改过的形式。例如,你记得放入两勺冰激凌,而不是记得放三勺冰激凌然后再去掉一勺。

其次,在每一步接收到新信息后,你更新了状态并产生了输出。输出取决于你接收到的输入和你的内部状态,但外部观察者无法看到你的状态,因此他们可能无法理解你的输出是如何从刚收到的输入中产生的。实际上,外部观察者通常无法看到系统的内部状态。我们通过有时将系统的状态称为隐藏状态来强调这一点。

最后,输入的顺序很重要。这是这个例子的关键所在,它使得这个过程是一个顺序,而不仅仅是一些输入,因此它区别于我们在本章开始时提到的简单的全连接层。如果你先把巧克力放进杯子里,你会做出完全不同的甜点,而且你可能不会在状态中创造出对你朋友 Marty 的引用。

我们将每个输入称为时间步。当输入表示时间中的事件时,这个概念是有意义的,就像这里一样。其他序列可能没有时间成分,比如描述河流从源头到终点沿途各个点的深度的序列。特别是,句子中的单词在被大声朗读时具有时间成分,但这个概念在它们被打印出来时并不适用。尽管如此,术语时间步仍被广泛用来指代序列中的每个连续元素。

我们的图表汇总

如果我们需要处理一长串的输入,一个像图 19-9 这样的图可能会占据页面很大的空间。因此,我们通常会将它绘制成更紧凑的形式,如图 19-10。我们在这里用连字符连接这些词,以表示每个小短语应该作为一个信息块来理解。

F19010

图 19-10:图 19-9 的汇总版本

右侧的循环表示了一个输入与下一个输入之间的状态。每次输入后,系统(由大而浅蓝色的框表示)会创建一个新的状态,该状态进入黑色方框。这个方框称为延迟,我们可以将其视为一小块记忆。当下一个输入到达时,系统会从延迟中取出状态并计算输出和新状态。新的状态再次从系统中产生,并停留在延迟中,直到下一个输入到达。延迟的目的是为了清楚地表明,在每个时间步中产生的状态不会立即以某种方式被再次使用,而是被保持直到需要处理下一个输入。

我们说图 19-9 中的图示是过程的展开版本。图 19-10 中的更紧凑的版本称为卷起收缩版本。

在深度学习中,我们通过将一切打包成一个循环单元来实现管理状态和输出呈现的过程,如图 19-11 所示。循环一词指的是我们反复使用状态记忆,尽管它的内容通常会随着每次输入的不同而变化(注意,这与递归一词不同,尽管这两个词听起来相似,但意义完全不同)。单元的工作通常由多个神经网络管理。像往常一样,当我们训练包含图 19-11 作为一层的完整网络时,这些网络会学习如何执行它们的任务。

我们将看到,尽管一个单元的内部状态通常是私有的,但一些网络可以很好地利用这些信息,因此这里我们将导出的状态表示为虚线,表示它是可用的,但如果不需要可以忽略。

我们经常将一个循环单元放在单独的层上,并称之为循环层。以循环层为主的网络被称为循环神经网络,或RNN。这个术语也常常应用于循环层本身,有时甚至是循环单元,因为它们内部包含神经网络。正确的解释通常可以从上下文中明确得知。

F19011

图 19-11:一个循环神经单元。如果需要,隐藏状态可以被导出到单元外部。

循环单元的内部状态以张量的形式保存。由于这个张量通常只是一个一维的数字列表,我们有时会谈到循环单元的宽度大小,指的是状态中记忆元素的数量。如果网络中所有单元的宽度相同,我们有时会将其称为网络的宽度。

图 19-12 的左侧显示了我们用来表示循环单元的图标,这通常出现在展开的图示中。右侧则显示了当我们将单元放入一个层时的图标,为了方便,我们将其收起。在层版本中,我们不绘制单元的内部状态。

F19012

图 19-12:左:我们为递归单元设计的图标。右:我们为递归层设计的图标。

我们可以使用图 19-11 中的基本递归单元来构建一个语言模型。假设标有“神经网络”的框中包含一个小型神经网络,由我们喜欢的任何层构建。我们可以将单元喂入一系列的单词(以数字形式)。每个单词之后,单元会生成一个预测,预测下一个单词,并更新其内部状态以记住到目前为止出现的单词。为了复现本章开始时的实验,我们可以连续输入五个单词,忽略前四个单元的输出。第五个输入后的输出将是它对第六个单词的预测。如果我们正在训练,并且预测不正确,那么像往常一样,我们使用反向传播和优化来改进神经网络中权重的值,并继续训练。目标是最终这些网络能够非常擅长于解读输入并控制状态,从而能够做出良好的预测。

递归单元的应用

让我们看看一个递归单元如何预测五个单词序列的下一个单词。我们可以通过展开的图示来看到输入和可能的输出,如图 19-13 所示。

F19013

图 19-13:递归单元预测单词。图示为展开形式。预测从单元的顶部输出,而状态由水*箭头表示。

我们从一个隐藏状态已被初始化为零的单元开始,表示尚未学到任何东西。那就是最左边的空圆圈。第一个单词“it”到达,单元考虑输入和隐藏状态,并预测下一个单词“swam”。单元告诉我们,以“it”开头的句子最可能接着是“swam”这个单词,但我们忽略这个预测,因为我们只关心第五个单词后的预测。

现在进入有趣的部分。利用在训练过程中学到的信息,RNN 更新其隐藏状态,以包含它接收到单词“it”作为输入,并生成“swam”作为输出的某种表示。

现在出现第二个单词:was。单元再次根据其隐藏状态和输入进行计算,并生成新的输出预测。这里是“night”,完成了短语“it was night”。单元更新其隐藏状态,记住接收了“it”,然后是“was”,然后是预测“night”。我们再次忽略了对“night”的预测。

这个过程持续进行,直到我们提供第五个单词“of”。如果我们在训练初期,系统可能会生成像“jellyfish”这样的内容,完成句子“它是最好的 jellyfish”。但在对原始文本进行了足够的训练后,递归单元内部的网络将学会如何在隐藏状态中表示“它是最好的”这个短语的连续单词,从而使得单词“times”具有很高的概率。

训练递归神经网络

假设我们正在开始训练图 19-13 中的递归单元。我们给它输入五个单词,然后通过将单元的最终预测与文本中的下一个单词进行比较来计算误差。如果预测与文本不匹配,我们会像往常一样执行反向传播和优化。查看图示,我们首先在图中最右侧的单元中找到梯度,然后将梯度传播到左边的前一个单元,再将梯度传播到它前面的单元,依此类推。按顺序应用反向传播非常重要,因为这些是处理的顺序步骤。

但是我们不能对图 19-13 中的每个框进行优化,因为这些都是相同的单元!对于系统来说,这看起来就像图 19-11 的一个实例,坐落在自己的层上,而不是一些展开的、重复使用相同单元的列表。我们必须以某种方式对同一层应用反向传播,这可能会造成混乱的记录工作。为了处理这个问题,我们使用了一种特殊的反向传播变体,叫做反向传播通过时间(BPTT)。它处理这些细节,使我们能够在训练时字面上解释图 19-13。

BPTT(反向传播通过时间)允许我们高效地训练递归单元,但它并不能完全解决训练问题。假设在使用 BPTT 时,我们为图 19-13 中最右侧单元的某个特定权重计算了一个梯度。然后,当我们向左传播梯度时,发现前一个单元中该权重的梯度更小。这意味着,当我们一次又一次地通过同一个单元将梯度向左传播时,相同的过程会不断重复,梯度会变得越来越小。如果梯度每次减少 60%,那么在经过八个单元后,梯度将降到原来值的千分之一以下。这个过程的开始只需要梯度在向后传播时变小,这是很常见的情况。然后它不可避免地在每次向后传播时按相同比例变小。

这是一个非常糟糕的消息。回想一下,当梯度变得非常小的时候,学习进程会变慢;如果梯度变为零,学习就完全停止。这不仅对递归单元不利,因为它停止了学习,而且对其之前层的神经元也有不利影响,因为它们失去了改进的机会。整个学习过程可能在我们达到网络的最小误差之前就彻底停滞。

这一现象被称为梯度消失问题(Hochreiter et al. 2001;Pascanu, Mikolov 和 Bengio 2013)。如果梯度在每次反向传播时增大,类似的问题也会出现。在经过相同的八步之后,每步增长 60%的梯度到达第一个单元时已经大约大了 43 倍。这就是梯度爆炸问题(R2RT 2016)。这些是严重的问题,可能会阻止网络的学习。

长短期记忆和门控递归网络

我们可以通过一个更复杂的递归单元——长短期记忆(LSTM)来避免梯度消失和梯度爆炸的问题。这个名字可能让人困惑,但它指的是内部状态会频繁变化,因此可以视为短期记忆。但有时我们也可以选择将某些信息在状态中保持较长时间。将其看作是选择性持久的短期记忆可能更有意义。LSTM 的框图见图 19-14。

F19014

图 19-14:长短期记忆(LSTM)的框图

LSTM 使用三个内部神经网络。第一个用来移除(或忘记)不再需要的状态信息。第二个用来插入单元想要记住的新信息。第三个网络将内部状态的一个版本作为单元的输出。

约定俗成地,“忘记”一个数字意味着将其移向零,而记住一个新数字则意味着将其添加到状态记忆的适当位置。

LSTM 不需要像图 19-11 中的基本递归单元那样重复自身,因此它避免了梯度消失和梯度爆炸的问题。我们可以将这个 LSTM 单元放在一个层上,并使用常规的反向传播和优化方法训练其中的神经网络。实际的实现有许多细节在这里没有展开,但它们遵循这个一般流程(Hochreiter et al. 2001; Olah 2015)。

LSTM 已被证明是一种非常好的递归单元实现方式,因此当人们提到“RNN”时,通常指的是特别使用 LSTM 的网络。LSTM 的一个常见变体是门控递归单元(GRU)。在网络中尝试 LSTM 和 GRU,看看哪个在特定任务上表现更好,这并不罕见。

使用递归神经网络

构建一个包含递归单元的网络(无论是 LSTM、GRU 还是其他类型)很容易。我们只需在网络中添加一个递归层并像往常一样进行训练。

使用太阳黑子数据

让我们用太阳黑子数据来演示这个。我们将训练一个网络,该网络具有一个包含单个 LSTM 的递归层,LSTM 的隐藏状态中只有三个值,如图 19-15 所示(本书中的约定是,除非另有说明,否则递归单元是 LSTM)。让我们将其与我们旧的全连接网络的输出进行比较,该网络有五个神经元,显示在图 19-1 中。我们必须小心比较苹果和橙子,因为这些方法如此不同,但两个网络都尽可能小,并且仍能做一些有用的事情。

F19015

图 19-15:由单个 LSTM 组成的微型 RNN,其隐藏状态中有三个值

和之前一样,我们将使用从训练数据中提取的五个连续值进行训练。与一次性接收所有五个值的全连接层不同,RNN 在五个连续步骤中每次接收一个值。结果如图 19-16 所示。记住我们关于苹果和橙子的警告,这个小型 RNN 的结果看起来与我们全连接网络的结果非常相似,后者的结果显示在图 19-5 中(在训练过程中测量的损失值和总体误差也大致相同)。

F19016

图 19-16:使用图 19-15 中的微型 RNN 预测太阳黑子数据

生成文本

上一次的结果令人鼓舞,所以让我们尝试下一个挑战,使用 RNN 生成文本。与之前预测下一个单词不同,这次我们将给系统一个字母序列,并要求它预测下一个字母。如前所述,这是一个更简单的任务,因为字母比单词少得多。我们将使用标准英文键盘上的 89 个符号作为我们的字符集。幸运的话,使用字符可能让我们避免使用比基于单词的方法更大的网络。

让我们在从《福尔摩斯短篇故事集》收集的字符序列上训练我们的 RNN,并要求它预测下一个字符(Doyle 1892)。

训练 RNN 需要权衡。我们可以使用更多的单元,或在每个单元中使用更多的状态,但这些都需要时间或内存。较大的网络可以让我们处理更长的窗口,这可能会导致更好的预测。另一方面,使用更少、更小的单元和较小的窗口使系统更快,这样我们就可以在给定时间内运行更多的训练样本。像往常一样,任何系统和数据的最佳选择都需要一些实验。

经过一些试验和错误后,我们最终选择了图 19-17 中的网络。这个网络肯定可以进一步改进,但它足够小,并且在本讨论中已经能很好地工作。我们的输入窗口长 40 个字符。每个 LSTM 单元包含 128 个状态记忆元素。最终的全连接层有 89 个输出,每个输出对应一个可能的符号。最后全连接层后的一个小框是我们在本章(以及第二十章)中对 softmax 激活函数的简写。因此,这个网络的输出是一个包含 89 个概率值的列表,每个值对应一个可能的字符。每次我们都会选择最有可能的字符。

F19017

图 19-17:一个用于逐字符处理文本的小型 RNN

为了创建训练集,我们将原始源材料切分成约 50 万个重叠的 40 字符字符串,每隔三个字符开始一次。

训练完成后,我们可以通过自回归生成新的文本,每次通过将最后一个输出添加到前一个输入的末尾,并丢弃前一个输入的第一个字符来生成新的 40 字符输入(Chen 等人,2017)。我们可以根据需要重复这个过程。 图 19-18 展示了一个四字符窗口的自回归过程。

F19018

图 19-18:使用自回归逐字符生成文本

为了观察网络的进展,在每个 epoch 的训练后,我们使用当前网络生成了一些文本。我们从源材料中的一个随机位置开始,选取 40 个连续字符作为种子。自回归的一个优点是我们可以一直运行它,生成无限量的输出。以下是经过第一次 epoch 训练后的运行开始(种子以红色显示):

er price.” “If he waits a little longer wew fet ius ofuthe henss lollinod fo snof thasle, anwt wh alm mo gparg lests and and metd tingen, at uf tor alkibto-Panurs the titningly ad saind soot on ourne” Fy til, Min, bals’ thid the

从某种意义上说,这已经非常不错了。这些“单词”大致相当于英语单词的长度,尽管它们不是实际的单词,但它们可能是。也就是说,它们不是随机字符的组合。它们中的许多甚至可以轻松发音。而且这只是经过一次 epoch 的结果。经过 50 次 epoch,效果有了很大改善。以下是根据一个新的随机种子生成的一些输出。

nt blood to the face, and no man could hardly question off his pockets of trainer, that name to say, yisligman, and to say I am two out of them, with a second. “I conturred these cause they not you means to know hurried at your little platter.’ “‘Why shoubing, you shout it of them,” Treating, I found this step-was another write so put.” “Excellent!” Holmes to be so lad, reached.

哇,情况好多了。这些单词大多数是真实的。标点符号也很好。甚至有些不在字典中的单词,比如 conturred 和 shoubing,看起来也像是可能存在的单词。

请记住,系统完全不知道单词是什么。它只知道字母跟随其他字母序列的概率。对于这样一个简单的网络来说,这非常了不起。通过让它运行,我们可以生成任意数量的文本。它不会变得更加连贯,但也不会变得更加不连贯。

一个更大的模型,包含更大的 LSTM、更高的数量,或者两者兼有,将以更长的训练时间为代价,给我们带来更可信的结果(Karpathy 2015)。

不同的架构

我们可以将递归单元(recurrent cells)整合到其他类型的网络中,从而扩展我们已经见过的一些网络类型的功能。我们还可以将多个递归单元组合起来,执行超出任何单个单元能完成的序列操作。让我们来看几个例子。

CNN-LSTM 网络

我们可以将 LSTM 单元与 CNN 混合,创建一种混合网络,叫做 CNN-LSTM 网络。这对于分类视频帧等任务非常有效。卷积层负责查找和识别物体,而紧随其后的递归层则负责跟踪物体如何从一帧移动到另一帧。

深度 RNN

使用递归单元的另一种方法是将它们按顺序堆叠起来。我们称结果为 深度 RNN。我们只是将一层中单元的输出作为下一层单元的输入。图 19-19 展示了三层的连接方式,分别以卷起和展开的形式绘制。像往常一样,每一层的 RNN 单元都有自己的内部权重和隐藏状态。

F19019

图 19-19:深度 RNN。左:使用我们图标的网络。右:未展开形式的各层。

这种架构的吸引力在于,每个 RNN 都可以针对特定任务进行专业化。例如,在图 19-19 中,第一层可能将输入句子翻译成一个抽象的、通用的语言,第二层可能重新表述它以改变语气,第三层则可能将其翻译成另一种目标语言。通过单独训练每个 LSTM,我们可以获得专业化的优势,比如能够独立更新或改进每一层。如果我们用另一个 LSTM 层替换其中一个,我们需要对整个网络进行一些额外的训练,以确保各层协同工作顺畅。

双向 RNN

让我们回到翻译问题,并考虑这个问题有多么困难。拿句子“我看到了热狗火车”来说。我们至少可以找到六种不同的解释方式(目睹一只温暖的狗做锻炼、一只迷人的狗、一根香肠,或目睹一辆火车拉着这些三种东西中的每一种)。有些解释比其他的更荒谬,但它们都是有效的。当我们翻译时,应该选择哪一种呢?

另一个著名的句子是:“I saw the man on the hill in Texas with the telescope at noon on Monday”(我在星期一中午在德克萨斯州用望远镜看到山丘上的那个男人),它有 132 种不同的解释(Mooney 2019)。除了单词本身外,语调的变化也会对意思产生巨大影响。通过强调“I didn’t say he stole the money”(我没说他偷了钱)中的每个词,我们可以产生七种完全不同的意思(Bryant 2019)。语言的歧义性在 Groucho Marx 在电影Animal Crackers中的经典台词中得到了体现:“One morning I shot an elephant in my pajamas. How he got into my pajamas, I’ll never know”(有一天早上,我穿着睡衣射杀了一只大象。它是怎么进我的睡衣的,我永远也不知道)(Heerman 1930)。

处理所有这些复杂性的一个方法是,在翻译句子时考虑句子中的多个词,而不是一个一个词地翻译。例如,考虑这些句子:I cast my fate to the wind(我把命运交给了风),The cast on my arm is heavy(我手臂上的石膏很重),The cast of the play is all here(话剧的演员阵容都在这里)。这些句子说明了英语单词“cast”是一个同形异义词,或者说是一个具有不同含义的词。语言学家称之为多义性,这是许多语言的一个特征(Vicente 和 Falkum 2017)。我们的三个含有“cast”的句子分别翻译成葡萄牙语是:Eu lancei meu destino ao vento(我把命运交给了风),O gesso no meu braço é pesado(我手臂上的石膏很重),和 O elenco da peça está todo aqui(话剧的演员阵容都在这里)。其中,“cast”分别翻译成了 lancei、gesso 和 elenco(Google 2020)。在这些例子中,选择合适的葡萄牙语单词的方法是,除了了解“cast”前面的词外,还需要知道它后面的词。

如果我们在实时翻译,那么仅凭目前听到的词,我们可能无法确定使用哪个翻译。在这种情况下,我们能做的就是猜测,或者等待更多的词汇到来,然后试着赶上。但如果我们在翻译整句,比如在翻译一本书或故事时,我们已经可以访问所有的词汇。

句子中后面的词语的一种使用方式是将这些词倒序输入到我们的 RNN 中,例如:wind the to fate my cast I。但这通常不能解决问题,因为有时我们也可能需要前面的词。我们真正需要的是能够同时访问前后的词语。

我们可以通过现有的工具和一点巧妙的方法来实现这一点,即创建两个独立的 RNN。第一个按自然顺序接收词语,第二个按倒序接收词语,如图 19-20 所示。我们称之为双向 RNN,或双 RNN(Schuster 和 Paliwal 1997)。

F19020

图 19-20:双向 RNN,或双 RNN。左:我们为这一层设计的图标。右:展开的双 RNN 图示。

在图 19-20 中,我们将句子同时输入到前向顺序的下部递归单元和反向顺序的上部递归单元。也就是说,我们在将输入 0 给下部单元的同时,将输入 4 给上部单元。接着我们将输入 1 给下部单元,同时将输入 3 给上部单元,依此类推。所有单词处理完成后,每个递归单元将为每个单词生成一个输出。我们只需将这些输出连接起来,这就是双向 RNN 的输出。

我们可以堆叠多个双向 RNN,形成深度双向 RNN。图 19-21 展示了一个包含三层双向 RNN 的网络。左侧是我们为该层绘制的示意图,右侧是每一层展开后的形式。在这个图中,我们有三层,每层包含两个独立的递归单元。

F19021

图 19-21:一个深度双向 RNN。左侧:使用我们标注的框图。右侧:一个展开的深度双向 RNN。

和之前一样,这里的一部分价值在于,每个双向 RNN 可以为不同的任务独立训练,如果我们找到(或训练)另一个表现更好的双向 RNN,可以将其替换进来。

Seq2Seq

任何翻译系统面临的挑战之一是不同语言使用不同的词序。一个经典的例子是,在英语中,形容词通常位于名词之前,而在法语中情况则不那么简单。例如,“I love my big friendly dog”翻译成法语为“J’adore mon gros chien amical”,其中“chien”对应“dog”,但形容词“gros”和“amical”分别对应“big”和“friendly”,却位于名词的两侧。

这表明,与其一次翻译一个单词,不如翻译整句话。当输入和输出句子的长度不同的时候,这种方法显得更为合理。例如,五个单词的英文句子“My dog is eating dinner”在葡萄牙语中只需四个单词:“Meu cachorro está jantando”,而在苏格兰盖尔语中则需要六个单词:“Tha mo chù ag ithe dinnear”(Google 2020)。

因此,与其逐个词地翻译,不如将完整的序列转化为另一个完整的序列,可能长度不同。一个常用的算法,用于将一个完整序列转换为另一个序列,称为seq2seq(“sequence to sequence”的缩写)(Sutskever, Vinyals, 和 Le 2014)。

seq2seq 的关键思想是使用两个 RNN,我们将其视为编码器解码器。让我们看看训练完成后系统是如何工作的。我们按常规将输入数据喂给编码器,一次输入一个单词,但我们忽略其输出。当整个输入处理完毕后,我们将编码器的最终隐藏状态交给解码器。解码器使用编码器的最终隐藏状态作为自己的初始隐藏状态,并通过自回归生成输出序列。图 19-22 展示了这一思想。

f19022

图 19-22:seq2seq 的架构。左侧是编码器,处理输入并将其隐藏状态发送给右侧的解码器,解码器生成输出。

在图 19-22 中,我们通过将每个解码器步骤的输出作为输入喂给下一个解码器,明确展示了自回归步骤。如果编码器-解码器结构看起来很熟悉,那是因为它与我们在第十八章看到的自编码器的基本结构相同。在这种用法中,我们之前称为潜在向量的部分现在被称为 上下文向量

让我们更仔细地看看这两个 RNN 以及它们如何翻译一个句子。

编码器从其隐藏状态设置为某个初始值开始,比如全零。它处理第一个词,更新其隐藏状态,并计算一个输出值。我们简单地忽略输出值。我们关心的唯一事情是编码器内隐藏状态的变化。

当最后一个词被处理完后,编码器的隐藏状态将用于初始化解码器的隐藏状态。

像任何 RNN 一样,解码器需要输入。按照惯例,我们给解码器一个特殊的开始标记。这个标记可以用任何我们喜欢的方式书写,只要它明显是特殊的,并且不属于输入或输出的正常词汇。常见的惯例是将其大写并放在方括号或尖括号中,例如 [START]。像我们词汇表中的所有词一样,这个特殊标记也有一个独特的编号。

现在解码器有了输入,它更新其隐藏状态(最初是编码器的最终隐藏状态),并生成一个输出值。我们确实关注这个输出,因为它是我们翻译的第一个词。

现在我们使用自回归来完成其余的翻译。解码器将前一个输出词作为输入,更新其隐藏状态,并生成新的输出。这一过程会持续,直到解码器决定没有更多的词需要生成。它通过生成另一个特殊标记(例如 [END])来标记这一点,然后停止。

我们训练了一个 seq2seq 模型,将英文翻译成荷兰语(Hughes 2020)。这两个 RNN 的状态中各有 1,024 个元素。训练数据包含大约 50,000 个荷兰语句子,以及它们的英文翻译(Kelly 2020)。我们使用了大约 40,000 个句子进行训练,其余的用于测试。我们训练了十个周期。在接下来的两个例子中,我们提供了一个英文句子,seq2seq 提供的荷兰语翻译,以及谷歌翻译将荷兰语翻译回英文的结果。

  1. 你知道现在几点了吗

  2. 您知道现在几点了吗

  3. 你知道现在几点了吗

  4. 我喜欢弹钢琴

  5. 我喜欢弹钢琴

  6. 我喜欢弹钢琴

对于如此小的网络和训练集,这些结果相当不错!另一方面,当输入变得更加复杂时,我们的小模型表现得不太好,正如这个输入输出集合所示:

  1. 约翰告诉萨姆,他的老板说如果他加班,他们会给他奖金

  2. hij nodig had hij een nieuw hij te helpen

  3. 他需要一个新的他帮助

seq2seq 方法有很多优点。它在概念上很简单,在许多情况下都能很好地工作,并且在现代库中实现起来很容易(Chollet 2017;Robertson 2017)。但是,seq2seq 存在一个内在的限制,即上下文向量。这只是编码器在处理完最后一个单词后的隐藏状态,因此它是固定的、有限的大小。这个向量必须包含关于句子的一切,因为它是解码器所获得的唯一信息。

如果我们给编码器一个句子,开始是“桌子有四条坚固的腿”,那么我们可以想象,合理的记忆量能够保留足够的关于序列中每个单词的信息,记得我们在谈论一张桌子,且下一个单词应该是“腿”。但是,无论我们给编码器的隐藏状态多么大,我们总是可以构造一个比它能记住的内容更长的句子。例如,假设我们的句子是:“桌子,尽管经历了所有长距离的移动,书本掉到上面,孩子们全速撞向它,作为堡垒、阶梯和门挡等多重功能,它仍然有四条坚固的腿。”下一个单词应该仍然是“腿”,但我们的隐藏状态需要变得更大,才能记住足够的信息来理解这一点。

无论我们的隐藏状态有多大,一个更长的句子总是会出现,并且需要比我们拥有的更多的记忆。这就是所谓的长期依赖问题(Hochreiter et al. 2001;Olah 2015)。图 19-23 展示了一个展开的 seq2seq 图,其中输入包含许多单词(Karim 2019)。一个能够记住所有这些信息的上下文向量需要很大,且每个 RNN 中必须有相应的大型神经网络来管理和控制它。

f19023

图 19-23:在将非常长的输入句子发送到解码器之前进行编码

或许依赖于单一的上下文向量来表示输入中每个有用的信息并不是最好的做法。seq2seq 架构忽略了编码器的所有隐藏状态,除了最后一个。对于长输入,这些中间的隐藏状态可能包含的信息会在我们到达句子结尾时被遗忘。

依赖单一的上下文向量并且需要一次训练一个单词,对于 RNN 架构来说是一个很大的问题。尽管它们在许多应用中非常有用,但这些都是严重的缺点。

尽管存在这些问题,RNN 仍然是处理序列的一种流行方式,尤其是当序列不太长时。

摘要

在这一章中,我们已经讨论了很多关于处理语言和序列的内容。我们看到我们可以用全连接层预测序列中的下一个元素,但它们存在问题,因为没有输入的记忆。我们还看到了如何使用带有局部或隐藏记忆的递归单元来保持它们在一个上下文向量中看到的一切,并随着每个输入的变化进行修改。

我们看了一些使用 RNN 的例子,然后学习了如何使用两个 RNN 构建一个名为 seq2seq 的翻译器。虽然 seq2seq 简单且能做得很好,但它有两个常见于大多数 RNN 系统的缺点。例如,该系统依赖于一个上下文向量来携带关于句子的所有信息。第二,网络需要逐个单词进行训练。

尽管存在这些问题,RNN 仍然是处理任何类型序列数据的流行且强大的工具,从语言到地震数据、歌曲歌词和病历等。

在下一章,我们将探讨另一种处理序列的方法,它避免了 RNN 的局限性。

第二十章:注意力和变换器

在第十九章中,我们探讨了如何使用 RNN 处理序列数据。尽管 RNN 非常强大,但也有一些缺点。因为所有关于输入的信息都通过一个单一的状态记忆(或上下文向量)来表示,每个递归单元内部的网络需要努力将所有必要的信息压缩到有限的空间中。而且,无论我们将状态记忆做得多大,总会遇到超出记忆容量的输入,因此总有一些信息会丢失。

另一个问题是,RNN 必须一次处理一个词。这可能是一种较慢的工作方式,尤其是在处理大型数据库时。

一种替代方法基于一个叫做注意力网络的小型网络,这种网络没有状态记忆,可以并行训练和使用。注意力网络可以组合成更大的结构,叫做变换器(transformers),这些变换器能够作为语言模型执行像翻译这样的任务。变换器的构建模块可以用于其他架构,提供更强大的语言模型,包括生成器。

在本章中,我们首先介绍了一种更强大的方式来表示词汇,而不是将其作为单一数字,然后逐步构建我们的注意力机制和使用变换器模块执行多种自然语言处理任务的现代架构。

嵌入

在第十九章中,我们承诺将我们的词汇描述提升到不仅仅是一个数字的层次。这一变化的价值在于它让我们可以以有意义的方式操作词汇的表示。例如,我们可以找到一个与另一个词相似的词,或者我们可以将两个词融合,找到它们之间的一个词。这个概念是发展注意力机制以及后续变换器(transformer)的关键。

这种技术称为词嵌入(或者当我们应用于更一般的标记概念时,称为标记嵌入)。这个概念有点抽象,所以让我们先通过一个具体的例子来看一下这些想法。

假设你在一部电影中担任动物驯养员,而导演脾气暴躁。今天你正在拍摄一个场景,其中人类英雄被一些动物追赶。导演要求你提供一个可以在数量上满足需求的动物列表,用来拍摄一个令人害怕的追逐场面。你打电话给办公室,他们准备了这个列表,并且将这些动物安排成一张图表,水*轴代表每种成年动物的*均最高速度,垂直轴代表其*均体重,如图 20-1 所示。

F20001

图 20-1:一组动物,按照地面速度大致水*排列,成年体重垂直排列,尽管这些轴标签没有显示(数据来源:Reisner 2020)

但是,由于打印错误,你的办公室发给你的图表缺少了轴标签,所以你手上有一张二维图表,动物们被排列在其中,但你不知道这些轴代表什么。

导演甚至没有看图表。“马,”她说,“我想要马。它们正是我想要的,完美无缺,别的都不行。”于是你带来了马,它们开始排练场景。

不幸的是,导演不满意。“不,不,不!”她说。“这些马太过敏捷和迅速,像狐狸一样。给我一些不那么像狐狸的马。”

你怎么可能满足这个要求呢?这到底是什么意思?幸运的是,你可以按照她的要求,利用图表通过组合箭头来实现。

你只需要做两件事:加箭头和减箭头。要将箭头 B 加到箭头 A 上,把 B 的尾部放到 A 的头部。新的箭头 A + B 从 A 的尾部开始,到 B 的头部结束,如图 20-2 中间所示。

F20002

图 20-2:箭头运算。左:两个箭头。中:和 A + B。右:差 A – B。

要从 A 中减去 B,只需将 B 逆时针旋转 180 度,变成–B,然后将 A 和–B 加在一起。结果,A – B,从 A 的尾部开始,到–B 的头部结束,如图 20-2 右侧所示。

现在你可以满足导演移除马身上狐狸特征的要求。首先,从图表的左下角画一支箭头指向马,再画一支箭头指向狐狸,如图 20-3 左侧所示。

F20003

图 20-3:左:从左下角到马和狐狸的箭头。右:从马中减去狐狸得到一只巨大的树懒。

现在按照要求从马中减去狐狸,方法是将狐狸箭头从马箭头中减去。按照图 20-2 的规则,这意味着将狐狸箭头翻转并将其尾部放在马箭头的头部。我们得到图 20-3 的右侧。

一只巨大的树懒。好吧,导演想要的就是这个。我们甚至可以像做一些小算术一样写出这个:马 – 狐狸 = 巨大树懒(至少根据我们的图示来说)。

导演把她的拿铁扔到地上。“不不不!当然,树懒看起来很好,但它们几乎不动!让它们更快!给我像路跑鸟一样快的树懒!”

现在我们知道如何满足这个荒谬的要求:找到从左下角到路跑鸟的箭头,如图 20-4 左侧所示,并将其添加到指向树懒的箭头的头部,就得到了一只棕熊。也就是说,马 – 狐狸 + 路跑鸟 = 棕熊,如图 20-4 右侧所示。

f20004

图 20-4:左:我们可以画一个指向路跑鸟的箭头。右:巨大的树懒 + 路跑鸟 = 棕熊。

你给导演提供了一组棕熊(称为棕熊群)。导演翻了个白眼。“终于,像马一样快,但不像狐狸那么敏捷,像路跑鸟一样迅速。这正是我最初要求的。”他们用熊拍摄了追逐场景,电影后来获得了广泛好评。

这个故事有两个关键要素。第一个是,我们图表中的动物虽然我们不知道它们的排列方式或坐标轴代表什么数据,但它们的布局非常有用。

第二个关键点是,最终我们发现其实不需要坐标轴标签。我们仅通过添加和减去指向图表中元素的箭头,就能在图表中导航。也就是说,我们没有尝试找到一匹“较慢的马”。相反,我们直接与动物本身进行操作,它们的各种属性隐式地随之而来。将狐狸的快速特性移除后,结合像马这样的大动物,我们得到了一只又大又慢的动物。

这和语言处理有什么关系呢?

嵌入词

为了将我们刚才看到的应用到词汇上,我们将动物替换为词语。并且,不仅仅使用两个坐标轴,我们将在一个拥有数百维的空间中放置这些词语。

我们通过一个算法来实现这一点,算法会自动确定这个空间中每个坐标轴应该代表什么,并将每个词放置在适当的位置。算法并不是为每个词分配一个单一的数字,而是为词分配一个数字列表,表示它在这个庞大空间中的坐标。

这个算法被称为嵌入器,我们说这个过程是将词嵌入嵌入空间中,从而创造出词嵌入

嵌入器会自动确定如何构造空间,并找出每个词的坐标,使得相似的词靠得更*。例如,如果它看到很多以“I just drank some”开头的句子,那么接下来出现的任何名词都会被理解为某种饮品,并被放置在其他饮品附*。如果它看到“I just ate a red”这样的句子,那么接下来出现的任何词都会被理解为红色且可食用的东西,并被放置在其他红色和其他可食用的物体附*。对于其他数十种甚至数百种关系,不管是显而易见的还是微妙的,情况也一样。由于空间有许多维度,并且坐标轴可以有任意复杂的含义,词汇可以同时属于许多不同的群体,基于看似不相关的特征。

这个概念既抽象又强大,所以让我们通过一些实际例子来说明它。我们尝试了一些“词汇算术”表达式,使用的是一个预训练的词嵌入,其中包含 684,754 个词,保存在一个 300 维的空间中(spaCy authors 2020)。我们的第一个测试是一个著名的例子:king – man + woman(El Boukkouri 2018)。系统返回了 queen 作为最可能的结果,这很有道理:我们可以想象,嵌入器在一个坐标轴上捕捉到了贵族的概念,在另一个坐标轴上捕捉到了性别的差异。其他测试结果也很接*,但并不完美。例如,lemon – yellow + green 返回了 ginger 作为最佳匹配,但预期的 lime 也排在第五*的词汇。类似地,trumpet – valves + slide 返回了 saxophone 作为最可能的结果,但预期的 trombone 排在了第一位的候选。

训练一个嵌入模型在一个拥有数百(甚至数千)维度的空间中的美妙之处在于,它能够比任何人更高效地利用这个空间,从而使其能够同时表示大量的关系。

我们刚刚看到的词汇算式是嵌入空间的一个有趣示范,但它也使我们能够有意义地对词汇进行操作,比如比较、缩放和加法,这些操作对于本章的算法都非常重要。

一旦我们拥有了词嵌入,将它们整合到几乎任何网络中都变得容易。我们不是为每个词分配一个单独的整数,而是分配词嵌入,它是一个数字列表。因此,系统处理的不是零维张量(单一数字),而是一维张量(数字列表)。

这巧妙地解决了我们在第十九章看到的问题,即那些与目标接*但不完全正确的预测往往给我们带来无意义的结果。现在我们能够容忍一些不精确的情况,因为相似的词汇被嵌入在彼此接*的位置。例如,我们可能会给语言模型输入短语“巨龙逼*并发出一声响亮的”,期望下一个词是 roar。算法可能会预测出一个接* roar 的张量,但不完全相同,而给出 bellow 或 blast。我们大概率不会得到一个完全无关的词汇,如 daffodil。

图 20-5 展示了我们提供给标准词嵌入模型的六组四个相关词汇。任何两个词嵌入的相似度越高,这对词汇的得分就越高,因此它们的交集看起来越暗。图形在从左上到右下的对角线两侧对称,因为我们比较词汇的顺序并不重要。

F20005

图 20-5:通过比较词嵌入的相似性来比较词对。

我们可以从图 20-5 看到,每个词与自身的匹配度最高,同时也比与不相关的词汇匹配得更强。由于我们将相关的词汇并排放置,图形显示它们的相似度为小块。然而,也有一些奇特之处。例如,为什么 fish 与 chocolate 和 coffee 的匹配度比*均值更高?又为什么 blue 与 caramel 的得分较高?这些可能是由于这个嵌入模型使用的特定训练数据所导致的伪影。

咖啡饮品和口味之间的得分相对较高,也许是因为人们会用这些口味的糖浆来调味咖啡饮品。颜色和口味之间也隐约存在一些关系。

许多预训练的词嵌入器可以免费获得,并且可以轻松下载到几乎任何库中。我们可以简单地导入它们,立即获得任何单词的向量。GLoVe(Mikolov 等,2013a;Mikolov 等,2013b)和 word2vec(Pennington、Socher 和 Manning,2014)嵌入在许多项目中得到应用。较新的 fastText(Facebook 开源,2020)项目提供了 157 种语言的嵌入向量。

我们还可以对整个句子进行嵌入,这样我们就可以整体比较句子,而不是逐词比较(Cer 等,2018)。图 20-6 展示了对 12 个句子(TensorFlow,2018)的嵌入比较。在本书中,我们将重点讨论词嵌入,而不是句子嵌入。

F20006

图 20-6:比较句子嵌入。分数越大,表示句子之间的相似度越高。

ELMo

词嵌入比为单词分配单个整数要更为先进。然而,即便词嵌入功能强大,我们之前描述的创建词嵌入的方法仍然存在一个问题:细微差别。

正如我们在第十九章看到的,许多语言中有些单词的意义不同,但书写和发音是相同的。如果我们想理解这些单词,就需要区分它们的不同含义。做到这一点的一种方法是为每个单词的每个含义分配一个独特的嵌入向量。例如,cupcake 只有一个意思,因此只有一个嵌入向量。但 train 则有两个嵌入向量,一个表示名词(例如,“我坐了一次火车”),另一个表示动词(例如,“我喜欢训练狗”)。这两个意思实际上是完全不同的概念,只是恰好使用了相同的字母序列。

这类词汇面临两个挑战。首先,我们必须为每个含义创建独特的嵌入向量。其次,当这些词作为输入时,我们必须选择正确的嵌入向量。解决这些问题需要我们考虑每个单词的上下文。第一个能够大规模处理这一问题的算法叫做Embedding from Language Models,但它更广为人知的是其友好的缩写ELMo(Peters 等,2018),它的名字来自儿童电视节目《芝麻街》中的一个木偶角色 Elmo。我们说 ELMo 生成了上下文化的词嵌入

ELMo 的架构类似于我们在图 19-20 中看到的一对双向 RNN,但它的结构组织方式不同。在标准的双向 RNN 中,我们将两个朝相反方向运行的 RNN 相结合。

ELMo 对这一点进行了改变。虽然它使用两个正向 RNN 网络和两个反向 RNN 网络,它们按方向分组。每个组都是一个两层深的 RNN,就像我们在图 19-21 中看到的那样。ELMo 的架构如图 20-7 所示。通常,我们使用红色配色方案绘制 ELMo 的示意图,因为《芝麻街》中的 Elmo 是一个明亮的红色角色。

F20007

图 20-7:ELMo 展开后的结构。输入文本位于底部。每个输入元素的嵌入位于顶部。

这种架构意味着每个输入单词都会转化为两个新的张量,一个来自前向网络(标记为 F1 和 F2),考虑到前面的单词,另一个来自反向网络(标记为 B1 和 B2),考虑到后面的单词。通过将这些结果连接在一起,我们得到了一个上下文化的单词嵌入,这些嵌入受到句子中所有其他单词的影响。

训练版的 ELMo 可以在多个大小的版本中免费提供下载(Gluon 2020)。一旦我们拥有了预训练的 ELMo,它可以轻松地用于任何语言模型。我们将整个句子输入 ELMo,得到一个上下文化的单词嵌入,基于其上下文。

图 20-8 展示了四个句子使用“train”作为动词,四个句子使用“train”作为名词的情况。我们将这些句子提供给了一个在 10 亿单词的数据库上训练的标准 ELMo 模型,该模型将每个单词放入一个 1024 维的空间中(TensorFlow 2020a)。我们提取了 ELMo 在每个句子中“train”一词的嵌入,并将其与所有其他句子中“train”一词的嵌入进行比较。尽管该词在每个句子中的书写方式相同,ELMo 能够根据该词的上下文识别出正确的嵌入。

F20008

图 20-8:比较 ELMo 在不同句子中使用train时的嵌入。较深的颜色表示嵌入更相似。

我们通常将像 ELMo 这样的嵌入算法放置在深度学习系统中的单独层级。这通常是语言处理网络中的第一层。我们为嵌入算法设计的图标,如图 20-9 所示,旨在表示将单词的空间放入更大的嵌入空间中。

F20009

图 20-9:我们为嵌入层设计的图标

ELMo 和其他类似的算法,如通用语言模型微调(Universal Language Model Fine-Tuning,简称ULMFiT)(Howard 和 Ruder 2018),通常在通用数据库上进行训练,例如来自网页的书籍和文档。当我们需要它们来处理某些特定的下游任务时,如医学或法律应用,我们通常会用来自这些领域的额外示例对其进行微调。结果是,一组嵌入包含了这些领域的专业语言,按其在该术语中的特殊含义进行聚类。

我们将在本章稍后的系统构建中使用嵌入。这些网络将依赖于注意力机制,因此让我们现在来看一下这个机制。

注意力

在第十九章中,我们看到如何通过考虑句子中的所有单词来改善翻译。但当我们翻译一个特定的单词时,句子中的每个单词并不是同等重要的,甚至有些单词可能与之无关。

例如,假设我们在翻译句子“I saw a big dog eat his dinner”(我看到一只大狗吃晚餐)时。在翻译“dog”时,我们可能不关心单词“saw”,但要正确翻译代词“his”可能需要我们将其与“大狗”这两个词联系起来。

如果我们能够为输入中的每个单词找出哪些其他单词能够影响我们的翻译,那么我们就可以只关注那些单词,忽略其他单词。这将大大节省内存和计算时间。而且,如果我们能够以一种不依赖于串行处理单词的方式来实现这一点,我们甚至可以并行处理。

执行这一任务的算法叫做注意力自注意力(Bahdanau, Cho, 和 Bengio 2016;Sutskever, Vinyals, 和 Le 2014;Cho 等人 2014)。注意力机制使我们能够将资源集中在输入中重要的部分。

现代的注意力机制通常基于一种名为查询、键、值QKV)的技术。这些术语来自数据库领域,在这个背景下可能显得有些晦涩。因此,我们将使用一组不同的术语来描述这些概念,并最终将其与查询、键和值连接起来。

一个激发思考的类比

让我们从一个类比开始。假设你需要购买一些油漆,但你得到的唯一信息是颜色应该是“浅黄色带有一点深橙色”。

在镇上唯一的油漆店里,唯一在职的店员刚刚加入油漆部门,对颜色不太熟悉。你们都假设你需要将几种标准油漆混合起来以得到你想要的颜色,但你不知道该选择哪些油漆,也不知道每种油漆的用量。

店员建议你将你想要的颜色描述与每个油漆罐上的颜色名称进行比较。有些名称可能比其他名称匹配得更好。店员在一个空油漆罐上放了一个漏斗,并建议你根据每个油漆罐名称与描述的匹配度,将不同的油漆倒入漏斗中。也就是说,你将把你想要的描述“浅黄色带有一点深橙色”与每个油漆罐标签上的内容进行比较,匹配度越好,你就倒入更多这种油漆。

图 20-10 通过六个油漆罐形象地展示了这个概念。它展示了油漆罐的名称以及每个名称与所需颜色描述的匹配度。我们得到了“阳光黄色”和“橙色粉碎”的不错匹配,尽管由于与“with”一词的匹配,稍微混入了一些“午餐配青绿色”。

F20010

图 20-10:根据颜色描述(左),我们根据每个油漆罐名称与描述的匹配程度(中)来混合不同的油漆,最终得到结果(右)。

在这个故事中,有三件事需要关注。首先是你的请求:“浅黄色带有一点深橙色”。第二是每罐油漆上的描述,例如“阳光黄”或“温柔蓝”。第三是每罐油漆的内容,即罐内实际的油漆。在故事中,你将自己的请求与每罐油漆的描述进行比较,以了解它们的匹配度。匹配度越高,最终混合物中使用的该罐油漆的内容就越多。

这就是注意力机制的核心。给定一个请求,将其与每个可能项目的描述进行比较,并根据描述与请求的匹配程度,选择每个项目的一部分内容。

第一篇关于注意力机制的论文的作者将这一过程与数据库中常见的一种事务类型进行了比较。在数据库的术语中,我们通过向数据库发送查询来查找某个内容。在这样的过程中,数据库中的每个对象都有一个描述性的,它可以与对象的实际不同。请注意,这里所说的指的是对象的内容,无论它是一个单一的数字,还是更复杂的东西,例如字符串或张量。

数据库系统将查询(或请求)与每个键(或描述)进行比较,并使用该得分来决定最终结果中包含多少对象的值(或内容)。因此,我们的请求、描述和内容对应于查询、键和值,或更常见的术语,QKV。

自注意力

图 20-11 以抽象形式展示了注意力机制的基本操作。这里有五个输入词。每个彩色框表示一个小型神经网络,该网络接收一个词的数值表示并将其转化为新的东西(通常这些网络每个只是一个单一的全连接层)。在这个例子中,词“dog”是我们想要翻译的词。所以,一个神经网络(红色)将“dog”的张量转换为一个新的张量,代表查询 Q。正如图中所示,另外两个小型神经网络将“dinner”的张量翻译成新的张量,分别对应其键 K(来自蓝色网络)和其值 V(来自绿色网络)。

f20011

图 20-11:使用dog作为查询来确定单词dinner相关性的注意力核心步骤。每个框代表一个小型神经网络,它将输入转化为查询、键或值。

实际操作中,我们将“dog”的查询与句子中每个单词的键进行比较,包括“dog”本身。在这个例子中,我们只关注与“dinner”一词的比较。

我们比较查询和键,以确定它们的相似度。我们用一个小的评分函数来完成这一操作,函数表示为一个圆圈中的字母S。不深入讨论数学部分,这个函数比较两个张量并产生一个单一的数字。两个张量越相似,得出的数字就越大。评分函数通常设计为产生一个介于 0 和 1 之间的数字,较大的值表示更好的匹配。

我们使用评分函数的输出对表示晚餐的值的张量进行缩放。查询和键的匹配度越高,缩放步骤的输出越大,晚餐的值会更多地影响最终输出。

让我们看看当我们将这个基本步骤同时应用到输入中的所有单词时会是什么样子。我们将继续观察翻译单词 dog 的情况。总体结果是所有输入单词的单独缩放值的总和。图 20-12 展示了这种情况。

F20012

图 20-12:使用注意力机制同时确定句子中所有五个单词对单词dog的贡献。QKV 的空间和颜色编码与图 20-11 相匹配。图中的所有数据都朝上流动。

在图 20-12 中有几点需要注意。首先,只有三个神经网络参与——分别用于计算查询、键和值张量。我们使用相同的“输入到查询”网络(图中的红色部分)将每个输入转化为其查询,使用相同的“输入到键”网络(图中的蓝色部分)将每个输入转化为其键,使用相同的“输入到值”网络(图中的绿色部分)将每个输入转化为其值。我们只需要对每个单词应用一次这些转换。

第二,得分后和数值缩放前有一条虚线。这代表了对得分应用的 softmax 步骤,之后是一个除法操作。这两个操作可以防止得分的数值变得过大或过小。softmax 还会夸大相似项的影响。

第三,我们将所有的缩放值加起来,得到 dog 的新张量,其中包括 dog 本身的值。我们常发现每个单词与自己的得分最高。这并不是坏事,因为在这种情况下,翻译 dog 时最重要的单词确实是 dog 本身。但有时其他单词会更加重要。一些例子包括单词顺序改变、某个单词没有直接翻译且必须依赖其他单词,或者我们正在试图解析代词时。

第四个重要点是我们将图 20-12 中的处理同时应用到输入句子中的所有单词。也就是说,每个单词都被视为查询,整个过程独立地为该单词执行,如图 20-13 所示。

F20013

图 20-13:将注意力应用于句子中的其他四个单词

我们的第五个也是最后一个要点只是对我们一直在注意的一点进行明确的回顾:在图 20-12 和图 20-13 中的所有这些处理可以在四个步骤中并行完成,而不依赖于句子的长度。步骤 1 将输入转化为查询、键和值张量。步骤 2 对所有查询和键进行互相评分。步骤 3 使用这些评分来缩放值,步骤 4 将缩放后的值相加,生成每个输入的新输出。

这些步骤都不依赖于输入的长度,因此我们可以在与短句子相同的时间内处理长句子,只要我们拥有所需的内存和计算能力。

我们称图 20-12 和图 20-13 的过程为自注意力,因为注意力机制使用相同的输入集来计算所有内容:查询、键和值。也就是说,我们在找出输入应该如何关注自身。

当我们将自注意力置于深度网络中时,我们将其放入自己的自注意力层,通常简洁地称为注意力层。输入是一个数字形式的单词列表,输出也是相同的。

驱动注意力机制的引擎是评分函数和神经网络,后者将输入转化为查询、键和值。我们简要地考虑一下它们。

评分函数将查询与键进行比较,返回一个从 0 到 1 的值,两个值越相似,得分越高。因此,我们认为相似的输入需要将相似的值传入评分函数。现在我们可以看到嵌入的实际价值。回想我们在第十九章讨论的《双城记》,我们根据每个单词在文本中的顺序给它们分配了一个数字。这使得单词“keep”和“flint”分别得到了 1,003 和 1,004 的编号。如果我们仅仅比较这些数字,它们会得到一个很高的相似度分数。对于大多数句子,这并不是我们想要的。如果我们使用动词“keep”的查询值,我们通常希望它与“retain”、“hold”和“reserve”等同义词的键相似,而与“flint”、“preposterous”或“dinosaur”等无关单词的键完全不同。嵌入是通过它们为相似的单词(或以相似方式使用的单词)提供相似表示的方式。

对嵌入进行必要的微调是神经网络的工作,神经网络将输入的单词转化为可以在它们所使用的句子上下文中有意义地比较的表示。我们之所以有可能做到这一点,是因为单词已经嵌入到一个空间中,其中相似的单词彼此靠*。

同样,将输入转化为值的网络的任务是以一种可以有效缩放和组合这些值的方式表示这些值。将两个嵌入的单词混合会得到一个介于它们之间的单词。

Q/KV 注意力

在图 20-12 的自注意力网络中,查询、键和值都来自相同的输入,这也就是自注意力(self-attention)这个名字的由来。

一个流行的变种使用一个源来获取查询,另一个源来获取键和值。这更接*我们的油漆店类比,我们带着查询进店,而商店有键和值。我们称这种变种为Q/KV网络,其中斜杠表示查询来自一个源,键和值来自另一个源。当我们将注意力加入像 seq2seq 这样的网络时,查询来自编码器,键和值来自解码器,因此有时也称为编码器-解码器注意力层。其结构如图 20-14 所示。

F20014

图 20-14:Q/KV 层就像自注意力(见图 20-12)一样,区别在于查询(queries)不来自输入。

多头注意力

注意力的想法是识别相似的词并创建它们的有用组合。但是,词可以根据许多不同的标准被认为是相似的。我们可以考虑名词是相似的,或者颜色,或者空间概念,如上和下,或者时间概念,如昨天和明天。这些中哪一个是最好的选择?

当然,没有一个最佳答案。实际上,我们通常希望同时使用多个标准来比较词语。例如,在写歌词时,我们可能希望对意义相似、最后一个音节发音相似、音节数相同并且重音模式相同的词对赋予高分。而在写关于体育的文章时,我们可能更倾向于说,同一队伍中的球员以及扮演相同角色的球员彼此相似。

我们可以通过同时运行多个独立的注意力网络,简单地沿多个标准为词汇打分。每个网络称为一个。通过独立初始化每个头,我们希望在训练过程中,每个头将学习根据对比其他层所用的标准,同时有用且不同的标准来比较输入。如果我们愿意,我们可以增加额外的处理,明确鼓励不同的头关注输入的不同方面。这个想法被称为多头注意力,我们可以将其应用于像图 20-12 这样的自注意力网络和像图 20-14 这样的 Q/KV 网络。

每个头都是一个独立的注意力网络。我们拥有的头越多,它们就能集中注意力的输入方面就越多。

图 20-15 展示了一个多头注意力层的示意图。如图所示,我们通常将各个头的输出组合成一个列表,并将其通过一个单一的全连接层。这使得整个多头网络的输出与其输入具有相同的形状。这种方法使得将多个多头网络串联在一起变得容易。

F20015

图 20-15:多头注意力层。一个带菱形的框是我们用来表示注意力层的图标。

注意力是一种通用概念,我们可以将其以不同形式应用于任何类型的深度网络。例如,在 CNN 中,我们可以缩放滤波器的输出,以强调响应输入中最相关位置的值(Liu 等人,2018;H. Zhang 等人,2019)。

层图标

图 20-16 展示了我们为不同类型的注意力层设计的图标。多头注意力被绘制成一个小的 3D 框,暗示着一堆注意力网络。对于 Q/KV 注意力,我们在菱形内部放置一条短线,以标识 Q 输入,并将 K 和 V 输入放置在相邻的侧面。

F20016

图 20-16:注意力层图标。(a)自注意力。(b)多头自注意力。(c)Q/KV 注意力。(d)多头 Q/KV 注意力。

Transformer

现在我们有了嵌入和注意力,准备好兑现我们之前承诺的改进 RNN 的目标。

我们的目标是构建一个基于注意力网络而非 RNN 的翻译器。关键思想是,注意力层将学习如何根据词与词之间的关系将我们的输入转换为其翻译。

这种方法首次出现在一篇标题为《Attention Is All You Need》(Vaswani 等人,2017)的论文中。作者将他们基于注意力的模型称为transformer(这个名字不幸地有些模糊,但现在已经牢牢地成为该领域的术语)。transformer 模型效果如此优秀,以至于我们现在拥有了一类新的语言模型,它们不仅可以并行训练,而且在广泛的任务中可以超越 RNN。

Transformer 使用了我们尚未讨论的三个新概念。让我们现在来介绍它们,这样当我们真正进入 transformer 架构时,就能顺利进行。

跳跃连接

我们要介绍的第一个新概念被称为残差连接跳跃连接(He 等人,2015)。其灵感来自于减少深度网络层所需的工作量。

让我们从一个类比开始。假设你正在用丙烯画颜料在画布上画一幅真实的物理肖像画。在经过数周的坐姿之后,肖像完成了,你将它送给你的模特以供审批。他们说他们喜欢这幅画,但他们后悔自己在某个手指上戴了一个特定的戒指,应该戴另一个他们更喜欢的戒指。你能改变它吗?

一种做法是邀请你的对象回到工作室,从头开始在空白画布上重新绘制一幅全新的肖像,这次是戴上了新戒指。那会需要大量时间和精力。如果他们允许的话,更快速的方法是拿出现有的肖像,悄无声息地在旧戒指上绘制新戒指。

现在考虑深度网络中的一层。一个张量进入,层对该张量进行一些处理以改变它。如果该层只需要对输入做出小的改变,或者仅仅是在某些地方改变,那么对那些不需要变化的张量部分进行处理将是浪费资源的。就像画画一样,如果该层仅计算它想做的改变,那会更加高效。然后它可以将这些改变与原始输入结合,生成输出。

这个理念在深度学习网络中表现得非常出色。它让我们可以构建更小、更快的层,甚至改善反向传播中的梯度流,从而让我们能够高效地训练由几十层甚至上百层组成的网络。

该机制在图 20-17 的左侧展示。我们像往常一样将输入张量传递到某一层,让它计算变化,然后将该层的输出添加到其输入张量中。

F20017

图 20-17:左侧:跳跃连接,红色标出。右侧:我们可以将跳跃连接放置在多个层之间。

图中额外的线条,传递输入到加法节点,称为跳跃连接,或残差连接,因为它在数学上的解释。

如果需要,我们可以像图 20-17 右侧那样,将跳跃连接放置在多个层之间。

跳跃连接之所以有效,是因为每一层都在尝试减少自己对最终误差的贡献,同时参与由其他所有层组成的网络。跳跃连接是网络的一部分,因此该层学会了不需要处理那些不需要变化的张量部分。这使得该层的工作变得更简单,从而能够变得更小、更快。

稍后我们将看到,变压器不仅仅因为效率和速度使用跳跃连接,更因为它们允许变压器巧妙地跟踪输入中每个元素的位置。

Norm-Add

在通往 Transformer 的道路上,第二个要讲解的概念更像是一种概念性和符号性缩写。在 Transformer 中,我们通常对层的输出应用一个叫做层归一化的正则化步骤,或者称为层规范化,如图 20-18 左侧所示(Vaswani 等人,2017)。层归一化属于我们在第十五章看到的正则化技术的范畴,例如 dropout 和 batchnorm,它们通过控制网络中流动的值不至于过大或过小,从而帮助防止过拟合。层归一化步骤学习调整来自某一层的值,使它们逼*均值为 0,标准差为 1 的高斯分布。

F20018

图 20-18:左侧:层归一化后跟随跳跃连接的加法步骤。右侧:归一化-加法的组合图标。这只是对左侧网络的视觉和概念性缩写。

执行层归一化在使 Transformer 工作良好时非常重要,但关于这个步骤可以放置的位置有一定的灵活性。一种常见的方法是在跳跃连接的加法步骤之前放置层归一化,如图 20-18 左侧所示。由于这两个操作总是成对出现,因此将它们组合成一个我们称之为归一化-加法的单一操作是非常方便的。我们为归一化-加法设计的图标是层归一化和求和图标的组合,如图 20-18 右侧所示。这只是层归一化后跟随跳跃连接加法的两个独立步骤的视觉缩写。

有人尝试过将层归一化操作放置在其他位置,例如放在层之前(Vaswani 等人,2017)或加法节点之后(TensorFlow 2020b)。这些方法在细节上有所不同,但在实践中,似乎所有这些选择都可以互相比较。我们将在这里继续使用图 20-18 中的版本。

位置编码

在我们开始讨论 Transformers 之前,需要讲解的第三个概念旨在解决一个问题,即当我们将 RNN 从系统中移除时:我们失去了每个单词在输入句子中的位置。这个重要信息是 RNN 结构固有的,因为单词是一个一个地输入,这使得循环单元中的隐藏状态能够记住单词到达的顺序。

但是,正如我们所看到的,注意力机制将多个单词的表示混合在一起。那么后续阶段如何知道每个单词在句子中的位置呢?

解决方案是将每个单词的位置或索引插入到单词本身的表示中。这样,在处理单词的表示时,位置信息自然会跟随其一起传递。这个过程的通用名称是位置编码

一种简单的位置编码方法是将几个比特附加到每个单词的末尾,用来表示它的位置,如图 20-19 左侧所示。但在某些情况下,我们可能会遇到需要更多比特的句子,而这时我们就会遇到问题,因为我们无法为每个单词分配一个唯一的位置编号。如果我们把存储空间做得过大,就会浪费资源并使一切变得更慢。这种方法也很难实现,因为我们需要引入一些特殊的机制来处理这些比特(Thiruvengadam 2018)。

F20019

图 20-19:跟踪句子中每个单词的位置。左:将索引附加到每个单词。中:使用函数 F 将每个索引转换为向量,然后将其添加到单词的表示中。右:我们的位置嵌入层的图标。

更好的答案是使用一个数学函数,为序列中的每个位置创建一个唯一的向量。假设我们的词向量长度为 128 个元素。然后我们将每个单词的索引(它可以根据需要变得非常大)传递给这个函数,函数会返回一个新的 128 元素的向量,某种程度上描述了该位置。基本上,它将索引转换为一个唯一的值列表。我们的期望是,网络将学会将这些列表与输入中单词的位置关联起来。

与其将这个向量附加到单词的表示中,我们将两个向量相加,如图 20-19 中间所示。在这里,我们将编码中每个元素中的数字加到单词嵌入中相应位置的数字上。这种方法的吸引力在于我们不需要额外的比特或特殊的处理。这种位置编码的形式叫做位置嵌入,因为它类似于我们在像 ELMo 这样的算法中看到的词嵌入。图的右侧展示了我们为这个过程设计的图标,它用一个小的正弦波来表示,因为流行的嵌入函数选择基于正弦波(来源于 Vaswani 等,2017)。

将位置信息添加到每个单词中,而不是附加到后面,可能看起来有点奇怪,因为这改变了单词的表示。似乎位置信息也容易在注意力网络处理这些值时丢失。

事实证明,用于计算位置嵌入向量的特定函数通常只会影响单词向量一端的几个比特(Vaswani 等,2017;Kazemnejad 2019)。此外,似乎变压器模型在处理过程中学会了区分每个单词的表示和位置信息,因此它们被分别解释(TensorFlow 2019a)。

那么为什么位置嵌入在处理过程中不会完全丢失呢?毕竟,注意力通过神经网络将输入转换为 QKV 值,然后混合这些值。位置相关的信息肯定会被混乱并丢失吧。

这个问题的巧妙解决方案内建于变换器的架构中。正如我们将看到的,变换器网络将每个操作(除了最后一个)包装在跳跃连接中。嵌入信息从未丢失,因为它在每个处理阶段后都会被重新添加。图 20-20 说明了位置嵌入和规范加跳跃连接在结构上的相似性。简而言之,每一层都可以以任何方式改变其输入向量,然后位置嵌入会被重新加回,以便下层使用。

F20020

图 20-20:左图:创建位置嵌入并将其添加到单词中。右图:一个规范加操作在处理后隐式地将单词的嵌入信息添加回来。

组装一个变换器

现在我们已经具备了构建变换器的所有组成部分。我们将继续使用基于单词的翻译作为我们的运行示例。

需要注意的是,变换器这一名称指的是受原始变换器论文(Vaswani 等,2017)架构启发的各种网络。在本讨论中,我们将坚持使用一种通用版本。

我们的变换器框图如图 20-21 所示。标记为ED的模块是由注意力层构建的重复层序列或模块。稍后我们会详细介绍这两种模块。大体框架是,编码器阶段(由编码器模块,标记为E构成)接受一个句子,解码器(由解码器模块,标记为D构成)接受来自编码器的信息并产生新的输出(该图的结构在某些方面类似于展开的 seq2seq 图,但这里没有递归单元)。

F20021

图 20-21:一个变换器的框图。输入被编码然后解码。解码器的输出被递归地反馈到其输入。虚线表示重复的元素。

编码器和解码器都从词嵌入开始,接着是位置嵌入。解码器末尾有常见的全连接层和 softmax,用于预测下一个词。解码器是自回归的,因此它将每个输出词附加到其输出列表中(见图底部的框),该列表成为生成下一个词的解码器输入。解码器包含多头 Q/KV 注意力网络,如图 20-14 所示,这些网络的键和值来自编码器块的输出,见图 20-21 中部,编码器的输出被传递到解码器块中。这说明了为什么 Q/KV 注意力也叫做编码器-解码器注意力。

让我们更仔细地看看图 20-21 中的块,从编码器块开始,见图 20-22。

F20022

图 20-22:变压器的编码器块。第一层是自注意力。

编码器块以一层多头自注意力开始,这里展示了八个头。因为这一层应用的是自注意力,查询、键和值都来源于到达该块的单一输入集。这个多头注意力层被一个归一化加跳跃连接所包围,帮助保持数值呈高斯分布,并保留位置信息嵌入。

紧接着是两层,通常统称为逐点前馈层(另一个遗憾的模糊名称)。虽然原始的变压器论文将其描述为一对修改过的全连接层(Vaswani 等人,2017),我们可以更方便地将其视为两层 1×1 卷积(Chromiak 2017;Singhal 2020;A. Zhang 等人,2020)。它们学习如何调整多头注意力层的输出,以去除冗余并集中处理接下来最有价值的信息。第一层卷积使用 ReLU 激活函数,而第二层则不使用激活函数。像往常一样,这两步被包裹在归一化加跳跃连接中。

现在让我们看看解码器块,见图 20-23。

F20023

图 20-23:变压器的解码器块。请注意,第一个注意力层是自注意力,而第二个是 Q/KV 注意力。自注意力层左侧的三角形表示该层使用了掩蔽。

从高层次来看,它与编码器块非常相似,只是多了一步注意力操作。让我们逐层分析。

我们从一个多头自注意力层开始,和编码器模块一样。该层的输入是到目前为止 Transformer 输出的单词。如果我们刚开始,这句话只包含 [START] 标记。像任何自注意力层一样,目的在于查看所有输入单词,并确定哪些单词彼此之间关系最为紧密。像往常一样,这一过程通过一个跳跃连接与一个归一化加节点(norm-add node)包裹。训练过程中,我们在这一自注意力步骤中添加一个额外的细节,称为 遮蔽(masking),这一点在图 20-23 中通过一个小三角形标出,稍后我们会详细解释。

自注意力层后面是一个多头 Q/KV 注意力层。查询向量(Q)来自前一个自注意力层的输出。键(keys)和值(values)则来自所有编码器模块的连接输出。这个层同样通过一个跳跃连接与一个归一化加节点(norm-add node)包裹。该阶段使用前一个注意力网络的输出,在来自编码器的键中进行选择,并混合与这些键对应的值。最后,我们有一对 1×1 卷积,遵循与编码器模块相同的模式。

现在我们可以将各个部分组合起来了。图 20-24 展示了一个 Transformer 模型的结构。

F20024

图 20-24: 完整的 Transformer。图标显示的两个叠加框代表两个连续的 1×1 卷积。虚线表示没有绘制的重复元素。

我们曾经承诺回到每个解码器模块中第一个注意力层的一个细节。正如我们提到的,Transformer 中核心的注意力机制的一个重要价值就是它允许大量并行化。无论注意力块接收五个单词还是五百个单词,它都能在相同的时间内运行。

假设我们在训练系统预测句子中的下一个单词。我们可以提供整个句子,并让它并行预测第一个单词、第二个单词、第三个单词,以此类推。

但是这里有一个问题。假设句子是 "My dog loves taking long walks"。我们可以给系统输入 "My dog loves taking long",并让它预测第六个单词 "walks"。但因为我们是在并行训练,我们希望它能利用相同的输入同时预测每一个之前的单词。也就是说,我们还希望它能从输入 "My dog loves taking long" 中预测第五个单词 "long"。

这太简单了:单词 long 就在这里!系统会发现它只需要返回第五个单词,而这显然不同于学会如何预测它。我们希望给系统输入 "My dog loves taking long",但在预测第五个单词时,它只应该看到 "My dog loves taking"。当我们尝试预测 long 时,我们希望将其隐藏或屏蔽。同样地,为了预测第四个单词,它应该只看到 "My dog loves",为了预测第三个单词,它应该只看到 "My dog",以此类推。

简而言之,我们的变换器将执行五个并行计算,每个计算预测一个不同的单词,但每个计算应该只接收它应该预测的单词之前的单词。

实现这一点的机制叫做屏蔽。我们在解码器模块的第一个自注意力层中增加了一个额外的步骤,屏蔽或隐藏每个预测步骤不应该看到的单词。因此,预测第一个单词的计算不会看到任何输入单词,预测第二个单词的计算只会看到 "My",预测第三个单词的计算只会看到 "My dog",以此类推。由于这个额外的步骤,解码器模块中的第一个注意力层有时被称为屏蔽多头自注意力层,这个名字有点难以说清,因此我们通常将其简称为屏蔽注意力层。

变换器的实际应用

让我们看一下变换器在执行翻译时的表现。我们训练了一个变换器,基本遵循了图 20-24 的架构,用于将葡萄牙语翻译成英语(TensorFlow 2019b)。我们使用了一个包含 50,000 个训练示例的数据集,按照今天的标准,这个数据集比较小,但足以展示这些概念,同时也足够大,可以在家用计算机上进行训练(Kelly 2020)。

我们给训练好的变换器输入了葡萄牙语问题 "você se sente da mesma maneira que eu?",而 Google 翻译将其翻译成了英语 "do you feel the same that way I do?"。我们的系统产生的翻译是 "do you see, do you get the same way i do?"。这并不完美,但考虑到小规模的训练数据库,它已经很好地捕捉到了问题的精神。像往常一样,更多的训练数据和训练时间肯定能改进结果。

图 20-25 显示了热图,展示了每个输出单词在解码器的最终 Q/KV 注意力层中,每个注意力头对每个输入单词的关注程度。单元格越亮,表示越多的注意力被关注。请注意,一些输入单词被预处理器拆分成多个标记。

在比这个示例更大的数据集上训练,并且训练时间更长的转换器模型,能够产生与 RNN 相媲美甚至更好的结果,而且它们可以并行训练。它们不需要具有有限内部状态的递归单元,也不需要多个神经网络来学习如何控制这些状态。这些是巨大的优势,也解释了为什么转换器在许多应用中取代了 RNN。

F20025

图 20-25:在将葡萄牙语句子“Você se sente da mesma maneira que eu?”翻译成英语时,解码器中最终 Q/KV 注意力层的八个头部的热力图

转换器的一个缺点是,随着输入大小的增加,注意力层所需的内存会急剧增加。为了减少不同情况下的这些开销,有一些方法可以调整注意力机制以及转换器(Tay 等,2020)。

BERT 和 GPT-2

图 20-24 中的完整转换器模型由一个编码器组成,编码器的设计目的是分析输入文本并创建一系列描述它的上下文向量,接着是一个解码器,解码器使用这些信息进行自回归生成输入的翻译。

组成编码器和解码器的模块并不特定于翻译。每个模块只是一个或多个注意力层,后面跟着一对 1×1 卷积层。这些模块可以作为通用处理器,用于处理序列元素之间的关系,尤其是语言方面。让我们看看两个最*的架构,它们以超出翻译范畴的方式使用了转换器模块。

BERT

让我们使用转换器模块来创建一个通用语言模型。它可以用于我们在第十九章开头列出的任何任务。

该系统被称为双向编码器表示转换器,但通常以其缩写BERT为人们所熟知(Devlin 等,2019)(另一个来自芝麻街的木偶,也是对我们之前看到的 ELMo 系统的一个点头式致敬)。BERT 的结构从一个词嵌入层和一个位置嵌入层开始,接着是多个转换器编码器模块。BERT 的基本架构如图 20-26 所示(在实际操作中,还有其他有助于训练和性能的细节,如丢弃层)。在这个图中,我们展示了多个输入和输出,以清楚地表明 BERT 正在处理整个句子。为了保持一致性和清晰性,我们只在模块内部使用单行,但并行操作仍在进行。传统上,BERT 的图示通常使用黄色配色方案,因为芝麻街中的 Bert 是一个黄色角色。

F20026

图 20-26:BERT 的基本结构。虚线表示未绘制的更多编码器模块。

原始的“大型”版本的 BERT 确实名副其实,拥有 3.4 亿个权重或参数。该系统在维基百科和超过 1 万本书籍上进行了训练(Zhu 等人 2015)。目前,原始 BERT 系统的 24 个训练版本可以免费在线获得(Devlin 等人 2020),同时,基于该方法的变化和改进也在不断增加(Rajasekharan 2019)。

BERT 经过两项任务的训练。第一项任务叫做下一个句子预测,或称NSP。在这项技术中,我们一次性给 BERT 两个句子(用一个特殊符号将它们分开),并让它判断第二个句子是否合理地跟随第一个句子。第二项任务给系统提供了一些句子,其中一些词被去掉,我们要求它填补这些空缺(语言学教育者称之为填空任务;Taylor 1953)。这与视觉过程中的闭合现象类似,描述了人类倾向于在图像中填补空白。闭合现象在图 20-27 中有所展示。

F20027

图 20-27:展示闭合原理。像这样的不完整形状通常会被人类视觉系统填补,从而形成物体。

BERT 能够在这些任务中表现良好,因为与之前看到的基于 RNN 的方法相比,BERT 的注意力层能从输入中提取更多信息。我们最初的 RNN 模型是单向的,从左到右读取输入。然后它们变成了双向的,最终发展为 ELMo,可以说是浅层双向的,其中浅层指的是每个方向上仅使用两层架构。得益于注意力机制,BERT 能够确定每个词对其他词的影响,并通过重复编码器块,它可以连续执行这一过程多次。BERT 有时被称为深度双向的,但将其视为深度密集的可能更为有用,因为它同时考虑每个词。在使用注意力时,方向的概念实际上并不适用。

让我们来体验一下 BERT。我们将从一个包含 12 个编码器块的预训练模型开始(McCormick 和 Ryan 2020)。我们将对其进行微调,以确定输入句子是否符合语法(Warstadt, Singh, 和 Bowman 2018;Warstadt, Singh, 和 Bowman 2019)。这基本上是一个分类问题,产生是/否的答案。因此,我们的下游模型应该是某种分类器。我们使用一个由单个全连接层组成的简单分类器。我们这对模型的组合展示在图 20-28 中。

F20028

图 20-28:BERT 加上一个小型下游分类器。虚线表示存在的 10 个附加的相同编码器块,但没有绘制出来。

在四个训练周期之后,以下是测试数据的六个结果。前三个是语法正确的,后三个则不是。BERT 在六个结果中都给出了正确答案。

  • 克里斯走路,帕特吃西兰花,桑迪打壁球。

  • 有一只特别的狗,拯救了每个家庭。

  • 苏珊吓到了她。

  • 这个人承认负责。

  • 猫睡得又香又毛茸茸。

  • 那只香甜且毛茸茸的猫睡着了。

在约 1,000 个句子的测试集上,这个小版本的 BERT 正确预测了大约 82%的示例。某些 BERT 变体在这个任务中达到了超过 88%的正确率(Wang 等,2019;Wang 等,2020)。

让我们在另一个任务中尝试 BERT,称为情感分析。我们将把短片影评分类为正面或负面。数据来自一个名为SST2的数据库,包含* 7,000 条电影评论,每条评论都标记为正面或负面(Socher 等,2013a;Socher 等,2013b)。

对于这次运行,我们使用了一个名为 DistillBERT 的预训练 BERT 模型(Sanh 等,2020;Alammar 2019)(术语蒸馏通常用于当我们仔细修剪一个已训练的神经网络,使其更小更快,同时不失去太多性能时)。我们再次进行分类任务,因此可以重用图 20-28 中的模型。

下面是从测试数据中逐字摘录的六个示例(无法得知它们分别指代哪些电影)。DistillBERT 正确将前 3 条评论分类为正面,将后 3 条评论分类为负面(这些评论都是小写字母,逗号被当作独立的标记)。

  • 一部美丽、娱乐性十足的两小时电影。

  • 这是一部聪明且有效的电影,导演懂得如何创造并维持情绪。

  • 一部彻底引人入胜、出人意料地感人的英国喜剧。

  • 一旦强势的动作片惯例占据主导,电影便开始下滑。

  • 一部完全意义上的僵尸电影,毫无头脑、毫无生气、漫无目的、喧闹、痛苦、令人讨厌。

  • 它是那种罕见的糟糕写作、糟糕导演和糟糕表演的结合,糟糕的三重奏。

在测试集中的 1,730 条评论中,DistillBERT 正确预测了大约 82%的情感。

总结一下,基于 BERT 架构的模型的共同点在于它们使用了一系列的编码器块。它们创建了一个句子的嵌入,捕捉了足够的信息,以便下游应用能够对其进行广泛的操作。通过合适的下游模型,BERT 可以用来执行我们在第十九章开始时提到的许多自然语言处理任务。

如果我们愿意变得聪明一些,我们可以让 BERT 生成语言,但这并不容易(Mishra 2020;Mansimov 等,2020)。一个更好的解决方案是使用解码器块,接下来我们将看到这一点。

GPT-2

我们已经看到变换器如何使用一系列解码器块来生成翻译的单词。我们也可以使用一系列解码器块来生成新的文本。

由于我们没有像完整 transformer 中的图 20-24 那样接收 KV 值的编码器阶段,我们将从每个解码器块中移除 Q/KV 多头注意力层,留下掩蔽自注意力和一对 1×1 卷积。第一个以这种方式做的系统被称为生成预训练模型 2,简称GPT-2(Radford 等人,2019 年)。其架构如图 20-29 所示。

F20029

图 20-29:GPT-2 的框图,由没有 Q/KV 层的 transformer 解码器块构成。虚线表示更多重复的、相同的解码器块。请注意,由于这些是解码器块的版本,每个块中的第一个多头注意力层是一个掩蔽注意力层。

与 BERT 类似,我们从令牌嵌入开始,然后为每个输入单词添加位置嵌入。每个解码器块中的自注意力层像以前一样使用掩蔽处理,这样在计算任何给定单词的注意力时,我们只能使用该单词以及之前单词的信息。

原始的 GPT-2 模型发布了几种不同的大小版本,其中最大的版本一次处理 512 个 token,通过 48 个解码器块,每个块有 12 个头,总共有 1,542 百万个参数。也就是 1.5 十亿个参数。GPT-2 是在一个名为WebText的数据集上进行训练的,该数据集包含大约八百万个文档,总共大约 40GB 的文本(Radford 等人,2019 年)。

我们通常通过从预训练模型开始使用 GPT-2,然后通过提供一个附加数据集来微调它,过程中调整所有的权重(Alammar 2018 年)。

我们在第十九章中启动了每个文本生成器时都使用了一个种子,但那只是让它们开始运行的一种方式。一个更简单的方法是用一般的指导和提示来启动系统。这被称为零-shot场景,因为系统在没有任何“示例”的情况下开始工作,也就是说,它没有任何用于生成新文本的示例或模型。

例如,假设我们建立了一个系统来建议每天穿什么。一个零-shot 场景可能以指令“描述今天的服装”开始,接着是提示“今天我应该穿:” 然后生成器会继续。它没有任何示例或上下文可以使用,所以它可能会建议穿盔甲、太空服或者熊皮。

另外,我们可以提供一个或多个示例,或称为 shot。在一个 one-shot 场景中,我们可能给出指令“描述今天的服装”,接着是示例“昨天我穿了一件蓝色衬衫和黑色裤子”,然后以提示“今天我应该穿:”结尾。思路是,在提示之前提供的文本可以帮助引导系统生成我们想要的输出。在这种情况下,熊皮的可能性会小一些。

如果我们给系统两三次机会,但不再多次尝试,通常称之为少-shot情境(这些术语并没有明确的界限)。人们通常倾向于选择那些需要尽可能少的尝试来提供我们所需输出的生成器。

让我们看看 GPT-2 的实际应用,使用一个中等大小、预训练的 GPT-2 模型(von Platen 2020)。我们不进行微调,因此系统将仅根据其核心训练数据生成文本。我们采取零-shot 方法,除了起始提示语“今天早上我被河马的咆哮声吵醒”外,不提供任何信息。以下是一个典型的输出,逐字输出:

今天早上我被河马的咆哮声吵醒。我正在进行一次长时间的散步,突然看到一只巨大的河马。我非常激动。我非常激动。我非常激动。我非常激动。

此时,系统不停地重复“我非常激动”。这并不是生成器的错,而是我们的错。系统生成了符合语法的输出,这是我们训练它的目标。问题在于,尽管它强调了激动,但输出却很无聊。一个句子的结尾恰好回到了同一句话的开头,我们陷入了死循环。现有系统并不知道这样的输出是无聊或不理想的。

为了使输出更有趣,我们可以逐步削减问题,去除我们认为不理想的输出特征。我们来看两个这样的更改(Vijayakumar et al. 2018;Shao et al. 2017)。

首先,让我们去除这些重复内容。如果系统反复生成相同的词组,我们可以对其进行惩罚。这叫做n-gram 惩罚,因为一系列n个词被称为n-gram(Paulus, Xiong 和 Socher 2017;Klein 等人 2017)。我们对系统宽容一点,只惩罚重复的词对,或者 2-grams。

其次,与其总是选择最高概率的词语,不如选择几个词语,看看它们会引领我们走向哪里。也就是说,对于每个词,我们将生成更多的后续词。当我们沿着多个路径走了一段时间后,我们会对每条路径进行评分,以确定哪一条路径的生成概率最高。我们将该分支的第一个词作为预测词,并抛弃该分支的其他部分以及其他所有分支。然后,我们为下一个词重复这一过程,依此类推。这叫做束搜索(Russell 和 Norvig 2009)。这样我们应该能够得到更长的、连贯的句子。而且我们不再总是选择概率最高的词,而是从几个最有可能的词中进行选择,依据它们的概率,这将有助于使输出更加不可预测。这被称为增加词语选择过程的温度(温度为零时对应总是选择最可能的词)。

做出这两个更改后,让我们再次尝试使用相同的起始句子生成文本。以下是一个逐字的结果:

今天早晨,我被一只河马的吼声吵醒。“天啊,”我说,“真对不起。我不是故意伤害你的。我只是说,我不知道该如何应对它。你明白我的意思吗?我不会让你伤害我,好吗?你是我的朋友,我非常爱你。我想让你知道,你在这件事上并不孤单。我们已经在一起很久了,所以这不是我们能做什么的事,但我忍不住为你感到难过,因为我知道,你再也不会孤单地和我在一起了。”

哇,真是了不起。我们本可以通过使用更多的处理技术来继续改善输出(Welleck 等人 2020),但这已经相当不错了,尤其是考虑到我们使用的是精简版的 GPT-2 模型。

为了对比,我们回顾一下第十九章中的同一篇《福尔摩斯探案》。我们从一个小型、预训练的 GPT-2 开始,针对这些故事进行了微调(Woolf 2019),然后给出了提示:“我请福尔摩斯解释。”

我们使用了稍微温暖的温度,因此每次运行算法时,得到的结果都不同。这只是其中之一,我们随意在几段后将其截断。

我请福尔摩斯解释。

“其实很简单,”他说。“简短的回答当然是,法律不适用于这些事项,案件应完全由皇家检察机关和警方自行裁量。但长的回答是,案件显然不在我们手中,警方有很大的权力来防止它发生。”

“你还记得,华生,就是在今天,年轻人拉蒂默先生拿起了那把他用来烙印我的长型左轮手枪。这把左轮手枪可能是世界上最著名的武器。你记得,华生,我当时说过,与其恐惧它,不如不知道它。现在,我们已经听到了,而且我们不能让自己陷入困境。事情已经复杂到一定程度,而且不必用这种方式来评论事实。那个不知道的人,实际上并不是对自己构成危险。让我们来看看知道的人。”

这些结果是语法正确的,甚至可以自我指涉。将这个输出与我们从基于字符的 RNN 自回归中得到的输出进行比较。

GPT-2 可以很好地完成许多其他任务,例如运行完形填空测试、预测短语中下一个单词(在该单词前至少 50 个词的地方出现了关键信息)、回答文本问题、总结文档,以及进行语言之间的翻译。

生成器讨论

GPT-2 表明,如果我们每次处理 512 个标记,通过 48 层解码器,其中每层有 12 个注意力头,总共有 15 亿个参数,我们可以生成相当不错的文本。如果我们将一切规模化呢?也就是说,我们不会修改基本架构,只是增加更多的每一部分。

这是 GPT-2 继任者的计划,名为(惊讶)GPT-3。GPT-3 的框架图通常类似于 GPT-2 的图 20-29(除了某些效率改进之外)。只是各方面的规模更大。大得多。GPT-3 一次处理 2,048 个标记,使用 96 个解码器层,每个层有 96 个注意力头,总共有 1750 亿个参数(Brown 等,2020 年)。1750 亿。训练这个庞然大物估计需要 355 个 GPU 年,费用约为 460 万美元(Alammar,2018 年)。

GPT-3 的训练数据集叫做Common Crawl数据集(Common Crawl,2020 年)。它最初包含来自书籍和网络的大约 1 万亿个单词。去除重复和清理数据库后,数据集仍包含约 4200 亿个单词(Raffel 等,2020 年)。

GPT-3 能够生成许多不同类型的数据。它曾作为一种 beta 测试阶段向公众开放,但现在已成为一款商业产品(Scott,2020 年)。在 beta 测试期间,人们将 GPT-3 用于许多应用,如编写网页布局的代码、编写实际的计算机程序、参加虚拟求职面试、将法律文本重写为通俗语言、编写类似法律语言的新文本,当然,还包括创作小说和诗歌等创意类写作(Huston,2020 年)。

所有这些强大功能是一个复杂的组合。微调这样一个系统需要巨大的资源,而且随着系统规模的增大,微调变得越来越困难,因为这需要找到原始数据中没有的特定任务数据。

如果更大就是更好,那么更大是不是还会更好呢?GPT-3 背后的研究人员估计,我们可以用一个拥有 1 万亿个参数并在 1 万亿个标记上训练的模型,从任何文本中提取我们需要知道的所有信息(至少从 NLP 任务的角度来看)(Kaplan 等,2020 年)。这些数字是粗略预测,可能偏差较大,但有趣的是,或许会有一个时刻,解码器层堆叠(以及一些支持机制)可以从一段文本中提取出我们所需的几乎所有信息。我们很可能很快就能知道答案,因为其他拥有巨大资源的大公司肯定会推出他们自己的庞大 NLP 系统,基于他们自己的庞大数据库进行训练。训练这些庞大系统是只有大公司和富有企业才能参与的游戏。

轻松一点,我们可以在线玩一个由 GPT-3 实现的互动文字冒险游戏(Walton,2020 年)。该系统经过多种类型的训练,从奇幻、赛博朋克到间谍故事等。也许玩这个系统最有趣的方式是将 AI 当作即兴表演的伙伴,赞同并扩展系统抛给我们的任何内容。让 AI 设定节奏,随它走。

生成的文本通常在短时间内表现良好,但当我们仔细查看时,它的表现如何呢?最*的一项研究要求多种语言生成器,包括 GPT-3,执行 57 个任务,任务的主题涵盖了从法律和历史等人文学科到经济学和心理学等社会科学,再到物理学和数学等 STEM 学科(Hendrycks et al. 2020)。大多数输出从未接*人类表现。这些系统在道德和法律等重要社会问题上表现尤为糟糕。

这不应该让人感到惊讶。这些系统只是根据单词彼此搭配的概率生成单词。从实际和根本的意义上讲,它们根本不知道自己在说什么。

尽管它们非常强大,但像我们在这里看到的文本生成器并没有常识。更糟糕的是,它们盲目重复了从性别、种族、社会、政治、年龄和其他偏见中继承下来的刻板印象和偏见。文本生成器对准确性、公*、善良或诚实一无所知。它们不知道自己是在陈述事实,还是在编造东西。它们只是生成遵循训练数据统计规律的单词,并延续其中的每一个偏见和局限。

数据中毒

我们在第十七章看到,敌对攻击可以欺骗卷积神经网络生成错误的结果。自然语言处理算法也容易受到有意攻击,这种攻击被称为数据中毒

数据中毒的背后思想是通过某种方式操纵 NLP 系统的训练数据,使得系统产生一种期望的、不准确的结果,可能是持续的,或者仅仅是在出现某个触发词或短语时。例如,可以在训练数据中插入句子或短语,暗示草莓是由水泥做成的。如果这些新加入的内容未被发现,那么如果系统稍后被用来为超市或建筑承包商生成库存订单,可能会发现他们的库存数据一直并且神秘地错误。

这尤其令人担忧,因为正如我们所见,NLP 系统通常是在包含数百万或数十亿个单词的大型数据库上进行训练的,因此没有人会仔细审查数据库中可能误导的短语。即使一个或更多的人仔细阅读整个训练集,这些中毒的文本也可以被设计成不明确提及其目标,使得它们几乎无法被检测到,且其效果不可预测。

回到我们之前的例子,这样的短语可以让系统相信草莓是由水泥做成的,而根本不涉及水果或建筑材料。这被称为隐蔽数据中毒,它可能非常难以检测和防止(Wallace et al. 2020)。

另一种攻击方式是通过看似无害的方式改变训练数据。假设我们在使用一个将新闻标题分类到不同类别的系统。任何给定的标题都可以被微妙地重写,以便显而易见的意思不变,但故事会被错误地分类。例如,原本的标题“土耳其已被列入欧盟成员国名单”将被正确分类为“世界”类。但如果编辑将其改写成主动语态——“欧盟将土耳其列入全面成员国候选名单”——这将被错误分类为“商业”(Xu, Ramirez, 和 Veeramachaneni 2020)。

数据污染特别恶劣,原因有几个。首先,它可以由与构建或训练 NLP 模型的组织没有任何联系的人完成。由于大量的训练数据通常来自公共来源,比如互联网,污染者只需要在一个公共博客或其他可能被抓取并使用的位置发布有操控性的短语。其次,数据污染可以在任何特定系统使用之前,甚至在系统构思之前,就提前完成。

目前无法得知有多少训练数据已经被污染,并且仅仅在等待激活,就像《孟乔理候选人》(Frankenheimer 1962)中的沉睡特工一样。最后,与对卷积神经网络(CNN)的对抗攻击不同,数据污染是从内部削弱 NLP 系统,使其影响成为训练模型的固有部分。

当一个受损的系统被用来做出重要决策时,比如评估学校录取作文、解读医疗笔记、监控社交媒体中的欺诈与操控,或是搜索法律记录,那么数据污染就可能导致错误,改变人们的生活轨迹。在任何 NLP 系统被用于这类敏感应用之前,除了检查其是否存在偏见和历史偏见外,我们还必须对其进行数据污染分析,并且只有当它明确无偏或未受污染时,才能认证为安全。不幸的是,目前还没有有效的强健检测或认证这些问题的方法。

摘要

本章开始时我们讨论了词嵌入,它为每个词分配一个高维空间中的向量,表示其使用方式。我们看到,ELMo 能够基于内容捕捉多个含义。

我们讨论了注意力机制,它使我们能够同时在输入中找到看似相关的词,并构建描述这些词的向量版本的组合。

然后我们看了 Transformer 模型,它完全舍弃了递归单元,取而代之的是多个注意力网络。这个变化使得我们能够并行训练,具有巨大的实际价值。

最后,我们看到了如何使用多个 Transformer 编码块来构建 BERT,一个高质量的编码系统,和如何使用多个解码块来构建 GPT-2,一个高质量的文本生成器。

在下一章,我们将把注意力转向强化学习,它通过评估神经网络的猜测来训练它们,而不是期望它们预测一个单一的正确答案。

第二十一章:强化学习

训练机器学习系统有很多方法。当我们有一组带标签的样本时,可以使用监督学习来教计算机预测每个样本的正确标签。当我们无法提供任何反馈时,可以使用无监督学习,让计算机尽力而为。但有时候我们介于这两者之间。也许我们对希望系统学习的内容有一些了解,但并不像有标签的样本那样明确。也许我们所知道的只是如何区分一个更好的解决方案和一个更差的解决方案。

例如,我们可能试图教一种新型的人形机器人如何用两条腿走路。我们并不确切知道它应该如何保持*衡和移动,但我们知道我们希望它站立起来而不是摔倒。如果机器人尝试趴在地上滑行,或者单腿跳跃,我们可以告诉它那不是正确的做法。如果它两条腿都放在地上,然后用它们前进,我们可以告诉它这条路是对的,并鼓励它继续探索这类行为。奖励我们认定为进步的策略被称为强化学习RL)(Sutton 和 Baro 2018)。这个术语描述了一种通用的学习方法,而非具体的算法。

本章我们将介绍这一广阔领域的一些基本概念。核心思想是,强化学习将模拟的世界分解为一个采取行动的实体和响应这些行动的其他世界。为了更具体地说明,我们将使用强化学习来学习如何玩一个简单的单人游戏,然后深入探讨这种技术的细节。我们将从一个有缺陷的简单算法开始,并将其升级为能够高效学习的更好版本。

基本概念

假设你正在和朋友下跳棋,轮到你了。此时,你可以移动你的一枚棋子,朋友需要等待。在强化学习中,我们说你是行动者智能体,因为你有选择行动的权利。宇宙中的其他一切——棋盘、棋子、规则,甚至你的朋友——都被归为环境。这些角色并非固定不变。当轮到你朋友走棋时,他们就是智能体,而其他的一切——棋盘、棋子、规则,甚至你——现在都成了环境的一部分。

当演员或代理选择一个行动时,他们会改变环境。在我们的跳棋游戏中,你是代理,所以你会移动你的棋子,也许还会移除对手的一些棋子。结果是,世界发生了变化。在强化学习中,代理的每一个行动之后,都会得到一条反馈,也叫奖励,它告诉他们“行动”有多“好”,这个评估可以依据我们喜欢的任何标准。反馈或奖励通常只是一个单一的数字。由于我们在创建这个世界,反馈可以代表任何我们想要的意义。例如,在一局跳棋游戏中,一步能赢得比赛的走法会被分配一个巨大的正向奖励,而一部导致失败的走法会被分配一个巨大的负向奖励。在两者之间,越是能够带来胜利的走法,奖励越大。

通过反复试验,代理能够发现不同情境下哪些行动比其他行动更好,从而随着经验的积累,逐渐做出更好的选择。这种方法对于我们并不总是知道最佳行动的情况特别有效。例如,考虑一下在一座高楼繁忙的办公大楼中安排电梯的问题。即便只是弄清楚空车应该去哪里也是个难题。电梯车厢应该总是返回到一楼吗?是否应该让一些车停在楼顶?还是应该停在楼层之间,分布均匀?也许这些策略应该随时间变化,在清晨和午饭后,电梯车应停在一楼,等待从街上来的人员,但在下午晚些时候,车应停得更高一点,准备帮助人们下楼,回家。如何安排一座特定大楼的电梯并没有明显的答案。一切都取决于大楼的*均交通模式(而这个模式本身可能依赖于时间、季节或天气)。

这是强化学习的理想问题。电梯的控制系统可以尝试一种指引空车的策略,然后利用来自环境的反馈(例如等待电梯的人数、他们的*均等待时间、电梯车厢的密度等)来帮助调整该策略,以便在我们衡量的指标上尽可能表现得更好。

强化学习可以帮助我们解决那些我们不知道最佳结果的问题。我们可能没有像游戏的胜利条件那样明确的衡量标准,而只有更好或更差的结果。这是一个关键点:我们可能无法找到任何客观一致的“正确”或“最佳”答案。相反,我们是在根据我们所拥有的信息,并通过我们所衡量的任何标准,尝试找到我们能得到的最佳答案。在某些情况下,我们甚至可能不知道自己在过程中做得如何。例如,在一场复杂的游戏中,我们可能无法判断自己是领先还是落后,直到在我们赢或输的惊讶时刻。对于这些情况,我们只能根据最终任务完成时的结果来评估我们的行为。

强化学习提供了一种很好的方式来建模不确定性。在简单的基于规则的游戏中,我们原则上可以评估任何棋盘并选择最佳的移动,假设另一个玩家总是做出相同的动作。但在现实世界中,其他玩家会做出让我们感到惊讶的举动。而且,当我们处理现实世界时,比如有些天比其他天更多人需要电梯,我们需要有能够在面对惊讶时仍然表现良好的策略。强化学习可能是应对这类情况的好选择。

让我们通过一个具体的例子更详细地了解强化学习。

学习一款新游戏

让我们通过使用强化学习来教程序如何玩井字游戏(也叫圈叉,或X 与 O)的步骤来了解。游戏中,玩家交替在一个三乘三的网格中放置 X 或 O,第一个在一行(任何方向)中放置三个相同符号的人获胜。在图 21-1 的示例中,我们玩 O,我们的计算机学习者玩 X。

F21001

图 21-1:一局井字游戏,从左到右阅读。X 先走。

在这个场景中,我们正在训练的程序是代理。它正在与环境进行对抗,环境可能由另一个了解游戏及如何玩的程序模拟。代理不知道游戏规则、如何获胜或失败,甚至不知道如何进行移动。但我们的代理并不会完全一无所知。在每次代理轮到行动时,环境会给它两个重要的信息:当前的棋盘和可用的动作列表。这个过程在图 21-2 的步骤 1 和步骤 2 中有所展示。

F21002

图 21-2:井字游戏中玩家与环境之间的基本信息交换循环

在第 3 步中,代理基于任何喜欢的方法选择一个动作。例如,它可以随机选择,或者查询在线资源,或者使用它对之前游戏的记忆。在强化学习中,挑战的一部分是设计一个能够充分利用我们为其提供的资源的代理。

一旦代理选择了一个动作,它将在第 4 步中将其传达给环境。然后,环境按照第 5 步进行,首先通过在选择的单元格中放置一个 X 来实际执行该动作。接着,环境检查代理是否赢得了比赛。如果是,它会给出一个较大的奖励。否则,它会根据该动作对代理的好处来计算奖励。现在,环境模拟另一个玩家,进行自己的回合。如果环境赢了,它会将奖励设置为一个非常低的数值。如果游戏因环境或代理的动作结束,我们称该奖励为最终奖励终极奖励。在第 6 步中,环境将奖励(有时称为奖励信号)发送给代理,这样代理就可以学习它选择的动作有多好。如果没有人获胜,我们将返回到循环的开始,代理将再次进行一次回合。

在某些情况下,我们并不直接给代理提供可用的动作列表。这可能是因为可供选择的动作太多,或者它们有太多的变化。此时我们可能会给代理一些指导,或者甚至不提供任何指导。

按照这个程序,代理在开始学习时可能会做出无用或糟糕的动作,但通过下面的技术,我们希望代理能够逐渐学会找到好的动作。为了讨论的方便,我们将简化问题,假设代理提供了一个可供选择的动作列表。

强化学习的结构

让我们将井字游戏的例子重新组织和概括成一个更抽象的描述。这将使我们能够涵盖超出轮流游戏的情形。我们将事情组织成三个步骤,接下来我们将逐一讨论这些步骤。

在我们开始之前,先介绍一些术语。在训练开始时,我们将环境置于初始状态。在棋盘游戏中,这相当于为新游戏的开始进行布置。在我们的电梯示例中,这可能是将所有电梯轿厢放置在底楼。一个完整的训练周期(例如从开始到结束的游戏)称为一个回合。我们通常期望在许多回合中训练代理。

步骤 1:代理选择一个动作

我们从图 21-3 开始。

F21003

图 21-3:环境向代理提供当前的世界状态以及一系列可选择的动作。代理选择一个动作并将其传达给环境。

记住,环境是所有代理动作发生的世界。环境通过一组数字来完全描述,这些数字统称为环境状态状态变量,或简单地称为状态。这可能是一个简短的列表,也可能是一个非常长的列表,具体取决于环境的复杂性。在棋盘游戏的情况下,状态通常由所有棋子在棋盘上的位置以及每个玩家持有的任何游戏资产(如游戏币、能量增强、隐藏卡牌等)组成。

然后,智能体从可用的动作中选择一个。我们常常将智能体拟人化,谈论它如何“希望”达成某个结果,比如赢得一场游戏或调度电梯使得没人需要等太长时间。在基本的强化学习中,智能体处于空闲状态,直到环境告诉它该采取行动。然后,智能体使用一种叫做策略的算法,以及智能体可能访问的任何私有信息(包括它从先前的回合中学到的知识)来选择一个动作。

我们通常将智能体的私有信息视为一个数据库。它可能包含可能策略的描述,或者记录在先前状态中采取的动作以及得到的奖励的某种历史记录。与此相对,策略是一个通常由一组参数控制的算法。这些参数通常会随着智能体的游戏进行而变化,在此过程中智能体会搜索更好的动作选择策略。

我们通常不认为智能体执行其动作。相反,选择的动作会被报告给环境,由环境负责执行该动作。这是因为环境负责状态的管理。回到我们的电梯示例,如果智能体指示一辆电梯从 13 楼移动到 8 楼,智能体并不会更新状态来将电梯置于 8 楼。途中可能会出问题,比如机械故障导致电梯卡住。智能体只是告诉环境它想做什么,环境会尽力去实现这一目标,同时保持状态,使其始终反映当前的情况。在我们的井字游戏中,状态包含了棋盘上当前 X 和 O 标记的分布情况。

第 2 步:环境响应

图 21-4 显示了我们强化学习概述的第 2 步。

F21004

图 21-4:我们强化学习过程的第 2 步。此步骤从计算新状态(最右侧)开始。

在这一过程中,环境处理智能体的动作,生成一个新状态,并处理这一变化所带来的信息。环境将其新状态保存在状态变量中,以便在智能体下一次选择动作时,状态能够反映新的环境。环境还使用其新状态来确定下一步可以供智能体选择的动作。前一个状态和可用的动作完全被它们的新版本所替代。最后,环境提供一个奖励信号,告诉智能体它上次选择的动作有多“好”。“好”的含义完全取决于整个系统在做什么。在游戏中,好的动作是那些能带来更强位置或甚至胜利的动作。在电梯调度系统中,好的动作可能是那些能最小化等待时间的动作。

第 3 步:智能体更新自身

图 21-5 展示了强化学习概述中的步骤 3。

F21005

图 21-5:强化学习过程中步骤 3,智能体根据奖励更新自身

在这一步骤中,智能体利用奖励值更新其私有信息和策略参数,这样下次遇到相同情境时,能够建立在从此次选择中学到的东西上。步骤 3 完成后,智能体可能会安静地等待,直到环境告诉它再次行动的时机。或者它可以立即开始为下一步行动进行规划。这对于某些实时系统特别有用,因为奖励通常在新状态的完整计算之前就已经产生。

智能体通常不会只是将每一个奖励存入其私有信息中,而是会以某种方式处理这些奖励,从中提取尽可能多的价值。这甚至可能涉及改变其他行动的值。例如,如果我们刚刚赢得了一场游戏并获得了终极奖励,我们可能希望将一些奖励分配到导致我们胜利的每一步行动上。

强化学习的目标是在这种情境下帮助智能体从反馈中学习,选择带来最佳奖励的行动。无论是赢得游戏、调度电梯、设计疫苗还是操作机器人,我们都希望创建一个能够从经验中学习的智能体,使其能够尽可能有效地操控环境,带来积极的奖励。

回到大局观

现在我们已经了解了整体方法,让我们来看一些大局观的问题。当智能体更新其策略时,它可能能够访问整个状态的所有参数,或者只访问其中的一部分。如果智能体能够看到整个状态,我们称它为具有完全可观察性,否则它只有有限可观察性(或部分可观察性)。我们可能给智能体仅限的可观察性,其中一个原因是某些参数可能计算成本非常高,而我们不确定它们是否相关。因此,我们限制智能体访问这些参数,以观察这样做是否会影响智能体的表现。如果不包含这些参数不会造成任何伤害,我们可以从此以后完全忽略它们,节省计算精力。或者我们可以仅在看似必要时计算这些参数并使其可见。部分可观察性的另一个例子是我们正在教系统玩一款扑克牌游戏。我们不会向正在学习的系统揭示对手手中有哪些牌。

一旦我们开始考虑如何利用反馈来训练代理,就会发现自己面临两个有趣的问题。首先,当我们获得最终奖励时(例如赢得或输掉一场游戏),我们希望将这一奖励分配给我们沿途做出的每一个决策。假设我们正在玩一场游戏,并做出了一个获胜的决策。这个最终的决策会得到很好的反馈,但中间的步骤同样至关重要,我们应该记住它们也促成了胜利。这样,如果我们再次看到这些中间的棋盘,我们更有可能选择那个能够带来胜利的决策。以这种方式分配最终奖励的问题叫做信用分配问题。同样地,如果我们输了,我们也希望让那些导致失败的步骤承担一些责任,这样我们就不太可能再次选择它们。

其次,假设在某一时刻,代理看到了一种它曾经见过的情境(比如一个游戏棋盘),并且在之前尝试过一个获得相对较好分数的动作。但它还没有尝试过其他可能的动作。它应该选择那个已知回报的安全动作,还是冒险尝试一个可能会失败或者带来更大成功的新动作?我们需要在每次选择动作时做出决定,是要冒险探索未知的行动,看看它能带我们去哪里,还是选择我们已经尝试过的安全行动,利用我们已经学到的东西。这就是探索还是利用困境。设计强化学习系统的一部分任务就是思考我们如何*衡已知与未知、保证与风险之间的问题。

理解奖励

为了使代理表现得尽可能好,它应该受到一个策略的引导,使代理选择那些能带来最高奖励的行为。理解奖励的性质,并学会明智地使用奖励,是非常值得投入的时间。让我们深入探讨一下。

我们可以区分两类奖励:即时奖励长期奖励。即时奖励是我们迄今为止关注的重点。环境在执行一个动作后,立即将这些奖励反馈给代理,就像我们在图 21-2 中看到的那样。长期奖励则更为广泛,指的是我们的整体目标,比如赢得一场游戏。

我们希望在考虑每次游戏或回合中的所有其他奖励的背景下,理解每个即时奖励。奖励的解释方式有很多种,不同的方式会让它们对我们产生不同的意义。让我们来看一种流行的方法,称为折扣未来奖励(DFR)。这是一种解决信用分配问题的方法,或者说,确保所有带领我们走向成功的行为都能共享最终的胜利。

为了理解 DFR(动态未来奖励)是如何工作的,我们需要稍微拆解一下奖励过程。假设我们是一个在玩游戏的代理。游戏结束后,我们可以将我们在这局游戏中获得的所有奖励按顺序列出,并记录下为这些奖励所做的操作。将所有奖励加起来,我们就得到了这局游戏的总奖励,如图 21-6 所示。

F21006

图 21-6:与任何回合相关的总奖励是从回合的第一步到最后一步所获得的所有奖励的总和。

我们可以将这个列表中的任意一部分加起来,例如前五个条目,或最后八个条目。让我们从第五个操作开始,将从此处到游戏结束的所有奖励加起来,如图 21-7 所示。

图 21-7 展示了与游戏第五个操作相关的总未来奖励(TFR)。它是第五个操作及其之后所有操作所带来的总奖励的一部分。

游戏的第一次操作是特别的,因为它的总未来奖励与游戏的总奖励相同。由于我们至今所获得的奖励总是零或正数,每个后续操作的 TFR 都等于或小于前一个操作的 TFR。

F21007

图 21-7:任何操作的总未来奖励是该操作奖励与该操作之后所有其他操作奖励的总和,直到本局结束。

总未来奖励是一个很好的描述,说明了某一特定操作对我们刚完成的游戏的贡献,但它并不是预测该操作在未来游戏中可能有多大作用的最佳方式,即使它们从完全相同的操作序列开始。这是因为现实环境是不可预测的。如果我们在玩一款多人游戏,我们不能确定其他玩家(或玩家们)在下一局中是否会像上一局那样行动。如果他们做出了不同的操作,那么这可能会改变游戏的轨迹,从而也会改变我们所获得的奖励,甚至可能改变我们是赢还是输。即使我们在玩单人游戏,我们也可能是在用一副洗过的扑克牌玩,或者是在玩带有伪随机数的电脑游戏,因此即使我们按以前相同的方式玩,我们也不能确定未来会发生什么。

立即奖励更加可靠。我们可以想象两种类型的立即奖励。第一种奖励告诉我们刚才所做操作的质量,环境做出回应之前。例如,在我们的井字棋游戏中,如果代理在某个格子里放置了一个 X,他们可以在环境回应之前,得到一个即时奖励,这个奖励描述了玩家如何为未来的胜利做准备。这种奖励是完全可预测的。如果我们再次面对相同的环境并做出相同的操作,我们会得到相同的奖励。

第二类奖励告诉我们在环境响应之后我们刚刚做出的动作的质量,因此奖励可能会受到环境动作的影响。这种类型的奖励,我们可以称之为结果奖励,它不像即时奖励那样可预测,因为每次我们做出动作时,环境的响应可能会不同。

让我们来比较这两者。假设我们正在训练一个由智能体驱动的机器人如何使用遥控器打开设备。它可能会拿起遥控器,按下电源按钮,然后将遥控器放回,重复做同样的事情 100 次,获得高奖励。但在这个过程中,电池一直在消耗,所以第 101 次机器人重复这个过程时,设备无法打开。如果智能体得到的是按下按钮时的即时奖励,也就是在环境响应之前计算并返回的奖励,智能体将获得大量奖励,因为它做对了事情。另一方面,结果奖励是在环境响应之后计算并返回的,将会很低,甚至为 0,因为设备未能打开。

从现在开始,当我们提到即时奖励时,我们将使用结果奖励。

当某件事连续 100 次有效,但第 101 次失败时,那就是一个惊讶

处理惊讶的情况非常重要,因为大多数环境都是不可预测的。一般来说,我们采取的每个行动都是为了带来一个结果。因此,即使我们不能确定会发生什么,等待看到这个结果也是理解我们行动是否是一个好选择的重要部分。

我们说真实的环境因为其中的不确定因素是随机的。相比之下,一个完全可预测的环境(比如完全基于逻辑的游戏)是确定性的。不确定性(或者随机性)的程度可以有所不同。如果不确定性低(即环境大致上是确定性的),那么我们可能会相当有信心地认为我们刚刚获得的奖励很可能在未来的游戏中重复,或者几乎如此。在不确定性非常高的情况下(也就是说,在一个主要是随机的环境中),我们必须假设,如果我们重复相同的动作,任何关于未来奖励的预测都应被视为仅仅是估算。

我们通过折扣因子来量化对环境随机性或不确定性的估计。这个数值介于 0 和 1 之间,通常用小写的希腊字母γ(gamma)表示。我们选择的γ值代表我们对环境可重复性的信心。如果我们认为环境接*于确定性的,并且每次给定动作都能获得相似的奖励,我们会将γ设置为接* 1 的值。如果我们认为环境是混乱且不可预测的,我们会将γ设置为接* 0 的值。

我们需要以一种有原则的方式将意外的惊喜考虑到我们已经学习到的奖励中。实现这一点的一种方法是创建一个修改版的总未来奖励,考虑到我们对游戏是否会以相同方式继续进行的信心。我们通常会将高的修改版 TFR 值附加在我们有信心的动作上,而将较低的值附加在其他动作上。

我们可以使用折扣因子来创建一个称为折扣未来奖励(DFR)的总未来奖励版本。与为 TFR 加总所有动作之后的奖励不同,我们从即时奖励开始,然后通过将后续奖励分别乘以γ,将其逐步折扣。未来一步的奖励乘以γ一次,接下来的奖励乘以γ两次,依此类推。这考虑了我们对未来奖励的可靠性逐渐降低的事实。此技术在图 21-8 中有图示说明。

请注意,在图 21-8 中,每个后续的值都比前一个值多乘以一次γ。这些增加的乘法可能会对每个奖励对总和的贡献量产生显著影响。

F21008

图 21-8:通过将即时奖励、下一个奖励乘以γ、下一个奖励再乘以γ两次,以此类推,来得到 DFR。

让我们看看这个实际应用。我们可以考虑在一局游戏中的开局动作所获得的奖励和折扣后的未来奖励,使用不同的γ值。图 21-9 展示了一个假想游戏中 10 步的即时奖励。

F21009

图 21-9:一个有 10 步的游戏的即时奖励。游戏结束时没有明确的赢家。

对这些奖励应用不同的未来折扣,参照图 21-8,我们得到了图 21-10 中的曲线。请注意,随着折扣因子γ的减小,奖励很快就会降到 0。这意味着我们对未来的预测不再那么确定。

F21010

图 21-10:在不同的γ值下,折扣后的图 21-9 奖励。

如果我们将图 21-10 中每条曲线的值加起来,我们就得到了在不同γ值下,第一步动作的折扣未来奖励。这些 DFR 在图 21-11 中显示了出来。请注意,随着我们认为未来变得越来越不可预测(也就是γ变小),DFR 也变得更小,因为我们对获得这些未来奖励的信心降低了。

γ接* 1 时,未来的奖励几乎没有被削减,所以折扣未来奖励(DFR)接*总未来奖励(TFR)。换句话说,我们是在说,做出这个动作所获得的总奖励很可能与我们如果再次做这个动作所获得的总奖励相似。

但是当γ的值接* 0 时,未来的奖励被大幅缩小到几乎不重要的地步,最终我们只剩下即时奖励。换句话说,我们表示对游戏是否会继续像这次一样进行信心很小,因此我们能确定的唯一奖励就是即时奖励。

在许多强化学习场景中,我们通常会选择一个大约为 0.8 或 0.9 的γ值来开始,然后随着对系统的随机性和代理学习效果的了解,逐步调整该值。

F21011

图 21-11:来自图 21-10 的 DFR,适用于不同的γ

到目前为止,我们讨论的都是一些原则和思路,但我们仍然没有一个具体的算法来指导代理在选择行动时的决策。为了制定这样的算法,我们从描述环境开始。

Flippers

在接下来的部分,我们将探讨学习游戏的实际算法。为了让我们将重点放在算法上而非游戏本身,让我们将井字游戏简化为一个新的单人游戏,我们称之为Flippers

我们在一个三乘三的方格网格上玩 Flippers 游戏。每个格子中都有一个围绕杠杆转动的小瓦片,正如图 21-12 所示。

每个瓦片的一面是空白的,而另一面则有一个点。在每一步操作中,玩家推动一个瓦片使其翻转。如果该面显示的是点,点会消失,反之亦然。

游戏开始时,瓦片处于随机状态。胜利的条件是恰好有三个蓝点显示,并且这些点排列成竖直列或水*行,其他所有瓦片显示为空白。这可能不是有史以来最具智力挑战的游戏,但它将帮助我们澄清算法的逻辑。

F21012

图 21-12:Flippers 游戏的棋盘。每个瓦片一面是空白的,另一面有一个点。游戏中的一步操作包括翻转(或旋转)一个瓦片。

从随机棋盘开始,我们希望在最少的翻转次数内获得胜利。由于对角线不算作胜利条件,因此有六种不同的棋盘配置满足我们胜利的条件:三种水*行和三种竖直列。

图 21-13 展示了一个示例游戏,并附上了表示操作的符号。我们从左到右读取游戏。每个棋盘(除了最后一个)都展示了该步的起始配置,并且有一个格子被用红色高亮标出。这个格子就是将要被翻转的格子。

F21013

图 21-13:玩 Flippers 游戏。(a)初始棋盘,显示三个点。红色方块表示我们这一步要翻转的瓦片。(b)结果棋盘与(a)相似,但右上角的瓦片已经从空白变为点。我们这一步的操作是翻转中央的瓦片。(c)到(e)展示了游戏进行中的后续步骤。棋盘(e)是一个获胜的棋盘。

现在我们有了一个可以玩的游戏,我们可以看看如何利用强化学习来赢得它。

L-学习

让我们构建一个完整的 Flippers 学习系统。虽然我们将在下一节中大大改进这个算法,但这个初步版本的表现会差到我们称它为L-学习,其中 L 代表“糟糕的”。请注意,L-学习是我们发明的一个垫脚石,目的是帮助我们达到更好的结果,而不是文献中出现的实用算法。毕竟,它很糟糕。

基础知识

为了简化问题,我们将使用一个非常简单的奖励系统。在 Flippers 中,我们每走一步都会得到一个即时奖励 0,除了最后一步赢得游戏的那一步。因为 Flippers 是一个非常简单的游戏,每一局都能获胜。为了证明这一点,我们可以从任何一个起始棋盘开始,将所有显示点的瓷砖翻转,这样就没有点显示出来。然后,我们可以翻转任意一行或一列中的三块瓷砖,就算获胜了。因此,任何游戏最多不应超过 12 步。

我们的目标不仅仅是获胜,而是在最少的步数内获胜。最终的胜利动作会获得一个奖励,这个奖励取决于游戏的时长。如果只需要一步就能获胜,奖励是 1。如果需要更多的步骤,这个最终奖励会随着所用步数的增加而迅速下降。这个曲线的具体公式并不重要,重要的是它下降得很快,并且一直在变小。图 21-14 展示了最终奖励与游戏时长的曲线图。

F21014

图 21-14:Flippers 中胜利的奖励从一步获胜的 1 开始,但随着获胜所需步数的增加,奖励迅速下降。

我们系统的核心是一个数字网格,我们称之为L 表。L 表的每一行代表棋盘的一个状态。每一列代表我们在该棋盘状态下可以采取的九个动作之一。表中每个单元格的内容是一个数字,我们称之为L 值。图 21-15 展示了这一点的示意图。

F21015

图 21-15:L 表包含 512 种可能的 Flipper 棋盘上空白和点的模式,每一行代表一种模式,每一列代表 9 种可能的动作之一。

这个表很大,但并不算太大。棋盘只有 512 种可能的配置,因此我们需要 512 行。每行有 9 列,总共有 512 × 9 = 4,608 个单元格。我们将使用 L 表来帮助我们选择在每种棋盘状态下最有奖励的行动。为了实现这一点,我们将在表中的每个单元格里填写一个分数:一个基于经验的数字,告诉我们对应的动作有多好。

我们将值保存在 L 表中,以便在学习如何评估动作时使用,并在游戏进行时通过读取这些值来指导我们的动作选择。在开始为 L 表分配值之前,我们首先将每个单元格初始化为 0。随着游戏的进行,我们会记录所有我们所采取的动作。当游戏结束时,我们将回顾整个游戏中我们所做的所有动作,并为每个动作确定一个值。然后,我们将这个值与该动作所在单元格中已有的值结合,产生该动作的新值(我们稍后会详细介绍这一过程)。我们将旧值和新值结合的方式称为更新规则

在游戏进行过程中(无论是在学习阶段还是后来的真实游戏中),我们通过查看该步开始时棋盘的对应行来选择一个动作。我们使用策略来告诉我们在该行中选择哪个动作。

让我们把这些步骤具体化。首先,在每局游戏(或情节)结束后,我们需要确定分配给每个我们所采取的动作的分数。我们使用之前讨论过的总未来奖励,或者 TFR。回想一下,TFR 来源于将所有动作及其奖励排列起来,然后将该动作之后的所有奖励加总起来。

在游戏进行过程中,每一步都获得一个即时的奖励 0,但最终一步会根据游戏的长度获得正向奖励:游戏越短,奖励越大。这意味着我们沿途所采取的每个动作的 TFR 与最终的奖励相同。

第二,我们选择一个简单的更新规则,即在每局游戏结束后,我们为每个单元格计算的 TFR 会直接替换该单元格之前的内容。换句话说,这局游戏中每个动作的 TFR 将成为该单元格的新值,该单元格位于我们采取该动作时所看的棋盘行和我们选择的动作所在的列的交点。

这个简单的更新规则有助于让我们熟悉 L 学习系统的工作方式。但由于它没有将我们的新经验与之前学到的内容结合起来,因此这个规则是该算法表现不佳的一个重要原因。

现在我们在 L 表中有了值,我们需要一个策略来告诉我们在面对某个棋盘配置时应选择哪个动作。假设我们选择对应于行中最大 L 值的动作。如果多个单元格有相同的最大值,我们将随机选择一个。图 21-16 以图形化方式展示了这一过程。

F21016

图 21-16:策略步骤包括选择一个动作以应对棋盘上的局面。

在图 21-16 中,我们看到 L 表格的一行,列出了我们可以对最左边的棋盘状态做出的可能动作。每列都保存了在该棋盘状态下采取该动作时最*计算出的 TFR(即时奖励)。请注意,有两列的值为 0,因为我们还没有尝试这些动作。在 L 学习中,我们选择最大的值。在这里,这意味着我们翻转中心右侧的棋盘格。

L 学习算法

现在我们已经有了进行 L 学习所需的所有步骤。让我们将它们组合成一个功能性但不太完善的强化学习算法。我们从一个包含 512×9 表格并填充零的私有记忆开始,这个表格代表 L 表格。

在第一局游戏的第一步中,代理看到一个棋盘。它在 L 表格中找到该棋盘对应的行,并扫描该行中的九个条目,选择得分最高的动作。因为这些值都是零,所以它会随机选择一个。这种情况会持续一段时间,因为代理会看到很多它以前从未见过的棋盘。棋盘翻转后,代理会考虑新的棋盘,选择新的动作,依此类推,直到它最终赢得游戏(即使所有动作完全随机选择,计算机最终也会产生一个获胜的棋盘)。

当游戏结束时,代理希望将最终奖励分配给所有帮助它赢得胜利的动作。为此,在游戏进行过程中,系统需要保持一个按顺序排列的列表,记录每一步它所执行的动作。

我们稍后会发现,如果每个条目不仅保存选择的动作,还保存更多信息,这个列表会更有用。预见到这一需求,我们假设在每一步之后,代理会保留一个小的组合,包含起始棋盘、代理所采取的动作、收到的即时奖励以及该动作所导致的结果棋盘。图 21-17 展示了这一过程。代理将这些组合保存在一个列表中,游戏开始时该列表为空,每一步后列表都会增加一个组合。

F21017

图 21-17:每次我们采取一个动作时,我们都会将一个包含四个值的组合追加到不断增长的组合列表的末尾:起始状态、我们选择的动作、我们收到的奖励以及环境在采取该动作后返回给我们的最终状态。

如图 21-17 所示,我们可以将这个组合保存为一个包含四个数字的列表:起始状态的行号、动作的列号、奖励的值和结果状态的行号。

为了进行第一次移动,我们查看 L 表格中对应起始棋盘的行,并查看该行中的九个数字。我们的策略通常是选择该行中最大的值,但有时也会为了探索而选择其他值。如果所有值都相同(就像我们刚开始时的情况),我们会随机选择一个。

环境为我们翻转那个方块,可能让一个点出现或消失。然后,环境给我们回馈一个奖励以及新的棋盘。我们会把这个动作打包成一个小的集合:我们开始时的棋盘、我们刚刚采取的动作、我们获得的奖励以及因此产生的新状态。我们把这个集合加入到我们动作列表的末尾。

因为我们是单人游戏,环境不会主动采取任何动作。一旦它给我们反馈了,它就会告诉我们采取新动作。因此,我们再次查看当前棋盘,在 L-table 中找到它对应的行,选择该行中最大的单元格,并将其报告为我们的动作。我们会得到一个奖励和一个新状态,并将描述这个动作的四个项目作为一个新集合添加到我们的列表中。

这一过程会一直持续到游戏结束。在最后的反馈中,我们会得到唯一的非零奖励。它是基于我们在游戏中所进行的移动次数的最终奖励,正如我们在图 21-14 中看到的那样,这个奖励会迅速递减。通过这个最终的非零奖励,我们知道游戏结束了,接下来是时候从我们的经验中学习了。

我们从查看我们的动作列表中的集合开始。从概念上讲,我们排列出我们的棋盘状态和相应的动作,以及它们的奖励,如图 21-18 所示。我们逐一查看每个动作,并通过将所有后续奖励相加来找到它的 TFR。在图 21-18 中,计算并不特别有趣,因为除了最后一个奖励,所有即时奖励都是零。但值得注意的是,随着后续步骤的发展,我们将会遇到非零的即时奖励。

F21018

图 21-18:为每个动作找到 TFR。我们将每个动作的即时奖励(直接显示在其下方)与所有后续动作的即时奖励相加。在我们的游戏中,除了最后的奖励,所有即时奖励都是零,因此这些和都是相同的。

然后,我们使用简单的更新规则以及我们所做的动作列表,并将每个动作的 TFR 值放入 L-table 中对应该棋盘的单元格,如图 21-19 所示。

F21019

图 21-19:使用每个动作的最新 TFR 值来更新我们的 L-table。我们找到对应我们在执行每个动作时所看到棋盘的行,以及对应我们所采取动作的列。新的 TFR 值会替换掉之前单元格中的任何值。

如果我们想要学习更多,我们会回到流程的起始点并开始一个新游戏。当我们完成后,我们为每个我们执行的动作计算一个 TFR 值,并将其存储在相应的单元格中(覆盖之前的值)。请注意,我们在每次游戏后不会重置 L-table,因此,随着我们玩更多的回合,TFR 会逐渐填满整个表格。

当训练结束,开始实际游戏时,我们使用 L 表来选择动作。也就是说,在每次行动时,我们会呈现一个棋盘,我们查找该行,选择该行中 L 值最大的列,并选择对应的动作。

测试我们的算法

让我们看看我们的系统表现如何。我们先进行 3,000 局 Flippers 游戏,从头到尾进行,这样 L 表就可以得到充分填充。图 21-20 显示了在 3,000 局训练后从头到尾玩的 Flippers 游戏。这不是一个很理想的结果。其实有一个简单的两步解法,任何人都能轻松发现:翻转左中间的单元格,然后是左上角的单元格(或者反过来)。然而,我们的算法似乎在随机地游走,直到最终在六步之后偶然找到了解决方案。

F21020

图 21-20:使用 L 表算法经过 3,000 局训练后玩 Flippers 游戏。游戏从左到右阅读。

图 21-20 所示的排列将 L 表的行排列为列,以便更好地适应页面。每一列代表一个棋盘配置(或状态)。每行显示了九个可能的动作,红色高亮显示。粗黑边框表示代理从该列表中选择的动作,导致其右侧列的新棋盘。阴影单元格表示已执行的动作。如果该动作导致出现一个点,则该动作显示为实心红点。如果该动作导致点消失,则显示为红色轮廓点。每个棋盘下方的彩条显示了该棋盘的 L 值,值越大,条形越绿,表示通过折扣未来奖励计算出的 L 值越大。

右侧的棋盘比左侧的棋盘具有更大的 L 值。这是因为这些棋盘有时是游戏随机选择的起始棋盘。如果我们选择了一个好的动作并立即获胜,或者在少数几步内获胜,最终的奖励会很大。

回到这个游戏,从最左边的位置开始,算法的第一步是翻转左下角的单元格,引入一个新点。从这个结果开始,算法接着翻转最左列中间的方块,再次引入一个点。从这个位置,它又翻转了左上角的方块,移除了原本存在的点。游戏以这种方式继续,直到找到解决方案。

我们预计算法会随着更多的训练而改进,结果确实如此。图 21-21 显示了将训练时间延长到 6,000 局后,与图 21-20 相同的游戏。

F21021

图 21-21:在 6,000 局训练后与图 21-20 相同的游戏

这非常好。算法找到了简单的答案,并直接采取了这个行动。

看起来我们创建了一个很棒的学习和游戏算法。那么为什么我们要给它贴上“L”(差劲)的标签呢?它似乎运作得相当不错。

只要环境保持完全可预测,其实是没问题的。记得在本章之前,我们讨论过不可预测的环境。实际上,大多数环境都是不可预测的。基于逻辑的单人游戏,例如我们一直在研究的 Flippers 游戏,是少数完全确定性的活动之一。如果我们的目标是只玩单人游戏,并且这些游戏在完全确定的环境中进行,我们能够完美地执行每一个预定的动作,并且环境每次都作出相同的反应,那么这个算法其实并不糟糕。但是这种确定性的游戏和环境是很少的。例如,一旦有第二个玩家,便会产生不确定性,游戏变得不可预测。在任何环境不完全确定的情况下,L 学习算法都会陷入困境。

让我们来看看原因,然后我们将看到如何解决这个问题。

处理不可预测性

因为在电脑上玩 Flippers 时我们没有对手,所以我们拥有一个完全确定性的系统。每次我们做出一个动作,我们都能保证得到相同的结果。但在现实世界中,即便是单人活动也可能发生不可预测的事件。视频游戏会给我们带来随机的惊喜,割草机可能撞到一块石头并跳到一边,或者互联网连接可能会卡顿,导致我们错过在拍卖中做出获胜的出价。

由于处理不可预测性非常重要,让我们在 Flippers 中引入一些人工随机性,看看我们的 L 学习算法如何响应。我们对随机性的模型表现为一辆大卡车,它不时经过我们的游戏区域,震动我们的棋盘。有时,这足以导致一个或多个随机的棋盘格自发翻转。当然,我们仍然希望能够在面对这样的惊讶时玩得愉快并赢得比赛,但我们的 L 学习系统在这种事件面前束手无策。

正是我们的策略和更新规则的结合引发了问题。记得在我们开始学习之前,每一行都是从零开始的。当一场训练游戏最终获胜时,每个动作都根据游戏的时长获得相同的分数,正如我们在图 21-19 中看到的那样。当我们继续进行训练游戏时,下次当我们遇到那个棋盘时,我们会选择具有最大值的单元格。

假设我们正在进行一场训练游戏。我们看到一个曾经作为起始棋盘获得的棋盘,并且我们在两步之内就赢得了它。那两步的 L 表格值得到了很高的分数,所以我们选择得分较高的动作,准备在下一次翻转时获胜。但是就在我们第一次行动后,那辆大卡车咆哮着驶过,震动了我们的棋盘并翻转了一个棋盘格。从这个棋盘开始向前玩,我们最终需要更多的步骤才能获胜。这意味着,如果卡车没有经过,最终从这个动作中得到的 TFR 会更高。

这是问题所在:那个较小的值覆盖了每个导致这场长时间游戏的格子的先前值。换句话说,正因为那次事件,我们所采取的每个行动的 L 值都被降低了。特别是,那一开始就导致胜利的绝佳起手,现在得分很低。当我们在以后的游戏中再次遇到这个棋盘时,可能会发现另一个格子的值比原来那个进行绝佳走法的格子还要大。结果是,这个偶然事件导致我们不再执行到目前为止找到的最佳行动。我们“忘记”了这一步曾经是绝佳走法,因为一次随机事件让它变成了一个糟糕的选择。这个低分使得我们不太可能再选择这一步。

让我们看看这个问题是如何发生的。图 21-22 显示了一个没有不可预测事件的例子。我们从顶部开始,棋盘上有三个点,我们发现该行中最大的值是 0.6,对应于翻转中心格子。我们执行这个操作,假设下一步也选得不错,我们将在两步内获胜,如中间行所示。0.7 的奖励替代了我们第一步时的 0.6,巩固了这一走法作为最好的选择。一切都很顺利。

F21022

图 21-22:当没有意外事件时,我们的算法表现良好。(a)起始棋盘的 L 表格行。(b)游戏顺利进行,并在两步内获胜。(c)0.7 的值覆盖了所有导致这一成功的表格条目的先前值。

在图 21-23 中,我们介绍了我们的隆隆卡车。就在我们翻转中心瓷砖后,卡车震动了棋盘,右下角的瓷砖被翻转了。这将我们带上了一条全新的道路。假设算法在再进行四步之后最终获得了胜利。总共五步,奖励 0.44 被放置在每个导致成功的格子中。

这真糟糕。只需一笔,我们就“忘记”了我们的最佳走法。在这个例子中,另外两个行动现在得分更高。下次我们遇到这个棋盘时,会选择得分为 0.55 的格子,这样就不能像之前那样让我们一步之内获胜。换句话说,我们的最佳走法现在被遗忘了,我们将永远做出更差的选择。

F21023

图 21-23:(a)当卡车经过时,它翻转了右下角的格子,导致游戏需要五步才能获胜。(b)新的奖励 0.44 覆盖了之前的 0.6 值。这个格子不再是该行中得分最高的格子。

回想一下,我们曾说过,在训练过程中,我们会偶尔随机选择行中的一个单元格,以探索可能发生的情况。因此,某天我们可能会做出新的选择,或者卡车再次轰隆而过,帮助我们记住这个单元格,但可能很久才会发生一次。直到卡车经过并再次设置正确这个动作时,其他地方可能已经出错。L-table 几乎总是比应有的状态差,因此,*均而言,使用 L-学习的游戏会更长,奖励也较低。一个意外使我们忘记了如何好好玩这个棋盘。

这就是我们称这个算法为糟糕的原因。

但并非一切都丧失了。我们之所以查看这个算法,是因为其较差的版本可以被改进。大部分算法本身没有问题。我们只需要修复它在面对不可预测性时的缺陷。从现在开始,我们假设在玩 Flippers 时,可能会有一辆大卡车突然驶过,造成不确定性,偶尔会翻动一个随机的方块。在接下来的章节中,我们将看到如何优雅地处理这种不可预测事件,并改进学习算法,使其更有效。

Q-学习

不费太大力气,我们可以将 L-学习升级为一个如今广泛使用的更有效的算法,称为Q-学习(Q 代表质量)(Watkins 1989;Eden、Knittel 和 van Uffelen 2020)。Q-学习看起来和 L-学习很像,但自然地,它用 Q-值填充 Q-table。最大的改进是,Q-学习在随机或不可预测的环境中表现良好。

为了从 L-学习转向 Q-学习,我们进行三个升级:我们改进了 Q-table 单元格中新值的计算方式,如何更新现有值,以及我们选择动作的策略。

Q-table 算法基于两个重要原则。首先,我们期望结果中存在不确定性,因此从一开始就将其考虑在内。第二,我们在过程中逐步计算新的 Q-table 值,而不是等到最终的奖励。这第二个想法使我们能够处理那些持续很长时间的游戏(或过程),甚至可能永远无法得出结论的情况(如电梯调度)。通过逐步更新,我们能够在即使没有最终奖励的情况下,开发出有用的 Q-table 值。

为了让这个工作有效,我们还需要升级上一节中环境的超简单奖励过程。环境不再总是除最后一步外奖励零,而是会根据每次采取的行动立即返回估计每个动作质量的奖励。

Q-值与更新

Q-值是一种*似未来总奖励的方法,即使我们不知道事情最终会如何发展。要找出 Q-值,我们将即时奖励与所有未来可能的奖励加在一起。到目前为止,这不过是总未来奖励的定义。变化在于,现在我们通过使用下一个状态的奖励来寻找未来的奖励。

在图 21-17 中,我们为每个动作保存了四个信息:起始状态、我们选择的动作、我们获得的奖励和该动作带来的新状态。我们保存了这个新状态,以便现在使用它,并用它来计算其余的未来奖励。

关键的见解是注意到我们的下一步从那个新状态开始,并且通过遵循我们的策略,我们总是会选择 Q 值最大的动作。如果那个单元格的 Q 值是动作的总未来奖励,那么将那个单元格的值与我们即时的奖励相加就能得到当前单元格的总未来奖励。这是可行的,因为我们的策略保证了我们始终为任意给定的棋盘状态选择 Q 值最大的单元格。

如果下一个状态中的多个单元格共享最大值,那么当我们到达那里时,选择哪个单元格并不重要。现在我们关心的只是来自下一个动作的总未来奖励。

图 21-24 直观地展示了这个概念。请注意,我们在此步骤中计算的值并不是最终的 Q 值,但它几乎接*。

F21024

图 21-24:计算单元格新 Q 值的过程的一部分。新值是两个其他值的和。第一个值是采取与该单元格对应的动作所得到的即时奖励,这里是 0.2。第二个值是属于新状态的所有动作中 Q 值最大的,这里是 0.6。

缺失的步骤是 Q 学习如何考虑随机性。我们不会使用下一个动作单元格的值,而是使用该单元格的折扣值。请回忆,这意味着我们将其乘以我们的折扣因子,一个介于 0 到 1 之间的数值,通常写作γ(伽马)。如前所述,γ的值越小,我们就越不确定未来不可预测的事件不会改变这个值。图 21-25 展示了这一概念。

F21025

图 21-25:为了找到 Q 值,我们修改了图 21-24,加入了折扣因子γ,该因子根据我们对未来不可预测事件的信心来减少未来奖励。

请注意,图 21-8 中显示的折扣未来奖励中的多个乘法操作是由这个方案自动处理的。第一次乘法在此显式地包含在内。对于之后的状态的乘法,会在评估下一个状态的 Q 值时考虑。

现在我们已经计算出了一个新值,如何更新当前的值呢?在 L-learning 中我们看到,面对不确定性时,单纯地用新值替换当前值并不是一个好选择。但我们仍然需要以某种方式更新单元格的 Q 值,否则我们永远无法改进。

解决这个难题的 Q-learning 方法是将新单元格的值更新为旧值和新值的混合。混合的程度由我们指定的一个参数决定。也就是说,混合由一个介于 0 和 1 之间的数字控制,通常写作小写希腊字母α(alpha)。在α = 0 的极端值下,单元格中的值完全不变。在另一个极端值α = 1 时,新的值完全替代旧的值,就像在 L-learning 中一样。介于 0 和 1 之间的α值会混合这两个值,如图 21-26 所示。

F21026

图 21-26:α的值让我们能够*滑地从旧值(当α = 0 时)过渡到新值(当α = 1 时),或者是介于两者之间的任何值。

参数α被称为学习率,它由我们来设置。遗憾的是,这是反向传播更新步骤中也使用的同一个术语,但通常上下文会使我们明确指的是哪种“学习率”。

在实际操作中,我们通常会将α设置为接* 1 的值,例如 0.9 甚至 0.99。接* 1 的这些值使得新值在单元格中占主导地位。例如,当α = 0.9 时,存储在单元格中的新值为旧值的 10%,和新值的 90%。但是即使是 0.99 的值与 1 也有很大不同,因为即使记住旧值的 1%,也足以带来差异。

使用我们的α值,我们让系统通过一些训练,看看它的表现如何。然后我们可以根据观察到的结果调整这个值,再次尝试,重复这个过程,直到找到看起来最有效的α值。我们通常会自动化这个搜索过程,这样就不需要我们自己手动调整了。

这里面有一个显而易见的问题,那就是整个论证是基于在下一状态中就拥有正确的 Q 值,尽管我们还没有到达那个状态。那么,这些 Q 值是从哪里来的呢?如果我们已经有了正确的 Q 值,为什么还要做这些呢?

这些问题很有道理,我们将在了解了新的策略规则后再来讨论。

Q-Learning 策略

回想一下,策略规则告诉我们在给定环境状态时选择哪个动作。在学习时,我们使用这个策略,之后在实际游戏中也使用它。我们在 L-learning 中使用的策略是通常选择当前棋盘对应的表格行中具有最高 L 值的动作。这是有道理的,因为我们已经学到这是能够带来最高奖励的动作。但这个策略并没有明确解决探索与利用的困境。在一个不可预测的环境中,某个动作带来的最佳奖励有时可能并不是最好的奖励,而完全没有尝试过的动作,如果我们给它们一个机会,可能会带来更好的结果。

但是,我们并不希望每次都随机选择动作,因为我们确实希望偏向那些我们知道会带来高回报的动作。我们只是希望不是每次都这样做。Q 学习选择了一条中间道路。我们不是总是选择得分最高的动作,而是几乎总是选择得分最高的动作。其余的时间我们会选择其他的动作之一。让我们来看两种常见的策略来实现这一点。

我们将要查看的第一个方法叫做epsilon-greedy(或epsilon-soft)(这些名称来源于希腊小写字母ε,epsilon,因此有时也会出现ε-greedy 和ε-soft)。这两种算法几乎相同。我们从 0 到 1 之间选择一个ε值,通常这个值很小,接* 0,比如 0.01 或更小。

每当我们处于某一行并准备选择一个动作时,我们从系统请求一个 0 到 1 之间的随机数,这个数来自均匀分布。如果随机数大于ε,那么我们像*常一样选择得分最高的 Q 值对应的动作。但在那种偶尔的情况下,如果随机数小于ε,我们会从该行的所有其他动作中随机选择一个。在这种方式下,我们通常会选择最有前景的动作,但偶尔也会选择其他动作,看看它会带我们走向哪里。图 21-27 以图形方式展示了这一思想。

我们要查看的另一种策略叫做softmax。这与我们在第十三章中讨论的 softmax 层的工作方式相似。当我们对一行的 Q 值应用 softmax 时,它们会以一种复杂的方式进行转换,使得它们的总和为 1。这让我们能够将结果值视为一个离散的概率分布,然后根据这些概率随机选择其中一个值。

F21027

图 21-27:epsilon-greedy 策略

这样,我们通常会选择得分最高的动作。偶尔,我们会选择得分第二高的动作。更少的时候,我们会选择得分第三高的动作,依此类推。图 21-28 展示了这一思想。

F21028

图 21-28:softmax 策略暂时缩放行中的所有动作,使它们的总和为 1。

这种方案的一个吸引人的特点是,选择每个动作的概率始终反映了与给定状态相关的所有动作的最新 Q 值。因此,随着值的变化,选择动作的概率也会变化。

softmax 进行的特定计算有时会导致系统未能稳定在一组良好的 Q 值上。一个替代方法是mellowmax策略,它使用稍有不同的数学(Asadi 和 Littman 2017 年)。

将所有内容结合起来

我们可以用几句话和一个图示来总结 Q 学习的策略和更新规则。用语言描述时,当需要执行一步时,我们使用当前的状态来找到 Q 表中的相应行。然后我们根据策略(epsilon-greedy 或 softmax)从该行中选择一个动作。我们执行这个动作,得到一个奖励和一个新状态。现在,我们希望更新我们的 Q 值,以反映从奖励中学到的东西。我们查看新状态下的 Q 值,并选择其中最大的一个。我们根据环境的不可预测性对其进行折扣,然后将其加到我们刚得到的即时奖励上,并将该新值与当前的 Q 值进行混合,生成我们刚采取的动作的新 Q 值,并将其保存。

图 21-29 总结了这个过程。

F21029

图 21-29:Q 学习策略和更新过程。(a)选择一个动作。(b)为该动作找到新的 Q 值。

当我们开始执行一个动作时,如图 21-29(a)所示,我们查看当前状态对应的 Q 表行,并用我们的策略来选择一个动作,这里用红色表示。这个动作会传达给环境,环境会做出回应,给出奖励和新状态。如图 21-29(b)所示,我们找到新状态对应的 Q 表行,并选择其中最大的奖励(假设我们到达新状态时会选择最大的动作,虽然我们知道这并不总是如此。我们很快会回到这个问题)。我们通过将该奖励乘以γ来对其进行折扣,然后将其加到我们此时的即时奖励中,从而得到原来选择的动作的新值。我们用α将旧值和新值混合,这个新值就被放入 Q 表中原来动作的单元格里。

策略参数ε、学习率α和折扣因子γ的最佳值必须通过反复试验来找到。这些因素与我们执行的任务的具体性质、环境的特性以及我们使用的数据密切相关。经验和直觉通常能为我们提供良好的起点,但没有什么能比传统的试错法更能找到任何特定学习系统的最佳值。

居于眼前的大象

之前我们承诺会回到一个问题,那就是我们需要准确的 Q 值来评估更新规则,但这些值本身是通过更新规则计算出来的,而更新规则使用的是后续的值,依此类推。每一步似乎都依赖于下一步的数据。我们怎么能使用那些尚未创建的数据呢?

这是解决该问题的简单美丽答案:我们忽略它。令人难以置信的是,我们可以将 Q 表初始化为全零,然后开始学习。刚开始时,由于 Q 表中没有任何信息帮助系统选择一个单元格,它的行动会显得非常混乱。系统会随机选择一个单元格并执行该操作。结果状态下的所有动作也都是零,因此更新规则无论我们使用什么值的αγ,都会使该单元格的得分保持为零。

我们的系统进行的游戏看起来混乱且愚蠢,做出糟糕的选择,错失明显的好动作。但最终,系统偶然发现了胜利。这个胜利会得到一个正数的奖励,这个奖励会更新导致胜利的动作的 Q 值。稍后,某个导致我们执行该动作的动作会融入一些巨大的奖励,因为 Q 学习的步骤会预见到下一个状态。这个波动效应会继续缓慢地向后影响系统,因为新的游戏进入到那些曾经导致胜利的状态。

请注意,信息并没有真正地向后移动。每场游戏从头到尾进行,每次更新都在每一步后立即进行。信息看似向后移动,是因为 Q 学习涉及在评估更新规则时向前看一步。下一步的得分能够影响当前步骤的得分。

在某个时刻,感谢我们的策略,有时尝试新的动作,每个动作最终都会通向胜利的路径,这些值也会影响到越来越早的动作。最终,Q 表会填充上准确预测每个动作奖励的值。进一步的游戏只会提高这些值的准确性。这个过程被称为收敛。我们说 Q 学习算法收敛了。

我们可以从数学上证明 Q 学习是收敛的(Melo 2020)。这种证明保证了 Q 表会逐渐变得更好。但我们无法说出这个过程需要多长时间。表格越大,环境越不可预测,训练过程所需的时间也越长。收敛的速度还取决于系统试图学习的任务的性质、提供的反馈,当然,还有我们为策略变量ε、学习率α和折扣因子γ选择的值。像往常一样,没有什么能代替通过反复试验来学习任何特定系统的独特性。

请注意,Q 学习算法很好地解决了我们之前讨论的两个问题。奖励分配问题要求我们确保引导胜利的动作得到奖励,即使环境没有提供这个奖励。更新规则的性质解决了这个问题,将成功动作的奖励从导致胜利的最终步骤反向传播,直到第一步。算法还通过使用 epsilon-greedy 或 softmax 策略解决了探索或利用的困境。它们都偏好选择那些已被证明成功的动作(利用),但有时也会尝试其他动作,看看可能的结果是什么(探索)。

Q 学习法在行动中

让我们让 Q 学习法发挥作用,看看它是否能在不可预测的环境中学会如何玩《翻转者》游戏。衡量算法表现的一种方式是让训练过的模型进行大量随机游戏,并看看它们需要多长时间。算法在找到好的动作并消除坏动作方面做得越好,每局游戏在胜利前所需的步数就越少。

最长的良好游戏是从九个格子都显示点开始的。然后,我们需要翻转六个格子才能获胜。所以,我们希望看到我们的算法每局游戏都能在六步或更少的步骤内获胜。

为了查看训练对算法的影响,我们来看一下不同训练量下大量游戏长度的图表。我们的图表显示了在一个具有相当程度不可预测性的环境中,从每种可能的 512 种点和空格的起始模式开始的游戏结果。我们为每个起始棋盘玩了 10 局游戏,总共进行了 5120 局游戏。我们会中断任何超过 100 步的游戏。

我们将α设置为 0.95,因此每个格子在更新时只保留 5%的旧值。这样,我们不会完全失去之前学到的东西,但我们期望新的值比旧的值更好,因为它们将基于改进后的 Q 表值来选择下一步行动。为了选择动作,我们采用了一个相对较高ε值为 0.1 的 epsilon-greedy 策略,鼓励算法每 10 次中有 1 次去尝试新的动作。

我们通过模拟每次移动后有 1/10 的概率随机卡车经过,每次翻转一个随机的瓦片,引入了很多不可预测性。为了解决这个问题,我们将折扣因子γ设置为 0.2。这个较低的值意味着我们只对未来的走势有 20%的确定性,因为这些随机事件的影响。我们将这个值设置得比我们知道卡车引入的噪声水*(10%)还要高,因为我们预计大多数经过良好操作的游戏只有三到四步长,因此它们出现随机事件的概率低于十步或更多步的游戏。

这些 αγε 的值基本上是基于经验的猜测。特别是,γ 的选择是基于我们对随机事件发生频率的了解,而这种情况我们通常无法提前知道。在实际情况中,我们会通过实验调整参数,以找到最适合此游戏和噪音量的设置。

图 21-30 显示了在仅训练 300 场游戏后的游戏时长。算法已经找到了很多快速获胜的方法。

F21030

图 21-30:使用经过 300 场游戏训练的 Q 表,在 0 到 40 步之间获胜所需的游戏数量(我们对每个 512 种起始局面进行了 10 次游戏)

“瞬间获胜”位于第一列,对应于零步。这些游戏的起始局面已经有三个点,排列成垂直列或水*行。由于有六种可能的获胜局面,我们对所有可能的局面进行了 10 次游戏,因此我们从一个获胜局面开始了 60 次游戏。

由于 图 21-30 中没有一场游戏达到了我们的 100 步限制,我们可以看到算法从未陷入长期循环。循环可能只是两个状态永远交替,或者是一串长的状态,它最终会回到自身。Flippers 中是有可能出现循环的,而且基本的 Q-learning 算法并没有明确防止系统进入循环。

我们可以说系统“发现”了循环无法获胜,因此不会带来任何奖励,所以它学会了避免循环。如果某个时候它确实回到了先前访问过的状态,无论是通过做出那个动作,还是通过随机引入的翻转,ε 的相对较高值意味着它有很大的机会最终选择一个新的动作,从而进入一个新的方向。

让我们将训练游戏的数量提高到 3,000,如 图 21-31 所示。

F21031

图 21-31:根据训练 3,000 场游戏的 Q 表,进行 5,120 场游戏后,不同长度的游戏数量

该算法已经学到了很多东西。现在最久的游戏仅需 20 步,大多数游戏在 10 步内就能获胜。可以看到,四步和五步周围的集群更密集,效果相当不错。

让我们看看在这 3,000 轮训练后进行的一场典型游戏。图 21-32 显示了这场从左到右进行的游戏。该算法用了八步才赢得了比赛。

图 21-32 的结果并不令人鼓舞。仅从起始局面看,我们就能看到至少四种不同的方式在四步内赢得游戏。例如,可以先翻转左下角的方块,然后翻转中间和最右列的三个点。但我们的算法似乎在随机翻动方块。它最终偶然找到了解法,但这绝对不是一个优雅的结果。

F21032

图 21-32:在训练了 3,000 轮 Q-learning 后,进行 Flippers 游戏

如果我们对算法进行更多的训练回合,我们预计其表现会有所改善。经过额外 3,000 次训练回合(总共 6,000 次),并查看需要不同步数的游戏数量,我们得到了图 21-33 的结果。

与我们在图 21-31 中的结果相比,经过 3,000 场训练后,最久的游戏从 20 步减少到 18 步,且只有 3 步和 4 步的短游戏变得更加频繁。

这张图表表明算法正在学习,但它在实际玩游戏时表现如何?事实上,算法的能力已经有了巨大的飞跃。

图 21-34 显示了与图 21-32 相同的游戏,这个游戏最初需要八步才能获胜。现在它只需要四步,这是这个棋盘上最少的步数(虽然有不止一种方法可以实现)。

F21033

图 21-33:在训练了 6,000 次游戏的 Q 表之后,我们的 5,120 场游戏中,获胜所需的步数分布。

Q-learning 在这种高度不可预测的学习环境中表现出色,在每进行 10% 的动作后,瓦片会随机翻转。它成功应对了这种不可预测性,并且在仅进行 6,000 次训练的情况下,为大多数游戏找到了理想的解决方案。

SARSA

Q-learning 表现出色,但它存在一个缺陷,可能会降低其所依赖的 Q 值的准确性。这就是我们在讨论图 21-29 时提到的问题,我们注意到,尽管并不一定是采取的行动,仍然根据最可能的下一步行动的得分来预测未来的奖励。换句话说,更新规则假设我们在下一步会选择得分最高的动作,它基于这个假设计算新的 Q 值。这一假设并非不合理,因为我们的 epsilon-greedy 和 softmax 策略通常会选择最有奖励的动作。但当这些策略选择其他动作时,这个假设就不成立了。

当我们的策略选择了更新规则中没有使用的其他动作时,计算将使用错误的数据,最终导致我们计算该动作的新值时准确性降低。幸运的是,我们可以解决这个问题。

F21034

图 21-34:通过更多训练回合,Q-learning 更高效地解决了图 21-32 中的游戏

算法

如果能保留 Q 学习的所有优点,同时避免犯错,通过使用最高得分的下一个动作的 Q 值来计算某个动作的 Q 值,而实际上我们可能并不会在下一个动作中选择那个动作,那该多好啊。我们可以通过稍微修改 Q 学习,创建一个新算法,称为SARSA(Rummery 和 Niranjan 1994)。这是“状态-动作-奖励-状态-动作”的缩写。我们从图 21-17 开始,已经涵盖了“SARS”部分,即我们保存了初始状态(S)、动作(A)、奖励(R)和结果状态(S)。这里新增的是末尾的额外动作“A”。

SARSA 通过使用我们的策略(而不是仅仅选择得分最高的那个)来选择下一个状态中的正确单元,并记住我们选择的动作(这就是末尾的额外“A”)。然后,当我们需要做出新的动作时,我们选择之前计算并保存的动作。

换句话说,我们已经将应用我们选择动作策略的时间推迟了。我们不是在动作开始时选择我们的动作,而是在前一个动作中选择,并记住我们的选择。这使得我们在构建新的 Q 值时,能够使用我们实际将会使用的动作的值。

这两项改变(推迟动作选择步骤和记住我们选择的动作)是 SARSA 与 Q 学习的区别所在,但它们可以显著提高学习速度。

让我们来看一下使用 SARSA 的三个连续动作。第一个动作如图 21-35 所示。由于这是第一次动作,我们使用我们的策略为此动作选择一个动作,如图 21-35(a)所示。这是唯一一次这么做。选择完我们的动作后,我们使用策略为第二步选择动作。我们从环境中获得奖励,并更新我们刚刚选择的动作的 Q 值,如图 21-35(b)所示。

F21035

图 21-35:在游戏的第一步中使用 SARSA。(a)我们使用策略选择当前动作。(b)我们也使用策略选择下一个动作,并使用该下一个动作的 Q 值来更新当前 Q 值。

第二个动作如图 21-36 所示。现在我们使用上次为自己选择的动作,然后选择在第三个动作中使用的动作。

F21036

图 21-36:使用 SARSA 的第二步动作。(a)我们执行上次为自己选择的动作。(b)我们选择下一个动作,并利用其 Q 值来更新当前动作的 Q 值。

第三个动作如图 21-37 所示。在这里,我们再次选择之前确定的动作,并为下一个第四个动作计算出相应的动作。

F21037

图 21-37:使用 SARSA 的第三步。(a)我们采取了第二步时确定的动作。(b)我们为第四步选择一个动作,并利用其 Q 值来改善当前动作的 Q 值。

值得高兴的是,我们可以证明 SARSA 也会收敛。像以前一样,我们不能保证它需要多久,但通常它比 Q 学习更早产生良好的结果,并且很快改善这些结果。

SARSA 的应用

让我们来看看 SARSA 在 Flippers 游戏中的表现,采用与 Q 学习相同的方法。图 21-38 显示了我们在使用 SARSA 进行 3000 次训练后,5120 局游戏所需的步数。在此图和接下来的图中,我们继续使用与 Q 学习图中相同的参数:学习率α为 0.95,每步后引入 0.1 的随机翻转,折扣因子γ为 0.2,并采用 epsilon 贪心策略,ε设为 0.1。

F21038

图 21-38:使用 SARSA 在训练 3000 局后,5120 局游戏的步数。注意只有少数游戏需要超过 6 步。

这看起来不错,大多数值聚集在 4 左右。最长的游戏仅为 15 步,很少有超过 8 步的游戏。

让我们来看一个典型的游戏。图 21-39 显示了从左到右的游戏过程。该算法需要七步才能获胜。虽然不算差,但我们知道它可以更快地解决。

一如既往,更多的训练应当带来更好的表现。像之前一样,让我们将训练次数增加到 6000 次。

图 21-40 显示了我们在进行 6000 次训练后,5120 局游戏的步数。

F21039

图 21-39:在 SARSA 进行 3000 次训练后玩 Flippers 游戏

F21040

图 21-40:使用 SARSA 在训练 6000 局后,我们的 5120 局游戏的步数。注意大多数游戏的步数变得更短,且没有游戏陷入循环。

最长的游戏从 15 步减少到 14 步,这个变化不算大,但长度为 3 和 4 步的短游戏数量现在更加明显。需要超过 6 步的游戏并不多。

图 21-41 显示了与图 21-39 相同的游戏,该游戏需要 7 步获胜。现在只需 3 步,这是该棋盘的最小步数(尽管同样的,3 步也有不止一种获胜方法)。

F21041

图 21-41:与图 21-39 相同的游戏,经过 3000 次训练后

比较 Q 学习与 SARSA

让我们来比较 Q 学习与 SARSA 算法。图 21-42 显示了经过 6000 局训练后,Q 学习和 SARSA 的 5120 种可能游戏的步数。这些结果与之前的图略有不同,因为它们是通过新一轮的算法运行生成的,因此随机事件有所不同。

它们大致相当,但 Q 学习产生了一些游戏,其时长超过了 SARSA 的最大时长 12 步。

F21042

图 21-42:在 6,000 场训练游戏后比较 Q 学习和 SARSA 的游戏时长。SARSA 的最长游戏是 11 步,而 Q 学习则有高达 18 步的情况。

更多的训练是有帮助的。我们将训练长度增加了 10 倍,每个训练进行了 60,000 场游戏。结果如图 21-43 所示。

f21043

图 21-43:与图 21-42 相同的训练场景,但这次我们进行了 60,000 场训练游戏

在这个训练阶段,SARSA 在 Flippers 上表现非常出色,几乎所有的游戏都在 6 步或更少内完成(很少有游戏需要 7 步)。Q 学习总体上稍微差一些,某些游戏需要多达 16 步才能解决,但它也大部分集中在 4 步以内。

另一种比较 Q 学习和 SARSA 在这个简单游戏中的方法是绘制经过逐渐延长的训练后的*均游戏时长。这可以让我们了解它们在学习如何赢得游戏方面的有效性。图 21-44 展示了我们 Flippers 游戏的结果。

f21044

图 21-44:从 1 到 100,000 场训练游戏(以 1,000 场为增量)的*均游戏时长

这里的趋势很容易看出来。两种算法都迅速下降,然后趋于*稳,但在经历了一段噪声较大的起步后,SARSA 的表现始终更好,最终每场游戏节省了*半步(也就是说,一般每两场游戏少走一步)。当我们达到 100,000 场训练游戏时,似乎两种算法的表现都停止了改善。看起来每个算法的 Q 表已经稳定下来,随着时间的推移,因环境的随机波动而略微变化。

所以,尽管 Q 学习和 SARSA 都能在学习玩 Flippers 时表现得很出色,但 SARSA 的游戏通常会更短。

全貌

让我们退后一步,回顾一下强化学习的全貌。

这里有一个环境和一个智能体。环境向智能体提供两组数字(状态变量和可用动作)。利用其策略,智能体会考虑这两组列表,以及它保存的任何私人信息,从中选择一个动作,并将其返回给环境。作为回应,环境会返回一个数字(奖励)和两组新的列表。

将列表解释为棋盘和动作是很棒的,因为这让我们能够将 Q-learning 看作是学习如何玩游戏。但代理并不知道它正在玩一个游戏,也不知道有规则,或者实际上对任何事情知之甚少。它只知道两个数字列表传入,它从其中一个列表中选一个值,然后返回一个奖励值。这个小过程能做出许多有趣的事情,令人惊讶。但如果我们能够找到一种方法来描述我们的环境,以及在这个环境中采取的行动,使用数字集合表示,并且我们能找到一种粗略的方式来区分好的行动和坏的行动,那么这个算法就能学习如何执行高质量的行动。

这在我们简单的 Flippers 游戏中是有效的,但所有这些 Q-table 内容在实际操作中有多实用呢?在 Flippers 中,有九个方格,每个方格可以有一个点或者没有,因此游戏需要一个包含 512 行和 9 列的 Q-table,即 4,608 个单元格。在井字游戏中,有九个方格,每个方格可以有三种符号之一:空白、X 或 O。这个游戏的 Q-table 需要 20,000 行和 9 列,即 180,000 个单元格。

这很大,但对于现代计算机来说并不算荒谬大。但如果我们想玩一个稍微有挑战性的游戏呢?假设我们不是在 3×3 的棋盘上玩井字游戏,而是在 4×4 的棋盘上玩。这样的棋盘数量略超过 4300 万个,所以我们的表格将需要 4300 万行和 9 列,或者稍低于 3.9 亿个单元格。即使是对于现代计算机来说,这也开始变得非常庞大。我们再稍微增加一点,假设我们在 5×5 的棋盘上玩井字游戏。这个似乎并不荒唐。然而,这个棋盘有接* 850 十亿 种状态。如果我们再稍微大胆一点,在 13×13 的棋盘上玩,我们会发现状态数量超过了可见宇宙中原子数量(Villanueva 2009)。实际上,这大约是一个十亿个可见宇宙中的原子数量。

存储这个游戏的表格并不是现实可行的,但想要这么做是完全合理的。更为合理的情况是,我们可能想玩围棋。围棋的标准棋盘是一个 19×19 的交叉点网格,每个交叉点可以为空、黑子或白子。这就像我们的井字游戏棋盘,但大得无法想象。我们需要一个行标签需要 173 位数字的表格。这样的数字不仅完全不实际,简直是无法理解的。

然而,这正是 Deep Mind 团队用来构建 AlphaGo 的基本策略,AlphaGo famously 击败了世界冠军人类玩家(DeepMind 2020)。他们通过将强化学习与深度学习相结合来实现这一目标。这种深度强化学习方法的一个关键见解是消除 Q-table 的显式存储。我们可以将这个表格看作是一个函数,它以棋盘状态为输入,返回一个动作编号和 Q 值作为输出。正如我们所见,神经网络在预测这种事情上非常擅长。

我们可以构建一个深度学习系统,接受棋盘输入,并预测如果我们继续保持棋盘存在,每一步的 Q 值。通过足够的训练,这个网络可以变得足够准确,以至于我们可以放弃 Q 表格,单纯使用网络。训练这样的系统可能是具有挑战性的,但是可以做到的,且效果非常好(Mnih et al. 2013;Matiisen 2015)。深度强化学习已被应用于视频游戏、机器人学,甚至医疗保健等领域(François-Lavet et al. 2018)。它还是 AlphaZero 背后的核心算法,AlphaZero 无疑是有史以来围棋的最佳玩家(Silver et al. 2017;Hassabis and Silver 2017;Craven and Page 2018)。

强化学习相较于监督学习的一个优势在于,它不需要手动标注的数据库,而人工标注通常是一个既费时又昂贵的过程。另一方面,它要求我们设计一个奖励生成算法,引导代理朝着期望的行为前进。在复杂的情况下,这可能是一个难以解决的问题。

这必然是一个关于一个大主题的高层次概述。更多关于强化学习的信息可以在专门的参考资料中找到(François-Lavet et al. 2018;Sutton and Baro 2018)。

总结

在本章中,我们介绍了强化学习(RL)的一些基本概念。我们看到,强化学习的基本思想是将世界分为一个行动的代理和一个包含其他一切的环境。代理被赋予一系列选项,并根据一个策略选择其中一个。环境执行该动作,并产生后续效果(这可能包括在游戏中做出回合动作,或进行模拟或现实世界中的操作),然后返回一个奖励,描述代理所选动作的质量。通常,奖励描述了代理在某种程度上改善环境的成功程度。

我们将这些思想应用于单人游戏《Flippers》,使用一种简单的算法将奖励记录在表格中,并使用一个简单的策略在可能的情况下选择具有最高奖励的动作。我们看到,这种方法并不能很好地应对现实世界的不可预测性,因此我们将其改进为具有更好更新规则和学习策略的 Q 学习算法。

接着,我们通过预先选择下一个动作再次改进了该方法,得出了 SARSA 算法。这个算法学会了更好地玩《Flippers》。

实际上,很多算法都属于强化学习范畴,并且新的算法不断涌现。它是一个充满活力的研究与开发领域。

在下一章中,我们将探讨一种强大的方法,用于训练生成器,可以生成图像、视频、音频、文本及其他类型的数据,生成的数据与训练集中的数据难以区分。

第二十二章:生成对抗网络

生成数据是令人兴奋的。它让我们能够创作出新的画作、歌曲和雕塑,这些作品与它们的输入数据相似。在第十八章中,我们看到如何使用自动编码器生成与训练数据相似的新数据。在这一章中,我们将探索一种完全不同的数据生成方法。我们研究的系统类型称为生成对抗网络,简称GAN。它基于一种巧妙的策略,通过让两个不同的深度网络相互对抗,目标是让一个网络创造出新的样本,这些样本不是来自训练数据,但足够像训练数据,以至于另一个网络无法分辨。

GAN 方法实际上是一种训练生成新数据的网络的技术。训练后的生成器就像任何其他神经网络一样,而我们用来训练它的方法已经不再重要了。但该领域的语言通常将使用 GAN 方法训练的生成器本身称为 GAN。虽然这种命名方式有点奇怪,因为它是根据网络学习如何完成任务的方式来命名的,而不是根据它的实际功能,但我们就是这么做的。因此,我们使用 GAN 技术来训练生成器,这个生成器通常被称为生成器,但也经常被称为 GAN。

让我们从讨论 GAN 方法开始,首先看看一个由两个人组成的小组如何通过互相帮助学习伪造钞票。然后,我们可以将这两个人替换成神经网络。其中一个网络变得越来越擅长识别伪钞,另一个则变得越来越擅长制作伪钞。当训练过程结束时,伪造者能够制作出任意数量的新钞票,并且判别器无法可靠地区分伪钞和真钞。这一过程适用于任何类型的数据,从狗的图片到某人说话的声音。

我们将看到如何构建、训练并使用这两个网络来合成新数据,使用不同类型的层。章节最后,我们会讨论在训练和使用这些网络生成数据时需要注意的问题。

伪造钞票

介绍生成对抗网络(GAN)通常通过类比伪造操作来进行。我们将展示一种变体的典型介绍方式,以更好地揭示关键思想。

故事开始于两位共谋者,Glenn 和 Dawn。Glenn 的名字以 G 开头,因为他扮演的是生成器的角色,在这种情况下是伪造新钞票。Dawn 的名字以 D 开头,因为她扮演的是判别器的角色,负责判断某张钞票是否真实,或者是 Glenn 伪造的。Glenn 和 Dawn 都会随着时间的推移不断改进,从而推动彼此的改进。

作为伪钞制造者,Glenn 整天坐在后面的房间里,仔细制作金属版并印刷假币。Dawn 是质量控制的一部分。她的工作是将一堆混合的真钞与 Glenn 的伪钞一起检查,分辨哪个是真哪个是假。伪钞在他们国家的刑罚是终身监禁,所以他们都非常有动力制造出没人能分辨的钞票。假设他们国家的货币叫做 Solar,他们想要伪造 10,000 Solar 钞票。

需要注意的一点是,所有的 10,000 Solar 钞票都不相同。至少,每一张钞票都有一个独特的序列号。但真正的钞票也会被刮伤、折叠、涂画、撕裂、弄脏,或者以其他方式被处理过。由于崭新的钞票显眼,Glenn 和 Dawn 希望制造出看起来和流通中的其他旧钞票一模一样的货币,这样它才能融入其中,不引人注意。就像真正的钞票一样,每一张伪造钞票也应该看起来独特。

在实际情况下,Glenn 和 Dawn 肯定会从一大堆真钞开始,仔细查看每一个细节,尽可能地学习所有信息。但我们只把他们的操作作为一个隐喻,因此我们会设置一些限制,使这个情况更符合本章讨论的算法。首先,我们简化一下,假设我们只关心钞票的一面。其次,我们不会给 Glenn 和 Dawn 每人一堆钞票让他们在开始之前研究。事实上,假设 Dawn 和 Glenn 都不知道真正的 10,000 Solar 钞票长什么样。显然,这会让事情变得更加困难。稍后我们会对此做出解释。唯一给他们的东西是 Glenn:一大堆空白矩形纸张,形状和大小与 10,000 Solar 钞票相匹配。

他们各自遵循着日常的例行公事。每天早上,Glenn 坐下来,利用他目前拥有的所有信息制作一些伪钞。一开始,他什么都不知道,所以他可能只是随便在纸上涂抹不同颜色的墨水。或者也许他画一些面孔或数字。他基本上就是随便画一些东西。与此同时,Dawn 去银行取出一堆真钞,轻轻地在每一张背面用铅笔写下Real字样。然后,当 Glenn 做完时,她收集起当天的伪钞,并在每张背面轻轻写下Fake字样。接着,她将这两堆钞票混合在一起。图 22-1 展示了这一过程。

f22001

图 22-1:Dawn 从银行拿到真钞,拿到 Glenn 的伪钞后,把它们混合在一起(中间的堆),然后分成真钞和假钞。

现在,Dawn 开始她的主要工作。她逐一检查这些钞票,不看背面,将每一张钞票分类为真或假。假设她问自己:“这张钞票是真的吗?”我们称“是”的回答为对这张钞票的正面回应,称“不是”的回答为对这张钞票的负面回应。

Dawn 小心地将她的起始堆栈分成两堆:真钞和假钞。由于每张钞票都可以是真或假,因此有四种可能的情况,如图 22-2 所示。

f22002

图 22-2:当 Dawn 检查一张钞票时,它可能是真的也可能是假的,而她可能会判断它是真的或假的。这给我们带来了四种组合。

当 Dawn 看一张钞票时,如果它是真的,她也判断它是真的,那么她的“正面”决策是准确的,我们就有了真正的正例(TP)。如果钞票是真的,但她的判断是“负面”(她认为它是假的),那么就是假阴性(FN)。如果钞票是假的,但她认为它是真的,那就是假阳性(FP)。最后,如果它是假的,她正确地识别它为假,那么就是真正的负例(TN)。在除了真正的正例之外的所有情况下,Dawn 或 Glenn 都会利用这个例子来改进他们的工作。

从经验中学习

我们已经提到,Dawn 和 Glenn 只是分别代表神经网络 判别器生成器 的人类替身。

判别器是一个分类器。它将每个输入分为两类之一:真或假。当预测错误时,那个网络的误差函数值很大。然后,我们按照常规方式用反向传播和优化训练判别器,这样下次该分类就更有可能是正确的。

生成器的工作则完全不同。它根本看不见训练数据。相反,我们给它一个随机输入(比如几百个数字的列表),它从中产生一个输出。这就是它所做的所有工作。如果判别器认为该输出是真的(即来自训练集),那么生成器就得到了这个伪造物并且不需要改进。但如果判别器认为输出是假的(即来自生成器或是合成的),那么生成器会收到一个误差信号,我们使用反向传播和优化来让它避免产生类似被判别器识别为假的结果。每次运行生成器时,我们都会给它新的随机起始值。生成器面临的任务非常艰巨:将这小小的数字列表转化为一个能够欺骗判别器的输出。例如,预期的输出可能是一首听起来像巴赫所写的歌、一段听起来像人的讲话、一张看起来像人的脸,或者一张价值 10,000 Solars 的旧钞票。

我们怎么训练这样的系统呢?生成器从未看到它试图模仿的数据,因此它无法从中学习。它只知道何时做错。

出人意料的有效方法是通过试错法。我们一开始,如前所述,使用一个完全未经训练的生成器和鉴别器。当我们给鉴别器一些数据时,它基本上只是将每一项数据分配给一个随机类别。与此同时,生成器也在随机生成输出。它们都在摸索,本质上输出的是毫无意义的结果。

然而,鉴别器会慢慢开始学习,因为我们为它提供了正确的标签来标注它分类的数据。随着鉴别器的表现逐渐提高,生成器尝试不同的输出变体,直到某个变体能通过鉴别器的检测(即,鉴别器认为它是来自真实数据,而非生成器的输出)。生成器将这个输出视为它迄今为止的最佳作品。然后,鉴别器进一步提高,生成器也随之进步。随着时间的推移,每个网络中的微小改进会累积,直到鉴别器对真实数据和生成数据之间的差异非常敏感,而生成器也非常擅长尽量缩小这些差异。

与神经网络的结合

图 22-2 展示了在 Dawn 为每张钞票做出决定后可能出现的四种情况。让我们更仔细地看看如何训练鉴别器和生成器,使它们互相推动对方改进。请注意,本讨论旨在讲解这些概念,因此我们将逐个样本地进行阐述。实际上,我们通常会以更复杂但更高效的方式实现这些想法(例如,通过批量训练,而不是逐个样本地训练)。

让我们更仔细地看看图 22-2 中四种可能情况的流程图形式。

从真正的正例开始,鉴别器正确地报告输入的真实钞票图像确实是一张真实的钞票。由于这正是我们希望鉴别器在这种情况下执行的操作,因此无需进行学习。图 22-3 通过图形方式展示了这个过程。

f22003

图 22-3:在真正的正例(TP)情况下,鉴别器(D)接收到一张真实的钞票并正确预测它为真实钞票。结果不需要进行任何操作。

接下来是错误的负例,当鉴别器错误地将真实钞票判断为假钞时。此时,鉴别器需要更多地学习真实钞票的特征,以避免重复此错误。图 22-4 展示了这种情况。

f22004

图 22-4:当钞票为真但鉴别器判断为假时,我们会得到一个错误的负例(FN)。鉴别器需要更多地学习真实钞票的特征,以避免重复此错误。

假阳性情况发生在判别器被生成器欺骗,错误地判断伪造的钞票为真实的情况下。在这种情况下,判别器需要更加仔细地检查钞票,找出任何错误或不准确的地方,以免再次被欺骗。图 22-5 展示了这个过程。

f22005

图 22-5:在假阳性(FP)情况下,判别器接收到生成器的伪钞,但将其误判为真实钞票。为了迫使生成器变得更好,判别器从自己的错误中学习,以防止这张伪钞再次通过。

最后,真实负例的情况是当判别器正确识别出伪钞时。在这种情况下,如图 22-6 所示,生成器需要学习如何改进其输出。

请注意,在这四种可能性中,其中一种(TP)对任何网络都没有影响,另外两种(FN 和 FP)促使判别器提高识别真实和伪钞的能力,而只有一种(TN)促使生成器学习并避免重复错误。

f22006

图 22-6:在真实负例(TN)场景中,我们给判别器一张来自生成器的伪钞,判别器正确地将其识别为伪钞。在这种情况下,生成器意识到其输出不够好,必须提高其伪造技能。

一次学习回合

现在让我们将上一节的反馈回路汇总成判别器和生成器的单一训练步骤。一般来说,我们会反复执行这四个步骤。在每个步骤中,我们给判别器提供一张真实的或伪造的钞票,然后根据其反应,遵循我们刚才看到的四个流程图之一。

首先我们训练判别器,然后是生成器,再然后是判别器,最后是生成器。这个过程的目的是测试在每种情况下,哪个网络需要学习。真实负例的情况,即生成器学习的情形,会重复两次,原因稍后会解释。图 22-7 总结了这四个步骤。

f22007

图 22-7:学习回合的四个步骤

首先,在部分 (a) 中,我们尝试从假阴性中学习。我们给判别器一张来自真实钞票数据集的随机钞票。如果它错误地将其归类为伪钞,我们会告诉判别器从这个错误中学习。

第二,在部分 (b) 中,我们寻找真实负例。我们给生成器一些随机数字,生成一张伪钞,并交给判别器。如果判别器识别出伪造行为,我们会告诉生成器,生成器尝试学习如何制作更好的伪钞。

第三,在部分 (c) 中,我们寻找假阳性。我们给生成器一组新的随机值,让它生成一张新的伪钞,然后交给判别器。如果判别器被欺骗并认为钞票是真的,那么判别器将从这个错误中学习。

最后,在第(d)部分,我们重复第二步中的真正负样本测试。我们为生成器提供新的随机数,生成一张新的伪钞,如果判别器识破了伪钞,生成器就会学习。

重复生成器学习步骤两次的原因是,实践表明,在许多情况下,最有效的学习计划是以大致相同的速率更新这两个网络。因为判别器从两种类型的错误中学习,而生成器只从一种错误中学习,所以我们将生成器的学习机会加倍,从而使两个网络能够大致以相同的速度进行学习。

通过这个过程,判别器在识别真钞和发现伪钞错误方面变得越来越好,而生成器则相应地在如何制造无法被识破的伪钞方面变得越来越擅长。这对网络一起构成了一个完整的 GAN。我们可以将这两个网络想象成一场“学习之战”(Geitgey 2017)。随着判别器在识别伪钞方面变得越来越精准,生成器必须相应地变得更好,才能通过判别器的检测,这反过来又促使判别器在发现伪造时变得更强,生成器则变得更擅长制造伪钞,依此类推。

最终目标是让判别器达到最佳状态,对真实数据的每个方面都有深入广泛的了解,同时生成器依然能成功地欺骗判别器。这告诉我们,尽管伪钞与真实样本不同,但它们在统计上与真实样本无法区分,这正是我们一直以来的目标。

为什么是对抗性?

生成对抗网络(GAN) 这个名称,在前述描述的背景下可能显得有些奇怪。我们刚刚描述的两个网络似乎是合作的,而非敌对的。选择对抗性一词,来自于以稍微不同的方式看待问题。我们可以将 Dawn 想象成一名与警方合作的侦探,而 Glenn 则是独自行动。为了使这个比喻成立,我们还必须假设 Glenn 能通过某种方式得知哪些伪造的钞票被识破(也许他在 Dawn 的办公室有一个同谋,能够将这些信息转发给他)。

如果我们将伪造者和侦探视为对立的两方,那么它们确实是敌对的。这也是原始论文中对生成对抗网络(GANs)主题的表述方式(Goodfellow 等人,2014)。这种对抗性视角并不会改变我们如何设置或训练网络,但它提供了不同的思考方式(Goodfellow 2016)。

对抗性一词来源于一种叫做博弈论的数学分支(Watson 2013),在这种理论中,我们将判别器和生成器视为在一场欺骗与识别的博弈中的对手。

博弈论的研究领域致力于研究竞争者如何最大化他们的优势(Chen, Lu, 和 Vekhter 2016; Myers 2002)。我们在 GAN 训练中的目标是使每个网络达到其最高能力,尽管其他网络能够抵挡它。博弈论学者称这一状态为Nash 均衡(Goodfellow 2016)。

现在我们已经知道了训练的基本技巧,接下来我们看看如何实际构建一个鉴别器和一个生成器。

实现 GAN

当我们谈论 GAN 时,我们讨论的是三个不同的网络:鉴别器、生成器以及生成器和鉴别器的组合。我们在图 22-7 中看到了这两种结构。在(a)部分只有鉴别器,在(b)到(d)部分则是生成器和鉴别器结合的结构。正如我们稍后将看到的那样,当训练完成后,我们要生成新数据时,会丢弃鉴别器,仅使用生成器。

通常可以从上下文中明确知道正在讨论的是哪一个网络。如前所述,当提到 GAN 时,通常指的是训练后的生成器,在它经过对抗过程的训练之后。GAN 这个词在该领域使用非常灵活。它既可以指我们刚才描述的训练方法,也可以指训练过程中使用的生成器-鉴别器组合网络,或者指训练后我们得到的独立生成器。通常人们会说他们将“训练一个 GAN”,意思是他们将使用 GAN 方法来训练一个生成器,随后这个生成器本身可能会被称作 GAN。听起来可能有些困惑,但通常从上下文中可以清晰地理解正确的解释。

够多的背景知识了!让我们开始构建和训练一个 GAN。

鉴别器

鉴别器是三种模型中最简单的,如图 22-8 所示。它以一个样本作为输入,输出一个单一值,表示网络对输入是来自训练集而非伪造尝试的置信度。

f22008

图 22-8:鉴别器的框图

对于我们如何制作鉴别器并没有其他限制。它可以是浅层的或深层的,并且可以使用任何类型的层:全连接层、卷积层、递归层、转换器等等。在我们伪钞制作的例子中,输入是钞票的图像,输出是一个实数,反映网络的判断。值为 1 意味着鉴别器确定输入是一个真实的钞票,值为 0 意味着鉴别器确定它是伪钞。值为 0.5 意味着鉴别器无法判断。

生成器

生成器接受一堆随机数作为输入。生成器的输出是一个合成样本。其框图见图 22-9。

f22009

图 22-9:生成器的框图

与判别器一样,生成器的构建没有任何约束。它可以是浅层的或深层的,使用我们喜欢的任何类型的层。

在我们伪造货币的例子中,输出将是一个图像。

图 22-9 中生成器的损失函数本身是无关紧要的,在某些实现中,我们甚至从未定义过一个。正如我们在下一节中将看到的那样,我们通过将生成器与判别器连接起来训练生成器,因此生成器从组合网络的损失函数中学习。

一旦我们的 GAN 完全训练好,我们通常会丢弃判别器,只保留生成器。毕竟,判别器的目的是训练生成器,以便我们可以用它来生成新数据。当生成器与判别器断开连接时,我们可以使用生成器无限制地生成新数据,供我们随意使用。

现在我们有了生成器和判别器的框图,我们可以更仔细地查看实际的训练过程。在那之后,我们将查看两个网络的实现。然后我们将训练它们,看看它们的表现如何。

训练 GAN

现在让我们来看一下如何训练我们的 GAN。我们将扩展图 22-7 中学习轮次的四个步骤,展示更新应用的位置。

我们的第一步是寻找假阴性,所以我们将真实的钞票输入到判别器中,如图 22-10 所示。在这一步,我们完全不涉及生成器。此时,误差函数的设计是,如果判别器错误地将真实钞票分类为假币,它会受到惩罚。如果发生这种情况,误差将驱动通过判别器的反向传播步骤,更新其权重,使其更擅长识别真实钞票。

f22010

图 22-10:在假阴性步骤中,判别器连接到一个误差函数,如果它将真实钞票错误地分类为假币,就会受到惩罚。

第二步是寻找真实的负样本。在这一步,我们将生成器的输出直接连接到判别器的输入,形成一个大模型。我们从输入随机数到生成器开始,如图 22-11 所示。生成器的输出是伪钞,然后将其输入到判别器中。误差函数的设计是,如果这个伪钞被正确识别为假币,则它的值较大,意味着生成器被抓到制造伪钞。

f22011

图 22-11:在真实负样本步骤中,随机数输入到生成器,生成一个伪钞。如果判别器将其标记为假币,我们就通过判别器推送梯度,但只更新生成器。

在图 22-11 中,我们将判别器的更新步骤灰显处理,然而标记为“更新”的箭头显然还是通过了判别器。这里发生的事情是,我们的“更新”箭头同时包含了反向传播和优化。回想一下,反向传播计算每个权重的梯度,但并不会实际改变任何东西。真正更新权重的是优化步骤,它基于梯度来更新权重。在图 22-11 中,我们希望对生成器进行优化,这意味着我们需要找到它的梯度。但由于反向传播是从网络的末端计算到起始端的,找到生成器的梯度的唯一方法是首先计算判别器的梯度。尽管我们在两个网络中都计算了梯度,但我们只会改变生成器中的权重。我们说判别器是冻结的,意味着它的权重不会被改变,即使我们计算了它们的梯度。这确保了在任何给定时刻,我们只训练生成器或只训练判别器。

改进生成器的权重可以让它更好地欺骗判别器。

现在我们寻找假阳性。我们生成一个假钞,并在判别器将其分类为真实时惩罚它,如图 22-12 所示。

f22012

图 22-12:在假阳性步骤中,我们给判别器一个假钞。如果它将其分类为真实钞票,那么我们更新判别器,使其更好地识别假钞。

最后,我们重复图 22-11 中的真实负例步骤,这样每轮训练中,判别器和生成器都有两次更新的机会。

GANs 实战

够了,理论!让我们构建一个 GAN 系统并训练它。我们选择一个非常简单的例子,这样我们可以在 2D 中绘制出有意义的过程插图。

让我们把训练集中的所有样本想象成一个点云,位于某个抽象的空间中。毕竟,每个样本最终都是一个数字列表,我们可以将这些数字视为空间中的坐标,空间的维度与数字的数量相同。我们的“真实”样本集合将是属于一个二维云的点,该云具有高斯分布。回想一下第二章,高斯曲线在中心有一个大的峰值,因此我们期望大多数点位于峰值附*,随着向外扩展,点的数量会逐渐减少。每个样本都是该分布中的一个点。让我们将二维云的中心定在(5,5),并赋予其标准差为 1。图 22-13 展示了这个分布。

f22013

图 22-13:我们的起始分布是一个以(5,5)为中心、标准差为 1 的高斯峰。左图:3D 中的云。右图:一个圆圈显示二维中一个标准差位置的云,以及从这个分布中随机抽取的一些代表性点。

我们的生成器将尝试学习如何将其给定的随机数转化为看似属于此分布的点。目标是做到如此之好,以至于判别器无法分辨真实点和生成器生成的合成点。换句话说,我们希望生成器接受随机数并生成可能来自我们原始高斯分布中心(5,5)附*的随机点的输出。

仅给定一个点,如图 22-14 所示,判别器很难确定它是否是从我们的高斯分布中提取的真实样本,还是生成器创建的合成样本。

f22014

图 22-14:我们有一个单一的样本,我们想要判断它是否是从高斯分布中提取的。

我们可以通过使用第十五章中的一个老朋友:小批量(或通常称为批量)来简化判别器的任务。与其每次只运行一个样本,我们可以一次运行大量样本,通常是 32 到 128 之间的 2 的幂。给定一大堆点,判定它们是否来自我们的高斯云就容易多了。图 22-15 展示了生成器可能生成的几组点。我们希望判别器能轻松意识到这些点不太可能来自我们原始的分布。

f22015

图 22-15:一些不太可能是从我们的起始高斯分布中随机挑选的结果的点集

我们希望我们的生成器生成的点更像图 22-13 右侧的那些点,而不是图 22-15 中的任何点。我们还希望判别器能够将图 22-15 中的点集分类为假样本,因为这些点不太可能来自原始的高斯数据。

构建判别器和生成器

让我们为这个问题构建判别器和生成器网络。由于我们的原始分布(二维高斯分布)非常简单,我们的网络也可以相应地简单。

不过,在深入讨论具体的机制之前,先提醒一下。GANs 以其不稳定和敏感著称。它们 notoriously 很难训练(Achlioptas 等人,2018)。生成器或判别器的架构中轻微的变化,甚至某些超参数(如学习率或丢弃率)的微小调整,都可能将一个几乎无用的 GAN 转变为一个明星模型,反之亦然。更糟糕的是,我们不仅要训练一个网络,而是两个,并且还需要让它们协同工作,因此需要搜索和微调的超参数选择数量可能会变得让人不堪重负(Bojanowski 等人,2019)。因此,在我们开发一个 GAN 时,至关重要的是使用我们想要学习的特定数据进行实验,并尽快找到一个好的设计和合适的超参数。这通常意味着我们需要通过一些小的实验来尝试训练数据中的小片段,同时寻找合适的网络和超参数。

在接下来的讨论中,我们跳过了许多失败的尝试和表现不佳的模型。相反,我们将直接讨论我们发现对这个数据集有效的模型。通过进一步的更改,或者可能只是对一些细节进行小的调整,我们有可能显著提高我们展示的架构(即使它们能更快、更准确地学习)。

我们从一个简单的生成器开始,如图 22-16 所示。

f22016

图 22-16:一个简单的生成器。它输入四个随机数并计算一个(x,y)对。

模型输入四个随机值,这些值是从 0 到 1 的范围内均匀选择的。我们从一个包含 16 个神经元的完全连接层开始,并使用带泄漏的 ReLU 激活(回想一下第十三章,带泄漏的 ReLU 像普通的 ReLU 一样,但对于负值,它不是返回 0,而是将其缩放为一个小数,这里是 0.1)。

接下来是另一个完全连接的层,只有两个神经元,并且没有激活函数。这就是生成器的全部。生成的两个值是一个点的 x 和 y 坐标。

这两层只有 18 个神经元和 54 个权重,它们要完成的任务其实相当复杂。我们希望它们学会如何将一组四个均匀分布的随机数转换为一个二维点,这个点本应是从一个均值为(5,5),标准差为 1 的高斯分布中抽取出来的,但我们什么都不告诉它关于这个目标的信息。我们只在它生成的一小批点与我们想要的目标不匹配时告知它,然后让神经元自己去弄明白它哪里出了问题,以及如何纠正。

我们的判别器见图 22-17。

f22017

图 22-17:一个简单的判别器。它输入一个(x,y)点并告诉我们它是真的还是假的。

这从两个与生成器开始部分相同的层开始。每一层都是一个包含 16 个神经元的全连接层,并使用泄漏 ReLU 激活函数。最后是一个包含 1 个神经元并带有 sigmoid 激活函数的全连接层。输出是一个单一的数字,表示网络对输入是否来自与训练数据相同数据集的信心。

最后,我们将生成器和判别器组合在一起,形成组合模型,有时被称为生成器-判别器。图 22-18 展示了这种组合。

f22018

图 22-18:将生成器和判别器组合在一起。

由于生成器在其输出中呈现一个(x,y)对,判别器在其输入中接收一个(x,y)对,因此这两个网络完全匹配。生成器的输入是一组四个随机数,而判别器的输出告诉我们生成器创建的点是否来自训练集的分布。

需要注意的是,图 22-18 中标记为“生成器”和“判别器”的模型并不是图 22-16 和图 22-17 中的模型的副本,而实际上它们是完全相同的模型,只是一个接一个地连接在一起,组成了一个大模型。换句话说,只有一个生成器模型和一个判别器模型。当我们构建图 22-18 中的组合模型时,我们只是将这两个现有模型链接在一起。现代深度学习库允许我们通过共享组件构建多个模型,正是为了这种应用。使用这些不同配置中的相同模型是有意义的,因为组合模型需要使用生成器和判别器的最新版本。

训练我们的网络

当我们使用图 22-18 中的组合模型训练生成器时,我们不希望同时训练判别器。我们在图 22-11 中看到过这种情况,当时我们在更新步骤中将判别器灰显。我们需要通过判别器进行反向传播,因为它是网络的一部分,并帮助为生成器创建梯度,但我们只会对生成器中的权重应用更新步骤。

请记住,我们希望交替训练判别器和生成器。如果我们对图 22-18 中的整个网络应用反向传播,那么我们会同时更新判别器和生成器中的权重。因为我们希望以大致相同的速率训练这两个模型,并且我们知道我们将单独训练判别器(因为它也需要在真实数据上进行训练),所以我们希望告诉我们的库只更新生成器中的权重而不是判别器。

控制是否更新某一层权重的机制是与库相关的,但通常来说,它们使用像冻结锁定禁用这样的术语来防止在特定层上进行更新。然后,当我们训练判别器时,如果我们希望这些层能够学习,就可以解冻解锁启用更新。

总结训练过程时,我们从训练集中的一个小批量点开始。然后,我们按照图 22-7 中的四阶段过程,交替训练判别器和生成器。

测试我们的网络

让我们来看一些结果。为了训练我们的 GAN,我们通过从起始的高斯分布中随机抽取 10,000 个点来创建训练集。然后,我们使用 32 个点的小批量训练网络。将所有 10,000 个点通过系统处理一次就是一轮训练。

第 1 到第 13 轮的结果见于图 22-19。

f22019

图 22-19:我们简单的 GAN 在运行中。蓝色点是原始数据集,橙色点是生成器产生的。图表的阅读顺序是从左到右,从上到下。第 0 轮指的是训练的第一轮结果。

我们的初始高斯分布通过蓝色点表示,并且有一个蓝色圆圈显示其均值和标准差。GAN 学习到的分布用橙色表示,并且有一个椭圆显示生成的小批量点的中心和标准差。图表展示了从 0 到 10 轮训练后的结果,以及第 13 轮的结果。为了保持图表的清晰,我们在每个图表中只显示原始数据和生成数据的随机子集。

我们可以看到,在一轮训练后,GAN 生成的点在西南-东北方向形成了一条模糊的线,大致集中在(1,1)附*。随着每一轮训练,它们越来越接*原始数据的中心和形状。在第 4 轮左右,生成的样本超出了中心,并且变得越来越椭圆,而不是圆形。但它们最终回到并纠正了这两种特性,到第 13 轮时,匹配效果非常好。

图 22-20 展示了判别器和生成器的损失曲线。

F22020

图 22-20:我们的 GAN 损失值。它们似乎趋于并保持在一个略高于理想值 0.5 的位置。

理想情况下,判别器的损失值应该在大约 0.5 处*稳,这意味着它永远不能确定输入是来自真实数据集还是由生成器生成的。在这个小示例中,它非常接* 0.5。

DCGANs

我们之前提到过,我们可以使用任何架构来构建判别器和生成器。到目前为止,我们的简单模型由密集层组成,能够很好地处理我们的二维小型数据集。但如果我们想处理图像,那么我们可能更倾向于使用卷积层,因为正如我们在第十六章所看到的,卷积层非常适合处理图像。由多个卷积层构建的 GAN 被称为DCGAN,代表深度卷积生成对抗网络

让我们在之前章节中使用过的 MNIST 数据上训练一个 DCGAN。我们将使用 Gildenblat(2020)提出的模型。生成器和判别器见图 22-21。

f22021

图 22-21:上:用于 MNIST 的 DCGAN 判别器。下:生成器。

在这个网络中,我们在判别器中使用了显式的下采样(或池化)层,在生成器中使用了上采样(或扩展)层,而不是将它们作为卷积步骤的一部分,因为这就是网络最初提出的方式。生成器中带有圆点的圆形是一个批标准化层,它有助于防止过拟合。tanh 激活函数后的那个小 3D 方框是一个重塑层,它将从第二个全连接层输出的 1D 张量转换为 3D 张量,以便进行后续的上采样和卷积层处理。我们使用标准的二元交叉熵损失函数和设置了学习率为 0.0005、动量为 0.9 的 Nesterov SGD 优化器进行训练。

生成器中的第二个密集层使用了 6,272 个神经元。这个数字看起来可能有些神秘,但它使得生成器和判别器能够处理相等数量的数据。判别器中第二个下采样层的输出形状是 7 × 7 × 128,即 6,272 个元素。通过给生成器中的第二个全连接层提供 6,272 个值,我们可以为其第一个上采样层提供一个相同形状的张量。换句话说,判别器的卷积阶段结束时是一个形状为 7 × 7 × 128 的张量,因此我们为生成器的卷积阶段开始提供一个形状为 7 × 7 × 128 的张量。

判别器和生成器遵循大致相同的步骤,但顺序相反。

经过一个训练周期后,生成器的结果相当难以理解,这也是我们预期的结果。图 22-22 展示了它们的样子。

F22022

图 22-22:训练一个周期后生成器的斑点图

经过 100 个训练周期后,生成器产生了图 22-23 中的结果。我们本可以训练更久,因为判别器仍然有时能够识别出生成器的输出,但这里是一个很好的停止点,因为它展示了生成器的进展。

当我们退后一步考虑这个过程时,这是一个令人吃惊的结果。请记住,生成器从未见过数据集。它完全不知道 MNIST 数据是什么样子。它所做的仅仅是生成 3D 张量的实数,然后收到反馈,告诉它这些张量中的值有多好或多差。随着时间的推移,它生成了看起来像数字的张量。不知怎么的,生成器设法找到了将随机数转化为可识别数字的方法。真是太神奇了。虽然有些错误,但大多数数字都是容易辨认的。

这就总结了我们对 GAN 的基础讨论。

f22023

图 22-23:经过 100 次迭代训练后,深度卷积 GAN 在 图 22-21 上的输出结果,使用的是 MNIST 数据集

在继续之前,值得回顾一些实践建议。我们之前提到过,GAN 对其特定架构和训练变量非常敏感。一篇著名的论文研究了 DCGAN,发现了一些经验法则,似乎能带来不错的结果(Radford、Metz 和 Chintala 2016)。像往常一样,实验是成功的关键。小的改变往往决定了一个 GAN 是学习高效,还是学习缓慢,甚至根本不学习。

挑战

也许在实践中使用 GAN 的最大挑战是它们对结构和超参数的敏感性。进行一场猫捉老鼠的游戏要求双方始终保持密切匹配。如果判别器或生成器的性能比另一个提高得太快,另一个就永远追赶不上。如前所述,找到这些值的正确组合对于从 GAN 中获得良好的表现至关重要,但找到这种组合可能具有挑战性(Arjovsky 和 Bottou 2017;Achlioptas 等 2017)。通常建议遵循前面给出的一些经验法则,作为训练新 DCGAN 时的良好起点。

GAN 的一个理论问题是,我们目前没有证明它们会 收敛。回想一下我们在第十三章提到的单层感知机,它找出了两个线性可分数据集之间的分割线。我们可以 证明,只要有足够的训练时间,感知机总会找到这个分割线。但对于 GAN,这样的证明是不存在的。我们能说的只是,许多人已经找到了至少能让部分 GAN 正常训练的方法,但除此之外并没有保证。

使用大样本

当我们尝试训练生成器生成大图像(如 1000×1000 像素)时,GAN 的基本结构可能会遇到问题。计算问题在于,面对如此大量的数据,判别器很容易将生成的假图像与真实图像区分开。试图同时修复所有这些像素可能导致误差梯度,使得生成器的输出几乎随机地变化,而不是朝着与输入匹配的方向前进(Karras 等,2018)。除此之外,还有一个实际问题,那就是找到足够的计算能力、内存和时间来处理大量的大样本。回想一下,每个像素都是一个特征,因此每张边长为 1000 像素的图像有一百万个特征(如果是彩色照片则有三百万个)。

因为我们希望最终的高分辨率图像能够经得起审查,所以我们需要使用一个大型的训练集。处理这些巨型图像的大量数据所需的时间会很快累积。即使是快速的硬件,也可能无法在我们有限的时间内完成任务。

构建大图像的实用方法被称为渐进式 GANProGAN)(Karras 等,2018)。要开始使用这种技术,首先将训练集中的图像调整为各种较小的尺寸,例如一边 512 像素,然后是 128 像素,接着是 64 像素,依此类推,直到 4 像素一边。然后构建一个小型的生成器和判别器,每个网络只有几层卷积。使用 4×4 像素的图像训练这些小型网络。当它们表现得很好时,向每个网络的末端添加几层卷积,并逐渐融入它们的贡献,直到网络能够很好地处理 8×8 像素的图像。然后再向每个网络的末端添加更多的卷积层,并用 16×16 像素的图像训练它们,依此类推。

这样,生成器和判别器能够在它们的训练过程中不断进步。这意味着,当我们逐渐训练到 1024 像素一边的全尺寸图像时,我们已经有一个能够很好地生成和判别 512 像素一边图像的 GAN。我们不需要在大图像上进行太多额外的训练,直到系统也能很好地处理它们。与从一开始就只使用全尺寸图像进行训练相比,这个过程所需的时间要少得多。

模式崩溃

GAN 有一种有趣的方式,可以利用我们训练中的漏洞。记住,我们希望生成器能够学会欺骗判别器。它可能会以几乎对我们没有用的方式成功地完成这一任务。

假设我们试图训练我们的 GAN 生成猫的图片。假设生成器设法创建一张被鉴别器接受为真实的猫图像。然后一个狡猾的生成器可以每次只是产生那张图像。无论我们使用什么样的噪声输入值,我们总是得到那张图片。鉴别器告诉我们,它收到的每张图片都可能是真实的,所以生成器已经完成了它的目标并停止学习。

这是神经网络找到的另一个为我们提供我们要求的但不一定是我们想要的狡猾解决方案的例子。生成器确实完成了我们要求的事情,因为它将随机数转化为鉴别器无法区分的全新样本。问题在于生成器生成的每个样本都是相同的。这是一种非常狡猾的成功。

这种反复产生一个成功输出的问题称为模态崩溃(请注意,第一个词是模态,发音为“mode′-ull”,指的是一种模式或工作方式,并非“模型”)。如果生成器陷入只有一个样本(在这种情况下是一张猫的图片)的模式,这种情况被描述为完全模态崩溃。更常见的情况是系统产生相同的少数几个输出或它们的轻微变化。这种情况称为部分模态崩溃

图 22-24 显示了我们的 DCGAN 经过三个训练周期使用一些选择不当的超参数后的运行情况。很明显,系统正在向一个模式崩溃的方向崩溃,输出一些类型 1 的情况比其他任何情况都多。

有一些方案可以解决这个问题。也许最好的建议是从使用数据的小批次开始,就像我们之前做的那样。然后我们可以扩展鉴别器的损失函数,加入一些额外的项来衡量在该小批次中产生的输出的多样性。如果输出落入几个组,其中它们全部相同或几乎相同,鉴别器可以对结果分配较大的错误。生成器因此多样化,因为这个动作减少了错误(Arjovsky, Chintala 和 Bottou 2017 年)。

使用生成数据进行训练

GANs 最常见的用途是训练一个欺骗鉴别器的生成器。然后我们丢弃鉴别器,留下一个能够创建尽可能多新数据的生成器,所有这些数据看起来都来自原始数据集。因此,我们可以创建无限数量的新猫图像、帆船图像、口头短语或木火的烟雾喷发。

f22024

图 22-24:在仅经过三次训练周期后,这个 DCGAN 显示出明显的模态崩溃迹象。

使用生成的或合成的数据来训练另一个神经网络可能会很有诱惑力。毕竟,庞大的数据集正是我们训练神经网络所需要的。然而,这是一种非常危险的做法,因为我们训练的生成器很少是完美的。一个问题是,很难制作出足够健壮的判别器,以察觉生成器输出中的每一个细节。生成器的输出可能总是以某种方式轻微失真,而判别器未能注意到,或者它只赋予了一个非常低的惩罚。另一个问题是,生成器的输出可能是不完整的。正如我们在模式崩塌的例子中所看到的,生成器的结果可能无法涵盖所有输入的范围。例如,当被要求生成某位艺术家风格的新画作时,生成器可能总是生成风景画、肖像画或静物画,即使该艺术家的作品涵盖了更广泛的主题。

完全捕捉可能出现的每个问题是非常困难的。尽管我们尽力构建一个完美的生成器,它总是能找到另一种巧妙的方式来满足我们期望的标准(由判别器表达),同时生成的数据并不如我们所希望的那样多样化或逼真。另一个问题是,我们的标准本身往往并不像我们想象的那样清晰或广泛。

简而言之,生成器的输出可能包含错误和偏见,这些错误和偏见可能会绕过判别器。如果我们用这些数据训练一个新的系统,它将继承这些错误和偏见,而我们可能完全没有意识到这些问题。这些差异可能是微妙的,但它们仍然可以在实际中影响结果。这可能会导致一种危险的局面,我们认为自己已经训练了一个强健的神经网络,能够做出重要的决策,却没有意识到它存在盲点和偏见。当我们将训练过的网络应用于关键的安全或医疗领域,或在社会场合(如招聘面试、学校招生或银行贷款授予)中使用时,可能会因为我们未意识到的持续性错误而做出严重有缺陷或不公*的决策。数据库中的偏见、错误、偏见、错误判断以及其他常见问题,成为生成器创建新数据的内在基础。结果是一个自我延续、自我实现,但却错误的系统。我们可以用一个简单的信条来总结这一点:偏见输入,偏见输出

简而言之,使用优秀数据训练神经网络仍然可能产生有缺陷的结果。用有缺陷的数据训练网络则可能产生更加严重的缺陷。一般来说,通常应该抵制使用合成数据或生成数据来训练网络的诱惑。

总结

在本章中,我们看到如何将生成对抗网络(GAN)构建为两个较小的部分。生成器学习如何创建与给定数据集中的数据相似的新数据,而判别器学习如何将生成器的输出与给定数据集中的真实数据区分开来。它们在训练过程中相互学习,提升各自的技能。当训练成功完成时,判别器无法可靠地区分合成数据和真实数据。此时,我们通常会丢弃判别器,使用生成器进行任何需要生成大量新数据的应用。

我们看到,训练是交替进行的步骤,以便生成器和判别器大致以相同的速度学习。然后,我们构建了一个简单的 GAN,使用全连接层学习如何在二维空间中生成数据点,接着构建了一个卷积 GAN,学习如何从 MNIST 数据集中生成新的图像数据。

由于 GAN 的训练通常非常困难,我们讨论了一些卷积 GAN 的经验法则,这些法则通常能帮助我们顺利开始。我们看到,通过在训练过程中逐步增加大小,我们可以生成较大的输出;同时,我们还看到,使用小批量数据可以避免模式崩溃现象,在这种情况下,生成器总是生成相同的输出(或少数几个输出)。

最后,我们简要讨论了使用合成数据进行训练的风险。

第二十三章:创意应用

我们已经到了本书的最后。在我们结束之前,让我们放松一下,玩得开心一点。在这一章中,我们将探讨一些使用神经网络创造艺术的创意方式。我们将介绍两个基于图像的应用:深度梦境,它将图像转化为狂野的迷幻艺术,和神经风格迁移,它允许我们将照片转化为看起来像是不同艺术家风格的画作。在最后,我们简要介绍一下文本生成,并使用深度学习生成更多本书内容。

深度梦境

在深度梦境中,我们使用了一些发明出来的概念,这些概念最初是为了帮助我们可视化卷积网络中的滤波器,但我们用它们来创造艺术。最终的结果是,我们修改图像以激活不同的滤波器,使得图像爆发出迷幻的图案。

刺激滤波器

在第十七章中,我们创建了卷积神经网络中滤波器的图像或可视化。深度梦境和风格迁移都建立在这种可视化技术的基础上,因此让我们更仔细地看一下。我们可以通过再次使用 VGG16 来具体说明,就像在第十七章中那样(Simonyan 和 Zisserman 2020),尽管我们也可以替换为任何卷积神经网络图像分类器。我们唯一感兴趣的是卷积阶段,因此尽管我们会使用第十七章中描述的整个网络,但本章中的图示只显示卷积层和池化层,如图 23-1 所示。

f23001

图 23-1:VGG16 的简化示意图,仅显示卷积层和池化层

我们省略了 VGG16 的最后几个阶段,因为它们的作用是帮助网络预测输出的正确类别。在这个应用中,我们不关心网络的输出。我们唯一感兴趣的是将一张图片输入网络,以便卷积层中的滤波器对其输入进行评估。我们的目标是修改一张初始图片,使其尽可能激活某些选择的层。例如,如果中间的几个像素变得更暗,那可能会让寻找眼睛的滤波器稍微有些反应。我们的目标是修改这些像素,使它们激活那个滤波器越来越强烈,也就是说,它们看起来越来越像眼睛。

我们不需要任何新工具来完成这个。我们只需选择我们想要最大化的滤波器输出。我们可以选择一个滤波器,或者从网络的不同部分选择多个滤波器。我们选择使用哪些滤波器完全是个人的和艺术性的。通常,我们会四处尝试,测试不同的滤波器,直到看到我们的输入图像以我们喜欢的方式发生变化。

让我们来看一下步骤。假设我们从三个不同的层中各选一个滤波器,如图 23-2 所示。我们首先向网络提供一张图片,网络会对其进行处理。

我们从我们选择的第一个滤波器中提取特征图,将其所有值加起来,并通过乘以我们选择的权重来确定这个总和的影响力。尽管我们使用了“权重”这个词,但这并不是网络内部的权重。它只是一个我们用来控制每个滤波器在深度梦境过程中的影响的值。我们将其他选择的滤波器加总并加权。现在,我们将这些结果加起来。这给了我们一个单一的数字,告诉我们我们选择的滤波器对输入图像的响应强度,且该强度是根据我们希望赋予每一层滤波器的影响力来加权的。我们称这个数字为多滤波器损失,或者多层损失

f23002

图 23-2:深度梦境算法使用由多层构建的损失

现在进入棘手的部分:多滤波器损失变成了网络的“误差”。在前几章中,我们使用误差来驱动反向传播,计算网络所有权重的梯度,从最后一层开始,逐步向后推导到第一层。然后,我们使用这些梯度来修改网络的权重,以最小化误差。但在这里,我们不是这样做的。相反,我们希望误差(滤波器响应)尽可能大。而我们不想通过改变网络来实现这一点,因为我们并没有训练它。我们将冻结网络,使其权重无法改变。相反,我们将修改像素本身的颜色。

所以,从这个误差开始,我们像往常一样使用反向传播来找到网络中所有权重的梯度,但当我们到达第一个隐藏层时,我们再向后退一步,来到输入层,这里包含了像素本身。然后,我们像往常一样使用反向传播来找到像素的梯度。毕竟,改变输入像素会导致网络计算的值发生变化,从而导致我们的误差发生变化。就像我们可以使用反向传播来学习如何改变网络的权重以减少误差,在典型的训练设置中一样,我们也可以使用相同的反向传播算法来找出如何改变像素值以增加这个误差。

现在,像往常一样,我们应用优化步骤。由于我们没有进行训练,网络是被冻结的,我们不会触及网络的权重。但我们确实使用像素的梯度来修改它们的颜色值,以便它们最大化误差,或者更强烈地激发我们选择的滤波器。

结果是,像素的颜色会稍微改变,以使滤波器响应更强,从而产生更大的误差,我们使用这个误差来找到像素的新梯度,使它们更强烈地激发滤波器,如此循环往复,每次我们重复这个循环,图像都会发生越来越大的变化。

由于这是一个艺术过程,我们通常在每次更新后(或者每几次更新后)查看输出,并在看到自己喜欢的结果时停止。

运行深度梦境

让我们让这个算法发挥作用,以图 23-3 中的青蛙为起点。

f23003

图 23-3:一只*静且深思的青蛙

图 23-4 显示了使用我们通过反复试验选择的一些滤镜(及其权重)从青蛙图像中生成的一些“梦想”。

f23004

图 23-4:从起始青蛙图像(左上角)开始的一些深度梦想结果

在图 23-4 中,我们看到很多眼睛,因为我们选择的部分滤镜响应了眼睛。如果我们选择了响应马和鞋子的滤镜,那么我们就会期望看到图像中出现大量的马和鞋子。

图 23-5 显示了从一张狗的图像开始的结果。图像的变化主要体现在更细腻的纹理,因为这张狗的图像每边大约有 1000 像素,是网络训练时使用的图像的四倍多。右下角的图像使用了缩放到与网络训练数据相同大小(224 x 224 像素)的狗图像版本。

f23005

图 23-5:关于狗的深度梦想

这种技术的原始名称是 启示主义(Mordvintsev, Olah, 和 Tyka 2015),以电影 盗梦空间(Inception)为灵感,但它现在更常被称为 深度梦想。这个名字是一个富有诗意的暗示,表明网络正在“梦想”原始图像,我们得到的图像展示了网络的梦想去了哪里。深度梦想之所以流行,不仅因为它创造了奇异的图像,还因为使用现代深度学习库实现它并不困难(Bonaccorso 2020)。

这个基本算法的许多变种已被探索过(Tyka 2015),但它们只是略微触及了表面。我们可以设想一些方案,自动确定层上的权重,甚至对每层的单个滤镜应用权重。我们可以在叠加激活图之前对其进行“掩膜”,以忽略某些区域(如背景),或者我们可以对像素更新进行掩膜,这样原始图像中的某些像素在响应某一层的输出时完全不改变,而是在响应另一组层的输出时发生较大变化。我们甚至可以对输入图像的不同区域应用不同的层和权重组合。深度梦想艺术创作的方法仍有很大的探索空间。

没有“正确”或“最佳”的深度梦想方法。这是一种创造性的练习,我们根据自己的审美、直觉或天马行空的猜测来寻找让我们感兴趣的图像。很难预测任何特定的网络、层和权重组合会产生什么效果,因此这个过程奖励耐心和大量的实验。

神经风格迁移

我们可以使用深度梦境技术的一个变种来做一些了不起的事情:将一种艺术家的风格转移到另一张图像上。这个过程被称为神经风格迁移

表示风格

各种文化常常庆祝艺术家独特的视觉风格。我们将重点讨论画作。那么,是什么特征定义了一幅画的风格呢?这是一个大问题,因为“风格”可以包括一个人的世界观,这种世界观会影响他们在主题、构图、材料和工具等方面的选择。让我们仅专注于视觉外观。即使这样限制范围,精确地定义画作的“风格”仍然很困难,但我们或许可以说,它指的是如何使用颜色和形状来创造形式,以及这些形式在画布上分布和类型的方式(Art Story Foundation 2020;Wikipedia 2020)。

与其试图完善这个描述,不如看看我们是否能找到一个大致相符的东西,同时又能在深度卷积网络的层和过滤器的框架内进行形式化。

本节的目标是获取我们想要修改的图片,称为基础图像,以及我们希望匹配其风格的第二张图片,称为风格参考图像。例如,我们的青蛙可以是基础图像,任何一幅画作都可以是风格参考图像。我们希望使用这两者来创建一张新图像,称为生成图像,它将基础图像的内容以风格参考图像的风格进行表达。

为了开始,我们将做出一个看似无根据的断言。我们说,图像(例如画作)的风格可以通过观察它产生的层激活来进行表征,并找到大致以相同方式激活的层对。这个想法来源于 2015 年发表的一篇开创性论文(Gatys, Ecker, 和 Bethge 2015)。不深入细节,过程从将风格参考图像输入深度卷积网络开始。与深度梦境一样,我们忽略它的输出,而专注于卷积过滤器。

给定层中的所有激活图具有相同的大小,因此我们可以很容易地将它们进行比较。让我们从层中的第一个激活图开始(即,第一个滤波器的输出)。我们可以将其与第二个滤波器产生的激活图进行比较,并为这对激活图打分。如果这两个图非常相似(即,滤波器在相同位置触发),我们就为这一对分配一个高分;如果这两个图非常不同,我们就为这一对打低分。然后,我们将第一个图与第三个图进行比较,并计算它们的分数,接着将第一个图与第四个图进行比较,计算它们的分数,依此类推。然后,我们可以从第二个图开始,将其与层中的其他所有图进行比较。我们可以将这些结果组织成一个网格,每一侧的格子数与该层的滤波器数量相等。网格中的每个单元格的值告诉我们那一对层的分数。这个网格叫做Gram 矩阵。我们将为每一层制作一个这样的 Gram 矩阵。

现在我们可以更正式地重新陈述我们的风格概念:一张图像的风格是由所有层的 Gram 矩阵表示的。也就是说,每种风格都会产生自己特定形式的 Gram 矩阵。

让我们看看这个说法是否成立。图 23-6 展示了巴勃罗·毕加索 1907 年的一幅著名自画像。这里有大量的风格元素,例如大块的颜色和浓重的黑线。让我们将其通过 VGG16 并保存每一层的 Gram 矩阵。我们称这些为风格矩阵,并将它们保存为该图像的风格表示。

f23006

图 23-6:1907 年巴勃罗·毕加索的自画像

如果 Gram 矩阵代表风格,那么我们可以用它们来修改一张随机噪声的初始图像。我们将噪声输入图像通过网络并计算其 Gram 矩阵,我们称之为图像矩阵。如果风格矩阵确实以某种方式代表了毕加索图像的风格,那么如果我们能够改变噪声图像中像素的颜色,使得最终图像矩阵接*风格矩阵,那么这张噪声图像就应该呈现出这幅画的风格。

让我们就这么做。我们将噪声通过网络,计算每一层的图像矩阵,并将其与我们为该层保存的风格矩阵进行比较。我们将这两个矩阵之间的差异加总起来,差异越大,结果就越大。然后,我们将所有层的差异加在一起,这就是我们网络的误差。像深度梦境一样,我们使用这个误差来计算整个网络的梯度,包括起始的像素,但我们只修改像素的颜色。与深度梦境不同,我们现在的目标是最小化误差,从而改变像素的颜色,使其生成的 Gram 矩阵与风格参考的 Gram 矩阵相似。

图 23-7 显示了这个过程的结果。对于这个可视化,我们计算了每一层的误差,即所有层的矩阵差异的总和,直到包括该层为止。

f23007

图 23-7:使噪声匹配 VGG16 中的 Gram 矩阵的结果

这非常值得注意。到达 Block 3 中的三个卷积层时,我们生成的抽象图像与我们原始的风格参考在图 23-6 中非常相似。颜色的斑块展示了颜色的渐变变化。不同颜色区域之间有暗线,甚至可以看到画笔的纹理。

Gram 矩阵确实捕捉到了毕加索画作的风格。但为什么呢?令人失望的答案是没有人真正知道(Li 等,2017)。我们有不同的方式来表达 Gram 矩阵所测量的数学内容,但这并没有帮助我们理解为什么这项技术能够捕捉到我们所说的“风格”这一难以捉摸的概念。神经风格迁移的原始论文(Gatys、Ecker 和 Bethge 2015)以及后续的较为详细的论文(Gatys、Ecker 和 Bethge 2016)都没有解释作者是如何想到这个想法的,或为何它如此有效。

表示内容

在深度梦境(deep dreaming)中,我们从一张图像开始,通过改变其像素来操控它。如果我们在神经风格迁移中做同样的事情,从图像而非噪声开始,图像很快就会丢失。最小化 Gram 矩阵之间差异的效果会对输入图像产生巨大变化,使其朝我们想要的风格靠拢,但在这个过程中丧失了图像的内容。

解决这个问题的一种方法是依然从噪声开始,因为它非常有效(如图 23-7 所示),但通过添加第二个误差项来保留原始图像的本质。除了施加惩罚输入与风格参考匹配不良的风格损失(通过 Gram 矩阵的差异来衡量),我们还施加了惩罚输入与基图像(我们希望进行风格化的图片)差异过大的内容损失。通过从噪声开始并将这两个误差项加在一起(通常有不同的权重),我们使噪声中的像素发生变化,从而使它们同时更接*我们想要修改的图片的颜色和我们希望呈现的风格。

收集内容损失非常简单。我们拿到基图像,比如图 23-3 中的青蛙,并将其输入到网络中。然后我们保存每个滤波器的激活图。此后,每次我们将新图像输入到网络时,内容损失就是该输入的滤波器响应与我们从基图像中获得的响应之间的差异。毕竟,如果所有滤波器对输入的响应与起始图像相同,那么输入就是起始图像(或非常接*它)。

风格和内容的结合

总结一下,我们将风格参考输入到网络中,并在每一层之后保存每对滤波器的 Gram 矩阵。接下来,我们找到一个希望风格化的基础图像,将其输入网络,并保存每个滤波器生成的特征图。

使用这些保存的数据,我们可以创建一个风格化版本的图像。我们从噪声开始,并将其输入到网络中。整个过程的框图如图 23-8 所示。

让我们从内容损失开始。在最左侧的浅蓝色圆角矩形中,我们收集来自第一卷积层滤波器的特征图,并计算这些特征图与我们从基础图像(例如青蛙)中保存的特征图之间的差异。我们对第二卷积层的特征图也进行相同的处理。我们可以对所有层进行这种操作,但对于本图和后续示例,我们在第二层后停止(这是另一种个人选择,基于实验指导)。我们将所有这些差异或内容损失加在一起,并通过某个值对其总和进行缩放,以便控制图像内容对我们最终在输入图像的颜色上所做变化的影响程度。

f23008

图 23-8:神经风格迁移的框图

现在我们处理风格问题。对于每一层,在浅黄色的圆角矩形中,我们计算 Gram 矩阵,告诉我们每个滤波器的输出与其他滤波器输出之间的对应关系。然后,我们将这些矩阵与我们之前保存的风格矩阵进行比较。我们将所有这些差异加起来,得到风格损失,并通过某个值对其进行缩放,以便控制风格对我们修改像素颜色时的影响程度。

内容损失和风格损失的总和就是我们的误差。与深度梦想一样,我们计算整个网络的梯度,并一直反向传播到像素。再次强调,我们保持网络中的权重不变。与深度梦想不同的是,我们修改像素值以最小化这个总误差,因为我们希望输入与我们之前保存的内容和风格信息相匹配。结果是,原始噪声会慢慢变化,使其既更像原始图像,又具有风格的滤波器关系。

与深度梦想相似,使用现代深度学习库实现神经风格迁移是直接的(Chollet 2017;Majumdar 2020)。

运行风格迁移

让我们看看这个方法在实践中效果如何。我们再次使用 VGG16 作为我们的网络,并按照图 23-8 中总结的过程进行。

图 23-9 展示了九张每张都有独特风格的图像。这些是我们的风格参考。

f23009

图 23-9:九张不同风格的图像,它们作为我们的风格参考。从左到右,从上到下,它们分别是《星夜》,由文森特·梵高绘制,《米诺陶斯的沉船》,由 J. M. W. 塔纳绘制,《呐喊》,由爱德华·蒙克绘制,《坐姿女性裸体》,由巴勃罗·毕加索绘制,《1907 年自画像》,由巴勃罗·毕加索绘制,《夜鹰》,由爱德华·霍普绘制,《克罗奇中士》,由作者绘制,《睡莲:黄色与丁香》,由克劳德·莫奈绘制,以及《构成 VII》,由瓦西里·康定斯基绘制。

让我们将这些风格应用到我们老朋友——青蛙身上。图 23-10 展示了结果。

f23010

图 23-10:将图 23-9 中的九种风格应用到青蛙的照片上(顶部)

哇,这效果真棒。这些图像值得仔细观察,因为它们有很多细节。乍一看,我们可以看到每种风格参考的色彩调色板已经被转移到了青蛙照片中。但注意纹理和边缘,以及色块的形状。这些图像不仅仅是颜色变化的青蛙,或是两张图像的叠加或混合。相反,这些是不同风格下青蛙的高质量详细图像。为了更清晰地看到这一点,图 23-11 展示了每只青蛙的相同放大区域。

这些图像有显著不同,而且每一张都与其所基于的风格相匹配。

让我们看另一个例子。图 23-12 展示了将我们的风格应用到一张城镇照片上。

f23011

图 23-11:图 23-10 中九只风格化青蛙的详细信息

当我们记得这些图像最初都是从随机噪声开始时,它们显得尤为引人注目。对于每一张图像,我们将内容损失的权重设为 0.025,风格损失的权重设为 1,因此风格对像素变化的影响是内容的 40 倍。在这些示例中,少量的内容产生了很大的效果。

如图 23-10 至 23-12 所示,神经风格迁移的基本算法产生了出色的结果。这项技术在许多方面得到了扩展和修改,以提高算法的灵活性、它产生的结果类型,以及艺术家可以应用的控制范围,以创造他们想要的效果(Jing et al. 2018)。它甚至已被应用于视频和完全包围观众的球形图像(Ruder, Dosovitskiy, and Brox 2018)。

与深度梦境类似,神经风格迁移是一个通用算法,允许进行大量的变化和探索。肯定还有许多有趣且美丽的艺术效果等待被发现。

f23012

图 23-12:将图 23-9 中的九种风格应用到一张俯视的城镇照片(顶部)

生成更多本书内容

为了好玩,我们将本书第一版的文本(这一节除外)通过一个 RNN 进行了处理,该 RNN 按字生成新文本,如第十九章所讨论的。完整的文本大约包含 427,000 个单词,来自大约 10,300 个单词的词汇表。为了学习这些文本,我们使用了一个由两层 LSTM 组成的网络,每层 128 个单元。

该算法通过自回归地生成输出,根据它到目前为止生成的文本找到下一个最可能的单词,然后是下一个最可能的单词,再然后是下一个,以此类推,直到我们停止它。以这种方式生成文本就像是玩一个游戏,通过不断选择手机在你打字时建议的三到四个单词中的一个,来创建信息(Lowensohn 2014)。

这是在 250 次迭代后从输出中手动选择的一些句子。它们原封不动地包括在此,其中包括标点符号。

  • 所有红圈中的样本的响应共享两个数字,就像最后一步的底部,当它们的数字会影响输入与其类别的关系时。

  • 梯度取决于损失,它们是墙上的一些小像素。

  • 让我们看看这个三段论中不同狗的代码。

令人惊讶的是,这些几乎是有意义的!

整个句子很有趣,但一些最具娱乐性和诗意的片段出现在训练开始不久,当时系统只生成片段。以下是仅经过 10 个周期后手动选择的一些摘录,再次按照它们生成的原样呈现:

  • 一组应用,我们+信息。

  • 假设我们只有参数化。

  • 通常的怪异(我们比那更应该使用它们的方式上)。

这些大多是无序的,但从这些合成短语中,我们可以提炼出一个事实:这本书的主要目标之一肯定是“+信息”。

循环神经网络(RNN)很棒,但基于转换器的生成器更好。我们对本书当前版本(除了这一节)使用了一个中等大小的 GPT-2 生成器进行微调。以下是一些精选的输出片段,挑选它们是因为它们的创意范围(第二组似乎是图表标题)。

  • 这是被誉为人工神经元女王的神经网络。她的名字是 Christine,这一点毫不奇怪,但这确实能充分说明该领域的现状。

  • 我们可以将这些版本链接在一起,形成一个分类器的单一张量,它本质上是一个“惊喜盒子”。

  • 让我们使用一个小工具,采取捷径,使我们能够即时进行单词预测,即使我们不是在线或离线时合适的人。

  • 在这个视图中,范围内的 1 是完美的整数,而范围内的 0 是一个超现实的数字字符串。我们在第六章采用的方法是将所有的 0 视为不完整,将所有的 1 视为不完整,因为我们仍然对我们正在评估的系统有一些信息。

  • 图 10-7:一组没有标签的负图像。

  • 图 10-10:一个深度学习系统学习如何从数据集中创建和移除车牌。

总结

本章开始时,我们研究了深度梦境技术,这是一种通过操控图像来激发网络中的特定滤镜,创造出狂野、迷幻图像的方法。接着,我们探讨了神经风格迁移技术。利用这项技术,我们逐渐将随机噪声输入转变为既像输入图像,又像风格参考图像的形式,比如各种画家的作品。最后,我们使用了一个小型的 RNN 和一个 Transformer 来从本书的手稿中生成新的文本。生成看似熟悉或合理的新文本是件有趣的事!

最后的思考

本书只涉及了深度学习的基本概念。这个领域正在以惊人的速度发展。每年新的突破似乎都打破了对计算机能够识别、分析、预测和合成的所有预期。

仅仅跟上新工作的步伐就可能是一份全职工作。保持对新进展的关注的一个方法是访问一个名为 arXiv(读作“archive”)的网站,网址是arxiv.org/,更具体地说,是访问机器学习板块:arxiv.org/list/cs.LG/recent*。这个网站发布新论文的预印本,在论文正式出现在期刊和会议之前。但即使是关注 arXiv,也可能让人感到信息量过大,因此,许多人使用 arXiv Sanity Preserver 网站(www.arxiv-sanity.com)和 Semantic Sanity 项目(s2-sanity.apps.allenai.org/cold-start)。这两个网站帮助过滤文库,只展示涉及特定关键词和思想的论文。

本书重点介绍了深度学习的技术背景。需要记住的是,当我们以可能影响人的方式使用这些系统时,我们需要考虑的远不止算法本身(O'Neil 2016)。由于深度学习系统的高效性,它们被广泛且迅速地部署,通常缺乏监管或对其对社会影响的考虑。

深度学习系统如今被广泛应用于影响或决定工作录用、学校录取、监禁判决、个人和商业贷款,甚至是医学检测的解读。深度学习系统控制着人们在新闻和社交媒体中的信息流,选择展示的内容不是为了建立一个健康且信息丰富的社会,而是为了增加提供这些信息流的组织的利润(Orlowski 2020)。在“智能音响”和“智能显示器”中(这些设备也配有麦克风和摄像头),深度学习系统不断监听和监视人们在家中的活动,有时将捕捉到的数据发送到远程服务器进行分析。过去,文化上曾经害怕这种持续的监视,但现在人们愿意为这些设备付费,并将它们放置在以前属于私人空间的地方,如家中和卧室里。深度学习被用于在学校中挑出可能有问题的孩子,评估在边境检查时回答问题的人的诚实性,并通过识别面部特征来识别抗议活动和其他集会中的个体。这些算法所犯的错误可能从令人烦恼到彻底改变人生。即使结果不完全错误,这些系统也越来越多地做出深远的决定,影响着我们的公共和私人生活。

深度学习系统的效果取决于其训练和算法的质量,一次又一次,我们发现训练数据中的偏见、成见和明显错误被算法继承并加以强化。此类系统远远无法达到我们在与人类和其他生物打交道时所期待的准确性和公*性。我们的算法完全缺乏同情心和怜悯心。它们没有对特殊情况的理解,也无法理解它们的决策可能带来的喜悦或痛苦。它们无法理解爱、善良、恐惧、希望、感恩、悲伤、慷慨、智慧,或我们彼此之间所珍视的其他品质。它们无法崇拜、哭泣、微笑、悲痛、庆祝或后悔。

深度学习为我们个人和社会提供了巨大的希望。但任何工具都可以被当作武器使用,利益仅仅归工具的拥有者,而对受影响的人则带来不利。机器学习系统往往是有效隐形的,因此任何系统性的错误可能会长期未被发现。即使问题被揭露,追究受益于这些系统的组织的责任也需要巨大的社会和政治行动,甚至更大的努力才能带来改变。

机器学习系统的另一个危险在于它们对大量训练数据的贪婪需求。这为那些专门收集、整理和出售曾经私密的个人信息的组织创造了市场,这些信息涉及个人的友谊和家庭关系,以及他们喜欢去哪里和什么时候旅行,喜欢吃什么食物,正在服用什么药物,以及他们的 DNA 揭示了什么。这些数据可以用来骚扰、威胁、恐吓甚至伤害个人。

对大量数据的需求也意味着,随着一个组织的规模变大(且通常责任感变弱),它能收集的数据就越多,其算法也就越强大,决策的影响力也随之增加,这意味着它们可以收集更多的数据,从而在反馈循环中巩固组织的权力。这样系统中的每一个不完美都会被放大,并且由于其规模庞大,负面影响可能完全被运营这些系统的人忽视。通常,唯一能够与这些组织竞争的将是其他同样规模的组织,它们提供的系统包含着自己的巨大数据库,内含着各自的偏见和错误,最终导致我们今天所熟悉的偏见巨头之间的对抗。在没有强有力和严格执行的控制和监管的情况下,这种持续和日益集中的权力在自由社会中是一股危险的力量。不幸的是,今天这样的控制几乎无法看到。

深度学习已经产生了能够让任何人的外貌——从娱乐圈人士到政治家——都可以被用来制作出看似真实的图片、音频和视频,仿佛那个被操控的人说或做了任何操控软件的人所希望的事情。社会已经开始依赖录制的音频、照片和视频来执行合同、调解冲突、赞美或羞辱公众人物、影响选举,以及作为法庭证据。这个时代即将结束,我们将回到一个没有可靠照片、录音和视频的时代,那时流言、记忆和观点将取代客观的历史记录。如果没有可靠的音频和视觉证据来记录某人实际说了什么或做了什么,那么在场中最响亮、最富有或最有说服力的声音将决定公共舆论、选举和法庭中的许多结果,因为客观事实将越来越难以被找到或信任。

深度学习是一个令人着迷的领域,我们才刚刚开始理解它将如何影响我们的文化和社会。学习算法无疑会继续扩展其范围和影响力。它们有机会通过帮助人们更幸福、使社会更加公*和支持性强、创造更健康和多样化的个人、社会、政治和物理环境,带来巨大的好处。即使这些可能会削减企业利润或政府控制,我们仍然应该努力追求这些积极的成果。

我们应当记住,像使用我们所有工具一样,深度学习也应该用来激发人类的最佳潜力,让世界变得对每个人来说更加美好。

第二十四章:参考文献

在可能的情况下,我倾向于使用在线可访问的参考文献,这样你可以立即访问它们。例外情况通常是书籍和其他印刷材料,但偶尔我也会包括一些重要的在线参考文献,即使它们被放置在付费墙后面。这里的每个链接在写作时都是有效的。但互联网是瞬息万变的,这些链接肯定会发生变化,或者干脆停止工作。如果你发现某个链接无法访问,我建议你使用搜索引擎查找你想要定位的参考文献的标题和/或作者。通常你会发现,它可能只是搬到了新位置。如果它已消失,你可以尝试在archive.org/web/的 Wayback Machine 上找到保存的副本。你还可以尝试使用 Google 搜索引擎查找该参考文献,它有时会提供一个已不再在线的页面的缓存版本。

如果你是以电子书的形式阅读本书,你可以直接点击任何链接以访问。如果你是纸质版阅读,可以将这些链接输入你最喜欢的浏览器,但更好的方法是前往 No Starch Press 网站上该章节的在线版本,那里链接是有效的:nostarch.com/deep-learning-visual-approach/

第一章

Bishop, Christopher M. 2006. 模式识别与机器学习。纽约:Springer-Verlag 出版社。可在docs.google.com/viewer?a=v&pid=sites&srcid=aWFtYW5kaS5ldXxpc2N8Z3g6MjViZDk1NGI1NjQzOWZiYQ获取。

Goodfellow, Ian, Yoshua Bengio, and Aaron Courville. 2017. 深度学习。剑桥,MA:MIT 出版社。可在www.deeplearningbook.org/获取。

Saba, Luca, Mainak Biswas, Venkatanareshbabu Kuppili, Elisa Cuadrado Godia, Harman S. Suri, Damodar Reddy Edla, Tomaž Omerzu, John R. Laird, Narendra N. Khanna, Sophie Mavrogeni 等. 2019. “深度学习在放射学中的现状与未来。” 欧洲放射学杂志 114(5 月):14-24。

第二章

Anscombe, F. J. 1973. “统计分析中的图形。” 美国统计学家 27,第一期(2 月):17-21。

Banchoff, Thomas F. 1990. 超越第三维:几何学、计算机图形学与高维空间。科学美国人图书系列。纽约:W. H. Freeman 出版社。

Brownlee, Jason. 2017. “如何在 Python 中计算机器学习结果的 Bootstrap 置信区间。” 机器学习精通(博客)。最后更新于 2020 年 8 月 14 日。machinelearningmastery.com/calculate-bootstrap-confidence-intervals-machine-learning-results-python/

Efron, Bradley, 和 Robert J. Tibshirani. 1993. 引导法介绍。佛罗里达州博卡拉顿:Chapman and Hall/CRC,Taylor and Francis 集团。

Matejka, Justin, 和 George Fitzmaurice. 2017. “相同统计,不同图表:通过模拟退火生成外观不同但统计相同的数据集。” 收录于 2017 年 CHI 计算机系统中的人类因素会议论文集(丹佛,CO,5 月 6–11 日):1290–94。 www.autodesk.com/research/publications/same-stats-different-graphs.

Norton, John D. 2014. “四维空间是什么样的?” 讲义。匹兹堡大学历史与哲学科学系。 www.pitt.edu/~jdnorton/teaching/HPS_0410/chapters/four_dimensions/index.html.

Teknomo, Kardi. 2015. “自助法抽样教程。” Revoledu. people.revoledu.com/kardi/tutorial/Bootstrap/index.html.

ten Bosch, Marc. 2020. “N维刚体动力学。” ACM 图形学期刊 39, 第 4 期(7 月)。 marctenbosch.com/ndphysics/NDrigidbody.pdf.

Wikipedia. 2017a. “安斯科姆四重奏。” 最后修改于 2020 年 6 月 21 日。 en.wikipedia.org/wiki/Anscombe%27s_quartet.

Wikipedia. 2017b. “随机变量。” 最后修改于 2020 年 8 月 21 日。 en.wikipedia.org/wiki/Random_variable.

第三章

Glen, Stephanie. 2014. “边际分布。” Statisticshowto.com. 2014 年 2 月 6 日。 www.statisticshowto.com/marginal-distribution/.

Jaynes, Edwin Thompson. 2003. 概率论:科学的逻辑。剑桥,英国:剑桥大学出版社。

Kirby, Roger. 2011. 小腺体,大问题。伦敦,英国:Health Press。

Kunin, Daniel, Jingru Guo, Tyler Dae Devlin, 和 Daniel Xiang. 2020. “理论可视化。” Seeing Theory. 访问于 2020 年 9 月 16 日。 seeing-theory.brown.edu/#firstPage.

Levitin, Daniel J. 2016. 谎言指南:信息时代的批判性思维。纽约:Viking Press。

Walpole, Ronald E., Raymond H. Myers, Sharon L. Myers, 和 Keying E. Ye. 2011. 工程与科学家的概率与统计学,第 9 版。纽约:Pearson。

Wikipedia. 2020. “灵敏度与特异性。” 最后更新于 2020 年 10 月 20 日。 en.wikipedia.org/wiki/Sensitivity_and_specificity.

第四章

Cthaeh, The. 2016a. “贝叶斯定理:非正式推导。” 概率世界。2016 年 2 月 28 日。 www.probabilisticworld.com/anatomy-bayes-theorem/.

Cthaeh, The. 2016 年 b. “通过贝叶斯定理计算硬币偏差。” 概率世界。2016 年 3 月 21 日. www.probabilisticworld.com/calculating-coin-bias-bayes-theorem/.

Genovese, Christopher R. 2004 年. “贝叶斯分析教程(神经成像领域)。” 论文发表于加利福尼亚大学洛杉矶分校纯数学与应用数学研究所会议,2004 年 7 月 20 日. www.stat.cmu.edu/~genovese/talks/ipam-04.pdf.

Kruschke, John K. 2014 年. 做贝叶斯数据分析:使用 R、JAGS 和 Stan 的教程,第 2 版. 剑桥,马萨诸塞州:学术出版社.

Stark, P. B. 和 D. A. Freedman. 2016 年. “地震发生的概率是多少?” 加州大学伯克利分校统计系,技术报告 611,2016 年 10 月. www.stat.berkeley.edu/~stark/Preprints/611.pdf.

VanderPlas, Jake. 2014 年. “频率主义与贝叶斯主义:一个基于 Python 的入门教程。” 康奈尔大学,天体物理学,arXiv:1411.5018,2014 年 11 月 18 日. arxiv.org/abs/1411.5018.

第五章

3Blue1Brown. 2020 年. 3Blue1Brown 主页. 访问日期:2020 年 9 月 1 日. www.3blue1brown.com.

Apostol, Tom M. 1991 年. 微积分,第 1 卷:单变量微积分及线性代数导论,第 2 版. 纽约:Wiley.

Berkey, Dennis D. 和 Paul Blanchard. 1992 年. 微积分。波士顿:霍顿·米夫林·哈考特学校.

第六章

Bellizzi, Courtney. 2011 年. “被遗忘的历史:阿尔弗雷德·维尔与塞缪尔·莫尔斯。” 史密森学会档案馆. 2011 年 5 月 24 日. siarchives.si.edu/blog/forgotten-history-alfred-vail-and-samuel-morse.

Ferrier, Andrew. 2020 年. “生成哈夫曼树的快速教程。” Andrew Ferrier(教程)。访问日期:2020 年 11 月 12 日. www.andrewferrier.com/oldpages/huffman_tutorial.html.

Huffman, David A. 1952 年. “最小冗余编码构建方法。” 收录于IRE 会议录 40 卷,第 9 期. web.stanford.edu/class/ee398a/handouts/papers/Huffman%20-%20Min%20Redundancy%20Codes%20-%20IRE52.pdf.

Kurt, Will. 2017 年. “Kullback-Leibler 散度解释。” Probably a Probability(博客),Count Bayesie. 2017 年 5 月 10 日. www.countbayesie.com/blog/2017/5/9/kullback-leibler-divergence-explained.

Longden, George. 1987 年. “G3ZQS 对 FISTS 名字由来的解释。” FISTS CW 俱乐部. 访问日期:2020 年 9 月 1 日. fists.co.uk/g3zqsintroduction.html.

McEwen, Neal. 1997. “摩尔斯电码还是维尔电码?”电报办公室。www.telegraph-office.com/pages/vail.html

Pope, Alfred. 1887. “电报的美国发明者,特别提到阿尔弗雷德·维尔的贡献。” 世纪插图月刊 35, no. 1 (十一月)。tinyurl.com/jobhn2b

Serrano, Luis. 2017. “香农熵、信息增益和从桶中挑球。” Medium。2017 年 11 月 5 日。medium.com/udacity/shannon-entropy-information-gain-and-picking-balls-from-buckets-5810d35d54b4

Seuss, Dr. 1960. 绿鸡蛋与火腿。初学者图书。

Shannon, Claude E. 1948. “通信的数学理论。” 贝尔实验室技术期刊(七月)。people.math.harvard.edu/~ctm/home/text/others/shannon/entropy/entropy.pdf

Stevenson, Robert Louis. 1883. 金银岛。古腾堡计划。可在www.gutenberg.org/ebooks/120查阅。

Thomas, Andrew. 2017. “机器学习中的熵、交叉熵和 KL 散度简介。” 机器学习历险(博客)。2017 年 3 月 29 日。

Twain, Mark. 1885. 哈克贝里·费恩历险记(汤姆·索亚的同伴)。查尔斯·L·韦伯斯特公司。可在www.gutenberg.org/ebooks/32325查阅。

Wikipedia. 2020. “字母频率。”维基百科。最后修改日期:2020 年 8 月 31 日。en.wikipedia.org/wiki/Letter_frequency

第七章

Aggarwal, Charu C., Alexander Hinneburg, 和 Daniel A. Keim. 2001. “高维空间中距离度量的惊人表现。” 论文发表于 2001 年 1 月 4 日至 6 日在英国伦敦举行的国际数据库理论会议。bib.dbvis.de/uploadedFiles/155.pdf

Arcuri, Lauren. 2019. “如何照蛋。” The Spruce(博客)。2019 年 4 月 20 日。www.thespruce.com/definition-of-candling-3016955

Bellman, Richard Ernest. 1957. 动态规划。普林斯顿,NJ:普林斯顿大学出版社。

Domingos, Pedro. 2012. “关于机器学习的一些有用知识。” ACM 通讯 55, no. 10(十月)。homes.cs.washington.edu/~pedrod/papers/cacm12.pdf

Hughes, G. F. 1968. “统计模式识别器的*均准确性。” IEEE 信息理论学报 14, no. 1: 55–63。

Nebraska Extension. 2017. “照蛋法。”内布拉斯加州兰开斯特县内布拉斯加大学扩展中心。lancaster.unl.edu/4h/embryology/candling

Numberphile. 2017 年。“高维中的奇异球体。”YouTube。2017 年 9 月 18 日。www.youtube.com/watch?v=mceaM2_zQd8

Spruyt, Vincent. 2014 年。“维度灾难。”《傻瓜计算机视觉》(博客)。2014 年 4 月 16 日。www.visiondummy.com/2014/04/curse-dimensionality-affect-classification/

第八章

Muehlhauser, Luke. 2011 年。“机器学习与意外后果。”LessWrong(博客)。2011 年 9 月 22 日。lesswrong.com/lw/7qz/machine_learning_and_unintended_consequences/

Schneider, Jeff,和 Andrew W. Moore。1997 年。“交叉验证。”收录于使用 Vizier 1.0 的局部加权学习教程。卡内基梅隆大学计算机科学系,美国宾夕法尼亚州,1997 年 2 月 1 日。www.cs.cmu.edu/~schneide/tut5/node42.html

第九章

Bishop, Christopher M. 2006 年。《模式识别与机器学习》。纽约:施普林格出版社。

Bullinaria, John A. 2015 年。“偏差与方差,欠拟合与过拟合。”神经计算,第 9 讲(讲义),伯明翰大学,英国。www.cs.bham.ac.uk/~jxb/INC/l9.pdf

Domke, Justin. 2008 年。“正则化为什么有效?”Justin Domke 的博客(博客)。2008 年 12 月 12 日。justindomke.wordpress.com/2008/12/12/why-does-regularization-work/

Domingos, Pedro. 2015 年。《大师算法》。纽约:基础书籍。

Foer, Joshua. 2012 年。《与爱因斯坦一起月球漫步:记忆的艺术与科学》。纽约:企鹅书籍。

Macskassy, Sofus A. 2008 年。“机器学习(CS 567)笔记。”(PowerPoint 演示文稿,偏差-方差。2008 年秋季)www-scf.usc.edu/~csci567/17-18-bias-variance.pdf

Proctor, Philip,和 Peter Bergman。1978 年。“脑力激荡记忆学校,”收录于《给我们一个休息》,水星唱片。www.youtube.com/watch?v=PD2Uh_TJ9X0

第十章

美国疾病控制与预防中心。2020 年。“体质指数(BMI)。”CDC.gov。2020 年 6 月 30 日。www.cdc.gov/healthyweight/assessing/bmi/

Crayola. 2020 年。“1903 年 Crayola 蜡笔盒中的原始八种(8)颜色是什么?”访问时间:2020 年 9 月 10 日。www.crayola.com/faq/your-history/what-were-the-original-eight-8-colors-in-the-1903-box-of-crayola-crayons/

Turk, Matthew, 和 Alex Pentland. 1991. “用于识别的特征脸.” 认知神经科学学报 3, 第 1 期. www.face-rec.org/algorithms/pca/jcn.pdf.

第十一章

Bishop, Christopher M. 2006. 模式识别与机器学习. 纽约: 斯普林格.

Raschka, Sebastian. 2015. Python 机器学习. 伯明翰, 英国: Packt 出版社.

Steinwart, Ingo, 和 Andreas Christmann. 2008. 支持向量机. 纽约: 斯普林格.

VanderPlas, Jake. 2016. Python 数据科学手册. 塞巴斯托波尔, CA: O'Reilly 出版社.

第十二章

Bonab, Hamed, R., 和 Fazli Can. 2016. “在线集成学习中理想分类器数量的理论框架.” 收录于 2016 年第 25 届 ACM 国际信息与知识管理会议(CIKM)论文集(2016 年 10 月): 2053–56.

Ceruzzi, Paul. 2015. “阿波罗制导计算机与首批硅芯片.” 史密森国家航空航天博物馆, 2015 年 10 月 14 日. airandspace.si.edu/stories/editorial/apollo-guidance-computer-and-first-silicon-chips.

Freund, Y., 和 R. E. Schapire. 1997. “在线学习的决策理论推广及其在 Boosting 中的应用.” 计算机与系统科学学报 55 (1): 119–39.

Fumera, Giorgio, Roli Fabio, 和 Serrau Alessandra. 2008. “将 Bagging 视为分类器的线性组合的理论分析.” IEEE 模式分析与机器智能学报 30, 第 7 期: 1293–99.

Kak, Avinash. 2017. “决策树:如何构建决策树以及如何利用它们进行新数据分类.” 普渡大学 RVL 教程讲座, 2017 年 8 月 28 日. engineering.purdue.edu/kak/Tutorials/DecisionTreeClassifiers.pdf.

RangeVoting.org. 2020. “与投票相关术语词汇表.” 访问于 2020 年 9 月 16 日. rangevoting.org/Glossary.html.

Schapire, Robert E., 和 Yoav Freund. 2012. Boosting 基础与算法. 剑桥, MA: MIT 出版社.

Schapire, Robert E. 2013. “解释 Adaboost.” 收录于 经验推断:献给弗拉基米尔·N·瓦普尼克的庆典论文集. 柏林, 德国: Springer-Verlag. 可在 rob.schapire.net/papers/explaining-adaboost.pdf 查阅.

第十三章

Clevert, Djork-Arné, Thomas Unterthiner, 和 Sepp Hochreiter. 2016. “通过指数线性单元(ELUs)实现快速且准确的深度网络学习.” 康奈尔大学, 计算机科学, arXiv:1511.07289, 2016 年 2 月 22 日. arxiv.org/abs/1511.07289.

Estebon, Michele D. 1997. “感知器:一种联想学习网络,”弗吉尼亚理工大学。 ei.cs.vt.edu/~history/Perceptrons.Estebon.html.

Furber, Steve. 2012. “低功耗芯片模拟十亿神经元。” IEEE Spectrum(7 月 31 日)。spectrum.ieee.org/computing/hardware/lowpower-chips-to-model-a-billion-neurons.

Glorot, Xavier, 和 Yoshua Bengio. 2010. “理解训练深度前馈神经网络的难度,”发表于 第 13 届国际人工智能与统计会议论文集(AISTATS 2010)(意大利萨丁岛基亚拉拉古纳度假村):249–56. jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf.

Goldberg, Joseph. 2015. “不同抗抑郁药的作用机制。”WebMD 医学参考(8 月)。www.webmd.com/depression/how-different-antidepressants-work.

Goodfellow, Ian J., David Warde-Farley, Mehdi Mirza, Aaron Courville, 和 Yoshua Bengio. 2013. “Maxout 网络。”发表于 第 30 届国际机器学习大会论文集(PMLR) 28 卷,第 3 期:1319–27. jmlr.org/proceedings/papers/v28/goodfellow13.pdf.

He, Kaiming, Xiangyu Zhang, Shaoqing Ren, 和 Jian Sun. 2015. “深入探讨整流器:超越人类水*的 ImageNet 分类性能。”康奈尔大学,计算机科学,arXiv:1502.01852,2015 年 2 月 6 日. arxiv.org/abs/1502.01852.

Julien, Robert M. 2011. 药物作用概论,第 12 版. 纽约:沃思出版公司.

Khanna, Asrushi. 2018. “神经系统的细胞。” 教我生理学(博客)。最后修改日期:2018 年 8 月 2 日. teachmephysiology.com/nervous-system/components/cells-nervous-system.

Kuphaldt, Tony R. 2017. “二极管与整流器介绍,第三章 - 二极管与整流器。”发表于 电路课程,第 III 卷。关于电路的一切。访问日期:2020 年 9 月 18 日. www.allaboutcircuits.com/textbook/semiconductors/chpt-3/introduction-to-diodes-and-rectifiers/.

LeCun, Yann, Leon Bottou, Genevieve B. Orr, 和 Klaus-Rober Müller. 1998. “高效反向传播算法。”发表于 神经网络:实践技巧,由 Grégoire Montavon、Genevieve B. Orr 和 Klaus-Rober Müller 编辑。柏林:Springer-Verlag. 9–48. yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf.

林默,斯特芬,和斯拉沃米尔·斯坦奇扎克。2017 年。“通过拉普拉斯技术的稀疏恢复的最优深度神经网络。”康奈尔大学,计算机科学,arXiv:1709.01112,2017 年 9 月 26 日。arxiv.org/abs/1709.01112

洛迪什,哈维,阿诺德·伯克,S·劳伦斯·齐普尔斯基,保罗·松田,戴维·巴尔的摩,和詹姆斯·达内尔。2000 年。分子细胞生物学,第四版。纽约:W·H·弗里曼出版社;2000 年。www.ncbi.nlm.nih.gov/books/NBK21535/

麦卡洛赫,沃伦·S. 和沃尔特·皮茨。1943 年。“神经活动中固有思想的逻辑演算。” 数学生物物理学通报 5,第 1/2 期:115–133。www.cs.cmu.edu/~./epxing/Class/10715/reading/McCulloch.and.Pitts.pdf

穆尼耶,大卫,雷诺·兰比奥特,亚历克斯·福尔尼托,凯伦·D·厄舍,和爱德华·T·布尔莫尔。2009 年。“人脑功能网络中的层次模块化。” 神经信息学前沿(2009 年 10 月 30 日)。www.frontiersin.org/articles/10.3389/neuro.11.037.2009/full

明斯基,马丁和西摩·帕帕特。1969 年。感知机:计算几何学导论。马萨诸塞州剑桥:麻省理工学院出版社。

奥本海姆,艾伦·V. 和萨米德·哈米德·纳瓦布。1996 年。信号与系统,第二版。新泽西州上萨德尔河:普伦蒂斯霍尔出版社。

普尔维斯,戴尔,乔治·J·奥古斯丁,戴维·菲茨帕特里克,劳伦斯·C·卡茨,安东尼-塞缪尔·拉曼提亚,詹姆斯·O·麦克纳马拉,和 S·马克·威廉姆斯。2001 年。神经科学,第二版,马萨诸塞州桑德兰:西纳尔出版社。www.ncbi.nlm.nih.gov/books/NBK11117/

拉马钱德兰,普拉吉特,巴雷特·佐夫,和阔克·V·李。2017 年。“Swish:一种自门控激活函数。”康奈尔大学,计算机科学,arXiv:1710.05941,2017 年 10 月 27 日。arxiv.org/abs/1710.05941

罗斯布拉特,弗兰克。1962 年。神经动力学原理:感知机与大脑机制理论。华盛顿特区:斯巴达出版社。

鲁梅尔哈特,大卫·E.,杰弗里·E.·欣顿,和罗纳德·J·威廉姆斯,1986 年。“通过反向传播误差学习表征。” 自然 323,第 9 期:533–536。www.cs.utoronto.ca/~hinton/absps/naturebp.pdf

塞尔,托马斯。2014 年。“视觉系统的层次模型。”(研究笔记),认知语言学与心理学系,大脑科学研究所,布朗大学。serre-lab.clps.brown.edu/wp-content/uploads/2014/10/Serre-encyclopedia_revised.pdf

宋,塞巴斯蒂安。2013 年。大脑连接组:大脑线路如何塑造我们是谁。波士顿:马林纳图书出版社。

Sitzmann, Vincent, Julien N. P. Martel, Alexander W. Bergman, David B. Lindell, 和 Gordon Wetzstein. 2020. “带周期激活函数的隐式神经表示。” 康奈尔大学,计算机科学,arXiv:2006.09661,2020 年 6 月 17 日。 arxiv.org/abs/2006.09661

Sporns, Olaf, Giulio Tononi, 和 Rolf Kötter. 2005. “人类连接组:人脑的结构描述。” PLoS 计算生物学 1 卷,第 4 期(2005 年 9 月)。 journals.plos.org/ploscompbiol/article/file?id=10.1371/journal.pcbi.0010042&type=printable

Timmer, John. 2014. “IBM 研究人员制造了一颗充满人工神经元的芯片。” Ars Technica,2014 年 8 月 7 日。 arstechnica.com/science/2014/08/ibm-researchers-make-a-chip-full-of-artificial-neurons/

Trudeau, Richard J. 1994. 图论导论,第二版。纽约花园市:Dover 数学图书出版社。

Wikipedia. 2020a. “人工智能历史。” 最后修改于 2020 年 9 月 4 日。 en.wikipedia.org/wiki/History_of_artificial_intelligence

Wikipedia. 2020b. “神经元。” 最后修改于 2020 年 9 月 11 日。 en.wikipedia.org/wiki/Neuron

Wikipedia. 2020c. “感知机。” 最后修改于 2020 年 8 月 28 日。 en.wikipedia.org/wiki/Perceptron

第十四章

Dauphin, Yann, Razvan Pascanu, Caglar Gulcehre, Kyunghyun Cho, Surya Ganguli, 和 Yoshua Bengio. 2014. “识别并解决高维非凸优化中的鞍点问题。” 康奈尔大学,计算机科学,arXiv:1406.2572,2014 年 6 月 10 日。 arxiv.org/abs/1406.2572

Fullér, Robert. 2010. “Δ学习规则教程。” 高级管理系统研究所,信息技术系,阿波大学,2010 年 11 月 4 日。 uni-obuda.hu/users/fuller.robert/delta.pdf

NASA. 2012. “天文学家预测巨型碰撞:银河系对抗仙女座。” NASA 科学博客,制作编辑 Dr. Tony Phillips,2012 年 5 月 31 日。 science.nasa.gov/science-news/science-at-nasa/2012/31may_andromeda

Nielsen, Michael A. 2015. “使用神经网络识别手写数字。” 载于 神经网络与深度学习。Determination Press。可于 neuralnetworksanddeeplearning.com/chap1.html 获取。

Pyle, Katherine. 1918 年。母亲的育儿故事。纽约:E. F. Dutton & Company。可在www.gutenberg.org/files/49001/49001-h/49001-h.htm#Page_207查阅。

Quote Investigator, 2020 年。“你只需去除一切不似大卫的部分。” Quote Investigator: 追溯名言。访问时间:2020 年 10 月 26 日。quoteinvestigator.com/tag/michelangelo/

Seung, Sebastian。2005 年。“神经网络导论。”9.641J 课程笔记,2005 年春季。MIT OpenCourseware,麻省理工学院。ocw.mit.edu/courses/brain-and-cognitive-sciences/9-641j-introduction-to-neural-networks-spring-2005/

第十五章

Bengio, Yoshua。2012 年。“基于梯度的深度架构训练的实用建议。”康奈尔大学,计算机科学系,arXiv:1206.5533。arxiv.org/abs/1206.5533v2

Darken, C., J. Chang 和 J. Moody。1992 年。“加速随机梯度搜索的学习率调度。”见 神经网络与信号处理 II,第 1992 年 IEEE 研讨会论文集(9 月):1-11。

Dauphin, Y., R. Pascanu, C. Gulcehre, K. Cho, S. Ganguli 和 Y. Bengio。2014 年。“识别并攻克高维非凸优化中的鞍点问题。”康奈尔大学,计算机科学系,arXiv:1406.2572,2014 年 6 月 10 日。arxiv.org/abs/1406.2572

Duchi, John, Elad Hazan 和 Yoram Singer。2011 年。“在线学习与随机优化的自适应子梯度方法。” 机器学习研究期刊 12 卷,第 61 期:2121-2159。jmlr.org/papers/v12/duchi11a.html

Hinton, Geoffrey, Nitish Srivastava 和 Kevin Swersky。2015 年。“机器学习中的神经网络:第 6a 讲,迷你批量梯度下降概述。”(讲座幻灯片)。多伦多大学,计算机科学系。www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf

Ioffe, Sergey 和 Christian Szegedy。2015 年。“批量归一化:通过减少内部协变量偏移加速深度网络训练。”康奈尔大学,计算机科学系,arXiv:1502.03167,2015 年 3 月 2 日。arxiv.org/abs/1502.03167

Karpathy, Andrej。2016 年。“神经网络第三部分:学习与评估。”(斯坦福大学 CS231n 课程笔记)。加利福尼亚州,斯坦福大学。cs231n.github.io/neural-networks-2/

Kingma, Diederik. P. 和 Jimmy L. Ba。2015 年。“Adam:一种随机优化方法。”第三届国际学习表征会议论文(美国加利福尼亚州圣地亚哥,5 月 7-9 日):1-13。

Nesterov, Y. 1983. “一种无约束凸最小化问题的解法,收敛速率为 o(1/k2)。” Doklady ANSSSR(翻译为 Soviet Mathematics: Doclady)269:543–47。

Orr, Genevieve. 1999a. “学习率自适应。” 在 CS-449: Neural Networks。(课程笔记。)俄勒冈州塞勒姆:威拉米特大学。www.willamette.edu/~gorr/classes/cs449/intro.html

Orr, Genevieve. 1999b. “动量。” 在 CS-449: Neural Networks。(课程笔记。)俄勒冈州塞勒姆:威拉米特大学。www.willamette.edu/~gorr/classes/cs449/intro.html

Qian, N. 1999. “梯度下降学习算法中的动量项。” Neural Networks 12(1):145–51。www.columbia.edu/~nq6/publications/momentum.pdf

Ruder, Sebastian. 2017. “梯度下降优化算法概述。” 康奈尔大学,计算机科学,arXiv:1609.04747,2017 年 6 月 15 日。arxiv.org/abs/1609.04747

Srivasta, Nitish, Geoffrey Hinton, Alex Krizhevsky, 和 Ilya Sutskever. 2014. “Dropout:一种防止神经网络过拟合的简单方法。” Journal of Machine Learning Research 15(2014):1929–58。jmlr.org/papers/volume15/srivastava14a.old/srivastava14a.pdf

Wolpert, David H. 1996. “学习定理之间缺乏先验区分。” Neural Computation 8,1341–90。citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.51.9734&rep=rep1&type=pdf

Wolpert, D. H., 和 W. G. Macready. 1997. “优化的无免费午餐定理。” IEEE Transactions on Evolutionary Computation 1, 第 1 期:67–82。ti.arc.nasa.gov/m/profile/dhw/papers/78.pdf

Zeiler, Matthew D. 2012. “ADADELTA:一种自适应学习率方法。” 康奈尔大学,计算机科学,arXiv:1212.5701。arxiv.org/abs/1212.5701

第十六章

Aitken, Andrew, Christian Ledig, Lucas Theis, Jose Caballero, Zehan Wang, 和 Wenzhe Shi. 2017. “无棋盘伪影的子像素卷积:关于子像素卷积、尺寸调整卷积和卷积的简要说明。” 康奈尔大学,计算机科学,arXiv:1707.02937,2017 年 7 月 10 日。arxiv.org/abs/1707.02937

Britz, Denny. 2015. “理解卷积神经网络在 NLP 中的应用,” WildML(博客),2015 年 11 月 7 日。www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/

Canziani, Alfredo, Adam Paszke, 和 Eugenio Culurciello. 2017. “深度神经网络模型在实际应用中的分析。” 康奈尔大学,计算机科学,ArXiv:1605.07678,2016 年 4 月 4 日。arxiv.org/abs/1605.07678

Culurciello, Eugenio. 2017. “神经网络架构。” Medium: Towards Data Science,2017 年 3 月 23 日。medium.com/towards-data-science/neural-network-architectures-156e5bad51ba

Dumoulin, Vincent, 和 Francesco Visin. 2018. “深度学习卷积算术指南。” 康奈尔大学,计算机科学,arXiv:1603.07285,2018 年 1 月 11 日。arxiv.org/abs/1603.07285

Esteva, Andre, Brett Kuprel, Roberto A. Novoa, Justin Ko, Susan M. Swetter, Helen M. Blau, 和 Sebastian Thrun. 2017. “使用深度神经网络进行皮肤癌的皮肤科医生级分类。” 自然 542(2 月 2 日):115–18。cs.stanford.edu/people/esteva/nature/

Ewert, J. P. 1985. “脊椎动物神经行为学的概念。” 动物行为 33,第 1 期(2 月):1–29。

Glorot, Xavier, 和 Yoshua Bengio. 2010. “理解训练深度前馈神经网络的困难。” 载于 第 13 届国际人工智能与统计会议论文集(意大利萨丁岛,5 月 13–15 日):249–56。jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf

He, Kaiming, Xiangyu Zhang, Shaoqing Ren, 和 Jian Sun. 2015. “深入研究修正函数:超越人类级别的 ImageNet 分类性能。” 康奈尔大学,计算机科学,arXiv:1502.01852,2015 年 2 月 6 日。arxiv.org/abs/1502.01852v1

Kalchbrenner, Nal, Edward Grefenstette, 和 Phil Blunsom. 2014. “用于建模句子的卷积神经网络。” 康奈尔大学,计算机科学,arXiv:1404.2188v1,2014 年 4 月 8 日。arxiv.org/abs/1404.2188

Karpathy, Andrej. 2016. “优化:随机梯度下降。” 斯坦福 CS231n 课程讲义,斯坦福,加利福尼亚州:斯坦福大学。cs231n.github.io/neural-networks-2/

Kim, Yoon. 2014. “用于句子分类的卷积神经网络。” 康奈尔大学,计算机科学,arXiv:1408.5882,2014 年 9 月 3 日。arxiv.org/abs/1408.5882

Levi, Gil, 和 Tal Hassner. 2015 年。“使用卷积神经网络进行年龄与性别分类。”IEEE 面部与手势分析与建模研讨会(AMFG),IEEE 计算机视觉与模式识别大会(CVPR)(波士顿,6 月)。www.openu.ac.il/home/hassner/projects/cnn_agegender/

Lin, Min, Qiang Chen, 和 Shuicheng Yan. 2014 年。“网络中的网络。”康奈尔大学,计算机科学,arXiv:1312-4400v3,2014 年 3 月 4 日。arxiv.org/abs/1312.4400v3

Mao, Xiao-Jiao, Chinhua Shen, 和 Yu-Bin Yang. 2016 年。“使用对称跳跃连接的卷积自编码器进行图像恢复。”康奈尔大学,计算机科学,arXiv:16.06.08921v3,2016 年 8 月 30 日。arxiv.org/abs/1606.08921

Memmott, Mark. “你是否患有 RAS 综合症?” ‘Memmos’:Memmott 的信件与思考,NPR,2015 年。www.npr.org/sections/memmos/2015/01/06/605393666/do-you-suffer-from-ras-syndrome

Odena, Augustus, Vincent Dumoulin, 和 Chris Olah. 2016 年。“反卷积与棋盘伪影。” Distill,2016 年 10 月 17 日。distill.pub/2016/deconv-checkerboard/

Oppenheim, Alan V., 和 S. Hamid Nawab. 1996 年。信号与系统,第 2 版。新泽西州上萨德尔河:Prentice Hall。

Quiroga, R. Quian, L. Reddy, G. Kreiman, C. Koch, 和 I. Fried, 2005 年。“人脑单神经元的不可变视觉表征。” 自然 435(6 月 23 日):1102–07。www.nature.com/articles/nature03687

Serre, Thomas. 2014 年。“视觉系统的层次模型。”在 Jaeger D., Jung R.(编),计算神经科学百科全书。纽约:Springer。link.springer.com/referenceworkentry/10.1007%2F978-1-4614-7320-6_345-1

Snavely, Noah. “CS1114 第六部分:卷积。”康奈尔大学 CS1114 课程笔记,使用 Matlab 和机器人学进行计算机入门,纽约伊萨卡:康奈尔大学,2013 年 2 月 27 日。www.cs.cornell.edu/courses/cs1114/2013sp/sections/S06_convolution.pdf

Springenberg, Jost Tobias, Alexey Dosovitskiy, Thomas Brox, 和 Martin Riedmiller. 2015 年。“追求简洁:全卷积网络。”康奈尔大学,计算机科学,arXiv:1412.6806,2015 年 4 月 13 日。arxiv.org/abs/1412.6806

Sun, Y., X. Wang, 和 X. Tang. 2014 年。“通过预测 10,000 个类别进行深度学习面部表征。”会议论文,2014 IEEE 计算机视觉与模式识别大会(俄亥俄州哥伦布市,6 月 23-28 日):1891–98。

Zeiler, Matthew D., Dilip Crishnana, Graham W. Taylor, 和 Rob Fergus. 2010. “去卷积网络.” 计算机视觉与模式识别大会论文(2010 年 6 月 13 日–18 日)。www.matthewzeiler.com/mattzeiler/deconvolutionalnetworks.pdf.

Zhang, Richard. 2019. “使卷积网络重新具备*移不变性.” 康奈尔大学计算机科学,arXiv:1904.11486,2019 年 6 月 9 日。 arxiv.org/abs/1904.11486.

第十七章

Chollet, François. 2017. “Keras-team/Keras.” GitHub. keras.io/.

Fei-Fei, Li, Jia Deng, Olga Russakovsky, Alex Berg, 和 Kai Li. 2020. “下载.” ImageNet 网站。斯坦福视觉实验室。斯坦福大学/普林斯顿大学。访问日期:2020 年 10 月 4 日。 image-net.org/download.

ImageNet. 2020. “ILSVRC2014 的结果:分类 + 定位结果.” 斯坦福视觉实验室。斯坦福大学/普林斯顿大学。访问日期:2020 年 10 月 4 日。 image-net.org/challenges/LSVRC/2014/results.

LeCun, Y., B. Boser, J. S. Denker, D. Henderson, R. E. Howard, W. Hubbard, 和 L. D. Jackel. 1989. “反向传播应用于手写邮政编码识别.” Neural Computing 1(4): 541–51. 可在 yann.lecun.com/exdb/publis/pdf/lecun-89e.pdf 查阅.

Moosavi-Dezfooli, Seyed-Mohsen, Alhussein Fawzi, Omar Fawzi, 和 Pascal Frossard. 2017. “通用对抗扰动.” 康奈尔大学计算机科学,arXiv:1610.08401,2017 年 3 月 9 日。 arxiv.org/abs/1610.08401.

Rauber, Jonas, 和 Wieland Brendel. 2017. “欢迎使用 Foolbox Native.” Foolbox. foolbox.readthedocs.io/en/latest.

Rauber, Jonas, Wieland Brendel, 和 Matthias Bethge. 2018. “Foolbox: 一个用于基准测试机器学习模型鲁棒性的 Python 工具箱.” 康奈尔大学计算机科学,arXiv:1707.04131,2018 年 3 月 20 日。 arxiv.org/abs/1707.04131.

Russakovsky, Olga, 等. 2015. “ImageNet 大规模视觉识别挑战赛.” 康奈尔大学计算机科学,arXiv:1409.0575,2015 年 1 月 30 日。 arxiv.org/abs/1409.0575.

Simonyan, Karen, 和 Andrew Zisserman. 2015. “用于大规模图像识别的非常深的卷积网络.” 康奈尔大学计算机科学,arXiv:1409.1556,2015 年 4 月 10 日。 arxiv.org/abs/1409.1556.

Zeiler, Matthew D., 和 Rob Fergus. 2013. “可视化和理解卷积网络.” 康奈尔大学计算机科学,arXiv:1311.2901,2013 年 11 月 28 日。 arxiv.org/abs/1311.2901.

第十八章

Altosaar, Jann. 2020. “教程:什么是变分自编码器?” Jaan Altosaar(博客)。访问时间:2020 年 9 月 30 日。jaan.io/what-is-variational-autoencoder-vae-tutorial/.

Audio Mountain. 2020. “音频文件大小计算。” AudioMountain.com 技术资源。访问时间:2020 年 9 月 30 日。www.audiomountain.com/tech/audio-file-size.html.

Bako, Steve, Thijs Vogels, Brian McWilliams, Mark Meyer, Jan Novák, Alex Harvill, Prdeep Sen, Tony DeRose, 和 Fabrice Rousselle. 2017. “用于去噪蒙特卡洛渲染的核预测卷积网络。”《SIGGRAPH 17 会议录,ACM 图形学期刊》36,第 4 期(文章 97)。s3-us-west-1.amazonaws.com/disneyresearch/wp-content/uploads/20170630135237/Kernel-Predicting-Convolutional-Networks-for-Denoising-Monte-Carlo-Renderings-Paper33.pdf.

Chaitanya, Chakravarty R. Alla, Anton Kaplanyan, Christoph Schied, Marco Salvi, Aaron Lefohn, Derek Nowrouzezahrai, 和 Timo Aila. 2017. “使用递归去噪自编码器进行蒙特卡洛图像序列的交互式重建。”《SIGGRAPH 17 会议录,ACM 图形学期刊》36,第 4 期(7 月 1 日)。research.nvidia.com/publication/interactive-reconstruction-monte-carlo-image-sequences-using-recurrent-denoising.

Chollet, François. 2017. “在 Keras 中构建自编码器。” Keras 博客,2017 年 3 月 14 日。blog.keras.io/building-autoencoders-in-keras.html.

Doersch, Carl. 2016. “变分自编码器教程。” 康奈尔大学,统计学,arXiv:1606.05908,2016 年 8 月 13 日。arxiv.org/abs/1606.05908.

Donahue, Jeff. 2015. “mnist_autoencoder.prototxt。” BVLC/Caffe. GitHub. 2015 年 2 月 5 日。github.com/BVLC/caffe/blob/master/examples/mnist/mnist_autoencoder.prototxt.

Dürr, Oliver. 2016. “变分自编码器简介。” 在瑞士温特图尔举行的 Datalab-Lunch 研讨会系列中做的报告,2016 年 5 月 11 日。tensorchiefs.github.io/bbs/files/vae.pdf.

Frans, Kevin. “变分自编码器解析。”(教程)Kevin Frans 网站,2016 年 8 月 6 日。kvfrans.com/variational-autoencoders-explained/.

Jia, Yangqing, 和 Evan Shelhamer, 2020. “Caffe。” 伯克利视觉在线文档,访问时间:2020 年 10 月 1 日。http://caffe.berkeleyvision.org/

Kingma, Diederik P., 和 Max Welling, “自动编码变分贝叶斯。” 康奈尔大学,统计学,arXiv:1312.6114,2014 年 5 月 1 日。arxiv.org/abs/1312.6114

Raffel, Colin. 2019. “一些不寻常的自编码器。” Slideshare.net。2019 年 2 月 24 日。www.slideshare.net/marlessonsa/a-few-unusual-autoencoder-colin-raffel

Rezende, Danilo Jimenez, Shakir Mohamed, 和 Daan Wierstra. 2014. “深度生成模型中的随机反向传播和*似推断。” 第 31 届国际机器学习大会(ICML)论文集,JMLR: W&CP 32(2014 年 5 月 30 日)。arxiv.org/abs/1401.4082

Wikipedia. 2020a. “JPEG.” 访问时间:2020 年 9 月 30 日。en.wikipedia.org/wiki/JPEG

Wikipedia. 2020b. “MP3。” 访问时间:2020 年 9 月 30 日。en.wikipedia.org/wiki/MP3

第十九章

Barrat, Robbie. 2018. “Rapping-Neural-Network。” GitHub,2018 年 10 月 29 日。github.com/robbiebarrat/rapping-neural-network

Bryant, Alice. 2019. “一个简单的句子,七种含义。” VOA Learning English: Everyday Grammar(博客)。2019 年 5 月 16 日。learningenglish.voanews.com/a/a-simple-sentence-with-seven-meanings/4916769.html

Chen, Yutian, Matthew W. Hoffman, Sergio Gómez Colmenarejo, Misha Denil, Timothy P. Lillicrap, Matt Botvinick, 和 Nando de Freitas. 2017. “通过梯度下降来学习,无需梯度下降。” 康奈尔大学,统计学,arXiv:1611.03824v6,2017 年 6 月 12 日。arxiv.org/abs/1611.03824

Chen, Qiming, 和 Ren Wu. 2017. “CNN 就是你所需的一切。” 康奈尔大学,计算机科学,arXiv:1712.09662,2017 年 12 月 27 日。arxiv.org/abs/1712.09662

Chollet, Francois. 2017. “Keras 中的序列到序列学习十分钟入门。” The Keras Blog,2017 年 9 月 29 日。blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html

Chu, Hang, Raquel Urtasun, 和 Sanja Fidler. 2016. “PI 之歌:一个音乐上合理的流行音乐生成网络。” 康奈尔大学,计算机科学,arXiv:1611.03477,2016 年 11 月 10 日。arxiv.org/abs/1611.03477

Deutsch, Max. 2016a. “哈利·波特:由人工智能编写。” 深度写作(博客),Medium。2016 年 7 月 8 日。medium.com/deep-writing/harry-potter-written-by-artificial-intelligence-8a9431803da6.

Deutsch, Max. 2016b. “硅谷:由人工智能编写的新一集。” 深度写作(博客),Medium,2016 年 7 月 11 日。medium.com/deep-writing/silicon-valley-a-new-episode-written-by-ai-a8f832645bc2.

Dickens, Charles. 1859. 双城记. Project Gutenberg. www.gutenberg.org/ebooks/98.

Dictionary.com. 2020. “英语语言中有多少个单词?” 访问于 2020 年 10 月 29 日。www.dictionary.com/e/how-many-words-in-english/.

Doyle, Arthur Conan. 1892. 福尔摩斯的冒险. Project Gutenberg. www.gutenberg.org/files/1661/1661-0.txt.

Full Fact. 2020. “自动化事实核查。” 访问于 2020 年 10 月 29 日。fullfact.org/automated.

Geitgey, Adam. 2016. “机器学习很有趣,第六部分:如何用深度学习做语音识别。” Medium,2016 年 12 月 23 日。medium.com/@ageitgey/machine-learning-is-fun-part-6-how-to-do-speech-recognition-with-deep-learning-28293c162f7a.

Google. “Google 翻译。” 2020 年。translate.google.com/.

Graves, Alex, Abdel-rahman Mohamed, 和 Geoffrey Hinton, “使用深度递归神经网络进行语音识别。” 2013 年 IEEE 国际声学、语音与信号处理会议(ICASSP),加拿大温哥华,2013 年 5 月 26 日–31 日。www.cs.toronto.edu/~fritz/absps/RNN13.pdf.

Heerman, Victor, 导演。1930. 动物饼干。由 George S. Kaufman、Morrie Ryskind、Bert Kalmar 和 Harry Ruby 编剧。派拉蒙影业公司。www.imdb.com/title/tt0020640/.

Hochreiter, Sepp, Yoshua Bengio, Paolo Frasconi, 和 Jürgen Schmidhuber. 2001. “递归神经网络中的梯度流:学习长期依赖的困难。” 收录于 S. C. Kremer 和 J. F. Kolen 编,动态递归神经网络实用指南。纽约:IEEE 出版社。www.bioinf.jku.at/publications/older/ch7.pdf.

Hughes, John. 2020. “通过 Seq2Seq 架构进行英语到荷兰语的神经机器翻译。” GitHub。访问时间:2020 年 10 月 29 日。colab.research.google.com/github/hughes28/Seq2SeqNeuralMachineTranslator/blob/master/Seq2SeqEnglishtoDutchTranslation.ipynb#scrollTo=8q4ESVzKJgHd

Johnson, Daniel. 2015. “使用递归神经网络作曲。” Daniel D. Johnson(博客)。2015 年 8 月 3 日。www.danieldjohnson.com/2015/08/03/composing-music-with-recurrent-neural-networks/

Jurafsky, Dan. 2020. “语言建模:介绍 N-grams。” 课堂笔记,斯坦福大学,2020 年冬季。web.stanford.edu/~jurafsky/slp3/slides/LM_4.pdf

Kaggle. 2020. “太阳黑子。” 数据集,Kaggle.com。访问时间:2020 年 10 月 29 日。www.kaggle.com/robervalt/sunspots

Karim, Raimi. 2019. “Attn:插图中的注意力。” Towards Data Science(博客),Medium,2019 年 1 月 20 日。towardsdatascience.com/attn-illustrated-attention-5ec4ad276ee3

Karpathy, Andrej, 和 Fei-Fei Li. 2013. “使用卷积神经网络和递归神经网络进行自动图像描述。” 演示文稿,斯坦福大学计算机科学系,斯坦福大学。 cs.stanford.edu/people/karpathy/sfmltalk.pdf

Karpathy, Andrej. 2015. “递归神经网络的非凡有效性。” Andrej Karpathy 博客,GitHub,2015 年 5 月 21 日。karpathy.github.io/2015/05/21/rnn-effectiveness/

Kelly, Charles. 2020. “制表符分隔的双语句子对。” Manythings.org。最后更新时间:2020 年 8 月 23 日。www.manythings.org/anki/

Krishan. 2016. “通过递归神经网络生成宝莱坞歌词。” 从数据到决策(博客),2016 年 12 月 8 日。iksinc.wordpress.com/2016/12/08/bollywood-lyrics-via-recurrent-neural-networks/

LISA Lab, 2018. “使用 RNN-RBM 建模和生成复调音乐序列。” 教程,Deeplearning.net。最后更新时间:2018 年 6 月 15 日。deeplearning.net/tutorial/rnnrbm.html#rnnrbm

Mao, Junhua, Wei Xu, Yi Yang, Jiang Wang, Zhiheng Huang, 和 Alan Yuille. 2015. “使用多模态递归神经网络(m-RNN)进行深度图像描述。” 康奈尔大学计算机科学,arXiv:1412.6632,2015 年 6 月 11 日。arxiv.org/abs/1412.6632

McCrae, Pat. 2018 年。“关于‘英语中有多少个名词’的评论。”Quora。2018 年 11 月 15 日。www.quora.com/How-many-nouns-are-there-in-English

Moocarme, Matthew. 2020 年。“使用递归神经网络创建的乡村歌词。”Matthew Moocarme(博客)。访问日期:2020 年 10 月 29 日。www.mattmoocar.me/blog/RNNCountryLyrics/

Mooney, Raymond J. 2019 年。“CS 343:人工智能:自然语言处理。”课程笔记,PowerPoint 幻灯片,德克萨斯大学奥斯汀分校。www.cs.utexas.edu/~mooney/cs343/slides/nlp.ppt

O’Brien, Tim, 和 Irán Román. 2017 年。“用于音乐结构处理和期望的递归神经网络。”CS224d:自然语言处理深度学习报告,斯坦福大学,2017 年冬季。cs224d.stanford.edu/reports/O%27BrienRom%C2%B4an.pdf

Olah, Christopher. 2015 年。“理解 LSTM 网络。”Colah’s Blog,GitHub,2015 年 8 月 27 日。colah.github.io/posts/2015-08-Understanding-LSTMs/

Pascanu, Razvan, Tomas Mikolov, 和 Yoshua Bengio. 2013 年。“训练递归神经网络的难点。”康奈尔大学,计算机科学,arXiv:1211.5063,2013 年 2 月 16 日。arxiv.org/abs/1211.5063

R2RT. 2016 年。“书面记忆:理解、推导和扩展 LSTM。”R2RT(博客),2016 年 7 月 26 日。r2rt.com/written-memories-understanding-deriving-and-extending-the-lstm.html

Rajpurkar, Pranav, Robin Jia, 和 Percy Liang. 2018 年。“了解你不知道的:SQuAD 中的无解问题。”康奈尔大学,计算机科学,arXiv:1806.03822,2018 年 6 月 11 日。arxiv.org/abs/1806.03822

Roberts, Adam, Colin Raffel, 和 Noam Shazeer. 2020 年。“你能将多少知识封装进语言模型的参数中?”康奈尔大学,计算机科学,arXiv:2002.08910,2020 年 10 月 5 日。arxiv.org/abs/2002.08910

Robertson, Sean. 2017 年。“从零开始的 NLP:使用序列到序列网络和注意力机制的翻译。”教程,PyTorch。pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html

Schuster, Mike, 和 Kuldip K. Paliwal. 1997 年。“双向递归神经网络。”IEEE 信号处理学报,45 卷,第 11 期(11 月)。http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.331.9441&rep=rep1&type=pdf

Sturm, Bob L. 2015a. “无限的爱尔兰传统音乐会。” High Noon GMT(博客),Folk the Algorithms,2015 年 8 月 7 日. highnoongmt.wordpress.com/2015/08/07/the-infinite-irish-trad-session/.

Sturm, Bob L. 2015b. “‘Lisl 的 Stis’: 用于民间音乐生成的递归神经网络.” High Noon GMT(博客),Folk the Algorithms,2015 年 5 月 22 日. highnoongmt.wordpress.com/2015/05/22/lisls-stis-recurrent-neural-networks-for-folk-music-generation/.

Sutskever, Ilya, Oriol Vinyals, 和 Quoc V. Le. 2014. “使用神经网络的序列到序列学习.” 康奈尔大学,计算机科学,arXiv:1409.3215,2014 年 12 月 14 日. arxiv.org/abs/1409.3215.

Unicode 联盟. 2020. 版本 13.0.0,2020 年 3 月 10 日. www.unicode.org/versions/Unicode13.0.0/.

van den Oord, Äaron, Sander Dieleman, Heiga Zen, Karen Simonyan, Oriol Vinyals, Alex Graves, Nal Kalchbrenner, Andrew Senior, 和 Koray Kavukcuoglu. 2016. “WaveNet:一种原始音频的生成模型。” 康奈尔大学,计算机科学,arXiv:1609.03499,2016 年 9 月 19 日. arxiv.org/abs/1609.03499.

Vicente, Agustin, 和 Ingrid L. Falkum. 2017. “多义性.” 牛津研究百科全书:语言学。2017 年 7 月 27 日. oxfordre.com/linguistics/view/10.1093/acrefore/9780199384655.001.0001/acrefore-9780199384655-e-325.

第二十章

Alammar, Jay. 2018. “GPT3 的工作原理 - 可视化和动画.” Jay Alammar: 一次性可视化机器学习概念(博客)。GitHub。2020 年 11 月 5 日访问. jalammar.github.io/how-gpt3-works-visualizations-animations/.

Alammar, Jay. 2019. “首次使用 BERT 的可视化指南.” Jay Alammar: 一次性可视化机器学习概念(博客)。GitHub。2019 年 11 月 26 日. jalammar.github.io/a-visual-guide-to-using-bert-for-the-first-time/.

Bahdanau, Dzmitry, Kyunghyun Cho, 和 Yoshua Bengio. 2016. “通过联合学习对齐和翻译的神经机器翻译.” 康奈尔大学,计算机科学,arXiv:1409.0473,2016 年 5 月 19 日. arxiv.org/abs/1409.0473.

Brown, Tom B., 等. 2020. “语言模型是少量学习者。” 康奈尔大学,计算机科学,arXiv:2005.14165,2020 年 7 月 22 日. arxiv.org/pdf/2005.14165.pdf.

Cer, Daniel, 等. 2018. “通用句子编码器。”康奈尔大学,计算机科学,arXiv:1803.11175,2018 年 4 月 12 日。arxiv.org/abs/1803.11175.

Cho, Kyunghyun, Dzmitry Bahdanau, Fethi Bougares, Holger Schwenk, 和 Yoshua Bengio. 2014. “使用 RNN 编码器-解码器学习短语表示法进行统计机器翻译。”发表于 2014 年自然语言处理实证方法会议(EMNLP)(卡塔尔多哈,2014 年 10 月 25 日至 29 日):1724–34。emnlp2014.org/papers/pdf/EMNLP2014179.pdf.

Chromiak, Michał. 2017. “变换器—注意力就是一切。”Michał Chromiak 的博客,GitHub,2017 年 10 月 30 日。mchromiak.github.io/articles/2017/Sep/12/Transformer-Attention-is-all-you-need/.

Common Crawl. 2020. Common Crawl 主页。访问时间:2020 年 11 月 15 日。commoncrawl.org/the-data/.

Devlin, Jacob, Ming-Wei Chang, Kenton Lee, 和 Kristina Toutanova. 2019. “BERT: 深度双向变换器的语言理解预训练。”康奈尔大学,计算机科学,arXiv:1810.04805,2019 年 5 月 24 日。arxiv.org/abs/1810.04805.

Devlin, Jacob, Ming-Wei Chang, Kenton Lee, Kristina Toutanova. 2020. “Google-Research/bert。”GitHub。访问时间:2020 年 11 月 5 日。github.com/google-research/bert.

El Boukkouri, Hicham. 2018. “词嵌入的算术性质。”数据前沿(博客),Medium,2018 年 11 月 21 日。medium.com/data-from-the-trenches/arithmetic-properties-of-word-embeddings-e918e3fda2ac.

Facebook 开源项目。2020. “fastText: 高效文本分类与表示学习库。”开源软件。访问时间:2020 年 11 月 5 日。fasttext.cc/.

Frankenheimer, John, 导演。1962. 重庆森林,由 George Axelrod 编剧,改编自 Richard Condon 的小说。M. C. Productions。www.imdb.com/title/tt0056218/.

Gluon 作者。2020. “使用预训练 ELMo 提取句子特征。”教程,Gluon,访问时间:2020 年 11 月 5 日。gluon-nlp.mxnet.io/examples/sentence_embedding/elmo_sentence_representation.html.

He, Kaiming, Xiangyu Zhang, Shaoqing Ren, 和 Jian Sun. 2015. “深度残差学习用于图像识别。”康奈尔大学,计算机科学,arXiv:1512.03385,2015 年 12 月 10 日。arxiv.org/abs/1512.03385.

Hendrycks, Dan, Collin Burns, Steven Basart, Andy Zou, Mantas Mazeika, Dawn Song, 和 Jacob Steinhardt. 2020 年。“测量大规模多任务语言理解。”康奈尔大学,计算机科学,arXiv:2009.03300,2020 年 9 月 21 日。arxiv.org/abs/2009.03300

Howard, Jeremy 和 Sebastian Ruder. 2018 年。“用于文本分类的通用语言模型微调。”康奈尔大学,计算机科学,arXiv:1801.06146,2018 年 5 月 23 日。arxiv.org/abs/1801.06146

Huston, Scott. 2020 年。“GPT-3 简介:理解 OpenAI 的前沿语言模型。”Towards Data Science(博客),2020 年 8 月 20 日。towardsdatascience.com/gpt-3-primer-67bc2d821a00

Kaplan, Jared, Sam McCandlish, Tom Henighan, Tom B. Brown, Benjamin Chess, Rewon Child, Scott Gray, Alec Radford, Jeffrey Wu, 和 Dario Amodei. 2020 年。“神经语言模型的扩展规律。”康奈尔大学,计算机科学,arXiv:2001.08361,2020 年 1 月 23 日。arxiv.org/abs/2001.08361

Kazemnejad, Amirhossein. 2019 年。“Transformer 架构:位置编码。”Amirhossein Kazemnejad 的博客,2019 年 9 月 20 日。kazemnejad.com/blog/transformer_architecture_positional_encoding/

Kelly, Charles. 2020 年。“制表符分隔的双语句子对。”Manythings.org。访问于 2020 年 11 月 6 日。www.manythings.org/anki/

Klein, Guillaume, Yoon Kim, Yuntian Deng, Jean Senellart, 和 Alexander M. Rush. 2017 年。“OpenNMT:开源神经机器翻译工具包。”康奈尔大学,计算机科学,arXiv:1701.02810,2017 年 3 月 6 日。arxiv.org/abs/1701.02810

刘扬、季力新、黄瑞阳、明拓思宇、高超、张剑鹏。2018 年。“一种用于句子分类的注意力门控卷积神经网络。”康奈尔大学,计算机科学,arXiv:2018.07325,2018 年 12 月 28 日。arxiv.org/abs/1808.07325

Mansimov, Elman, Alex Wang, Sean Welleck, 和 Kyunghyun Cho. 2020 年。“一种用于无向序列模型的序列生成广义框架。”康奈尔大学,计算机科学,arXiv:1905.12790,2020 年 2 月 7 日。arxiv.org/abs/1905.12790

McCormick Chris 和 Nick Ryan,2020 年。“BERT 微调教程与 PyTorch。”Chris McCormick(博客)。最后更新于 2020 年 3 月 20 日。mccormickml.com/2019/07/22/BERT-fine-tuning/

Mikolov, Tomas, Kai Chen, Greg Corrado, 和 Jeffrey Dean. 2013 年. “词表示的高效估计.” 康奈尔大学,计算机科学,arXiv:1301.3781,2013 年 9 月 7 日. arxiv.org/abs/1301.3781.

Mikolov, Tomas, Ilya Sutskever, Kai Chen, Greg Corrado, 和 Jeffrey Dean. 2013 年. “词语和短语的分布式表示及其组合性.” 康奈尔大学,计算机科学,arXiv:1310.4546,2013 年 10 月 16 日. arxiv.org/abs/1310.4546.

Mishra, Prakhar. 2020 年. “基于 BERT 的自然语言生成介绍.” TechViz: The Data Science Guy(博客)。访问于 2020 年 11 月 6 日. prakhartechviz.blogspot.com/2020/04/natural-language-generation-using-bert.html.

Paulus, Romain, Caiming Xiong, 和 Richard Socher, 2017 年. “用于抽象摘要的深度强化学习模型.” 康奈尔大学,计算机科学,arXiv:1705.04304,2017 年 11 月 13 日. arxiv.org/abs/1705.04304.

Pennington, Jeffrey, Richard Socher, 和 Christopher D. Manning. 2014 年. “GloVe: 全局词向量表示.” 收录于2014 年自然语言处理实证方法会议(EMNLP)论文集(10 月):1532–43. nlp.stanford.edu/pubs/glove.pdf.

Peters, Matthew E., Mark Neumann, Mohit Iyyer, Matt Gardner, Christopher Clark, Kenton Lee, 和 Luke Zettlemoyer. 2018 年. “深度上下文化词表示.” 康奈尔大学,计算机科学,arXiv:1802.05365,2018 年 3 月 22 日. arxiv.org/abs/1802.05365.

Radford, Alec, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei, 和 Ilya Sutskever. 2019 年. “语言模型是无监督的多任务学习者.” OpenAI, 旧金山,加利福尼亚州,2019 年. cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf.

Raffel, Colin, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang, Michael Matena, Yanqi Zhou, Wei Li, 和 Peter J. Liu. 2020 年. “探索统一文本到文本转换器的迁移学习极限.” 康奈尔大学,计算机科学,arXiv:1910.10683,2020 年 7 月 28 日. arxiv.org/abs/1910.10683.

Rajasekharan, Ajit. 2019 年. “BERT 基础模型的综述.” Towards Data Science(博客),2019 年 6 月 17 日. towardsdatascience.com/a-review-of-bert-based-models-4ffdc0f15d58.

Reisner, Alex. 2020 年. “做一只动物是什么感觉?” SpeedofAnimals.com. 访问于 2020 年 11 月 6 日. www.speedofanimals.com/.

Russell, Stuart, 和 Peter Norvig. 2009. 人工智能:一种现代方法,第 3 版。纽约:Pearson 出版社。

Sanh, Victor, Lysandre Debut, Julien Chaumond, 和 Thomas Wolf. 2020. “DistilBERT,BERT 的蒸馏版本:更小、更快、更便宜、更轻。” 康奈尔大学,计算机科学,arXiv:1910.01108,2020 年 3 月 1 日。arxiv.org/abs/1910.01108

Scott, Kevin. 2020. “微软与 OpenAI 合作,独家授权 GPT-3 语言模型。” 官方微软博客,2020 年 9 月 22 日。blogs.microsoft.com/blog/2020/09/22/microsoft-teams-up-with-openai-to-exclusively-license-gpt-3-language-model/

Shao, Louis, Stephan Gouws, Denny Britz, Anna Goldie, Brian Strope, 和 Ray Kurzweil. 2017. “使用序列到序列模型生成高质量和有信息量的对话回复。” 康奈尔大学,计算机科学,arXiv:1701.03185,2017 年 7 月 31 日。arxiv.org/abs/1701.03185

Singhal, Vivek. 2020. “NLP 中的变换器。” 研究/博客,CellStrat,2020 年 5 月 19 日。www.cellstrat.com/2020/05/19/transformers-for-nlp/

Socher, Richard, Alex Perelygin, Jean Y. Wu, Jason Chuang, Christopher D. Manning, Andrew Y. Ng, 和 Christopher Potts. 2013a. “深刻的移动:情感分析的深度学习——数据集。” 情感分析。2013 年 8 月。nlp.stanford.edu/sentiment/index.html

Socher, Richard, Alex Perelygin, Jean Y. Wu, Jason Chuang, Christopher D. Manning, Andrew Y. Ng, 和 Christopher Potts. 2013b. “递归深度模型用于情感树库上的语义组合性。” 在自然语言处理经验方法会议(EMNLP)上的口头报告(西雅图,华盛顿州,2013 年 10 月 18 日至 21 日)。nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf

spaCy 作者. 2020. “词向量与语义相似性。” spaCy:使用。spacy.io/usage/vectors-similarity

Sutskever, Ilya, Oriol Vinyals, 和 Quoc V. Le. 2014. “使用神经网络进行序列到序列学习。” 康奈尔大学,计算机科学,arXiv:1409.3215,2014 年 12 月 14 日。arxiv.org/abs/1409.3215

Tay, Yi, Mostafa Dehghani, Dara Bahri, 和 Donald Metzler. 2020. “高效的变换器:综述。” 康奈尔大学,数学,arXiv:2009.0673,2020 年 9 月 1 日。arxiv.org/abs/2009.0673

Taylor, Wilson L. 1953. “‘Cloze 程序’:一种衡量可读性的新工具。” Journalism Quarterly,30(4): 415–33。www.gwern.net/docs/psychology/writing/1953-taylor.pdf

TensorFlow 作者. 2018. “通用句子编码器。”教程,TensorFlow 模型档案,GitHub,2018 年。colab.research.google.com/github/tensorflow/hub/blob/master/examples/colab/semantic_similarity_with_tf_hub_universal_encoder.ipynb#scrollTo=co7MV6sX7Xto

TensorFlow 作者, 2019a. “为什么使用位置嵌入而不是拼接?” tensorflow/tensor2tensor(博客)。2019 年 5 月 30 日。github.com/tensorflow/tensor2tensor/issues/1591

TensorFlow 作者. 2019b. “用于语言理解的 Transformer 模型。”文档,TensorFlow,GitHub。colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/text/transformer.ipynb

TensorFlow 作者. 2020a. “Elmo。”TensorFlow Hub,2020 年 11 月 6 日。tfhub.dev/google/elmo/3

TensorFlow 作者. 2020b. “用于语言理解的 Transformer 模型。”教程,TensorFlow 核心文档。最后更新于 2020 年 11 月 2 日。www.tensorflow.org/tutorials/text/transformer

Thiruvengadam, Aditya. 2018. “Transformer 架构:Attention Is All You Need。” Aditya Thiruvengadam(博客),Medium。2018 年 10 月 9 日。medium.com/@adityathiruvengadam/transformer-architecture-attention-is-all-you-need-aeccd9f50d09

Vaswani, Ashish, Noam Shazeer, Niki Parmar, Jacob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, 和 Illia Polosukhim. 2017. “Attention Is All You Need。”康奈尔大学,计算机科学,arXiv:1706.03762v5,2017 年 12 月 6 日。arxiv.org/abs/1706.03762v5

Vijayakumar, Ashwin K., Michael Cogswell, Ramprasath R. Selvaraju, Qing Sun, Stefan Lee, David Crandall, 和 Dhruv Batra. 2018. “多样性束搜索:从神经序列模型中解码多样化的解决方案。”康奈尔大学,计算机科学,arXiv:1610.02424。2018 年 10 月 22 日。arxiv.org/abs/1610.02424

von Platen, Patrick. 2020. “如何生成文本:使用不同的解码方法生成语言,使用 Transformer。” Huggingface(博客),GitHub,2020 年 5 月。huggingface.co/blog/how-to-generate

Wallace, Eric, Tony Z. Zhao, Shi Feng, 和 Sameer Singh. 2020. “通过隐蔽数据投毒自定义触发器。” 康奈尔大学,计算机科学,arXiv:2010.12563,2020 年 10 月 3 日. arxiv.org/abs/2010.12563.

Walton, Nick. 2020. “AI Dungeon: Dragon 模型升级。” Nick Walton(博客)2020 年 7 月 14 日. medium.com/@aidungeon/ai-dungeon-dragon-model-upgrade-7e8ea579abfeplay.aidungeon.io/main/home.

Wang, Alex, Amanpreet Singh, Julian Michael, Felix Hill, Omer Levy, 和 Samuel R. Bowman. 2019. “GLUE: 用于自然语言理解的多任务基准和分析*台。” 康奈尔大学,计算机科学,arXiv:1804.07461,2019 年 2 月 22 日. arxiv.org/abs/1804.07461.

Wang, Alex, Amanpreet Singh, Julian Michael, Felix Hill, Omer Levy, 和 Samuel R. Bowman. 2020. “GLUE 排行榜。” GLUE 基准测试。访问于 2020 年 11 月 6 日. gluebenchmark.com/leaderboard/submission/zlssuBTm5XRs0aSKbFYGVIVdvbj1/-LhijX9VVmvJcvzKymxy.

Warstadt, Alex, Amanpreet Singh, 和 Sam Bowman. 2018. “CoLA: 语言接受度语料库。” NYU-MLL. nyu-mll.github.io/CoLA/.

Warstadt, Alex, Amanpreet Singh, 和 Sam Bowman. 2019. “神经网络能力评估。” 康奈尔大学,计算机科学,arXiv:1805.12471,2019 年 10 月 1 日. arxiv.org/abs/1805.12471.

Welleck, Sean, Ilia Kulikov, Jaedeok Kim, Richard Yuanzhe Pang, 和 Kyunghyun Cho. 2020. “递归语言模型在不完全解码下的一致性。” 康奈尔大学,计算机科学,arXiv:2002.02492,2020 年 10 月 2 日. arxiv.org/abs/2002.02492.

Woolf, Max. 2019. “使用 GPU 训练 GPT-2 文本生成模型。” Google Colab Notebook, 2019. colab.research.google.com/drive/1VLG8e7YSEwypxU-noRNhsv5dW4NfTGce#scrollTo=-xInIZKaU104.

Xu, Lei, Ivan Ramirez, 和 Kalyan Veeramachaneni. 2020. “通过条件 BERT 采样重写有意义的句子,并应用于欺骗文本分类器。” 康奈尔大学,计算机科学,arXiv:2010.11869,2020 年 10 月 22 日. arxiv.org/abs/2010.11869.

Zhang, Aston, Zachary C. Lipton, Mu Li, 和 Alexander J. Smola. 2020. “10.3: Transformer。” 在 Dive into Deep Learning 中. d2l.ai/chapter_attention-mechanisms/transformer.html.

Zhang, Han, Ian Goodfellow, Dimitris Metaxas, and Augustus Odena. 2019. “自注意力生成对抗网络。” 康奈尔大学,统计学,arXiv:1805.08318,2019 年 6 月 14 日。 arxiv.org/abs/1805.08318.

Zhu, Yukun, Ryan Kiros, Richard Zemel, Ruslan Salakhutdinov, Raquel Urtasun, Antonio Torralba, and Sanja Fidler. 2015. “对齐书籍与电影:通过观看电影和阅读书籍,向故事般的视觉解释迈进。” 康奈尔大学,计算机科学,arXiv:1506.06724,2015 年 6 月 22 日。 arxiv.org/abs/1506.06724.

第二十一章

Asadi, Kavosh, and Michael L. Littman. 2017. “强化学习的另一种 Softmax 操作符。” 在第 34 届国际机器学习大会论文集(澳大利亚悉尼,8 月 6 日至 11 日)。 arxiv.org/abs/1612.05628.

Craven, Mark, and David Page. 2018. “使用 DNN 的强化学习:从 AlphaGo 到 AlphaZero。” CS 760 课程笔记,春季,威斯康星大学麦迪逊分校医学与公共卫生学院。 www.biostat.wisc.edu/~craven/cs760/lectures/AlphaZero.pdf.

DeepMind team. 2020. “Alpha Go。” DeepMind(博客)。2020 年 10 月 8 日访问。 deepmind.com/research/alphago/.

Eden, Tim, Anthony Knittel, and Raphael van Uffelen. 2020. “强化学习。” 新南威尔士大学。2020 年 10 月 8 日访问。 www.cse.unsw.edu.au/~cs9417ml/RL1/algorithms.html.

François-Lavet, Vincent, Peter Henderson, Riashat Islam, Marc G. Bellemare, Joelle Pineau, “深度强化学习简介。” 康奈尔大学,机器学习,arXiv:1811.12560,2018 年 12 月 3 日。 arxiv.org/abs/1811.12560.

Hassabis, Demis, and David Silver. 2017. “AlphaGo Zero:从零开始学习。” DeepMind(博客),2017 年 10 月 18 日。 deepmind.com/blog/alphago-zero-learning-scratch/.

Matiisen, Tambet. 2015. “揭开深度强化学习的神秘面纱。” 计算神经科学实验室,塔尔图大学计算机科学学院,2015 年 12 月 15 日。 neuro.cs.ut.ee/demystifying-deep-reinforcement-learning/.

Melo, Francisco S. 2020. “Q 学习的收敛性:一个简单的证明。” 系统与机器人研究所,葡萄牙高级技术学院。2020 年 10 月 9 日访问。 users.isr.ist.utl.pt/~mtjspaan/readingGroup/ProofQlearning.pdf.

Mnih, Volodymyr, Koray Kavukcuoglu, David Silver, Alex Graves, Ioannis Antonoglou, Daan Wierstra, 和 Martin Riedmiller. 2013 年. “通过深度强化学习玩 Atari 游戏。” NIPS 深度学习研讨会,2013 年 12 月 19 日。arxiv.org/abs/1312.5602v1

Rummery, G. A., 和 M. Niranjan. 1994 年. “使用联结系统进行在线 Q 学习。” 剑桥大学工程系,英国,1994 年 9 月。citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.17.2539&rep=rep1&type=pdf

Silver, David, 等. 2017 年. “无须人类知识即可掌握围棋游戏。” 自然 550(2017 年 10 月 19 日):354–59。www.nature.com/articles/nature24270.epdf

Sutton, Richard S., 和 Andrew G. Baro. 2018 年. 强化学习:导论,第二版. 美国马萨诸塞州剑桥:MIT 出版社。可在www.incompleteideas.net/book/the-book-2nd.html查看。

Villanueva, John Carl. 2009 年. “宇宙中有多少个原子?” 宇宙今日,2009 年 7 月 30 日。www.universetoday.com/36302/atoms-in-the-universe/

Watkins, Christopher. 1989 年. “从延迟奖励中学习。” 博士论文,剑桥大学,英国。www.cs.rhul.ac.uk/~chrisw/new_thesis.pdf

第二十二章

Achlioptas, Panos, Olga Diamanti, Ioannis Mitliagkas, 和 Leonidas Guibas. 2018 年. “3D 点云的表示学习与对抗生成。” 康奈尔大学,计算机科学,arXiv:1707.02392,2018 年 6 月 12 日。arxiv.org/abs/1707.02392v1

Arjovsky, Martin, 和 Léon Bottou. 2017 年. “朝着训练生成对抗网络的原则性方法迈进。” 康奈尔大学,统计学,arXiv:1701.04862,2017 年 1 月 17 日。arxiv.org/abs/1701.04862v1

Arjovsky, Martin, Soumith Chintala, 和 Léon Bottou. 2017 年. “Wasserstein GAN。” 康奈尔大学,统计学,arXiv:1701.07875,2017 年 12 月 6 日。arxiv.org/abs/1701.07875v1

Bojanowski, Piotr, Armand Joulin, David Lopez-Paz, 和 Arthur Szlam. 2019 年. “优化生成网络的潜在空间。” 康奈尔大学,统计学,arXiv 1717.05776,2019 年 5 月 20 日。arxiv.org/abs/1707.05776

Chen, Janet, Su-I Lu, 和 Dan Vekhter. 2020 年. “博弈策略。” 载于 博弈论,斯坦福大学计算机科学系,斯坦福,加利福尼亚州。访问日期:2020 年 10 月 6 日。cs.stanford.edu/people/eroberts/courses/soco/projects/1998-99/game-theory/Minimax.html

Geitgey, Adam. 2017. “Machine Learning Is Fun Part 7: Abusing Generative Adversarial Networks to Make 8-bit Pixel Art.” Medium。发表日期:2017 年 2 月 12 日。medium.com/@ageitgey/abusing-generative-adversarial-networks-to-make-8-bit-pixel-art-e45d9b96cee7#.v1o6o0dyi.

Gildenblat, Jacob. 2020. “KERAS-DCGAN.” GitHub。访问日期:2020 年 10 月 6 日。github.com/jacobgil/keras-dcgan.

Goodfellow, Ian J., Jean Pouget-Abadie, Mehdi Mirza, Bing Xu, David Warde-Farley, Sherjil Ozair, Aaron Courville, 和 Yoshua Bengio. 2014. “Generative Adversarial Networks.” 康奈尔大学,统计学,arXiv:1406.2661,2014 年 6 月 10 日。arxiv.org/abs/1406.2661.

Goodfellow, Ian. 2016. “NIPS 2016 Tutorial: Generative Adversarial Networks.” 康奈尔大学,计算机科学,arXiv:1701.00160,2016 年 12 月 31 日。arxiv.org/abs/1701.00160.

Karras, Tero, Timo Aila, Samuli Laine, 和 Jaakko Lehtinen. 2018. “Progressive Growing of GANs for Improved Quality, Stability, and Variation.” 康奈尔大学,计算机科学,arXiv:1710.10196,2018 年 2 月 26 日。arxiv.org/abs/1710.10196.

Myers, Andrew. 2002. “CS312 Recitation 21: Minimax Search and Alpha-Beta Pruning.” 康奈尔大学,计算机科学系。www.cs.cornell.edu/courses/cs312/2002sp/lectures/rec21.htm.

Radford, Alec, Luke Metz, 和 Soumith Chintala. 2016. “Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks.” 康奈尔大学,计算机科学,arXiv:1511.06434,2016 年 1 月 7 日。arxiv.org/abs/1511.06434.

Watson, Joel. 2013. Strategy: An Introduction to Game Theory,第 3 版。纽约:W.W. Norton and Company。

Chapter 23

The Art Story Foundation, 2020. “Classical, Modern, and Contemporary Movements and Styles.” 艺术故事网站。访问日期:2020 年 10 月 7 日。www.theartstory.org/section_movements.htm.

Bonaccorso, Giuseppe. 2020. “Neural_Artistic_Style_Transfer.” GitHub。访问日期:2020 年 10 月 7 日。github.com/giuseppebonaccorso/keras_deepdream.

Chollet, François. 2017. Deep Learning with Python。纽约:Manning Publications。github.com/fchollet/deep-learning-with-python-notebooks/blob/master/8.3-neural-style-transfer.ipynb.

Gatys, Leon A., Alexander S. Ecker, 和 Matthias Bethge. 2015. “艺术风格的神经算法。” 康奈尔大学,计算机科学,arXiv:1508.06576,2015 年 9 月 2 日。 arxiv.org/abs/1508.06576.

Gatys, Leon A., Alexander S. Ecker, 和 Matthias Bethge. 2016. “使用卷积神经网络的图像风格迁移。” 载于 2016 年 IEEE 计算机视觉与模式识别大会论文集 (拉斯维加斯,NV,6 月 27 日至 30 日)。 pdfs.semanticscholar.org/7568/d13a82f7afa4be79f09c295940e48ec6db89.pdf.

Jing, Yongcheng, Yezhou Yang, Zunlei Feng, Jingwen Ye, 和 Mingli Song. 2018. “神经风格迁移:综述。” 康奈尔大学,计算机科学,arXiv:1705.04058v1,2018 年 10 月 30 日。 arxiv.org/abs/1705.04058.

Li, Yanghao, Naiyan Wang, Jiaying Liu, 和 Xiaodi Hou. 2017. “揭开神经风格迁移的神秘面纱。” 康奈尔大学,计算机科学,arXiv:1701.01036,2017 年 7 月 1 日。 arxiv.org/abs/1701.01036.

Lowensohn, Josh. 2014. “我让苹果的 QuickType 键盘接管了我的 iPhone。” The Verge (博客),2014 年 9 月 17 日。 www.theverge.com/2014/9/17/6337105/breaking-apples-quicktype-keyboard.

Majumdar, Somshubra. 2020. “Titu1994/Neural-Style-Transfer。” GitHub,2020 年 10 月 7 日访问。 github.com/titu1994/Neural-Style-Transfer.

Mordvintsev, Alexander, Christopher Olah, 和 Mike Tyka. 2015. “Inceptionism:深入神经网络的探索。” Google AI Blog,2015 年 6 月 17 日。 research.googleblog.com/2015/06/inceptionism-going-deeper-into-neural.html.

O’Neil, Cathy. 2016. 数学毁灭武器。纽约:百老汇图书。

Orlowski, Jeff. 2020. 社交困境。Exposure Labs,Argent Pictures,和 Netflix。2020 年 10 月 7 日访问。 www.thesocialdilemma.com/the-film/.

Ruder, Manuel, Alexey Dosovitskiy, 和 Thomas Brox. 2018. “视频和球形图像的艺术风格迁移。” 康奈尔大学,计算机科学,arXiv:1708.04538,2018 年 8 月 5 日。 arxiv.org/abs/1708.04538.

Simonyan, Karen, 和 Andrew Zisserman. 2020. “用于大规模视觉识别的非常深的卷积网络。” 视觉几何组 (博客),牛津大学。2020 年 10 月 7 日访问。 www.robots.ox.ac.uk/~vgg/research/very_deep/.

Tyka, Mike. 2015 年. “Deepdream/Inceptionism - 回顾。”Mike Tyka(博客),2015 年 7 月 21 日。 mtyka.github.io/code/2015/07/21/one-month-after-deepdream.html.

Wikipedia 作者. 2020 年. “风格(视觉艺术)。”维基百科。2020 年 9 月 2 日。 en.wikipedia.org/wiki/Style_(visual_arts).

第二十五章:图片来源

来自 Wikimedia 和 Wikiart 的艺术作品被认定为公有领域。来自 Pixabay 的图像受 Creative Commons CC0 许可证保护,这使它们属于公有领域。未注明来源的图像由作者提供。

第一章

  1. 图 1-3,香蕉

  2. pixabay.com/en/bananas-1642706

  3. 图 1-3,猫

  4. pixabay.com/en/cat-2360874

  5. 图 1-3,相机

  6. pixabay.com/en/photography-603036

  7. 图 1-3,玉米

  8. pixabay.com/en/pop-corn-785074

第十章

  1. 图 10-3,牛

  2. pixabay.com/en/cow-field-normande-800306

  3. 图 10-3,斑马

  4. pixabay.com/en/zebra-chapman-steppe-zebra-1975794

  5. 图 10-40,哈士奇

  6. pixabay.com/en/husky-sled-dogs-adamczak-1105338

  7. 图 10-40,哈士奇

  8. pixabay.com/en/husky-dog-outdoor-siberian-breed-1328899

  9. 图 10-40,哈士奇

  10. pixabay.com/en/dog-husky-sled-dog-animal-2016708

  11. 图 10-40,哈士奇

  12. pixabay.com/en/dog-husky-friend-2332240

  13. 图 10-40,哈士奇

  14. pixabay.com/en/green-grass-playground-nature-2562252

  15. 图 10-40,哈士奇

  16. pixabay.com/en/husky-dog-siberian-husky-sled-dog-2671006

第十六章

  1. 图 16-5,16-7,青蛙

  2. pixabay.com/en/frog-toxic-yellow-netherlands-1463831

第十七章

  1. 图 17-17 到 图 17-22,鸭子

  2. pixabay.com/en/duck-kaczor-animal-wild-bird-duck-268105

  3. 图 17-23,老虎

  4. pixabay.com/photos/tiger-animal-wildlife-mammal-165189/

第十八章

  1. 图 18-1,牛

  2. pixabay.com/en/cow-field-normande-800306

  3. 图 18-2,斑马

  4. pixabay.com/en/zebra-chapman-steppe-zebra-1975794

  5. 图 18-8 及本章各处,老虎

  6. pixabay.com/photos/tiger-animal-wildlife-mammal-165189/

第二十三章

  1. 图 23-3 及本章各处,青蛙

  2. pixabay.com/en/waters-nature-frog-animal-swim-3038803/

  3. 图 23-5,狗

  4. pixabay.com/photos/labrador-retriever-dog-pet-1210559/

  5. 图 23-6,图 23-7,图 23-9,巴勃罗·毕加索,“自画像 1907”

  6. www.wikiart.org/en/pablo-picasso/self-portrait-1907

  7. 图 23-9,文森特·梵高,“星夜”

  8. commons.wikimedia.org/wiki/File:VanGogh-starry_night_ballance1.jpg

  9. 图 23-9,J.M.W. 特纳,“米诺陶号沉船”

  10. commons.wikimedia.org/wiki/File:Shipwreck_of_the_Minotaur_William_Turner.jpg

  11. 图 23-9,爱德华·蒙克,“呐喊”

  12. www.wikiart.org/en/edvard-munch/the-scream-1893

  13. 图 23-9,巴勃罗·毕加索,“坐着的女性裸体”

  14. www.wikiart.org/en/pablo-picasso/seated-female-nude-1910

  15. 图 23-9,爱德华·霍普,“夜鹰”

  16. commons.wikimedia.org/wiki/File:Nighthawks_by_Edward_Hopper_1942.jpg

  17. 图 23-9,克劳德·莫奈,“水仙花,黄色和丁香色”

  18. www.wikiart.org/en/claude-monet/water-lilies-yellow-and-lilac-1917

  19. 图 23-9,瓦西里·康定斯基,“构图 VII”

  20. www.wikiart.org/en/wassily-kandinsky/composition-vii-1913

  21. 图 23-12,城镇

  22. pixabay.com/en/town-building-urban-architecture-2430571/

第二十六章:索引

请注意,索引链接指向每个术语的大致位置。

符号与数字

α (alpha),Q-learning 混合,629

β1 (beta 1),Adam 参数,421

β2 (beta 2),Adam 参数,421

δ (delta),误差变化比例,357

Δ (Delta),误差变化比例,357

ε (epsilon)

数值稳定性参数,419

Q-learning 策略,630

η (eta),学习率

介绍,377

优化过程中实际问题,389

γ (gamma)

Adagrad 缩放参数,419

折扣因子,611

动量缩放因子,410

λ (lambda),正则化控制,204

σ (sigma),标准差,24

θ (theta),硬币偏差,103

P(A),简单概率,50

P(A,B),联合概率,53

P(A|B),条件概率,50

1-hot 编码,226

1D 卷积,446

1×1 卷积,447

20 问题,269

68-95-99.7 规则,25

A

准确度,64

激活函数

指数 ReLU,340

函数画廊,344

Heaviside 步阶,334

身份,331

介绍,318

渗漏 ReLU,337

线性,332

逻辑曲线,341

maxout,338

嘈杂的 ReLU,339

参数化 ReLU,337

分段线性,336

ReLU,336

防止网络崩溃的作用,329

移位 ReLU,338

sigmoid,341

符号函数,335

正弦波,343

softmax,345

softplus,340

阶梯步阶,333

步长,333

swish,341

tanh,341

单位阶跃,334

演员,602

非循环图,324

Adaboost,309

Adadelta,418

Adagrad,417

Adam,420

自适应编码,137

自适应梯度学习,417

自适应矩估计,420

哈克贝利·芬的冒险,146

对抗扰动,491

对手,491

智能体,602

AI 冬天,317

祖先(在神经网络中),324

锚点,436

动物饼干,559

安斯科姆四重奏,40

回答问题,540

阿波罗航天器,297

神经网络中的弧,323

考古学抛硬币游戏(情景),103

数组,329

人工神经元。参见 神经元

大气示例,205

空洞卷积,457

攻击(带扰动),493

注意力

引言,574

键,575

Q/KV,579

QKV,575

查询,575

自注意力,576

值,575

注意力层

引言,578

示意图符号,581

自编码器

基本结构,495

卷积,516

潜在层,501

自动贝叶斯,97

自回归,541

*均值

常见含义,16

调和*均数,71

均值,16

中位数,16

众数,16

*均池化,452

B

婴儿身长(情景),33

反向传播,351

反向传播,351

时间反向传播,553

反向传播,376

装袋法,299

*衡决策树,270

基础图像,680

批量梯度下降,401

批归一化

讨论,424

示意图符号,424

巴克斯特,威廉,139

贝叶斯规则

讨论,95

证据,96

假设,102

似然,96

线性拟合,212

多个假设,109

观察,102

后验,96

先验,96

精细化估计,101

重复,101

声明,94

贝叶斯定理。参见 贝叶斯规则

贝叶斯,托马斯,83

贝叶斯概率

与频率派相比,85

概述,85

光束搜索,595

钟形曲线,22

伯努利分布,26

BERT,590

双向循环神经网络(bi-RNN),559

偏差

人工神经元中的 318

偏置技巧,320

曲线族中的,204

翻转硬币的结果,86

偏置输入,偏置输出,673

偏差-方差权衡,210

双向 RNN 层

介绍,559

结构符号,559

二分类器网络,378

二进制交叉熵,150

二元相关性,161

绑定(到神经元),314

位,136

非均匀性福祉,173

结构福祉,173

数字块,329

BMI(体质指数),243

强驱动方法,400

提升,302

自助法,299

自助聚合,299

自助法。 自助法

自助法

在集成中的袋装(bagging),299

在统计学中,31

线性,39

多重,39

负面,38

部分,39

正面,38

简单,39

强,38

弱,38

瓶颈,501

在最小值附*反弹,394

边界

分类器,156

决策边界,158

方法,156

简单,58

方框滤波器,451

BPTT(时间反向传播),553

分支(在决策树中),270

构建圣代(场景),548

C

C(支持向量机参数),285

倒影,157

笛卡尔空间,213

类别数据,226

类别分布,27

分类变量决策树,271

分类。 分类器

分类器。 分类器

类别,156

通道(在张量中),430

作弊

寻找坦克时,185

过拟合时,197

子节点

在决策树中,271

在神经网络中,324

选择商店背景音乐(场景),198

类别,156

分类。 分类器

分类器

二元,156

二元相关性,161

多类别,160

非参数化,264

一对多,161

一对多,161

一对一,163

一对剩余,161

概述,6

参数化,264

训练,182

结束(训练任务),591

完形填空任务,591

聚类

K 均值,166

概述,8

CNN(卷积神经网络), 431

CNN-LSTM, 557

代码

自适应, 137

固定长度, 141

固定长度, 141

霍夫曼, 148

可变比特率, 143

硬币检测(场景), 86

投掷硬币

基本思想, 86

贝叶斯方法, 87

投掷硬币游戏(场景), 87

崩溃, 329

Common Crawl, 597

压缩, 496

压缩比, 148

隐蔽数据投毒, 598

条件概率, 50

置信度(在集成投票中), 299

置信区间, 32

混淆矩阵

使用贝叶斯法则, 97

正确分析, 77

定义, 60

正确使用, 74

错误使用, 76

连接组, 315

数据准备中的一致性, 223

恒定分布, 22

固定长度编码, 141

污染数据, 187

内容融合, 498

内容损失(用于风格迁移), 683

上下文(在信息中), 135

上下文向量, 562

上下文化词嵌入, 571

连续概率分布, 21

连续变量决策树, 272

收敛

GAN 的, 670

Q 学习的, 633

卷积神经网络, 431

卷积

介绍, 433

层, 446

层级示意符号, 471

多维, 443

步长, 453

转置卷积, 457

带有下采样层符号的卷积, 521

带有上采样层符号的卷积, 521

卷积自编码器, 516

卷积神经网络, 431

正确性, 56

相关性, 37

相关系数, 38

成本(神经网络错误), 352

协方差, 35

cpd(连续概率分布), 21

信用分配问题, 608

交叉熵, 145

交叉验证

基础, 190

信息泄露, 239

k 折交叉验证, 192

维度灾难, 168

突点, 118

循环(在神经网络中), 323

D

DAG(有向无环图),324

投掷飞镖(场景),48

数据

数据增强,255

清理,221

污染,187

卫生,187

漏泄,187

中毒,598

准备,221

数据集扩展,255

DCGAN(深度卷积生成对抗网络),667

死神经元,345

衰减参数(对于学习率),396

衰减计划,398

决策边界,158

决策节点(在决策树中),270

决策区域,158

决策树桩,303

决策树

*衡,270

分支,270

子节点,271

决策节点,270

深度,270

深度限制,280

远程子节点,271

边缘,270

集成,299

直接子节点,271

内部节点,270

leaf, 270

链接,270

节点,270

过拟合,275

概述,269

父节点,271

剪枝,280

根节点,270

同胞节点,271

树桩,303

子树,271

终端节点,270

不*衡,270

解码器

对于自编码器,496

对于 seq2seq,561

解码器块(变压器),587

反卷积(作为转置卷积),457

深度卷积生成对抗网络,667

深度梦想,675

深度学习

网络结构,326

概述,10

深度强化学习,647

延迟步长,550

延迟指数衰减,398

增量值(对于神经元),360

去噪,519

密集层。参见 全连接层

密度,169

因变量,29

部署系统,183

深度

在决策树中,270

深度学习网络的,327

张量的,430

深度限制,280

导数

定义,119

要求,123

后代(在神经网络中),324

确定性

环境,611

函数,118

确定性环境,611

DFR(折扣未来奖励), 609

查尔斯·狄更斯, 545

数字, 4

膨胀卷积, 457

降维, 243

收益递减(在集成中), 300

有向无环图, 324

定向散度, 151

有向图, 323

折扣因子, 611

折扣未来奖励, 609

离散概率分布, 20

鉴别信息, 151

鉴别器, 650

决策树中的远离节点, 271

蒸馏, 593

区分奶牛和斑马(场景), 224

dit, 137

域, 158

下采样, 451

裁员, 457

下游

网络, 542

任务, 542

苏斯博士, 137

随机失活

讨论, 422

线路符号, 422

双重表示, 213

虚拟变量, 226

E

E(神经网络误差), 359

早期停止, 202

在决策树中, 270

在神经网络中, 323

鸡蛋

受精的, 157

放弃者, 160

未受精的, 157

胜者, 160

yolker, 160

特征狗, 256

特征向量, 256

元素

在神经网络中, 323

在张量中, 430

按元素处理, 234

电梯调度(场景), 9

ELMo, 571

ELU(指数 ReLU 激活函数), 340

嵌入

上下文化的, 572

句子, 571

空间, 569

标记, 540

单词, 566

嵌入层线路符号, 574

编码器

对于自编码器, 496

对于 seq2seq, 561

编码器块(变压器), 587

编码器-解码器注意力, 579

集成, 280

熵, 143

环境(在强化学习中), 602

环境状态, 605

轮次, 605

纪元, 182

纪元梯度下降, 401

epsilon-贪心策略, 630

epsilon-soft 策略, 630

误差(神经网络误差), 352

错误曲线(神经网络错误),355

基于错误的衰减,399

事件

在信息理论中,136

在概率中,50

证据,96

期望值,28

专家系统,4

可解释性。 透明度

梯度爆炸,553

开发利用,608

探索或开发困境,608

指数衰减(学习率的),396

指数 ReLU 激活函数,340

超级树,302

极端随机树,302

F

f1 分数,71

公正的硬币,86

虚假警报,73

假发现率,73

假阴性,60

假阴性率,73

假阳性,60

假阳性率,73

FC。 完全连接层

特征

作为样本的一部分,157

卷积滤波器的目标,438

特征袋法,301

特征检测器,438

特征工程,5

特征过滤,243

特征图,438

特征选择,243

特征处理,233

前馈网络,322

反馈,602

反馈(神经网络中的),324

受精,157

少量训练,594

张量的纤维大小,430

纤维,234

编码的保真度,497

用球体填充盒子(场景),175

滤波器

核,433

值,433

寻找电影中的动物(场景),566

寻找信息,4

微调,542

有限分布,22

拳头(发莫尔斯电码),138

拟合风速数据(场景),205

固定长度编码,141

固定步长衰减,399

*坦分布,22

扁*层

示例,475

插图,476

示意符号,474

翻转游戏(场景),614

折叠(交叉验证中的),192

卷积滤波器的足迹,436

森林,301

伪造货币(场景),650

分数卷积,457

频率学派概率

与贝叶斯方法相比,85

概览,84

冻结权重

深度梦境(deep dreaming),677

在生成对抗网络中(in a GAN),660

完全模态崩溃(full modal collapse),671

全连接层(fully connected layer)

介绍(introduction),328

示意符号(schematic symbol),328

全连接网络(fully connected network),328

函数(function)

参数(argument),118

连续的(continuous),118

图示(graphed),124

数学的(mathematical),118

返回值(return values),118

单值(single-valued),119

*滑(smooth),118

G

g(梯度),410

博弈论(game theory),656

生成对抗网络(GAN),649

垃圾进,垃圾出,223

门控递归单元(gated recurrent unit),554

高斯分布(Gaussian distribution),22

GD。参见 梯度下降(gradient descent)

泛化(generalization)

准确率(accuracy),197

错误(error),197

损失(loss),197

生成图像(用于风格迁移)(generated image for style transfer),680

生成对抗网络(generative adversarial network),649

生成器(generator),650

生成器-判别器(generator-discriminator),664

基尼不纯度(Gini impurity),281

Glorot 正态初始化(Glorot Normal initialization),325

Glorot 均匀初始化(Glorot Uniform initialization),325

GPT-2,593

GPT-3

讨论(discussion),596

性能(performance),597

梯度(gradient)

定义(definition),126

消失(vanishing),129

零(zero),129

梯度上升(gradient ascent),481

梯度下降(gradient descent)

Adadelta,418

Adagrad,417

Adam,420

批次(batch),401

纪元(epoch),401

小批量(mini-batch),405

动量(momentum),409

Nesterov,414

RMSprop,418

随机(stochastic),403

典型意义(typical meaning),407

Gram 矩阵(Gram matrix),681

图(graph)

无环(acyclic),324

有向(directed),323

介绍(introduction),323

加权图(weighted graph),324

图论(graph theory),323

大丹犬(Great Dane),184

贪婪算法(决策树)(greedy algorithm for decision trees),274

Green Eggs and Ham,137

网格(grid),329

真实值(ground truth),57

分组。参见 聚类(clustering)

GRU(门控递归单元),554

吉他数据(guitar data),228

H

He 正态初始化(He Normal initialization),325

He 均匀初始化(He Uniform initialization),325

头(在注意力层中)(head in attention layer),580

Heaviside 阶跃激活函数(Heaviside step activation function),334

隐藏层(hidden layer),327

隐藏状态(hidden state),549

层级(hierarchy)

特征的(of features),440

过滤器,461

尺度问题,471

高维空间,42

高维怪异性

超立方体,175

hyperorange,178

超球体,175

在超立方体中包装超球体,177

超立方体中的超球体体积,175

山丘(3D 表面),129

命中率,66

福尔摩斯,夏洛克(故事)

使用 GPT2 生成,596

使用 RNN 生成,555

霍普尔,爱德华,685

哈夫曼编码,148

哈士奇,185

超立方体,175

hyperorange,178

超参数

聚类数量,166

调整,168

超球体,175

假设(在贝叶斯规则中),102

I

i.i.d.(独立同分布),29

冰淇淋店(场景),55

理想化曲线,205

同分布,29

识别狗品种(场景),183

身份激活函数,331

IG(信息增益),281

图像分类器,6

图像矩阵(用于风格迁移),681

ImageNet,478

直接子节点(在决策树中),271

不纯度,280

初始主义,679

独立同分布,29

自变量,29

惯性,408

无穷分布,24

信息

定义,137

密度,143

发散,151

发现,4

增益,281

属性,136

信息交换

全局上下文,135

局部上下文,135

接收者,134

发送者,134

惊讶,134

信息泄漏

基本概念,187

在交叉验证中,239

信息理论,133

初始状态(在强化学习中),605

神经网络初始化,325

输入层,327

输入张量,329

即时奖励,610

内部节点(在决策树中),270

区间衰减,399

逆变换,234

编码反转,497

J

联合概率,53

JPG 编码器,495

垃圾场(场景), 17

K

k

PCA 后的维度, 249

聚类数目, 166

折数, 192

最*邻数目, 264

k-means 聚类, 166

k *邻算法, 264

Kandinsky, Wassily, 685

卷积核(卷积滤波器的核), 433

核技巧, 287

关键字(对于 Transformer), 575

切线线(切线), 122

KL 散度, 151

kNN(k *邻算法), 264

Kullback-Leibler 散度, 151

L

L 学习, 616

L 表, 617

L 值, 617

标签, 156

语言模型, 541

最后时刻停止, 202

潜在层, 501

潜在空间, 509

潜变量, 501

集成构建中的收益递减法则, 300

层归一化, 583

层次示意图符号

注意力, 581

批归一化, 425

双向递归, 559

卷积, 471

卷积与下采样, 521

卷积与上采样, 521

随机失活, 423

嵌入, 574

展*, 474

完全连接, 328

遮蔽注意力, 588

多头注意力, 581

norm-add, 583

池化, 451

位置信息嵌入, 584

递归, 555

递归单元, 551

重新调整形状, 667

自注意力, 581

引言, 322

概述, 10

懒算法(kNN), 265

叶节点(决策树中的叶子节点), 270

泄漏 ReLU 激活函数, 337

学习率

在神经网络中, 377

在 Q 学习中, 629

学习率调整

常数, 391

延迟指数衰减, 398

基于误差的衰减, 399

指数衰减, 396

固定步长衰减, 399

间隔衰减, 399

慢速学习法(场景), 354

LeCun 正态初始化, 325

LeCun 均匀初始化, 325

英语中字母频率, 139

寻求生命的探测器(场景), 97

似然, 96

直线(在神经网络中),323

使用贝叶斯规则拟合直线(场景),212

线性激活函数,332

线性函数,331

线性层。参见 全连接层

链接(在决策树中),270

列表,329

局部感受野,436

逻辑流程,540

逻辑曲线激活函数,341

长短期记忆,553

长期依赖问题,563

循环(在神经网络中),324

损失,352

无损编码,496

有损编码,496

糟糕的学习,616

低维犬种描述(场景),255

低通滤波器,451

LSTM(长短期记忆),553

M

m

神经元值的修改,357

动量(在梯度下降中),410

机器学习,3

梯度的大小,128

曼彻斯特候选人,598

映射(变换),236

边际概率,55

马克思,格劳乔,559

掩码注意力层示意图,588

掩码(注意力解码器),588

匹配面罩(场景),462

矩阵,329

最大池化,452

最大上升,126

最大下降,126

函数的最大值

使用导数查找,125

使用梯度查找,128

全局最大值,119

局部最大值,121

maxout 激活函数,338

McGlassface,眼镜,61

均值,16

均值归一化,228

高斯均值,24

均方误差,365

均值减法,228

中位数,16

慢速最大值,632

用镜子发送消息(场景),497

小批量,405

小批量梯度下降,405

函数的最小值

使用导数查找,125

使用梯度查找,128

全局最小值,119

局部最小值,121

混合油漆颜色(场景),574

MLP(多层感知器),328

MNIST

用于自编码器,505

用于卷积,473

用于 GAN,667

模态崩溃,671

模式, 16

动量, 408

动量梯度下降, 409

莫奈,克劳德, 685

拇指病(情境),63

莫尔斯电码, 137

莫尔斯,塞缪尔, 139

MP (拇指病), 63

MP3 编码器, 495

均方误差 (MSE), 365

多头注意力

介绍, 580

示意图符号, 581

多类分类, 160

多维卷积, 443

多滤波损失, 676

多层损失, 676

多层感知机, 328

多项式分布, 27

多重相关性, 39

多变量变换, 231

蒙克,爱德华, 685

木偶剧

BERT, 590

ELMo, 571

音乐系统

收集数据, 199

良好拟合, 201

过拟合, 199

欠拟合, 200

N

n-gram, 595

朴素贝叶斯分类器, 290

纳什均衡, 657

自然语言生成, 541

自然语言处理, 540

自然语言理解, 540

井字棋, 603

负相关, 38

负协方差, 35

负梯度, 128

负预测值, 73

邻居 (kNN), 265

邻域, 120

Nesterov 动量, 414

网络崩溃, 329

神经网络

介绍, 315

简单示例, 321

神经风格迁移, 680

神经元

人工, 317

死亡, 345

概述, 11

真实, 314

神经递质, 314

新视野号太空探测器, 190

下一句预测, 591

自然语言生成 (NLG), 541

NLP (自然语言处理), 540

NLU (自然语言理解), 540

无免费午餐定理, 422

节点

在决策树中, 270

在神经网络中, 323

节点分裂

基尼不纯度, 281

不纯度, 280

信息增益, 281

概述, 272

纯度, 280

噪声曲线, 205

噪声 ReLU 激活函数, 339

名义数据, 226

非确定性(变分自编码器), 521

非参数分类器, 264

非线性函数, 331

非线性。参见 激活函数

norm-add

定义, 583

示意符号, 583

正态偏差, 24

正态分布, 22

归一化, 228

NSP(下一个句子预测), 591

数值数据, 226

O

OAA(对所有进行分类), 161

可观测性, 608

观察(在贝叶斯法则中), 102

离线算法, 403

按需算法(kNN), 265

一对所有, 161

一对一卷积, 447

独热编码, 226

一对多, 161

一对一, 153

一对其余, 161

在线算法, 405

优化器, 387

序数数据, 226

异常值

清理数据时, 223

在划定边界时, 201

输出层, 327

输出张量, 329

OvA(一对多), 161

过拟合

决策树, 275

定义, 196

狗的品种, 185

过冲(在梯度下降过程中), 396

OvO(一对一), 163

OvR(对其他分类进行分类), 161

P

填充, 440

参数空间(用于自编码器), 508

参数化混合, 498

参数化分类器, 264

参数化 ReLU 激活函数, 337

改写(文本), 540

在决策树中, 271

在神经网络中, 324

偏相关, 39

部分模态坍塌, 671

PCA。参见 主成分分析

PDF(概率密度函数), 21

惩罚(用于神经网络错误), 352

感知机

介绍, 316

Mark I 感知机, 317

完美精度, 69

完美召回率, 69

性能指标

插图, 75

总结, 73

扰动, 491

毕加索, 巴勃罗, 681

分段线性激活函数, 336

像素, 430

行星开采(场景), 97

*台(3D 表面), 129

多数投票, 299

冥王星图像,190

pmf(概率质量函数),20

点对点前馈层,587

策略,606

多义性,559

贵宾犬,183

池化

*均值,452

讨论,449

层,452

最大值,452

示意符号,451

人口(统计学),32

位置信息

编码,584

示意符号,584

位置信息嵌入,585

正相关,38

正协方差,35

正向预测值,64

后验,96

精度

定义,64

完美,69

精度-召回权衡,67

预测值,57

预测温度下的交通(场景),234

预测,6

预训练,542

主成分分析

描述,244

最大方差线,246

投影,244

先验,96

概率

条件,50

依赖性,50

事件,50

独立性,50

介绍,19

联合,53

边际,55

简单,50

概率密度函数,21

概率分布

钟形曲线,22

伯努利,26

分类的,27

连续,20

垃圾场中的例子,19

有限,22

高斯,22

无限,24

介绍,17

多项式,27

正态,22

均匀,21

概率质量函数,20

ProGAN,670

投影,244

提示(文本生成),541

保护一个运输中的橙子(场景),177

剪枝,280

纯度,280

Q

Q 学习,626

Q 表,627

Q 值,627

Q/KV(查询/键,v 值),579

QKV(查询,键,v 值),575

二次代价函数,365

娃娃质量控制(场景),61

质量学习,626

定量数据,226

查询(对于转换器),575

退出者,160

R

随机森林, 301

随机标记, 302

随机变量

描述, 20

绘图, 20

介绍, 17

随机性, 16

真实世界数据, 183

记忆

定义, 66

完美的, 69

受体位点(神经元), 314

配方, 6

重构(编码信号的还原), 496

循环单元

介绍, 550

原理符号, 551

循环层原理符号, 555

循环神经网络

双向, 558

深度, 557

深度双向, 560

介绍, 548

卷积滤波器的参考点, 436

正则化

批量归一化, 424

丢弃法, 422

介绍, 203

层归一化, 583

强化学习, 602

拒绝, 73

相对熵, 151

发布数据, 183

发布的系统, 183

ReLU 激活函数, 336

记住人名(情景), 196

重新参数化技巧, 527

表示融合, 498

形状变换层

介绍, 667

原理符号, 667

残差连接, 582

结果奖励, 610

奖励

折扣未来, 609

最终, 604

即时, 610

介绍, 602

结果, 610

总和, 609

总未来, 609

最终的, 604

奖励信号。 奖励

被操控的硬币, 86

强化学习, 602

RMSprop, 418

RNN。 循环神经网络

卷起的图示, 550

决策树中的根节点, 270

旋转验证。 交叉验证

规则, 4

S

鞍点(3D 曲面), 130

样本, 156

样本集, 32

样本处理, 232

SARSA, 639

情景

考古学聚类, 8

考古学掷硬币游戏, 103

婴儿长度, 33

制作圣代, 548

选择商店背景音乐, 198

硬币检测, 86

掷硬币游戏, 87

投掷飞镖, 48

区分牛和斑马, 224

电梯调度, 9

将一个球体填入盒子, 175

寻找电影中的动物, 566

拟合风速数据, 205

Flippers 游戏, 614

伪造货币, 650

冰淇淋店, 55

识别犬种, 183

垃圾场, 17

慢速学习法, 354

寻找生命的探测器, 97

使用贝叶斯定理进行线性拟合, 212

低维度犬种描述, 255

匹配面罩, 462

通过镜子传递信息, 497

油漆混色, 574

morbus pollicus, 63

行星采矿, 97

通过温度预测交通, 234

保护运输中的橙子, 177

娃娃的质量控制, 61

记住人的名字, 196

分类蛋, 156

疾病检测, 63

短信, 135

传输书籍, 146

科学记数法, 222

种子(用于文本生成), 541

选择

有放回抽样(SWR), 30

无放回抽样(SWOR), 30

有放回, 30

无放回, 30

有放回选择, 30

无放回选择, 30

选择性持久短期记忆, 553

自注意力, 576

自注意力层

引言, 578

原理图符号, 581

语义学, 546

半监督学习, 501

敏感度, 66

句子嵌入, 571

情感分析

引言, 540

使用 BERT, 592

seq2seq, 561

序列, 539

SGD(随机梯度下降), 401

香农, 克劳德, 133

移位不变性, 453

移位 ReLU 激活函数, 338

SI(斜截式)空间, 213

西伯利亚哈士奇, 185

同胞(决策树中的), 271

Sigmoid 激活函数, 341

符号激活函数, 334

简单相关性, 39

简单概率, 50

正弦波激活函数, 343

跳跃连接, 582

熊的群体, 568

切片处理

按元素计算,234

按特征计算,233

按样本计算,232

滑动窗口,543

切线(斜率),122

softmax

作为激活函数,345

在 Q 学习中,630

softplus 激活函数,340

排序玩偶(场景),61

排序鸡蛋(场景),156

空间滤波器,436

特异性,73

分裂。节点分裂

阶梯激活函数,332

标准差,24

标准化,229

星际飞船忒修斯,97

状态

代理(强化学习),605

环境(强化学习),605

隐藏(RNN),549

一个 RNN,548

启动(RNN),548

变量(强化学习),605

状态变量,605

统计,15

步骤激活函数,332

斯蒂文森,罗伯特·路易斯,137

随机环境,611

随机梯度下降,401

步长,453

强学习者,303

风格图像,680

风格损失,683

风格矩阵,681

风格迁移,680

主观贝叶斯,97

子树(决策树中的),271

总结(文本),540

监督学习

用于分类,156

概述,6

支持层,327

支持向量,284

支持向量机

核技巧,287

概述,282

严格参数 C,285

支持向量,284

惊讶,134

支持向量机。支持向量机

滤波器(扫掠),433

swish 激活函数,340

SWOR(无替换选择),30

SWR(有替换选择),30

T

双城记

带嵌入,579

预测,545

切线,122

切线,122

目标(一热编码标签),361

温度(选择输出),595

张量,328

终端节点(在决策树中),270

测试数据

定义,186

永远无法从中学习,186

疾病测试(场景),63

短信(场景),135

TFR(总未来奖励), 609

阈值

对于人工神经元, 316

对于真实神经元, 314

井字棋, 603

时间步长, 549

蟾蜍视觉系统

层次结构, 461

引言, 437

令牌

嵌入, 566

作为一个词, 540

总未来奖励, 609

总奖励, 609

训练

准确度, 196

基本概念, 182

深度网络概述, 12

错误, 196

流程图, 189

损失, 196

训练集, 182

传递函数。参见 激活函数

迁移学习, 542

变换

逆, 234

多变量, 231

概述, 223

单变量, 231

转换器

引言, 586

结构, 588

翻译, 540

传送书籍(场景), 146

透明度

决策树, 274

一对一分类器, 164

转置卷积, 457

宝岛, 137

三重模块冗余, 297

真实发现率, 73

真实负例, 60

真实正例, 60

真实正例率, 66

乔治·特纳, 685

马克·吐温, 146

双月数据集, 275

I 型错误, 73

II 型错误, 73

U

不*衡决策树, 270

不确定性, 143

欠拟合, 197

不公*的硬币, 86

未受精, 157

均匀分布, 21

单位(人工神经元), 315

单位步激活函数, 334

单变量变换, 231

通用语言模型微调, 574

普适扰动, 491

展开图, 550

无监督学习, 8

更新规则, 617

上采样, 457

增大尺寸, 457

用户数据, 183

V

VAE(变分自编码器), 521

艾尔弗雷德·维尔, 139

验证数据。参见 验证集

验证错误, 197

验证集

定义, 187

从中估算误差, 189

山谷(3D 表面), 129

值(对于转换器), 575

文森特·梵高, 685

消失梯度

在表面上, 128

训练一个 RNN, 553

可变比特率编码, 143

方差

相对于偏置, 204

在统计学中, 26

方差归一化, 229

变分自编码器, 521

向量, 329

顶点(神经网络中的), 323

VGG16

用于创意应用, 676

引言, 478

可视化滤波器, 480

词汇表, 541

体积, 329

投票(集成方法)

信心, 299

概述, 298

多数投票, 299

加权多数投票, 299

W

弱学习器, 303

权重

神经元命名约定, 322

对神经元的作用, 316

深度网络中的概述, 11

权重共享, 433

加权硬币, 86

加权图, 324

加权多数投票, 299

奇异性(高维), 175

(递归单元的)宽度, 551

胜者(蛋), 160

神经网络中的连接, 323

词嵌入, 566

X

Xavier 正常初始化, 325

Xavier 均匀初始化, 325

Y

蛋黄, 160

约克夏犬, 184

Z

零梯度, 128

零填充, 442

零点, 436

零维数组, 328

零样本训练, 594

本书的构思是在一对三折科学展览板上贴满了几十张黄色便签纸,每完成一个部分就贴上一个闪亮的星星贴纸。手稿是在 MacBook Pro 和 iMac 上使用 vi 文本编辑器编写的,生成 Markdeep 文件。最终编辑是使用 Microsoft Word 完成的,随后使用 Adobe Acrobat 和 Adobe InDesign 进行处理。插图是用彩色笔手绘的,然后在 Adobe Illustrator 和 Photoshop 中重新绘制。计算机生成的图像是通过在 Jupyter notebooks 中编写的 Python 代码生成的。重要的 Python 库包括 scikit-learn、scikit-image、numpy、scipy、pandas、matplotlib、TensorFlow 和 PyTorch。

posted @ 2025-12-01 09:40  绝不原创的飞龙  阅读(0)  评论(0)    收藏  举报