20

理解BERT:一个突破性NLP框架的综合指南

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAxMjMwODMyMQ%3D%3D&%3Bmid=2456341170&%3Bidx=1&%3Bsn=619824dfc84455a4d5f7baccd8638ffd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

FzAjyaB.jpg!web

作者 | MOHD SANAD ZAKI RIZVI

编译 | VK

来源 | Analytics Vidhya

概述

  • Google的BERT改变了自然语言处理(NLP)的格局

  • 了解BERT是什么,它如何工作以及产生的影响等

  • 我们还将在Python中实现BERT,为你提供动手学习的经验

BERT简介

想象一下——你正在从事一个非常酷的数据科学项目,并且应用了最新的最先进的库来获得一个好的结果!几天后,一个新的最先进的框架出现了,它有可能进一步改进你的模型。

这不是一个假想的场景——这是在自然语言处理(NLP)领域工作的真正现实!过去的两年的突破是令人兴奋的。

谷歌的BERT就是这样一个NLP框架。 我敢说它可能是近代最有影响力的一个(我们很快就会知道为什么)。

RVbMzei.png!web

毫不夸张地说,BERT极大地改变了NLP的格局。 想象一下,使用一个在大型未标记数据集上训练的单一模型,然后在11个单独的NLP任务上获得SOTA结果。 所有这些任务都需要fine-tuning。 BERT是我们设计NLP模型的一个结构性转变。

BERT启发了许多最近的NLP架构、训练方法和语言模型,如谷歌的TransformerXL、OpenAI的GPT-2、XLNet、ERNIE2.0、RoBERTa等。

我的目标是给你一个全面的指导,不仅BERT,还有它带来的影响以及如何影响未来的NLP研究。

目录

  1. 什么是BERT?

  2. 从Word2Vec到BERT:NLP的学习语言表示的探索

  3. BERT如何工作?

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

  5. 超越BERT:NLP的最新技术

什么是BERT?

你可能大概听说过BERT,你看到过它是多么不可思议,它是如何潜在地改变了NLP的前景。 但是BERT到底是什么呢?

BERT背后的研究团队是这样描述NLP框架的:

"BERT代表Transformers的双向编码器。它被设计为通过对左右的上下文的联合来预训练未标记文本得到深层的双向表示。因此,只需一个额外的输出层,就可以对预训练的BERT模型进行微调,从而为各种NLP任务创建SOTA结果。"

作为一开始,这听起来太复杂了。 但是它确实总结了BERT的出色表现,因此让我们对其进行细分。

首先,很容易理解BERT是Transformers的双向编码器表示。 这里的每个词都有其含义,我们将在本文中逐一讨论。 这一行的关键是, BERT是基于Transformer架构的

其次,BERT是在大量未标记文本的预训练,包括整个Wikipedia(有25亿个词!)和图书语料库(有8亿个单词)。

这个 预训练 步骤是BERT成功背后的一半。这是因为当我们在大型文本语料库上训练模型时,我们的模型开始获得对语言工作原理的更深入和深入的了解。 这种知识几乎可用于所有NLP任务

第三,BERT是" 深度双向 "模型。 双向意味着BERT在训练阶段从目标词的左右两侧上下文来学习信息。

模型的双向性对于真正理解语言的意义很重要。 让我们看一个例子来说明这一点。 在此示例中,有两个句子,并且两个句子都包含单词"bank":

veUjma3.png!web

如果我们仅通过选择左侧或右侧上下文来预测"bank"一词的意义,那么在两个给定示例中至少有一个会出错。

解决此问题的一种方法是在进行预测之前考虑左右上下文。 这正是BERT所做的!我们将在本文的后面看到如何实现这一目标。

最后,BERT最令人印象深刻的方面。 我们可以通过仅添加几个其他输出层来微调它,以创建用于各种NLP任务的最新模型。

从Word2Vec到BERT:NLP的学习语言表示的探索

"自然语言处理中的最大挑战之一是训练数据的短缺。由于NLP是一个具有许多不同任务的多元化领域,因此大多数特定于任务的数据集仅包含数千或数十万个人标记的训练示例。" – Google AI

Word2Vec和GloVe

通过在大型未标记文本数据上进行预训练模型来学习语言表示的要求始于诸如Word2Vec和GloVe之类的Word Embedding。 这些Embedding改变了我们执行NLP任务的方式。 现在,我们有了Embedding,可以捕获单词之间的上下文关系。

uuiEzy7.png!web

这些Embedding被用来训练下游NLP任务的模型,并做出更好的预测。 这可以通过利用来自Embedding本身的附加信息,甚至使用较少的特定于任务的数据来完成。

这些Embedding的一个限制是使用非常浅的语言模型 这意味着他们能够获取的信息的数量是有限的,这促使他们使用更深入、更复杂的语言模型(LSTMs和GRUs层)。

另一个关键的限制是,这些模型没有考虑单词的上下文 让我们以上面的“bank”为例。 同一个单词在不同的上下文中有不同的意思。 然而,像Word2Vec这样的Embedding将在这两个上下文中为“bank”提供相同的Word Embedding。

这是导致模型不准确的一个因素。

ELMO和ULMFiT

ABNryub.png!web

ELMo是NLP社区对 一词多义 问题的回应——相同的词在不同的语境中有不同的含义。 从训练浅层前馈网络(Word2vec),逐步过渡到使用复杂的双向LSTM体系结构的层来训练Word Embedding。 这意味着同一个单词可以根据它所在的上下文有多个ELMO Embedding。

那是我们开始看到 预训练 作为NLP的训练机制的优势。

ie2mymy.png!web

ULMFiT则更进一步。 这个框架可以训练语言模型,这些模型可以进行微调,从而在各种文档分类任务中,即使使用更少的数据(少于100个示例)也可以提供出色的结果。 可以肯定地说,ULMFiT破解了NLP中迁移学习的密码。

这就是我们在NLP中建立迁移学习黄金法则的时候:

NLP中的迁移学习 =预训练和微调

ULMFIT之后的大多数NLP的突破调整了上述等式的组成部分,并获得了最先进的基准。

OpenAI的GPT

OpenAI的GPT扩展了ULMFiT和ELMo引入的预训练和微调方法。 GPT本质上用基于转换的体系结构取代了基于lstm的语言建模体系结构。

GPT模型可以微调到文档分类之外的多个NLP任务,如常识推理、语义相似性和阅读理解。

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

OpenAI的GPT通过实现多个最先进的技术,验证了Transformer架构的健壮性和有用性。

这就是Transformer如何启发了BERT以及接下来在NLP领域的所有突破。

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

BERT

因此,解决NLP任务的新方法变成了一个2步过程:

  1. 在大型无标签文本语料库(无监督或半监督)上训练语言模型

  2. 将这个大型模型微调到特定的NLP任务,以利用这个大型知识库训练模型(监督)

在这样的背景下,让我们来理解BERT是如何从这里开始建立一个模型,这个模型将在很长一段时间内成为NLP中优秀的基准。

BERT如何工作?

让我们仔细看一下BERT,了解为什么它是一种有效的语言建模方法。 我们已经知道BERT可以做什么,但是它是如何做到的?我们将在本节中回答这个相关问题。

1. BERT的体系结构

BERT架构建立在Transformer之上。 我们目前有两个可用的版本:

  • BERT Base:12层transformer,12个attention heads和1.1亿个参数

  • BERT Large:24层transformer,16个attention heads和3.4亿个参数

q6v6jqa.png!web

出于比较的目的,BERT基础架构具有与OpenAI的GPT相同的模型大小。 所有这些Transformer层都是只使用Transformer的 编码器

现在我们已经了解了BERT的总体架构,接下来让我们看看在进入模型构建阶段之前需要哪些文本处理步骤。

2.文本预处理

ueY3eez.png!web

BERT背后的开发人员已经添加了一组特定规则来表示模型的输入文本。 其中许多是创造性的设计选择,目的是使模型更好。

对于初学者,每个输入的Embedding是3个嵌入的组合:

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

  2. 段嵌入(Segment Embeddings) :BERT还可以将句子对作为任务的输入(可用于问答)。 这就是为什么它学习第一和第二句话的独特嵌入,以帮助模型区分它们。 在上面的例子中,所有标记为EA的标记都属于句子A(对于EB也是一样)

  3. 目标词嵌入(Token Embeddings):这些是从WordPiece词汇表中对特定词汇学习到的嵌入

对于给定的目标词,其输入表示是通过对相应的目标词、段和位置的Embedding进行求和来构造的。

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

这些预处理步骤的组合使BERT如此多才多艺。 这意味着,不需要对模型的体系结构进行任何重大更改,我们就可以轻松地对它进行多种NLP任务的训练。

3.预训练任务

BERT已接受两项NLP任务的预训练:

  • 屏蔽语言建模

  • 下一句预测

让我们更详细地了解这两个任务!

1. 屏蔽语言建模(双向性)

双向性

BERT被设计成一个 深度双向 模型。 网络有效地从第一层本身一直到最后一层捕获来自目标词的左右上下文的信息。

传统上,我们要么训练语言模型预测句子中的下一个单词(GPT中使用的从右到左的上下文),要么训练语言模型预测从左到右的上下文。 这使得我们的模型容易由于信息丢失而产生错误。

6NJZnyU.png!web

ELMo试图通过在左到右和从右到左的上下文中训练两个LSTM语言模型并对其进行浅级连接来解决此问题。 即使它在现有技术上有了很大的改进,但这还不够。

"凭直觉,我们有理由相信,深层双向模型比左向右模型或从左至右和从右至左模型的浅级连接严格更强大。" – BERT

这就是BERT在GPT和ELMo上都大大改进的地方。 看下图:

zua2InZ.jpg!web

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

从上图可以明显看出:BERT是双向的,GPT是单向的(信息仅从左向右流动),而ELMO是浅双向的。

关于屏蔽语言模型

假设我们有一句话——“我喜欢阅读关于分析数据科学的博客”。 我们想要训练一个双向的语言模型。 与其试图预测序列中的下一个单词,不如构建一个模型,从序列本身预测缺失的单词。

让我们把“分析”替换成“[MASK]”。 这是表示被屏蔽的单词。 然后,我们将以这样一种方式训练该模型,使它能够预测“分析”这个词语,所以句子变为:“我喜欢阅读关于[MASK]数据科学的博客”

这是掩蔽语言模型的关键所在。 BERT的作者还提出了一些注意事项,以进一步改进这项技术:

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

  • 掩码字并不总是被掩码令牌[掩码]替换,因为[掩码]令牌在调优期间不会出现

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

  • 80%的情况下,单词被替换成带面具的令牌[面具]

  • 10%的情况下,这些单词被随机替换

  • 有10%的时间单词是保持不变的

2. 下一句预测

掩蔽语言模型(MLMs)学习理解单词之间的关系。 此外, BERT还接受了下一个句子预测任务的训练,这些任务需要理解句子之间的关系

此类任务的一个很好的例子是问题回答系统。

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

由于它是一个二分类任务,因此可以通过将任何语料库分成句子对来轻松生成数据。 就像mlm一样,作者在这里也添加了一些注意事项。 让我们举个例子:

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

  • 对于50%的对来说,第二个句子实际上是第一个句子的下一个句子

  • 对于剩下的50%,第二句是语料库中的一个随机句子

  • 第一种情况的标签是“ IsNext ”,而第二种情况的标签是“ NotNext

这就是为什么BERT能够成为一个真正的任务不可知的模型。 它结合了掩蔽语言模型(MLM)和下一个句子预测(NSP)的预训练任务。

在Python中实现BERT以进行文本分类

你的头脑一定被BERT所开辟的各种可能性搅得团团转。 我们有许多方法可以利用BERT的大量知识来开发我们的NLP应用程序。

最有效的方法之一是根据你自己的任务和特定于任务的数据对其进行微调。 然后我们可以使用BERT中的Embedding作为文本文档的Embedding。

在本节中,我们将学习如何在NLP任务中使用BERT的Embedding。 我们将在以后的文章中讨论对整个BERT模型进行微调的概念。

为了从BERT中提取Embedding,我们将使用一个非常有用的开源项目:

https://github.com/hanxiao/bert-as-service

RrUvmuy.gif

这个开源项目如此有用的原因是它允许我们只需两行代码使用BERT获取每个句子的Embedding。

安装BERT-As-Service

服务以一种简单的方式工作。 它创建了一个BERT服务器。 每次我们将一个句子列表发送给它时,它将发送所有句子的Embedding。

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

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

注意,服务器必须在 Python >= 3.5 上运行,而 TensorFlow >= 1.10

此外,由于运行BERT是一个GPU密集型任务,我建议在基于云的GPU或其他具有高计算能力的机器上安装BERT服务器。

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

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

BERT-Base, Uncased    12-layer, 768-hidden, 12-heads, 110M parameters

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

BERT-Large, Uncased    24-layer, 1024-hidden, 16-heads, 340M parameters

https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-24_H-1024_A-16.zip

BERT-Base, Cased    12-layer, 768-hidden, 12-heads, 110M parameters

https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip

BERT-Large, Cased    24-layer, 1024-hidden, 16-heads, 340M parameters

https://storage.googleapis.com/bert_models/2018_10_18/cased_L-24_H-1024_A-16.zip

BERT-Base, Multilingual Cased (New)    104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters

https://storage.googleapis.com/bert_models/2018_11_23/multi_cased_L-12_H-768_A-12.zip

BERT-Base, Multilingual Cased (Old)    102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters

https://storage.googleapis.com/bert_models/2018_11_03/multilingual_L-12_H-768_A-12.zip

BERT-Base, Chinese    Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters

https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip

我们将下载BERT Uncased,然后解压缩zip文件:

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,试着获取以下句子的Embedding:“I love data science and analytics vidhya”。

from bert_serving.client import BertClient

# 使用BERT服务器的ip地址与它建立连接;如果是同一台电脑,不用填写IP
bc = BertClient(ip="SERVER_IP_HERE")
# 获取embedding
embedding = bc.encode(["I love data science and analytics vidhya."])
# 检查embedding的形状,应该是 1x768
print(embedding.shape)

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

返回的embedding形状为(1,768),因为BERT的架构中一个句子由768个隐藏单元表示。

问题:在Twitter上对不良言论进行分类

让我们拿一个真实世界的数据集来看看BERT有多有效。 我们将使用一个数据集,该数据集由一系列推文组成,这些推文被归类为“不良言论”或非“不良言论”。

为了简单起见,如果一条推文带有种族主义或性别歧视的情绪,我们就说它包含不良言论。 因此, 我们的任务是将种族主义或性别歧视的推文与其他推文进行分类

数据集链接

https://datahack.analyticsvidhya.com/contest/practice-problem-twitter-sentiment-analysis/?utm_source=blog&utm_medium=demystifying-bert-groundbreaking-nlp-framework。

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

以下是该项目的整体结构:

FFnMBz3.png!web

现在让我们看一下代码:

import pandas as pd
import numpy as np

# 加载数据
train = pd.read_csv('BERT_proj/train_E6oV3lV.csv', encoding='iso-8859-1')
train.shape

你会熟悉大多数人是如何发推特的。 有许多随机的符号和数字(又名聊天语言!)我们的数据集也一样。 我们需要在通过BERT之前对它进行预处理:

import re

# 清理噪声
def clean_text(text):
    # 只剩字符
    text = re.sub(r'[^a-zA-Z\']', ' ', text)

    # 去除unicode字符
    text = re.sub(r'[^\x00-\x7F]+', '', text)

    # 转换成小写
    text = text.lower()

    return text

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

现在数据集是干净的,它被分割成训练集和验证集:

from sklearn.model_selection import train_test_split

# 分割成训练集和验证集
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)

让我们在训练和验证集中获得所有推特的Embedding:

from bert_serving.client import BertClient

# 连接BERT服务器
bc = BertClient(ip="YOUR_SERVER_IP")
# 编码训练集和验证集
X_tr_bert = bc.encode(X_tr.tolist())
X_val_bert = bc.encode(X_val.tolist())

现在是建模时间!我们来训练分类模型:

from sklearn.linear_model import LogisticRegression

# 使用LR模型
model_bert = LogisticRegression()
# 训练
model_bert = model_bert.fit(X_tr_bert, y_tr)
# 预测
pred_bert = model_bert.predict(X_val_bert)

检查分类精度:

from sklearn.metrics import accuracy_score

print(accuracy_score(y_val, pred_bert))

即使使用如此小的数据集,我们也可以轻松获得大约95%的分类精度,这真的非常棒。

我鼓励你继续尝试BERT对不同问题进行尝试

超越BERT:目前最先进的NLP

BERT激发了人们对NLP领域的极大兴趣,特别是NLP任务中Transformer的应用。 这导致了研究实验室和组织的数量激增,他们开始研究预训练、BERT和fine-tuning的不同方面。

许多这样的项目在多个NLP任务上都比BERT做得好。 其中最有趣的是RoBERTa,这是Facebook人工智能对BERT和DistilBERT的改进,而DistilBERT是BERT的精简版和快速版。

VNvY7jj.gif

J3aYnyj.jpg!web长按扫码,关注我们

你与世界 只差一个

磐创AI

点击 阅读原文 ,获得更多精彩内容


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK