Skip to content

Commit

Permalink
ch14.5-14.10 (d2l-ai#940)
Browse files Browse the repository at this point in the history
* ch14.5-14.10
  • Loading branch information
xiaotinghe committed Aug 17, 2021
1 parent f1f0d4e commit e7f63e0
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 268 deletions.
107 changes: 49 additions & 58 deletions chapter_natural-language-processing-pretraining/bert-dataset.md

Large diffs are not rendered by default.

59 changes: 29 additions & 30 deletions chapter_natural-language-processing-pretraining/bert-pretraining.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 培训前培训 BERT
# 预训练BERT
:label:`sec_bert-pretraining`

随着在 :numref:`sec_bert` 中实施了 BERT 模型,以及 :numref:`sec_bert-dataset` 中从 WikiText-2 数据集生成的预训练示例,我们将在本节的 WikiText-2 数据集上预训练 BERT
利用 :numref:`sec_bert` 中实现的BERT模型和 :numref:`sec_bert-dataset` 中从WikiText-2数据集生成的预训练样本,我们将在本节中在WikiText-2数据集上对BERT进行预训练

```{.python .input}
from d2l import mxnet as d2l
Expand All @@ -17,17 +17,17 @@ import torch
from torch import nn
```

首先,我们加载 Wikitext-2 数据集作为用于掩码语言建模和下一句话预测的预训练示例的小组。批次大小为 512,BERT 输入序列的最大长度为 64。请注意,在原始的 BERT 模型中,最大长度为 512
首先,我们加载WikiText-2数据集作为小批量的预训练样本,用于遮蔽语言模型和下一句预测。批量大小是512,BERT输入序列的最大长度是64。注意,在原始BERT模型中,最大长度是512

```{.python .input}
#@tab all
batch_size, max_len = 512, 64
train_iter, vocab = d2l.load_data_wiki(batch_size, max_len)
```

## 培训前培训 BERT
## 预训练BERT

原来的 BERT 有两个不同型号尺寸 :cite:`Devlin.Chang.Lee.ea.2018` 的版本。基本型号($\text{BERT}_{\text{BASE}}$)使用 12 层(变压器编码器块),其中包含 768 个隐藏单元(隐藏尺寸)和 12 个自我注意头。大型模型($\text{BERT}_{\text{LARGE}}$)使用 24 层,其中有 1024 个隐藏单元和 16 个自我注意头。值得注意的是,前者有 1.1 亿个参数,而后者有 3.4 亿个参数。为了轻松进行演示,我们定义了[**一个小型BERT,它使用2层、128个隐藏单位和2个自我注意头**]
原始BERT :cite:`Devlin.Chang.Lee.ea.2018` 有两个不同模型尺寸的版本。基本模型($\text{BERT}_{\text{BASE}}$)使用12层(Transformer编码器块),768个隐藏单元(隐藏大小)和12个自注意头。大模型($\text{BERT}_{\text{LARGE}}$)使用24层,1024个隐藏单元和16个自注意头。值得注意的是,前者有1.1亿个参数,后者有3.4亿个参数。为了便于演示,我们定义了一个小的BERT,使用了2层、128个隐藏单元和2个自注意头

```{.python .input}
net = d2l.BERTModel(len(vocab), num_hiddens=128, ffn_num_hiddens=256,
Expand All @@ -48,7 +48,7 @@ devices = d2l.try_all_gpus()
loss = nn.CrossEntropyLoss()
```

在定义训练循环之前,我们定义了一个助手函数 `_get_batch_loss_bert`鉴于训练示例的数量,此函数[**计算蒙版语言建模的损失和下一句预测任务的损失**]。请注意,BERT 预训练的最后损失只是蒙版语言建模损失和下一句预测损失的总和
在定义训练代码实现之前,我们定义了一个辅助函数`_get_batch_loss_bert`给定训练样本,该函数计算遮蔽语言模型和下一句子预测任务的损失。请注意,BERT预训练的最终损失是遮蔽语言模型损失和下一句预测损失的和

```{.python .input}
#@save
Expand All @@ -63,16 +63,16 @@ def _get_batch_loss_bert(net, loss, vocab_size, tokens_X_shards,
tokens_X_shards, segments_X_shards, valid_lens_x_shards,
pred_positions_X_shards, mlm_weights_X_shards, mlm_Y_shards,
nsp_y_shards):
# Forward pass
# 前向传播
_, mlm_Y_hat, nsp_Y_hat = net(
tokens_X_shard, segments_X_shard, valid_lens_x_shard.reshape(-1),
pred_positions_X_shard)
# Compute masked language model loss
# 计算遮蔽语言模型损失
mlm_l = loss(
mlm_Y_hat.reshape((-1, vocab_size)), mlm_Y_shard.reshape(-1),
mlm_weights_X_shard.reshape((-1, 1)))
mlm_l = mlm_l.sum() / (mlm_weights_X_shard.sum() + 1e-8)
# Compute next sentence prediction loss
# 计算下一句子预测任务的损失
nsp_l = loss(nsp_Y_hat, nsp_y_shard)
nsp_l = nsp_l.mean()
mlm_ls.append(mlm_l)
Expand All @@ -89,21 +89,22 @@ def _get_batch_loss_bert(net, loss, vocab_size, tokens_X,
segments_X, valid_lens_x,
pred_positions_X, mlm_weights_X,
mlm_Y, nsp_y):
# Forward pass
# 前向传播
_, mlm_Y_hat, nsp_Y_hat = net(tokens_X, segments_X,
valid_lens_x.reshape(-1),
pred_positions_X)
# Compute masked language model loss
# 计算遮蔽语言模型损失
mlm_l = loss(mlm_Y_hat.reshape(-1, vocab_size), mlm_Y.reshape(-1)) *\
mlm_weights_X.reshape(-1, 1)
mlm_l = mlm_l.sum() / (mlm_weights_X.sum() + 1e-8)
# Compute next sentence prediction loss
# 计算下一句子预测任务的损失
nsp_l = loss(nsp_Y_hat, nsp_y)
l = mlm_l + nsp_l
return mlm_l, nsp_l, l
```

调用上述两个辅助函数,以下`train_bert`函数定义了[**在Wikitext-2(`train_iter`)数据集上预训练BERT(`net`)**]的过程。培训 BERT 可能需要很长时间。以下函数的输入 `num_steps` 没有像 `train_ch13` 函数那样指定训练的时代数量(参见 :numref:`sec_image_augmentation`),而是指定训练的迭代步数。

通过调用上述两个辅助函数,下面的`train_bert`函数定义了在WikiText-2(`train_iter`)数据集上预训练BERT(`net`)的过程。训练BERT可能需要很长时间。以下函数的输入`num_steps`指定了训练的迭代步数,而不是像`train_ch13`函数那样指定训练的迭代周期数(参见 :numref:`sec_image_augmentation` )。

```{.python .input}
def train_bert(train_iter, net, loss, vocab_size, devices, num_steps):
Expand All @@ -112,8 +113,7 @@ def train_bert(train_iter, net, loss, vocab_size, devices, num_steps):
step, timer = 0, d2l.Timer()
animator = d2l.Animator(xlabel='step', ylabel='loss',
xlim=[1, num_steps], legend=['mlm', 'nsp'])
# Sum of masked language modeling losses, sum of next sentence prediction
# losses, no. of sentence pairs, count
# 遮蔽语言模型损失的和,下一句预测任务损失的和,句子对的数量,计数
metric = d2l.Accumulator(4)
num_steps_reached = False
while step < num_steps and not num_steps_reached:
Expand Down Expand Up @@ -156,8 +156,7 @@ def train_bert(train_iter, net, loss, vocab_size, devices, num_steps):
step, timer = 0, d2l.Timer()
animator = d2l.Animator(xlabel='step', ylabel='loss',
xlim=[1, num_steps], legend=['mlm', 'nsp'])
# Sum of masked language modeling losses, sum of next sentence prediction
# losses, no. of sentence pairs, count
# 遮蔽语言模型损失的和,下一句预测任务损失的和,句子对的数量,计数
metric = d2l.Accumulator(4)
num_steps_reached = False
while step < num_steps and not num_steps_reached:
Expand Down Expand Up @@ -191,16 +190,16 @@ def train_bert(train_iter, net, loss, vocab_size, devices, num_steps):
f'{str(devices)}')
```

我们可以绘制 BERT 预训期间的蒙版语言建模损失和下一句话预测损失
在预训练过程中,我们可以绘制出遮蔽语言模型损失和下一句预测损失

```{.python .input}
#@tab all
train_bert(train_iter, net, loss, len(vocab), devices, 50)
```

## [**用BERT表示文本**]
## 用BERT表示文本

在预训练 BERT 之后,我们可以用它来表示单个文本、文本对或其中的任何词元。以下函数返回 `tokens_a``tokens_b` 中所有词元的 BERT (`net`) 表示形式
在预训练BERT之后,我们可以用它来表示单个文本、文本对或其中的任何词元。下面的函数返回`tokens_a``tokens_b`中所有词元的BERT(`net`)表示

```{.python .input}
def get_bert_encoding(net, tokens_a, tokens_b=None):
Expand All @@ -224,43 +223,43 @@ def get_bert_encoding(net, tokens_a, tokens_b=None):
return encoded_X
```

[**考虑“起重机在飞”这句话**]回想一下 :numref:`subsec_bert_input_rep` 中讨论的 BERT 的输入表示形式。插入特殊词元 “<cls>”(用于分类)和 “<sep>”(用于分隔)后,BERT 输入序列的长度为 6。由于零是 “<cls>” 词元的索引,所以 `encoded_text[:, 0, :]` 是整个输入句子的 BERT 表示。为了评估 polysemy 词元 “鹤”,我们还打印了代币 BERT 表示的前三个元素
考虑“a crane is flying”这句话回想一下 :numref:`subsec_bert_input_rep` 中讨论的BERT的输入表示。插入特殊标记“&lt;cls&gt;”(用于分类)和&lt;sep&gt;”(用于分隔)后,BERT输入序列的长度为6。因为零是“&lt;cls&gt;”词元,`encoded_text[:, 0, :]`是整个输入语句的BERT表示。为了评估一词多义词元“crane”,我们还打印出了该词元的BERT表示的前三个元素

```{.python .input}
#@tab all
tokens_a = ['a', 'crane', 'is', 'flying']
encoded_text = get_bert_encoding(net, tokens_a)
# Tokens: '<cls>', 'a', 'crane', 'is', 'flying', '<sep>'
# 词元: '<cls>', 'a', 'crane', 'is', 'flying', '<sep>'
encoded_text_cls = encoded_text[:, 0, :]
encoded_text_crane = encoded_text[:, 2, :]
encoded_text.shape, encoded_text_cls.shape, encoded_text_crane[0][:3]
```

[**现在考虑一对句子“起重机司机来了”和“他刚离开”**]。同样`encoded_pair[:, 0, :]` 是预训练的 BERT 整个句子对的编码结果。请注意,polysemy 词元 “鹤” 的前三个元素与上下文不同时的前三个元素不同。这支持 BERT 表示是上下文相关的
现在考虑一个句子“a crane driver came”和“he just left”。类似地`encoded_pair[:, 0, :]`是来自预训练BERT的整个句子对的编码结果。注意,多义词元“crane”的前三个元素与上下文不同时的元素不同。这支持了BERT表示是上下文敏感的

```{.python .input}
#@tab all
tokens_a, tokens_b = ['a', 'crane', 'driver', 'came'], ['he', 'just', 'left']
encoded_pair = get_bert_encoding(net, tokens_a, tokens_b)
# Tokens: '<cls>', 'a', 'crane', 'driver', 'came', '<sep>', 'he', 'just',
# 词元: '<cls>', 'a', 'crane', 'driver', 'came', '<sep>', 'he', 'just',
# 'left', '<sep>'
encoded_pair_cls = encoded_pair[:, 0, :]
encoded_pair_crane = encoded_pair[:, 2, :]
encoded_pair.shape, encoded_pair_cls.shape, encoded_pair_crane[0][:3]
```

在 :numref:`chap_nlp_app` 中,我们将为下游自然语言处理应用程序微调预训练的 BERT 模型。
在 :numref:`chap_nlp_app` 中,我们将为下游自然语言处理应用微调预训练的BERT模型。

## 小结

* 原来的 BERT 有两个版本,其中基本模型有 1.1 亿个参数,而大型模型有 3.4 亿个参数
* 在预训练 BERT 之后,我们可以用它来表示单个文本、文本对或其中的任何词元。
* 在实验中,当上下文不同时,同一个词元具有不同的 BERT 表示形式。这支持 BERT 表示是上下文相关的
* 原始的BERT有两个版本,其中基本模型有1.1亿个参数,大模型有3.4亿个参数
* 在预训练BERT之后,我们可以用它来表示单个文本、文本对或其中的任何词元。
* 在实验中,同一个词元在不同的上下文中具有不同的BERT表示。这支持BERT表示是上下文敏感的

## 练习

1. 在实验中,我们可以看到,蒙版语言建模损失明显高于下一个句子预测损失。为什么?
2. 将 BERT 输入序列的最大长度设置为 512(与原始 BERT 模型相同)。使用原始 BERT 模型的配置,例如 $\text{BERT}_{\text{LARGE}}$。运行此部分时你会遇到任何错误吗?为什么?
1. 在实验中,我们可以看到遮蔽语言模型损失明显高于下一句预测损失。为什么?
2. 将BERT输入序列的最大长度设置为512(与原始BERT模型相同)。使用原始BERT模型的配置,如$\text{BERT}_{\text{LARGE}}$。运行此部分时是否遇到错误?为什么?

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

0 comments on commit e7f63e0

Please sign in to comment.