之前一直对bert 预训练结果是怎么来的感兴趣,今天参考了下github 上的代码,跑了一边终于知道是怎么来的了,在这分享下。
代码地址:
在bert 模型中,主要干了两个事,一是判断两句话 是否是上下文,二是从这两句话中挖几个单词(英文数据,随机挖,15%的概率),然后用[mask](在代码中其实是替换为mask 的id,其他单词也是要替换为id的)替换。这15% 也不全是用mask 替换,其中的10% 替换为其他单词的id,10% 保持不变。在代码中是这么写的:
if prob < 0.15:
prob /= 0.15
# 80% randomly change token to mask token
if prob < 0.8:
tokens[i] = self.vocab.mask_index
# 10% randomly change token to random token
elif prob < 0.9:
tokens[i] = random.randrange(len(self.vocab))
# 10% randomly change token to current token
else:
tokens[i] = self.vocab.stoi.get(token, self.vocab.unk_index)
output_label.append(self.vocab.stoi.get(token, self.vocab.unk_index))
为了研究代码,弄了一个很小的数据集,如下:
Welcome to the the jungle I can stay here all night
只用这两行数据集,代码就能跑起来。
损失计算:
开始的时候对损失是怎么计算的也比较好奇,看了下代码后,揭开了它神奇的面纱。
损失的计算分两块:
第一个是计算分类任务的损失,就是判断是否是上下文的损失,就是一个二分类任务 [0, 1], 0不是上下文, 1 是上下文,二分类的输出如下:
第二个是计算 每个单词标签是是否预测正确的损失,是一个多分类任务。这里数据集比较下,也就15个单词,所以相当于是一个15分类的任务,如果数据集大,则变成了 vocab_size 的分类任务。代码如下:
然后使用 NLLLOSS 来计算损失,最后使用常规手段,梯度下降等方式,经过不断的训练生成最终的bert 预训练模型。
总结 :
不得不说这份代码写的很清晰,跟着代码跑一两变就了解了bert是怎么训练的。收获不错。