带你从入门到精通——自然语言处理(十. BERT)
建议先阅读我之前的博客,掌握一定的自然语言处理前置知识后再阅读本文,链接如下:
带你从入门到精通——自然语言处理(一. 文本的基本预处理方法和张量表示)-CSDN博客
带你从入门到精通——自然语言处理(二. 文本数据分析、特征处理和数据增强)-CSDN博客
带你从入门到精通——自然语言处理(三. RNN扩展和LSTM)-CSDN博客
带你从入门到精通——自然语言处理(四. GRU和seq2seq模型)-CSDN博客
带你从入门到精通——自然语言处理(五. 自注意力机制和transformer的输入部分)-CSDN博客
带你从入门到精通——自然语言处理(六. Transformer的编码器部分)-CSDN博客
带你从入门到精通——自然语言处理(七. Transformer的解码器部分、输出部分和整体搭建)-CSDN博客
带你从入门到精通——自然语言处理(八. FastText)-CSDN博客
带你从入门到精通——自然语言处理(九. 迁移学习和transformers库)-CSDN博客
目录
十. BERT
10.1 BERT概述
10.2 Embedding模块
10.2.1 Wordpiece分词
10.2.2 Segment embeddings
10.2.3 Position embeddings
10.3 Transformer模块
10.4 微调模块
10.5 BERT的预训练任务
10.5.1 MLM任务
10.5.2 NSP任务
10.5.3 长文本的处理
十. 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的默认截断方式。