本篇文章共 15326 个词,一个字一个字手码的不容易,转载请标明出处:
BERT 模型详解 - 二十三岁的有德html
BERT 在天然语言处理(NLP)领域刷新了 11 个任务的记录,万众瞩目,成为异常火热的一个预训练语言模型。python
今天咱们就来详细讲解下 BERT 这个预训练语言模型,虽然 BERT 刷新了各个任务的记录,可是 BERT 并非一个具备极大创新的算法,更多的是一个集大成者,把 BERT 以前各个语言模型的优势集于一身,并做出了适当的改进,而拥有了现在无与伦比的能力。git
BERT 既然是集大成者,那么集了哪些大成呢?github
既然是集大成者,那么咱们只要把上述五点大成一一解释清楚,相信再带你看 BERT 时,你必定会豁然开朗:“BERT 不过如此”。算法
在叙述图像领域的预训练以前,咱们经过下图来看看 CNN 是如何对图片作特征提取的:一张图片放入到 CNN 中,由浅层到深层api
经过上述分析,也就是说,CNN 浅层提取的特征对于各个任务而言都是通用的。网络
图 1 - CNN 提取图片特征架构
聪明的读者应该想到了,什么是 “预训练”,但很懵懵懂懂,下面咱们来详细解释。app
图 2 - 预训练在图像领域的应用框架
假设咱们拥有两个尽可能类似的任务 A 和 B(能够都为图像处理任务),任务 A 是咱们目标任务,任务 B 是能够提早训练好的一个任务:
上述两个步骤便是预训练的思想,那么预训练有什么好处呢?
在讲解 CBOW 思想以前,咱们不得不讲解下词向量的发展史,这算是预训练语言模型的始祖,语言模型的预训练的思想最先来源于此,而且 CBOW 模型就是为了生成单词的词向量应运而生。
One-hot 编码相信你们都很熟悉了,一个很简单的小知识,让计算机认识现实世界中的单词。
因为计算机并不认识单词 “time、fruit……”,为此,人们构建一个词典 \(V\) ,以下图所示词典包含 8 个单词,对于词典中的每一个单词,都有之对应的 \(|V|\)(词典大小的)维度的向量表示,以下图的 “time” 对应的向量为 8 维的 “1000 0000”。
图 3 - 词的 One-hot 编码
One-hot 编码虽然解决了计算机不认识单词的这一缺陷,可是 One-hot 编码自己也是有问题的,对于上图 “fruit” 和 “banana” 的 One-hot 编码,能够经过余弦类似度计算二者的类似性,能够发现结果为 0,也就是二者绝不相关,然而实际上二者是相关的,由此,词向量应运而生。
图 4 - 神经网络语言模型架构
如今让咱们看看词向量究竟是个什么玩意儿,对于上述的 3 层神经网络语言模型(NNLM),它的学习任务是输入某个句中单词 \(w_t = banana\) 前的 \(t-1\) 个单词,要求网络正确预测单词 “banana”,即最大化:
其中 \(\theta\) 是 NNLM 的参数。
首先输入前 \(t-1\) 个单词的 One-hot 编码
单词的 One-hot 编码通过第一层,输入层,One-hot 编码左乘矩阵 Q 获得词向量 \(C(w_i)\)
词向量 \(C(w_i)\) 通过第二层,隐层(至关于全链接层),隐层的输出为 \(\tanh(Wx+d)\),其中 \(x\) 为输入的词向量,\(W\) 为权重矩阵,\(d\) 为偏置,\(\tanh\) 为激活函数。
[11, 22, 55, 44, 87, 84, 88]
第二层输出的向量通过第三层,输出层,通过 Softmax 函数归一化。
假设输入为一个输出为一个 8 维的向量 [11, 22, 55, 44, 87, 84, 88]
,则该层输出为 [0.000, 0.000, 0.000, 0.000, 0.265, 0.013, 0.721]
,对应图三,则该层输出的对应的词为 “banana”
y = torch.FloatTensor([11, 22, 55, 44, 87, 84, 88]) print(functional.softmax(y, dim=0)) # 输出: [0.000, 0.000, 0.000, 0.000, 0.265, 0.013, 0.721]
在讲解 NNLM 的过程当中,能够看到该模型的一个副产品词向量 \(C(w_i)\),这就是大名鼎鼎的词向量。
这个词向量是如何解决 One-hot 编码对于本应该类似的单词而不类似的问题的呢?假设矩阵 Q 己经被成功训练,下面咱们来计算 “banana” 的词向量:
同理 “fruit” 的词向量为 [23,5,7]
,二者的余弦类似度为 “0.638”,能够发现词向量顺利的解决了词类似的问题。
y1 = torch.FloatTensor([7, 6, 18]) y2 = torch.FloatTensor([23, 5, 7, ]) print(torch.cosine_similarity(y1, y2, dim=0)) # 输出:0.638
上过构建词向量的过程被称做 Word Embedding,并且细心地读者应该发现训练矩阵 Q 并获得词向量的过程其实就是一个预训练的过程,对于将来的 NLP 的任务,直接使用训练获得的矩阵 Q 和词向量便可。
下图给出网上找的几个例子,能够看出有些效果仍是蛮不错的:
图 5 - Word Embedding 例子
NNLM 的学习任务是输入某个句中单词 \(w_t = banana\) 前的 \(t-1\) 个单词,要求网络正确预测单词 “banana”,矩阵 Q 和词向量都只是它的副产品。
为此,2013 年出现了一个最火的用语言模型专门作 Word Embedding 的工具 Word2Vec ,它的架构以下图所示:
图 6 - Word2Vec 模型
因为 Word2Vec 专门用来作 Word Embedding,它再也不是利用前 \(t-1\) 个单词,而是利用了上下文信息,这个也是很好理解的,对于一个单词的解释,利用上下文的信息做出的解释会更合理。
其实 Word2Vec 和 NNLM 很是相似,都是三层架构,除了利用了上下文信息,Word2Vec 还提供了 2 种训练方法:
当你看到 CBOW 的时候,想必你也想到了为何要在这里讲讲 Word2Vec 的缘由,由于将来的 BERT 将会用到 CBOW 的思想:利用单词 \(w_t\) 上下文的信息,预测单词 \(w_t\)
Attention 机制来源于人类的视觉注意力机制:人类视觉经过快速扫描全局图像,得到须要重点关注的目标区域,也就是通常所说的注意力焦点,然后对这一区域投入更多注意力资源,以获取更多所须要关注目标的细节信息,而抑制其余无用信息。
对于下图,若是只关心颜色,咱们可能第一眼就会把重心放在显眼的红色上,而后再观察其余地方。
图 7 - 婴儿图像
经过对人类视觉注意力机制的描述,Attention 机制简单点讲就是:经过一个 Query(查询变量)从一堆信息(Key-Value 表示的信息,能够把 Key 看做信息的地址,Value 表示信息的内容)中找到对于 Query 而言重要的信息,以此获取更多对于 Query 而言更重要的细节信息,进而抑制其余无用信息。
Attention 的具体作法以下图所示:
图 8 - Attention 计算图
计算 Q 和 K 的类似度,用 f 来表示:\(f(Q,K_i)\quad i=1,2,\cdots,m\),Self-Attention 模型中 \(f(Q,K_i) = Q^TK_i\)
第二步:将获得的类似度进行 softmax 操做,进行归一化:\(\alpha_i = softmax(\frac{f(Q,K_i)}{\sqrt d_k})\)
这里简单讲解除以 \(\sqrt d_k\) 的做用:假设 \(Q\) , \(K\) 里的元素的均值为0,方差为 1,那么 \(A^T=Q^TK\) 中元素的均值为 0,方差为 d。当 d 变得很大时, \(A\) 中的元素的方差也会变得很大,若是 \(A\) 中的元素方差很大(分布的方差大,分布集中在绝对值大的区域),在数量级较大时, softmax 将几乎所有的几率分布都分配给了最大值对应的标签,因为某一维度的数量级较大,进而会致使 softmax 将来求梯度时会消失。总结一下就是 \(\operatorname{softmax}\left(A\right)\) 的分布会和d有关。所以 \(A\) 中每个元素乘上 \(\frac{1}{\sqrt{d_k}}\) 后,方差又变为 1,而且 \(A\) 的数量级也将会变小。
y1 = torch.FloatTensor([11, 22, 55, 44, 87, 84, 88]) y2 = torch.FloatTensor([11, 22, 55, 44, 80, 80, 88]) print(functional.softmax(y1, dim=0)) # 输出: [0.000, 0.000, 0.000, 0.000, 0.265, 0.013, 0.721] print(functional.softmax(y2, dim=0)) # 输出:[0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.999]
针对计算出来的权重 \(\alpha_i\),对 \(V\) 中的全部 values 进行加权求和计算,获得 Attention 向量:\(Attention = \sum_{i=1}^m \alpha_i V_i\)
Self-Attention 模型是 Attention 机制的具体应用,下面咱们给出 Self-Attention 的架构图:
图 9 - Self-Attention 模型架构图
经过上图能够看到 Self Attention 有三个输入 Q、K、V:对于 Self Attention,Q、K、V 来自输入句子 X 的 词向量 x 的线性转化,即对于词向量 x,给定三个可学习的矩阵参数 \(W_Q,W_k,W_v\),x 分别右乘上述矩阵获得 Q、K、V。这也是Self-Attention 名字的来源:Q、K、V 三个矩阵由同一个词向量线性转化而得。
下面咱们来详细了解下 Self-Attention 的具体计算流程:
第一步,Q、K、V 的获取
、
图 10 - Q、K、V 矩阵的获取
上图操做:两个单词 Thinking 和 Machines。经过线性变换,即 \(x_i\) 和 \(x_2\) 两个向量分别与\(W_q,W_k,W_v\) 三个矩阵点乘获得 ${q_1,q_2},{k_1,k_2},{v_1,v_2} $ 共 6 个向量。矩阵 Q 则是向量 \(q_1,q_2\) 的拼接,K、V 同理。
第二步,MatMul
图 11 - \(q_1\) 和 \(k一、k_2\) 的计算
上图操做:向量 \({q_1,k_1}\) 作点乘获得得分 112, \({q_1,k_2}\) 作点乘获得得分96。注意:这里是经过 \(q_1\) 这个信息找到 \(x_1,x_2\) 中的重要信息。
第三步和第四步,Scale + Softmax
图 12 - \(q_1\) 和 \(k一、k_2\) 的计算作 Softmax
上图操做:对该得分进行规范,除以 \(\sqrt {d_k} = 8\)
第五步,MatMul
图 13 - \(z_1\) 的计算
用得分比例 [0.88,0.12] 乘以 \([v_1,v_2]\) 值获得一个加权后的值,将这些值加起来获得 \(z_1\)。
上述所说就是 Self Attention 模型所作的事,仔细感觉一下,用 \(q_1\)、\(K=[k_1,k_2]\) 去计算一个 Thinking 相对于 Thinking 和 Machine 的权重,再用权重乘以 Thinking 和 Machine 的 \(V=[v_1,v_2]\) 获得加权后的 Thinking 和 Machine 的 \(V=[v_1,v_2]\),最后求和获得针对各单词的输出 \(z_1\)。
同理能够计算出 Machine 相对于 Thinking 和 Machine 的加权输出 \(z_2\)。
上述所计算的结果 \(z_1\) 就能够看做是查询变量 Thinking 的词向量,而且是 Thinking 从 Thinking Machine 这句话中找到了对于 Thinking 而言更为重要的信息,而且抑制了不重要的信息。这样讲,可能没法理解 Self-Attention 机制的好处,咱们能够经过下图来了解 Self-Attention 机制的优势到底在哪里:
图 14 - attention 获取句法特征
图 15 - attention 获取语义特征
从图 14 能够看出,Self-Attention 捕获了同一个句子中单词之间的一些句法特征(有必定距离的短语结构);从图 15 能够看出,Self-Attention 捕获了同一个句子中单词之间的语义特征(“its” 的指代对象 “Law”)。
而且从上面的计算步骤和图片能够看出,不管句子序列多长,均可以很好的提取句子特征,而且既能够提取句法特征还能够提取语义特征,这很好的解决了 RNN 序列长距离依赖的问题,并且对于一个句子而言,每一个单词的计算是能够并行处理的。
图 16 - RNN 结构图
上述例子是 Self-Attention 单个向量运算的例子。下图展现的是 Self-Attention 的矩阵运算的例子,输入是一个 [2x4] 的矩阵(句子中每一个单词的词向量的拼接),每一个运算是 [4x3] 的矩阵,求得 Q、K、V。
Q 对 K 转制作点乘,除以 \(\sqrt d_k\),作一个 softmax 获得合为 1 的比例,对 V 作点乘获得输出 Z。那么这个 Z 就是一个考虑过 Thinking 周围单词 Machine 的输出。
注意看这个公式,\(QK^T\) 其实就会组成一个 word2word 的 attention map!(加了 softmax 以后就是一个合为 1 的权重了)。好比说你的输入是一句话 "i have a dream" 总共 4 个单词,这里就会造成一张 4x4 的注意力机制的图:
这样一来,每个单词对应每个单词都会有一个权重,这也是 Self Attention 名字的来源,即 Attention 的计算来源于 Source(源句) 和 Source 自己,通俗点讲就是 Q、K、V 都来源于输入 X 自己。
趁热打铁,咱们讲讲 Transformer 将来会用到的 Masked Self Attention 模型,这里的 Masked 就是要在作语言模型(或者像翻译)的时候,不给模型看到将来的信息,它的结构以下图所示:
上图中和 Self Attention 重复的部分此处就不讲了,主要讲讲 Mask 这一块。
假设在此以前咱们已经经过 scale 以前的步骤获得了一个 attention map,而 mask 就是沿着对角线把灰色的区域用0覆盖掉,不给模型看到将来的信息,以下图所示:
详细来讲:
而且在作完 softmax 以后,横轴结果合为 1。以下图所示:
具体为何 在 Transformer 中要用到 Masked Self Attention,将来在讲解 Transformer 的时候会详细解释。
因为 Transformer 使用的都是 Self Attention 的进阶版 Multi-head Self Attention,咱们简单讲讲 Multi-head Self Attention 的架构,而且在该小节结尾处讲讲它的优势。
Multi-Head Attention 就是把 Self Attention的过程作 H 次,而后把输出 Z 合起来。论文中,它的结构图以下:
咱们仍是以上面的形式来解释,首先,咱们使用 8 组不一样的 \(W_Q^i,W_k^i,W_V^i\quad i=1,2,\cdots,8\) ,重复 8 次和 Self Attention 类似的操做,获得 8 个 \(Z_i\) 矩阵:
为了使得输出与输入结构相同,拼接矩阵 \(Z_i\) 后乘以一个线性 \(W_0\) 获得最终的Z:
看完了 Multi-head Self Attention 的架构,发现它与 Self Attention 的区别,在于用了 \(n\) 组 \(W_Q^i,W_k^i,W_V^i\quad i=1,2,\cdots,n\) 获得 \(n\) 组 \(Q_i,K_i,V_i \quad i=1,2,\cdots,n\)。
能够经过下图看看 multi-head attention 的整个流程:
上述操做有什么好处呢?使用多套参数,多套参数至关于把原始信息 Source 放入了多个子空间中,也就是捕捉了多个信息,对于使用 multi-head(多头) attention 的简单回答就是,多头保证了 attention 能够注意到不一样子空间的信息,捕捉到更加丰富的特征信息。其实本质上是论文原做者发现这样效果确实好。
在 Attention 和 RNN 的对比中,咱们说到 Attention 解决了长距离依赖问题,而且能够支持并行化,可是它就真的百利而无一害了吗?
其实否则,咱们往前回顾,Self Attention 的 Q、K、V 三个矩阵是由同一个输入 \(X_1=(x_1,x_2,\cdots,x_n)\) 线性转换而来,也就是说对于这样的一个被打乱序列顺序的 \(X_2=(x_2,x_1,\cdots,x_n)\) 而言,因为 Attention 值的计算最终会被加权求和,也就是说二者最终计算的 Attention 值都是同样的,进而也就代表了 Attention 丢掉了 \(X_1\) 的序列顺序信息。
如上图所示,为了解决 Attention 丢失的序列顺序信息,Transformer 的提出者提出了 Position Embedding,也就是对于输入 \(X\) 进行 Attention 计算以前,在 \(X\) 的词向量中加上位置信息,也就是说 \(X\) 的词向量为 \(X_{final\_embedding} = Embedding + Positional\, Embedding\)
可是如何获得 \(X\) 的位置向量呢?
其中位置编码公式以下图所示:
其中 pos 表示位置、i 表示维度、\(d_{model}\)表示位置向量的向量维度 、\(2i、2i+1\) 表示的是奇偶数(奇偶维度),上图所示就是偶数位置使用 \(\sin\) 函数,奇数位置使用 \(\cos\) 函数。
有了位置编码,咱们再来看看位置编码是如何嵌入单词编码的(其中 512 表示编码维度),经过把单词的词向量和位置向量进行叠加,这种方式就称做位置嵌入,以下图所示:
Position Embedding 自己是一个绝对位置的信息,但在语言模型中,相对位置也很重要。那么为何位置嵌入机制有用呢?
咱们不要去关心三角函数公式,能够看看下图公式(3)中的第一行,咱们作以下的解释,对于 “我爱吃苹果” 这一句话,有 5 个单词,假设序号分别为 一、二、三、四、5。
假设 \(pos=1=我、k=2=爱、pos+k=3=吃\),也就是说 \(pos+k=3\) 位置的位置向量的某一维能够经过 \(pos=1\) 位置的位置向量的某一维线性组合加以线性表示,经过该线性表示能够得出 “吃” 的位置编码信息蕴含了相对于前两个字 “我” 的位置编码信息。
总而言之就是,某个单词的位置信息是其余单词位置信息的线性组合,这种线性组合就意味着位置向量中蕴含了相对位置信息。
万事俱备,只欠东风,下面咱们来说讲咱们的重点之一,Transformer,你能够先记住这一句话:Transformer 简单点看其实就是 self-attention 模型的叠加,首先咱们来看看 Transformer 的总体框架。
Transformer 的总体框架以下图所示:
上图所示的总体框架乍一眼一看很是复杂,因为 Transformer 起初是做为翻译模型,所以咱们以翻译举例,简化一下上述的总体框架:
从上图能够看出 Transformer 至关于一个黑箱,左边输入 “Je suis etudiant”,右边会获得一个翻译结果 “I am a student”。
再往细里讲,Transformer 也是一个 Seq2Seq 模型(Encoder-Decoder 框架的模型),左边一个 Encoders 把输入读进去,右边一个 Decoders 获得输出,以下所示:
在这里,咱们穿插描述下 Encoder-Decoder 框架的模型是如何进行文本翻译的:
- 将序列 \((x_1,x_2,\cdots,x_n)\) 做为 Encoders 的输入,获得输出序列 \((z_1,z_2,\cdots,z_n)\)
- 把 Encoders 的输出序列 \((z_1,z_2,\cdots,z_n)\) 做为 Decoders 的输入,生成一个输出序列 \((y_1,y_2,\cdots,y_m)\)。注:Decoders 每一个时刻输出一个结果
第一眼看到上述的 Encodes-Decoders 框架图,随之产生问题就是 Transformer 中 左边 Encoders 的输出是怎么和右边 Decoders 结合的。由于decoders 里面是有N层的,再画张图直观的看就是这样:
也就是说,Encoders 的输出,会和每一层的 Decoder 进行结合。
如今咱们取其中一层进行详细的展现:
经过上述分析,发现咱们想要详细了解 Transformer,只要了解 Transformer 中的 Encoder 和 Decoder 单元便可,接下来咱们将详细阐述这两个单元。
有了上述那么多知识的铺垫,咱们知道 Eecoders 是 N=6 层,经过上图咱们能够看到每层 Encoder 包括两个 sub-layers:
注意:在每一个 sub-layer 咱们都模拟了残差网络(在下面的数据流示意图中会细讲),每一个sub-layer的输出都是 \(LayerNorm(x+Sub\_layer(x))\),其中 \(sub\_layer\) 表示的是该层的上一层的输出
如今咱们给出 Encoder 的数据流示意图,一步一步去剖析
须要注意的是,上述的 \(x、z、r\) 都具备相同的维数,论文中为 512 维。
Decoders 也是 N=6 层,经过上图咱们能够看到每层 Decoder 包括 3 个 sub-layers:
以上,就讲完了 Transformer 编码和解码两大模块,那么咱们回归最初的问题,将 “机器学习” 翻译成 “machine learing”,解码器的输出是一个浮点型的向量,怎么转化成 “machine learing” 这两个词呢?让咱们来看看 Encoders 和 Decoders 交互的过程寻找答案:
从上图能够看出,Transformer 最后的工做是让解码器的输出经过线性层 Linear 后接上一个 softmax
假设词汇表维度是 6,那么输出最大几率词汇的过程以下:
首先咱们来看看拿 Transformer 做翻译时,如何生成翻译结果的:
继续进行:
假设上图是训练模型的某一个阶段,咱们来结合 Transformer 的完整框架描述下这个动态的流程图:
如今咱们来解释咱们以前遗留的两个问题。
训练阶段:咱们知道 “je suis etudiant” 的翻译结果为 “I am a student”,咱们把 “I am a student” 的 Embedding 输入到 Decoders 里面,翻译第一个词 “I” 时
测试阶段:咱们不知道 “我爱中国” 的翻译结果为 “I love China”,咱们只能随机初始化一个 Embedding 输入到 Decoders 里面,翻译第一个词 “I” 时:
总结下就是:Decoder 作 Mask,是为了让训练阶段和测试阶段行为一致,不会出现间隙,避免过拟合
咱们在讲解 Attention 机制中曾提到,Query 的目的是借助它从一堆信息中找到重要的信息。
如今 Encoder 提供了 \(K_e、V_e\) 矩阵,Decoder 提供了 \(Q_d\) 矩阵,经过 “我爱中国” 翻译为 “I love China” 这句话详细解释下。
当咱们翻译 “I” 的时候,因为 Decoder 提供了 \(Q_d\) 矩阵,经过与 \(K_e、V_e\) 矩阵的计算,它能够在 “我爱中国” 这四个字中找到对 “I” 翻译最有用的单词是哪几个,并以此为依据翻译出 “I” 这个单词,这就很好的体现了注意力机制想要达到的目的,把焦点放在对本身而言更为重要的信息上。
BERT 模型能够做为公认的里程碑式的模型,可是它最大的优势不是创新,而是集大成者,而且这个集大成者有了各项突破,下面让咱们看看 BERT 是怎么集大成者的。
以下图所示,咱们来看看 ELMo、GPT 和 BERT 三者的区别
BERT 的模型结构以下图所示:
从上图能够发现,BERT 的模型结构其实就是 Transformer Encoder 模块的堆叠。在模型参数选择上,论文给出了两套大小不一致的模型。
\(BERT_{BASE}\) :L = 12,H = 768,A = 12,总参数量为 1.1 亿
\(BERT_{LARGE}\):L = 24,H = 1024,A = 16,总参数量为 3.4 亿
其中 L 表明 Transformer Block 的层数;H 表明特征向量的维数(此处默认 Feed Forward 层中的中间隐层的维数为 4H);A 表示 Self-Attention 的头数,使用这三个参数基本能够定义 BERT的量级。
BERT 参数量级的计算公式:
训练过程也是很花费计算资源和时间的,总之表示膜拜,普通人即使有 idea 没有算力也只能跪着。
BERT 采用二段式训练方法:
不一样于 GPT 等标准语言模型使用 \(P(w_i|w_1,\cdots,w_{i-1})\) 为目标函数进行训练,能看到全局信息的 BERT 使用 \(P(w_i|w_1,\cdots,w_{i-1},w_{i+1},\cdots,w_n)\) 为目标函数进行训练。
而且 BERT 用语言掩码模型(MLM)方法训练词的语义理解能力;用下句预测(NSP)方法训练句子之间的理解能力,从而更好地支持下游任务。
BERT 做者认为,使用自左向右编码和自右向左编码的单向编码器拼接而成的双向编码器,在性能、参数规模和效率等方面,都不如直接使用深度双向编码器强大,这也是为何 BERT 使用 Transformer Encoder 做为特征提取器,而不使用自左向右编码和自右向左编码的两个 Transformer Decoder做为特征提取器的缘由。
因为没法使用标准语言模型的训练模式,BERT 借鉴完形填空任务和 CBOW 的思想,使用语言掩码模型(MLM )方法训练模型。
MLM 方法也就是随机去掉句子中的部分 token(单词),而后模型来预测被去掉的 token 是什么。这样实际上已经不是传统的神经网络语言模型(相似于生成模型)了,而是单纯做为分类问题,根据这个时刻的 hidden state 来预测这个时刻的 token 应该是什么,而不是预测下一个时刻的词的几率分布了。
随机去掉的 token 被称做掩码词,在训练中,掩码词将以 15% 的几率被替换成 [MASK],也就是说随机 mask 语料中 15% 的 token,这个操做则称为掩码操做。注意:在CBOW 模型中,每一个词都会被预测一遍。
可是这样设计 MLM 的训练方法会引入弊端:在模型微调训练阶段或模型推理(测试)阶段,输入的文本中将没有 [MASK],进而致使产生由训练和预测数据误差致使的性能损失。
考虑到上述的弊端,BERT 并无总用 [MASK] 替换掩码词,而是按照必定比例选取替换词。在选择 15% 的词做为掩码词后这些掩码词有三类替换选项:
“地球是[MASK]八大行星之一”
“地球是太阳系八大行星之一”
“地球是苹果八大行星之一”
做者在论文中提到这样作的好处是,编码器不知道哪些词须要预测的,哪些词是错误的,所以被迫须要学习每个 token 的表示向量,另外做者也表示双向编码器比单项编码器训练要慢,进而致使BERT 的训练效率低了不少,可是实验也证实 MLM 训练方法可让 BERT 得到超出同期全部预训练语言模型的语义理解能力,牺牲训练效率是值得的。
在不少天然语言处理的下游任务中,如问答和天然语言推断,都基于两个句子作逻辑推理,而语言模型并不具有直接捕获句子之间的语义联系的能力,或者能够说成单词预测粒度的训练到不了句子关系这个层级,为了学会捕捉句子之间的语义联系,BERT 采用了下句预测(NSP )做为无监督预训练的一部分。
NSP 的具体作法是,BERT 输入的语句将由两个句子构成,其中,50% 的几率将语义连贯的两个连续句子做为训练文本(连续句对通常选自篇章级别的语料,以此确保先后语句的语义强相关),另外 50% 的几率将彻底随机抽取两个句子做为训练文本。
连续句对:[CLS]今每天气很糟糕[SEP]下午的体育课取消了[SEP]
随机句对:[CLS]今每天气很糟糕[SEP]鱼快被烤焦啦[SEP]
其中 [SEP] 标签表示分隔符。 [CLS] 表示标签用于类别预测,结果为 1,表示输入为连续句对;结果为 0,表示输入为随机句对。
经过训练 [CLS] 编码后的输出标签,BERT 能够学会捕捉两个输入句对的文本语义,在连续句对的预测任务中,BERT 的正确率能够达到 97%-98%。
BERT 在预训练阶段使用了前文所述的两种训练方法,在真实训练中通常是两种方法混合使用。
因为 BERT 经过 Transformer 模型堆叠而成,因此 BERT 的输入须要两套 Embedding 操做:
对于分割编码,Segment Embeddings 层只有两种向量表示。前一个向量是把 0 赋给第一个句子中的各个 token,后一个向量是把 1 赋给第二个句子中的各个 token ;若是输入仅仅只有一个句子,那么它的 segment embedding 就是全 0,下面咱们简单举个例子描述下:
[CLS]I like dogs[SEP]I like cats[SEP] 对应编码 0 0 0 0 0 1 1 1 1
[SEP]I Iike dogs and cats[SEP] 对应编码 0 0 0 0 0 0 0
BERT 根据天然语言处理下游任务的输入和输出的形式,将微调训练支持的任务分为四类,分别是句对分类、单句分类、文本问答和单句标注,接下来咱们将简要的介绍下 BERT 如何经过微调训练适应这四类任务的要求。
给定两个句子,判断它们的关系,称为句对分类,例如判断句对是否类似、判断后者是否为前者的答案。
针对句对分类任务,BERT 在预训练过程当中就使用了 NSP 训练方法得到了直接捕获句对语义关系的能力。
以下图所示,句对用 [SEP] 分隔符拼接成文本序列,在句首加入标签 [CLS],将句首标签所对应的输出值做为分类标签,计算预测分类标签与真实分类标签的交叉熵,将其做为优化目标,在任务数据上进行微调训练。
针对二分类任务,BERT 不须要对输入数据和输出数据的结构作任何改动,直接使用与 NSP 训练方法同样的输入和输出结构就行。
针对多分类任务,须要在句首标签 [CLS] 的输出特征向量后接一个全链接层和 Softmax 层,保证输出维数与类别数目一致,最后经过 arg max 操做(取最大值时对应的索引序号)获得相对应的类别结果。
下面给出句对分类似性任务的实例:
任务:判断句子 “我很喜欢你” 和句子 “我很中意你” 是否类似
输入改写:“[CLS]我很喜欢你[SEP]我很中意你”
取 “[CLS]” 标签对应输出:[0.02, 0.98]
经过 arg max 操做获得类似类别为 1(类别索引从 0 开始),即两个句子类似
给定一个句子,判断该句子的类别,统称为单句分类,例如判断情感类别、判断是否为语义连贯的句子。
针对单句二分类任务,也无须对 BERT 的输入数据和输出数据的结构作任何改动。
以下图所示,单句分类在句首加入标签 [CLS],将句首标签所对应的输出值做为分类标签,计算预测分类标签与真实分类标签的交叉熵,将其做为优化目标,在任务数据上进行微调训练。
一样,针对多分类任务,须要在句首标签 [CLS] 的输出特征向量后接一个全链接层和 Softmax 层,保证输出维数与类别数目一致,最后经过 argmax 操做获得相对应的类别结果。
下面给出语义连贯性判断任务的实例:
任务:判断句子“海大球星饭茶吃” 是否为一句话
输入改写:“[CLS]海大球星饭茶吃”
取 “[CLS]” 标签对应输出:[0.99, 0.01]
经过 arg max 操做获得类似类别为 0,即这个句子不是一个语义连贯的句子
给定一个问句和一个蕴含答案的句子,找出答案在后这种的位置,称为文本问答,例如给定一个问题(句子 A),在给定的段落(句子 B)中标注答案的其实位置和终止位置。
文本问答任何和前面讲的其余任务有较大的差异,不管是在优化目标上,仍是在输入数据和输出数据的形式上,都须要作一些特殊的处理。
为了标注答案的起始位置和终止位置,BERT 引入两个辅助向量 s(start,判断答案的起始位置) 和 e(end,判断答案的终止位置)。
以下图所示,BERT 判断句子 B 中答案位置的作法是,将句子 B 中的每个次获得的最终特征向量 \(T_i'\) 通过全链接层(利用全链接层将词的抽象语义特征转化为任务指向的特征)后,分别与向量 s 和 e 求内积,对全部内积分别进行 softmax 操做,便可获得词 Tok m(\(m\in [1,M]\))做为答案其实位置和终止位置的几率。最后,去几率最大的片断做为最终的答案。
文本回答任务的微调训练使用了两个技巧:
下面给出文本问答任务的实例:
任务:给定问句 “今天的最高温度是多少”,在文本 “天气预报显示今天最高温度 37 摄氏度” 中标注答案的起始位置和终止位置
输入改写:“[CLS]今天的最高温度是多少[SEP]天气预报显示今天最高温度 37 摄氏度”
BERT Softmax 结果:
篇章文本 天气 预报 显示 今天 最高温 37 摄氏度 起始位置几率 0.01 0.01 0.01 0.04 0.10 0.80 0.03 终止位置几率 0.01 0.01 0.01 0.03 0.04 0.10 0.80 对 Softmax 的结果取 arg max,获得答案的起始位置为 6,终止位置为 7,即答案为 “37 摄氏度”
给定一个句子,标注每一个次的标签,称为单句标注。例如给定一个句子,标注句子中的人名、地名和机构名。
单句标注任务和 BERT 预训练任务具备较大差别,但与文本问答任务较为类似。
以下图所示,在进行单句标注任务时,须要在每一个词的最终语义特征向量以后添加全链接层,将语义特征转化为序列标注任务所需的特征,单句标注任务须要对每一个词都作标注,所以不须要引入辅助向量,直接对通过全链接层后的结果作 Softmax 操做,便可获得各种标签的几率分布。
因为 BERT 须要对输入文本进行分词操做,独立词将会被分红若干子词,所以 BERT 预测的结果将会是 5 类(细分为 13 小类):
将 5 大类的首字母结合,可得 IOBES,这是序列标注最经常使用的标注方法。
下面给出命名实体识别(NER)任务的示例:
任务:给定句子 “爱因斯坦在柏林发表演讲”,根据 IOBES 标注 NER 结果
输入改写:“[CLS]爱 因 斯坦 在 柏林 发表 演讲”
BERT Softmax 结果:
BOBES 爱 因 斯坦 在 柏林 发表 演讲 O 0.01 0.01 0.01 0.90 0.01 0.90 0.90 B-PER 0.90 0.01 0.01 0.01 0.01 0.01 0.01 I-PER 0.01 0.90 0.01 0.01 0.01 0.01 0.01 E-PER 0.01 0.01 0.90 0.01 0.01 0.01 0.01 S-LOC 0.01 0.01 0.01 0.01 0.01 0.01 0.01 对 Softmax 的结果取 arg max,获得最终地 NER 标注结果为:“爱因斯坦” 是人名;“柏林” 是地名
不管如何,从上述讲解能够看出,NLP 四大类任务均可以比较方便地改形成 Bert 可以接受的方式,总之不一样类型的任务须要对模型作不一样的修改,可是修改都是很是简单的,最多加一层神经网络便可。这实际上是 Bert 的很是大的优势,这意味着它几乎能够作任何NLP的下游任务,具有普适性,这是很强的。
可是讲了这么多,一个新模型好很差,效果才是王道。那么Bert 采用这种两阶段方式解决各类 NLP 任务效果如何?
在 11 个各类类型的 NLP 任务中达到目前最好的效果,某些任务性能有极大的提高。
最后,我讲讲我对Bert的评价和见解,我以为 Bert 是 NLP 里里程碑式的工做,对于后面 NLP 的研究和工业应用会产生长久的影响,这点毫无疑问。可是从上文介绍也能够看出,从模型或者方法角度看,Bert 借鉴了 ELMO,GPT 及 CBOW,主要提出了 Masked 语言模型及 Next Sentence Prediction,可是这里 Next Sentence Prediction 基本不影响大局,而 Masked LM 明显借鉴了 CBOW 的思想。因此说 Bert 的模型没什么大的创新,更像最近几年 NLP 重要进展的集大成者,这点若是你看懂了上文估计也没有太大异议,若是你有大的异议,杠精这个大帽子我随时准备戴给你。
若是概括一下这些进展就是:首先是两阶段模型,第一阶段双向语言模型预训练,这里注意要用双向而不是单向,第二阶段采用具体任务 Fine-tuning 或者作特征集成;第二是特征抽取要用Transformer 做为特征提取器而不是 RNN 或者 CNN;第三,双向语言模型能够采起 CBOW 的方法去作(固然我以为这个是个细节问题,不算太关键,前两个因素比较关键)。Bert 最大的亮点在于效果好及普适性强,几乎全部 NLP 任务均可以套用 Bert 这种两阶段解决思路,并且效果应该会有明显提高。能够预见的是,将来一段时间在 NLP 应用领域,Transformer 将占据主导地位,并且这种两阶段预训练方法也会主导各类应用。
另外,咱们应该弄清楚预训练这个过程本质上是在作什么事情,本质上预训练是经过设计好一个网络结构来作语言模型任务,而后把大量甚至是无穷尽的无标注的天然语言文本利用起来,预训练任务把大量语言学知识抽取出来编码到网络结构中,当手头任务带有标注信息的数据有限时,这些先验的语言学特征固然会对手头任务有极大的特征补充做用,由于当数据有限的时候,不少语言学现象是覆盖不到的,泛化能力就弱,集成尽可能通用的语言学知识天然会增强模型的泛化能力。如何引入先验的语言学知识其实一直是 NLP 尤为是深度学习场景下的 NLP 的主要目标之一,不过一直没有太好的解决办法,而 ELMO/GPT/Bert 的这种两阶段模式看起来无疑是解决这个问题天然又简洁的方法,这也是这些方法的主要价值所在。
对于当前 NLP 的发展方向,我我的以为有两点很是重要:
目前看预训练这种两阶段方法仍是颇有效的,也很是简洁,固然后面确定还会有更好的模型出现。
完了,这就是预训练语言模型的前世此生。
因为我的刚入门 NLP 方向,就不妄自总结,上述总结所有来自知乎文章:从Word Embedding到Bert模型—天然语言处理中的预训练技术发展史-张俊林
我只是知识的搬运工,想详细了解各个知识点的读者能够自行选择参考下列资料。
参考书籍:
《预训练语言模型》- 卲浩、刘一烽
《基于 BERT 模型的天然语言处理实战》- 李金洪
参考论文:
参考博客: