建议先阅读我之前的博客,掌握一定的自然语言处理前置知识后再阅读本文,链接如下:
带你从入门到精通——自然语言处理(一. 文本的基本预处理方法和张量表示)-CSDN博客
带你从入门到精通——自然语言处理(二. 文本数据分析、特征处理和数据增强)-CSDN博客
带你从入门到精通——自然语言处理(三. RNN扩展和LSTM)-CSDN博客
带你从入门到精通——自然语言处理(四. GRU和seq2seq模型)-CSDN博客
带你从入门到精通——自然语言处理(五. 自注意力机制和transformer的输入部分)-CSDN博客
带你从入门到精通——自然语言处理(六. Transformer的编码器部分)-CSDN博客
带你从入门到精通——自然语言处理(七. Transformer的解码器部分、输出部分和整体搭建)-CSDN博客
带你从入门到精通——自然语言处理(八. FastText)-CSDN博客
带你从入门到精通——自然语言处理(九. 迁移学习和transformers库)-CSDN博客
目录
十. BERT
10.1 BERT概述
BERT(Bidirectional Encoder Representations from Transformers)是Google于2018年提出的一种基于transformer的预训练模型
BERT的总体架构如下图所示:
从上述的架构图中可以看到,BERT分三个主要模块:最底层黄色标记的embedding模块、中间层蓝色标记的transformer模块以及最上层绿色标记的微调模块。
10.2 Embedding模块
10.2.1 Wordpiece分词
在BERT中使用wordpiece进行分词,wordpiece分词是子词级别(例如:worked会被拆分为work、##ed)的分词算法,具体步骤如下:
1. 将训练文本进行字符级别的分词,并为每个单词的所有非首字符添加##前缀,作为初始词表,例如单词"word",拆分为:w ##o ##r ##d。
2. 计算合并分数,也称作互信息,具体公式如下:
其中分子为子词A、B在训练文本中共同出现并相邻的总次数,分母为子词A、B在训练文本中单独出现的总次数的乘积。
3. 合并合并分数最高的子词对,并将其作为一个新的子词加入词表,注意:w与##o会合并为wo,而##o和##r会合并为##or。
4. 重复步骤2、3直到到达预定的词表大小或合并次数。
5. 使用最终得到的词汇表对文本进行分词,使用贪婪最长匹配原则,即从词首开始,优先匹配词表中存在的最长子词。
使用wordpiece完成分词后在训练文本开头添加添加[CLS]标记,训练文本中不同的句子结尾添加[SEP]标记,并通过可学习的词嵌入矩阵将训练文本映射为token embeddings(长度为hidden_size)。
10.2.2 Segment embeddings
Segment embeddings为分段嵌入张量,是一个可学习的嵌入张量,用于区分同一文本中的不同句子,通常使用全0和全1交替编码不同的句子,并且0和1分别对应了不同的长度为hidden_size的嵌入向量,因此BERT中的segment embeddings可以用一个2 * hidden_size的嵌入矩阵来表示,注意:[CLS]标记属于第一个句子,[SEP]标记属于位于它之前的第一个句子。
10.2.3 Position embeddings
BERT中的position embeddings也是一个可学习的嵌入张量,BERT能处理的最长序列长度为512,因此BERT中的position embeddings可以用一个512 * hidden_size的嵌入矩阵来表示。
BERT的整个embedding模块的输出张量就是这token embeddings、segment embeddings以及position embeddings的逐元素相加结果。
10.3 Transformer模块
BERT只使用了transformer架构中的encoder部分,而完全舍弃了decoder部分,因此BERT的transformer模块架构与transformer的encoder架构完全一致,只是参数略有不同,BERT - base版本的模型参数为12层encoder layer,12个注意力头,hidden_size为768,总参数量为110M。
10.4 微调模块
经过中间层transformer模块的处理后,会得到一个shape为[batch_size,seq_len,hidden_size]的文本表示张量,对于不同的下游任务可以使用不同的微调策略进行调整,在transformers库中BERT模型最终的输出分为两个部分,一个为last_hidden_state,即为前文提到的文本表示张量,另一个为pooler_output,为last_hidden_state中[CLS]标记的隐藏状态再经过一个输出神经元个数为hidden_size的全连接层后得到的输出张量,形状为[batch_size,hidden_size]。
BERT的几种常见的微调任务如下:
句子对分类任务:输入为:[CLS] + 句子1 + [SEP] + 句子2 + [SEP];输出为:取pooler_output再经过一个输出神经元个数为分类数的全连接层,输出分类结果。
句子分类任务:输入为:[CLS] + 句子 + [SEP];输出为:取pooler_output再经过一个输出神经元个数为分类数的全连接层,输出分类结果。
问答(QA)任务:输入为:[CLS] + 问题 + [SEP] + 上下文 + [SEP];输出为:start_logits以及end_logits,表示模型预测的答案在上下文中的起始和结束位置,两个输出的shape都为[batch_size,上下文的seq_len]。
命名实体识别(NER)任务:输入为:[CLS] + 句子 + [SEP];输出为:last_hidden_state再经过一个全连接层,输出命名实体的标签(如人名、地名)
10.5 BERT的预训练任务
10.5.1 MLM任务
MLM(Masked Language Model)任务会在原始训练文本中随机抽取15%的token作为参与MLM任务的对象,在这些被选中的token中,以80%的概率用[MASK]标记替换该token,以10%的概率用一个随机的单词替换该token,以10%的概率保持该token不变,而模型需要基于上下文预测被遮盖的token。
10.5.2 NSP任务
NSP(Next Sentence Prediction)任务中输入为一个句子对(A,B),模型需要预测句子B是不是句子A的下一句话,所有原始训练文本的语句都被选中作为句子A,而句子B以50%的概率选取为句子A的下一句话,以50%的概率选取为原始文本中(句子A的下一句话除外)随机抽取的一句话。
但是后续研究对NSP任务的有效性表示存疑,NSP甚至可能对某些任务产生负面影响,许多改进的BERT版本已经移除了NSP任务。
10.5.3 长文本的处理
BERT能接收的最长序列长度为512,对于超长文本BERT的常用截断策略如下:
1. head-only:只保留长文本的前512个token。
2. tail-only:只保留长文本的后512个token。
3. head-tail:保留长文本前后的256个token,这也是BERT的默认截断方式。