赞
踩
原文:
zh.annas-archive.org/md5/a1e65552fc41f3b5a667f63d9bed854c
译者:飞龙
在本章中,我们将从头开始构建一个 RoBERTa 模型。该模型将使用我们在 BERT 模型中所需的Transformers构造工具的模块。此外,不会使用预训练的分词器或模型。RoBERTa 模型将按照本章描述的十五步过程构建。
我们将利用在之前章节中获得的Transformers知识,逐步构建一个可以对遮蔽标记执行语言建模的模型。在第二章,开始使用Transformers模型的体系结构中,我们研究了原始Transformers的构建模块。在第三章,对 BERT 模型进行微调中,我们微调了一个预训练的 BERT 模型。
本章将重点介绍如何使用基于 Hugging Face 无缝模块的 Jupyter 笔记本从头开始构建一个预训练的Transformers模型。该模型被命名为 KantaiBERT。
KantaiBERT 首先加载了专门为本章创建的伊曼纽尔·康德的书籍的合集。您将看到数据是如何获取的。您还将看到如何为此笔记本创建自己的数据集。
KantaiBERT 从头训练自己的分词器。它将构建自己的合并和词汇文件,在预训练过程中使用。
然后 KantaiBERT 处理数据集,初始化一个训练师,并训练模型。
最后,KantaiBERT 使用训练好的模型执行一个实验性的下游语言建模任务,并使用伊曼纽尔·康德的逻辑填充掩码。
到本章结束时,您将了解如何从头开始构建一个Transformers模型。您将拥有足够的Transformers知识,以应对使用强大的预训练Transformers(如 GPT-3 引擎)的第四次工业革命挑战,这需要不仅有开发技能,还需要实施能够实现它们的技能。本章为第七章,GPT-3 引擎崛起的超人类Transformers做好了准备。
本章涵盖以下主题:
类似 RoBERTa 和 DistilBERT 的模型
如何从头训练一个分词器
字节级字节对编码
将训练好的分词器保存到文件中
重新创建用于预训练过程的分词器
从头初始化 RoBERTa 模型
探索模型的配置
探索模型的 8000 万参数
为训练师构建数据集
初始化训练师
预训练模型
保存模型
将模型应用于遮蔽语言建模(MLM)的下游任务
我们的第一步将是描述我们将要构建的Transformers模型。
在本章中,我们将使用 Hugging Face 提供的用于类似 BERT 模型的构建模块来训练一个名为 KantaiBERT 的Transformers模型。我们在第三章,对 BERT 模型进行微调中介绍了我们将使用的模型构建模块的理论。
我们将在之前章节中获得的知识基础上描述 KantaiBERT。
KantaiBERT 是一种基于 BERT 架构的鲁棒优化 BERT 预训练方法(RoBERTa)-风格的模型。
最初的 BERT 模型为初始的Transformers模型带来了创新特性,正如我们在第三章中看到的那样。RoBERTa 通过改进预训练过程的机制来提高Transformers对下游任务的性能。
例如,它不使用WordPiece
分词,而是下降到字节级别的字节对编码(BPE)。这种方法为各种 BERT 和类似 BERT 的模型铺平了道路。
在本章中,与 BERT 一样,KantaiBERT 将使用掩码语言建模(MLM)进行培训。MLM 是一种语言建模技术,它会在序列中掩盖一个单词。Transformers模型必须训练以预测被掩盖的单词。
KantaiBERT 将作为一个小型模型进行培训,包括 6 层、12 个头和 84,095,008 个参数。也许 8400 万个参数看起来很多。然而,这些参数分布在 12 个头上,使其成为一个相对较小的模型。一个小模型将使预训练体验得以顺畅进行,以便每一步都可以实时查看,而不必等待数小时才能看到结果。
KantaiBERT 是一种类似 DistilBERT 的模型,因为它具有相同的 6 层和 12 个头的架构。DistilBERT 是 BERT 的精简版本。如其名所示,DistilBERT 比 RoBERTa 模型包含更少的参数。因此,它运行速度更快,但与 RoBERTa 模型相比,结果略微不够准确。
我们知道,大型模型能够取得出色的性能。但是,如果你想在智能手机上运行一个模型会怎样呢?微型化一直是技术发展的关键。在实施过程中,Transformers有时必须遵循相同的路径。Hugging Face 提出使用 BERT 的精简版本是迈出的一大步。因此,通过使用更少的参数或其他类似方法进行精炼,是将预训练的最佳内容变得有效用于许多下游任务的巧妙方式。
显示所有可能的架构很重要,包括在智能手机上运行一个小型模型。然而,Transformers的未来也将是准备好的可用 API,正如我们将在第七章的超人类Transformers与 GPT-3 引擎的崛起中所看到的那样。
KantaiBERT 将实现一种类似 GPT-2 所使用的字节级字节对编码分词器。特殊的分词将使用 RoBERTa 所使用的分词。BERT 模型通常使用 WordPiece 分词器。
没有标记类型 ID 来指示标记属于哪个片段的哪个部分。片段将用分隔标记</s>
来分隔。
KantaiBERT 将使用自定义数据集,训练分词器,训练Transformers模型,保存它,并在 MLM 示例中运行它。
让我们开始从头构建一个Transformers。
我们将从头构建 KantaiBERT,并在 MLM 示例中运行它的 15 个步骤。
打开 Google Colaboratory(你需要一个 Gmail 帐号)。然后上传KantaiBERT.ipynb
,它在本章的 GitHub 目录中。
本节的 15 个步骤的标题与笔记本单元格的标题类似,这使得它们易于跟踪。
让我们从加载数据集开始。
预先准备好的数据集提供了一个客观的方法来训练和比较 transformers。在第五章,使用 Transformers 进行下游 NLP 任务中,我们将探讨几个数据集。然而,本章旨在理解一个 transformer 的训练过程,使用可以实时运行的笔记本单元格,而不需要等待数小时才能获得结果。
我选择使用伊曼纽尔·康德(1724-1804)的作品,他是德国哲学家,是启蒙时代的典范。这个想法是为下游推理任务引入类似于人类的逻辑和预训练推理。
Project Gutenberg,www.gutenberg.org
,提供了大量免费的可以以文本格式下载的电子书。如果你想基于书籍创建自定义数据集,可以使用其他书籍。
我将以下三本伊曼纽尔·康德的书编译成名为kant.txt
的文本文件:
纯粹理性批判
实践理性批判
道德形而上学基本原理
kant.txt
为本章的 transformer 模型提供了一个小的训练数据集。所得到的结果仍然是试验性的。对于一个真实的项目,我会添加伊曼纽尔·康德、勒内·笛卡尔、帕斯卡尔和莱布尼茨等人的全部作品,例如。
文本文件包含书籍的原始文本:
`…For it is in reality vain to profess _indifference_ in regard to such`
`inquiries, the object of which cannot be indifferent to humanity.`
数据集将在KantaiBERT.ipynb
笔记本的第一个单元格中自动从 GitHub 下载。
你也可以使用 Colab 的文件管理器加载kant.txt
,它位于本章在 GitHub 上的目录中。在这种情况下,使用 curl
从 GitHub 检索它:
#@title Step 1: Loading the Dataset
#1.Load kant.txt using the Colab file manager
#2.Downloading the file from GitHub
!curl -L https://raw.githubusercontent.com/Denis2054/Transformers-for-NLP-2nd-Edition/master/Chapter04/kant.txt --output "kant.txt"
一旦加载或下载,你可以在 Colab 文件管理器窗格中看到它出现:
图 4.1:Colab 文件管理器
请注意,当您重新启动 VM 时,Google Colab 将删除文件。
数据集被定义并加载。
不要在没有kant.txt
的情况下运行后续单元格。训练数据是先决条件。
现在,程序将安装 Hugging Face transformers。
我们需要安装 Hugging Face transformers 和分词器,但在这个 Google Colab VM 实例中我们不需要 TensorFlow:
#@title Step 2:Installing Hugging Face Transformers
# We won't need TensorFlow here
!pip uninstall -y tensorflow
# Install 'transformers' from master
!pip install git+https://github.com/huggingface/transformers
!pip list | grep -E 'transformers|tokenizers'
# transformers version at notebook update --- 2.9.1
# tokenizers version at notebook update --- 0.7.0
输出显示已安装的版本:
Successfully built transformers
tokenizers 0.7.0
transformers 2.10.0
Transformer 版本发展得非常迅速。你运行的版本可能会有所不同,并且显示方式也可能不同。
程序现在将通过训练一个分词器来开始。
在本节中,程序不使用预训练的分词器。例如,可以使用预训练的 GPT-2 分词器。但是,本章的训练过程包括从头开始训练一个分词器。
使用 Hugging Face 的 ByteLevelBPETokenizer()
将使用 kant.txt
进行训练。 BPE 标记器将字符串或单词分解为子字符串或子词。这有两个主要优点,其中之一是:
标记器可以将单词分解为最小组件。然后它将这些小组件合并成统计学上有趣的组件。例如,“smaller" and smallest
" 可以变成“small
,”,“er
,” 和“est
。” 标记器可以进一步操作。例如,我们可以得到“sm
" 和“all
。” 无论如何,单词都被分解成子词标记和子词部分的更小单位,例如“sm
" 和“all
",而不仅仅是“small
。”
使用 WordPiece
级别编码,将被分类为未知的字符串块 unk_token
,实际上会消失。
在此模型中,我们将使用以下参数训练标记器:
files=paths
是数据集的路径
vocab_size=52_000
是我们标记器模型长度的大小
min_frequency=2
是最小频率阈值
special_tokens=[]
是特殊标记的列表
在这种情况下,特殊标记列表是:
<s>
:一个开始标记
<pad>
:一个填充标记
</s>
:一个结束符号
<unk>
:一个未知标记
<mask>
:语言建模的掩码标记
标记器将被训练以生成合并的子字符串标记并分析它们的频率。
在句子中间拿这两个词来说:
...the tokenizer...
第一步是将字符串标记化:
'Ġthe', 'Ġtoken', 'izer',
字符串现在被标记为带有 Ġ
(空格)信息的标记。
下一步是用它们的索引替换它们:
‘Ġthe’ | ‘Ġtoken’ | ‘izer’ |
---|---|---|
150 | 5430 | 4712 |
表 4.1:三个标记的索引
程序如预期般运行标记器:
#@title Step 3: Training a Tokenizer
%%time
from pathlib import Path
from tokenizers import ByteLevelBPETokenizer
paths = [str(x) for x in Path(".").glob("**/*.txt")]
# Initialize a tokenizer
tokenizer = ByteLevelBPETokenizer()
# Customize training
tokenizer.train(files=paths, vocab_size=52_000, min_frequency=2, special_tokens=[
"<s>",
"<pad>",
"</s>",
"<unk>",
"<mask>",
])
标记器输出训练时间:
CPU times: user 14.8 s, sys: 14.2 s, total: 29 s
Wall time: 7.72 s
标记器已经训练完成并准备保存。
当训练完成时,标记器将生成两个文件:
merges.txt
,其中包含合并的标记化子字符串
vocab.json
,其中包含标记化子字符串的索引
程序首先创建 KantaiBERT
目录,然后保存两个文件:
#@title Step 4: Saving the files to disk
import os
token_dir = '/content/KantaiBERT'
if not os.path.exists(token_dir):
os.makedirs(token_dir)
tokenizer.save_model('KantaiBERT')
程序输出显示两个文件已保存:
['KantaiBERT/vocab.json', 'KantaiBERT/merges.txt']
这两个文件应该出现在文件管理器窗格中:
图 4.2:Colab 文件管理器
此示例中的文件很小。您可以双击它们以查看其内容。merges.txt
包含按计划标记化的子字符串:
#version: 0.2 - Trained by 'huggingface/tokenizers'
Ġ t
h e
Ġ a
o n
i n
Ġ o
Ġt he
r e
i t
Ġo f
vocab.json
包含以下索引:
[…,"Ġthink":955,"preme":956,"ĠE":957,"Ġout":958,"Ġdut":959,"aly":960,"Ġexp":961,…]
训练好的标记化数据集文件已准备好进行处理。
我们本可以加载预训练的标记器文件。 但是,我们训练了自己的标记器,现在准备加载文件:
#@title Step 5 Loading the Trained Tokenizer Files
from tokenizers.implementations import ByteLevelBPETokenizer
from tokenizers.processors import BertProcessing
tokenizer = ByteLevelBPETokenizer(
"./KantaiBERT/vocab.json",
"./KantaiBERT/merges.txt",
)
标记器可以对序列进行编码:
tokenizer.encode("The Critique of Pure Reason.").tokens
“The Critique of Pure Reason
” 将变成:
['The', 'ĠCritique', 'Ġof', 'ĠPure', 'ĠReason', '.']
我们还可以要求看到此序列中的标记数:
tokenizer.encode("The Critique of Pure Reason.")
输出将显示序列中有 6 个标记:
Encoding(num_tokens=6, attributes=[ids, type_ids, tokens, offsets, attention_mask, special_tokens_mask, overflowing])
分词器现在处理这些标记以适应本笔记本中使用的 BERT 模型变体。后处理器将添加一个开始和结束标记;例如:
tokenizer._tokenizer.post_processor = BertProcessing(
("</s>", tokenizer.token_to_id("</s>")),
("<s>", tokenizer.token_to_id("<s>")),
)
tokenizer.enable_truncation(max_length=512)
让我们对一个后处理序列进行编码:
tokenizer.encode("The Critique of Pure Reason.")
输出显示我们现在有 8 个标记:
Encoding(num_tokens=8, attributes=[ids, type_ids, tokens, offsets, attention_mask, special_tokens_mask, overflowing])
如果我们想看看添加了什么,我们可以要求分词器对后处理序列进行编码,运行以下单元格:
tokenizer.encode("The Critique of Pure Reason.").tokens
输出显示已添加了开始和结束标记,这将标记数增加到了 8,包括开始和结束标记:
['<s>', 'The', 'ĠCritique', 'Ġof', 'ĠPure', 'ĠReason', '.', '</s>']
用于训练模型的数据现在已经准备好了。我们现在将检查运行笔记本的机器的系统信息。
KantaiBERT 在 图形处理单元(GPU)上以最佳速度运行。
我们首先运行一个命令来查看 NVIDIA GPU 卡是否存在:
#@title Step 6: Checking Resource Constraints: GPU and NVIDIA
!nvidia-smi
输出显示了卡的信息和版本:
图 4.3:NVIDIA 卡信息
输出可能随着每个 Google Colab VM 配置而变化。
我们现在将检查 PyTorch
是否看到 CUDA:
#@title Checking that PyTorch Sees CUDA
import torch
torch.cuda.is_available()
结果应该是 True
:
True
CUDA(计算统一设备体系结构)是由 NVIDIA 开发的,用于利用其 GPU 的并行计算能力。
对于更多关于 NVIDIA GPU 和 CUDA 的信息,请参阅 附录 II,Transformer 模型的硬件限制。
我们现在准备定义模型的配置。
我们将使用与 DistilBERT Transformers相同数量的层和头部来预训练 RoBERTa 类型的Transformers模型。模型的词汇量设置为 52,000,具有 12 个注意力头和 6 个层:
#@title Step 7: Defining the configuration of the Model
from transformers import RobertaConfig
config = RobertaConfig(
vocab_size=52_000,
max_position_embeddings=514,
num_attention_heads=12,
num_hidden_layers=6,
type_vocab_size=1,
)
我们将更详细地探讨配置,见 步骤 9:从零开始初始化模型。
让我们首先在我们的模型中重新创建分词器。
我们现在准备加载我们训练过的分词器,这是我们预训练的分词器 RobertaTokenizer.from_pretained()
:
#@title Step 8: Re-creating the Tokenizer in Transformers
from transformers import RobertaTokenizer
tokenizer = RobertaTokenizer.from_pretrained("./KantaiBERT", max_length=512)
现在我们加载了我们的训练过的分词器,让我们从零开始初始化一个 RoBERTa 模型。
在本节中,我们将从零开始初始化一个模型,并检查模型的大小。
程序首先导入一个 RoBERTa 掩码模型进行语言建模:
#@title Step 9: Initializing a Model From Scratch
from transformers import RobertaForMaskedLM
该模型使用 步骤 7 中定义的配置进行初始化:
model = RobertaForMaskedLM(config=config)
如果我们打印模型,我们可以看到它是一个具有 6 层和 12 个头的 BERT 模型:
print(model)
原始 Transformer 模型的编码器的构建模块以不同的尺寸存在,如输出摘录所示:
RobertaForMaskedLM( (roberta): RobertaModel( (embeddings): RobertaEmbeddings( (word_embeddings): Embedding(52000, 768, padding_idx=1) (position_embeddings): Embedding(514, 768, padding_idx=1) (token_type_embeddings): Embedding(1, 768) (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True) (dropout): Dropout(p=0.1, inplace=False) ) (encoder): BertEncoder( (layer): ModuleList( (0): BertLayer( (attention): BertAttention( (self): BertSelfAttention( (query): Linear(in_features=768, out_features=768, bias=True) (key): Linear(in_features=768, out_features=768, bias=True) (value): Linear(in_features=768, out_features=768, bias=True) (dropout): Dropout(p=0.1, inplace=False) ) (output): BertSelfOutput( (dense): Linear(in_features=768, out_features=768, bias=True) (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True) (dropout): Dropout(p=0.1, inplace=False) ) ) (intermediate): BertIntermediate( (dense): Linear(in_features=768, out_features=3072, bias=True) ) (output): BertOutput( (dense): Linear(in_features=3072, out_features=768, bias=True) (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True) (dropout): Dropout(p=0.1, inplace=False) ) ) …/…
在继续之前,请花一些时间仔细阅读配置的输出详情。您将从内部了解模型。
Transformers的 LEGO^® 类型构建模块使得分析变得有趣。例如,您将注意到在子层中存在辍学正则化。
现在,让我们探索参数。
该模型小巧,含有 84,095,008 个参数。
我们可以检查其大小:
print(model.num_parameters())
输出显示大约的参数数量,这可能因Transformers版本而异:
84095008
现在让我们查看参数。我们首先将参数存储在LP
中,并计算参数列表的长度:
#@title Exploring the Parameters
LP=list(model.parameters())
lp=len(LP)
print(lp)
输出显示大约有108
个矩阵和向量,这可能因Transformers模型而异:
108
现在,让我们显示张量中的108
个矩阵和向量:
for p in range(0,lp):
print(LP[p])
输出显示所有参数,如下所示:
Parameter containing:
tensor([[-0.0175, -0.0210, -0.0334, ..., 0.0054, -0.0113, 0.0183],
[ 0.0020, -0.0354, -0.0221, ..., 0.0220, -0.0060, -0.0032],
[ 0.0001, -0.0002, 0.0036, ..., -0.0265, -0.0057, -0.0352],
...,
[-0.0125, -0.0418, 0.0190, ..., -0.0069, 0.0175, -0.0308],
[ 0.0072, -0.0131, 0.0069, ..., 0.0002, -0.0234, 0.0042],
[ 0.0008, 0.0281, 0.0168, ..., -0.0113, -0.0075, 0.0014]],
requires_grad=True)
花几分钟时间来观察参数,以了解Transformers是如何构建的。
参数数量是通过取模型中的所有参数并相加来计算的;例如:
词汇表 (52,000) x 维度 (768)
向量的大小为1 x 768
发现许多其他维度
你会注意到* d [model] = 768。模型中有 12 个头。因此,每个头的 d *[k]维度将是。
再次展示了Transformers优化的 LEGO^®概念,其构建快速。
现在我们将看一下模型的参数数量是如何计算,以及如何得到图中的 84,095,008。
如果我们在笔记本中悬停在LP上,我们将看到 torch 张量的一些形状:
图 4.4:LP
请注意,数字可能会根据您使用的 transformers 模块的版本而变化。
我们将进一步计算每个张量的参数数量。首先,程序初始化了一个名为np
(参数数量)的参数计数器,并遍历了参数列表lp
(108
)中的元素:
#@title Counting the parameters
np=0
for p in range(0,lp):#number of tensors
参数是不同大小的矩阵和向量;例如:
768 x 768
768 x 1
768
我们可以看到一些参数是二维的,而一些是一维的。
查看列表中的参数LP[p]
是否有两个维度的简便方法是:
PL2=True
try:
L2=len(LP[p][0]) #check if 2D
except:
L2=1 #not 2D but 1D
PL2=False
如果参数有两个维度,那么它的第二个维度将是L2>0
且PL2=True (2 dimensions=True)
。如果参数只有一个维度,那么它的第二个维度将是L2=1
且PL2=False (2 dimensions=False)
。
L1
是参数的第一个维度大小。L3
是由下列定义的参数大小:
L1=len(LP[p])
L3=L1*L2
现在我们可以在循环的每一步中添加参数:
np+=L3 # number of parameters per tensor
我们将获得参数的总和,但我们也想看到Transformers模型的参数数量是如何计算的:
if PL2==True:
print(p,L1,L2,L3) # displaying the sizes of the parameters
if PL2==False:
print(p,L1,L3) # displaying the sizes of the parameters
print(np) # total number of parameters
请注意,如果参数只有一个维度,那么PL2=False
,那么我们只显示第一个维度。
输出是所有张量中参数数量的列表,如下所示:
0 52000 768 39936000
1 514 768 394752
2 1 768 768
3 768 768
4 768 768
5 768 768 589824
6 768 768
7 768 768 589824
8 768 768
9 768 768 589824
10 768 768
RoBERTa 模型的总参数数量显示在列表的末尾:
84,095,008
参数数量可能会随所使用的库版本而变化。
现在我们精确地知道转换模型中参数的数量代表什么。花几分钟回头看看配置的输出,参数的内容以及参数的大小。在这一点上,你将对模型的构建块有一个精确的心理表征。
程序现在构建数据集。
程序现在将逐行加载数据集以生成用于批量训练的样本,使用block_size=128
限制一个示例的长度:
#@title Step 10: Building the Dataset
%%time
from transformers import LineByLineTextDataset
dataset = LineByLineTextDataset(
tokenizer=tokenizer,
file_path="./kant.txt",
block_size=128,
)
输出显示 Hugging Face 已经投入了大量资源来优化处理数据所需的时间:
CPU times: user 8.48 s, sys: 234 ms, total: 8.71 s
Wall time: 3.88 s
墙时间,处理器实际活动的时间,已经被优化。
程序现在将定义一个数据收集器来创建一个反向传播的对象。
在初始化训练器之前,我们需要运行一个数据收集器。数据收集器将从数据集中取样并将其汇集成批次。结果类似于字典对象。
我们通过设置mlm=True
为 MLM 准备了一个批处理样本过程。
我们还将训练的蒙版标记数目设置为mlm_probability=0.15
。这将决定在预训练过程中屏蔽的标记的百分比。
现在我们使用我们的分词器初始化data_collator
,MLM 被激活,并且蒙版标记的比例设置为0.15
:
#@title Step 11: Defining a Data Collator
from transformers import DataCollatorForLanguageModeling
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)
现在我们准备好初始化训练器了。
前面的步骤已经准备好初始化训练器所需的信息。数据集已经被分词化和加载。我们的模型已经构建完成。数据收集器已经被创建。
程序现在可以初始化训练器了。出于教育目的,程序快速训练模型。训练的轮次数被限制为一个。GPU 非常方便,因为我们可以共享批次并多进程处理训练任务:
#@title Step 12: Initializing the Trainer from transformers import Trainer, TrainingArguments training_args = TrainingArguments( output_dir="./KantaiBERT", overwrite_output_dir=True, num_train_epochs=1, per_device_train_batch_size=64, save_steps=10_000, save_total_limit=2, ) trainer = Trainer( model=model, args=training_args, data_collator=data_collator, train_dataset=dataset, )
现在模型已经准备好进行训练。
一切准备就绪。通过一行代码启动训练器:
#@title Step 13: Pre-training the Model
%%time
trainer.train()
输出显示了实时的训练过程,显示了loss
、learning rate
、epoch
和步骤:
Epoch: 100%
1/1 [17:59<00:00, 1079.91s/it]
Iteration: 100%
2672/2672 [17:59<00:00, 2.47it/s]
{"loss": 5.6455852394104005, "learning_rate": 4.06437125748503e-05, "epoch": 0.18712574850299402, "step": 500}
{"loss": 4.940259679794312, "learning_rate": 3.12874251497006e-05, "epoch": 0.37425149700598803, "step": 1000}
{"loss": 4.639936000347137, "learning_rate": 2.1931137724550898e-05, "epoch": 0.561377245508982, "step": 1500}
{"loss": 4.361462069988251, "learning_rate": 1.2574850299401197e-05, "epoch": 0.7485029940119761, "step": 2000}
{"loss": 4.228510192394257, "learning_rate": 3.218562874251497e-06, "epoch": 0.9356287425149701, "step": 2500}
CPU times: user 11min 36s, sys: 6min 25s, total: 18min 2s
Wall time: 17min 59s
TrainOutput(global_step=2672, training_loss=4.7226536670130885)
模型已经训练完毕。现在是保存我们工作成果的时候了。
现在我们将保存模型和配置:
#@title Step 14: Saving the Final Model(+tokenizer + config) to disk
trainer.save_model("./KantaiBERT")
点击文件管理器中的刷新,文件应该会出现:
图 4.5:Colab 文件管理器
config.json
、pytorh_model.bin
和training_args.bin
现在应该出现在文件管理器中。
merges.txt
和vocab.json
包含数据集的预训练标记化。
我们从零开始构建了一个模型。让我们导入管道来执行一个语言建模任务,使用我们预训练的模型和分词器。
现在我们将导入一个语言建模的fill-mask
任务。我们将使用我们训练过的模型和训练过的分词器来执行 MLM:
#@title Step 15: Language Modeling with the FillMaskPipeline
from transformers import pipeline
fill_mask = pipeline(
"fill-mask",
model="./KantaiBERT",
tokenizer="./KantaiBERT"
)
现在我们可以让我们的模型像康德一样思考:
fill_mask("Human thinking involves human <mask>.")
输出可能会在每个运行中发生变化,因为我们正在从有限的数据量中从头预训练模型。然而,在这次运行中获得的输出很有趣,因为它引入了概念语言建模:
[{'score': 0.022831793874502182,
'sequence': '<s> Human thinking involves human reason.</s>',
'token': 393},
{'score': 0.011635891161859035,
'sequence': '<s> Human thinking involves human object.</s>',
'token': 394},
{'score': 0.010641072876751423,
'sequence': '<s> Human thinking involves human priori.</s>',
'token': 575},
{'score': 0.009517930448055267,
'sequence': '<s> Human thinking involves human conception.</s>',
'token': 418},
{'score': 0.00923212617635727,
'sequence': '<s> Human thinking involves human experience.</s>',
'token': 531}]
预测可能在每次运行和每次 Hugging Face 更新其模型时都会有所变化。
然而,经常会得到以下输出:
Human thinking involves human reason
这里的目标是看如何训练一个转换器模型。我们可以看到有趣的类人预测是可能的。
这些结果是实验性的,在训练过程中可能会有变化。每次重新训练模型时,它们都会改变。
这个模型需要来自其他启蒙时代思想家的更多数据。
然而,这个模型的目标是展示我们可以创建数据集来训练一个特定类型的复杂语言建模任务的转换器。
由于转换器,我们只是处于人工智能新时代的开端!
你已经从零开始训练了一个转换器模型。花些时间想象一下你可以在个人或公司环境中做些什么。你可以为特定任务创建一个数据集,并从头开始训练。利用你感兴趣的领域或公司项目来探索转换器构建工具的迷人世界!
一旦你有了喜欢的模型,就可以与 Hugging Face 社区分享。你的模型将出现在 Hugging Face 模型页面上:huggingface.co/models
你可以按照本页描述的说明,用几个步骤上传你的模型:huggingface.co/transformers/model_sharing.html
你也可以下载 Hugging Face 社区共享的模型,以获取个人和专业项目的新思路。
在这一章中,我们使用了 Hugging Face 提供的构建块从零开始构建了 KantaiBERT
,一个类似 RoBERTa 的模型转换器。
我们首先开始加载一个关于伊曼纽尔·康德作品相关主题的定制数据集。根据你的目标,你可以加载现有数据集或创建自己的数据集。我们发现使用定制数据集可以帮助我们了解转换器模型的思维方式。然而,这种实验方法是有局限性的。要训练一个超越教育目的的模型,需要更大规模的数据集。
KantaiBERT 项目被用来在kant.txt
数据集上训练一个分词器。训练好的merges.txt
和vocab.json
文件被保存。用我们预训练的文件重新创建了一个分词器。KantaiBERT 建立了定制数据集,并定义了一个数据收集器来处理反向传播的训练批次。训练器被初始化,我们详细探索了 RoBERTa 模型的参数。模型被训练并保存。
最后,保存的模型被加载,用于一个下游语言建模任务。目标是用伊曼纽尔·康德的逻辑来填补空白。
现在大门已经敞开,你可以尝试现有或定制的数据集来看看你得到了什么结果。你可以与 Hugging Face 社区分享你的模型。Transformers是数据驱动的。你可以利用这一点来发现使用Transformers的新方法。
现在,你已经准备好了解如何运行无需预训练或微调的 API 的现成Transformers引擎。 第七章, 超人Transformers与 GPT-3 引擎的崛起,将带领你走进人工智能的未来。通过学习这一章和之前的章节,你将做好准备!
在下一章,用Transformers进行下游 NLP 任务,我们将继续准备实施Transformers。
RoBERTa 使用字节级字对编码分词���。(True/False)
经过训练的 Hugging Face 分词器会生成 merges.txt
和 vocab.json
。 (True/False)
RoBERTa 不使用 token-type ID。(True/False)
DistilBERT 有 6 层和 12 个头。(True/False)
一个拥有 8000 万参数的Transformers模型是庞大的。(True/False)
我们无法训练分词器。(True/False)
BERT-风格的模型有 6 个解码器层。(True/False)
掩盖语言建模 (MLM) 预测句子中掩盖的标记中包含的单词。(True/False)
BERT-风格的模型没有自注意力子层。(True/False)
数据收集器对反向传播很有帮助。(True/False)
Yinhan Liu, Myle Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, 和 Veselin Stoyano, 2019, RoBERTa: 一个鲁棒性优化的 BERT 预训练方法:arxiv.org/abs/1907.11692
Hugging Face 分词器文档:huggingface.co/transformers/main_classes/tokenizer.html?highlight=tokenizer
Hugging Face 参考笔记本:colab.research.google.com/github/huggingface/blog/blob/master/notebooks/01_how_to_train.ipynb
Hugging Face 参考博客:colab.research.google.com/github/huggingface/blog/blob/master/notebooks/01_how_to_train.ipynb
更多 DistilBERT:arxiv.org/pdf/1910.01108.pdf
更多关于 RoBERTa:huggingface.co/transformers/model_doc/roberta.html
更多关于 DistilBERT:huggingface.co/transformers/model_doc/distilbert.html
加入该书的 Discord 工作空间,每月与作者进行问我任何事会话:
当我们释放预训练模型并观察它们在下游自然语言理解(NLU)任务中表现时,转换器展现出其全部潜力。对一个庞大的参数转换器模型进行预训练和微调需要大量时间和精力,但当我们看到一个模型在一系列 NLU 任务中发挥作用时,这些努力是值得的。
我们将从超越人类基线的探索开始这一章。人类基线代表人类在 NLU 任务上的表现。人类在幼年时学会了转导,并迅速发展出归纳思维。我们人类通过感官直接感知世界。机器智能完全依赖我们的感知被转录为文字来理解我们的语言。
我们随后将看到如何衡量转换器的性能。衡量自然语言处理(NLP)任务仍然是一种直观的方法,涉及基于真假结果的各种形式的准确性得分。这些结果是通过基准任务和数据集获得的。例如,SuperGLUE 是一个很好的例子,展示了谷歌 DeepMind、Facebook AI、纽约大学、华盛顿大学等共同努力设定了用于衡量 NLP 性能的高标准。
最后,我们将探索几个下游任务,如标准情感树库(SST-2)、语言可接受性和温格拉德模式。
转换器正在通过在设计良好的基准任务上胜过其他模型来迅速将 NLP 推向下一级。替代的转换器体系结构将继续出现和演化。
本章涵盖以下主题:
机器与人类智能的转导和归纳比较
自然语言处理的转导和归纳过程
衡量转换器性能与人类基线
测量方法(准确性、F1 分数和 MCC)
基准任务和数据集
SuperGLUE 下游任务
使用 CoLA 进行语言可接受性评估
使用 SST-2 进行情感分析
温格拉德模式
让我们首先了解人类和机器如何表示语言。
自动机器学习(AutoML)的出现,意味着自动化云 AI 平台中的 API,已经深刻改变了每个 AI 专家的工作描述。例如,Google Vertex 宣称实现 ML 所需的开发量减少了 80%。这表明任何人都可以使用现成的系统实现 ML。这是否意味着开发人员的工作减少了 80%?我认为不是。我认为工业 4.0 AI 专家会组装具有附加价值的 AI 项目。
工业 4.0。NLP AI 专家更多地投资于知识而不是源代码,以成为团队中的 AI 专家。
Transformers拥有独特的能力,可以将它们的知识应用于它们没有学习过的任务。例如,BERT Transformers通过序列到序列和掩码语言建模获得语言。然后可以对 BERT Transformers进行微调,以执行它从头开始没有学习的下游任务。
在本节中,我们将进行一次心灵实验。我们将使用Transformers的图表来表示人类和机器如何使用语言理解信息。机器以与人类不同的方式理解信息,但达到了非常高效的结果。
图 5.1,一个使用Transformers架构层和子层设计的心灵实验,显示了人类和机器之间的欺骗性相似性。让我们研究Transformers模型的学习过程,以了解下游任务:
图 5.1: 人类和 ML 方法
对于我们的例子,N=2
。这个概念性表示有两层。这两层显示了人类从一代到下一代积累的知识。机器只处理我们给它们的东西。机器将我们的输出用作输入。
在 图 5.1 的左侧,我们可以看到人类的输入是层 0 的原始事件的感知,输出是语言。我们首先作为孩子用感官感知事件。逐渐地,输出变成了潺潺的语言,然后是结构化的语言。
对于人类来说,转导 经历了一个反复试错的过程。转导意味着我们将我们感知到的结构并用模式来表示它们,例如。我们制作了我们应用于归纳思维的世界的表示。我们的归纳思维依赖于我们转导的质量。
例如,作为孩子,我们经常被迫在下午早些时候午睡。著名的儿童心理学家皮亚杰发现,这可能导致一些孩子说,例如,“我没有午睡,所以现在还不是下午。”孩子看到两个事件,用转导之间创建了链接,然后进行推理概括和归纳。
起初,人类通过转导注意到这些模式,并通过 归纳 将它们概括。我们通过反复试错来训练,以理解许多事件是相关的:
已训练的相关事件 = {日出 – 光,日落 – 黑暗,乌云 – 雨,蓝天 – 跑步,食物 – 好,火 – 温暖,雪 – 寒冷}
随着时间的推移,我们被训练去理解数百万相关事件。新一代人类不必从头开始。他们只是由上一代对许多任务进行了 微调。例如,他们被教导“火会烧伤你”。从那时起,一个孩子就知道这个知识可以被微调为任何形式的“火”:蜡烛、森林火灾、火山和每一种“火”的实例。
最后,人类将他们所知道、想象到的或预测到的一切都记录到书面 语言 中。第 0 层的输出诞生了。
对于人类来说,下一层(第 1 层)的输入是大量经过训练和微调的知识。此外,人类感知大量事件,然后通过转导、归纳、训练和微调子层以及先前的转录知识。
这些事件很多源自气味、情绪、情境、经历,以及构成人类独特性的一切。机器无法获取这种个体身份。人类对单词有一种个人的感知方式,对每个人来说都是特定的。
机器接收我们通过大量异质未经过滤的非个人数据给予它的内容。机器的目标是执行非个人的高效任务。人类的目标是个人的福祉。
我们的无限方法循环从第 0 层到第 1 层,然后回到第 0 层,带着更多的原始和处理后的信息。
结果令人着迷!我们不需要从头学习(训练)我们的母语就能获得摘要能力。我们利用预先训练的知识来调整(微调)以适应摘要任务。
Transformers通过不同的方式经历相同的过程。
机器学习基本任务,就像本章描述的任务一样,然后使用它们学习如何预测的序列来执行数百个任务。
在图 5.1的右侧,我们可以看到机器的输入是语言形式的二手信息。我们的输出是机器分析语言的唯一输入。
在人类和机器历史的这一阶段,计算机视觉识别图像,但没有包含语言的语法结构。语音识别将声音转换成单词,这将我们带回书面语言。音乐模式识别不能导致用单词表达的客观概念。
机器起步有点吃力。我们对它们施加了人为的劣势。机器必须依赖我们的随机质量语言输出来:
执行连接语言序列中所有一起出现的令牌(子词)的转导
从这些转导中建立归纳
基于令牌训练这些归纳,以产生令牌的模式
让我们在这一点上停下来,窥探一下注意子层的过程,它努力产生有效的归纳:
Transformers模型排除了以前基于循环的学习操作,并使用自注意力来提高模型的视野
注意子层在这一点上比人类有优势:它们可以处理数百万个示例来进行归纳思维操作
像我们一样,它们通过转导和归纳在序列中找到模式
它们使用存储在模型中的参数来记忆这些模式。
通过利用其能力:大量数据、优秀的 NLP Transformers算法和计算能力,它们已经获得了语言理解。由于它们对语言的深刻理解,它们已经准备好运行数百个它们未经过训练的任务。
像人类一样,Transformers通过有限数量的任务来获得语言理解。像我们一样,它们通过转导检测到连接,然后通过归纳操作对其进行概括。
当Transformers模型达到机器智能的微调子层时,它的反应就像我们一样。它不是从头开始训练以执行新任务。像我们一样,它认为这只是一个只需要微调的下游任务。如果它需要学习如何回答问题,它不会从头开始学习一种语言。Transformers模型只是像我们一样微调其参数。
在本节中,我们看到Transformers模型在学习方式上存在困难。一旦它们依赖于我们用语言转录的感知,它们就从一开始就处于劣势。然而,它们可以利用无限多的数据和大量的计算能力。
现在让我们看看如何衡量Transformers性能与人类基线。
像人类一样,Transformers可以通过继承预训练模型的属性来进行微调,以执行下游任务。预训练模型通过其参数提供其架构和语言表示。
预训练模型在关键任务上训练,以获得对语言的一般知识。微调模型在下游任务上训练。并非每个Transformers模型都使用相同的任务进行预训练。潜在地,所有任务都可以预先训练或微调。
每个 NLP 模型都需要使用标准方法进行评估。
本节首先将介绍一些关键的测量方法。然后,我们将介绍一些主要的基准任务和数据集。
让我们从一些关键的度量方法开始。
没有使用度量标准的通用测量系统,不可能将一个Transformers模型与另一个Transformers模型(或任何其他 NLP 模型)进行比较。
在本节中,我们将分析 GLUE 和 SuperGLUE 使用的三种测量评分方法。
无论您使用的是哪种变体,准确度得分都是一个实用的评估标准。得分函数为给定子集的每个结果计算一个简单的真值或假值。要么模型的输出,,与给定样本集的正确预测,,匹配,要么不匹配。基本函数为:
如果子集的结果是正确的,则我们将获得1
,如果是错误的,则为0
。
现在让我们来看看更灵活的 F1 分数。
F1 分数引入了一种更灵活的方法,可以在面对包含不均匀类分布的数据集时提供帮助。
F1 分数使用精确率和召回率的加权值。它是精确率和召回率值的加权平均值:
F1score= 2* (precision * recall)/(precision + recall)
在这个方程中,真正例(p)、假正例(p)和假负例(n)被放入精确率(P)和召回率(R)的方程中:
因此,F1 分数可以视为精确率(P)和召回率(R)的调和平均值(算术平均值的倒数):
现在让我们回顾一下 MCC 方法。
MCC 在 Fine-Tuning BERT Models 章节的 Evaluating using Matthews Correlation Coefficient 部分进行了描述和实现。MCC 计算出一个值,其中包括真正例(T[P])、真负例(T[N])、假正例(F[P])和假负例(F[N])。
MCC 可以用以下方程总结:
MCC 为二元分类模型提供了一个出色的度量标准,即使类别的大小不同也可以。
现在我们已经很好地了解了如何衡量给定 transformer 模型的结果,并将其与其他 transformer 模型或 NLP 模型进行比较。
有了测量评分方法的考虑,现在让我们来看看基准任务和数据集。
有三个前提条件需要证明Transformers已经达到了最先进的性能水平:
一个模型
一个以数据集驱动的任务
在本章的 Evaluating models with metrics 部分描述的度量标准
我们将从探索 SuperGLUE 基准开始,以说明一个 transformer 模型的评估过程。
SuperGLUE 基准是由 Wang 等人(2019 年)设计并公开的。Wang 等人(2019 年)首次设计了 General Language Understanding Evaluation (GLUE) 基准。
GLUE 基准的动机是要表明,为了有用,NLU 必须适用于广泛的任务。相对较小的 GLUE 数据集旨在鼓励 NLU 模型解决一系列任务。
然而,随着Transformers的到来,NLU 模型的性能开始超越平均人类水平,正如我们可以在 GLUE 排行榜(2021 年 12 月)中看到的。GLUE 排行榜可在 gluebenchmark.com/leaderboard
查看,展示了 NLU 天才的显著表现,保留了一些以前的 RNN/CNN 思想,同时主要关注突破性的Transformers模型。
排行榜的下面摘录显示了顶尖领导者和 GLUE 人类基线的位置:
图 5.2:GLUE 排行榜 - 2021 年 12 月
新模型和人类基线的排名将不断变化。这些排名只是给出了经典 NLP 和Transformers带领我们走向何方的一个概念!
我们首先注意到 GLUE 人类基线并不处于顶级位置,这表明自然语言理解模型已经在 GLUE 任务上超越了非专业人类。人类基线代表了我们人类能够达到的水平。人工智能现在能够胜过人类。到了 2021 年 12 月,人类基线仅排在第 17 位。这是一个问题。如果没有一个要超越的标准,就很难盲目地寻找基准数据集来改进我们的模型。
我们还注意到 transformer 模型已经领先。
我喜欢将 GLUE 和 SuperGLUE 看作是语言理解从混乱到有序的转折点。对我来说,理解是让词语彼此契合并形成一种语言的粘合剂。
随着自然语言理解的进步,GLUE 排行榜将不断发展。然而,Wang 等人(2019)引入 SuperGLUE 是为了为人类基线设立更高的标准。
Wang 等人(2019)认识到了 GLUE 的局限性。他们为更困难的自然语言理解任务设计了 SuperGLUE。
SuperGLUE 立即将人类基线重新确定为第一名(2020 年 12 月),如下面排行榜摘录所示,super.gluebenchmark.com/leaderboard
:
图 5.3:SuperGLUE 排行榜 2.0 – 2020 年 12 月
然而,随着我们生产出更好的自然语言理解(NLU)模型,SuperGLUE 排行榜也在发展。在 2021 年,transformers 已经超过了人类基线。到了 2021 年 12 月,人类基线已经下降到第 5 位,如 图 5.4 所示:
图 5.4:SuperGLUE 排行榜 2.0 – 2021 年 12 月
随着新的创新模型的到来,人工智能算法排名将不断变化。这些排名只是给出了自然语言处理至高无上的战斗有多么艰难的一个概念!
现在让我们看看评估过程是如何工作的。
Wang 等人(2019)为他们的 SuperGLUE 基准选择了八个任务。这些任务的选择标准比 GLUE 更严格。例如,这些任务不仅要理解文本,还要进行推理。推理的水平不是顶级专家的水平。然而,性能水平已足以替代许多人类任务。
这八个 SuperGLUE 任务被呈现在一个现成的列表中:
图 5.5:SuperGLUE 任务
任务列表是交互式的:super.gluebenchmark.com/tasks
。
每个任务都包含执行该任务所需信息的链接:
名称 是一个微调、预训练模型的下游任务的名称
标识符 是名称的缩写或简称
下载 是数据集的下载链接
更多信息 通过链接到设计数据集驱动任务的团队的论文或网站提供更多细节
度量 是用于评估模型的测量分数
SuperGLUE 提供任务说明、软件、数据集以及描述要解决问题的论文或网站。一旦团队运行基准任务并达到排行榜,结果将显示如下:
图 5.6:SuperGLUE 任务分数
SuperGLUE 显示了整体评分以及每个任务的评分。
例如,让我们看看王等人(2019 年)在他们的论文的表 6中为选择合理答案(COPA)任务提供的说明。
第一步是阅读由Roemmele等人(2011 年)撰写的杰出论文。简言之,目标是让 NLU 模型展示它的机器思考能力(当然不是人类思考)。在我们的案例中,transformer 必须选择最合理的答案来回答问题。数据集提供前提,transformer 模型必须找出最合理的答案。
例如:
前提:我敲了敲邻居的门。
结果是什么?
另一种选择:我的邻居邀请我进去了。
另一种选择:我的邻居离开了他的房子。
这个问题需要人类花上一两秒钟来回答,这表明它需要一些常识机器思考。COPA.zip
是一个可以直接从 SuperGLUE 任务页面下载的现成数据集,所提供的度量使得这个过程对于所有参与基准竞赛的人员都是公平和可靠的。
这些例子可能看起来很困难。然而,如果我们考虑所有的任务,transformers 接近了 COPA 人类基准线(图 5.7),它只排在第 5 名:
图 5.7:COPA 的 SuperGLUE 结果
看上去难以置信,transformers 在很短的时间内就攀登到了排行榜梯子上!而且这只是一个开始。新的想法几乎每个月都在出现!
我们已经介绍了 COPA。让我们定义另外七个 SuperGLUE 基准任务。
一个任务可以是一个用于生成训练模型的预训练任务。同一个任务可以是另一个模型的下游任务,该模型将对其进行微调。然而,SuperGLUE 的目标是展示给定的 NLU 模型可以执行多个下游任务并进行微调。多任务模型证明了 transformers 的思考能力。
任何 transformer 的力量都在于它能够使用预训练模型执行多个任务,然后将其应用于微调的下游任务。原始 Transformer 模型及其变体现在所有 GLUE 和 SuperGLUE 任务中处于领先地位。我们将继续关注 SuperGLUE 下游任务,而人类基准线很难击败。
在上一部分,我们介绍了 COPA。在本节中,我们将介绍王等人(2019 年)在其论文的表 2中定义的其他七个任务。
让我们继续进行一个布尔问题任务。
BoolQ 是一个布尔型的是或否回答任务。如在 SuperGLUE 中所定义的,数据集包含 15,942 个自然发生的例子。train.jsonl
数据集的第 3 行原始样本包含一段文章、一个问题和答案(true
):
{`"question"`: "is windows movie maker part of windows essentials"
`"passage"`: "Windows Movie Maker -- Windows Movie Maker (formerly known as Windows Live Movie Maker in Windows 7) is a discontinued video editing software by Microsoft. It is a part of Windows Essentials software suite and offers the ability to create and edit videos as well as to publish them on OneDrive, Facebook, Vimeo, YouTube, and Flickr.", "idx": 2, "label": true}
提供的数据集可能会随时间改变,但概念保持不变。
现在,让我们来看看 CB,这是一个需要人类和机器共同关注的任务。
承诺银行(CB)是一个困难的蕴含任务。我们要求Transformers模型阅读一个前提,然后检验基于前提构建的一个假设。例如,假设会确认前提或与之矛盾。然后Transformers模型必须将假设标记为中性、蕴含或矛盾,例如。
数据集包含自然语篇。
以下示例中的 #77
,取自 train.jsonl
训练数据集,展示了 CB 任务的难度:
{`"premise"`: "The Susweca. It means ''dragonfly'' in Sioux, you know. Did I ever tell you that's where Paul and I met?"
`"hypothesis"`: "Susweca is where she and Paul met,"
`"label"`: `"entailment"`, "idx": 77}
我们现在来看看多句问题。
多句阅读理解(MultiRC)要求模型阅读一段文本,并从几个可能的选择中作出选择。这个任务对人类和机器来说都很困难。模型需要面对一个文本,多个问题,以及每个问题可能对应的答案,并带有 0
(错误)或 1
(正确)的标签。
让我们来看 train.jsonl
中的第二个样本:
"Text": "text": "The rally took place on October 17, the shooting on February 29\. Again, standard filmmaking techniques are interpreted as smooth distortion: \"Moore works by depriving you of context and guiding your mind to fill the vacuum -- with completely false ideas. It is brilliantly, if unethically, done.\" As noted above, the \"from my cold dead hands\" part is simply Moore's way to introduce Heston. Did anyone but Moore's critics view it as anything else? He certainly does not \"attribute it to a speech where it was not uttered\" and, as noted above, doing so twice would make no sense whatsoever if Moore was the mastermind deceiver that his critics claim he is. Concerning the Georgetown Hoya interview where Heston was asked about Rolland, you write: \"There is no indication that [Heston] recognized Kayla Rolland's case.\" This is naive to the extreme -- Heston would not be president of the NRA if he was not kept up to date on the most prominent cases of gun violence. Even if he did not respond to that part of the interview, he certainly knew about the case at that point. Regarding the NRA website excerpt about the case and the highlighting of the phrase \"48 hours after Kayla Rolland is pronounced dead\": This is one valid criticism, but far from the deliberate distortion you make it out to be; rather, it is an example for how the facts can sometimes be easy to miss with Moore's fast pace editing. The reason the sentence is highlighted is not to deceive the viewer into believing that Heston hurried to Flint to immediately hold a rally there (as will become quite obvious), but simply to highlight the first mention of the name \"Kayla Rolland\" in the text, which is in this paragraph. "
样本包含四个问题。为了说明这个任务,我们只研究其中两个。模型必须预测正确的标签。请注意,模型被要求获取的信息分布在整个文本中:
`"question"`: "When was Kayla Rolland shot?"
`"answers"`:
[{"text": "February 17", "idx": 168, "label": 0},
`{"text": "February 29", "idx": 169, "label": 1},`
{"text": "October 29", "idx": 170, "label": 0},
{"text": "October 17", "idx": 171, "label": 0},
{"text": "February 17", "idx": 172, "label": 0}], "idx": 26},
`{"question"`: "Who was president of the NRA on February 29?",
`"answers": [{"text": "Charleton Heston", "idx": 173, "label": 1}`,
{"text": "Moore", "idx": 174, "label": 0},
{"text": "George Hoya", "idx": 175, "label": 0},
{"text": "Rolland", "idx": 176, "label": 0},
{"text": "Hoya", "idx": 177, "label": 0}, {"text": "Kayla", "idx": 178, "label": 0}], "idx": 27},
在这一点上,人们只能欣赏一个优秀的、经过精确调校的预训练模型在这些困难的下游任务上的表现。
现在,让我们来看看阅读理解任务。
通用常识推理数据集(ReCoRD)代表着另一个具有挑战性的任务。该数据集包含来自超过 70,000 篇新闻文章的 120,000 多个查询。Transformers必须运用常识推理来解决这个问题。
让我们来查看 train.jsonl
中的一个样本:
`"source"`: "Daily mail"
A passage contains the text and indications as to where the entities are located.
A passage begins with the text:
`"passage"`: {
`"text"`: "A Peruvian tribe once revered by the Inca's for their fierce hunting skills and formidable warriors are clinging on to their traditional existence in the coca growing valleys of South America, sharing their land with drug traffickers, rebels and illegal loggers. Ashaninka Indians are the largest group of indigenous people in the mountainous nation's Amazon region, but their settlements are so sparse that they now make up less than one per cent of Peru's 30 million population. Ever since they battled rival tribes for territory and food during native rule in the rainforests of South America, the Ashaninka have rarely known peace.\n@highlight\nThe Ashaninka tribe once shared the Amazon with the like of the Incas hundreds of years ago\n@highlight\nThey have been forced to share their land after years of conflict forced rebels and drug dealers into the forest\n@highlight\n. Despite settling in valleys rich with valuable coca, they live a poor pre-industrial existence",
实体如下摘录所示:
`"entities": [{"start": 2,"end": 9}, …,"start": 711,"end": 715}]`
最后,模型必须通过找到适当的值来回答一个查询,填写占位符:
{`"``query"`: "Innocence of youth: Many of the `@placeholder's` younger generations have turned their backs on tribal life and moved to the cities where living conditions are better",
`"answers"`:[{"start":263,"end":271,"text":"Ashaninka"},{"start":601,"end":609,"text":"Ashaninka"},{"start":651,"end":659,"text":"Ashaninka"}],"idx":9}],"idx":3}
一旦Transformers模型经历了这个问题,它现在必须面对一个包含的任务。
对于文本蕴涵识别(RTE),Transformers模型必须阅读前提,检查一个假设,并预测蕴含假设状态的标签。
让我们来看看 train.jsonl
数据集的样本 #19
:
{`"premise"`: "U.S. crude settled $1.32 lower at $42.83 a barrel.",
`"hypothesis"`: "Crude the light American lowered to the closing 1.32 dollars, to 42.83 dollars the barrel.", "label": `"not_entailment"`, >"idx": 19}
RTE 需要理解和逻辑推理。现在让我们来看 Words in Context 任务。
上下文中的词语(WiC)和以下的 Winograd 任务测试模型处理模棱两可的词语的能力。在 WiC 中,多任务 transformer 必须分析两个句子,并确定目标词在两个句子中是否具有相同的含义。
让我们检查train.jsonl
数据集的第一个样本。
首先指定目标词:
"word": `"place"`
模型必须读取包含目标词的两个句子:
"sentence1": "Do you want to come over to my place later?",
"sentence2": "A political system with no `place` for the less prominent groups."
train.jsonl
指定了样本索引、标签的值以及sentence1(start1, end1)
和sentence2(start2, end2)
中目标词的位置:
"idx": 0,
"label": false,
"start1": 31,
"start2": 27,
"end1": 36,
"end2": 32,
在完成这个艰巨的任务后,transformer 模型将面临 Winograd 任务。
Winograd 模式任务以 Terry Winograd 命名。如果一个 transformer 经过良好训练,它应该能够解决消歧问题。
该数据集包含针对代词性别细微差异的句子。
这构成了一个共指解析问题,这是执行的最具挑战性的任务之一。然而,允许自注意的 transformer 架构对于这个任务来说是理想的。
每个句子包含一个职业、一个参与者和一个代词。要解决的问题是找出代词是否与职业或参与者共指。
让我们看看从train.jsonl
中取出的样本。
首先,样本要求模型阅读一段文本:
{`"text"`: >"I poured water from the bottle into the cup until it was full.",
The WSC ask the model to find the target pronoun token number 10 starting at 0:
`"target"`: {`"span2_index"`: 10,
Then it asks the model to determine if "it" refers to "the cup" or not:
`"span1_index"`: 7,
`"span1_text"`: "the cup",
`"span2_text"`: "it"},
For sample index #4, the label is true:
"idx": 4, `"label"`: true}
我们已经了解了一些主要的 SuperGLUE 任务。还有许多其他任务。
然而,一旦你了解 transformer 的架构和基准任务的机制,你将迅速适应任何模型和基准。
现在让我们运行一些下游任务。
在本节中,我们将随便选几辆 transformer 车,开一下看看它们是如何运行的。有许多模型和任务。我们将在本节运行其中几个。一旦你了解了运行几个任务的过程,你会很快理解所有任务。毕竟,所有这些任务的人类基线就是我们自己!
下游任务是从预训练 transformer 模型继承模型和参数的微调 transformer 任务。
因此,下游任务是一个从预训练模型运行微调任务的视角。这意味着,根据模型的不同,如果任务没有用于完全预训练该模型,则该任务是下游任务。在本节中,我们将考虑所有任务都是下游任务,因为我们没有预训练它们。
模型将会发展,数据库也会发展,基准方法、准确性测量方法和排行榜标准也会发展。但是反映在本章的下游任务中的人类思维结构将会保持不变。
让我们从 CoLA 开始。
语言接受性语料库 (CoLA),一个 GLUE 任务,gluebenchmark.com/tasks
,包含了标记为语法接受性的英语句子的成千上万个样本。
Alex Warstadt等人(2019)的目标是评估 NLP 模型对句子的语言能力,以判断句子的语言可接受性。NLP 模型应该按照预期对句子进行分类。
句子被标记为语法正确或不符合语法规范。如果句子不符合语法规范,则标记为 0
。如果句子在语法上是正确的,则标记为 1
。例如:
分类 = 1
代表 ‘我们喊得嘶哑了.’
分类 = 0
代表 ‘我们在喊.’
你可以在 第三章,Fine-Tuning BERT Models 中查看我们在 CoLA 数据集上对 BERT 模型进行微调的内容,打开 BERT_Fine_Tuning_Sentence_Classification_GPU.ipynb
。我们使用了 CoLA 数据:
#@title Loading the Dataset
#source of dataset : https://nyu-mll.github.io/CoLA/
df = pd.read_csv("in_domain_train.tsv", delimiter='\t', header=None, names=['sentence_source', 'label', 'label_notes', 'sentence'])
df.shape
我们还加载了一个预训练的 BERT 模型:
#@title Loading the Hugging Face Bert Uncased Base Model
model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
最后,我们使用的度量方法是 MCC,在 第三章,Fine-Tuning BERT Models 的 使用 Matthews 相关系数进行评估 部分和本章中有详细描述。
如果需要,您可以参考该部分以获取 MCC 的数学描述,并有必要时重新运行源代码。
一句话在语法上可能不可接受,但仍然传达情绪。情感分析可以为机器增加某种程度的同理心。
斯坦福情感树库(SST-2)包含电影评论。在这一部分,我们将描述 SST-2(二元分类)任务。然而,数据集远不止如此,可以将情感分类为 0(负面)到 n(正面)。
Socher等人(2013)将情感分析发展到了超越二元积极-消极 NLP 分类的程度。我们将在 第十二章,检测客户情绪以做出预测 中使用 Transformer 模型探索 SST-2 多标签情感分类。
在这一部分,我们将在 Hugging Face transformer pipeline 模型上运行从 SST 中取出的样本以说明二元分类。
打开Transformer_tasks.ipynb
并运行以下单元格,其中包含从SST获取的正面和负面电影评论:
#@title SST-2 Binary Classification
from transformers import pipeline
nlp = pipeline("sentiment-analysis")
print(nlp("If you sometimes like to go to the movies to have fun , Wasabi is a good place to start."),"If you sometimes like to go to the movies to have fun , Wasabi is a good place to start.")
print(nlp("Effective but too-tepid biopic."),"Effective but too-tepid biopic.")
输出是准确的:
[{'label': 'POSITIVE', 'score': 0.999825656414032}] If you sometimes like to go to the movies to have fun , Wasabi is a good place to start .
[{'label': 'NEGATIVE', 'score': 0.9974064230918884}] Effective but too-tepid biopic.
SST-2 任务使用准确度指标进行评估。
我们对一个序列的情感进行分类。现在让我们看看一个序列中的两个句子是否是释义。
微软研究释义语料库(MRPC),一个 GLUE 任务,包含从网上新闻来源中提取的句子对。每对句子都经过人工标注,标明句子是否基于两个密切相关属性等价:
解释等价
语义等价
让我们使用 Hugging Face BERT 模型运行一个样本。打开Transformer_tasks.ipynb
并前往以下单元格,然后运行从 MRPC 中提取的样本:
#@title Sequence Classification : paraphrase classification
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
import tensorflow as tf
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased-finetuned-mrpc")
model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased-finetuned-mrpc")
classes = ["not paraphrase", "is paraphrase"]
sequence_A = "The DVD-CCA then appealed to the state Supreme Court."
sequence_B = "The DVD CCA appealed that decision to the U.S. Supreme Court."
paraphrase = tokenizer.encode_plus(sequence_A, sequence_B, return_tensors="tf")
paraphrase_classification_logits = model(paraphrase)[0]
paraphrase_results = tf.nn.softmax(paraphrase_classification_logits, axis=1).numpy()[0]
print(sequence_B, "should be a paraphrase")
for i in range(len(classes)):
print(f"{classes[i]}: {round(paraphrase_results[i] * 100)}%")
输出是准确的,尽管可能会收到警告消息,表明模型需要更多的下游训练:
The DVD CCA appealed that decision to the U.S. Supreme Court. should be a paraphrase
not paraphrase: 8.0%
is paraphrase: 92.0%
MRPC 任务使用 F1/准确率分数方法进行度量。
现在让我们运行一个Winograd schema。
我们在本章的*The Winograd schema challenge (WSC)*部分描述了 Winograd 模式。训练集是英文的。
但是,如果我们要求Transformers模型解决英法翻译中的代词性别问题会发生什么?法语对具有语法性别(阴性、阳性)的名词有不同的拼写。
以下句子包含代词it,可以指代car或garage。Transformers能消除这个代词的歧义吗?
打开Transformer_tasks.ipynb
,转到#Winograd
单元格,并运行我们的示例:
#@title Winograd
from transformers import pipeline
translator = pipeline("translation_en_to_fr")
print(translator("The car could not go in the garage because it was too big.", max_length=40))
翻译是完美的:
[{'translation_text': "La voiture ne pouvait pas aller dans le garage parce qu'elle était trop grosse."}]
Transformers检测到单词it指的是单词car,这是女性形式。女性形式适用于it和形容词big:
elle在法语中意味着she,这是it的翻译。阳性形式将是il,意思是he。
grosse是单词big的翻译的女性形式。否则,阳性形式将是gros。
我们给Transformers一个困难的 Winograd 模式来解决,它给出了正确的答案。
还有许多基于数据集的 NLU 任务可用。我们将在本书中探索其中一些,以将更多构建块添加到我们的 transformer 工具箱中。
本章分析了人类语言表征过程与机器智能执行传导的方式之间的差异。我们看到,Transformers必须依赖于我们在书面语言中表达的极其复杂的思维过程的输出。语言仍然是表达大量信息的最精确的方式。机器没有感觉,必须将语音转换为文本,以从原始数据集中提取含义。
然后,我们探讨了如何衡量多任务Transformers的性能。Transformers在下游任务中获得顶级结果的能力在 NLP 历史上是独一无二的。我们经历了许多艰难的 SuperGLUE 任务,这些任务将Transformers带到了 GLUE 和 SuperGLUE 排行榜的前列。
BoolQ、CB、WiC 等我们涵盖的许多任务,甚至对人类来说都不容易处理。我们通过几个下游任务的示例展示了Transformers模型在证明其效率时面临的困难。
Transformers已经通过胜过以前的 NLU 架构证明了其价值。为了说明实施下游微调任务是多么简单,我们在 Google Colaboratory 笔记本中使用 Hugging Face 的 transformers 管道运行了几个任务。
在Winograd 模式中,我们给Transformers一个困难的任务,即解决英法翻译中的 Winograd 消歧问题。
在下一章,第六章,使用Transformers进行机器翻译,我们将进一步开展翻译任务,并使用 Trax 构建翻译模型。
机器智能使用与人类相同的数据进行预测。(是/否)
对于 NLP 模型来说,SuperGLUE 比 GLUE 更难。(是/否)
BoolQ 期望一个二进制答案。(True/False)
WiC 代表上下文中的单词。(True/False)
文本蕴含识别(RTE)检测一个序列是否蕴含另一个序列。(True/False)
Winograd 模式预测动词是否拼写正确。(True/False)
Transformer 模型现在占据了 GLUE 和 SuperGLUE 的前几名。(True/False)
人类基准标准并非一劳永逸。它们被 SuperGLUE 制定得更难达到。(True/False)
Transformer 模型永远不会达到 SuperGLUE 人类基准标准。(True/False)
变种的 Transformer 模型已经超过了 RNN 和 CNN 模型。(True/False)
Alex Wang、Yada Pruksachatkun、Nikita Nangia、Amanpreet Singh、Julian Michael、Felix Hill、Omer Levy、Samuel R. Bowman,2019 年,《SuperGLUE:通用语言理解系统的更棘手的基准》:w4ngatang.github.io/static/papers/superglue.pdf
Alex Wang, Yada Pruksachatkun, Nikita Nangia, Amanpreet Singh, Julian Michael, Felix Hill, Omer Levy, Samuel R. Bowman, 2019, GLUE:用于自然语言理解的多任务基准和分析平台
Yu Sun、Shuohuan Wang、Yukun Li、Shikun Feng、Hao Tian、Hua Wu、Haifeng Wang,2019 年,《ERNIE 2.0:语言理解的持续预训练框架》:arxiv.org/pdf/1907.12412.pdf
Melissa Roemmele、Cosmin Adrian Bejan和Andrew S. Gordon,2011 年,《可选择的合理替代方案:常识因果推理评估》:people.ict.usc.edu/~gordon/publications/AAAI-SPRING11A.PDF
Richard Socher、Alex Perelygin、Jean Y. Wu、Jason Chuang、Christopher D. Manning、Andrew Y. Ng和Christopher Potts,2013 年,《递归深度模型用于情感树库的语义组合》:nlp.stanford.edu/~socherr/EMNLP2013_RNTN.pdf
Thomas Wolf、Lysandre Debut、Victor Sanh、Julien Chaumond、Clement Delangue、Anthony Moi、Pierric Cistac、Tim Rault、Rémi Louf、Morgan Funtowicz和Jamie Brew,2019 年,《HuggingFace 的 Transformer:最先进的自然语言处理》:arxiv.org/abs/1910.03771
Hugging Face Transformer 用法: huggingface.co/transformers/usage.html
加入该书的 Discord 工作区,与作者进行每月的问我任何事会话:
人类精通序列转换,将表示转移到另一个对象上。我们可以轻松想象一个序列的心理表示。如果有人说我的花园里的花很漂亮,我们可以轻松地想象出一个有花的花园。我们看到花园的图像,尽管我们可能从未见过那个花园。我们甚至可能想象出鸟鸣和花香。
机器必须从头开始学习用数字表示进行转换。循环或卷积方法产生了有趣的结果,但还没有达到显著的 BLEU 翻译评估分数。翻译需要将语言 A 的表示转换成语言 B。
Transformers模型的自注意创新提高了机器智能的分析能力。在尝试将语言 A 的序列翻译成语言 B 之前,必须对其进行充分的表示。自注意带来了机器获得更好 BLEU 分数所需的智能水平。
注意力全靠你 Transformers在 2017 年的英语-德语和英语-法语翻译中取得了最佳结果。从那以后,其他Transformers的得分已经有所提高。
在本书的这一部分,我们已经涵盖了Transformers的基本方面:Transformer 的 架构,从头开始 训练 一个 RoBERTa 模型,微调 一个 BERT,评估 一个经过微调的 BERT,并探索一些Transformers示例中的 下游任务。
在本章中,我们将通过三个额外的主题来介绍机器翻译。我们首先会定义什么是机器翻译。然后,我们将预处理一个机器翻译研讨会(WMT)数据集。最后,我们将看到如何实现机器翻译。
本章涵盖以下主题:
定义机器翻译
人类转换和翻译
机器转换和翻译
预处理 WMT 数据集
使用 BLEU 评估机器翻译
几何评估
Chencherry 平滑
介绍 Google 翻译的 API
使用 Trax 初始化英语-德语问题
我们的第一步将是定义机器翻译。
Vaswani 等人(2017 年)在设计 Transformer 时解决了 NLP 中最困难的问题之一。机器翻译的人类基线似乎对我们这些人类-机器智能设计者来说是遥不可及的。这并没有阻止 Vaswani 等人(2017 年)发布 Transformer 的架构并取得最先进的 BLEU 结果。
在本节中,我们将定义机器翻译。机器翻译是通过机器转换和输出来复制人类翻译的过程:
图 6.1:机器翻译过程
图 6.1 中的一般思想是让机器在几个步骤中执行以下操作:
选择要翻译的句子
学习单词如何相互关联以及数以亿计的参数
学习单词之间相互指代的多种方式
使用机器传导将学习的参数传递给新的序列
为单词或序列选择候选翻译
这个过程总是以从源语言A翻译的句子开始。过程以包含语言B中翻译的句子的输出结束。中间计算涉及传导。
例如,欧洲议会的人类口译员不会逐字逐句地翻译句子。逐字翻译通常毫无意义,因为它们缺乏适当的语法结构,并且无法产生正确的翻译,因为忽略了每个词的上下文。
人类传导将语言A中的一句话构建成一种认知表征这句话的含义。欧洲议会的口译员(口头翻译)或翻译员(书面翻译)只有在此之后才会将该传导转化为语言B中对该句话的解释。
我们将口译员或翻译员在语言B中完成的翻译称为参考句子。
在图 6.1中描述的机器翻译过程中,您会注意到几个参考资料。
人类翻译员在现实生活中不会将句子A翻译为句子B多次,而只会翻译一次。然而,在现实生活中可能会有不止一个翻译员翻译句子A。例如,你可以找到蒙田的《论文》的多个法语到英语的翻译。如果你从原始法语版本中取出句子A,你会发现被标注为引用1
到n
的句子B的几个版本。
如果有一天你去欧洲议会,你可能会注意到口译员只会在有限的时间内翻译两个小时,例如。然后另一个口译员接替。没有两个口译员有相同的风格,就像作家有不同的风格一样。源语言中的句子A可能由同一个人在一天中重复几次,但会被翻译成几个参考句子B版本:
参考 = {参考 1, 参考 2,…参考 n}
机器必须找到一种以与人类翻译员相同的方式思考的方法。
原始 Transformer 架构的传导过程使用编码器堆栈、解码器堆栈和所有模型参数来表示一个参考序列。我们将称之为参考的输出序列。
为什么不直接说“输出预测”?问题在于没有单一的输出预测。Transformer,就像人类一样,将产生一个我们可以参考的结果,但如果我们对其进行不同的训练或使用不同的 Transformer 模型,结果可能会改变!
我们立即意识到人类语言序列转换的基线是一个相当大的挑战。然而,已经取得了很大的进步。
机器翻译的评估证明了自然语言处理的进步。为了确定一个解决方案是否比另一个更好,每个自然语言处理挑战者、实验室或组织必须参考相同的数据集,以便比较是有效的。
现在让我们探索一个 WMT 数据集。
Vaswani 等人(2017)展示了 Transformer 在 WMT 2014 英译德和 WMT 2014 英译法任务上的成就。Transformer 实现了最先进的 BLEU 分数。BLEU 将在本章的使用 BLEU 评估机器翻译部分进行描述。
2014 年的 WMT 包含了几个欧洲语言数据集。其中一个数据集包含了从欧洲议会语料库第 7 版中获取的数据。我们将使用来自欧洲议会会议平行语料库1996-2011 年的法语-英语数据集(www.statmt.org/europarl/v7/fr-en.tgz
)。
一旦你下载了文件并解压缩它们,我们将预处理这两个平行文件:
europarl-v7.fr-en.en
europarl-v7.fr-en.fr
我们将加载、清理并缩小语料库的大小。
让我们开始预处理。
在本节中,我们将预处理europarl-v7.fr-en.en
和europarl-v7.fr-en.fr
。
打开本章的 GitHub 目录中的read.py
。确保两个 europarl 文件与read.py
在同一目录中。
程序开始使用标准的 Python 函数和pickle
来转储序列化输出文件:
import pickle
from pickle import dump
然后我们定义将文件加载到内存中的函数:
# load doc into memory
def load_doc(filename):
# open the file as read only
file = open(filename, mode='rt', encoding='utf-8')
# read all text
text = file.read()
# close the file
file.close()
return text
文档加载后被分割成句子:
# split a loaded document into sentences
def to_sentences(doc):
return doc.strip().split('\n')
检索到最短和最长的长度:
# shortest and longest sentence lengths
def sentence_lengths(sentences):
lengths = [len(s.split()) for s in sentences]
return min(lengths), max(lengths)
导入的句子行必须经过清理以避免训练无用和嘈杂的标记。行被规范化,以空格分词,并转换为小写。从每个标记中删除标点符号,删除不可打印的字符,并排除包含数字的标记。清理后的行被存储为字符串。
程序运行清理函数并返回干净的附加字符串:
# clean lines import re import string import unicodedata def clean_lines(lines): cleaned = list() # prepare regex for char filtering re_print = re.compile('[^%s]' % re.escape(string.printable)) # prepare translation table for removing punctuation table = str.maketrans('', '', string.punctuation) for line in lines: # normalize unicode characters line = unicodedata.normalize('NFD', line).encode('ascii', 'ignore') line = line.decode('UTF-8') # tokenize on white space line = line.split() # convert to lower case line = [word.lower() for word in line] # remove punctuation from each token line = [word.translate(table) for word in line] # remove non-printable chars form each token line = [re_print.sub('', w) for w in line] # remove tokens with numbers in them line = [word for word in line if word.isalpha()] # store as string cleaned.append(' '.join(line)) return cleaned
我们已经定义了将调用来准备数据集的关键函数。首先加载并清理英文数据:
# load English data
filename = 'europarl-v7.fr-en.en'
doc = load_doc(filename)
sentences = to_sentences(doc)
minlen, maxlen = sentence_lengths(sentences)
print('English data: sentences=%d, min=%d, max=%d' % (len(sentences), minlen, maxlen))
cleanf=clean_lines(sentences)
数据集现在已经干净,pickle
将其转储到名为English.pkl
的序列化文件中:
filename = 'English.pkl'
outfile = open(filename,'wb')
pickle.dump(cleanf,outfile)
outfile.close()
print(filename," saved")
输出显示关键统计信息,并确认English.pkl
已保存:
English data: sentences=2007723, min=0, max=668
English.pkl saved
现在我们用相同的过程处理法语数据,并将其转储到名为French.pkl
的序列化文件中:
# load French data
filename = 'europarl-v7.fr-en.fr'
doc = load_doc(filename)
sentences = to_sentences(doc)
minlen, maxlen = sentence_lengths(sentences)
print('French data: sentences=%d, min=%d, max=%d' % (len(sentences), minlen, maxlen))
cleanf=clean_lines(sentences)
filename = 'French.pkl'
outfile = open(filename,'wb')
pickle.dump(cleanf,outfile)
outfile.close()
print(filename," saved")
输出显示法语数据集的关键统计信息,并确认French.pkl
已保存。
French data: sentences=2007723, min=0, max=693
French.pkl saved
主要的预处理已经完成。但我们仍需要确保数据集不包含嘈杂和混乱的标记。
现在,在与read.py
相同目录中打开read_clean.py
。我们的流程现在定义了一个函数,该函数将加载前一部分清理过的数据集,然后在预处理完成后保存它们:
from pickle import load
from pickle import dump
from collections import Counter
# load a clean dataset
def load_clean_sentences(filename):
return load(open(filename, 'rb'))
# save a list of clean sentences to file
def save_clean_sentences(sentences, filename):
dump(sentences, open(filename, 'wb'))
print('Saved: %s' % filename)
我们现在定义一个函数,该函数将创建一个词汇计数器。知道我们将解析的序列中每个单词的使用次数非常重要。例如,如果一个词在包含两百万行的数据集中只使用一次,我们将浪费我们的能量使用宝贵的 GPU 资源来学习它。让我们定义计数器:
# create a frequency table for all words
def to_vocab(lines):
vocab = Counter()
for line in lines:
tokens = line.split()
vocab.update(tokens)
return vocab
词汇计数器将检测频率低于min_occurrence
的单词:
# remove all words with a frequency below a threshold
def trim_vocab(vocab, min_occurrence):
tokens = [k for k,c in vocab.items() if c >= min_occurrence]
return set(tokens)
在这种情况下,min_occurrence=5
,低于或等于此阈值的单词已被移除,以避免浪费训练模型分析它们的时间。
现在我们必须处理Out-Of-Vocabulary(OOV)单词。 OOV 词可以是拼写错误的单词,缩写词或任何不符合标准词汇表示的单词。我们可以使用自动拼写,但它不会解决所有问题。在本例中,我们将简单地用unk
(未知)标记替换 OOV 词:
# mark all OOV with "unk" for all lines
def update_dataset(lines, vocab):
new_lines = list()
for line in lines:
new_tokens = list()
for token in line.split():
if token in vocab:
new_tokens.append(token)
else:
new_tokens.append('unk')
new_line = ' '.join(new_tokens)
new_lines.append(new_line)
return new_lines
现在我们将运行针对英语数据集的函数,保存输出,然后显示20
行:
# load English dataset filename = 'English.pkl' lines = load_clean_sentences(filename) # calculate vocabulary vocab = to_vocab(lines) print('English Vocabulary: %d' % len(vocab)) # reduce vocabulary vocab = trim_vocab(vocab, 5) print('New English Vocabulary: %d' % len(vocab)) # mark out of vocabulary words lines = update_dataset(lines, vocab) # save updated dataset filename = 'english_vocab.pkl' save_clean_sentences(lines, filename) # spot check for i in range(20): print("line",i,":",lines[i])
输出函数首先显示了获得的词汇压缩:
English Vocabulary: 105357
New English Vocabulary: 41746
Saved: english_vocab.pkl
预处理的数据集已保存。输出函数然后显示20
行,如下摘录所示:
line 0 : resumption of the session
line 1 : i declare resumed the session of the european parliament adjourned on friday december and i would like once again to wish you a happy new year in the hope that you enjoyed a pleasant festive period
line 2 : although, as you will have seen, the dreaded millennium bug failed to materialise still the people in a number of countries suffered a series of natural disasters that truly were dreadful
line 3 : you have requested a debate on this subject in the course of the next few days during this partsession
现在让我们运行针对法语数据集的函数,保存输出,然后显示20
行:
# load French dataset filename = 'French.pkl' lines = load_clean_sentences(filename) # calculate vocabulary vocab = to_vocab(lines) print('French Vocabulary: %d' % len(vocab)) # reduce vocabulary vocab = trim_vocab(vocab, 5) print('New French Vocabulary: %d' % len(vocab)) # mark out of vocabulary words lines = update_dataset(lines, vocab) # save updated dataset filename = 'french_vocab.pkl' save_clean_sentences(lines, filename) # spot check for i in range(20): print("line",i,":",lines[i])
输出函数首先显示了获得的词汇压缩:
French Vocabulary: 141642
New French Vocabulary: 58800
Saved: french_vocab.pkl
预处理的数据集已保存。输出函数然后显示20
行,如下摘录所示:
line 0 : reprise de la session
line 1 : je declare reprise la session du parlement europeen qui avait ete interrompue le vendredi decembre dernier et je vous renouvelle tous mes vux en esperant que vous avez passe de bonnes vacances
line 2 : comme vous avez pu le constater le grand bogue de lan ne sest pas produit en revanche les citoyens dun certain nombre de nos pays ont ete victimes de catastrophes naturelles qui ont vraiment ete terribles
line 3 : vous avez souhaite un debat a ce sujet dans les prochains jours au cours de cette periode de session
此部分显示了在训练之前必须处理原始数据的方法。数据集现在已准备好插入Transformers进行训练。
法语数据集的每一行都是要翻译的句子。 英语数据集的每一行都是机器翻译模型的参考。 机器翻译模型必须产生一个与参考匹配的英文候选翻译。
BLEU 提供了一种评估机器翻译模型生成的候选
翻译的方法。
Papineni等人(2002 年)提出了一种评估人类翻译的高效方法。定义人类基线很困难。但是,他们意识到,如果我们逐字比较人类翻译和机器翻译,我们可以获得有效的结果。
Papineni等人(2002 年)将他们的方法命名为双语评估助手分数(BLEU)。
在本节中,我们将使用自然语言工具包(NLTK)实现 BLEU:
www.nltk.org/api/nltk.translate.html#nltk.translate.bleu_score.sentence_bleu
我们将从几何评估开始。
BLEU 方法将候选句子的部分与参考句子或多个参考句子进行比较。
打开这本书的 GitHub 存储库的章节目录中的BLEU.py
。
程序导入了nltk
模块:
from nltk.translate.bleu_score import sentence_bleu
from nltk.translate.bleu_score import SmoothingFunction
然后,它模拟了机器翻译模型生成的候选翻译与数据集中实际翻译参考之间的比较。 请记住,一句话可能已经多次重复,并且以不同的方式被不同的译者翻译,这使得找到有效的评估策略变得具有挑战性。
该程序可以评估一个或多个参考:
#Example 1
reference = [['the', 'cat', 'likes', 'milk'], ['cat', 'likes' 'milk']]
candidate = ['the', 'cat', 'likes', 'milk']
score = sentence_bleu(reference, candidate)
print('Example 1', score)
#Example 2
reference = [['the', 'cat', 'likes', 'milk']]
candidate = ['the', 'cat', 'likes', 'milk']
score = sentence_bleu(reference, candidate)
print('Example 2', score)
这两个例子的得分都是1
:
Example 1 1.0
Example 2 1.0
候选C、参考R以及在C(N)中找到的正确令牌数量的简单评估P可以表示为几何函数:
如果您正在寻找 3 克重叠,例如:
#Example 3
reference = [['the', 'cat', 'likes', 'milk']]
candidate = ['the', 'cat', 'enjoys','milk']
score = sentence_bleu(reference, candidate)
print('Example 3', score)
如果您正在寻找 3 克重叠,则输出会很严重:
Warning (from warnings module):
File
"C:\Users\Denis\AppData\Local\Programs\Python\Python37\lib\site-packages\nltk\translate\bleu_score.py", line 490
warnings.warn(_msg)
UserWarning:
Corpus/Sentence contains 0 counts of 3-gram overlaps.
BLEU scores might be undesirable; use SmoothingFunction().
Example 3 0.7071067811865475
一个人可以看出得分应该是1
而不是0.7
。 超参数可以更改,但方法仍然保持不变。
上面代码中的警告是一个很好的警告,它预告了下一节的内容。
每个程序版本和每次运行的消息可能会有所不同,因为这是一个随机过程。
Papineni等人(2002 年)提出了一种修改过的一元方法。 思路是统计参考句子中的词出现次数,并确保在候选句子中不会过度评估该词。
考虑由Papineni等人(2002 年)解释的以下示例:
参考 1:地板上有一只猫。
参考 2:垫子上有一只猫。
现在考虑以下候选序列:
候选:the the the the the the the
现在我们来找出候选句子中的单词数(相同单词“the”的7
次出现)在参考 1
句子中(单词“the”的2
次出现)的情况。
标准的一元精确度将是7/7
。
修改后的一元精确度为2/7
。
请注意,BLEU 函数的输出警告表示同意并建议使用平滑。
让我们将平滑技术添加到 BLEU 工具包中。
Chen和Cherry(2014 年)引入了一种平滑技术,可以改进标准 BLEU 技术的几何评估方法。
标签平滑是一种非常有效的方法,可以在训练阶段改进Transformers模型的性能。 这对困惑度产生了负面影响。 然而,它迫使模型更加不确定。 这反过来对准确性产生积极影响。
例如,假设我们必须预测以下序列中的掩码单词是什么:
猫[mask]了牛奶。
想象一下输出以 softmax 向量的形式出现:
candidate_words=[drinks, likes, enjoys, appreciates]
candidate_softmax=[0.7, 0.1, 0.1,0.1]
candidate_one_hot=[1,0,0,0]
这将是一个残酷的方法。 标签平滑可以通过引入 epsilon = 使系统更开放。
candidate_softmax
的元素数量为k=4。
对于标签平滑,我们可以将 设置为 0.25
,例如。
标签平滑的几种方法之一可以是一个直观的函数。
首先,将 candidate_one_hot
的值减小 。
通过 0
值增加 。
如果我们应用这种方法,我们将得到以下结果:
candidate_smoothed=[0.75,0.083,0.083,0.083]
,使输出对未来的变换和更改开放。
变换器使用标签平滑的变体。
BLEU 的一个变体是 chencherry 平滑。
Chen 和 Cherry(2014)介绍了一种通过将 添加到否则为 0
的值来平滑候选评估的有趣方法。有几种 chencherry(Boxing Chen + Colin Cherry)方法:www.nltk.org/api/nltk.translate.html
。
让我们首先评估一个带有平滑的法语 - 英语示例:
#Example 4
reference = [['je','vous','invite', 'a', 'vous', 'lever','pour', 'cette', 'minute', 'de', 'silence']]
candidate = ['levez','vous','svp','pour', 'cette', 'minute', 'de', 'silence']
score = sentence_bleu(reference, candidate)
print("without soothing score", score)
尽管人类可以接受候选者,但输出分数较低:
without smoothing score 0.37188004246466494
现在,让我们对评估添加一些开放的平滑:
chencherry = SmoothingFunction()
r1=list('je vous invite a vous lever pour cette minute de silence')
candidate=list('levez vous svp pour cette minute de silence')
#sentence_bleu([reference1, reference2, reference3], hypothesis2,smoothing_function=chencherry.method1)
print("with smoothing score",sentence_bleu([r1], candidate,smoothing_function=chencherry.method1))
分数未达到人类可接受程度:
with smoothing score 0.6194291765462159
我们现在已经看到了数据集是如何预处理的以及 BLEU 如何评估机器翻译。
谷歌翻译,translate.google.com/
,提供了一个用于翻译的即用界面。谷歌正在逐步将变换器编码器引入其翻译算法中。在接下来的部分中,我们将使用谷歌 Trax 实现一个翻译任务的变换器模型。
但是,可能根本不需要 AI 专家。
如果我们在谷歌翻译中输入前一节中分析的句子,Levez-vous svp pour cette minute de silence
,我们将实时获得英文翻译:
图 6.2:谷歌翻译
翻译是正确的。
工业 4.0 是否仍需要 AI 专家进行翻译任务,或者只需要一个网络界面开发人员?
谷歌在其谷歌翻译平台上提供了翻译所需的每项服务:cloud.google.com/translate
:
一个翻译 API:一个网络开发人员可以为客户创建一个接口
可以翻译流媒体内容的媒体翻译 API
一个 AutoML 翻译服务,将为特定领域训练一个定制模型
一个谷歌翻译项目需要一个网络开发人员来处理界面,一个专业主题专家(SME),也许还需要一个语言学家。但是,AI 专家不是必需的。
工业 4.0 正朝着 AI 作为一种服务的方向发展。那么为什么要费心学习具有变换器的 AI 开发呢?成为工业 4.0 AI 专家有两个重要的原因:
在现实生活中,AI 项目经常遇到意想不到的问题。例如,谷歌翻译可能无论投入多少善意都无法满足特定需求。在这种情况下,谷歌 Trax 将会派上用场!
要使用谷歌 Trax 进行 AI,您需要是一个 AI 开发者!
你永远不知道!第四次工业革命正在将一切与一切连接起来。一些 AI 项目可能会顺利运行,而一些则需要 AI 专业知识来解决复杂问题。例如,在第十四章中,解释黑匣子Transformers模型,我们将展示有时需要 AI 开发来实现 Google 翻译。
现在我们已经准备好使用 Trax 进行翻译。
谷歌大脑开发了Tensor2Tensor(T2T)来使深度学习开发更容易。T2T 是 TensorFlow 的一个扩展,包含了一个深度学习模型库,其中包含许多Transformers示例。
虽然 T2T 是一个很好的开始,但谷歌大脑随后推出了 Trax,一个端到端的深度学习库。Trax 包含一个可以应用于翻译的Transformers模型。谷歌大脑团队目前负责维护 Trax。
这一部分将重点介绍初始化由瓦斯瓦尼等人(2017 年)描述的英德问题所需的最小功能,以说明Transformers的性能。
我们将使用预处理的英语和德语数据集来表明Transformers架构是语言无关的。
打开Trax_Translation.ipynb
。
我们将开始安装我们需要的模块。
谷歌大脑已经使 Trax 易于安装和运行。我们将导入基础知识和 Trax,可以在一行中安装:
#@title Installing Trax
import os
import numpy as np
!pip install -q -U trax
import trax
是的,就是这么简单!
现在,让我们创建我们的Transformers模型。
我们将创建原始的Transformers模型,就像第二章,开始使用Transformers模型架构中描述的那样。
我们的 Trax 函数将在几行代码中检索预训练模型配置:
#@title Creating a Transformer model.
# Pre-trained model config in gs://trax-ml/models/translation/ende_wmt32k.gin
model = trax.models.Transformer(
input_vocab_size=33300,
d_model=512, d_ff=2048,
n_heads=8, n_encoder_layers=6, n_decoder_layers=6,
max_len=2048, mode='predict')
模型是一个具有编码器和解码器堆栈的Transformers。每个堆栈包含6
层和8
个头。d_model=512
,与原始Transformers的架构相同。
Transformers需要预训练的权重才能运行。
预训练权重包含Transformers的智能。权重构成了Transformers对语言的表示。权重可以表示为将产生某种形式的机器智能 IQ的参数数量。
让我们通过初始化权重来给模型赋予生命:
#@title Initializing the model using pre-trained weights
model.init_from_file('gs://trax-ml/models/translation/ende_wmt32k.pkl.gz',
weights_only=True)
机器配置及其智能现已准备就绪。让我们对一个句子进行分词。
我们的机器翻译器已准备好对句子进行分词。笔记本使用由Trax
预处理的词汇表。预处理方法类似于本章中描述的预处理 WMT 数据集部分。
现在,这个句子将被分词:
#@title Tokenizing a sentence.
sentence = 'I am only a machine but I have machine intelligence.'
tokenized = list(trax.data.tokenize(iter([sentence]), # Operates on streams.
vocab_dir='gs://trax-ml/vocabs/',
vocab_file='ende_32k.subword'))[0]
程序现在将解码句子并产生一个翻译。
Transformers将句子编码为英语,然后将其解码为德语。模型及其权重构成了其能力集。
Trax
已经使解码函数变得直观易用:
#@title Decoding from the Transformer
tokenized = tokenized[None, :] # Add batch dimension.
tokenized_translation = trax.supervised.decoding.autoregressive_sample(
model, tokenized, temperature=0.0) # Higher temperature: more diverse results.
请注意,较高的温度会产生多样化的结果,就像人类翻译者一样,在本章的定义机器翻译部分有详细解释。
最终,程序将解密并显示翻译。
Google Brain 用Trax
开发了一个主流、颠覆性和直观的 Transformer 实现。
程序现在将解密并显示翻译的结果:
#@title De-tokenizing and Displaying the Translation
tokenized_translation = tokenized_translation[0][:-1] # Remove batch and EOS.
translation = trax.data.detokenize(tokenized_translation,
vocab_dir='gs://trax-ml/vocabs/',
vocab_file='ende_32k.subword')
print("The sentence:",sentence)
print("The translation:",translation)
输出相当令人印象深刻:
The sentence: I am only a machine but I have machine intelligence.
The translation: Ich bin nur eine Maschine, aber ich habe Maschinenübersicht.
转换器将machine intelligence
翻译为Maschinenübersicht
。
如果我们将Maschinenübersicht
拆分为Maschin(机器)
+ übersicht(智能)
,我们可以看到:
über
的字面意思是“over”
sicht
的意思是“视野”或“视图”
转换器告诉我们,尽管它是一台机器,它能够有视觉。机器智能通过 Transformer 不断增长,但它不是人类智能。机器用自身的智能学习语言。
这结束了我们对 Google Trax 的实验。
在本章中,我们探讨了原始 Transformer 的三个重要方面。
我们首先定义了机器翻译。人工翻译为机器设立了一个极高的基准。我们看到,英语-法语和英语-德语翻译意味着需要解决大量问题。Transformer 解决了这些问题,并创造了最先进的 BLEU 记录。
然后,我们对来自欧洲议会的 WMT 法语-英语数据集进行了预处理,并进行了数据清理。我们必须将数据集转换成行并清理数据。做完这些之后,我们通过消除低于频率阈值的单词来减小数据集的大小。
机器翻译 NLP 模型需要相同的评估方法。在 WMT 数据集上训练模型需要进行 BLEU 评估。我们看到了几何评估是打分翻译的一个很好的基础,但即使是修改后的 BLEU 也有其局限性。因此,我们添加了一种平滑技术来增强 BLEU。
我们看到,Google 翻译提供了标准的翻译 API、媒体流 API 和自定义的自动机器学习模型训练服务。如果项目顺利进行,实施 Google 翻译 API 可能不需要进行 AI 开发。如果不顺利,我们将不得不像从前一样动手做!
我们使用了 Trax,Google Brain 的端到端深度学习库,实现了英语到德语的翻译转换器。
现在我们已经介绍了构建转换器的主要构件:架构、预训练、训练、数据预处理和评估方法。
在下一章,GPT-3 引擎崛起的超人类 Transformer中,我们将探索使用我们在前几章中探讨的构件实现 Transformer 的令人惊叹的方式。
机器翻译现在已经超过了人类基准。(是/否)
机器翻译需要大规模的数据集。(是/否)
没有必要使用相同的数据集比较Transformers模型。(True/False)
BLEU 是法语单词蓝色的缩写,也是 NLP 度量的首字母缩写。(True/False)
平滑技术增强了 BERT。(True/False)
德英与英德的机器翻译相同。(True/False)
原始的 Transformer 多头注意力子层有 2 个头。(True/False)
原始的 Transformer 编码器有 6 层。(True/False)
原始的 Transformer 编码器有 6 层,但只有 2 层解码器。(True/False)
你可以训练没有解码器的转换器。(True/False)
英德 BLEU 分数与参考论文和代码:paperswithcode.com/sota/machine-translation-on-wmt2014-english-german
2014 年机器翻译研讨会(WMT):www.statmt.org/wmt14/translation-task.html
欧洲议会议事录 1996-2011,法英平行语料库:www.statmt.org/europarl/v7/fr-en.tgz
Jason Brownlee,博士,如何准备法语至英语的机器翻译数据集:machinelearningmastery.com/prepare-french-english-dataset-machine-translation/
Kishore Papineni,Salim Roukos,Todd Ward 和 Wei-Jing Zhu,2002 年,BLEU:自动评估机器翻译的方法:aclanthology.org/P02-1040.pdf
Jason Brownlee,博士,用 Python 计算文本的 BLEU 得分的简介:machinelearningmastery.com/calculate-bleu-score-for-text-python/
Boxing Chen 和 Colin Cherry,2014 年,句子级 BLEU 平滑技术的系统比较:acl2014.org/acl2014/W14-33/pdf/W14-3346.pdf
Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan N. Gomez,Lukasz Kaiser 和 Illia Polosukhin,2017 年,注意力就是一切:arxiv.org/abs/1706.03762
Trax 存储库:github.com/google/trax
加入该书的 Discord 工作区,与作者进行每月的Ask me Anything会话:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。