自然语言处理(NLP)是一个迅速发展的领域。在COVID-19疫情带来的“新常态”下,教育材料、新闻和讨论越来越多地通过数字媒体平台进行,这为提供了更多的文本数据来处理。最初,简单的递归神经网络(RNNs)被用来训练文本数据,但近年来,许多新的研究出版物提供了最先进的结果。其中之一就是BERT!在这篇博客中,将分享对BERT变换器及其应用的理解。
BERT代表“变换器的双向编码器表示”。在进一步讨论之前,先简要介绍一下变换器。变换器是由谷歌在论文“注意力就是所需要”中引入的架构,它使用自注意力机制,适合于语言理解。自注意力机制的必要性可以通过一个简单的例子来理解。假设有一句话:“今年夏天去了霍斯利山,考虑到上次去那里,它发展得很好。”最后一个词“那里”指的是霍斯利山。但要理解这一点,记住前几部分是必要的。为了实现这一点,注意力机制在输入序列的每一步决定序列的其他部分哪些是重要的。简而言之,“需要上下文!”
变换器具有编码器-解码器架构。它们由包含前馈和注意力层的模块组成。下面的图片来自研究论文。
通常,语言模型以一个方向读取输入序列:要么从左到右,要么从右到左。这种单向训练在目标是预测/生成下一个词时效果很好。但为了更深入地理解语言上下文,BERT使用双向训练。有时,它也被称为“非方向性”。因此,它同时考虑前面的和后面的标记。BERT将变换器的双向训练应用于语言建模,学习文本表示。请注意,BERT只是一个编码器。它没有解码器。编码器负责读取文本输入和处理。解码器负责为任务产生预测。
BERT是一个多层编码器。在论文中,介绍了两种模型,BERT基础和BERT大型。BERT大型的层数是基础模型的两倍。这里所说的层指的是变换器块。BERT基础在4个基于云的TPU上训练了4天,BERT大型在16个TPU上训练了4天。
BERT基础 - 12层,12个注意力头,1.1亿参数。BERT大型 - 24层,16个注意力头,3.4亿参数。
如上图所示,BERT基础是12个编码器的堆叠。每个编码器都是一个变换器块。输入必须提供给第一个编码器。BERT编码器期望一个标记序列。下图显示了标记如何处理和转换。[CLS]是插入在第一句话开头的特殊标记。[SEP]插入在每个句子的末尾。通过添加段'A'或'B'来创建段嵌入,以区分句子。还添加了每个标记在序列中的位置,以获得位置嵌入。
上述三个嵌入的总和是BERT编码器的最终输入。BERT接收一个输入序列,并不断向上堆叠。在每个块中,它首先通过自注意力层,然后传递到前馈神经网络。它被传递到下一个编码器。最后,每个位置将输出一个大小为hidden_size(BERT基础为768)的向量。这是词嵌入。
现在,让思考一个主要问题:BERT如何实现双向训练?它使用两种方法:MLM(掩蔽语言建模)和NSP(下一句预测)。
MLM(掩蔽语言建模)在序列中,随机掩蔽一定比例的词,用标记[MASK]替换它们。在论文中,他们掩蔽了15%的输入词。它被训练用来预测这些掩蔽的词,使用剩余词的上下文。例如:“喜欢在春天骑自行车” -> 喜欢在[MASK]季节骑自行车。这里的一个问题是,预训练模型将有15%的掩蔽标记,但当微调预训练模型并传递输入时,不传递掩蔽标记。为了解决这个问题,在15%的标记中选择掩蔽标记时:80% - 实际上被替换为标记[MASK],10%的时间标记被替换为随机标记,其余的保持不变。
NSP(下一句预测)为了理解两个句子之间的关系,BERT使用NSP训练。模型接收一对句子作为输入,它被训练用来预测第二个句子是否是第一个句子的下一句。在训练期间,提供50-50的输入案例。假设随机句子将与第一个句子在上下文意义上脱节。
现在,让看一个简单的例子,如何使用预训练的BERT模型来实现目的。首先,安装transformers库。
pip3 install transformers
Scikit-learn库提供了一些样本数据集来学习和使用。将使用Newsgroups数据集。
from sklearn.datasets import fetch_20newsgroups
from sklearn.model_selection import train_test_split
基本上,所有新闻文章的文本将被归类到20个组中。这是一个多类文本分类问题。下一步是从许多可用的预训练模型中选择。
关于流行模型的详细信息:
选择模型,并确定输入序列/句子的最大长度。如果将max_length设置得非常高,可能会在执行期间遇到内存不足的问题。
model_name = "bert-base-uncased"
max_length = 512
BERT还提供了tokenizers,它们将原始输入序列转换为标记,并将其传递给编码器。
from transformers import BertTokenizerFast
tokenizer = BertTokenizerFast.from_pretrained(model_name, do_lower_case=True)
让准备数据。
dataset = fetch_20newsgroups(subset="all", shuffle=True, remove=("headers","footers", "quotes"))
target_names=dataset.target_names
news_text = dataset.data
labels = dataset.target
(train_texts,valid_texts,train_labels,valid_labels)=train_test_split(news_text, labels, test_size=0.3)
标签范围从0到19,每个标签对应一个特定组,其名称存储在target_names中。接下来,使用tokenizer对输入进行编码。
train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=max_length)
接下来,将这些编码包装在数据集中,并使用Trainer进行训练。
model=BertForSequenceClassification.from_pretrained(model_name, num_labels=len(target_names))
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
num_train_epochs=10,
per_device_train_batch_size=16, # batch size per device during training
weight_decay=0.01, # strength of weight decay
load_best_model_at_end=True,
logging_steps=200,
evaluation_strategy="steps",
)
可以根据需要提供参数。
trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset)
trainer.train()
现在,可以将任何新文本以标记的形式传递给模型,并获得预测。
与BERT类似,其他一些变换器模型也证明了它们能够实现最先进的结果。一些变换器模型,如Roberta,试图改进BERT。下面的表格总结了目前最受欢迎的变换器模型。
除了这些,GPT模型在对话任务中被证明非常高效。有趣的是,GPT-3的最高版本有惊人的175B参数!架构基于变换器的解码器块。它有一个掩蔽自注意力机制。
XLNet模型引入了排列语言建模。在这里,所有标记都被预测,但是以随机顺序。这与仅仅尝试预测15%的掩蔽标记不同。
DistilBERT模型是BERT的一个更轻、更便宜、更快的版本。在这里,模型以BERT的97%的能力进行训练,但大小缩小了40%(6600万参数,与基于BERT的1.1亿参数相比)并且速度提高了60%。
在RoBERTa中,他们在训练过程中去掉了下一句预测。他们在训练周期之间改变了掩蔽标记,并使用不同的超参数以获得更好的准确性结果。
根据任务需求,可以选择其中之一!