bert中的special token到底是怎么发挥作用的(2)之bert引入额外知识
- 2021 年 4 月 12 日
- AI
我们看官方文档有一个非常有意思的功能,叫resize token embedding

我们来看下bert的vocab,可以save到本地之后看配置文件夹下的目录:
//raw.githubusercontent.com/microsoft/SDNet/master/bert_vocab_files/bert-base-uncased-vocab.txt

其中[unused*]
这些标记是未经训练的(随即初始化),是 Bert 预留出来用来增量添加词汇的标记,所以我们可以用它们来指代任何新字符。
这是一个非常有意思的功能,因为我们可以将额外的知识纳入到bert的模型中训练,而不仅仅是简单的进行concat。
//github.com/georgian-io/Multimodal-Toolkit?ref=hackernoon.com%EF%BC%89

上述的git中,作者适配了huggingface的model,封装了文本和普通表格数据的简易多模态bert,原理也很简单,文本特征进bert这个子网络,类别和连续特征进另一个自网络(可以自定义设计网络结构,根据上面的git链接的相关tutorial很容易上手),最后两个子网络的输出concat之后共同输出。
而bert的[unused]的这部分special token,实际上可以在更复杂的层面上引入额外的特征,
BERT类模型的特殊符号的使用可以有哪些调整和变体?可以起到怎样的效果?
这里的高赞提出的方法就是一个非常好的例子,这么做和上面的例子中的多模态网络结构不同,这里通过将先验知识引入unused token中,先验知识会进入transoformer结构从而参与到后续的自注意力机制的计算中。
这是一个非常有意思的应用,这意味着,我们只要能够将先验知识转化为离散的特征,那么我们就可以非常轻松地在bert fine tune的过程中引入额外的知识了。
方法1:
在词表(vocab.txt)中添加若干个自定义的特殊 tokens,词表大小由 N 增大到 M。
新建一个 M 维的 embedding layer。
将 BERT 原来的 N 维 embedding layer 中的 pretrained weights,按照词表顺序,复制到新的 M 维 embedding layer 中。
替换掉 BERT 原来的 N 维 embedding layer。
这里就需要使用到bert的add special token的api以及resize token embedding的api
方法2:
将先验知识的文本表示(bert目前主要介绍文本输入,即token的index作为输入)加入到原始的句子中去,然后直接修改bert的vocab,这样bert的tokenizer分词的时候就会把这些加入的先验知识的文本表示独立切分为一个token从而不会破坏先验知识。
方法3:
对原始的文本进行tokenizer处理之后,直接在句子后面加入先验知识,然后这个先验知识的word index指向bert的unused的token的index即可,注意,不同的先验知识需要指向不同的unused的token的index。
这里建议使用方法2或者3(其实二者是一样的处理思路没什么区别),因为这样我们不会引入更多维度的embedding向量,直接服用unused的token对应的embedding即可。
为了方便理解,还是举个例子吧。
假设我们要做文本分类的任务,判断一句评论是不是田园女拳发的,然后我们发现,凡是来自于豆瓣或者微博的文本,田园女拳的占比很高,而来自知乎的文本,田园女拳占比很低,那么这个时候我们就有了一个先验信息,我们可以对不同文本的来源进行标注,例如来自于豆瓣的文本,我们额外标注 “豆瓣”,对于来自知乎的文本,我们额外标注“知乎”,那么现在问题来了,我们怎么将我们的标注纳入bert中:
1、这类先验知识通过一个子网络进行前向传播,并将最终的输出和bert的输出进行concat然后进入文本分类的任务层;
2、这类先验知识通过方法1,或者方法2或者方法3 纳入到bert finetune的过程中,因为bert的直接输入主要还是支持文本,所以必须把先验知识转化为离散的文本特征的表示,因为bert本身内置了很多unsued的token,

这部分unused的token是从未训练过的但是他们在bert模型的token embedding中都有一个随机初始化,从来没训练过的token embedding向量对应,
3、比较麻烦的问题是,bert常用的是position embedding,即绝对位置嵌入的方式,这种就比较麻烦,简单的方法是我们对原始文本补0,padding到512位,然后512位之后加入这类文本形式的先验知识,这样这些先验知识就不会用到position embedding向量了,否则模型最终会将position embedding和先验知识对应的token embedding相加送入模型,但是这样做没有意义,先验知识的embedding没有位置的概念,第二种方式是使用相对位置编码的方式,这样每个位置的编码方式是固定不可训练的,不会对结果造成啥影响。
对于离散的先验知识这种方法倒是可以,如果是连续的先验知识比较麻烦,要不就里离散化转化为离散类型的input,要不就是使用更具有普适性的多模态bert结构:

但实际上这样的话,没有利用到text feature和先验知识之间的交互信息,很尴尬。