原书地址: https://zh.d2l.ai/index.html 可以在线也可以直接下载电子书看。作者里有大家熟知的大牛李沐 。
课件、作业、教学视频等资源可参考伯克利“深度学习导论” 课程大纲 中的链接(中文版课件)。
基于本书PyTorch版的教学视频在: B站
还有另一个项目是它的笔记,star是也上2k了,也可以看看:DeepLearning-MuLi-Notes
《动手学习深度学习》是李沐老师(AWS 资深首席科学家,美国卡内基梅隆大学计算机系博士)主讲的一系列深度学习视频。本项目收集了我们在寒假期间学习《动手学习深度学习》过程中详细的markdown笔记和相关的jupyter代码。赠人玫瑰,手留余香,我们将所有的markdown笔记开源,希望在自己学习的同时,也对大家学习掌握李沐老师的《动手学习深度学习》有所帮助。
另外还有一些其他资料也可以看看:
这篇文章仅做摘录和个人整理。建议直接看上边的电子书。排版上我更推荐看 pdf 版本。
全书结构
- 第一部分包括基础知识和预备知识。 1节 提供深度学习的入门课程。然后在 2节 中,我们将快速介绍实践深度学习所需的前提条件,例如如何存储和处理数据,以及如何应用基于线性代数、微积分和概率基本概念的各种数值运算。 3节 和 4节 涵盖了深度学习的最基本概念和技术,例如线性回归、多层感知机和正则化。
- 接下来的五章集中讨论现代深度学习技术。 5节 描述了深度学习计算的各种关键组件,并为我们随后实现更复杂的模型奠定了基础。接下来,在 6节 和 7节 中,我们介绍了卷积神经网络(convolutional neural network,CNN),这是构成大多数现代计算机视觉系统骨干的强大工具。随后,在 8节 和 9节 中,我们引入了循环神经网络(recurrent neural network,RNN),这是一种利用数据中的时间或序列结构的模型,通常用于自然语言处理和时间序列预测。在 10节 中,我们介绍了一类新的模型,它采用了一种称为注意力机制的技术,最近它们已经开始在自然语言处理中取代循环神经网络。这一部分将帮助读者快速了解大多数现代深度学习应用背后的基本工具。
- 第三部分讨论可伸缩性、效率和应用程序。 首先,在 11节 中,我们讨论了用于训练深度学习模型的几种常用优化算法。下一章 12节 将探讨影响深度学习代码计算性能的几个关键因素。在 13节 中,我们展示了深度学习在计算机视觉中的主要应用。在 14节 和 15节 中,我们展示了如何预训练语言表示模型并将其应用于自然语言处理任务。
一、引言
机器学习分为3类
- 监督学习(supervised learning)擅长在“给定输入特征”的情况下预测标签。
- 数据中不含有“目标”的机器学习问题通常被为无监督学习(unsupervised learning)
- 与预测不同,“与真实环境互动”的人工智能是“智能代理”,而不仅是“预测模型”。与环境交互并采取行动的最终可能会专注于强化学习(reinforcement learning)。在每个特定时间点,智能体从环境接收一些观察(observation),并且必须选择一个动作(action),然后通过某种机制(有时称为执行器)将其传输回环境,最后智能体从环境中获得奖励(reward)。 此后新一轮循环开始。
1.1 监督学习
需要向模型提供巨大数据集:每个样本包含特征和相应标签值:
-
回归(regression)是最简单的监督学习任务之一。 假设有一组房屋销售数据表格,其中每行对应一个房子,每列对应一个相关的属性,例如房屋的面积、卧室的数量、浴室的数量以及到镇中心的步行距离,等等。 每一行的属性构成了一个房子样本的特征向量。 当人们在市场上寻找新房子时,可能需要估计一栋房子的公平市场价值。
判断回归问题的一个很好的经验法则是,任何有关“有多少”的问题很可能就是回归问题。比如:
- 这个手术需要多少小时;
- 在未来6小时,这个镇会有多少降雨量。
- 预测用户对一部电影的评分
-
分类(classification)问题。 分类问题希望模型能够预测样本属于哪个类别(category,正式称为类(class))。
回归是训练一个回归函数来输出一个数值; 分类是训练一个分类器来输出预测的类别。
- 手写数字可能有10类,标签被设置为数字0~9。
- 最简单的分类问题是只有两类,这被称之为二项分类(binomial classification)。 例如,数据集可能由动物图像组成,标签可能是猫狗{猫,狗}两类。
-
标记问题。学习预测不相互排斥的类别的问题称为多标签分类(multi-label classification)。
- 人们在技术博客上贴的标签,比如“机器学习”“技术”“小工具”“编程语言”“Linux”“云计算”“AWS”。
- 一篇典型的文章可能会用5~10个标签,因为这些概念是相互关联的。
- 关于“云计算”的帖子可能会提到“AWS”,而关于“机器学习”的帖子也可能涉及“编程语言”。
-
搜索。有时,我们不仅仅希望输出一个类别或一个实值。 在信息检索领域,我们希望对一组项目进行排序。
-
推荐系统(recommender system),它的目标是向特定用户进行“个性化”推荐。 关于如何处理审查、激励和反馈循环的许多问题,都是重要的开放性研究问题。
-
序列学习。
以上大多数问题都具有固定大小的输入和产生固定大小的输出。 但是如果输入是连续的,模型可能就需要拥有“记忆”功能。 比如,我们该如何处理视频片段呢? 在这种情况下,每个视频片段可能由不同数量的帧组成。 通过前一帧的图像,我们可能对后一帧中发生的事情更有把握。 语言也是如此,机器翻译的输入和输出都为文字序列。
- 标记和解析。这涉及到用属性注释文本序列。
- 自动语音识别。在语音识别中,输入序列是说话人的录音(如 图1.3.5 所示),输出序列是说话人所说内容的文本记录。 它的挑战在于,与文本相比,音频帧多得多(声音通常以8kHz或16kHz采样)。 也就是说,音频和文本之间没有1:1的对应关系,因为数千个样本可能对应于一个单独的单词。 这也是“序列到序列”的学习问题,其中输出比输入短得多。
- 文本到语音。这与自动语音识别相反。
- 机器翻译。 在语音识别中,输入和输出的出现顺序基本相同。 而在机器翻译中,颠倒输入和输出的顺序非常重要。
打趣一下,“监督学习”模型像一个打工仔,有一份极其专业的工作和一位极其平庸的老板。 老板站在身后,准确地告诉模型在每种情况下应该做什么,直到模型学会从情况到行动的映射。 取悦这位老板很容易,只需尽快识别出模式并模仿他们的行为即可。
1.2 无监督学习
解决的问题:
- 聚类(clustering)问题:没有标签的情况下,我们是否能给数据分类呢?
- 主成分分析(principal component analysis)问题:我们能否找到少量的参数来准确地捕捉数据的线性相关属性?比如,一个球的运动轨迹可以用球的速度、直径和质量来描述。
- 因果关系(causality)和概率图模型(probabilistic graphical models)问题:我们能否描述观察到的许多数据的根本原因?例如,如果我们有关于房价、污染、犯罪、地理位置、教育和工资的人口统计数据,我们能否简单地根据经验数据发现它们之间的关系?
- 生成对抗性网络(generative adversarial networks):为我们提供一种合成数据的方法,甚至像图像和音频这样复杂的非结构化数据。潜在的统计机制是检查真实和虚假数据是否相同的测试,它是无监督学习的另一个重要而令人兴奋的领域。
1.3 强化学习
不管是监督学习还是无监督学习,我们都会预先获取大量数据,然后启动模型,不再与环境交互。 这里所有学习都是在算法与环境断开后进行的,被称为离线学习(offline learning)
这种简单的离线学习有它的魅力。 好的一面是,我们可以孤立地进行模式识别,而不必分心于其他问题。 但缺点是,解决的问题相当有限。 这时我们可能会期望人工智能不仅能够做出预测,而且能够与真实环境互动。 与预测不同,“与真实环境互动”实际上会影响环境。 这里的人工智能是“智能代理”,而不仅是“预测模型”。 因此,我们必须考虑到它的行为可能会影响未来的观察结果。
在强化学习问题中,智能体(agent)在一系列的时间步骤上与环境交互。 在每个特定时间点,智能体从环境接收一些观察(observation),并且必须选择一个动作(action),然后通过某种机制(有时称为执行器)将其传输回环境,最后智能体从环境中获得奖励(reward)。 此后新一轮循环开始,智能体接收后续观察,并选择后续操作,依此类推。 请注意,强化学习的目标是产生一个好的策略(policy)。 强化学习智能体选择的“动作”受策略控制,即一个从环境观察映射到行动的功能。
强化学习框架的通用性十分强大。 例如,我们可以将任何监督学习问题转化为强化学习问题。 假设我们有一个分类问题,可以创建一个强化学习智能体,每个分类对应一个“动作”。 然后,我们可以创建一个环境,该环境给予智能体的奖励。 这个奖励与原始监督学习问题的损失函数是一致的。
当然,强化学习还可以解决许多监督学习无法解决的问题。 例如,在监督学习中,我们总是希望输入与正确的标签相关联。 但在强化学习中,我们并不假设环境告诉智能体每个观测的最优动作。 一般来说,智能体只是得到一些奖励。 此外,环境甚至可能不会告诉是哪些行为导致了奖励。
以强化学习在国际象棋的应用为例。 唯一真正的奖励信号出现在游戏结束时:当智能体获胜时,智能体可以得到奖励1;当智能体失败时,智能体将得到奖励-1。 因此,强化学习者必须处理学分分配(credit assignment)问题:决定哪些行为是值得奖励的,哪些行为是需要惩罚的。 就像一个员工升职一样,这次升职很可能反映了前一年的大量的行动。 要想在未来获得更多的晋升,就需要弄清楚这一过程中哪些行为导致了晋升。
强化学习可能还必须处理部分可观测性问题。 也就是说,当前的观察结果可能无法阐述有关当前状态的所有信息。 比方说,一个清洁机器人发现自己被困在一个许多相同的壁橱的房子里。 推断机器人的精确位置(从而推断其状态),需要在进入壁橱之前考虑它之前的观察结果。
最后,在任何时间点上,强化学习智能体可能知道一个好的策略,但可能有许多更好的策略从未尝试过的。 强化学习智能体必须不断地做出选择:是应该利用当前最好的策略,还是探索新的策略空间(放弃一些短期回报来换取知识)。
一般的强化学习问题是一个非常普遍的问题。 智能体的动作会影响后续的观察,而奖励只与所选的动作相对应。 环境可以是完整观察到的,也可以是部分观察到的,解释所有这些复杂性可能会对研究人员要求太高。 此外,并不是每个实际问题都表现出所有这些复杂性。 因此,学者们研究了一些特殊情况下的强化学习问题。
当环境可被完全观察到时,强化学习问题被称为马尔可夫决策过程(markov decision process)。 当状态不依赖于之前的操作时,我们称该问题为上下文赌博机(contextual bandit problem)。 当没有状态,只有一组最初未知回报的可用动作时,这个问题就是经典的多臂赌博机(multi-armed bandit problem)。
1.4 总结
- 机器学习研究计算机系统如何利用经验(通常是数据)来提高特定任务的性能。它结合了统计学、数据挖掘和优化的思想。通常,它是被用作实现人工智能解决方案的一种手段。
- 表示学习作为机器学习的一类,其研究的重点是如何自动找到合适的数据表示方式。深度学习是通过学习多层次的转换来进行的多层次的表示学习。
- 深度学习不仅取代了传统机器学习的浅层模型,而且取代了劳动密集型的特征工程。
- 最近在深度学习方面取得的许多进展,大都是由廉价传感器和互联网规模应用所产生的大量数据,以及(通过GPU)算力的突破来触发的。
- 整个系统优化是获得高性能的关键环节。有效的深度学习框架的开源使得这一点的设计和实现变得非常容易。
二、背景知识
要学习深度学习,首先需要先掌握一些基本技能。所有机器学习方法都涉及从数据中提取信息。
机器学习通常需要处理大型数据集。我们可以将某些数据集视为一个表,其中表的行对应样本,列对应属性。 线性代数为人们提供了一些用来处理表格数据的方法。我们不会太深究细节,而是将重点放在矩阵运算的基 本原理及其实现上。
深度学习是关于优化的学习。对于一个带有参数的模型,我们想要找到其中能拟合数据的最好模型。在算法的 每个步骤中,决定以何种方式调整参数需要一点微积分知识。本章将简要介绍这些知识。
机器学习还涉及如何做出预测:给定观察到的信息,某些未知属性可能的值是多少?要在不确定的情况下进
行严格的推断,我们需要借用概率语言。
2.1 数据操作
为了能够完成各种数据操作,我们需要某种方法来存储和操作数据。通常,我们需要做两件重要的事:(1) 获取数据;(2)将数据读入计算机后对其进行处理。如果没有某种方法来存储数据,那么获取数据是没有意 义的。
首先,我们介绍n维数组,也称为张量(tensor)。
n维数组,也称为张量(tensor)。
无论使用哪个深度学习框架,张量(tensor)都与Numpy的ndarray
类似。深度学习框架又比Numpy的ndarray
多一些重要功能: 首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算; 其次,张量类支持自动微分。
2.2 数据预处理
为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始,而不是从那些准备好的张量格 式数据开始。在Python中常用的数据分析工具中,我们通常使用pandas软件包。像庞大的Python生态系统中 的许多其他扩展包一样,pandas可以与张量兼容。本节我们将简要介绍使用pandas预处理原始数据,并将原 始数据转换为张量格式的步骤。
2.3 线性代数
在介绍完如何存储和操作数据后,接下来将简要地回顾一下部分基本线性代数内容。这些内容有助于读者了解和实现本书中介绍的大多数模型。本节将介绍线性代数中的基本数学对象、算术和运算,并用数学符号和相应的代码实现来表示它们。
2.4 微积分
在深度学习中,我们“训练”模型,不断更新它们,使它们在看到越来越多的数据时变得越来越好。通常情 况下,变得更好意味着最小化一个损失函数(loss function),即一个衡量“模型有多糟糕”这个问题的分数。 最终,我们真正关心的是生成一个模型,它能够在从未⻅过的数据上表现良好。但“训练”模型只能将模型 与我们实际能看到的数据相拟合。因此,我们可以将拟合模型的任务分解为两个关键问题:
• 优化(optimization):用模型拟合观测数据的过程;
• 泛化(generalization):数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集 本身的模型。
2.5 自动微分
正如 2.4节中所说,求导是几乎所有深度学习优化算法的关键步骤。虽然求导的计算很简单,只需要一些基 本的微积分。但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错)。
深度学习框架通过自动计算导数,即自动微分(automatic differentiation)来加快求导。实际中,根据设计 好的模型,系统会构建一个计算图(computational graph),来跟踪计算是哪些数据通过哪些操作组合起来 产生输出。自动微分使系统能够随后反向传播梯度。这里,反向传播(backpropagate)意味着跟踪整个计算 图,填充关于每个参数的偏导数。
2.6 概率
简单地说,机器学习就是做出预测。
三、线性神经网络
在介绍深度神经网络之前,我们需要了解神经网络训练的基础知识。本章我们将介绍神经网络的整个训练过 程,包括:定义简单的神经网络架构、数据处理、指定损失函数和如何训练模型。为了更容易学习,我们将从经 典算法————线性神经网络开始,介绍神经网络的基础知识。经典统计学习技术中的线性回归和softmax回 归可以视为线性神经网络,这些知识将为本书其他部分中更复杂的技术奠定基础。
3.1 线性回归
回归(regression)是能为一个或多个自变量与因变量之间关系建模的一类方法。在自然科学和社会科学领域,回归经常用来表示输入和输出之间的关系。
在机器学习领域中的大多数任务通常都与预测(prediction)有关。当我们想预测一个数值时,就会涉及到 回归问题。常⻅的例子包括:预测价格(房屋、股票等)、预测住院时间(针对住院病人等)、预测需求(零 售销量等)。
但不是所有的预测都是回归问题。在后面的章节中,我们将介绍分类问题。分类问题的目标是预测数据属于一组类别中的哪一个。
3.2 线性回归的从零开始实现
在了解线性回归的关键思想之后,我们可以开始通过代码来动手实现线性回归了。在这一节中,我们将从零
开始实现整个方法,包括数据流水线、模型、损失函数和小批量随机梯度下降优化器。虽然现代的深度学习
框架几乎可以自动化地进行所有这些工作,但从零开始实现可以确保我们真正知道自己在做什么。同时,了
解更细致的工作原理将方便我们自定义模型、自定义层或自定义损失函数。在这一节中,我们将只使用张量
和自动求导。在之后的章节中,我们会充分利用深度学习框架的优势,介绍更简洁的实现方式。
3.3 线性回归的简洁实现
在过去的几年里,出于对深度学习强烈的兴趣,许多公司、学者和业余爱好者开发了各种成熟的开源框架。 这些框架可以自动化基于梯度的学习算法中重复性的工作。在 3.2节中,我们只运用了:(1)通过张量来进行 数据存储和线性代数;(2)通过自动微分来计算梯度。实际上,由于数据迭代器、损失函数、优化器和神经 网络层很常用,现代深度学习库也为我们实现了这些组件
3.4 softmax回归
通常,机器学习实践者用分类这个词来描述两个有微妙差别的问题:1. 我们只对样本的“硬性”类别感兴趣, 即属于哪个类别;2. 我们希望得到“软性”类别,即得到属于每个类别的概率。这两者的界限往往很模糊。 其中的一个原因是:即使我们只关心硬类别,我们仍然使用软类别的模型。
3.5 图像分类数据集
MNIST数据集 (LeCun et al., 1998) 是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。我们 将使用类似但更复杂的Fashion-MNIST数据集 (Xiao et al., 2017)。
- Fashion-MNIST是一个服装分类数据集,由10个类别的图像组成。我们将在后续章节中使用此数据集来评估各种分类算法。
- 我们将高度h像素,宽度w像素图像的形状记为h×w或(h,w)。
- 数据迭代器是获得更高性能的关键组件。依靠实现良好的数据迭代器,利用高性能计算来避免减慢训练过程。
3.6 softmax回归的从零开始实现
就像我们从零开始实现线性回归一样,我们认为softmax回归也是重要的基础,因此应该知道实现softmax回归的细节。本节我们将使用刚刚在 3.5节中引入的Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。
3.7 softmax回归的简洁实现
在 3.3节中,我们发现通过深度学习框架的高级API能够使实现 线性回归变得更加容易。同样,通过深度学习框架的高级API也能更方便地实现softmax回归模型。本节如在
3.6节中一样,继续使用Fashion-MNIST数据集,并保持批量大小为256。
四、多层感知机
在本章中,我们将第一次介绍真正的深度网络。最简单的深度网络称为多层感知机。多层感知机由多层神经 元组成,每一层与它的上一层相连,从中接收输入;同时每一层也与它的下一层相连,影响当前层的神经元。 当我们训练容量较大的模型时,我们面临着过拟合的⻛险。因此,本章将从基本的概念介绍开始讲起,包括 过拟合、欠拟合和模型选择。为了解决这些问题,本章将介绍权重衰减和暂退法等正则化技术。我们还将讨 论数值稳定性和参数初始化相关的问题,这些问题是成功训练深度网络的关键。在本章的最后,我们将把所 介绍的内容应用到一个真实的案例:房价预测。关于模型计算性能、可伸缩性和效率相关的问题,我们将放 在后面的章节中讨论。
4.1 多层感知机
在 3节中,我们介绍了softmax回归(3.4节),然后我们从零开始实现了softmax回归(3.6节),接着使用高 级API实现了算法(3.7节),并训练分类器从低分辨率图像中识别10类服装。在这个过程中,我们学习了如何 处理数据,如何将输出转换为有效的概率分布,并应用适当的损失函数,根据模型参数最小化损失。我们已 经在简单的线性模型背景下掌握了这些知识,现在我们可以开始对深度神经网络的探索,这也是本书主要涉 及的一类模型。
4.2 多层感知机的从零开始实现
我们已经在 4.1节中描述了多层感知机(MLP),现在让我们尝试自己实现一个多层感知机。为了与之 前softmax回归(3.6节 )获得的结果进行比较,我们将继续使用Fashion-MNIST图像分类数据集(3.5节)。
4.3 多层感知机的简洁实现
本节将介绍通过高级API更简洁地实现多层感知机。
4.4 模型选择、欠拟合和过拟合
作为机器学习科学家,我们的目标是发现模式(pattern)。但是,我们如何才能确定模型是真正发现了一种 泛化的模式,而不是简单地记住了数据呢? 如何发现可以泛化的模式是机器学习的根本问题。
将模型在训练数据上拟合的比在潜在分布中更接近的现象称为过拟合(overfitting),用于对抗过拟合的技术 称为正则化(regularization)。在前面的章节中,有些读者可能在用Fashion-MNIST数据集做实验时已经观 察到了这种过拟合现象。在实验中调整模型架构或超参数时会发现:如果有足够多的神经元、层数和训练迭 代周期,模型最终可以在训练集上达到完美的精度,此时测试集的准确性却下降了。
4.5 权重衰减
前一节我们描述了过拟合的问题,本节我们将介绍一些正则化模型的技术。我们总是可以通过去收集更多的训练数据来缓解过拟合。但这可能成本很高,耗时颇多,或者完全超出我们的控制,因而在短期内不可能做到。假设我们已经拥有尽可能多的高质量数据,我们便可以将重点放在正则化技术上。
4.6 暂退法(Dropout)
在 4.5节 中,我们介绍了通过惩罚权重的L2范数来正则化统计模型的经典方法。在概率⻆度看,我们可以通 过以下论证来证明这一技术的合理性:我们已经假设了一个先验,即权重的值取自均值为0的高斯分布。更直 观的是,我们希望模型深度挖掘特征,即将其权重分散到许多特征中,而不是过于依赖少数潜在的虚假关联。
4.7 前向传播、反向传播和计算图
我们已经学习了如何用小批量随机梯度下降训练模型。然而当实现该算法时,我们只考虑了通过前向传播 (forward propagation)所涉及的计算。在计算梯度时,我们只调用了深度学习框架提供的反向传播函数,而不知其所以然。
梯度的自动计算(自动微分)大大简化了深度学习算法的实现。在自动微分之前,即使是对复杂模型的微小 调整也需要手工重新计算复杂的导数,学术论文也不得不分配大量⻚面来推导更新规则。本节将通过一些基 本的数学和计算图,深入探讨反向传播的细节。首先,我们将重点放在带权重衰减(L2 正则化)的单隐藏层 多层感知机上。
4.8 数值稳定性和模型初始化
到目前为止,我们实现的每个模型都是根据某个预先指定的分布来初始化模型的参数。有人会认为初始化方案是理所当然的,忽略了如何做出这些选择的细节。甚至有人可能会觉得,初始化方案的选择并不是特别重 要。相反,初始化方案的选择在神经网络学习中起着举足轻重的作用,它对保持数值稳定性至关重要。此外, 这些初始化方案的选择可以与非线性激活函数的选择有趣的结合在一起。我们选择哪个函数以及如何初始化 参数可以决定优化算法收敛的速度有多快。糟糕选择可能会导致我们在训练时遇到梯度爆炸或梯度消失。本 节将更详细地探讨这些主题,并讨论一些有用的启发式方法。这些启发式方法在整个深度学习生涯中都很有用。
4.9 环境和分布偏移
前面我们学习了许多机器学习的实际应用,将模型拟合各种数据集。然而,我们从来没有想过数据最初从哪里来?以及我们计划最终如何处理模型的输出?通常情况下,开发人员会拥有一些数据且急于开发模型,而不关注这些基本问题。
许多失败的机器学习部署(即实际应用)都可以追究到这种方式。有时,根据测试集的精度衡量,模型表现 得非常出色。但是当数据分布突然改变时,模型在部署中会出现灾难性的失败。更隐蔽的是,有时模型的部 署本身就是扰乱数据分布的催化剂。举一个有点荒谬却可能真实存在的例子。假设我们训练了一个贷款申请 人违约⻛险模型,用来预测谁将偿还贷款或违约。这个模型发现申请人的鞋子与违约⻛险相关(穿牛津鞋申 请人会偿还,穿运动鞋申请人会违约)。此后,这个模型可能倾向于向所有穿着牛津鞋的申请人发放贷款,并 拒绝所有穿着运动鞋的申请人。
这种情况可能会带来灾难性的后果。首先,一旦模型开始根据鞋类做出决定,顾客就会理解并改变他们的行 为。不久,所有的申请者都会穿牛津鞋,而信用度却没有相应的提高。总而言之,机器学习的许多应用中都存在类似的问题:通过将基于模型的决策引入环境,我们可能会破坏模型。
虽然我们不可能在一节中讨论全部的问题,但我们希望揭示一些常⻅的问题,并激发批判性思考,以便及早 发现这些情况,减轻灾难性的损害。有些解决方案很简单(要求“正确”的数据),有些在技术上很困难(实 施强化学习系统),还有一些解决方案要求我们完全跳出统计预测,解决一些棘手的、与算法伦理应用有关的 哲学问题。
4.10 实战Kaggle比赛:预测房价
之前几节我们学习了一些训练深度网络的基本工具和网络正则化的技术(如权重衰减、暂退法等)。本节我 们将通过Kaggle比赛,将所学知识付诸实践。Kaggle的房价预测比赛是一个很好的起点。此数据集由Bart de Cock于2011年收集 (De Cock, 2011),涵盖了2006-2010年期间亚利桑那州埃姆斯市的房价。这个数据集是相 当通用的,不会需要使用复杂模型架构。它比哈里森和鲁宾菲尔德的波士顿房价71 数据集要大得多,也有更 多的特征。
本节我们将详细介绍数据预处理、模型设计和超参数选择。通过亲身实践,你将获得一手经验,这些经验将有益数据科学家的职业成⻓。
五、深度学习计算
除了庞大的数据集和强大的硬件,优秀的软件工具在深度学习的快速发展中发挥了不可或缺的作用。从2007年 发布的开创性的Theano库开始,灵活的开源工具使研究人员能够快速开发模型原型,避免了我们使用标准组 件时的重复工作,同时仍然保持了我们进行底层修改的能力。随着时间的推移,深度学习库已经演变成提供 越来越粗糙的抽象。就像半导体设计师从指定晶体管到逻辑电路再到编写代码一样,神经网络研究人员已经 从考虑单个人工神经元的行为转变为从层的⻆度构思网络,通常在设计架构时考虑的是更粗糙的块(block)。
之前我们已经介绍了一些基本的机器学习概念,并慢慢介绍了功能⻬全的深度学习模型。在上一章中,我们 从零开始实现了多层感知机的每个组件,然后展示了如何利用高级API轻松地实现相同的模型。为了易于学 习,我们调用了深度学习库,但是跳过了它们工作的细节。在本章中,我们将深入探索深度学习计算的关键 组件,即模型构建、参数访问与初始化、设计自定义层和块、将模型读写到磁盘,以及利用GPU实现显著的 加速。这些知识将使读者从深度学习“基础用戶”变为“高级用戶”。虽然本章不介绍任何新的模型或数据 集,但后面的高级模型章节在很大程度上依赖于本章的知识。
之前首次介绍神经网络时,我们关注的是具有单一输出的线性模型。在这里,整个模型只有一个输出。注意, 单个神经网络(1)接受一些输入;(2)生成相应的标量输出;(3)具有一组相关 参数(parameters),更新 这些参数可以优化某目标函数。
然后,当考虑具有多个输出的网络时,我们利用矢量化算法来描述整层神经元。像单个神经元一样,层(1) 接受一组输入,(2)生成相应的输出,(3)由一组可调整参数描述。当我们使用softmax回归时,一个单层本 身就是模型。然而,即使我们随后引入了多层感知机,我们仍然可以认为该模型保留了上面所说的基本架构。
对于多层感知机而言,整个模型及其组成层都是这种架构。整个模型接受原始输入(特征),生成输出(预测),并包含一些参数(所有组成层的参数集合)。同样,每个单独的层接收输入(由前一层提供),生成输出 (到下一层的输入),并且具有一组可调参数,这些参数根据从下一层反向传播的信号进行更新。
事实证明,研究讨论“比单个层大”但“比整个模型小”的组件更有价值。例如,在计算机视觉中广泛流行 的ResNet-152架构就有数百层,这些层是由层组(groups of layers)的重复模式组成。这个ResNet架构赢得 了2015年ImageNet和COCO计算机视觉比赛的识别和检测任务 (He et al., 2016)。目前ResNet架构仍然是许多 视觉任务的首选架构。在其他的领域,如自然语言处理和语音,层组以各种重复模式排列的类似架构现在也 是普遍存在。
为了实现这些复杂的网络,我们引入了神经网络块的概念。块(block)可以描述单个层、由多个层组成的组 件或整个模型本身。使用块进行抽象的一个好处是可以将一些块组合成更大的组件,这一过程通常是递归的, 如 图5.1.1所示。通过定义代码来按需生成任意复杂度的块,我们可以通过简洁的代码实现复杂的神经网络。
在选择了架构并设置了超参数后,我们就进入了训练阶段。此时,我们的目标是找到使损失函数最小化的模型参数值。经过训练后,我们将需要使用这些参数来做出未来的预测。此外,有时我们希望提取参数,以便在其他环境中复用它们,将模型保存下来,以便它可以在其他软件中执行,或者为了获得科学的理解而进行检查。
之前的介绍中,我们只依靠深度学习框架来完成训练的工作,而忽略了操作参数的具体细节。本节,我们将介绍以下内容:
• 访问参数,用于调试、诊断和可视化;
• 参数初始化;
• 在不同模型组件间共享参数。
到目前为止,我们忽略了建立网络时需要做的以下这些事情:
• 我们定义了网络架构,但没有指定输入维度。
• 我们添加层时没有指定前一层的输出维度。
• 我们在初始化参数时,甚至没有足够的信息来确定模型应该包含多少参数。
有些读者可能会对我们的代码能运行感到惊讶。毕竟,深度学习框架无法判断网络的输入维度是什么。这里 的诀窍是框架的延后初始化(defers initialization),即直到数据第一次通过模型传递时,框架才会动态地推 断出每个层的大小。
在以后,当使用卷积神经网络时,由于输入维度(即图像的分辨率)将影响每个后续层的维数,有了该技术将更加方便。现在我们在编写代码时无须知道维度是什么就可以设置参数,这种能力可以大大简化定义和修改模型的任务。
深度学习成功背后的一个因素是神经网络的灵活性:我们可以用创造性的方式组合不同的层,从而设计出适 用于各种任务的架构。例如,研究人员发明了专⻔用于处理图像、文本、序列数据和执行动态规划的层。有时 我们会遇到或要自己发明一个现在在深度学习框架中还不存在的层。在这些情况下,必须构建自定义层。本 节将展示如何构建自定义层。
到目前为止,我们讨论了如何处理数据,以及如何构建、训练和测试深度学习模型。然而,有时我们希望保 存训练的模型,以备将来在各种环境中使用(比如在部署中进行预测)。此外,当运行一个耗时较⻓的训练过 程时,最佳的做法是定期保存中间结果,以确保在服务器电源被不小心断掉时,我们不会损失几天的计算结 果。因此,现在是时候学习如何加载和存储权重向量和整个模型了。
在 1.5节中,我们回顾了过去20年计算能力的快速增⻓。简而言之,自2000年以来,GPU性能每十年增⻓1000倍。 本节,我们将讨论如何利用这种计算性能进行研究。首先是如何使用单个GPU,然后是如何使用多个GPU和多个服务器(具有多个GPU)。
六、卷积神经网络
在前面的章节中,我们遇到过图像数据。这种数据的每个样本都由一个二维像素网格组成,每个像素可能是一个或者多个数值,取决于是黑白还是彩色图像。到目前为止,我们处理这类结构丰富的数据的方式还不够有效。我们仅仅通过将图像数据展平成一维向量而忽略了每个图像的空间结构信息,再将数据送入一个全连接的多层感知机中。因为这些网络特征元素的顺序是不变的,因此最优的结果是利用先验知识,即利用相近像素之间的相互关联性,从图像数据中学习得到有效的模型。
本章介绍的卷积神经网络(convolutional neural network,CNN)是一类强大的、为处理图像数据而设计的 神经网络。基于卷积神经网络架构的模型在计算机视觉领域中已经占主导地位,当今几乎所有的图像识别、 目标检测或语义分割相关的学术竞赛和商业应用都以这种方法为基础。
现代卷积神经网络的设计得益于生物学、群论和一系列的补充实验。卷积神经网络需要的参数少于全连接架 构的网络,而且卷积也很容易用GPU并行计算。因此卷积神经网络除了能够高效地采样从而获得精确的模型, 还能够高效地计算。久而久之,从业人员越来越多地使用卷积神经网络。即使在通常使用循环神经网络的一 维序列结构任务上(例如音频、文本和时间序列分析),卷积神经网络也越来越受欢迎。通过对卷积神经网络 一些巧妙的调整,也使它们在图结构数据和推荐系统中发挥作用。
在本章的开始,我们将介绍构成所有卷积网络主干的基本元素。这包括卷积层本身、填充(padding)和步幅 (stride)的基本细节、用于在相邻区域汇聚信息的汇聚层(pooling)、在每一层中多通道(channel)的使用, 以及有关现代卷积网络架构的仔细讨论。在本章的最后,我们将介绍一个完整的、可运行的LeNet模型:这是 第一个成功应用的卷积神经网络,比现代深度学习兴起时间还要早。在下一章中,我们将深入研究一些流行的、相对较新的卷积神经网络架构的完整实现,这些网络架构涵盖了现代从业者通常使用的大多数经典技术。
我们之前讨论的多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。此时,多层感知机可能是最好的选择,然而对于高维感知数据,这种缺少结构的网络可能会变得不实用。
例如,在之前猫狗分类的例子中:假设我们有一个足够充分的照片数据集,数据集中是拥有标注的照片,每 张照片具有百万级像素,这意味着网络的每次输入都有一百万个维度。即使将隐藏层维度降低到1000,这个 全连接层也将有106 × 103 = 109个参数。想要训练这个模型将不可实现,因为需要有大量的GPU、分布式优 化训练的经验和超乎常人的耐心。
有些读者可能会反对这个观点,认为要求百万像素的分辨率可能不是必要的。然而,即使分辨率减小为十 万像素,使用1000个隐藏单元的隐藏层也可能不足以学习到良好的图像特征,在真实的系统中我们仍然需要 数十亿个参数。此外,拟合如此多的参数还需要收集大量的数据。然而,如今人类和机器都能很好地区分猫 和狗:这是因为图像中本就拥有丰富的结构,而这些结构可以被人类和机器学习模型使用。卷积神经网络(convolutional neural networks,CNN)是机器学习利用自然图像中一些已知结构的创造性方法。
上节我们解析了卷积层的原理,现在我们看看它的实际应用。由于卷积神经网络的设计是用于探索图像数据, 本节我们将以图像为例。
还有什么因素会影响输出的大小呢?本节我们将介绍填充(padding)和步幅(stride)。假设以下情景:有 时,在应用了连续的卷积之后,我们最终得到的输出远小于输入大小。这是由于卷积核的宽度和高度通常大 于1所导致的。比如,一个240 × 240像素的图像,经过10层5 × 5的卷积后,将减少到200 × 200像素。如此一 来,原始图像的边界丢失了许多有用信息。而填充是解决此问题最有效的方法;有时,我们可能希望大幅降 低图像的宽度和高度。例如,如果我们发现原始的输入分辨率十分冗余。步幅则可以在这类情况下提供帮助。
虽然我们在 6.1.4节中描述了构成每个图像的多个通道和多层卷积层。例如彩色图像具有标准的RGB通道来 代表红、绿和蓝。但是到目前为止,我们仅展示了单个输入和单个输出通道的简化例子。这使得我们可以将 输入、卷积核和输出看作二维张量。
当我们添加通道时,我们的输入和隐藏的表示都变成了三维张量。例如,每个RGB输入图像具有3 × h × w的 形状。我们将这个大小为3的轴称为通道(channel)维度。本节将更深入地研究具有多输入和多输出通道的 卷积核。
通常当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中
层叠的上升,每个神经元对其敏感的感受野(输入)就越大。
而我们的机器学习任务通常会跟全局图像的问题有关(例如,“图像是否包含一只猫呢?”),所以我们最后一 层的神经元应该对整个输入的全局敏感。通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表 示的目标,同时将卷积图层的所有优势保留在中间层。
此外,当检测较底层的特征时(例如 6.2节中所讨论的边缘),我们通常希望这些特征保持某种程度上的平 移不变性。例如,如果我们拍摄黑白之间轮廓清晰的图像X,并将整个图像向右移动一个像素,即Z[i, j] = X[i, j + 1],则新图像Z的输出可能大不相同。而在现实中,随着拍摄⻆度的移动,任何物体几乎不可能发 生在同一像素上。即使用三脚架拍摄一个静止的物体,由于快⻔的移动而引起的相机振动,可能会使所有物 体左右移动一个像素(除了高端相机配备了特殊功能来解决这个问题)。
本节将介绍汇聚(pooling)层,它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示 的敏感性。
通过之前几节,我们学习了构建一个完整卷积神经网络的所需组件。回想一下,之前我们将softmax回归模型 (3.6节)和多层感知机模型(4.2节)应用于Fashion-MNIST数据集中的服装图片。为了能够应用softmax回归 和多层感知机,我们首先将每个大小为28 × 28的图像展平为一个784维的固定⻓度的一维向量,然后用全连 接层对其进行处理。而现在,我们已经掌握了卷积层的处理方法,我们可以在图像中保留空间结构。同时,用 卷积层代替全连接层的另一个好处是:模型更简洁、所需的参数更少。
本节将介绍LeNet,它是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关 注。这个模型是由AT&T⻉尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像 (LeCun et al., 1998)中的手写数字。当时,Yann LeCun发表了第一篇通过反向传播成功训练卷积神经网络的 研究,这项工作代表了十多年来神经网络研究开发的成果。
当时,LeNet取得了与支持向量机(support vector machines)性能相媲美的成果,成为监督学习的主流方 法。LeNet被广泛用于自动取款机(ATM)机中,帮助识别处理支票的数字。时至今日,一些自动取款机仍 在运行Yann LeCun和他的同事Leon Bottou在上世纪90年代写的代码呢!
七、现代卷积神经网络
上一章我们介绍了卷积神经网络的基本原理,本章将介绍现代的卷积神经网络架构,许多现代卷积神经网 络的研究都是建立在这一章的基础上的。在本章中的每一个模型都曾一度占据主导地位,其中许多模型都 是ImageNet竞赛的优胜者。ImageNet竞赛自2010年以来,一直是计算机视觉中监督学习进展的指向标。
这些模型包括:
- AlexNet。它是第一个在大规模视觉竞赛中击败传统计算机视觉模型的大型神经网络;
- 使用重复块的网络(VGG)。它利用许多重复的神经网络块;
- 网络中的网络(NiN)。它重复使用由卷积层和1×1卷积层(用来代替全连接层)来构建深层网络;
- 含并行连结的网络(GoogLeNet)。它使用并行连结的网络,通过不同窗口大小的卷积层和最大汇聚层 来并行抽取信息;
- 残差网络(ResNet)。它通过残差块构建跨层的数据通道,是计算机视觉中最流行的体系架构;
- 稠密连接网络(DenseNet)。它的计算成本很高,但给我们带来了更好的效果。
虽然深度神经网络的概念非常简单——将神经网络堆叠在一起。但由于不同的网络架构和超参数选择,这些 神经网络的性能会发生很大变化。本章介绍的神经网络是将人类直觉和相关数学⻅解结合后,经过大量研究 试错后的结晶。我们会按时间顺序介绍这些模型,在追寻历史的脉络的同时,帮助培养对该领域发展的直觉。 这将有助于研究开发自己的架构。例如,本章介绍的批量规范化(batch normalization)和残差网络(ResNet) 为设计和训练深度神经网络提供了重要思想指导。
在LeNet提出后,卷积神经网络在计算机视觉和机器学习领域中很有名气。但卷积神经网络并没有主导这些 领域。这是因为虽然LeNet在小数据集上取得了很好的效果,但是在更大、更真实的数据集上训练卷积神经 网络的性能和可行性还有待研究。事实上,在上世纪90年代初到2012年之间的大部分时间里,神经网络往往 被其他机器学习方法超越,如支持向量机(support vector machines)。
在计算机视觉中,直接将神经网络与其他机器学习方法进行比较也许不公平。这是因为,卷积神经网络的输 入是由原始像素值或是经过简单预处理(例如居中、缩放)的像素值组成的。但在使用传统机器学习方法时, 从业者永远不会将原始像素作为输入。在传统机器学习方法中,计算机视觉流水线是由经过人的手工精心设 计的特征流水线组成的。对于这些传统方法,大部分的进展都来自于对特征有了更聪明的想法,并且学习到 的算法往往归于事后的解释。
虽然上世纪90年代就有了一些神经网络加速卡,但仅靠它们还不足以开发出有大量参数的深层多通道多层卷 积神经网络。此外,当时的数据集仍然相对较小。除了这些障碍,训练神经网络的一些关键技巧仍然缺失,包 括启发式参数初始化、随机梯度下降的变体、非挤压激活函数和有效的正则化技术。
因此,与训练端到端(从像素到分类结果)系统不同,经典机器学习的流水线看起来更像下面这样:
-
获取一个有趣的数据集。在早期,收集这些数据集需要昂贵的传感器(在当时最先进的图像也就100万 像素)。
-
根据光学、几何学、其他知识以及偶然的发现,手工对特征数据集进行预处理。
-
通过标准的特征提取算法,如SIFT(尺度不变特征变换)(Lowe,2004)和SURF(加速鲁棒特征)(Bayet
al., 2006)或其他手动调整的流水线来输入数据。
-
将提取的特征送入最喜欢的分类器中(例如线性模型或其它核方法),以训练分类器。
当人们和机器学习研究人员交谈时,会发现机器学习研究人员相信机器学习既重要又美丽:优雅的理论去证 明各种模型的性质。机器学习是一个正在蓬勃发展、严谨且非常有用的领域。然而,当人们和计算机视觉研 究人员交谈,会听到一个完全不同的故事。计算机视觉研究人员会告诉一个诡异事实————推动领域进步的 是数据特征,而不是学习算法。计算机视觉研究人员相信,从对最终模型精度的影响来说,更大或更干净的 数据集、或是稍微改进的特征提取,比任何学习算法带来的进步要大得多。
虽然AlexNet证明深层神经网络卓有成效,但它没有提供一个通用的模板来指导后续的研究人员设计新的网络。在下面的几个章节中,我们将介绍一些常用于设计深层神经网络的启发式概念。
与芯片设计中工程师从放置晶体管到逻辑元件再到逻辑块的过程类似,神经网络架构的设计也逐渐变得更加
抽象。研究人员开始从单个神经元的⻆度思考问题,发展到整个层,现在又转向块,重复层的模式。
使用块的想法首先出现在牛津大学的视觉几何组(visual geometry group)91的VGG网络中。通过使用循环 和子程序,可以很容易地在任何现代深度学习框架的代码中实现这些重复的架构。
LeNet、AlexNet和VGG都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征;然后 通过全连接层对特征的表征进行处理。AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 或者,可以想象在这个过程的早期使用全连接层。然而,如果使用了全连接层,可能会完全放弃表征的空间结 构。网络中的网络(NiN)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机 (Lin et al., 2013)
在2014年的ImageNet图像识别挑战赛中,一个名叫GoogLeNet (Szegedy et al., 2015)的网络架构大放异彩。 GoogLeNet吸收了NiN中串联网络的思想,并在此基础上做了改进。这篇论文的一个重点是解决了什么样 大小的卷积核最合适的问题。毕竟,以前流行的网络使用小到1 × 1,大到11 × 11的卷积核。本文的一个观点 是,有时使用不同大小的卷积核组合是有利的。本节将介绍一个稍微简化的GoogLeNet版本:我们省略了一 些为稳定训练而添加的特殊特性,现在有了更好的训练方法,这些特性不是必要的。
训练深层神经网络是十分困难的,特别是在较短的时间内使他们收敛更加棘手。本节将介绍批量规范化(batch normalization)(Ioffe and Szegedy, 2015),这是一种流行且有效的技术,可持续加速深层网络的收敛速度。 再结合在 7.6节中将介绍的残差块,批量规范化使得研究人员能够训练100层以上的网络。
随着我们设计越来越深的网络,深刻理解“新添加的层如何提升神经网络的性能”变得至关重要。更重要的是设计网络的能力,在这种网络中,添加层会使网络更具表现力,为了取得质的突破,我们需要一些数学基础知识。
ResNet极大地改变了如何参数化深层网络中函数的观点。稠密连接网络(DenseNet)(Huang et al., 2017)在某种程度上是ResNet的逻辑扩展。让我们先从数学上了解一下。
八、循环神经网络
到目前为止,我们遇到过两种类型的数据:表格数据和图像数据。 对于图像数据,我们设计了专门的卷积神经网络架构来为这类特殊的数据结构建模。 换句话说,如果我们拥有一张图像,我们需要有效地利用其像素位置, 假若我们对图像中的像素位置进行重排,就会对图像中内容的推断造成极大的困难。
最重要的是,到目前为止我们默认数据都来自于某种分布, 并且所有样本都是独立同分布的 (independently and identically distributed,i.i.d.)。 然而,大多数的数据并非如此。 例如,文章中的单词是按顺序写的,如果顺序被随机地重排,就很难理解文章原始的意思。 同样,视频中的图像帧、对话中的音频信号以及网站上的浏览行为都是有顺序的。 因此,针对此类数据而设计特定模型,可能效果会更好。
另一个问题来自这样一个事实: 我们不仅仅可以接收一个序列作为输入,而是还可能期望继续猜测这个序列的后续。 例如,一个任务可以是继续预测(2, 4, 6, 8, 10, \ldots)。 这在时间序列分析中是相当常见的,可以用来预测股市的波动、 患者的体温曲线或者赛车所需的加速度。 同理,我们需要能够处理这些数据的特定模型。
简言之,如果说卷积神经网络可以有效地处理空间信息, 那么本章的循环神经网络(recurrent neural network,RNN)则可以更好地处理序列信息。 循环神经网络通过引入状态变量存储过去的信息和当前的输入,从而可以确定当前的输出。
许多使用循环网络的例子都是基于文本数据的,因此我们将在本章中重点介绍语言模型。 在对序列数据进行更详细的回顾之后,我们将介绍文本预处理的实用技术。 然后,我们将讨论语言模型的基本概念,并将此讨论作为循环神经网络设计的灵感。 最后,我们描述了循环神经网络的梯度计算方法,以探讨训练此类网络时可能遇到的问题。
想象一下有人正在看网飞(Netflix,一个国外的视频网站)上的电影。 一名忠实的用户会对每一部电影都给出评价, 毕竟一部好电影需要更多的支持和认可。 然而事实证明,事情并不那么简单。 随着时间的推移,人们对电影的看法会发生很大的变化。 事实上,心理学家甚至对这些现象起了名字:
- 锚定(anchoring)效应:基于其他人的意见做出评价。 例如,奥斯卡颁奖后,受到关注的电影的评分会上升,尽管它还是原来那部电影。 这种影响将持续几个月,直到人们忘记了这部电影曾经获得的奖项。 结果表明( (Wu et al., 2017)),这种效应会使评分提高半个百分点以上。
- 享乐适应(hedonic adaption):人们迅速接受并且适应一种更好或者更坏的情况 作为新的常态。 例如,在看了很多好电影之后,人们会强烈期望下部电影会更好。 因此,在许多精彩的电影被看过之后,即使是一部普通的也可能被认为是糟糕的。
- 季节性(seasonality):少有观众喜欢在八月看圣诞老人的电影。
- 有时,电影会由于导演或演员在制作中的不当行为变得不受欢迎。
- 有些电影因为其极度糟糕只能成为小众电影。Plan9from Outer Space和Troll2就因为这个原因而臭名昭著的。
简而言之,电影评分决不是固定不变的。 因此,使用时间动力学可以得到更准确的电影推荐 (Koren, 2009)。 当然,序列数据不仅仅是关于电影评分的。 下面给出了更多的场景。
- 在使用程序时,许多用户都有很强的特定习惯。 例如,在学生放学后社交媒体应用更受欢迎。在市场开放时股市交易软件更常用。
- 预测明天的股价要比过去的股价更困难,尽管两者都只是估计一个数字。 毕竟,先见之明比事后诸葛亮难得多。 在统计学中,前者(对超出已知观测范围进行预测)称为外推法(extrapolation), 而后者(在现有观测值之间进行估计)称为内插法(interpolation)。
- 在本质上,音乐、语音、文本和视频都是连续的。 如果它们的序列被我们重排,那么就会失去原有的意义。 比如,一个文本标题“狗咬人”远没有“人咬狗”那么令人惊讶,尽管组成两句话的字完全相同。
- 地震具有很强的相关性,即大地震发生后,很可能会有几次小余震, 这些余震的强度比非大地震后的余震要大得多。 事实上,地震是时空相关的,即余震通常发生在很短的时间跨度和很近的距离内。
- 人类之间的互动也是连续的,这可以从微博上的争吵和辩论中看出。
总结:
- 内插法(在现有观测值之间进行估计)和外推法(对超出已知观测范围进行预测)在实践的难度上差别很大。因此,对于所拥有的序列数据,在训练时始终要尊重其时间顺序,即最好不要基于未来的数据进行训练。
- 序列模型的估计需要专门的统计工具,两种较流行的选择是自回归模型和隐变量自回归模型。
- 对于时间是向前推进的因果模型,正向估计通常比反向估计更容易。
- 对于直到时间步t的观测序列,其在时间步t+k的预测输出是“k步预测”。随着我们对预测时间k值的增加,会造成误差的快速累积和预测质量的极速下降。
对于序列数据处理问题,我们在 8.1节中 评估了所需的统计工具和预测时面临的挑战。 这样的数据存在许多种形式,文本是最常见例子之一。 例如,一篇文章可以被简单地看作一串单词序列,甚至是一串字符序列。 本节中,我们将解析文本的常见预处理步骤。 这些步骤通常包括:
- 将文本作为字符串加载到内存中。
- 将字符串拆分为词元(如单词和字符)。
- 建立一个词表,将拆分的词元映射到数字索引。
- 将文本转换为数字索引序列,方便模型操作。
- 文本是序列数据的一种最常见的形式之一。
- 为了对文本进行预处理,我们通常将文本拆分为词元,构建词表将词元字符串映射为数字索引,并将文本数据转换为词元索引以供模型操作。
尽管如此,语言模型依然是非常有用的。 例如,短语“to recognize speech”和“to wreck a nice beach”读音上听起来非常相似。 这种相似性会导致语音识别中的歧义,但是这很容易通过语言模型来解决, 因为第二句的语义很奇怪。 同样,在文档摘要生成算法中, “狗咬人”比“人咬狗”出现的频率要高得多, 或者“我想吃奶奶”是一个相当匪夷所思的语句, 而“我想吃,奶奶”则要正常得多。
- 语言模型是自然语言处理的关键。
- n元语法通过截断相关性,为处理长序列提供了一种实用的模型。
- 长序列存在一个问题:它们很少出现或者从不出现。
- 齐普夫定律支配着单词的分布,这个分布不仅适用于一元语法,还适用于其他n元语法。
- 通过拉普拉斯平滑法可以有效地处理结构丰富而频率不足的低频词词组。
- 读取长序列的主要方式是随机采样和顺序分区。在迭代过程中,后者可以保证来自两个相邻的小批量中的子序列在原始序列上也是相邻的。
回想一下,我们在 4节中讨论过的具有隐藏单元的隐藏层。值得注意的是,隐藏层和隐状态指的是两个截然 不同的概念。如上所述,隐藏层是在从输入到输出的路径上(以观测⻆度来理解)的隐藏的层,而隐状态则是 在给定步骤所做的任何事情(以技术⻆度来定义)的输入,并且这些状态只能通过先前时间步的数据来计算。
循环神经网络(recurrent neural networks,RNNs)是具有隐状态的神经网络。在介绍循环神经网络模型之 前,我们首先回顾 4.1节中介绍的多层感知机模型。
- 对隐状态使用循环计算的神经网络称为循环神经网络(RNN)。
- 循环神经网络的隐状态可以捕获直到当前时间步序列的历史信息。
- 循环神经网络模型的参数数量不会随着时间步的增加而增加。
- 我们可以使用循环神经网络创建字符级语言模型。
- 我们可以使用困惑度来评价语言模型的质量。
本节将根据 8.4节中的描述,从头开始基于循环神经网络实现字符级语言模型。这样的模型将在H.G.Wells的 时光机器数据集上训练。
- 我们可以训练一个基于循环神经网络的字符级语言模型,根据用户提供的文本的前缀生成后续文本。
- 一个简单的循环神经网络语言模型包括输入编码、循环神经网络模型和输出生成。
- 循环神经网络模型在训练以前需要初始化状态,不过随机抽样和顺序划分使用初始化方法不同。
- 当使用顺序划分时,我们需要分离梯度以减少计算量。
- 在进行任何预测之前,模型通过预热期进行自我更新(例如,获得比初始值更好的隐状态)。
- 梯度裁剪可以防止梯度爆炸,但不能应对梯度消失。
虽然 8.5节 对了解循环神经网络的实现方式具有指导意义,但并不方便。本节将展示如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。
- 深度学习框架的高级API提供了循环神经网络层的实现。
- 高级API的循环神经网络层返回一个输出和一个更新后的隐状态,我们还需要计算整个模型的输出层。
- 相比从零开始实现的循环神经网络,使用高级API实现可以加速训练。
到目前为止,我们已经反复提到像梯度爆炸或梯度消失,以及需要对循环神经网络分离梯度。例如,在 8.5节中, 我们在序列上调用了detach函数。为了能够快速构建模型并了解其工作原理,上面所说的这些概念都没有得 到充分的解释。本节将更深入地探讨序列模型反向传播的细节,以及相关的数学原理。
当我们首次实现循环神经网络(8.5节)时,遇到了梯度爆炸的问题。如果做了练习题,就会发现梯度截断对 于确保模型收敛至关重要。为了更好地理解此问题,本节将回顾序列模型梯度的计算方式,它的工作原理没 有什么新概念,毕竟我们使用的仍然是链式法则来计算梯度。
我们在 4.7节中描述了多层感知机中的前向与反向传播及相关的计算图。循环神经网络中的前向传播相对简 单。通过时间反向传播(backpropagation through time,BPTT)(Werbos, 1990)实际上是循环神经网络中反 向传播技术的一个特定应用。它要求我们将循环神经网络的计算图一次展开一个时间步,以获得模型变量和 参数之间的依赖关系。然后,基于链式法则,应用反向传播来计算和存储梯度。由于序列可能相当⻓,因此 依赖关系也可能相当⻓。例如,某个1000个字符的序列,其第一个词元可能会对最后位置的词元产生重大影 响。这在计算上是不可行的(它需要的时间和内存都太多了),并且还需要超过1000个矩阵的乘积才能得到 非常难以捉摸的梯度。这个过程充满了计算与统计的不确定性。在下文中,我们将阐明会发生什么以及如何 在实践中解决它们。
- “通过时间反向传播”仅仅适用于反向传播在具有隐状态的序列模型。
- 截断是计算方便性和数值稳定性的需要。截断包括:规则截断和随机截断。
- 矩阵的高次幂可能导致神经网络特征值的发散或消失,将以梯度爆炸或梯度消失的形式表现。
- 为了计算的效率,“通过时间反向传播”在计算期间会缓存中间值。
九、现代循环神经网络
前一章中我们介绍了循环神经网络的基础知识, 这种网络可以更好地处理序列数据。 我们在文本数据上实现了基于循环神经网络的语言模型, 但是对于当今各种各样的序列学习问题,这些技术可能并不够用。
例如,循环神经网络在实践中一个常见问题是数值不稳定性。 尽管我们已经应用了梯度裁剪等技巧来缓解这个问题, 但是仍需要通过设计更复杂的序列模型来进一步处理它。 具体来说,我们将引入两个广泛使用的网络, 即门控循环单元(gated recurrent units,GRU)和 长短期记忆网络(long short-term memory,LSTM)。 然后,我们将基于一个单向隐藏层来扩展循环神经网络架构。 我们将描述具有多个隐藏层的深层架构, 并讨论基于前向和后向循环计算的双向设计。 现代循环网络经常采用这种扩展。 在解释这些循环神经网络的变体时, 我们将继续考虑 8节中的语言建模问题。
事实上,语言建模只揭示了序列学习能力的冰山一角。 在各种序列学习问题中,如自动语音识别、文本到语音转换和机器翻译, 输入和输出都是任意长度的序列。 为了阐述如何拟合这种类型的数据, 我们将以机器翻译为例介绍基于循环神经网络的 “编码器-解码器”架构和束搜索,并用它们来生成序列。
在 8.7节中, 我们讨论了如何在循环神经网络中计算梯度, 以及矩阵连续乘积可以导致梯度消失或梯度爆炸的问题。 下面我们简单思考一下这种梯度异常在实践中的意义:
- 我们可能会遇到这样的情况:早期观测值对预测所有未来观测值具有非常重要的意义。 考虑一个极端情况,其中第一个观测值包含一个校验和, 目标是在序列的末尾辨别校验和是否正确。 在这种情况下,第一个词元的影响至关重要。 我们希望有某些机制能够在一个记忆元里存储重要的早期信息。 如果没有这样的机制,我们将不得不给这个观测值指定一个非常大的梯度, 因为它会影响所有后续的观测值。
- 我们可能会遇到这样的情况:一些词元没有相关的观测值。 例如,在对网页内容进行情感分析时, 可能有一些辅助HTML代码与网页传达的情绪无关。 我们希望有一些机制来跳过隐状态表示中的此类词元。
- 我们可能会遇到这样的情况:序列的各个部分之间存在逻辑中断。 例如,书的章节之间可能会有过渡存在, 或者证券的熊市和牛市之间可能会有过渡存在。 在这种情况下,最好有一种方法来重置我们的内部状态表示。
在学术界已经提出了许多方法来解决这类问题。 其中最早的方法是”长短期记忆”(long-short-term memory,LSTM)(Hochreiter and Schmidhuber, 1997), 我们将在 9.2节中讨论。 门控循环单元(gated recurrent unit,GRU) (Cho et al., 2014) 是一个稍微简化的变体,通常能够提供同等的效果, 并且计算 (Chung et al., 2014)的速度明显更快。 由于门控循环单元更简单,我们从它开始解读。
- 门控循环神经网络可以更好地捕获时间步距离很长的序列上的依赖关系。
- 重置门有助于捕获序列中的短期依赖关系。
- 更新门有助于捕获序列中的长期依赖关系。
- 重置门打开时,门控循环单元包含基本循环神经网络;更新门打开时,门控循环单元可以跳过子序列。
长期以来,隐变量模型存在着长期信息保存和短期输入缺失的问题。 解决这一问题的最早方法之一是长短期存储器(long short-term memory,LSTM) (Hochreiter and Schmidhuber, 1997)。 它有许多与门控循环单元( 9.1节)一样的属性。 有趣的是,长短期记忆网络的设计比门控循环单元稍微复杂一些, 却比门控循环单元早诞生了近20年。
- 长短期记忆网络有三种类型的门:输入门、遗忘门和输出门。
- 长短期记忆网络的隐藏层输出包括“隐状态”和“记忆元”。只有隐状态会传递到输出层,而记忆元完全属于内部信息。
- 长短期记忆网络可以缓解梯度消失和梯度爆炸。
到目前为止,我们只讨论了具有一个单向隐藏层的循环神经网络。 其中,隐变量和观测值与具体的函数形式的交互方式是相当随意的。 只要交互类型建模具有足够的灵活性,这就不是一个大问题。 然而,对一个单层来说,这可能具有相当的挑战性。 之前在线性模型中,我们通过添加更多的层来解决这个问题。 而在循环神经网络中,我们首先需要确定如何添加更多的层, 以及在哪里添加额外的非线性,因此这个问题有点棘手。
事实上,我们可以将多层循环神经网络堆叠在一起, 通过对几个简单层的组合,产生了一个灵活的机制。 特别是,数据可能与不同层的堆叠有关。 例如,我们可能希望保持有关金融市场状况 (熊市或牛市)的宏观数据可用, 而微观数据只记录较短期的时间动态。
- 在深度循环神经网络中,隐状态的信息被传递到当前层的下一时间步和下一层的当前时间步。
- 有许多不同风格的深度循环神经网络, 如长短期记忆网络、门控循环单元、或经典循环神经网络。 这些模型在深度学习框架的高级API中都有涵盖。
- 总体而言,深度循环神经网络需要大量的调参(如学习率和修剪) 来确保合适的收敛,模型的初始化也需要谨慎。
在序列学习中,我们以往假设的目标是: 在给定观测的情况下 (例如,在时间序列的上下文中或在语言模型的上下文中), 对下一个输出进行建模。 虽然这是一个典型情景,但不是唯一的。 还可能发生什么其它的情况呢? 我们考虑以下三个在文本序列中填空的任务。
- 我
___
。
- 我
___
饿了。
- 我
___
饿了,我可以吃半头猪。
根据可获得的信息量,我们可以用不同的词填空, 如“很高兴”(”happy”)、“不”(”not”)和“非常”(”very”)。 很明显,每个短语的“下文”传达了重要信息(如果有的话), 而这些信息关乎到选择哪个词来填空, 所以无法利用这一点的序列模型将在相关任务上表现不佳。 例如,如果要做好命名实体识别 (例如,识别“Green”指的是“格林先生”还是绿色), 不同长度的上下文范围重要性是相同的。 为了获得一些解决问题的灵感,让我们先迂回到概率图模型。
- 在双向循环神经网络中,每个时间步的隐状态由当前时间步的前后数据同时决定。
- 双向循环神经网络与概率图模型中的“前向-后向”算法具有相似性。
- 双向循环神经网络主要用于序列编码和给定双向上下文的观测估计。
- 由于梯度链更长,因此双向循环神经网络的训练代价非常高。
语言模型是自然语言处理的关键, 而机器翻译是语言模型最成功的基准测试。 因为机器翻译正是将输入序列转换成输出序列的 序列转换模型(sequence transduction)的核心问题。 序列转换模型在各类现代人工智能应用中发挥着至关重要的作用, 因此我们将其做为本章剩余部分和 10节的重点。 为此,本节将介绍机器翻译问题及其后文需要使用的数据集。
机器翻译(machine translation)指的是 将序列从一种语言自动翻译成另一种语言。 事实上,这个研究领域可以追溯到数字计算机发明后不久的20世纪40年代, 特别是在第二次世界大战中使用计算机破解语言编码。 几十年来,在使用神经网络进行端到端学习的兴起之前, 统计学方法在这一领域一直占据主导地位 (Brown et al., 1990, Brown et al., 1988)。 因为统计机器翻译(statistical machine translation)涉及了 翻译模型和语言模型等组成部分的统计分析, 因此基于神经网络的方法通常被称为 神经机器翻译(neural machine translation), 用于将两种翻译模型区分开来。
本书的关注点是神经网络机器翻译方法,强调的是端到端的学习。 与 8.3节中的语料库 是单一语言的语言模型问题存在不同, 机器翻译的数据集是由源语言和目标语言的文本序列对组成的。 因此,我们需要一种完全不同的方法来预处理机器翻译数据集, 而不是复用语言模型的预处理程序。
- 机器翻译指的是将文本序列从一种语言自动翻译成另一种语言。
- 使用单词级词元化时的词表大小,将明显大于使用字符级词元化时的词表大小。为了缓解这一问题,我们可以将低频词元视为相同的未知词元。
- 通过截断和填充文本序列,可以保证所有的文本序列都具有相同的长度,以便以小批量的方式加载。
正如我们在 9.5节中所讨论的, 机器翻译是序列转换模型的一个核心问题, 其输入和输出都是长度可变的序列。 为了处理这种类型的输入和输出, 我们可以设计一个包含两个主要组件的架构: 第一个组件是一个编码器(encoder): 它接受一个长度可变的序列作为输入, 并将其转换为具有固定形状的编码状态。 第二个组件是解码器(decoder): 它将固定形状的编码状态映射到长度可变的序列。 这被称为编码器-解码器(encoder-decoder)架构。
我们以英语到法语的机器翻译为例: 给定一个英文的输入序列:“They”“are”“watching”“.”。 首先,这种“编码器-解码器”架构将长度可变的输入序列编码成一个“状态”, 然后对该状态进行解码, 一个词元接着一个词元地生成翻译后的序列作为输出: “Ils”“regordent”“.”。 由于“编码器-解码器”架构是形成后续章节中不同序列转换模型的基础, 因此本节将把这个架构转换为接口方便后面的代码实现。
- “编码器-解码器”架构可以将长度可变的序列作为输入和输出,因此适用于机器翻译等序列转换问题。
- 编码器将长度可变的序列作为输入,并将其转换为具有固定形状的编码状态。
- 解码器将具有固定形状的编码状态映射为长度可变的序列。
正如我们在 9.5节中看到的, 机器翻译中的输入序列和输出序列都是长度可变的。 为了解决这类问题,我们在 9.6节中 设计了一个通用的”编码器-解码器“架构。 本节,我们将使用两个循环神经网络的编码器和解码器, 并将其应用于序列到序列(sequence to sequence,seq2seq)类的学习任务 (Cho et al., 2014, Sutskever et al., 2014)。
遵循编码器-解码器架构的设计原则, 循环神经网络编码器使用长度可变的序列作为输入, 将其转换为固定形状的隐状态。 换言之,输入序列的信息被编码到循环神经网络编码器的隐状态中。 为了连续生成输出序列的词元, 独立的循环神经网络解码器是基于输入序列的编码信息 和输出序列已经看见的或者生成的词元来预测下一个词元。
- 根据“编码器-解码器”架构的设计, 我们可以使用两个循环神经网络来设计一个序列到序列学习的模型。
- 在实现编码器和解码器时,我们可以使用多层循环神经网络。
- 我们可以使用遮蔽来过滤不相关的计算,例如在计算损失时。
- 在“编码器-解码器”训练中,强制教学方法将原始输出序列(而非预测结果)输入解码器。
- BLEU是一种常用的评估方法,它通过测量预测序列和标签序列之间的n元语法的匹配度来评估预测。
- 序列搜索策略包括贪心搜索、穷举搜索和束搜索。
- 贪心搜索所选取序列的计算量最小,但精度相对较低。
- 穷举搜索所选取序列的精度最高,但计算量最大。
- 束搜索通过灵活选择束宽,在正确率和计算代价之间进行权衡。
十、注意力机制
灵长类动物的视觉系统接受了大量的感官输入, 这些感官输入远远超过了大脑能够完全处理的程度。 然而,并非所有刺激的影响都是相等的。 意识的聚集和专注使灵长类动物能够在复杂的视觉环境中将注意力引向感兴趣的物体,例如猎物和天敌。 只关注一小部分信息的能力对进化更加有意义,使人类得以生存和成功。
自19世纪以来,科学家们一直致力于研究认知神经科学领域的注意力。 本章的很多章节将涉及到一些研究。
首先回顾一个经典注意力框架,解释如何在视觉场景中展开注意力。 受此框架中的注意力提示(attention cues)的启发, 我们将设计能够利用这些注意力提示的模型。 1964年的Nadaraya-Waston核回归(kernel regression)正是具有 注意力机制(attention mechanism)的机器学习的简单演示。
然后继续介绍的是注意力函数,它们在深度学习的注意力模型设计中被广泛使用。 具体来说,我们将展示如何使用这些函数来设计Bahdanau注意力。 Bahdanau注意力是深度学习中的具有突破性价值的注意力模型,它双向对齐并且可以微分。
最后将描述仅仅基于注意力机制的Transformer架构, 该架构中使用了多头注意力(multi-head attention) 和自注意力(self-attention)。 自2017年横空出世,Transformer一直都普遍存在于现代的深度学习应用中, 例如语言、视觉、语音和强化学习领域。
感谢读者对本书的关注,因为读者的注意力是一种稀缺的资源: 此刻读者正在阅读本书(而忽略了其他的书), 因此读者的注意力是用机会成本(与金钱类似)来支付的。 为了确保读者现在投入的注意力是值得的, 作者们尽全力(全部的注意力)创作一本好书。
自经济学研究稀缺资源分配以来,人们正处在“注意力经济”时代, 即人类的注意力被视为可以交换的、有限的、有价值的且稀缺的商品。 许多商业模式也被开发出来去利用这一点: 在音乐或视频流媒体服务上,人们要么消耗注意力在广告上,要么付钱来隐藏广告; 为了在网络游戏世界的成长,人们要么消耗注意力在游戏战斗中, 从而帮助吸引新的玩家,要么付钱立即变得强大。 总之,注意力不是免费的。
注意力是稀缺的,而环境中的干扰注意力的信息却并不少。 比如人类的视觉神经系统大约每秒收到108位的信息, 这远远超过了大脑能够完全处理的水平。 幸运的是,人类的祖先已经从经验(也称为数据)中认识到 “并非感官的所有输入都是一样的”。 在整个人类历史中,这种只将注意力引向感兴趣的一小部分信息的能力, 使人类的大脑能够更明智地分配资源来生存、成长和社交, 例如发现天敌、找寻食物和伴侣。
- 人类的注意力是有限的、有价值和稀缺的资源。
- 受试者使用非自主性和自主性提示有选择性地引导注意力。前者基于突出性,后者则依赖于意识。
- 注意力机制与全连接层或者汇聚层的区别源于增加的自主提示。
- 由于包含了自主性提示,注意力机制与全连接的层或汇聚层不同。
- 注意力机制通过注意力汇聚使选择偏向于值(感官输入),其中包含查询(自主性提示)和键(非自主性提示)。键和值是成对的。
- 可视化查询和键之间的注意力权重是可行的。
上节介绍了框架下的注意力机制的主要成分 图10.1.3:查询(自主提示)和键(非自主提示)之间的交互形成 了注意力汇聚;注意力汇聚有选择地聚合了值(感官输入)以生成最终的输出。本节将介绍注意力汇聚的更 多细节,以便从宏观上了解注意力机制在实践中的运作方式。具体来说,1964年提出的Nadaraya-Watson核 回归模型是一个简单但完整的例子,可以用于演示具有注意力机制的机器学习。
- Nadaraya-Watson核回归是具有注意力机制的机器学习范例。
- Nadaraya-Watson核回归的注意力汇聚是对训练数据中输出的加权平均。从注意力的角度来看,分配给每个值的注意力权重取决于将值所对应的键和查询作为输入的函数。
- 注意力汇聚可以分为非参数型和带参数型。
10.2节使用了高斯核来对查询和键之间的关系建模。 (10.2.6)中的 高斯核指数部分可以视为注意力评分函数(attention scoring function), 简称评分函数(scoring function), 然后把这个函数的输出结果输入到softmax函数中进行运算。 通过上述步骤,将得到与键对应的值的概率分布(即注意力权重)。 最后,注意力汇聚的输出就是基于这些注意力权重的值的加权和。
- 将注意力汇聚的输出计算可以作为值的加权平均,选择不同的注意力评分函数会带来不同的注意力汇聚操作。
- 当查询和键是不同长度的矢量时,可以使用可加性注意力评分函数。当它们的长度相同时,使用缩放的“点-积”注意力评分函数的计算效率更高。
9.7节中探讨了机器翻译问题: 通过设计一个基于两个循环神经网络的编码器-解码器架构, 用于序列到序列学习。 具体来说,循环神经网络编码器将长度可变的序列转换为固定形状的上下文变量, 然后循环神经网络解码器根据生成的词元和上下文变量 按词元生成输出(目标)序列词元。 然而,即使并非所有输入(源)词元都对解码某个词元都有用, 在每个解码步骤中仍使用编码相同的上下文变量。 有什么方法能改变上下文变量呢?
我们试着从 (Graves, 2013)中找到灵感: 在为给定文本序列生成手写的挑战中, Graves设计了一种可微注意力模型, 将文本字符与更长的笔迹对齐, 其中对齐方式仅向一个方向移动。 受学习对齐想法的启发,Bahdanau等人提出了一个没有严格单向对齐限制的 可微注意力模型 (Bahdanau et al., 2014)。 在预测词元时,如果不是所有输入词元都相关,模型将仅对齐(或参与)输入序列中与当前预测相关的部分。这是通过将上下文变量视为注意力集中的输出来实现的。
- 在预测词元时,如果不是所有输入词元都是相关的,那么具有Bahdanau注意力的循环神经网络编码器-解码器会有选择地统计输入序列的不同部分。这是通过将上下文变量视为加性注意力池化的输出来实现的。
- 在循环神经网络编码器-解码器中,Bahdanau注意力将上一时间步的解码器隐状态视为查询,在所有时间步的编码器隐状态同时视为键和值。
在实践中,当给定相同的查询、键和值的集合时, 我们希望模型可以基于相同的注意力机制学习到不同的行为, 然后将不同的行为作为知识组合起来, 捕获序列内各种范围的依赖关系 (例如,短距离依赖和长距离依赖关系)。 因此,允许注意力机制组合使用查询、键和值的不同子空间表示(representation subspaces)可能是有益的。
为此,与其只使用单独一个注意力汇聚, 我们可以用独立学习得到的h组不同的 线性投影(linear projections)来变换查询、键和值。 然后,这h组变换后的查询、键和值将并行地送到注意力汇聚中。 最后,将这h个注意力汇聚的输出拼接在一起, 并且通过另一个可以学习的线性投影进行变换, 以产生最终输出。 这种设计被称为多头注意力(multihead attention) (Vaswani et al., 2017)。 对于h个注意力汇聚输出,每一个注意力汇聚都被称作一个头(head)。 图10.5.1 展示了使用全连接层来实现可学习的线性变换的多头注意力。
- 多头注意力融合了来自于多个注意力汇聚的不同知识,这些知识的不同来源于相同的查询、键和值的不同的子空间表示。
- 基于适当的张量操作,可以实现多头注意力的并行计算。
在深度学习中,经常使用卷积神经网络(CNN)或循环神经网络(RNN)对序列进行编码。 想象一下,有了注意力机制之后,我们将词元序列输入注意力池化中, 以便同一组词元同时充当查询、键和值。 具体来说,每个查询都会关注所有的键-值对并生成一个注意力输出。 由于查询、键和值来自同一组输入,因此被称为 自注意力(self-attention) (Lin et al., 2017, Vaswani et al., 2017), 也被称为内部注意力(intra-attention) (Cheng et al., 2016, Parikh et al., 2016, Paulus et al., 2017)。 本节将使用自注意力进行序列编码,以及如何使用序列的顺序作为补充信息。
- 在自注意力中,查询、键和值都来自同一组输入。
- 卷积神经网络和自注意力都拥有并行计算的优势,而且自注意力的最大路径长度最短。但是因为其计算复杂度是关于序列长度的二次方,所以在很长的序列中计算会非常慢。
- 为了使用序列的顺序信息,可以通过在输入表示中添加位置编码,来注入绝对的或相对的位置信息。
10.6.2节中比较了卷积神经网络(CNN)、循环神经网络(RNN)和自注意力(self-attention)。值得注意的是,自注意力同时具有并行计算和最短的最大路径长度这两个优势。因此,使用自注意力来设计深度架构是很有吸引力的。对比之前仍然依赖循环神经网络实现输入表示的自注意力模型 (Cheng et al., 2016, Lin et al., 2017, Paulus et al., 2017),Transformer模型完全基于注意力机制,没有任何卷积层或循环神经网络层 (Vaswani et al., 2017)。尽管Transformer最初是应用于在文本数据上的序列到序列学习,但现在已经推广到各种现代的深度学习中,例如语言、视觉、语音和强化学习领域。
- Transformer是编码器-解码器架构的一个实践,尽管在实际情况中编码器或解码器可以单独使用。
- 在Transformer中,多头自注意力用于表示输入序列和输出序列,不过解码器必须通过掩蔽机制来保留自回归属性。
- Transformer中的残差连接和层规范化是训练非常深度模型的重要工具。
- Transformer模型中基于位置的前馈网络使用同一个多层感知机,作用是对所有序列位置的表示进行转换。
十一、优化算法
截止到目前,本书已经使用了许多优化算法来训练深度学习模型。优化算法使我们能够继续更新模型参数,并使损失函数的值最小化。这就像在训练集上评估一样。事实上,任何满足于将优化视为黑盒装置,以在简单的设置中最小化目标函数的人,都可能会知道存在着一系列此类“咒语”(名称如“SGD”和“Adam”)。
但是,为了做得更好,还需要更深入的知识。优化算法对于深度学习非常重要。一方面,训练复杂的深度学习模型可能需要数小时、几天甚至数周。优化算法的性能直接影响模型的训练效率。另一方面,了解不同优化算法的原则及其超参数的作用将使我们能够以有针对性的方式调整超参数,以提高深度学习模型的性能。
在本章中,我们深入探讨常见的深度学习优化算法。深度学习中出现的几乎所有优化问题都是非凸的。尽管如此,在凸问题背景下设计和分析算法是非常有启发性的。正是出于这个原因,本章包括了凸优化的入门,以及凸目标函数上非常简单的随机梯度下降算法的证明。
本节将讨论优化与深度学习之间的关系以及在深度学习中使用优化的挑战。对于深度学习问题,我们通常会先定义损失函数。一旦我们有了损失函数,我们就可以使用优化算法来尝试最小化损失。在优化中,损失函数通常被称为优化问题的目标函数。按照传统惯例,大多数优化算法都关注的是最小化。如果我们需要最大化目标,那么有一个简单的解决方案:在目标函数前加负号即可。
尽管优化提供了一种最大限度地减少深度学习损失函数的方法,但本质上,优化和深度学习的目标是根本不同的。前者主要关注的是最小化目标,后者则关注在给定有限数据量的情况下寻找合适的模型。在 4.4节中,我们详细讨论了这两个目标之间的区别。例如,训练误差和泛化误差通常不同:由于优化算法的目标函数通常是基于训练数据集的损失函数,因此优化的目标是减少训练误差。但是,深度学习(或更广义地说,统计推断)的目标是减少泛化误差。为了实现后者,除了使用优化算法来减少训练误差之外,我们还需要注意过拟合。
- 最小化训练误差并不能保证我们找到最佳的参数集来最小化泛化误差。
- 优化问题可能有许多局部最小值。
- 一个问题可能有很多的鞍点,因为问题通常不是凸的。
- 梯度消失可能会导致优化停滞,重参数化通常会有所帮助。对参数进行良好的初始化也可能是有益的。
凸性(convexity)在优化算法的设计中起到至关重要的作用, 这主要是由于在这种情况下对算法进行分析和测试要容易。 换言之,如果算法在凸性条件设定下的效果很差, 那通常我们很难在其他条件下看到好的结果。 此外,即使深度学习中的优化问题通常是非凸的, 它们也经常在局部极小值附近表现出一些凸性。 这可能会产生一些像 (Izmailov et al., 2018)这样比较有意思的新优化变体。
在深度学习的背景下,凸函数的主要目的是帮助我们详细了解优化算法。 我们由此得出梯度下降法和随机梯度下降法是如何相应推导出来的。
- 凸集的交点是凸的,并集不是。
- 根据詹森不等式,“一个多变量凸函数的总期望值”大于或等于“用每个变量的期望值计算这个函数的总值“。
- 一个二次可微函数是凸函数,当且仅当其Hessian(二阶导数矩阵)是半正定的。
- 凸约束可以通过拉格朗日函数来添加。在实践中,只需在目标函数中加上一个惩罚就可以了。
- 投影映射到凸集中最接近原始点的点。
尽管梯度下降(gradient descent)很少直接用于深度学习, 但了解它是理解下一节随机梯度下降算法的关键。 例如,由于学习率过大,优化问题可能会发散,这种现象早已在梯度下降中出现。 同样地,预处理(preconditioning)是梯度下降中的一种常用技术, 还被沿用到更高级的算法中。 让我们从简单的一维梯度下降开始。
- 学习率的大小很重要:学习率太大会使模型发散,学习率太小会没有进展。
- 梯度下降会可能陷入局部极小值,而得不到全局最小值。
- 在高维模型中,调整学习率是很复杂的。
- 预处理有助于调节比例。
- 牛顿法在凸问题中一旦开始正常工作,速度就会快得多。
- 对于非凸问题,不要不作任何调整就使用牛顿法。
在前面的章节中,我们一直在训练过程中使用随机梯度下降,但没有解释它为什么起作用。为了澄清这一点,我们刚在 11.3节中描述了梯度下降的基本原则。本节继续更详细地说明随机梯度下降(stochastic gradient descent)。
- 对于凸问题,我们可以证明,对于广泛的学习率选择,随机梯度下降将收敛到最优解。
- 对于深度学习而言,情况通常并非如此。但是,对凸问题的分析使我们能够深入了解如何进行优化,即逐步降低学习率,尽管不是太快。
- 如果学习率太小或太大,就会出现问题。实际上,通常只有经过多次实验后才能找到合适的学习率。
- 当训练数据集中有更多样本时,计算梯度下降的每次迭代的代价更高,因此在这些情况下,首选随机梯度下降。
- 随机梯度下降的最优性保证在非凸情况下一般不可用,因为需要检查的局部最小值的数量可能是指数级的。
到目前为止,我们在基于梯度的学习方法中遇到了两个极端情况: 11.3节中使用完整数据集来计算梯度并更新参数, 11.4节中一次处理一个训练样本来取得进展。 二者各有利弊:每当数据非常相似时,梯度下降并不是非常“数据高效”。 而由于CPU和GPU无法充分利用向量化,随机梯度下降并不特别“计算高效”。 这暗示了两者之间可能有折中方案,这便涉及到小批量随机梯度下降(minibatch gradient descent)。
- 由于减少了深度学习框架的额外开销,使用更好的内存定位以及CPU和GPU上的缓存,向量化使代码更加高效。
- 随机梯度下降的“统计效率”与大批量一次处理数据的“计算效率”之间存在权衡。小批量随机梯度下降提供了两全其美的答案:计算和统计效率。
- 在小批量随机梯度下降中,我们处理通过训练数据的随机排列获得的批量数据(即每个观测值只处理一次,但按随机顺序)。
- 在训练期间降低学习率有助于训练。
- 一般来说,小批量随机梯度下降比随机梯度下降和梯度下降的速度快,收敛风险较小。
在 11.4节一节中,我们详述了如何执行随机梯度下降,即在只有嘈杂的梯度可用的情况下执行优化时会发生什么。 对于嘈杂的梯度,我们在选择学习率需要格外谨慎。 如果衰减速度太快,收敛就会停滞。 相反,如果太宽松,我们可能无法收敛到最优解。
- 动量法用过去梯度的平均值来替换梯度,这大大加快了收敛速度。
- 对于无噪声梯度下降和嘈杂随机梯度下降,动量法都是可取的。
- 动量法可以防止在随机梯度下降的优化过程停滞的问题。
- 由于对过去的数据进行了指数降权,有效梯度数为11−β。
- 在凸二次问题中,可以对动量法进行明确而详细的分析。
- 动量法的实现非常简单,但它需要我们存储额外的状态向量(动量v)。
- AdaGrad算法会在单个坐标层面动态降低学习率。
- AdaGrad算法利用梯度的大小作为调整进度速率的手段:用较小的学习率来补偿带有较大梯度的坐标。
- 在深度学习问题中,由于内存和计算限制,计算准确的二阶导数通常是不可行的。梯度可以作为一个有效的代理。
- 如果优化问题的结构相当不均匀,AdaGrad算法可以帮助缓解扭曲。
- AdaGrad算法对于稀疏特征特别有效,在此情况下由于不常出现的问题,学习率需要更慢地降低。
- 在深度学习问题上,AdaGrad算法有时在降低学习率方面可能过于剧烈。我们将在 11.10节一节讨论缓解这种情况的策略
- RMSProp算法与Adagrad算法非常相似,因为两者都使用梯度的平方来缩放系数。
- RMSProp算法与动量法都使用泄漏平均值。但是,RMSProp算法使用该技术来调整按系数顺序的预处理器。
- 在实验中,学习率需要由实验者调度。
- 系数γ决定了在调整每坐标比例时历史记录的时长。
Adadelta是AdaGrad的另一种变体( 11.7节), 主要区别在于前者减少了学习率适应坐标的数量。 此外,广义上Adadelta被称为没有学习率,因为它使用变化量本身作为未来变化的校准。 Adadelta算法是在 (Zeiler, 2012)中提出的。
- Adadelta没有学习率参数。相反,它使用参数本身的变化率来调整学习率。
- Adadelta需要两个状态变量来存储梯度的二阶导数和参数的变化。
- Adadelta使用泄漏的平均值来保持对适当统计数据的运行估计。
本章我们已经学习了许多有效优化的技术。 在本节讨论之前,我们先详细回顾一下这些技术:
- 在 11.4节中,我们学习了:随机梯度下降在解决优化问题时比梯度下降更有效。
- 在 11.5节中,我们学习了:在一个小批量中使用更大的观测值集,可以通过向量化提供额外效率。这是高效的多机、多GPU和整体并行处理的关键。
- 在 11.6节中我们添加了一种机制,用于汇总过去梯度的历史以加速收敛。
- 在 11.7节中,我们通过对每个坐标缩放来实现高效计算的预处理器。
- 在 11.8节中,我们通过学习率的调整来分离每个坐标的缩放。
Adam算法 (Kingma and Ba, 2014)将所有这些技术汇总到一个高效的学习算法中。 不出预料,作为深度学习中使用的更强大和有效的优化算法之一,它非常受欢迎。 但是它并非没有问题,尤其是 (Reddi et al., 2019)表明,有时Adam算法可能由于方差控制不良而发散。 在完善工作中, (Zaheer et al., 2018)给Adam算法提供了一个称为Yogi的热补丁来解决这些问题。 下面我们了解一下Adam算法。
- Adam算法将许多优化算法的功能结合到了相当强大的更新规则中。
- Adam算法在RMSProp算法基础上创建的,还在小批量的随机梯度上使用EWMA。
- 在估计动量和二次矩时,Adam算法使用偏差校正来调整缓慢的启动速度。
- 对于具有显著差异的梯度,我们可能会遇到收敛性问题。我们可以通过使用更大的小批量或者切换到改进的估计值st来修正它们。Yogi提供了这样的替代方案。
到目前为止,我们主要关注如何更新权重向量的优化算法,而不是它们的更新速率。 然而,调整学习率通常与实际算法同样重要,有如下几方面需要考虑:
- 首先,学习率的大小很重要。如果它太大,优化就会发散;如果它太小,训练就会需要过长时间,或者我们最终只能得到次优的结果。我们之前看到问题的条件数很重要(有关详细信息,请参见 11.6节)。直观地说,这是最不敏感与最敏感方向的变化量的比率。
- 其次,衰减速率同样很重要。如果学习率持续过高,我们可能最终会在最小值附近弹跳,从而无法达到最优解。 11.5节比较详细地讨论了这一点,在 11.4节中我们则分析了性能保证。简而言之,我们希望速率衰减,但要比慢,这样能成为解决凸问题的不错选择。
- 另一个同样重要的方面是初始化。这既涉及参数最初的设置方式(详情请参阅 4.8节),又关系到它们最初的演变方式。这被戏称为预热(warmup),即我们最初开始向着解决方案迈进的速度有多快。一开始的大步可能没有好处,特别是因为最初的参数集是随机的。最初的更新方向可能也是毫无意义的。
- 最后,还有许多优化变体可以执行周期性学习率调整。这超出了本章的范围,我们建议读者阅读 (Izmailov et al., 2018)来了解个中细节。例如,如何通过对整个路径参数求平均值来获得更好的解。
鉴于管理学习率需要很多细节,因此大多数深度学习框架都有自动应对这个问题的工具。 在本章中,我们将梳理不同的调度策略对准确性的影响,并展示如何通过学习率调度器(learning rate scheduler)来有效管理。
- 在训练期间逐步降低学习率可以提高准确性,并且减少模型的过拟合。
- 在实验中,每当进展趋于稳定时就降低学习率,这是很有效的。从本质上说,这可以确保我们有效地收敛到一个适当的解,也只有这样才能通过降低学习率来减小参数的固有方差。
- 余弦调度器在某些计算机视觉问题中很受欢迎。
- 优化之前的预热期可以防止发散。
- 优化在深度学习中有多种用途。对于同样的训练误差而言,选择不同的优化算法和学习率调度,除了最大限度地减少训练时间,可以导致测试集上不同的泛化和过拟合量。
十二、计算性能
在深度学习中,数据集和模型通常都很大,导致计算量也会很大。 因此,计算的性能非常重要。 本章将集中讨论影响计算性能的主要因素:命令式编程、符号编程、 异步计算、自动并行和多GPU计算。 通过学习本章,对于前几章中实现的那些模型,可以进一步提高它们的计算性能。 例如,我们可以在不影响准确性的前提下,大大减少训练时间。
- 命令式编程使得新模型的设计变得容易,因为可以依据控制流编写代码,并拥有相对成熟的Python软件生态。
- 符号式编程要求我们先定义并且编译程序,然后再执行程序,其好处是提高了计算性能。
今天的计算机是高度并行的系统,由多个CPU核、多个GPU、多个处理单元组成。通常每个CPU核有多个线程,每个设备通常有多个GPU,每个GPU有多个处理单元。总之,我们可以同时处理许多不同的事情,并且通常是在不同的设备上。不幸的是,Python并不善于编写并行和异步代码,至少在没有额外帮助的情况下不是好选择。归根结底,Python是单线程的,将来也是不太可能改变的。因此在诸多的深度学习框架中,MXNet和TensorFlow之类则采用了一种异步编程(asynchronous programming)模型来提高性能,而PyTorch则使用了Python自己的调度器来实现不同的性能权衡。对PyTorch来说GPU操作在默认情况下是异步的。当调用一个使用GPU的函数时,操作会排队到特定的设备上,但不一定要等到以后才执行。这允许我们并行执行更多的计算,包括在CPU或其他GPU上的操作。
因此,了解异步编程是如何工作的,通过主动地减少计算需求和相互依赖,有助于我们开发更高效的程序。这能够减少内存开销并提高处理器利用率。
- 深度学习框架可以将Python前端的控制与后端的执行解耦,使得命令可以快速地异步插入后端、并行执行。
- 异步产生了一个相当灵活的前端,但请注意:过度填充任务队列可能会导致内存消耗过多。建议对每个小批量进行同步,以保持前端和后端大致同步。
- 芯片供应商提供了复杂的性能分析工具,以获得对深度学习效率更精确的洞察。
深度学习框架(例如,MxNet、飞桨和PyTorch)会在后端自动构建计算图。利用计算图,系统可以了解所有依赖关系,并且可以选择性地并行执行多个不相互依赖的任务以提高速度。例如, 12.2节中的 图12.2.2独立初始化两个变量。因此,系统可以选择并行执行它们。
通常情况下单个操作符将使用所有CPU或单个GPU上的所有计算资源。例如,即使在一台机器上有多个CPU处理器,dot
操作符也将使用所有CPU上的所有核心(和线程)。这样的行为同样适用于单个GPU。因此,并行化对单设备计算机来说并不是很有用,而并行化对于多个设备就很重要了。虽然并行化通常应用在多个GPU之间,但增加本地CPU以后还将提高少许性能。例如, (Hadjis et al., 2016)则把结合GPU和CPU的训练应用到计算机视觉模型中。借助自动并行化框架的便利性,我们可以依靠几行Python代码实现相同的目标。对自动并行计算的讨论主要集中在使用CPU和GPU的并行计算上,以及计算和通信的并行化内容。
请注意,本节中的实验至少需要两个GPU来运行。
- 现代系统拥有多种设备,如多个GPU和多个CPU,还可以并行地、异步地使用它们。
- 现代系统还拥有各种通信资源,如PCI Express、存储(通常是固态硬盘或网络存储)和网络带宽,为了达到最高效率可以并行使用它们。
- 后端可以通过自动化地并行计算和通信来提高性能。
很好地理解算法和模型才可以捕获统计方面的问题,构建出具有出色性能的系统。同时,至少对底层硬件有一定的了解也是必不可少的。本节不能替代硬件和系统设计的相关课程。相反,本节的内容可以作为理解某些算法为什么比其他算法更高效以及如何实现良好吞吐量的起点。一个好的设计可以很容易地在性能上造就数量级的差异,这也是后续产生的能够训练网络(例如,训练时间为1周)和无法训练网络(训练时间为3个月,导致错过截止期)之间的差异。我们先从计算机的研究开始。然后深入查看CPU和GPU。最后,再查看数据中心或云中的多台计算机的连接方式。
图12.4.1 每个程序员都应该知道的延迟数字
也可以通过 图12.4.1进行简单的了解,图片源自科林·斯科特的互动帖子,在帖子中很好地概述了过去十年的进展。原始的数字是取自于杰夫迪恩的Stanford讲座。下面的讨论解释了这些数字的一些基本原理,以及它们如何指导我们去设计算法。下面的讨论是非常笼统和粗略的。很显然,它并不能代替一门完整的课程,而只是为了给统计建模者提供足够的信息,让他们做出合适的设计决策。对于计算机体系结构的深入概述,建议读者参考 (Hennessy and Patterson, 2011)或关于该主题的最新课程,例如Arste Asanovic。
- 设备有运行开销。因此,数据传输要争取量大次少而不是量少次多。这适用于RAM、固态驱动器、网络和GPU。
- 矢量化是性能的关键。确保充分了解加速器的特定功能。例如,一些Intel Xeon CPU特别适用于INT8操作,NVIDIA Volta GPU擅长FP16矩阵操作,NVIDIA Turing擅长FP16、INT8和INT4操作。
- 在训练过程中数据类型过小导致的数值溢出可能是个问题(在推断过程中则影响不大)。
- 数据混叠现象会导致严重的性能退化。64位CPU应该按照64位边界进行内存对齐。在GPU上建议保持卷积大小对齐,例如:与张量核对齐。
- 将算法与硬件相匹配(例如,内存占用和带宽)。将命中参数装入缓存后,可以实现很大数量级的加速比。
- 在验证实验结果之前,建议先在纸上勾勒出新算法的性能。关注的原因是数量级及以上的差异。
- 使用调试器跟踪调试寻找性能的瓶颈。
- 训练硬件和推断硬件在性能和价格方面有不同的优点。
到目前为止,我们讨论了如何在CPU和GPU上高效地训练模型,同时在 12.3节中展示了深度学习框架如何在CPU和GPU之间自动地并行化计算和通信,还在 5.6节中展示了如何使用nvidia-smi
命令列出计算机上所有可用的GPU。 但是我们没有讨论如何真正实现深度学习训练的并行化。 是否一种方法,以某种方式分割数据到多个设备上,并使其能够正常工作呢? 本节将详细介绍如何从零开始并行地训练网络, 这里需要运用小批量随机梯度下降算法(详见 11.5节)。 后面我还讲介绍如何使用高级API并行训练网络(请参阅 12.6节)。
- 有多种方法可以在多个GPU上拆分深度网络的训练。拆分可以在层之间、跨层或跨数据上实现。前两者需要对数据传输过程进行严格编排,而最后一种则是最简单的策略。
- 数据并行训练本身是不复杂的,它通过增加有效的小批量数据量的大小提高了训练效率。
- 在数据并行中,数据需要跨多个GPU拆分,其中每个GPU执行自己的前向传播和反向传播,随后所有的梯度被聚合为一,之后聚合结果向所有的GPU广播。
- 小批量数据量更大时,学习率也需要稍微提高一些。
每个新模型的并行计算都从零开始实现是无趣的。此外,优化同步工具以获得高性能也是有好处的。下面我们将展示如何使用深度学习框架的高级API来实现这一点。数学和算法与 12.5节中的相同。本节的代码至少需要两个GPU来运行。
- 神经网络可以在(可找到数据的)单GPU上进行自动评估。
- 每台设备上的网络需要先初始化,然后再尝试访问该设备上的参数,否则会遇到错误。
- 优化算法在多个GPU上自动聚合。
当我们从一个GPU迁移到多个GPU时,以及再迁移到包含多个GPU的多个服务器时(可能所有服务器的分布跨越了多个机架和多个网络交换机),分布式并行训练算法也需要变得更加复杂。通过细节可以知道,一方面是不同的互连方式的带宽存在极大的区别(例如,NVLink可以通过设置实现跨6条链路的高达100GB/s的带宽,16通道的PCIe4.0提供32GB/s的带宽,而即使是高速100GbE以太网也只能提供大约10GB/s的带宽);另一方面是期望开发者既能完成统计学习建模还精通系统和网络也是不切实际的。
参数服务器的核心思想首先是由 (Smola and Narayanamurthy, 2010)在分布式隐变量模型的背景下引入的。然后,在 (Ahmed et al., 2012)中描述了Push和Pull的语义,又在 (Li et al., 2014)中描述了系统和开源库。下面,我们将介绍用于提高计算效率的组件。
- 同步需要高度适应特定的网络基础设施和服务器内的连接,这种适应会严重影响同步所需的时间。
- 环同步对于p3和DGX-2服务器是最佳的,而对于其他服务器则未必。
- 当添加多个参数服务器以增加带宽时,分层同步策略可以工作的很好。
十三、计算机视觉
近年来,深度学习一直是提高计算机视觉系统性能的变革力量。 无论是医疗诊断、自动驾驶,还是智能滤波器、摄像头监控,许多计算机视觉领域的应用都与我们当前和未来的生活密切相关。 可以说,最先进的计算机视觉应用与深度学习几乎是不可分割的。 有鉴于此,本章将重点介绍计算机视觉领域,并探讨最近在学术界和行业中具有影响力的方法和应用。
在 6节和 7节中,我们研究了计算机视觉中常用的各种卷积神经网络,并将它们应用到简单的图像分类任务中。 本章开头,我们将介绍两种可以改进模型泛化的方法,即图像增广和微调,并将它们应用于图像分类。 由于深度神经网络可以有效地表示多个层次的图像,因此这种分层表示已成功用于各种计算机视觉任务,例如目标检测(object detection)、语义分割(semantic segmentation)和样式迁移(style transfer)。 秉承计算机视觉中利用分层表示的关键思想,我们将从物体检测的主要组件和技术开始,继而展示如何使用完全卷积网络对图像进行语义分割,然后我们将解释如何使用样式迁移技术来生成像本书封面一样的图像。 最后在结束本章时,我们将本章和前几章的知识应用于两个流行的计算机视觉基准数据集。
7.1节提到过大型数据集是成功应用深度神经网络的先决条件。 图像增广在对训练图像进行一系列的随机变化之后,生成相似但不同的训练样本,从而扩大了训练集的规模。 此外,应用图像增广的原因是,随机改变训练样本可以减少模型对某些属性的依赖,从而提高模型的泛化能力。 例如,我们可以以不同的方式裁剪图像,使感兴趣的对象出现在不同的位置,减少模型对于对象出现位置的依赖。 我们还可以调整亮度、颜色等因素来降低模型对颜色的敏感度。 可以说,图像增广技术对于AlexNet的成功是必不可少的。本节将讨论这项广泛应用于计算机视觉的技术。
- 图像增广基于现有的训练数据生成随机图像,来提高模型的泛化能力。
- 为了在预测过程中得到确切的结果,我们通常对训练样本只进行图像增广,而在预测过程中不使用带随机操作的图像增广。
- 深度学习框架提供了许多不同的图像增广方法,这些方法可以被同时应用。
前面的一些章节介绍了如何在只有6万张图像的Fashion-MNIST训练数据集上训练模型。 我们还描述了学术界当下使用最广泛的大规模图像数据集ImageNet,它有超过1000万的图像和1000类的物体。 然而,我们平常接触到的数据集的规模通常在这两者之间。
假如我们想识别图片中不同类型的椅子,然后向用户推荐购买链接。 一种可能的方法是首先识别100把普通椅子,为每把椅子拍摄1000张不同角度的图像,然后在收集的图像数据集上训练一个分类模型。 尽管这个椅子数据集可能大于Fashion-MNIST数据集,但实例数量仍然不到ImageNet中的十分之一。 适合ImageNet的复杂模型可能会在这个椅子数据集上过拟合。 此外,由于训练样本数量有限,训练模型的准确性可能无法满足实际要求。
为了解决上述问题,一个显而易见的解决方案是收集更多的数据。 但是,收集和标记数据可能需要大量的时间和金钱。 例如,为了收集ImageNet数据集,研究人员花费了数百万美元的研究资金。 尽管目前的数据收集成本已大幅降低,但这一成本仍不能忽视。
另一种解决方案是应用迁移学习(transfer learning)将从源数据集学到的知识迁移到目标数据集。 例如,尽管ImageNet数据集中的大多数图像与椅子无关,但在此数据集上训练的模型可能会提取更通用的图像特征,这有助于识别边缘、纹理、形状和对象组合。 这些类似的特征也可能有效地识别椅子。
- 迁移学习将从源数据集中学到的知识迁移到目标数据集,微调是迁移学习的常见技巧。
- 除输出层外,目标模型从源模型中复制所有模型设计及其参数,并根据目标数据集对这些参数进行微调。但是,目标模型的输出层需要从头开始训练。
- 通常,微调参数使用较小的学习率,而从头开始训练输出层可以使用更大的学习率。
前面的章节(例如 7.1节— 7.4节)介绍了各种图像分类模型。 在图像分类任务中,我们假设图像中只有一个主要物体对象,我们只关注如何识别其类别。 然而,很多时候图像里有多个我们感兴趣的目标,我们不仅想知道它们的类别,还想得到它们在图像中的具体位置。 在计算机视觉里,我们将这类任务称为目标检测(object detection)或目标识别(object recognition)。
目标检测在多个领域中被广泛使用。 例如,在无人驾驶里,我们需要通过识别拍摄到的视频图像里的车辆、行人、道路和障碍物的位置来规划行进线路。 机器人也常通过该任务来检测感兴趣的目标。安防领域则需要检测异常目标,如歹徒或者炸弹。
接下来的几节将介绍几种用于目标检测的深度学习方法。
- 目标检测不仅可以识别图像中所有感兴趣的物体,还能识别它们的位置,该位置通常由矩形边界框表示。
- 我们可以在两种常用的边界框表示(中间,宽度,高度)和(左上,右下)坐标之间进行转换。
目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边界从而更准确地预测目标的真实边界框(ground-truth bounding box)。 不同的模型使用的区域采样方法可能不同。 这里我们介绍其中的一种方法:以每个像素为中心,生成多个缩放比和宽高比(aspect ratio)不同的边界框。 这些边界框被称为锚框(anchor box)我们将在 13.7节中设计一个基于锚框的目标检测模型。
- 我们以图像的每个像素为中心生成不同形状的锚框。
- 交并比(IoU)也被称为杰卡德系数,用于衡量两个边界框的相似性。它是相交面积与相并面积的比率。
- 在训练集中,我们需要给每个锚框两种类型的标签。一个是与锚框中目标检测的类别,另一个是锚框真实相对于边界框的偏移量。
- 预测期间可以使用非极大值抑制(NMS)来移除类似的预测边界框,从而简化输出。
在 13.4节中,我们以输入图像的每个像素为中心,生成了多个锚框。 基本而言,这些锚框代表了图像不同区域的样本。 然而,如果为每个像素都生成的锚框,我们最终可能会得到太多需要计算的锚框。 想象一个561×728的输入图像,如果以每个像素为中心生成五个形状不同的锚框,就需要在图像上标记和预测超过200万个锚框(561×728×5)。
- 在多个尺度下,我们可以生成不同尺寸的锚框来检测不同尺寸的目标。
- 通过定义特征图的形状,我们可以决定任何图像上均匀采样的锚框的中心。
- 我们使用输入图像在某个感受野区域内的信息,来预测输入图像上与该区域位置相近的锚框类别和偏移量。
- 我们可以通过深入学习,在多个层次上的图像分层表示进行多尺度目标检测。
目标检测领域没有像MNIST和Fashion-MNIST那样的小数据集。 为了快速测试目标检测模型,我们收集并标记了一个小型数据集。 首先,我们拍摄了一组香蕉的照片,并生成了1000张不同角度和大小的香蕉图像。 然后,我们在一些背景图片的随机位置上放一张香蕉的图像。 最后,我们在图片上为这些香蕉标记了边界框。
- 我们收集的香蕉检测数据集可用于演示目标检测模型。
- 用于目标检测的数据加载与图像分类的数据加载类似。但是,在目标检测中,标签还包含真实边界框的信息,它不出现在图像分类中。
在 13.3节— 13.6节中,我们分别介绍了边界框、锚框、多尺度目标检测和用于目标检测的数据集。 现在我们已经准备好使用这样的背景知识来设计一个目标检测模型:单发多框检测(SSD) (Liu et al., 2016)。 该模型简单、快速且被广泛使用。尽管这只是其中一种目标检测模型,但本节中的一些设计原则和实现细节也适用于其他模型。
- 单发多框检测是一种多尺度目标检测模型。基于基础网络块和各个多尺度特征块,单发多框检测生成不同数量和不同大小的锚框,并通过预测这些锚框的类别和偏移量检测不同大小的目标。
- 在训练单发多框检测模型时,损失函数是根据锚框的类别和偏移量的预测及标注值计算得出的。
除了 13.7节中描述的单发多框检测之外, 区域卷积神经网络(region-based CNN或regions with CNN features,R-CNN) (Girshick et al., 2014)也是将深度模型应用于目标检测的开创性工作之一。 本节将介绍R-CNN及其一系列改进方法:快速的R-CNN(Fast R-CNN) (Girshick, 2015)、更快的R-CNN(Faster R-CNN) (Ren et al., 2015)和掩码R-CNN(Mask R-CNN) (He et al., 2017)。 限于篇幅,我们只着重介绍这些模型的设计思路。
- R-CNN对图像选取若干提议区域,使用卷积神经网络对每个提议区域执行前向传播以抽取其特征,然后再用这些特征来预测提议区域的类别和边界框。
- Fast R-CNN对R-CNN的一个主要改进:只对整个图像做卷积神经网络的前向传播。它还引入了兴趣区域汇聚层,从而为具有不同形状的兴趣区域抽取相同形状的特征。
- Faster R-CNN将Fast R-CNN中使用的选择性搜索替换为参与训练的区域提议网络,这样后者可以在减少提议区域数量的情况下仍保证目标检测的精度。
- Mask R-CNN在Faster R-CNN的基础上引入了一个全卷积网络,从而借助目标的像素级位置进一步提升目标检测的精度。
在 13.3节— 13.8节中讨论的目标检测问题中,我们一直使用方形边界框来标注和预测图像中的目标。 本节将探讨语义分割(semantic segmentation)问题,它重点关注于如何将图像分割成属于不同语义类别的区域。 与目标检测不同,语义分割可以识别并理解图像中每一个像素的内容:其语义区域的标注和预测是像素级的。 图13.9.1展示了语义分割中图像有关狗、猫和背景的标签。 与目标检测相比,语义分割标注的像素级的边框显然更加精细。
- 语义分割通过将图像划分为属于不同语义类别的区域,来识别并理解图像中像素级别的内容。
- 语义分割的一个重要的数据集叫做Pascal VOC2012。
- 由于语义分割的输入图像和标签在像素上一一对应,输入图像会被随机裁剪为固定尺寸而不是缩放。
到目前为止,我们所见到的卷积神经网络层,例如卷积层( 6.2节)和汇聚层( 6.5节),通常会减少下采样输入图像的空间维度(高和宽)。 然而如果输入和输出图像的空间维度相同,在以像素级分类的语义分割中将会很方便。 例如,输出像素所处的通道维可以保有输入像素在同一位置上的分类结果。
为了实现这一点,尤其是在空间维度被卷积神经网络层缩小后,我们可以使用另一种类型的卷积神经网络层,它可以增加上采样中间层特征图的空间维度。 本节将介绍 转置卷积(transposed convolution) (Dumoulin and Visin, 2016), 用于逆转下采样导致的空间尺寸减小。
- 与通过卷积核减少输入元素的常规卷积相反,转置卷积通过卷积核广播输入元素,从而产生形状大于输入的输出。
- 如果我们将X输入卷积层f来获得输出Y=f(X)并创造一个与f有相同的超参数、但输出通道数是X中通道数的转置卷积层g,那么g(Y)的形状将与X相同。
- 我们可以使用矩阵乘法来实现卷积。转置卷积层能够交换卷积层的正向传播函数和反向传播函数。
如 13.9节中所介绍的那样,语义分割是对图像中的每个像素分类。 全卷积网络(fully convolutional network,FCN)采用卷积神经网络实现了从图像像素到像素类别的变换 (Long et al., 2015)。 与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同,全卷积网络将中间层特征图的高和宽变换回输入图像的尺寸:这是通过在 13.10节中引入的转置卷积(transposed convolution)实现的。 因此,输出的类别预测与输入图像在像素级别上具有一一对应关系:通道维的输出即该位置对应像素的类别预测。
- 全卷积网络先使用卷积神经网络抽取图像特征,然后通过1×1卷积层将通道数变换为类别个数,最后通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。
- 在全卷积网络中,我们可以将转置卷积层初始化为双线性插值的上采样。
摄影爱好者也许接触过滤波器。它能改变照片的颜色风格,从而使风景照更加锐利或者令人像更加美白。但一个滤波器通常只能改变照片的某个方面。如果要照片达到理想中的风格,可能需要尝试大量不同的组合。这个过程的复杂程度不亚于模型调参。
本节将介绍如何使用卷积神经网络,自动将一个图像中的风格应用在另一图像之上,即风格迁移(style transfer) (Gatys et al., 2016)。 这里我们需要两张输入图像:一张是内容图像,另一张是风格图像。 我们将使用神经网络修改内容图像,使其在风格上接近风格图像。 例如, 图13.12.1中的内容图像为本书作者在西雅图郊区的雷尼尔山国家公园拍摄的风景照,而风格图像则是一幅主题为秋天橡树的油画。 最终输出的合成图像应用了风格图像的油画笔触让整体颜色更加鲜艳,同时保留了内容图像中物体主体的形状。
- 风格迁移常用的损失函数由3部分组成:(1)内容损失使合成图像与内容图像在内容特征上接近;(2)风格损失令合成图像与风格图像在风格特征上接近;(3)全变分损失则有助于减少合成图像中的噪点。
- 我们可以通过预训练的卷积神经网络来抽取图像的特征,并通过最小化损失函数来不断更新合成图像来作为模型参数。
- 我们使用格拉姆矩阵表达风格层输出的风格。
之前几节中,我们一直在使用深度学习框架的高级API直接获取张量格式的图像数据集。 但是在实践中,图像数据集通常以图像文件的形式出现。 本节将从原始图像文件开始,然后逐步组织、读取并将它们转换为张量格式。
我们在 13.1节中对CIFAR-10数据集做了一个实验。CIFAR-10是计算机视觉领域中的一个重要的数据集。 本节将运用我们在前几节中学到的知识来参加CIFAR-10图像分类问题的Kaggle竞赛,比赛的网址是https://www.kaggle.com/c/cifar-10。
- 将包含原始图像文件的数据集组织为所需格式后,我们可以读取它们
- 我们可以在图像分类竞赛中使用卷积神经网络和图像增广。
本节我们将在Kaggle上实战狗品种识别问题。 本次比赛网址是https://www.kaggle.com/c/dog-breed-identification。 图13.14.1显示了鉴定比赛网页上的信息。 需要一个Kaggle账户才能提交结果。
在这场比赛中,我们将识别120类不同品种的狗。 这个数据集实际上是著名的ImageNet的数据集子集。与 13.13节中CIFAR-10数据集中的图像不同, ImageNet数据集中的图像更高更宽,且尺寸不一。
- ImageNet数据集中的图像比CIFAR-10图像尺寸大,我们可能会修改不同数据集上任务的图像增广操作。
- 要对ImageNet数据集的子集进行分类,我们可以利用完整ImageNet数据集上的预训练模型来提取特征并仅训练小型自定义输出网络,这将减少计算时间和节省内存空间。
十四、自然语言处理:预训练
人与人之间需要交流。 出于人类这种基本需要,每天都有大量的书面文本产生。 比如,社交媒体、聊天应用、电子邮件、产品评论、新闻文章、 研究论文和书籍中的丰富文本, 使计算机能够理解它们以提供帮助或基于人类语言做出决策变得至关重要。
自然语言处理是指研究使用自然语言的计算机和人类之间的交互。 在实践中,使用自然语言处理技术来处理和分析文本数据是非常常见的, 例如 8.3节的语言模型 和 9.5节的机器翻译模型。
要理解文本,我们可以从学习它的表示开始。 利用来自大型语料库的现有文本序列, 自监督学习(self-supervised learning) 已被广泛用于预训练文本表示, 例如通过使用周围文本的其它部分来预测文本的隐藏部分。 通过这种方式,模型可以通过有监督地从海量文本数据中学习,而不需要昂贵的标签标注!
本章我们将看到:当将每个单词或子词视为单个词元时, 可以在大型语料库上使用word2vec、GloVe或子词嵌入模型预先训练每个词元的词元。 经过预训练后,每个词元的表示可以是一个向量。 但是,无论上下文是什么,它都保持不变。 例如,“bank”(可以译作银行或者河岸)的向量表示在 “go to the bank to deposit some money”(去银行存点钱) 和“go to the bank to sit down”(去河岸坐下来)中是相同的。 因此,许多较新的预训练模型使相同词元的表示适应于不同的上下文, 其中包括基于Transformer编码器的更深的自监督模型BERT。 在本章中,我们将重点讨论如何预训练文本的这种表示, 如 图14.1中所强调的那样。
自然语言是用来表达人脑思维的复杂系统。 在这个系统中,词是意义的基本单元。顾名思义, 词向量是用于表示单词意义的向量, 并且还可以被认为是单词的特征向量或表示。 将单词映射到实向量的技术称为词嵌入。 近年来,词嵌入逐渐成为自然语言处理的基础知识。
- 词向量是用于表示单词意义的向量,也可以看作词的特征向量。将词映射到实向量的技术称为词嵌入。
- word2vec工具包含跳元模型和连续词袋模型。
- 跳元模型假设一个单词可用于在文本序列中,生成其周围的单词;而连续词袋模型假设基于上下文词来生成中心单词。
回想一下我们在 14.1节中的讨论。跳元模型的主要思想是使用softmax运算来计算基于给定的中心词wc生成上下文字wo的条件概率(如 (14.1.4)),对应的对数损失在 (14.1.7)给出。
由于softmax操作的性质,上下文词可以是词表V中的任意项, (14.1.7)包含与整个词表大小一样多的项的求和。因此, (14.1.8)中跳元模型的梯度计算和 (14.1.15)中的连续词袋模型的梯度计算都包含求和。不幸的是,在一个词典上(通常有几十万或数百万个单词)求和的梯度的计算成本是巨大的!
为了降低上述计算复杂度,本节将介绍两种近似训练方法:负采样和分层softmax。 由于跳元模型和连续词袋模型的相似性,我们将以跳元模型为例来描述这两种近似训练方法。
- 负采样通过考虑相互独立的事件来构造损失函数,这些事件同时涉及正例和负例。训练的计算量与每一步的噪声词数成线性关系。
- 分层softmax使用二叉树中从根节点到叶节点的路径构造损失函数。训练的计算成本取决于词表大小的对数。
现在我们已经了解了word2vec模型的技术细节和大致的训练方法,让我们来看看它们的实现。具体地说,我们将以 14.1节的跳元模型和 14.2节的负采样为例。本节从用于预训练词嵌入模型的数据集开始:数据的原始格式将被转换为可以在训练期间迭代的小批量。
- 高频词在训练中可能不是那么有用。我们可以对他们进行下采样,以便在训练中加快速度。
- 为了提高计算效率,我们以小批量方式加载样本。我们可以定义其他变量来区分填充标记和非填充标记,以及正例和负例。
我们继续实现 14.1节中定义的跳元语法模型。然后,我们将在PTB数据集上使用负采样预训练word2vec。首先,让我们通过调用d2l.load_data_ptb
函数来获得该数据集的数据迭代器和词表,该函数在 14.3节中进行了描述。
- 我们可以使用嵌入层和二元交叉熵损失来训练带负采样的跳元模型。
- 词嵌入的应用包括基于词向量的余弦相似度为给定词找到语义相似的词。
上下文窗口内的词共现可以携带丰富的语义信息。例如,在一个大型语料库中,“固体”比“气体”更有可能与“冰”共现,但“气体”一词与“蒸汽”的共现频率可能比与“冰”的共现频率更高。此外,可以预先计算此类共现的全局语料库统计数据:这可以提高训练效率。为了利用整个语料库中的统计信息进行词嵌入,让我们首先回顾 14.1.3节中的跳元模型,但是使用全局语料库统计(如共现计数)来解释它。
- 诸如词-词共现计数的全局语料库统计可以来解释跳元模型。
- 交叉熵损失可能不是衡量两种概率分布差异的好选择,特别是对于大型语料库。GloVe使用平方损失来拟合预先计算的全局语料库统计数据。
- 对于GloVe中的任意词,中心词向量和上下文词向量在数学上是等价的。
- GloVe可以从词-词共现概率的比率来解释。
在英语中,“helps”“helped”和“helping”等单词都是同一个词“help”的变形形式。“dog”和“dogs”之间的关系与“cat”和“cats”之间的关系相同,“boy”和“boyfriend”之间的关系与“girl”和“girlfriend”之间的关系相同。在法语和西班牙语等其他语言中,许多动词有40多种变形形式,而在芬兰语中,名词最多可能有15种变形。在语言学中,形态学研究单词形成和词汇关系。但是,word2vec和GloVe都没有对词的内部结构进行探讨。
- fastText模型提出了一种子词嵌入方法:基于word2vec中的跳元模型,它将中心词表示为其子词向量之和。
- 字节对编码执行训练数据集的统计分析,以发现词内的公共符号。作为一种贪心方法,字节对编码迭代地合并最频繁的连续符号对。
- 子词嵌入可以提高稀有词和词典外词的表示质量。
在 14.4节中,我们在一个小的数据集上训练了一个word2vec模型,并使用它为一个输入词寻找语义相似的词。实际上,在大型语料库上预先训练的词向量可以应用于下游的自然语言处理任务,这将在后面的 15节中讨论。为了直观地演示大型语料库中预训练词向量的语义,让我们将预训练词向量应用到词的相似性和类比任务中。
- 在实践中,在大型语料库上预先练的词向量可以应用于下游的自然语言处理任务。
- 预训练的词向量可以应用于词的相似性和类比任务。
我们已经介绍了几种用于自然语言理解的词嵌入模型。在预训练之后,输出可以被认为是一个矩阵,其中每一行都是一个表示预定义词表中词的向量。事实上,这些词嵌入模型都是与上下文无关的。让我们先来说明这个性质。
- word2vec和GloVe等词嵌入模型与上下文无关。它们将相同的预训练向量赋给同一个词,而不考虑词的上下文(如果有的话)。它们很难处理好自然语言中的一词多义或复杂语义。
- 对于上下文敏感的词表示,如ELMo和GPT,词的表示依赖于它们的上下文。
- ELMo对上下文进行双向编码,但使用特定于任务的架构(然而,为每个自然语言处理任务设计一个特定的体系架构实际上并不容易);而GPT是任务无关的,但是从左到右编码上下文。
- BERT结合了这两个方面的优点:它对上下文进行双向编码,并且需要对大量自然语言处理任务进行最小的架构更改。
- BERT输入序列的嵌入是词元嵌入、片段嵌入和位置嵌入的和。
- 预训练包括两个任务:掩蔽语言模型和下一句预测。前者能够编码双向上下文来表示单词,而后者则显式地建模文本对之间的逻辑关系。
为了预训练 14.8节中实现的BERT模型,我们需要以理想的格式生成数据集,以便于两个预训练任务:遮蔽语言模型和下一句预测。一方面,最初的BERT模型是在两个庞大的图书语料库和英语维基百科(参见 14.8.5节)的合集上预训练的,但它很难吸引这本书的大多数读者。另一方面,现成的预训练BERT模型可能不适合医学等特定领域的应用。因此,在定制的数据集上对BERT进行预训练变得越来越流行。为了方便BERT预训练的演示,我们使用了较小的语料库WikiText-2 (Merity et al., 2016)。
与 14.3节中用于预训练word2vec的PTB数据集相比,WikiText-2(1)保留了原来的标点符号,适合于下一句预测;(2)保留了原来的大小写和数字;(3)大了一倍以上。
- 与PTB数据集相比,WikiText-2数据集保留了原来的标点符号、大小写和数字,并且比PTB数据集大了两倍多。
- 我们可以任意访问从WikiText-2语料库中的一对句子生成的预训练(遮蔽语言模型和下一句预测)样本。
利用 14.8节中实现的BERT模型和 14.9节中从WikiText-2数据集生成的预训练样本,我们将在本节中在WikiText-2数据集上对BERT进行预训练。
- 原始的BERT有两个版本,其中基本模型有1.1亿个参数,大模型有3.4亿个参数。
- 在预训练BERT之后,我们可以用它来表示单个文本、文本对或其中的任何词元。
- 在实验中,同一个词元在不同的上下文中具有不同的BERT表示。这支持BERT表示是上下文敏感的。
十五、自然语言处理:应用
前面我们学习了如何在文本序列中表示词元, 并在 14节中训练了词元的表示。 这样的预训练文本表示可以通过不同模型架构,放入不同的下游自然语言处理任务。
前一章我们提及到一些自然语言处理应用,这些应用没有预训练,只是为了解释深度学习架构。 例如,在 8节中, 我们依赖循环神经网络设计语言模型来生成类似中篇小说的文本。 在 9节和 10节中, 我们还设计了基于循环神经网络和注意力机制的机器翻译模型。
然而,本书并不打算全面涵盖所有此类应用。 相反,我们的重点是如何应用深度语言表征学习来解决自然语言处理问题。 在给定预训练的文本表示的情况下, 本章将探讨两种流行且具有代表性的下游自然语言处理任务: 情感分析和自然语言推断,它们分别分析单个文本和文本对之间的关系。
图15.1 预训练文本表示可以通过不同模型架构,放入不同的下游自然语言处理应用(本章重点介绍如何为不同的下游应用设计模型)
如 图15.1所述, 本章将重点描述然后使用不同类型的深度学习架构 (如多层感知机、卷积神经网络、循环神经网络和注意力) 设计自然语言处理模型。 尽管在 图15.1中, 可以将任何预训练的文本表示与任何应用的架构相结合, 但我们选择了一些具有代表性的组合。 具体来说,我们将探索基于循环神经网络和卷积神经网络的流行架构进行情感分析。 对于自然语言推断,我们选择注意力和多层感知机来演示如何分析文本对。 最后,我们介绍了如何为广泛的自然语言处理应用, 如在序列级(单文本分类和文本对分类)和词元级(文本标注和问答)上 对预训练BERT模型进行微调。 作为一个具体的经验案例,我们将针对自然语言推断对BERT进行微调。
正如我们在 14.8节中介绍的那样, 对于广泛的自然语言处理应用,BERT只需要最少的架构更改。 然而,这一好处是以微调下游应用的大量BERT参数为代价的。 当空间或时间有限时,基于多层感知机、卷积神经网络、循环神经网络 和注意力的精心构建的模型更具可行性。 下面,我们从情感分析应用开始,分别解读基于循环神经网络和卷积神经网络的模型设计。
随着在线社交媒体和评论平台的快速发展,大量评论的数据被记录下来。这些数据具有支持决策过程的巨大潜力。情感分析(sentiment analysis)研究人们在文本中 (如产品评论、博客评论和论坛讨论等)“隐藏”的情绪。 它在广泛应用于政治(如公众对政策的情绪分析)、 金融(如市场情绪分析)和营销(如产品研究和品牌管理)等领域。
由于情感可以被分类为离散的极性或尺度(例如,积极的和消极的),我们可以将情感分析看作一项文本分类任务,它将可变长度的文本序列转换为固定长度的文本类别。在本章中,我们将使用斯坦福大学的大型电影评论数据集(large movie review dataset)进行情感分析。它由一个训练集和一个测试集组成,其中包含从IMDb下载的25000个电影评论。在这两个数据集中,“积极”和“消极”标签的数量相同,表示不同的情感极性。
- 情感分析研究人们在文本中的情感,这被认为是一个文本分类问题,它将可变长度的文本序列进行转换转换为固定长度的文本类别。
- 经过预处理后,我们可以使用词表将IMDb评论数据集加载到数据迭代器中。
与词相似度和类比任务一样,我们也可以将预先训练的词向量应用于情感分析。由于15.1节中的IMDb评论数据集不是很大,使用在大规模语料库上预训练的文本表示可以减少模型的过拟合。作为 图15.2.1中所示的具体示例,我们将使用预训练的GloVe模型来表示每个词元,并将这些词元表示送入多层双向循环神经网络以获得文本序列表示,该文本序列表示将被转换为情感分析输出 (Maas et al., 2011)。对于相同的下游应用,我们稍后将考虑不同的架构选择。
图15.2.1 将GloVe送入基于循环神经网络的架构,用于情感分析
- 预训练的词向量可以表示文本序列中的各个词元。
- 双向循环神经网络可以表示文本序列。例如通过连结初始和最终时间步的隐状态,可以使用全连接的层将该单个文本表示转换为类别。
在 6节中,我们探讨了使用二维卷积神经网络处理二维图像数据的机制,并将其应用于局部特征,如相邻像素。虽然卷积神经网络最初是为计算机视觉设计的,但它也被广泛用于自然语言处理。简单地说,只要将任何文本序列想象成一维图像即可。通过这种方式,一维卷积神经网络可以处理文本中的局部特征,例如n元语法。
本节将使用textCNN模型来演示如何设计一个表示单个文本 (Kim, 2014)的卷积神经网络架构。与 图15.2.1中使用带有GloVe预训练的循环神经网络架构进行情感分析相比, 图15.3.1中唯一的区别在于架构的选择。
图15.3.1 将GloVe放入卷积神经网络架构进行情感分析
- 一维卷积神经网络可以处理文本中的局部特征,例如n元语法。
- 多输入通道的一维互相关等价于单输入通道的二维互相关。
- 最大时间汇聚层允许在不同通道上使用不同数量的时间步长。
- textCNN模型使用一维卷积层和最大时间汇聚层将单个词元表示转换为下游应用输出。
在 15.1节中,我们讨论了情感分析问题。这个任务的目的是将单个文本序列分类到预定义的类别中,例如一组情感极性中。然而,当需要决定一个句子是否可以从另一个句子推断出来,或者需要通过识别语义等价的句子来消除句子间冗余时,知道如何对一个文本序列进行分类是不够的。相反,我们需要能够对成对的文本序列进行推断。
- 自然语言推断研究“假设”是否可以从“前提”推断出来,其中两者都是文本序列。
- 在自然语言推断中,前提和假设之间的关系包括蕴涵关系、矛盾关系和中性关系。
- 斯坦福自然语言推断(SNLI)语料库是一个比较流行的自然语言推断基准数据集。
我们在 15.4节中介绍了自然语言推断任务和SNLI数据集。鉴于许多模型都是基于复杂而深度的架构,Parikh等人提出用注意力机制解决自然语言推断问题,并称之为“可分解注意力模型” (Parikh et al., 2016)。这使得模型没有循环层或卷积层,在SNLI数据集上以更少的参数实现了当时的最佳结果。本节将描述并实现这种基于注意力的自然语言推断方法(使用MLP),如 图15.5.1中所述。
图15.5.1 将预训练GloVe送入基于注意力和MLP的自然语言推断架构
- 可分解注意模型包括三个步骤来预测前提和假设之间的逻辑关系:注意、比较和聚合。
- 通过注意力机制,我们可以将一个文本序列中的词元与另一个文本序列中的每个词元对齐,反之亦然。这种对齐是使用加权平均的软对齐,其中理想情况下较大的权重与要对齐的词元相关联。
- 在计算注意力权重时,分解技巧会带来比二次复杂度更理想的线性复杂度。
- 我们可以使用预训练好的词向量作为下游自然语言处理任务(如自然语言推断)的输入表示。
在本章的前几节中,我们为自然语言处理应用设计了不同的模型,例如基于循环神经网络、卷积神经网络、注意力和多层感知机。这些模型在有空间或时间限制的情况下是有帮助的,但是,为每个自然语言处理任务精心设计一个特定的模型实际上是不可行的。在 14.8节中,我们介绍了一个名为BERT的预训练模型,该模型可以对广泛的自然语言处理任务进行最少的架构更改。一方面,在提出时,BERT改进了各种自然语言处理任务的技术水平。另一方面,正如在 14.10节中指出的那样,原始BERT模型的两个版本分别带有1.1亿和3.4亿个参数。因此,当有足够的计算资源时,我们可以考虑为下游自然语言处理应用微调BERT。
下面,我们将自然语言处理应用的子集概括为序列级和词元级。在序列层次上,介绍了在单文本分类任务和文本对分类(或回归)任务中,如何将文本输入的BERT表示转换为输出标签。在词元级别,我们将简要介绍新的应用,如文本标注和问答,并说明BERT如何表示它们的输入并转换为输出标签。在微调期间,不同应用之间的BERT所需的“最小架构更改”是额外的全连接层。在下游应用的监督学习期间,额外层的参数是从零开始学习的,而预训练BERT模型中的所有参数都是微调的。
- 对于序列级和词元级自然语言处理应用,BERT只需要最小的架构改变(额外的全连接层),如单个文本分类(例如,情感分析和测试语言可接受性)、文本对分类或回归(例如,自然语言推断和语义文本相似性)、文本标记(例如,词性标记)和问答。
- 在下游应用的监督学习期间,额外层的参数是从零开始学习的,而预训练BERT模型中的所有参数都是微调的。
在本章的前面几节中,我们已经为SNLI数据集( 15.4节)上的自然语言推断任务设计了一个基于注意力的结构( 15.5节)。现在,我们通过微调BERT来重新审视这项任务。正如在 15.6节中讨论的那样,自然语言推断是一个序列级别的文本对分类问题,而微调BERT只需要一个额外的基于多层感知机的架构,如 图15.7.1中所示。
图15.7.1 将预训练BERT提供给基于多层感知机的自然语言推断架构
本节将下载一个预训练好的小版本的BERT,然后对其进行微调,以便在SNLI数据集上进行自然语言推断。
- 我们可以针对下游应用对预训练的BERT模型进行微调,例如在SNLI数据集上进行自然语言推断。
- 在微调过程中,BERT模型成为下游应用模型的一部分。仅与训练前损失相关的参数在微调期间不会更新。
十六、附录:深度学习工具