预训练过程的Mask机制

静态Mask

原始的BERT模型使用的是静态Mask机制. 随机遮盖或替换一句话里面任意token, 随机把一句话中15%的token替换成以下内容:

  • 80%的概率被替换成[MASK]

  • 10%的概率被替换成随机的另外一个token

  • 10%的概率不做任何操作

被选中的15%的词中仅Mask掉80%词的目的是?

虽然选定的15%的token中, 只有80%会被真正的Mask, 即15% * 80% = 12%的token被Mask, 但在预训练过程中, 这15%被选中的token都要去做MLM任务, 即都要通过预测来进行学习.

这个预训练Trick的目的是缓解Exposure Bias, 即预训练和Fine-tune两阶段不一致的情况. 预训练时, 要预测的单词被Mask住, 此时得到的向量是完全基于其上下文的语义向量; 但是在Fine-tune阶段, 当前词是可见的, 且当前位置得到的语义向量, 是包含这个token的. 两个阶段的情况出现了偏差.

因此, 留着80%的词[MASK], 10%的词保留, 剩下10%的词随机替换, 是在告诉模型, 输入的词有可能是错误的, 不要太相信它. 相当于加入噪音, 在训练数据有噪音的情况下, 缓解两节点的bias.

参考:

实际实现

实际操作时, 在生成预训练数据的时候, 提前对数据进行mask. 也就是说从一开始随机选择了这15%的Tokens, 之后的N个epoch里都不再改变了. 为了充分利用数据, BERT定义了dupe_factor参数, 这样可以将训练数据复制dupe_factor份, 同一条数据就可以有dupe_factor种不同的mask方法. 但每条样本mask的结果依然是固定的这dupe_factor种.

动态Mask

这种方法是RoBERTa中使用的Mask方法. 动态Mask, 是在每一次将训练example喂给模型的时候, 才进行随机mask. 相比于静态Mask, 动态mask相当于间接的增加了训练数据, 有助于提高模型性能.

实际实现

RoBERTa一开始把预训练的数据复制10份, 每一份都随机选择15%的Tokens进行Masking, 即同样的一句话有10种不同的mask方式. 也可以不复制数据, 真正在喂给模型之前, 实时地随机生成当前样本Mask的策略.

Whole-Word Masking

这是在中文BERT预训练模型中常用的预训练Mask机制. 例如我们常用的哈工大开源的Chinese-BERT-wwm模型中, 就使用了这种机制.

原始的BERT模型预训练时, 会随机选取整句中的最小输入单元作为token来进行遮盖. BERT中使用了Byte Pair Encoding(BPE)技术, 对原来的每个英文单词继续拆解得到了subword(子词), 例如superman被拆解成为superman两个token. 那么在预训练的过程中, 就会出现super没有被Mask但man被Mask的情况, 一个完整的单词被割裂开.

谷歌后面又提出了BERT WWM模型, 即原来在预训练时遮盖子词, 现在遮盖整个词. superman还是会被拆解为superman两个token, 但如果其中一个要被Mask, 就要将两个token同时Mask掉.

对于中文模型, 原始的谷歌开源的BERT中的中文, 是使用每个字作为token, 没有考虑到中文中的分词(CWS), 也会出现割裂的情况. 使用WWM的策略, 就可以对全词进行Mask:

说明

样例

原始文本

使用语言模型来预测下一个词的probability。

分词文本

使用 语言 模型 来 预测 下 一个 词 的 probability 。

原始Mask输入

使 用 语 言 [MASK] 型 来 [MASK] 测 下 一 个 词 的 pro [MASK] ##lity 。

全词Mask输入

使 用 语 言 [MASK] [MASK] 来 [MASK] [MASK] 下 一 个 词 的 [MASK] [MASK] [MASK] 。

与英文一样, 中文还是被拆解成单个字作为token, 只是在预训练过程中, 还需要借助中文分词工具, 在遮盖时对全词进行Mask. 使用的是哈工大LTP工具.

ERNIE (Baidu)

ERNIE在Mask的时候, 加入了外部的知识, 从而使得模型可以获得更可靠的语言表示. 这些知识的学习是在训练中隐性地学习, 而不是直接将外部知识的embedding加入到模型结构中. 这里的外部知识指的是各种实体, 短语等内容.

与原始的BERT的Mask进行对比:

ERNIE使用了三种Mask机制:

  • basic-level masking: 就是原始token级别的mask, 与原始BERT的mask方式是一样的, 按字进行

  • entity-level masking: 识别出样本中的实体, 如同WWM一样, 对实体全词进行Mask

  • phrase-level masking: 对短语全词进行Mask

这三种Mask是分三个阶段使用的.

  • 在第一个阶段, 用的是basic-level masking, 采用基本层级的masking, 即随机mask掉中文中的一个字

  • 第二阶段使用phrase-level masking, mask掉句子中一部分词组, 然后让模型预测这些词组; 在这个阶段, 词组的信息就被encoding到word embedding中了

  • 第三个阶段使用entity-level masking, 将句子中的实体, 如人名, 机构名, 商品名等mask掉; 模型在训练完成后, 也就学习到了这些实体的信息

实际实现

预训练过程中使用的每条样本, 由5部分组成:

  • token_ids: 输入句子对的token表示

  • sentence_type_ids: 0或者1表示token属于哪一个句子

  • position_ids: 绝对位置编码

  • seg_labels: 表示分词边界信息. 0表示词首, 1表示非词首, -1为占位符

  • next_sentence_label: 表示该句子对是否存在上下句的关系, 0为无1为有

在源码中, 多阶段分步mask机制实际上是对Batch内的每个样本, 以某个概率随机决定样本句子是mask_word还是mask_char模式:

  • mask_char: 即mask单字, 跟原始BERT一致

  • mask_word: 利用分词边界信息进行phrase-level masking

对单字的mask操作简单, 对词的mask, 需要使用到分词边界的信息. 在对词进行mask时, 以词为单位判断是否进行mask, 因此只需要根据分词边界, 用每个词的第一个字作为代表, 随机决定是否应该被mask. 如果要mask则遮盖整个词, 否则整个词都不要被mask.

参考资料

参考资料

最后更新于