BERT详解:开创性自然语言处理框架的全面指南

全文共9095字,预计学习时长18分钟

想象一下自己正在进行一个非常好的数据科学项目,还为了获得较好的结果用了最前沿的数据库。然后几天后,新一代前沿框架的出现,导致现有模型已经落后了。

这不是一个假设。这就是自然语言处理领域工作者所面对的现实(和挑战)!过去两年取得的突破令人惊叹,接连出现出现各种新框架,可能比目前掌握的框架还要好。

谷歌BERT就是这样一个自然语言处理框架。这可能是近几年最有影响力的一次革新。

可以毫不夸张地说,BERT已经对自然语言处理进行了显著的变革。比如使用一个在大型无标号数据集上训练的模型,在11个独立的自然语言处理任务中取得佳绩。只需稍加微调就能实现。BERT就能实现这一点!这是自然语言处理模型设计的根本转变。

BERT引发了许多新的自然语言处理体系结构、训练方法以及语言模型,如Google TransformerXL、OpenAI的GPT-2、XLNeT、ERNIE2.0、RoBERTa等。

本文旨在提供全面的指南,不仅包含BERT的概念,还包含对未来的自然语言处理研究产生的影响。当然,文中还有很多Python代码实例。

BERT详解:开创性自然语言处理框架的全面指南

目录

1.BERT是什么?

2.从Word2vec到BERT: 自然语言处理对语言学习表征的探究

3.深入探究BERT的工作原理

4.用BERT进行文本分类(Python代码实例)

5.其他信息: 自然语言处理的学科前沿

BERT详解:开创性自然语言处理框架的全面指南

1. BERT是什么?

你已经听说过BERT、它的强大功能,以及它是如何潜在地改变自然语言处理领域的。但是BERT到底是什么呢?

BERT的研发团队是这样描述自然语言处理框架的:

BERT(Bidirectional Encoder Representations from Transformers)是一种Transformer的双向编码器,旨在通过在左右上下文中共有的条件计算来预先训练来自无标号文本的深度双向表示。因此,经过预先训练的BERT模型只需一个额外的输出层就可以进行微调,从而为各种自然语言处理任务生成最新模型。

对于入门者而言,这听起来过于复杂,但确实总结了BERT的优势,接下来进行详细讲解。

首先,很容易理解BERT是Bidirectional Encoder Representations from Transformers的缩写,这里的每一个词都有其含义,本文将逐一介绍。目前,关键之处在于——BERT是以Transformer结构为基础的。

第二,BERT的预训练是在包含整个维基百科的无标签号文本的大语料库中(足足有25亿字!) 和图书语料库(有8亿字)中进行的。

在BERT成功的背后,有一半要归功于预训练。这是因为在一个大型文本语料库上训练一个模型时,模型开始获得对语言工作原理的更深入的理解。这些知识是瑞士军刀,几乎对任何自然语言处理任务都有用。

第三,BERT是一个“深度双向”的模型。双向意味着BERT在训练阶段从所选文本的左右上下文中汲取信息。

模型的双向性对于理解语言的真正意义很重要。来看一个例子,有两句话,都涉及“bank”一词:

BERT详解:开创性自然语言处理框架的全面指南

BERT同时捕获左右上下文

如果仅取左上下文或右上下文来预测单词“bank”的性质,那么在两个给定示例中,至少有一个会出错。

解决这个问题的一个方法是,在做出预测之前同时考虑左上下文和右上下文。就像BERT一样。本文稍后会介绍如何实现这一点。

最后,也是BERT最令人印象深刻的一面。可以通过添加几个额外的输出层来进行微调,从而成为各种自然语言处理任务最顶尖的模型。

BERT详解:开创性自然语言处理框架的全面指南

2. 从Word2vec到BERT: 自然语言处理对学习语言表征的探究

“自然语言处理的最大挑战之一是缺乏训练数据。因为自然语言处理是一个具有许多不同任务的多样化领域,大多数任务专用的数据集只包含几千或几十万个人工标记的培训示例。”——谷歌AI

Word2Vec与GloVe

通过对大量无标号文本数据的预训练模型来探究学习语言表征始于Word2Vec与GloVe中词的嵌入。这些嵌入改变了自然语言处理任务的执行方式。现在的嵌入可以捕捉词之间的上下文关系。

BERT详解:开创性自然语言处理框架的全面指南

这些嵌入用于训练自然语言处理下游任务的模型,做出更好的预测。即使使用较少的任务专用的数据,也可以通过利用嵌入本身的附加信息来实现这一点。

这些嵌入的局限性在于他们使用了非常浅显的语言模型。这意味着它们能够捕获的信息量有限,这促使人们使用更深入、更复杂的语言模型(LSTM和GRU层)。

另一个关键性的制约因素是:这些模式没有考虑到该词的上下文。以上面的“bank”为例,同一个词在不同的语境中有不同的含义。然而,像Word2vec这样的嵌入将在上下文中为“bank”提供相同的向量。

这一宝贵信息不应被忽视。

输入ELMo与ULMFiT

BERT详解:开创性自然语言处理框架的全面指南

自然语言处理社区用ELMo处理多义词,多义词指一个词由于上下文不同,含义也不同。从训练浅层前馈网络(Word2vec),到逐步使用复杂的双向LSTM体系结构层来训练词嵌入。这意味着同一个单词可以基于其上下文具有多个ELMo嵌入。

从那时起,预培训就成为了自然语言处理的一种培训机制。

BERT详解:开创性自然语言处理框架的全面指南

ULMFiT在这一点做得更深入。该框架可以训练可调的语言模型,即使在各种文档分类任务上的数据较少(少于100个示例)的情况下,也可以提供出色的结果。可以肯定地说,ULMFiT破解了自然语言处理中迁移学习的秘密。

这就是在自然语言处理中迁移学习的黄金公式:

自然语言处理中的迁移学习 = 预训练+微调

ULMFiT之后,大多数自然语言处理突破上述公式的要素,并取得了最新的基准。

OpenAI的GPT

OpenAI的GPT扩展了ULMFiT和ELMo带来的预训练和微调法。GPT本质上是用基于Transformer的体系结构代替了基于LSTM的语言建模体系结构。

GPT模型可以微调到超出文档分类的多个自然语言处理任务,例如常识推理、语义相似度和阅读理解。

GPT还强调了Transformer框架的重要性,该框架具有更简单的体系结构,其训练速度比基于LSTM的模型更快,还能够通过使用注意机制来学习数据中的复杂模式。

OpenAI的GPT通过实现多种先进技术证实了Transformer框架的强大和实用性。

这就是Transformer如何促成了BERT的开发,以及自然语言处理中的后续所有突破。

现在,还有一些没有提到的重要突破和研究成果,比如半监督序列学习。这是因为它们稍微超出了本文的范围,但可以酌情阅读链接文章以了解更多信息。

回到BERT

因此,解决自然语言处理任务的新方法可以简化成两步:

1.在大型无标号文本语料库(可以是未监督或半监督)中训练语言模型。

2.根据具体的自然语言处理任务对此大型模型进行微调,以利用此模型获得的大型知识库(有监督)。

在这样的背景下,来理解一下BERT是如何开始构建一个模型的,这个模型将在很长一段时间内成为自然语言处理的优秀基准。

BERT详解:开创性自然语言处理框架的全面指南

3. 深入探究BERT的工作原理

继续深入了解BERT,看看它为什么在建模语言中如此有效。之前已经介绍了BERT的功能,但它是如何做到的呢?本节将回答这个相关的问题。

BERT的结构

BERT是以Transformer为基础的,目前有两种变体:

•BERT Base: 12层(指transformer blocks), 12个attention head, 以及1.1亿个参数

•BERT Large: 24层(指transformer blocks), 16个attention head,以及3.4亿个参数

BERT详解:开创性自然语言处理框架的全面指南

来源:http://jalammar.github.io/illustrated-bert/

为了便于比较,基于BERT的体系结构的模型大小与OpenAI的GPT相同。所有这些Transformer层都是编码器专用。

既然已经清楚了BERT的全部结构,在构建模型之前,首先需要一些文本处理步骤。

文本处理

BERT详解:开创性自然语言处理框架的全面指南

BERT的开发人员加入了一组特定的规则来代表模型的输入文本,其中许多都是有助于优化模型的创造性设计。

对于初学者来说,每个输入嵌入都由3个嵌入组成:

1.位置嵌入:BERT学习并使用位置嵌入来表达词在句子中的位置,这些为了克服Transformer的限制而添加的。与RNN不同,Transformer不能捕获“序列”或“顺序”信息。

2.片段嵌入:BERT还可以将句子对作为任务(问答)的输入。这就是为什么它学习了第一个和第二个句子的嵌入,以帮助模型区分二者。在上面的例子中,所有标记为EA的标记都属于句A(EB同理)。

3.标记嵌入:从WordPiece标记词汇表中学习特定标记的嵌入。

对于特定的标记,其输入的表示是对应的标记、片段和位置嵌入之和。

这样一个综合的嵌入方案包含了很多对模型有用的信息。

这些预处理步骤综合起来,使BERT具有很强的通用性。这意味着,如果不对模型的结构进行任何重大更改,就可以轻松地将其训练到多种自然语言处理任务上。

预训练任务

BERT有两项自然语言处理任务的预训练:

•Masked Language Modeling

•Next Sentence Prediction

接下来进行详述

a. Masked Language Modeling (双向)

双向的必要性

BERT是一个深度双向模型。网络有效地从标记的右左上下文捕获信息,从第一层一直到最后一层。

一般来说,要么训练语言模型来预测句子中的下一个词(GPT使用从右到左的上下文),要么训练语言模型来预测从左到右的上下文。这使得模型容易由于信息的丢失而出错。

BERT详解:开创性自然语言处理框架的全面指南

按顺序预测词

ELMo试图在从左到右和从右到左的上下文中训练两个LSTM语言模型,并将它们简单连接起来,解决这个问题。尽管它大大改进了现有技术,但还不够。

“直观来说,人们有理由相信,深度双向模型比从左到右模型或从左到右和从右到左模型的简单连接更强大。” ——BERT

这就是BERT超越GPT和ELMo的地方。请看下图:

BERT详解:开创性自然语言处理框架的全面指南

箭头表示从一层到下一层的信息流。顶部的绿色框表示每个输入词的最终上下文化表示。

从上面的图像可以看出:BERT是双向的,GPT是单向的(信息只从左到右流动),ELMo是浅双向的。

这就是Masked Language Model在图像中的表示。

关于Masked Language Model

假设有这样一个句子:“我喜欢阅读Analytics Vidhya上的数据科学博客”。想要训练一个双向的语言模型,可以建立一个模型来预测序列中的遗漏单词,而不是试图预测序列中的下一个单词。

将“Analytics”替换为“[MASK]”,表示丢失的标记。然后,以这样的方式训练模型,使其能够预测“Analytics”是本句中遗漏的部分:“我喜欢阅读[MASK]Vidhya上的数据科学博客。”

这是Masked Language Model的关键。BERT的开发者还提出了一些进一步改进该技术的注意事项:

•为了防止模型过于关注特定的位置或掩盖的标记,研究人员随机掩盖了15%的词。

•掩盖的词并不总是被[MASK]替换,因为在微调时不会出现[MASK]。

•因此,研究人员使用了以下方法:

o[MASK]替换的概率为80%

o随机词替换的概率为10%

o不进行替换的概率为10%

b. Next Sentence Prediction

Masked Language Model是为了理解词之间的关系。BERT还接受了Next Sentence Prediction训练,来理解句子之间的关系。

问答系统 question answering systems是一个不错的例子。

该任务十分简单。给定两个句子,句A和句B,B是语料库中在A后面的下一个句子,还是只是一个随机的句子?

由于它属于到二进制分类任务,通过将数据拆分为句子对,就可以很容易地从任何语料库中生成数据。就像Masked Language Model一样,研发者也在这里添加了一些注意事项。例如:

假设有一个包含100,000个句子的文本数据集。因此,将有5万个训练样本或句子对作为训练数据。

•其中50%的句子对的第二句就是第一句的下一句。

•剩余50%的句子对,第二句从语料库中随机抽取。

•第一种情况的标签为‘IsNext’ ;第二种情况的标签为‘NotNext’。

这就是BERT为什么能够成为一个真正的任务无关模型。因为它结合了Masked Language Model (MLM)和Next Sentence Prediction (NSP)的预训练任务。

BERT详解:开创性自然语言处理框架的全面指南

4. 用BERT进行文本分类(Python代码实例)

你一定想知道BERT已经开发的各种功能。有很多方法可以利用BERT的大型知识库来开发自然语言处理应用程序。

最有效的方法之一是根据自己的任务和任务的专用数据对其进行微调。然后可以使用BERT的嵌入作为文本文档的嵌入。

本节中将学习如何将BERT的嵌入用于自己的自然语言处理任务。以后的文章将讨论如何微调整个BERT模型。

为了从BERT中提取嵌入,我们将使用一个真正有用的开源项目,称为Bert-as-Service:

BERT详解:开创性自然语言处理框架的全面指南

运行BERT可能是一个艰苦的过程,因为它需要很多代码和安装包。这就是为什么这个开源项目非常有用,因为它允许使用BERT在两行代码中提取每个句子的编码。

安装BERT-As-Service

BERT-As-Service运行原理十分简单。它创建了的BERT服务器,从中可以使用笔记本中的Python代码访问。每次发送一个句子作为列表,它就会发送所有句子的嵌入。

可以通过pip安装服务器和客户端,它们可以单独安装,甚至可以安装在不同的计算机上:

pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`

请注意,服务器必须在Python>=3.5上运行,且TensorFlow>=1.10(1-10)。

另外,由于运行BERT是一项GPU密集型任务,建议将BERT服务器安装在GPU云端或其他计算能力高的计算机上。

现在,回到终端并下载下列模型。然后将压缩文件解压缩到某个文件夹中, 如 /tmp/english_L-12_H-768_A-12/。

以下是发布的经过预先训练的BERT模型列表:

BERT-Base, Uncased
12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Large, Uncased
24-layer, 1024-hidden, 16-heads, 340M parameters
BERT-Base, Cased
12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Large, Cased
24-layer, 1024-hidden, 16-heads, 340M parameters
BERT-Base, Multilingual Cased (New)
104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Base, Multilingual Cased (Old)
102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
BERT-Base, Chinese
Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters

下载BERT Uncased,然后解压缩:

wget https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip && unzip uncased_L-12_H-768_A-12.zip

一旦将所有文件解压缩到一个文件夹中,就可以启动BERT服务了:

bert-serving-start -model_dir uncased_L-12_H-768_A-12/ -num_worker=2 -max_seq_len 50

现在可以直接从Python代码中调用BERT-As-Service(使用客户端库)。接下来进行代码实践。

打开一个新的Jupyter notebook,试着获取句子的嵌入内容:“我爱数据科学和analytics vidhya”。

from bert_serving.client import BertClient
# make a connection with the BERT server using it's ip address; do not give any ip if same computer
bc = BertClient(ip="SERVER_IP_HERE")
# get the embedding
embedding = bc.encode(["I love data science and analytics vidhya."])
# check the shape of embedding, it should be 1x768
print(embedding.shape)

这里,IP地址是服务器或云的IP。如果在同一台计算机上使用,则不需要此操作。

返回的嵌入的将是(1,768),因为在BERT的结构中只有一个由768个隐藏单元表示的句子。

问题陈述:对推特上的仇恨言论进行分类

接下来用现实世界的数据集证实BERT有多有效。下面将处理一个数据集,包括一组包含“仇恨言论”或“非仇恨言论”的推文。

为了简单起见,假如一条推文带有种族主义或性别歧视情绪,它就包含仇恨言论。因此,本次任务是将种族主义或性别歧视的推文与其他推文进行区分。

读者可下载数据集或阅读 DataHack平台的问题陈述:https://datahack.analyticsvidhya.com/contest/practice-problem-twitter-sentiment-analysis/?utm_source=blog&utm_medium=demystifying-bert-groundbreaking-nlp-framework

本文将使用BERT从数据集中的每个推文中提取嵌入,然后使用这些嵌入来训练文本分类模型。

项目的总体结构如下所示:

BERT详解:开创性自然语言处理框架的全面指南

目前代码如下:

import pandas as pd
import numpy as np
# load training data
train = pd.read_csv('BERT_proj/train_E6oV3lV.csv', encoding='iso-8859-1')
train.shape

读者应该熟悉大多数人是如何发推文的。有许多随机的符号和数字(又名聊天语言!),本数据集也一样。因此在将其传给BERT之前,需要对其进行预处理:

import re
# clean text from noise
def clean_text(text):
 # filter to allow only alphabets
 text = re.sub(r'[^a-zA-Z\']', ' ', text)

 # remove Unicode characters
 text = re.sub(r'[^\\x00-\\x7F]+', '', text)

 # convert to lowercase to maintain consistency
 text = text.lower()

 return text
train['clean_text'] = train.tweet.apply(clean_text)

现在数据集已经干净了,现在是时候将其拆分为训练和验证集了:

from sklearn.model_selection import train_test_split
# split into training and validation sets
X_tr, X_val, y_tr, y_val = train_test_split(train.clean_text, train.label, test_size=0.25, random_state=42)
print('X_tr shape:',X_tr.shape)

接下来获得训练集和验证集中所有推文的嵌入:

from bert_serving.client import BertClient
# make a connection with the BERT server using it's ip address
bc = BertClient(ip="YOUR_SERVER_IP")
# get the embedding for train and val sets
X_tr_bert = bc.encode(X_tr.tolist())
X_val_bert = bc.encode(X_val.tolist())

接下来开始建模。首先训练分类模型:

from sklearn.linear_model import LogisticRegression
# LR model
model_bert = LogisticRegression()
# train
model_bert = model_bert.fit(X_tr_bert, y_tr)
# predict
pred_bert = model_bert.predict(X_val_bert)

检查分类准确性:

from sklearn.metrics import accuracy_score
print(accuracy_score(y_val, pred_bert))

即使数据集的规模如此小,也可以轻松地得到95%左右的分类准确率。真是绝了。

BERT详解:开创性自然语言处理框架的全面指南

5. 其他信息: 自然语言处理当前发展状况

BERT激起了自然语言处理领域的极大兴趣,Transformer 在自然语言处理任务中的应用尤其受关注。由此在研究室和机构中,涌现出大量针对预训练、Transformer和微调的各方面。

其中许多项目在多个自然语言处理任务上都优于BERT。其中有一些十分新奇的发展,如Roberta,这是Facebook AI相对于BERT和 DistilBERT的改进,DistilBERT是BERT的更快的压缩版本。

BERT详解:开创性自然语言处理框架的全面指南

留言 点赞 关注

我们一起分享AI学习与发展的干货

如需转载,请后台留言,遵守转载规范

相关推荐