第 1 章 自然语言处理介绍
1.1 什么是自然语言处理
自然语言通常所指的不仅是文本数据,还包括语音和声音数据。
1.1.1 热门应用
自然语言生成的一个流行的商业应用是“数据到文本”的软件系统,它生成数据库和数据集的文本摘要。
自然语言处理的一个主要挑战是从非结构化或半结构化的文档集合中创建结构化的数据。例如,命名实体识别软件能够从主流新闻等长篇文本中提取任务、组织、地点、日期和货币。信息提取还包括关系提取,如果实体之间存在关系,则提取相应的关系。
1.2 基本的自然语言处理
1.2.1 定义自然语言处理任务
分词、词性标注、依存句法分析、组块、词形还原和词干提取是处理下游 NLP(自然语言处理)应用中的自然语言任务的基础。
一旦机器执行了命名实体识别和实体链接,信息检索就变得轻而易举了。
第 2 章 Transformer 和迁移学习
迁移学习
… 是一种机器学习算法,主要内容是使用在其他数据集上训练好的模型在新数据集上进行微调。
语言模型是一种函数,它接收一个单词序列并返回下一个所有可能的单词的概率分布。
2.2 利用 Hugging Face 系列库进行推理
2.2.1 载入模型
model.eval()
当调用这行代码时,model 对象内部会发生一些变化,从而允许模型生成预测。… 主要是禁用了 DropOut 和 BatchNorm 层,它们仅在模型训练期间用到。
2.2.2 生成预测
严格地说,我们不需要关闭梯度计算,但这节省了时间、内存和计算,并使推理运行得更快。
Python 的魔术方法是类中一类特殊的方法,函数名通常以“__”开头和结尾,因此也叫双下方法。
如果在 Python 类中定义了一个名为 call() 的函数,就可以将类的实例视为函数,并且 call() 函数将在每次这样做时被调用。
第 3 章 NLP 任务和应用程序
3.1 预训练语言模型
机器可以从标注数据或未标注数据中学习,机器学习中如果采用了标注数据(例如,一张带有“猫”或“狗”标签的图片),则称为监督学习,如果采用了未标注数据(例如,你有猫和狗的图片但是没有任何标签),则成为无监督学习。机器学习的第三个主要领域被称为强化学习,它使用智能体(agent)来学习如何在一个物理或数字环境中采取行动(action),以最大化可以得到的奖励(reward)。
一般来说,一旦机器学习或训练达到了满意的效果,模型会把训练过程中获得的知识以参数(例如权重)的形式存储。而参数,一般又是指在机器学习过程中做各种微积分或线性代数等运算所需的参数。
这种通过微调语言模型来应对另一个 NLP 任务的过程被称为迁移学习
3.3 NLP 任务
… 这些任务包括序列分类、问答系统、语言建模、文本生成、命名实体识别、文本摘要和翻译。这个列表 … 基本列出了目前最常见的 NLP 任务 …
语言建模
语言建模是给定一个单词序列预测下一个单词序列的任务。这种类型的语言建模被称为因果(causal)语言建模,在自然语言处理领域被广泛用于自然语言生成(NLG)。另一种类型的语言建模是掩码(masked)语言建模,机器需要在给定的上下文中预测一个或多个被遮盖的单词。
文本生成
文本生成类似于语言建模,因为任务也是给定文本,生成一个后续的文本序列,但与语言建模相比,它更开放,可以生成较长的序列文本预测。
3.5 NLP 任务 1:命名实体识别
可以将 NER(命名实体识别)看做给每个文档添加丰富元数据的过程,从而帮助我们在下游应用上做更加丰富的分析。
3.5.5 自定义 NER 模型与原始 NER 模型
将微调后基于 Transformer 的模型与原始的
en_core_web_trf
模型直接对比并不公平,因为微调后模型只支持四种实体类别(ORG、PERSON、GPE 和 TICKER),而原始的en_core_web_trf
支持更多的实体类别(但不支持 TICKER,这是我们刚为 AG 新闻数据集标注的新实体类别)。
3.7 总结
如果可能的话,最好从一个大模型的、预先训练好的语言模型开始,然后根据语料库和目标任务对其进行微调。
通过利用与训练模型,你只需要更少的标注和更少的训练时间,但能产出更棒的效果。
第 4 章 分词
4.2 Hugging Face 的分词器
tokenizers
是 Hugging Face 官方用 Rust 编程语言编写的分词工具
分词与深度学习流水线的其他部分不同,它通常是在 CPU 上完成的,但这并不意味着它必然会慢!Hugging Face 的库函数充分利用了计算机 CPU 中的多核,可以在一分钟内对十亿字节(对于非学术界的 NLP 任务来说,这已经相当大了)规模的大型数据集分词。
子词分词
所以,我们的目标有两个:
- 增加每个词条的信息量
- 减少词条的总数(词表大小)
子词分词器通过在字符、子词和单词之间找到平衡来有效地实现这一点。
子词分词算法(至少是较新的那一版)并不是一成不变的。在对文本分词之前有一个“训练”阶段,这不是指训练语言模型,而是在字符级和单词级分词之间寻找最佳平衡的过程。
子词分词算法的训练目标是识别出语料库中重复出现的文本,并将其“重构”为词条。如果一个特定的模式不经常重复,那么它就不会被视为词条。
4.3 搭建自己的分词器
虽然已有的子词分词器是挺好用的,但有时你可能确实需要自定义分词器,以便发现特定领域文本中的细微之处。典型的例子是法律和医学文本。这些领域通常有一些常用的专业术语,而且这些术语非常重要,值得拥有自己的词条(比如分子名称或法律文件的特定章节)。
4.4 总结
分词器不是大多数人应该去优化的步骤。
如果你有一个自定义的数据集,且具有其领域相关的词表(如法律或医疗),那么使用已有的分词器算法(如 WordPiece 或 SentencePiece)来重新训练分词器是有意义的。
第 5 章 向量嵌入:计算机如何“理解”单词
如果说分词器是模型用来读取文本的,那么向量嵌入就是模型用来理解文本的。
5.2 词向量
one-hot
向量是一个数组,其中一个元素为 1,其他所有元素为 0 …
… 使用高维
one-hot
向量是将单词映射到向量的最简单的方法之一。
字典中的第 n 个单词将有一个
one-hot
的表示,其中第 n 个元素为 1,其余为 0。
向量的维度是字典的大小。例如,一个有 20 000 个单词的字典意味着我们将拥有 20 000 维的 one-hot 词向量。这是因为字典中的每个单词在数组中占据一个位置,而数组只能有一个非零元素。
与 one-hot 向量不同,词嵌入向量的分量可以是任意浮点数
5.2.2 迁移学习时代的词向量嵌入
在微调模型的过程中,词向量嵌入层会得到更新。你会得到两全其美的效果 —— 一个建立在大型文本语料库的基础上的词向量嵌入矩阵,同时也捕捉到你的特定数据集的微妙之处。
5.3 词向量嵌入实践
5.3.1 预处理
理解
d
代表维度就是一个例子。这里,维度前面的数字是指词嵌入训练的字典的大小(例如,42B
表示420 亿
个不同的单词)
第 6 章 循环神经网络和其他序列模型
但 Transformer 并不是 NLP 领域中唯一的模型。
Transformer 本身是相对近期的成果 —— Vaswani 等人的原始论文于 2017 年 6 月首次在 arXiv 上发表 —— 注1:A. Waswani et al., “Attention Is All You Need”, arXiv, June 12, 2017, https://oreil.ly/f7uk1
循环神经网络(RNN)。RNN 是过去的主流模型。
comma.ai 产品中的自动驾驶系统就是一个很好的例子,它使用了 GRU(RNN 的另一种变体)。
由于向 Transformer 的快速过渡与迁移学习的革命几乎同时发生,一些研究人员也质疑 RNN 在正确使用的情况下或许和 Transformer 同样有效
Transformer 最大的卖点之一就是模型规模,这在很大程度上要归功于 OpenAI 在 GPT 上所做的工作。Transformer 可以更好地并行化,随着数据集大小的增加,起准确率稳步提高,并为迁移学习提供了一个可靠的平台。
注意力机制目前仍然是一个非常耗费内存的操作。在现实世界中,拥有完美的大规模数据集也不太常见。
RNN 呈现了一个有趣的中间地带,或许值得考虑。它们可能(但并非总是)更容易训练,整体规模更小,消耗的内存更少。如果你准备部署到一个低资源边缘终端(low-resource edge device)上,这是非常有吸引力的。
事实证明 RNN 对马尔可夫序列(一种特殊的序列)特别有效。
马尔可夫性意味着序列中的下一项仅取决于当前项是什么
在自然语言中,句子、段落等之间可能存在复杂的长期依赖关系。Transformer 中的注意力机制擅长捕捉长期依赖,但在马尔可夫序列中,这不是很有用。
6.1 循环神经网络
RNN 和其他所有神经网络一样:总体而言都是把很多矩阵乘法和非线性激活函数放在一起去完成一些有趣的事情。
RNN 在很多方面与 Transformer 非常相似:
- 它们都对词向量序列进行一些操作。
- 两者都可以应用于大多数 NLP 任务。
- 两者都可以利用注意力机制。
- 两者都可以使用类似的迁移学习技术。
但是 RNN 和 Transformer 之间有一些重要的区别:- Transformer 采用固定长度的序列,但 RNN 可以处理任意长度的序列。
- Transformer 并行处理多个单词,而 RNN 一次只处理一个单词。
实际上训练和使用 RNN 的过程与使用 Transformer 的过程并没有太大的不同。它们再架构上有一些根本的不同,但是你可以复用大部分甚至所有的数据流、训练循环(training loop)和其他与模型架构没有直接关联的周边基础设施。
RNN 本质上是一个带有 for 循环的神经网络。在最简单的形势下,你可以这样实现它:
for word in words: state = f(word, state)
在 RNN 文献中,你经常会听到“cell”这个词。cell 表示再每个时间点或字上执行计算的单元。
RNN 架构的不同变体(LSTM、GRU等)实现状态的方式不同。但就其核心而言,这就是所有 RNN 架构所做的 —— 每次读取一个单词的序列,并在此过程中更新一些状态。
RNN 的一个常见可视化描述是一个带有箭头的块,该箭头循环返回自身(如图 6-1 所示)。这精确地表达出了 RNN 循环遍历序列并将之前的状态作为输入传递回单元以获得下一个状态的和核心思想。
这里的环路可以展开,得到更像我们一直在 Transformer 结构图中看到的东西。展开的 RNN 如图 6-2 所示,其更清楚地描述了如何将一个句子传递到 RNN 中。
对于普通 RNN,单元是通过两个简单的矩阵乘法实现的:一个用于变换词向量,另一个用于变换隐藏状态。
6.1.1 在 PyTorch 中从零开始实现 RNN
transformers(这里指由 Hugging Face 开发的代码库,不是 Transformer 家族)主要专注于实现最先进的 Transformer 模型,并提供与模型交互的 API。它不是一个真正的通用 NLP 或深度学习框架。因此,transformers 并没有很好的方法来训练非 Transformer 模型
6.1.2 双向 RNN
双向 RNN 的思想类似于 BERT 中使用的双向 Transformer,但在这种情况下,方向性会更明显。双向 RNN 不只是从左到右地遍历句子,而是有两条同时计算的隐藏状态“路径”。实际上,你最终将分别得到一个从左到右和一个从右到左的隐藏状态序列。
双向 RNN 简而言之是增加了更多的计算量,但是在不做太多调整的情况下总体上提高了性能。只要你的 RNN 模型在计算上可行,就应该尽可能使用其双向变体。
双向 RNN 并不能完全替代 RNN,其中有一个重要的因素要考虑 —— 实际上,在当今时代(相对于 Transformer 而言),你希望使用 RNN 解决的许多问题并不具有双向结构。例如,在文本生成中,你的模型不能提前“看到”序列的结尾,因为序列的结尾就是我们所生成的!其实它最有用的地方应该是在具有较大输入的更简单的 NLP 任务中,比如文档分类或文本摘要,在这些任务中,Transformer 可能会表现得更好,但在计算上是不可行的。
对于 Transformer,一开始就没有方向性的概念,因为它们会并行地阅读你扔给它们的所有单词。但是使用循环模型,使它们双向是一种简单而有效地提高性能的方法,当然同时也会增加一些计算成本。
6.2 长短期记忆网络
几乎所有 RNN 都只是一个单元块,它再循环中更新一个隐藏的状态。
LSTM 架构使用更复杂的单元块。
LSTM 块不是将隐藏状态表示为单个向量,而是使用两个向量和一些机制来管理何时更新这些状态。
6.3 门控循环单元
门控循环单元(Gated Recurrent Unit,GRU)试图解决的问题与 LSTM 相同(学习长期依赖关系),使用一种类似但在计算和概念上都更简单的方法。
GRU 的好处是它们在每个单元中做的计算更少,这意味着其效率更高。这在端设备部署中特别有用,因为在端设备部署中,CPU 资源非常稀缺,LSTM 非常耗费 CPU。自动驾驶汽车初创公司 Comma AI 的车内计算模块使用了基于 GRU 的网络。
第 7 章 Transformer
7.2 注意力机制
注意力机制指的是深度神经网络中的一个层。它的主要功能(仍有待探索)是学习大范围、全局的特征。注意力机制的作用就像我们所说的“信息路由器”,它决定了输入序列的嵌入向量中哪些部分对输出的向量有贡献。
在使用之前,我们应该提到一个重要的话题:我们需要“注意”注意力的计算复杂度是多少,思考有多少个点积(dot product)或矩阵乘法以及包含的张量的大小。
7.2.1 点积注意力
严格地说,我们尚未看到这种点积类型的注意力在实际网络中的应用。首先会介绍的缩放点积注意力(scaled dot product attention)只是为后面的多头注意力(Multi-Head Self-Attention)作铺垫。
在这个奇特的注意力机制的世界里,首先面对的问题是:我们究竟是如何衡量事物之间的相似性的?这个核心思想经常被线性代数和大量 GPU 所掩盖,其实它才是驱动今天 NLP 神经网络发展的基础原理。
点积是一种向量操作,把两个向量的元素对应相乘,然后把结果相加即可。它能够度量相似性,因为如果要做“点积”的两个目标向量所对应的各元素(也可以理解为各分量)都很相似,那么点积就会很大,反之亦然(各元素不相似的向量之间点积很小)。
典型的 Transformer 将词向量序列作为输入,并在每一层将它们转换(不,我不认为这是它名称的由来)为另一个向量序列,我们称之为隐表达或者隐状态。
所以在网络的每一个隐藏层都有我们想要“注意”的向量序列
这里要做的是将每一个隐状态向量转换为三个完全独立的向量 —— 查询(query)、键(key)和值(value)。
我们通过简单的矩阵乘法来实现这个转换,这些向量的维数由人工设定。唯一的限制是 query 向量和 key 向量需要有相同的维数(因为我们要取它们之间的点积)
然后,我们取序列里每个时间步的 query 向量与所有 key 向量的点积来计算注意力权重,并对结果进行 softmax。要同时在所有时间点中执行此操作的话,将这些向量打包到一个矩阵中并行执行乘法运算会更有效
不止如此,由于每个 query 向量都是独立的,我们还可以实现跨时间点的并行化(这是 RNN 不可能实现的,因为计算依赖于 zi-1)
我们在 Transformer 的编码器部分计算所谓的“自注意力”(self-attention)时,用到了下面的隐向量:
$$
k_i,q_i,v_i
$$这些都来自编码器那一层的序列。解码器中的自注意力也是如此。不过还有一点不同,即解码器中的注意力层使用的是解码器的隐 query 向量,但 key 和 value 用的是编码器的。这使得解码器可以“注意”到前面的编码器的隐向量,这在一些任务中是有用的,如机器翻译。
7.2.3 多头自注意力
只让一组 query、key 和 value 完成所有的工作可能有点过分,… 多头注意力机制试图解决这个问题,方法是在同一序列中单独多次应用注意力机制。
最重要的是,query、key 和 value 矩阵必须是不同的,否则多次重做注意力相关的事情将只是浪费计算
这些“头”中的每一个都独立地(关键的是,并行地)执行缩放的点积注意力运算
Transformer 会将多个注意力头的输出连接起来,并通过另一个学习出的线性变换使其维数匹配,从而能正确传递
7.2.4 自适应跨度的注意力
注意力跨度(attention span),是指模型正在处理的先前词条的数量。所以如果一个头的注意力跨度为 5,这意味着该头仅在当前位置前的最后 5 个词条上运行注意力。
自适应的注意力跨度是如此库,因为没个头都可以通过训练过程学习自己的注意力跨度!
这个想法真的很苦,因为它把注意力的词条数量从一个超参数编程了一个简单的参数,可以通过反向传播来自动训练
7.3 计算机视觉 Transformer
计算机视觉中最常见的结构是卷积神经网络(CNN),类比于注意力层,它们使用卷积层来转换图像。
7.4 总结
注意力机制有多种形式,可以在许多领域和架构中使用,而不仅是 Transformer
Transformer 架构中使用的标准注意力机制被称为多头自注意力(MHSA)。它将输入转换成一个小的 key 空间,并运行多次点积注意力
注意力机制非常强大,但在计算上也很昂贵。标准的 MHSA 有 n2 的存储成本,这意味着如果你的句子中有 10 个单词,你需要存储 10*10=100 个注意力权重
x 和 y 之间的注意力权重可以解释为,在抽象意义上“x 和 y 有多大关联?”(在代词指代消解中有用)
注意力权重还是一个有用的可视化工具
第 8 章 BERT 方法论:博采众长创新篇
8.1 ImageNet
“ImageNet 时刻”是计算机视觉在效果上取得突破性进展,并具有较低的使用门槛,从而成功吸引到世界关注的时刻。
8.2 通往 NLP “ImageNet时刻”之路
2018 年,主流观点发生了巨大变化,因为 NLP 研究人员发现,预训练的语言模型可以被用于许多不同的 NLP 任务中并取得最优效果,我们并不需要总是从头训练语言模型来解决特定的 NLP 问题。这是 NLP 的一个转折点,因为从那时开始,机器学习应用工程师可以利用预训练的语言模型来解决大量的 NLP 任务,就像计算机视觉工程师利用预训练的 ImageNet 模型来解决大量的计算机视觉任务一样。通过复用几个预训练语言模型中的一些层,NLP 的科学家和工程师只需较少的标注数据就能解决特定的 NLP 问题。以前难以解决的 NLP 问题现在已经有越来越成熟的解决方案
8.3 预训练的词向量嵌入
8.3.1 one-hot 编码的局限性
如果我们对一个大型语料库中的每个单词都用一个 one-hot 编码来表示,那么编码矩阵的维度将等于不同单词的个数,这将是非常庞大且不实用的
由 Word2Vec、GloVe 和 fastText 训练得到的词向量嵌入可以捕获更多语言中的结构信息,这就是这些词向量嵌入算法从 2013 年开始实质性地推动 NLP 领域发展的原因
8.3.2 Word2Vec
Word2Vec 基于每个单词的上下文来学习每个单词的向量表示,换句话说,Word2Vec 用目标词周围的词来决定目标词的向量表示
Word2Vec 的神奇之处在于,因为语义上相似的单词会出现在相似的上下文中,因此通过 Word2Vec 学习到的向量(如数字表示)也是相似的。也就是说,在高维空间中,具有相似含义的词(如“皇后”和“国王”)也具有相似的表示(即向量),因此在向量空间中更近
尽管 Word2Vec 取得了较大的成功,但它也有些缺点。首先,虽然它依赖一种基于窗口的模型来学习特定单词的词向量嵌入,但它能使用的窗口相对较小。它没有将整个文件都视为目标单词的上下文来学习目标单词的向量表示。其次,它没有考虑子词信息,这意味着它不能有效地学习诸如“一个名词和一个形容词是如何从同一子词派生的”这种相关信息。… 再者,Word2Vec 不能处理未收录词(Out oOf Vocabulary,OOV),它只能向量化在训练过程中见过的单词。最后。Word2Vec 不能根据特定的上下文来消除单词的歧义
一般来说,预训练的词向量嵌入维度是几百维(通常是 300)。维度越大,词向量嵌入算法得到的嵌入表示中的信息就越细致,但这是以牺牲计算速度和增加计算复杂度为代价的。如果你想要一个效果更好的模型,那么对于你所能选择的任何给定的词向量嵌入算法,最好使用维度更大的词向量嵌入矩阵。如果你想要一个更快、更高效的计算模型,那么在所有其他设置都相同的情况下,最好使用一个维度较小的矩阵
8.3.3 GloVe
GloVe 解决了上文中提到的 Word2Vec 的第一个主要缺点,即 Word2Vec 窗口大小有限的问题。
虽然 GloVe 解决了 Word2Vec 的第一个缺点,但它仍然没有考虑子词信息。
8.3.4 fastText
它使用 n 元文法(n-gram)作为最小单位,而不是将每个单词看作最小单位。
fastText 只需更少的训练数据,因为 … 它能够从一组相同的语料中,通过 n 元文法生成的不同的字符串来学习到更多的内容
对于 fastText,它的主要缺点是不能根据上下文为每个单词生成不同的向量
8.3.5 上下文感知的预训练词向量嵌入
基于 Transformer 架构的大型预训练语言模型(如 ELMo 和 BERT)于 2018 年问世,它们通过引入上下文感知的词向量表示而改变了这点。… 我们应该根据上下文,使用不同的词向量来表示单词的不同含义。
8.4 序列模型
序列模型是一类建模方法,而不仅是某一个单一的方法,它包括循环神经网络(RNN)、长短期记忆(LSTM)和门控单元(GRU)等
总的来说,序列模型可处理一下多种类型的场景:a)输入是序列数据,而输出是单个值,如情感分析的场景;b)输入是单点值,而输出是序列数据,如用于图像描述生成任务;c)输入和输出均为序列数据,如机器翻译。
序列模型通常由编码器和解码器组成
序列建模的应用范围绝不仅限于 NLP。在自然语言处理中,序列建模与文本(例如机器翻译、文本摘要、问答等)、音频(如聊天机器人)和语音(如语音识别)相关。在 NLP 之外,序列建模涉及图像(如图像描述生成)、视频(如视频描述生成)、时间序列数据中的异常检测,以及包括传感器数据、股票市场数据、基因组数据和天气数据的时间序列预测等。
序列数据是一系列顺序上相互依赖、彼此之间具有某种相关性的数据项。
这是序列建模的关键:通过考虑数据中的序列模式,如文本、音频和时间序列数据,序列模型在这类任务上能够产生比传统的前馈神经网络更好的效果。
8.5 循环神经网络
序列模型从序列数据的时序性中学习,每步处理一个时刻对应的数据。
有一种简单直观的方式来理解循环神经网络:它是带有循环结构的网络,允许过去的信息以“记忆”的形式存在,并且该“记忆”可用来处理下一个输入。
模型的记忆力越好,它执行任务的能力就越强,比如把句子从一种语言翻译成另一种语言,或者问答任务。
循环神经网络面临的主要挑战是对时间跨度长的序列数据记忆力不足。
在机器学习中,卷积神经网络因其在计算机视觉任务上的出色表现而闻名,… 在卷积神经网络中,神经网络使用固定长度的窗口来表示数据。…虽然基于卷积神经网络的语言模型非常快,但它们能用到的单词的上下文信息很少;它们能记住的上下文,甚至比拥有短期记忆的循环神经网络更少。这就限制了卷积神经网络的效果,这也是一旦循环神经网络可用,研究人员就倾向于使用循环神经网络的原因。
8.5.1 vanilla 循环神经网络
循环神经网络考虑了时间因素,而传统的前馈神经网络则不考虑。传统的前馈神经网络沿着网络结构的一个固定方向传递信息(因此成为“前馈”),但是循环神经网络在将信息向前传递后,还会像环一样将信息循环传递回来。
在序列模型中,编码器和解码器都可以是循环神经网络。在每个时间步,编码器和解码器对应的循环神经网络都接受两个输入。例如,在机器翻译中,两个输入是:a)一个单词;b)一个隐藏状态。这个隐藏状态向量是循环神经网络从之前的时间步中保留下来的记忆。
然而,循环神经网络也有一个显著缺点:它不能很好地处理很长的序列。 —— 这是由于反向传播中“臭名昭著”的梯度消失问题
8.5.2 长短期记忆网络
长短期机器(LSTM)网络也使用了循环神经网络架构,但它环节了 vanilla 循环神经网络无法很好地处理长序列的问题。长短期记忆网络能够在更长的序列上存储记忆信息。它使用一种称为“门”的机制为其神经网络架构的一部分,以便记住序列数据中更长期的上下文信息。
长短期记忆网络在每个时间步根据一下规则更新它的记忆向量:a)它想从当前输入中添加哪些信息;b)它认为哪些信息不再相关,并从记忆中删除;c)它想保留哪些信息。这些门是一种机制,用来学习序列数据中哪些信息需要写入长短期记忆网络的记忆中,而哪些信息又不需要。这些门本身就是神经网络,其从数据中学习如何最好地执行各自的角色。这三种门如下:
- 输入门(input gate):它决定了应该使用当前输入中的哪些信息来修改记忆。
- 遗忘门(forget gate):它决定了哪些信息已经不再相关,应该从记忆中删除。
- 输出门(output gate):它决定了在给定当前输入和模型已确定要以往某些信息时,要保存到记忆中的信息(并将该信息传递给下一个时刻)。
8.5.3 门控循环单元
门控循环单元(Gated Recurrent Unit,GRU)是另一种具有门控机制的循环神经网络。它类似于长短期记忆网络,但结构更简单,只使用了两个门,而非三个。这两种门分别是:
- 更新门(update gate):更新们决定是否应该用当前输入中的信息来更新记忆。
- 重置门(reset gate):重置门决定了记忆中哪些是重要的(应该被保留和传递),哪些是不重要的(并因此被重置)。
尽管这些循环神经网络取得了成功,但直至 2016 年底,它们还不能很好地处理序列数据中的长期依赖关系,这也是 NLP 的下一个突破 —— 注意力机制所要解决的问题。
8.6 注意力机制
显然,试图将整个句子的含义压缩到一个向量(编码器的最终隐藏状态),并只将其传递给解码器以翻译输入的整个句子,并非最佳方案。当解码器开始工作时,最好能通过编码器的中间隐藏状态,使解码器在每一个时间步都能集中注意力到输入句子的相关位置上。
解码器应该在每一个时间步去关注编码器中相关的隐藏状态,而不是只是用一个向量(即编码器的最终隐藏状态)来执行翻译。
使长短期记忆网络能够集中注意力的机制被称为注意力机制。
长短期记忆网络中的注意力机制允许解码器访问编码器中的所有隐藏状态,而不仅是最终的隐藏状态。除此之外,它们允许解码器将注意力集中在特定的隐藏状态上,并在翻译时忽略输入语句中其他的隐藏状态。
在带有注意力机制的长短期记忆网络中,编码器将所有的隐藏状态都传递给解码器,而不带注意力机制的长短期记忆网络则仅将最后一个隐藏状态传递给解码器。
8.7 Transformer 架构
带有注意力机制的长短期记忆网络 … 仍有一些缺点。其中最显著的缺点是:带有注意力机制的长短期记忆网络的训练是计算密集型任务,且难以并行化。
在 2017 年带注意力机制的长短期记忆网络开始流行后不久,研究人员就设计了一个更好的架构,该架构不使用循环网络结构,完全依赖于注意力机制,从而可以更快地训练。这种新的体系结构称为 Transformer 架构(简称 Transformer)。Transformer 使用基于前馈神经网络的编码器 - 解码器结构与注意力机制的结合,来代替使用基于循环网络的编码器 - 解码器结构。
Transformer 是高度内存密集型的任务,但它易于并行化。
这种训练过程中的并行化突破导致了大规模预训练语言模型的出现。
Transformer 执行任务时只依赖注意力机制,不需要任何循环的序列处理模块。
与长短期记忆网络一样,Transformer 依赖于编码器 - 解码器架构。具体来说,编码模块用于处理输入数据,并由多个编码器堆叠构成;而解码模块则是用来处理编码器传递给它们的编码,由多个解码器堆叠而成。编码模块中堆叠的编码器和解码模块中堆叠的解码器数量相同。此外,所有编码器在结构上是相同的,所有解码器在结构上也是相同的。
每个编码器有两个组件(或子层):自注意力机制组件和前馈神经网络组件。自注意力机制组件接收来自前一个编码器的一组输入编码,并衡量编码间的相关性,以生成一组输出编码。输出编码被传递到前馈神经网络组件中,前馈神经网络组件会在将这组编码传递到下一个编码器层和解码器层之前,对这组编码做进一步处理。
解码模块中的每个解码器有三个组件:自注意力机制组件、注意力机制组件和前馈神经网络组件。
解码器中的自注意力机制组件只允许使用输出巨资中当前位置之前的信息,所有未来的信息都会被屏蔽,从而使 Transformer 不会使用当前或未来的信息来生成输出。
经解码模块解码之后,输出向量将被传递给线性变换层和 softmax 层,以得到每个词的输出概率。词表中生成概率最高的单词作为本时间步的最终输出。
在 2017 年最初版本的 Transformer 架构中,一个主要限制是输入 Transformer 的文本字符串需要是固定长度的。… 为了解决这个问题,研究人员在 2019 年提出了 Transformer-XL,它通过引入循环机制(recurrence mechanism)来学习输入 Transformer 注意力机制中的连续多个固定长度的文本片段之间的依赖关系。
8.8 NLP 的 “ImageNet 时刻”
8.8.1 ULMFiT
“可微调的通用语言模型”(Universal Language Model Fine-Tuning,ULMFiT)的论文表明:预训练的模型不仅能在自身训练数据对应的任务上表现良好,同时若在一个新任务对应的数据上微调预训练的语言模型,也有可能在此新任务上取得较好的效果。
ULMFiT 为那些很难收集到大量数据的企业打开了 NLP 的应用之门
8.8.2 ELMo
它首次引入了上下文感知的词向量表示
ELMo 可以根据单词出现的上下文,为同一个单词生成不同的词向量表示
此外,这些词向量表示是基于字符的,就像 fastText 的词向量嵌入一样,这使得基于 ELMo 的模型能够处理再训练过程中未见过的、词库之外的词条。
8.8.3 BERT
BERT(Bidirectional Encoder Representations from Transformers,基于 Transformer 的双向编码器表示)是近年来 NLP 多个进展(Transformer、迁移学习、上下文感知的词向量表示)融合的结果。
8.8.5 GPT-1、GPT-2、GPT-3
… GPT 模型,即 Generative Pretrained Transformer(生成式预训练 Transformer)的缩写
第 9 章 工欲善其事,必先利其器
9.1 深度学习框架
9.1.1 PyTorch
torch.tensor
对象是 PyTorch 的核心。它是一种多维数组,几乎与numpy.ndarray
相同。它可以存储于 GPU 内存中,用于快速并行计算。几乎所有的 PyTorch 的功能都是为了通过诸如矩阵乘法、卷积等运算来曹总这些张量而构建的。
PyTorch 的另一个重要组件是 autograd。当你使用 PyTorch 张量操作时,这个功能会自动计算一个叫做梯度(gradient)的量,这对训练神经网络至关重要。
描述 PyTorch 最简单的方式是将其成为“GPU 上的 NumPy”
9.1.3 Jax
Jax 是谷歌最近引入的一个新的数值计算库。它将 PyTorch 推广的 “NumPy on GPUS” 的理念提升到了一个全新的高度。Jax 的核心是直接在标准 NumPy 和 Python 函数之上提供 autograd 功能(计算链式函数的梯度而不明确指定导数的能力,这对深度学习框架非常重要)。这意味着 Jax 的 autograd 可以处理循环、条件、闭包和其他 Python 原生结构,而无需对代码进行任何修改!
9.2 可视化与实验跟踪
9.2.2 Weight & Biases
Weight & Biases 自动跟踪超参数、指标等,并把它们记录到云上。然后,你可以通过实时更新的交互式仪表盘可视化结果。实际上,你可以记录你可能关心的任何事情,包括情节、样本内预测、音频、视频、3D 模型,甚至原始 HTML。该工具还提供标签、过滤、分组和输出大多数格式的能力,以保障你的实验能有条不紊地进行。
9.5 边缘/终端侧推理
9.5.1 ONNX
开放神经网络交换(Open Neural Network Exchange,ONNX)是解决多设备和平台推理的最重要的项目。ONNX 提供了一种新的存储机器学习模型的格式,可以在各种推理引擎、编程语言、设备等上运行,而不是一个范围有限的库、框架等。这种方法将推理分为“前端”和“后端”,而 ONNX 模型则作为后端。
第 12 章 归纳提升
12.1 最后十课
12.1.1 第一课:从简单的方法开始
我们的目标应该是快速可靠地对所属机构产生价值,最后再用更好的方案取代权宜之计。正如伏尔泰所说:“完美是优秀的敌人”。
附录B CUDA
B.1 线程和线程块
CUDA 的基本单元是线程,它代表了运算的执行单位。线程中的每条指令都将按顺序执行。因此为了达到更高的并行读,CUDA 设备通常有很多线程,它们都是独立运行的。
CUDA 中的多个线程就组成了所谓的线程块(thread block),而多个块又组成了网格(grid)。