bert 的本质是学习单词的词向量表达。那我们先来回顾 word2vec,和 bert 预训练产生词向量的不同吧!
word2vec缺点: 学习到的是静态词向量,与上下文无关,如 “ I like to eat Apple" 和 ”Apple is a high-tech company in the United States“, 很显然这两个apple的意思不一样,但在Word2vec看来,它们是一样的词向量。对于训练的句子,只利用了句子的单向信息:如:今天 天气 真好,要预测 ”真好“ 概率时,根据语言模型概率公式: p(真好)= p(天气 | 今天,天气),预测 p(天气) = p(天气| 今天), 就是说预测”天气“时不能利用”真好“这个信息,因为这个词在”天气“后面。2.bert 完美的解决了这两个缺点, 可以学习动态的基于上下文的词向量;利用句子的双向信息
为什么要说到ELMO呢?因为它首先尝试解决word2vec的缺点。 总结起来: 即:
深度双向lstm,特点:deep,为什么要做深层??类似于图像识别一样,网络层越深,越能提取到图片中物体的具体信息。放到文本特征提取上,网络层越深,那么能提取的特征也依次具体化: 词特征 --> 语法特征 --> 语义特征 保留每一层lstm的隐层状态,包括正向和反向 [ →hi | hi←]最终某个单词的词向量是 该单词位置上所有隐层状态的加权和。举个栗子:
现在才开始介绍我们的主角:bert
这里需要用到transformer的知识,大家可以看transformer介绍 Transformer的网络架构如图所示,Transformer是一个encoder-decoder的结构,由编码器和解码器构成。图中的左侧部分为编码器,由若干个(论文中6个)encoder堆叠而成, 右侧部分为解码器,由若干个(论文中6个)decoder堆叠而成。 Bert只用到了Transformer的编码器部分,它的结构图如下: 其中Trm表示一个Transformer的编码器部分 真实应用中Trm 不止图中的 2 个,通常有两种结构 L表示网络的层数(即Transformer 编码器的数量),A表示Multi-Head Attention中self-Attention的头数,H 是 隐层单元的维度
掩码语言模型的提出就是为了防止:自己看见自己 Masked Language Model(MLM)是指在训练的时候随机从输入语料上mask掉一些单词,然后通过的上下文预测该单词,该任务非常像我们在中学时期经常做的完形填空。这一点是非常不同于RNN语言模型的。比如输入 A, B,C
RNN模型 p(ABC) = P(A)*p(B|A)*p(C|AB)Bert 假如把B给 mask掉,则 P(ABC) = P(B| AC)随机mask语料中15%的token,对于[Mask]这个符号,由于在测试集中不存在,为了减轻训练和预测之间的不匹配,作者按一定的比例在需要预测的token上动了手脚,如:my dog is hairy,则:在15%的单词当中
有80%的概率用“[mask]”标记来替换——my dog is [MASK]有10%的概率用随机采样的一个单词来替换——my dog is apple有10%的概率不做替换——my dog is hairy–为什么要提出这个预训练任务呢,主要也是很多譬如问答、推理之类的任务,更多的是要学习句子之间的关系,这是语言模型无法做到的,因为语言模型根据token预测token,是在句子内部进行学习的。
bert在训练时会判断这两个输入句子是否是连续的,相关的,也就是它会做一个二分类任务,若两个句子是挨在一起的,那么预测为1,否则为0。每个句子的结尾以 [SEP] 作为分隔符。 那么句子如何选取呢??训练的时候,输入到模型的第二个句子会以50%的可能从全部文本中随机选取,剩下50%的可能从紧挨着第一个句子的文本中选取
这样看来,模型就有两个损失函数了,一个是做完形填空时产生的,一个是做二分类时产生的,两个损失函数之和就是bert总的损失函数了。源码片段:
bert将输入句子转化为词向量,是经过了3 个Embedding 的加和, 即 input_embed = Token_embed + Sentence_embed + Position_embed. 源码是: maxLen:批训练时,最大句子长度。d_model:词嵌入的维度,n_segments:表示输入多少句话,一般最大取2 用pytorch调用模型的话,我们只需要输入:
input_ids:一个形状为[batch_size, sequence_length]的 torch.LongTensor,在词汇表中包含单词的token索引, 注意 在句子首尾分别加了 [cls] 和 [sep] 的 索引segment_ids :形状[batch_size, sequence_length]的可选 torch.LongTensor,在0, 1中选择token类型索引。类型0对应于句子A,类型1对应于句子B。如 [0,0,0,0,0,1,1,1,1,1], 0代表第一个句子A, 1代表第二个句子B,默认全为0input_mask:一个可选的 torch.LongTensor,形状为[batch_size, sequence_length],索引在0, 1中选择。0 是 padding 的位置,1是没有padding的字MNLI(Multi-Genre Natural Language Inference):给定一对句子,目标是预测第二句子和第一个句子是相关的、无关的还是矛盾的。 QQP(Quora Question Pairs):判断两个问句是否是同一个意思。 QNLI (Question Natural Language Inference):样本是(question,sentence)在一段文本中sentence是否是question的答案。 STS-B(Semantic Textual Similarity Benchmark):给出一对句子, 使用1~5的评分评价两者在语义上的相似程度。 MRPC (Microsoft Research Paraphrase Corpus):句子对来源于对同一条新闻的评论. 判断这一对句子在语义上是否相同。 RTE(Recognizing Textual Entailment):是一个二分类问题, 类似于MNLI, 但是数据量少很多。
SST-2(The Stanford Sentiment Treebank):单句的二分类问题, 句子的来源于人们对一部电影的评价, 判断这个句子的情感。 CoLA (The Corpus of Linguistic Acceptability):单句的二分类问题, 判断一个英文句子在语法上是不是可接受的。
SQuAD(Standford Question Answering Dataset):给定一个问题和一个来自包含答案的Wikipedia段落,任务是预测答案在段落中所在的位置。 CoNLL-2003 NER命名实体识别任务,应该是大家最熟悉的了,预测每个字的标签是什么。
总体来说对于(a)(b)中的任务,我们取得的是最终CLS位置的输出C。对于(c)中的任务主要是预测start和end标记所在的最大值,且是start的id值小于end的id值,其中start是答案所在段落中的起始位置,end是结束位置。对于NER任务,自然是取得每一个单词的输出的表达,后面再接一个CRF。