Skip to content

Commit

Permalink
ch15
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaotinghe committed Aug 29, 2021
1 parent ddb42d4 commit 0f005bb
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 322 deletions.
67 changes: 32 additions & 35 deletions chapter_natural-language-processing-applications/finetuning-bert.md

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions chapter_natural-language-processing-applications/index.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
# 自然语言处理:应用
:label:`chap_nlp_app`

我们已经看到了如何在文本序列中表示令牌并在 :numref:`chap_nlp_pretrain` 中训练它们的表示形式。这种预训练的文本表示法可以被馈送到不同的模型中,用于不同的下游自然语言处理
我们已经了解了如何在文本序列中表示词元,并在 :numref:`chap_nlp_pretrain` 中训练了词元的表示。这样的预训练文本表示可以送入不同的下游自然语言处理任务的不同模型。

事实上,前面的章节已经讨论过一些自然语言处理应用
*没有预培训 *
只是为了解释深度学习架构。例如,在 :numref:`chap_rnn` 中,我们依靠 RNN 来设计语言模型来生成类似小说的文本。在 :numref:`chap_modern_rnn` 和 :numref:`chap_attention` 中,我们还设计了基于 RNN 和机器翻译注意力机制的模型。
前面的章节已经讨论了一些自然语言处理应用,这些应用没有预训练,只是为了解释深度学习结构。例如,在 :numref:`chap_rnn` 中,我们依赖循环神经网络设计语言模型来生成类似中篇小说的文本。在 :numref:`chap_modern_rnn` 和 :numref:`chap_attention` 中,我们还设计了基于循环神经网络和注意力机制的机器翻译模型。

但是,本书不打算全面涵盖所有此类应用程序。相反,我们的重点是 * 如何应用语言的(深度)表示学习来解决自然语言处理问题 *。鉴于预先训练的文本表示法,本章将探讨两个常见的和具有代表性的下游自然语言处理任务:情绪分析和自然语言推断,它们分别分析单个文本和文本对的关系。
然而,本书并不打算全面涵盖所有此类应用。相反,我们的重点是*如何应用(深度)语言表征学习来解决自然语言处理问题*。在给定预训练的文本表示的情况下,本章将探讨两种流行且具有代表性的下游自然语言处理任务:情感分析和自然语言推理,它们分别分析单个文本和文本对之间的关系。

![Pretrained text representations can be fed to various deep learning architectures for different downstream natural language processing applications. This chapter focuses on how to design models for different downstream natural language processing applications.](../img/nlp-map-app.svg)
![预训练文本表示可以送入不同下游自然语言处理任务的不同模型。本章重点介绍如何为不同的下游自然语言处理应用设计模型。](../img/nlp-map-app.svg)
:label:`fig_nlp-map-app`

如 :numref:`fig_nlp-map-app` 所述,本章重点介绍使用不同类型的深度学习架构(例如 MLP、CNN、RNN 和注意力)设计自然语言处理模型的基本思想。尽管可以将任何预训练的文本表示形式与 :numref:`fig_nlp-map-app` 中的任何应用程序的任何架构结合起来,但我们选择了一些代表性组合。具体来说,我们将探索基于 RNN 和 CNN 的流行架构以进行情绪分析。对于自然语言推断,我们选择注意力和 MLP 来演示如何分析文本对。最后,我们介绍如何针对各种自然语言处理应用程序微调预训练的 BERT 模型,例如序列级别(单文本分类和文本对分类)和令牌级别(文本标记和问答)。作为一个具体的实证案例,我们将对 BERT 进行微调,以实现自然语言推断。
如 :numref:`fig_nlp-map-app` 所述,本章重点描述了使用不同类型的深度学习结构(如多层感知机、卷积神经网络、循环神经网络和注意力)设计自然语言处理模型的基本思想。尽管在 :numref:`fig_nlp-map-app` 中,可以将任何预训练的文本表示与任何应用的结构相结合,但我们选择了一些具有代表性的组合。具体来说,我们将探索基于循环神经网络和卷积神经网络的流行架构进行情感分析。对于自然语言推理,我们选择注意力和多层感知机来演示如何分析文本对。最后,我们介绍了如何为广泛的自然语言处理应用,如在序列级(单文本分类和文本对分类)和词元级(文本标注和问答)上对预训练BERT模型进行微调。作为一个具体的经验案例,我们将针对自然语言推理对BERT进行微调。

正如我们在 :numref:`sec_bert` 中引入的那样,对于各种自然语言处理应用程序,BERT 需要极少的体系结构更改。但是,这一好处的代价是为下游应用程序微调大量 BERT 参数。当空间或时间有限时,那些基于 MLP、CNN、RNN 和注意力精心制作的模型更加可行。在下面,我们从情绪分析应用程序开始,分别说明基于 RNN 和 CNN 的模型设计
正如我们在 :numref:`sec_bert` 中介绍的那样,对于广泛的自然语言处理应用,BERT只需要最少的结构更改。然而,这一好处是以微调下游应用的大量BERT参数为代价的。当空间或时间有限时,基于多层感知机、卷积神经网络、循环神经网络和注意力的精心构建的模型更具可行性。下面,我们从情感分析应用开始,分别说明基于循环神经网络和卷积神经网络的模型设计

```toc
:maxdepth: 2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
# 自然语言推理和数据集
# 自然语言推理与数据集
:label:`sec_natural-language-inference-and-dataset`

在 :numref:`sec_sentiment` 中,我们讨论了情绪分析的问题。此任务旨在将单个文本序列分为预定义的类别,例如一组情绪极性。但是,当需要决定是否可以从另一句话推断出另一句话,还是通过确定语义上等同的句子来消除冗余时,知道如何对一个文本序列进行分类是不够的。相反,我们需要能够对文本序列成对进行推理。
在 :numref:`sec_sentiment` 中,我们讨论了情感分析问题。这个任务的目的是将单个文本序列分类到预定义的类别中,例如一组情感极性中。然而,当需要决定一个句子是否可以从另一个句子推断出来,或者需要通过识别语义等价的句子来消除句子间冗余时,知道如何对一个文本序列进行分类是不够的。相反,我们需要能够对成对的文本序列进行推理。

## 自然语言推理

*自然语言推理*研究是否有*假设*
可以从*前提*推断出来,其中两者都是文本序列。换句话说,自然语言推理决定了一对文本序列之间的逻辑关系。这种关系通常分为三种类型:
*自然语言推理*研究了*假设*是否可以从*前提*中推断出来,其中两者都是文本序列。换言之,自然语言推理决定了一对文本序列之间的逻辑关系。这类关系通常分为三种类型:

* *蕴含(entailment)*:假设可以从前提中推断出来。
* *矛盾(contradiction)*:可以从前提中推断假设的否定
* *中立(neutral)*:所有其他情况。
* *蕴涵*:假设可以从前提中推断出来。
* *矛盾*:假设的否定可以从前提中推断出来
* *中性*:所有其他情况。

自然语言推理也被称为识别文本蕴含任务。例如,下面的词元对将被标记为*蕴含(entailment)*,因为假设中的 “显示亲情” 可以从前提中的 “相互拥抱” 推断出来。
自然语言推理也被称为识别文本蕴涵任务。
例如,下面的一个文本对将被贴上“蕴涵”的标签,因为假设中的“表白”可以从前提中的“拥抱”中推断出来。

> 前提:两个女人互相拥抱。
>前提:两个女人拥抱在一起。
> 假设:两个女人表现出亲情。
>假设:两个女人在示爱。
以下是 *矛盾* 的例子,因为 “运行编码示例” 表示 “没有睡觉” 而不是 “睡觉”。
下面是一个“矛盾”的例子,因为“运行编码示例”表示“不睡觉”,而不是“睡觉”。

> 前提:一个男人正在运行从 “潜入深度学习” 中的编码示例。
>前提:一名男子正在运行Dive Into Deep Learning的编码示例。
> 假设:那个男人在睡觉。
假设:该男子正在睡觉。

第三个例子显示了 * 中立性 * 关系,因为 “正在为我们表演” 这一事实既不能推断 “著名的” 也不是 “不出名”。
第三个例子显示了一种“中立”关系,因为“正在为我们表演”这一事实无法推断出“出名”或“不出名”。

> 前提:音乐家们正在为我们表演。
>前提:音乐家们正在为我们表演。
> 假设:音乐家很有名。
>假设:音乐家很有名。
自然语言推理一直是理解自然语言的核心话题。它拥有广泛的应用程序,从信息检索到开放域问答。为了研究这个问题,我们将首先研究一个流行的自然语言推理基准数据集。
自然语言推理一直是理解自然语言的中心话题。它有着广泛的应用,从信息检索到开放领域的问答。为了研究这个问题,我们将首先研究一个流行的自然语言推理基准数据集。

## 斯坦福大学自然语言推理(SNLI)数据集
## 斯坦福自然语言推理(SNLI数据集

[**斯坦福大学自然语言推理(SNLI)语料库**]是超过 50 万个标记为英语句子对 :cite:`Bowman.Angeli.Potts.ea.2015` 的集合。我们将提取的 SNLI 数据集下载并存储在路径 `../data/snli_1.0`
[**斯坦福自然语言推理语料库(Stanford Natural Language Inference,SNLI**]是由500000多个带标签的英语句子对组成的集合:cite:`Bowman.Angeli.Potts.ea.2015`。我们在路径`../data/snli_1.0`中下载并存储提取的SNLI数据集

```{.python .input}
from d2l import mxnet as d2l
Expand Down Expand Up @@ -68,20 +68,20 @@ d2l.DATA_HUB['SNLI'] = (
data_dir = d2l.download_extract('SNLI')
```

### [**阅读数据集**]
### [**读取数据集**]

原始 SNLI 数据集包含的信息比我们实验中真正需要的信息丰富得多。因此,我们定义了一个函数 `read_snli` 来仅提取部分数据集,然后返回前提、假设及其标签的列表。
原始的SNLI数据集包含的信息比我们在实验中真正需要的信息丰富得多。因此,我们定义函数`read_snli`以仅提取数据集的一部分,然后返回前提、假设及其标签的列表。

```{.python .input}
#@tab all
#@save
def read_snli(data_dir, is_train):
"""Read the SNLI dataset into premises, hypotheses, and labels."""
"""将SNLI数据集解析为前提、假设和标签。"""
def extract_text(s):
# Remove information that will not be used by us
# 删除我们不会使用的信息
s = re.sub('\\(', '', s)
s = re.sub('\\)', '', s)
# Substitute two or more consecutive whitespace with space
# 用一个空格替换两个或多个连续的空格
s = re.sub('\\s{2,}', ' ', s)
return s.strip()
label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2}
Expand All @@ -95,7 +95,7 @@ def read_snli(data_dir, is_train):
return premises, hypotheses, labels
```

现在让我们[**打印前3对前提和假设**],以及它们的标签(“0”、“1”“2” 分别对应于 “蕴含”、“矛盾”“中性”)。
现在让我们[**打印前3对**]前提和假设,以及它们的标签(“0”、“1”“2”分别对应于“蕴涵”、“矛盾”“中性”)。

```{.python .input}
#@tab all
Expand All @@ -106,7 +106,7 @@ for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]):
print('label:', y)
```

该训练套装有大约 550,000 对,测试套装有约 10000 对。以下表明,在培训套装和测试套装中,[**“蕴含(entailment)”、“矛盾(contradiction)”和“中性(neutral)”三个标签都是平衡的**]
训练集约有550000对,测试集约有10000对。下面显示了训练集和测试集中的三个[**标签“蕴涵”、“矛盾”和“中性”是平衡的**]

```{.python .input}
#@tab all
Expand All @@ -117,12 +117,12 @@ for data in [train_data, test_data]:

### [**定义用于加载数据集的类**]

下面我们定义一个类,通过从 Gluon 中的 `Dataset` 类继承来加载 SNLI 数据集。类构造函数中的参数 `num_steps` 指定了文本序列的长度,以便序列的每个小块都具有相同的形状。换句话说,在较长顺序中的第一个 `num_steps` 个代币之后的令牌被修剪,而特殊令牌 “<pad>” 将附加到较短的序列,直到它们的长度变为 `num_steps`。通过实现 `__getitem__` 函数,我们可以使用指数 `idx` 任意访问前提、假设和标签。
下面我们通过继承Gluon中的`Dataset`类来定义一个用于加载SNLI数据集的类。类构造函数中的变量`num_steps`指定文本序列的长度,使得每个小批量序列将具有相同的形状。换句话说,在较长序列中的前`num_steps`个标记之后的标记被截断,而特殊标记“&lt;pad&gt;”将被附加到较短的序列后,直到它们的长度变为`num_steps`。通过实现`__getitem__`功能,我们可以任意访问带有索引`idx`的前提、假设和标签。

```{.python .input}
#@save
class SNLIDataset(gluon.data.Dataset):
"""A customized dataset to load the SNLI dataset."""
"""用于加载SNLI数据集的自定义数据集。"""
def __init__(self, dataset, num_steps, vocab=None):
self.num_steps = num_steps
all_premise_tokens = d2l.tokenize(dataset[0])
Expand Down Expand Up @@ -153,7 +153,7 @@ class SNLIDataset(gluon.data.Dataset):
#@tab pytorch
#@save
class SNLIDataset(torch.utils.data.Dataset):
"""A customized dataset to load the SNLI dataset."""
"""用于加载SNLI数据集的自定义数据集。"""
def __init__(self, dataset, num_steps, vocab=None):
self.num_steps = num_steps
all_premise_tokens = d2l.tokenize(dataset[0])
Expand All @@ -180,14 +180,14 @@ class SNLIDataset(torch.utils.data.Dataset):
return len(self.premises)
```

### [**统筹合一**]
### [**把所有东西放在一起**]

现在我们可以调用 `read_snli` 函数和 `SNLIDataset` 类来下载 SNLI 数据集,并返回 `DataLoader` 实例用于训练和测试集合,以及训练集的词汇表。值得注意的是,我们必须使用从训练集中构建的词汇和测试集合的词汇。因此,在训练集中训练的模型将不知道测试集中的任何新令牌
现在,我们可以调用`read_snli`函数和`SNLIDataset`类来下载SNLI数据集,并返回训练集和测试集的`DataLoader`实例,以及训练集的词表。值得注意的是,我们必须使用从训练集构造的词表作为测试集的词表。因此,在训练集中训练的模型将不知道来自测试集的任何新词元

```{.python .input}
#@save
def load_data_snli(batch_size, num_steps=50):
"""Download the SNLI dataset and return data iterators and vocabulary."""
"""下载SNLI数据集并返回数据迭代器和词表。"""
num_workers = d2l.get_dataloader_workers()
data_dir = d2l.download_extract('SNLI')
train_data = read_snli(data_dir, True)
Expand All @@ -205,7 +205,7 @@ def load_data_snli(batch_size, num_steps=50):
#@tab pytorch
#@save
def load_data_snli(batch_size, num_steps=50):
"""Download the SNLI dataset and return data iterators and vocabulary."""
"""下载SNLI数据集并返回数据迭代器和词表。"""
num_workers = d2l.get_dataloader_workers()
data_dir = d2l.download_extract('SNLI')
train_data = read_snli(data_dir, True)
Expand All @@ -221,15 +221,15 @@ def load_data_snli(batch_size, num_steps=50):
return train_iter, test_iter, train_set.vocab
```

在这里,我们将批处理大小设置为 128,序列长度为 50,然后调用 `load_data_snli` 函数来获取数据迭代器和词汇。然后我们打印词汇量大小
在这里,我们将批量大小设置为128时,将序列长度设置为50,并调用`load_data_snli`函数来获取数据迭代器和词表。然后我们打印词表大小

```{.python .input}
#@tab all
train_iter, test_iter, vocab = load_data_snli(128, 50)
len(vocab)
```

现在我们打印第一个迷你手表的形状。与情绪分析相反,我们有两个输入 `X[0]``X[1]`,代表对前提和假设
现在我们打印第一个小批量的形状。与情感分析相反,我们有分别代表前提和假设的两个输入`X[0]``X[1]`

```{.python .input}
#@tab all
Expand All @@ -240,16 +240,16 @@ for X, Y in train_iter:
break
```

## 摘要
## 小结

* 自然语言推理研究是否可以从前提中推断假设,两者都是文本序列
* 在自然语言推断中,前提和假设之间的关系包括内涵、矛盾和中立
* 斯坦福自然语言推理 (SNLI) 语料库是一个受欢迎的自然语言推断基准数据集
* 自然语言推理研究假设是否可以从前提推断出来,其中两者都是文本序列
* 在自然语言推理中,前提和假设之间的关系包括蕴涵关系、矛盾关系和中性关系
* 斯坦福自然语言推理SNLI)语料库是目前比较流行的自然语言推理基准数据集

## 练习

1. 长期以来,机器翻译的评估基于输出翻译和基础真实翻译之间的肤浅 $n$ 克匹配。你能否通过自然语言推断来设计评估机器翻译结果的衡量标准
1. 我们如何更改超参数来减少词汇量
1. 机器翻译长期以来一直是基于翻译输出和翻译真实值之间的表面$n$元语法匹配来进行评估的。你能设计一种用自然语言推理来评价机器翻译结果的方法吗
1. 我们如何更改超参数以减小词表大小

:begin_tab:`mxnet`
[Discussions](https://discuss.d2l.ai/t/394)
Expand Down
Loading

0 comments on commit 0f005bb

Please sign in to comment.