代码学习的是前一篇博客中pytorch的代码的BertForTokenClassification模型,run的是ner例子:https://github.com/huggingface/transformers/blob/master/examples/run_ner.py。
1、模型概览:
使用的模型是:multi_cased_L-12_H-768_A-12.zip,https://github.com/google-research/bert/blob/master/README.md
可以看出模型是用transformer中的encoder堆积而成的,一共12层,最后一层是分类层,会对每个token预测ner数据中所有label的概率。
2、输入:
阅读代码时,设置的batchsize=3,为了方便观看数据,将模型需要用到的输入数据都列出部分。基本有
- input_ids: token的索引,后面直接用来找到token对应的embedding。
- attention_mask:用来标记token是否padding出来的。(在计算attention的时候,他们的值会变成0.实现方法是pad出来的token的attention得分加上-10000. 注意区别代码里attention得分和attention概率,attention概率是attention得分乘上Value之后的结果。)
- token_type_ids:标记输入的token是第一句话的token还是第二句话。
3、主体流程:
通过input_ids找到embedding(这个embedding是:
embeddings = inputs_embeds + position_embeddings(位置embedding) + token_type_embeddings(句子embedding)
已经算好的结果),然后顺图中标记的顺序看,返回过程没有用线连了。
具体过程分开理:(配合下面的贴图使用)
首先
是1、10、11标记的图片,是模型的主体。embedding通过encoder拿到token的表示(10中的output[0]),过了一个线性分类器得到每个token对应的label的概率分布,再利用attention_mask索引出未pading的token,最后的loss使用交叉熵直接算未pading的token。(这里并不是训练bert,所以并不是预测mask的token,这里也没mask的token了)
然后
主要关注encoder:主要是图中的2、3、4、5、6、7、8、9几个图。
- 首先embedding通过2、3、4、5过一个QKV的attention,具体实现可以看5,其中有mask吊pad的token的操作
- 接着attention出来后过了一个dense层,一个dropout层,Norm层,其中有残差连接,图6。
- 然后又过了一个dense层和gelu激活,gelu实现见图8。
- 继续过了一个dense+dropout+Norm+残差连接四连击,见图9
上图的过程是从代码中看出的,和论文 《attention is all you need》中的图完全一致:代码中堆了11层红框的结构。
附:
一开始一直没弄懂为什么bert是双向的,因为MLM(mask language model),训练的时候根据上下文预测mask,上下文体现出了双向的意思。