Bert不完全手册6. Bert在中文领域的尝试 Bert-WWM & MacBert & ChineseBert


一章我们来聊聊在中文领域都有哪些预训练模型的改良方案。Bert-WWM,MacBert,ChineseBert主要从3个方向在预训练中补充中文文本的信息:词粒度信息,中文笔画信息,拼音信息。与其说是推荐帖,可能更多需要客观看待以下'中文'改良的在实际应用中的效果~

Bert-WWM

  • Take Away: Whole Word Masking全词掩码
  • Paper:Bert-WWM,Pre-Training with Whole Word Masking for Chinese BERT
  • Github:https://github.com/ymcui/Chinese-BERT-wwm

全词掩码并不是中文的专属,而是google最早在英文预训练任务中提出的。在英文中的使用方式是当word piece tokenizer把一个单词分割成几个部分时,在MASK阶段只要一个部分被掩码,一个单词的其他部分也会被MASK。google提供的英文全词掩码的处理方式如下,很直观就是把所有'##'的部分都补充进掩码

def create_masked_lm_predictions(tokens, masked_lm_prob,
                                 max_predictions_per_seq, vocab_words, rng):
  """Creates the predictions for the masked LM objective."""
  cand_indexes = []
  for (i, token) in enumerate(tokens):
    if token == "[CLS]" or token == "[SEP]":
      continue
    if (FLAGS.do_whole_word_mask and len(cand_indexes) >= 1 and
        token.startswith("##")):
      cand_indexes[-1].append(i)
    else:
      cand_indexes.append([i])
    ...

而在中文使用中,并不存在字被拆分的情况,因此中文全词是指对词粒度进行掩码。在哈工大开源的Bert/Roberta-WWM系列中,以LTP分词结果作为掩码的最小粒度如下

实现方案其实可以借鉴上面的英文代码,只需要根据分词结果对字符加上'##'前缀,例如'语言模型',正常tokenize会得到['语','言','模','型'], 基于LTP的分词结果['语言','模型'], 只需要把tokenizer调整为['语','##言','模,'##型'],就可以和英文用相同的处理方式了~

这里的全词掩码只影响MLM预训练任务,和下游微调以及模型自身结构无关,因此可以迁移到任意的预训练模型框架和下游任务中。全词掩码有两个优点

  • 部分解决了MLM独立性假设,使得预测token之间拥有了一定的关联性
  • 提高了MLM任务难度,使得模型需要更多依赖远距离的上下文来判断掩码部分的内容

但是WWM是否在所有中文任务中都比字符粒度的模型更好呢?在使用过程中感觉也不尽然,例如在做一些信息抽取任务时会发现当抽取信息的粒度和词粒度存在一定不一致时,字符BERT的效果是要显著由于WWM的。猜测是WWM引入了部分词边界信息,和下游任务抽取的边界存在一定冲突。

简单说就是中文词本身的粒度在不同上下文语境中是存在差异的,这是和英文单词最大的差异,而这个差异并不能通过LTP单一的分词器来引入。Ref3的论文也指出在中文字符纠错任务中对WWM的效果并没有BERT好,不过论文只给出了字符+WWM的混合掩码方案,感觉。。。如果预训练模型都需要因地制宜的话,那只能说我们还没找到合适的预训练方案。。。

MacBert

  • Take Away: MLM->纠错任务,解决MASK在预训练和微调的不一致性
  • Paper:MacBert, Pre-Training with Whole Word Masking for Chinese BERT
  • Github:https://github.com/ymcui/MacBERT

MacBert可能放在中文这里并不太十分合适,其实是针对MASK在预训练和微调中的不一致问题进行的改良。在中聊到Electra和XLNET也分别通过生成-判别器,以及乱序语言模型的方案绕开MASK学习双向上下文信息,MacBert给出了另一种方案。

MacBert对原始的MLM任务做了如下调整

  • 用相似词替代[MASK]进行掩码,这里的相似词是基于word2vec similarity来召回候选,如果没有召回就降级为随机掩码,于是完形填空任务其实变成了纠错任务(这里应该也要限制召回的词和原词长度相同)
  • WWM + N-gram掩码,10%的4gram,20%3gram,30%2gram,40%1gram。个人理解这里的gram不是字符粒度而是词粒度,否则会有大量的ngram不再以上word2vec的词表内无法召回,不过MacBert并没有开源训练代码所以无从考证

在抽取式阅读理解上,MacBert略有提升,其他任务感觉提升不太明显。整体改良非常简单易懂,但是逻辑上其实有一点存疑,就是基于word2vec的相似词替换作为掩码,真的不会导致模型在训练过程中出现信息泄露的问题么?因为部分相似词替换例如高兴->快乐, 下雨->降雨,其实是提供了近乎一样的文本信息的,这种情况下模型其实不需要依赖上下文,直接通过替换后的文本也能进行预测。这里其实和Electra的生成器部分不能太强是有些相似的问题。N-gram的掩码可能一定程度上缓解了这个信息泄露的问题,但感觉这个问题还是存在的~

ChineseBert

  • Take Away: 引入拼音和笔画信息
  • paper: ChineseBERT: Chinese Pretraining Enhanced by Glyph and Pinyin Information
  • Github: https://github.com/ShannonAI/ChineseBert

最后这个模型是有一些起名nb症在的,虽然整体效果提升只能说比随机波动要更显著一些。不过改良的方向,包括拼音和笔画信息的引入比较有意思,相似的方案在一些文本纠错,风控文本变形之类的领域也看到过有类似的应用,所以还是来简单聊聊

  1. 拼音信息

相同汉字在不同的场景下会存在发音不同的情况,例如音乐和快乐中,yue发音对应的是乐曲,le对应的发音是喜悦,因此发音和汉字交互后也会提供一定的共享信息。不过一字多音的情况相对少,所以感觉拼音层能提供的信息比较有限。不过类似的方案之前有想在风控领域去做尝试,因为风控中经常会出现同音字的变形,例如把佳琪改写成嘉琪,加7等形式去绕过违禁词封禁。如果使用这里已经与训练好的拼音embedding去在下游进行迁移可能效果会有提升~

这里用了pypinyin来生成文字对应的拼音和声调,例如猫->mao1。拼音复用原始的token,4个声调额外用4个特殊token来表征,拼音部分用特殊字符'-'pad到等长。提取信息这里用了width=2的CNN+max pooling来生成每个字符最终的拼音embedding

  1. 笔画信息

笔画信息一定程度给字之间增加了共享信息,例如'江','河','湖','泊'都三点水的偏旁部首,他们存在的上下文也会有一定相似性,因此可以有一部分信息迁移,算是变相增加了上下文信息的丰富程度。不过ChineseBert使用的方式并没有用五笔,而是用了图像信息。使用了仿宋,隶书,行楷三种字体的图片向量化(24243),对3种字体的图片输入进行拼接,然后过全连接层得到每个字符的笔画embedding

  1. 信息融合

在原始Bert token embedding的基础上,加入了拼音和笔画embedding层。特征融合层,简单使用了3个embedding拼接,过全连接层的方式得到融合后的输入embedding,之后的模型结构就和BERT一致了。

  1. MASK: 使用了字符+WWM的混合掩码方式

Reference

  1. Spelling Error Correction with Soft-Masked BERT
  2. https://github.com/shibing624/pycorrector
  3. Is Whole Word Masking Always Better for Chinese BERT?": Probing on Chinese Grammatical Error Correction
  4. https://blog.csdn.net/Jiana_Feng/article/details/123539083