2018年是机器学习模型处理文本(更准确地说,是自然语言处理或简称NLP)的转折点。我们对如何以一种最能捕捉潜在含义和关系的方式最好地表示单词和句子的概念理解正在迅速发展。此外,NLP社区已经推出了非常强大的组件,您可以免费下载并在自己的模型和管道中使用这些组件(它被称为 NLP’s ImageNet moment,,指的是多年前类似的开发如何加速(ULM-FiT has nothing to do with Cookie Monster. But I couldn’t think of anything else..)机器学习在计算机视觉任务中的发展)。
(ULM-FiT与Cookie Monster无关。但我想不出别的了。)
此开发的最新里程碑之一是BERT的发布,该事件被描述为NLP新时代的开始。BERT模型打破了模型处理基于语言的任务的记录。在描述模型的文章发布后不久,团队还开放了模型的源代码,并提供了模型的下载版本,这些版本已经在大规模数据集上进行了预先训练。这是一个重大的发展,因为它使任何构建包含语言处理的机器学习模型的人都可以使用这个强大的组件,从而节省从零开始训练语言处理模型所需的时间、精力、知识和资源。
BERT是如何发展的两个步骤。您可以下载步骤1中预训练的模型(针对未注释的数据进行训练),只需要为步骤2进行微调。[Source of 图书图标]。
BERT基于最近NLP社区中涌现的一些聪明的想法—包括但不限于 Semi-supervised Sequence Learning (by Andrew Dai and Quoc Le), ELMo (by Matthew Peters and researchers from AI2 and UW CSE), ULMFiT (by fast.ai founder Jeremy Howard and Sebastian Ruder), the OpenAI transformer (by OpenAI researchers Radford, Narasimhan, Salimans, and Sutskever), and the Transformer (Vaswani et al).
要正确地理解BERT是什么,我们需要了解许多概念。因此,在研究模型本身所涉及的概念之前,我们先来看看如何使用BERT。
Example: Sentence Classification
使用BERT最直接的方法是使用它对单个文本进行分类。这个模型应该是这样的:
要训练这样一个模型,您主要必须训练分类器,在训练阶段对BERT模型进行最小的更改。这种训练过程称为微调,其根源在于Semi-supervised Sequence Learning 和ULMFiT。
对于不熟悉这个主题的人,因为我们在讨论分类器,那么我们就处于机器学习的监督学习领域。这意味着我们需要一个标记数据集来训练这样的模型。对于这个垃圾邮件分类器示例,标记的数据集将是一个电子邮件消息列表和一个标签(每个消息对应“spam” or “not spam”)。
此类用例的其他示例包括:
- 情绪分析
输入:电影/产品评论。输出:评论是正面的还是负面的?
示例数据集:SST
- 事实检查
输入:句子。输出:“Claim”或“Not Claim”
更具雄心壮志/更具前瞻性的例子:
输入:Claim sentence。输出:“true”或“false”
Full Fact 是一个为公众利益构建自动事实检查工具的组织。他们的管道的一部分是一个分类器,它读取新闻文章并检测声明(将文本分类为“claim”或“not claim”),这些声明稍后可以进行事实检查(现在由人类进行,希望以后用ML进行)。
视频: Sentence embeddings for automated factchecking - Lev Konstantinovskiy.
模型架构
既然您已经有了一个关于如何使用Bert的示例用例,那么让我们仔细看看它是如何工作的。
本文给出了BERT的两种模型尺寸:
- BERT BASE-在大小上可与OpenAI Transformer相比较,以便比较性能
- BERT LARGE-一个不可想象的巨大的模型,它达到了在论文中报道的最先进的结果
BERT基本上是一个经过训练的Transformer编码器堆栈。这是一个很好的时间来指导您阅读我之前的文章, The Illustrated Transformer 这解释了Transformer模型——BERT的一个基本概念,以及我们接下来将要讨论的概念。
两种BERT模型的尺寸都有大量的编码器层(本文将其称为Transformer Blocks)——12层用于基本版本,24层用于大型版本。它们也具有更大的前馈网络(分别为768和1024个隐藏单元),并且注意头(分别为12和16)比初始论文中Transformer参考实现中的默认配置(6个编码器层,512个隐藏单元,8个注意头)更多。
Model Inputs
第一个输入token由一个特殊的[CLS] token提供,原因稍后将变得明显。CLS在这里代表分类。
就像Transformer的普通编码器一样,BERT接受一系列单词作为输入,这些单词不断地向上流动。每一层应用self-attention,并通过一个前馈网络传递其结果,然后将其传递给下一个编码器。
在体系结构方面,到目前为止,这与Transformer是相同的(除了大小,这只是我们可以设置的配置)。正是在输出处,我们开始看到事物是如何有分歧的。
Model Outputs
每个位置输出一个大小为hidden_size的向量 (768 in BERT Base)。对于上面看到的句子分类示例,我们只关注第一个位置(我们将特殊的[CLS]令牌传递给它)的输出。
这个向量现在可以用作我们选择的分类器的输入。本文采用单层神经网络作为分类器,取得了较好的分类效果。
如果您有更多的标签(例如,如果您是一个电子邮件服务,它用“spam”、“not spam”、“social”和“promotion”来标记电子邮件),您只需调整分类器网络,使其具有更多的输出神经元,然后通过softmax。
与卷积网络相似
对于那些有计算机视觉背景的人来说,这个向量传递应该会让人想起VGGNet这样的网络的卷积部分和网络末端的全连接的分类部分之间发生了什么。
一个嵌入的新时代
这些新发展带来了单词编码方式的新变化。到目前为止,单词嵌入一直是引领NLP模型处理语言的主要力量。像Word2Vec和Glove这样的方法已经被广泛用于这类任务。在指出现在发生了什么变化之前,让我们先回顾一下它们是如何使用的。
字嵌入回顾
对于要由机器学习模型处理的单词,它们需要某种形式的数字表示,以便模型在计算中使用。Word2Vec显示,我们可以用一个向量(数字的列表)正确代表单词的方式捕捉语义或meaning-related关系(如判断单词相似的能力,或对立,或一对词“斯德哥尔摩”和“瑞典”这样的词与“开罗”和“埃及”这样的词之间的关系)以及语法,或基于语法,关系(e.g. the relationship between “had” and “has” is the same as that between “was” and “is”)。
该领域很快意识到,使用预训练了大量文本数据的嵌入式是一个好主意,而不是在通常是小数据集的模型旁边训练它们。因此,通过使用Word2Vec或GloVe进行预训练,可以下载单词列表及其嵌入项。这是一个GloVe嵌入单词“stick”的例子(嵌入向量大小为200)
The GloVe word embedding of the word "stick" - a vector of 200 floats (rounded to two decimals). It goes on for two hundred values.
由于这些都是很大,而且充满了数字,我在我的贴子里用下面的基本形状来表示向量:
ELMo: Context Matters
如果我们使用GloVe表示,那么单词“stick”将由这个向量表示,不管上下文是什么。“等一下”,许多NLP研究人员(Peters et. al., 2017, McCann et. al., 2017, and yet again Peters et. al., 2018 in the ELMo paper )说,“stick”有多种含义,取决于它在哪里使用。为什么不给它一个基于它所使用的上下文的嵌入——既捕获该上下文中的单词含义,又捕获其他上下文信息?”于是,更符合实际的单词嵌入就诞生了。
上下文的单词嵌入可以根据单词在句子上下文中的含义提供不同的嵌入。RIP Robin Williams
ELMo没有为每个单词使用固定的嵌入,而是在为每个单词分配嵌入之前查看整个句子。它使用针对特定任务的双向LSTM来创建这些嵌入。
ELMo为NLP在上下文的预处理提供了一个重要的步骤。ELMo LSTM将使用数据集的语言在一个大型数据集上进行训练,然后我们可以将它用作需要处理语言的其他模型中的组件。
ELMo的秘诀是什么?
ELMo通过训练预测单词序列中的下一个单词获得了语言理解能力,这个任务称为语言建模。这很方便,因为我们有大量的文本数据,这样的模型可以在不需要标签的情况下学习这些数据。
ELMo训练前的一个步骤:给定“Let 's stick to”作为输入,预测下一个最有可能出现的单词——语言建模任务。当对大型数据集进行训练时,模型开始学习语言模式。它不太可能准确地猜出这个例子中的下一个单词。更现实的情况是,在“hang”这样的单词之后,它会给“out”(拼写为“hang out”)这样的单词赋予比“camera”更高的概率。
我们可以看到每个展开的lstm步骤的隐藏状态,从ELMo的头部后面逐渐显露出来。在这个预训练完成后,这些在嵌入过程中就派上用场了。
ELMo实际上更进一步,训练了一个双向的LSTM——这样它的语言模型不仅能感知下一个单词,还能感知上一个单词。
Great slides on ELMo
ELMo通过将隐藏状态(和初始嵌入)以某种方式组合在一起(连接后进行加权求和)来实现上下文嵌入。
ULM-FiT:确定NLP中的迁移学习
ULM-FiT引入了一些方法来有效地利用模型在培训前学到的很多东西——不仅仅是嵌入式,也不仅仅是上下文化嵌入式。ULM-FiT引入了一个语言模型和一个过程,可以有效地对该语言模型进行微调,以适应各种任务。
NLP终于有了一种可能和计算机视觉一样好的迁移学习方法。
Transformer:超越LSTMs
Transformer论文和代码的发布,以及它在机器翻译等任务上取得的成果,开始让一些业内人士认为它们是LSTMs的替代品。此外,Transformer比LSTMs更能处理长期依赖关系。
Transformer的编解码器结构使其非常适合机器翻译。但是你如何用它来进行句子分类呢?您将如何使用它来预训练可以为其他任务进行微调的语言模型(下游任务是该领域所称的那些使用预训练模型或组件的受监督学习任务)。
OpenAI Transformer:预训练用于语言建模的Transformer解码器
事实证明,我们不需要一个完整的Transformer来采用迁移学习和用于NLP任务的可调语言模型。我们只需要Transformer的解码器就可以了。解码器是一个很好的选择,因为它是语言建模(预测下一个单词)的自然选择,因为它构建的目的是屏蔽未来的令牌——这在逐字生成翻译时是一个有价值的特性。
OpenAI Transformer由Transformer的解码器堆栈组成
模型堆叠了12个解码器层。由于在这个设置中没有编码器,这些解码器层将不具有普通Transformer解码器层具有的编码器-解码器注意子层。不过,它仍然具有自我关注层(为了不让它在未来的令牌处达到峰值,它进行了屏蔽)。
有了这个结构,我们可以继续在相同的语言建模任务上训练模型:使用大量(未标记的)数据集预测下一个单词。只是,把7000本书的文字扔给它,让它学习! 书籍非常适合这类任务,因为它允许模型学习关联相关信息,即使它们被大量文本分隔——例如,当您使用tweet或文章进行训练时,您不会得到这些信息。
OpenAI Transformer现在已经准备好接受训练,在由7000本书组成的数据集中预测下一个单词。
将迁移学习应用到下游任务
既然OpenAI Transformer已经经过了预训练,并且它的层已经被调优以合理地处理语言,那么我们就可以开始将它用于下游任务。让我们先看看句子分类(将电子邮件分类为“垃圾邮件”或“非垃圾邮件”):
如何使用一个预先训练的OpenAI Transformer来进行句子分类
OpenAI论文概述了一些输入转换,用于处理不同类型任务的输入。下图显示了模型的结构和执行不同任务的输入转换。
Isn’t that clever?
BERT:从解码器到编码器
openAI transformer为我们提供了一个基于transformer的可调预训练模型。但是在从LSTMs到transformer的转换过程中,遗漏了一些东西。ELMo的语言模型是双向的,但是openAI transformer只训练一个向前的语言模型。我们能否构建一个基于转换的模型,它的语言模型既向前又向后看(用技术术语来说——“受左右上下文的制约”)?
“拿着我的啤酒”,said R-rated BERT.
Masked Language Model
“我们将使用transformer编码器” BERT说。
“这太疯狂了,”replied Ernie,“每个人都知道双向调节会让每个单词在多层语境中间接地看到自己。”
“我们要用masks” BERT自信地说。
BERT聪明的语言建模任务掩盖了输入中15%的单词,并要求模型预测缺失的单词。
找到正确的任务来训练编码器的Transformer堆栈是一个复杂的障碍,BERT采用了来自早期文献的“屏蔽语言模型”概念(在那里它被称为完形填空任务)来解决这个问题。
除了屏蔽15%的输入,BERT还混合了一些东西,以改进模型后来的微调方式。有时它会随机地将一个单词替换成另一个单词,并要求模型预测在那个位置上的正确单词。
Two-sentence Tasks
如果您回顾OpenAI transformer处理不同任务时所做的输入转换,您会注意到有些任务需要模型对两句话给出一些智能的解释(例如,它们是否只是彼此的改写版本?给定一个wikipedia条目作为输入,以及一个关于该条目作为另一个输入的问题,我们能回答这个问题吗?)
为了让BERT更好地处理多个句子之间的关系,训练前的过程包括一个额外的任务:给定两个句子(A和B), B是否可能是A后面的句子?
BERT预训练的第二个任务是一个两句话的分类任务。在这个图中,标记化被过分简化了,因为BERT实际上使用单词片段而不是单词作为标记——所以有些单词被分解成更小的块。
Task specific-Models
BERT的论文展示了许多将BERT用于不同任务的方法。
BERT用于特征提取
微调方法并不是使用BERT的唯一方法。就像ELMo一样,您可以使用预先训练好的BERT创建上下文化的单词嵌入。然后,您可以将这些嵌入提供给您现有的模型——本文展示了一个过程,在对BERT进行诸如名称实体识别之类的任务进行微调后,其结果与BERT相差不远。
哪种向量作为上下文嵌入效果最好?我认为这取决于任务。本文考察了6个选项(与调优后的96.4分模型相比):
带BERT出去兜风
测试BERT的最佳方法是通过托管在谷歌Colab上的BERT FineTuning with Cloud TPUs笔记本。如果您以前从未使用过云TPUs,那么这也是一个很好的起点,您可以尝试使用它们,并且BERT代码也适用于TPUs、cpu和gpu。
下一步将是查看 BERT repo中的代码:
- 该模型是在modeling.py (
class BertModel
) 中构造的,与普通的Transformer编码器非常相似。 - run_classifier.py 是微调过程的一个例子。并构建了监督模型的分类层。如果您想构造自己的分类器,请查看该文件中的
create_model()
方法。 - 有几个预先训练的模型可供下载。这些数据涵盖了BERT Base和BERT Large,以及英语、汉语等语言,以及一个涵盖了在wikipedia上训练的102种语言的多语言模型。
- BERT不把words当作标记。相反,它关注的是WordPieces。 tokenization.py 是将您的words转换为适合BERT的wordPieces的tokenizer。
您还可以查看BERT的 PyTorch implementation of BERT.。 AllenNLP 库使用这个实现对任何模型 allow using BERT embeddings 。