大家好,我是半虹,这篇文章来讲 BERT\text{BERT}BERT (Bidirectional Encoder Representations from Transformers)
原始论文请戳这里
从某种程度上来说,深度学习至关重要的一环就是表征学习,也就是学习如何得到数据的向量表示
对于自然语言领域,一个典型的场景是学习文本的向量表示,关于这一点,前人已经做了很多探索
从一开始的 one-hot\text{one-hot}one-hot,到后来的 word2vec\text{word2vec}word2vec,然后到 ELMo\text{ELMo}ELMo,最后到现在的 BERT\text{BERT}BERT 及其众多变种
这些技术的发展代表着自然语言表征学习的历史进程,为各项自然语言处理任务提供了有力的支撑
在这里,我们不会展开说每种技术的细节以及它们之间的发展关系,这些内容会在之后有一篇文章单独介绍
现在大家只需要清楚,本文介绍的 BERT\text{BERT}BERT 本质上就是一个语言表征模型,用来获取文本向量表示
事实上,BERT\text{BERT}BERT 的模型架构非常简单,就是 Transformer\text{Transformer}Transformer 的编码器,具体的结构如下图所示
编码器由若干个编码层堆叠而成,而且模型中上一个编码层的输出会作为下一个编码层的输入
编码层包括多头注意力机制和前馈神经网络两个子模块,每个子模块后有残差连接和层正则化
由于我们在上一篇文章中已经非常详细介绍过模型细节,所以这里就不再重复
BERT\text{BERT}BERT 有两个版本,它们的架构是一样的,不同之处仅在于超参,具体的区别如下 :
BERT-BASE
:编码层数量是 12
,多头注意力头数是 12
,隐藏层维度是 768
,总参数量是 110M
BERT-LARGE
:编码层数量是 24
,多头注意力头数是 16
,隐藏层维度是 1024
,总参数量是 340M
首先明确一件事情, BERT\text{BERT}BERT 这个模型的输入输出究竟是啥
给定一段文本序列, BERT\text{BERT}BERT 的目标是得到序列中每个词元的向量表示
然而,计算机是无法直接识别文本的,我们需要先将其转化成数字表示,这样才能够作为模型的输入
通常,只需要将输入文本经过嵌入层,得到对应每个词元的可学习向量,然后通过数据进行训练即可
除了词元本身之外,模型还需要词元的位置信息以及用于区分不同句子的分割信息
这些都可以通过添加不同的嵌入层解决,最后同一词元的各种嵌入会以按元素相加的方式来进行融合
这里注意,对于原始输入文本,模型会在其中插入一些特殊标记后才将其送进嵌入层,特殊标记如下:
[CLS]
:将该标记插入到文本开头,当模型训练完后,该标记的向量表示可表征整段文本的语义[SEP]
:当文本中包含两个句子时,将该标记插入到两个句子中间,可起到相当于分隔符的作用[PAD]
:将该标记插入到文本末尾,使得同一批量里文本长度相同,要注意该标记可以多次插入这样说可能还不是很清晰,下面看一个实际的例子:
最后再来对比一下 Transformer\text{Transformer}Transformer 和 BERT\text{BERT}BERT 两个模型间输入表示的差异
Transformer\text{Transformer}Transformer 的输入表示由词元表示、位置表示相加得到
BERT\text{BERT}BERT 的输入表示由词元表示、位置表示、分割表示相加得到
Transformer\text{Transformer}Transformer 的位置表示不需要学习,能够直接计算得到
BERT\text{BERT}BERT 的位置表示由嵌入层中的可学习向量经过数据训练得到
在 BERT\text{BERT}BERT 提出后,预训练和微调 ( pre-training and fine-tuning\text{pre-training and fine-tuning}pre-training and fine-tuning ) 这个训练范式逐渐流行起来
所谓预训练,就是让模型在大规模无标注数据集上针对预设任务进行训练,使模型获得某些基础能力
所谓的微调,就是让模型在特定的有标注数据集上针对下游任务继续训练,使模型适应不同下游任务
在预训练后,BERT\text{BERT}BERT 能够得到输入文本中每个词元的向量表示,这些向量表示是融合上下文信息的
在微调时候,BERT\text{BERT}BERT 使用预训练好的模型参数进行初始化,添加额外的预测层在不同下游任务继续训练
只要预训练得到的向量表示足够的好,那么预测层很容易就可以适应各种下游任务
就目前来说,预训练通常是在大规模的数据集上进行的,因此需要花费大量的计算资源
这一般都是由大公司主导并把预训练好的模型开源出来,大家去下载这些模型并在各自下游任务进行微调
进行微调的优点在于:针对不同下游任务,模型架构无需修改,只要添加不同的预测层继续训练即可
在预训练和微调这个范式里,对于预训练任务的设计至关重要,这决定了模型所具备的基础能力
BERT\text{BERT}BERT 设计的预训练任务,正是其大获成功的关键,具体为:
掩码语言模型可使 BERT\text{BERT}BERT 获得捕捉词元级别语义表示的能力
下一句子预测可使 BERT\text{BERT}BERT 获得捕捉句子级别语义表示的能力
这里值得说明的是,这两个任务都不需要标注的数据,只需要连续的句子即可,而这在互联网上随处可见
下面将会详细地介绍这两个任务的相关细节哈
先来说掩码语言模型
所谓的掩码语言模型,其做法就是将句子中的某些词掩盖起来,然后让模型去预测这些掩盖的词是啥
若对比单向语言模型,掩码语言模型最重要的改进在于:预测词语时能同时利用前后两个方向的信息
正是因为如此,掩码语言模型所学到的向量表示会比单向语言模型所学到的向量表示要好
所以像 BERT\text{BERT}BERT 这种将掩码语言模型作为预训练任务的预训练模型会更适合做特征提取器,用于判别式任务
反之像 GPT\text{GPT}GPT 这种将单向语言模型作为预训练任务的预训练模型会更适合做文本生成器,用于生成式任务
更加具体来说,BERT\text{BERT}BERT 会随机选择 15% 的词元,将其替换成特殊标记 [MASK]
,然后对这些标记进行预测
对应图示如下:
在预训练阶段,模型通过 [MASK]
标记显式确认要预测的位置在哪里
但在微调阶段,是不存在 [MASK]
标记的,这就导致两个阶段的行为不匹配 ( mismatch\text{mismatch}mismatch )
这个不匹配会导致什么呢 ?
模型在预训练阶段学到的是根据掩码标记的上下文得到其对应的向量表示,从而推测掩码标记对应的值
这可能会导致模型较少地考虑其他正常词语的向量表示,但是在微调阶段,输入文本全部都是正常词语
为了解决不匹配的问题,模型用了一些设置掩码的技巧
BERT\text{BERT}BERT 还是先随机选择 15% 的词元,对这些被选中的词元,会有以下三种处理方式:
之后,同样是对这些被选中的处理后的词元进行预测,但是,此时模型并不知道哪些词元是被替换掉的
这使得模型需要考虑每个位置上的词,如此在微调时,模型对所有的词都能得到一个比较好的向量表示
再来说下一句子预测
在自然语言处理领域,很多任务都需要考虑句子之间的关系,例如问答以及自然语言推理
而引入下一句子预测,有助于模型学习到这种关系,增强模型对于长文本的建模能力
模型会以一定的概率从语料库中随机抽取两个句子来作为输入:
要求模型判断出输入的两句话是否相关, 其实本质上就是一个二分类问题,图示如下:
经过预训练阶段后,我们就可以得到一个具备语言表征能力的 BERT\text{BERT}BERT 模型
然后,我们可以用该模型在特定数据集与下游任务继续训练,从而使得模型能够适应各种下游任务
这就是微调的作用
具体来说,就是在预训练好的 BERT\text{BERT}BERT 模型的基础上,针对不同下游任务添加对应的预测层就可以
然后,我们会以下游任务作为目标同时优化 BERT\text{BERT}BERT 和预测层的参数
通常,BERT\text{BERT}BERT 的参数只需要微调,而预测层的参数则需要重新学习
BERT\text{BERT}BERT 可完成的下游任务基本能够涵盖所有自然语言处理领域中的问题,具体可以分为以下几类:
四种下游任务对应的示意图如下:
前三个任务很容易理解,这里主要解释一下最后一个任务
所谓抽取式问答,就是说给定问题及对应的段落,问题的答案就是段落中某段连续的词元序列
这意味着,只要能识别出这个序列的开始位置和结束位置,就能找到答案所在的位置
因此模型对段落中的每个词元有两个预测层,一个用于预测开始位置,一个用于预测结束位置
此时预测层的作用是计算词元作为开始位置或作为结束位置的置信度
最后段落中所有词元作为开始位置的置信度最大的作为预测值,结束位置也用同样的方式得到
好啦,本文到此结束,感谢您的阅读!
如果你觉得这篇文章有阐述不够清晰的地方,欢迎在评论区留下你宝贵的意见或者建议
如果你觉得这篇文章还不错的话,欢迎点赞、收藏、关注,你的支持是对我最大的鼓励 (/ω\)
上一篇:Zookeeper 教程