Press "Enter" to skip to content

打造自己的 PTM!新词挖掘+预训练

 

作者 | 周俊贤

 

整理 | NewBeeNLP

 

 

 

随着这几年 预训练模型 (Pre-trained Models,PTMs)的兴起,大家都应该感受过预训练模型的威力。对于一般的公司或者学生来说,很难去从头训练一个适应手头任务领域的预训练模型,因为

 

数据。训练预训练模型需要海量的数据,现在上100G的语料轻松平常,假如没有大量数据而强行训练,会过拟合

 

计算资源。听过训练一个BERT需要1w美金、训练XLNet需要6w美金的故事吗hh

 

因此,大家常会使用开源的预训练模型,然后根据下游任务数据来fine tuning。其中最出名的就是哈工大讯飞实验室推出的一系列中文PTMs [1] ,他们是用维基百科的中文语料来做的预训练。

 

正如我写的这篇文章介绍的,

 

Don’t stop pretraining,继续预训练!

 

「假如PTM预训练的语料与下游任务语料分布差异很大时,十分建议在任务领域语料继续做pre training,再做fine tuning」。例如,现在你的任务是从法律文件中做NER任务,最好用大量的法律领域语料来继续预训练得到一个适应法律领域的预训练模型。

 

问题:「为什幺要引入新词挖掘呢」?

 

因为不少实验证明了whole word masking比随机masking效果更加。其实很容易理解,假如一个句子【许昕爆冷输球无缘全运会男单4强】

 

随机MASK:【许昕[MASK]冷输球[MASK]缘全[MASK]会男单4强】,当模型预测“运”时,很容易根据上下文的相邻token分别是“全”和“会”来推出当前的token是“运”,这时候,其实模型是根据词汇的信息来预测的(是不是有点像word2bec),而不是根据语义,而我们更希望模型学到的是语义

 

whole word masking:【许昕爆冷输球无缘[MASK][MASK][MASK]男单4强】,这时候模型预测“全运会”就是主要根据语义来进行预测的了

 

既然要做whole word masking,那就要有分词器,哈工大训练联合实验室用的是LTP [2] 工具做分词,但直接用分词,对专业领域的效果并不好,如对词汇“自然语言处理”用jieba、LTP等工具会分成“自然语言、处理”,但我们更希望是直接当成一个词汇,mask的时候整个mask掉。

 

所以我们首先要做任务领域语料的新词挖掘,再加入到分词器的词典中,用于whole word masking。

 

做完新词挖掘后,再用huggingface的examples来对模型做继续预训练,损失就用wwm mlm损失,然后去掉BERT原生的NSP任务,来得到适应任务领域的预训练模型。这篇博客分成三部分:

 

新词挖掘算法:主要介绍 「基于频次」 和 「基于自由凝固度以及左右邻字熵」 的两种算法

 

预训练模型继续预训练:主要介绍huggingface的examples,并聊聊使用过程的问题

 

实验:验证继续预训练对下游任务的影响

 

本文全部实验代码地址:https://github.com/zhoujx4/NLP-Series-NewWordsMining-PTMPretraining

 

新词挖掘

 

把百度百科里面自然语言处理的介绍爬下来,作为待挖掘的语料

这是用LTP工具分词后的部分结果截图

 

 

方法一:基于频次的新词挖掘

 

原理很简单,首先我们有一个维护好的中文词典(PS:维护好一个手头任务领域的词典对于平日的工作来说是很重要的),假如分词后的词不在这个字典里面,就统计出现的次数,并把它记录下来作为候选新词。

 

还可以采用bigram的方式,例如””自然语言”,”处理”把它们合并后变成“自然语言处理”,发现它们不在我们的词典当前,那纳入到候选新词中,当然还可以采用trigram、4-gram、5-gram…

 

这是候选新词的结果

可以看到,效果还是不错的,能识别到”预训练”、”图神经”、”自然语言处理”、”深度学习”等新词,当然还有很多噪声数据,要 「人工进行一些清洗过滤」 (这是很正常的事,现在大部分的新词挖掘算法还是脱离不了人工清洗),例如我们可以过滤掉出现了”的”的候选词。总而言之,就是根据一些启发式的规则来从候选新词中过滤掉明显不make sense的词。

 

详细的可以看Github上的notebook代码。

 

方法二:基于自由凝固度以及左右邻字熵的新词挖掘

 

自由凝固度:表示一个字串的凝固程度,公式为:

 

左邻字熵与右邻字熵:表示一个字串左右搭配的丰富性,公式为:

 

举一个例子来阐述公式的计算,如现在假设语料只有一个句子,为” 自然语言处理是指利用人类交流所使用的自然语言与机器进行交互通讯的技术 “。

 

首先要指定一个超参数max_word_len,假设这里取为6,我们会这样切分句子得到候选词汇

 

自、自然、自然语、自然语言、自然语言处、自然语言处理、然、然语、然语言、然语言处、然语言处理、然语言处理是、是、是指、是指利、是指利用…

 

可以看到,每个token都会得到一个长度大小为max_word_len的词汇列表,这个句子共有34个字,所以共有个词汇(去重后,会少于204个,例如”自然语言”出现了两次)。

 

现在要统计”自然语言”这个词的凝固度,,这个词可以切分成【(自、然语言)、(自然、语言)、(自然语、言)】这三种不同的形式,那幺就取最大的那个,具体的

 

OK,自由凝固都会算了,现在来算”自然语言”这个词的右邻字熵,可以看到出现在这个词右边的有【处、与】两个token,那幺,同理可以算得左邻字熵。

 

思考一个问题: 「对于待挖掘的新词来说,自由凝固度是大还是小好,邻字熵是大还是小好?」

 

答案是,对于待挖掘的新词来说,自由凝固度越高越好,一个词自由凝固度越高,表示它们常常出现,对于邻字熵来说也是越高越高,一个词的左右邻字熵越高,表示它们更像是一个”整体”,这里举个反例就很好理解了,例如”然语”这个词的自由凝固度很高,但是它的邻字熵很低(在这个例子中,都为0),证明它只是某个词汇的一部分。

 

因此,我们可以通过卡阀值,设定自由凝固度和邻字熵的下届,来得到一批候选新词。下图为算法挖掘到的部分新词。

 

 

同样的,用一些启发式的规则来过滤掉一些候选词,例如过滤掉包含标点符号的词、过滤掉包含【的、在】这种介词的词等等。可以看到过滤后,算法能识别出””自然语言处理”、”自然语言生成”等新词,效果还是十分不错的!

 

预训练模型继续预训练

 

继续预训练直接用huggingface/transformers库的examples [3]

其中,

 

run_clm.py、run_clm_no_trainer.py是做GPT的autoregressive预训练任务

 

run_mlm.py、rum_mlm_no_trainer.py是做BERT/RoBERTa的denoising autoencoding任务(即MLM任务)

 

run_plm.py是做XLNet预训练permutated language model任务

 

其中,不包含trainer的脚本用了transformers库的Trainer API,有no_trainer后缀的是传统用pytorch训练的一样,假们想定制一些东西,例如除了mlm任务外,在预训练时增加多几个任务,就可以在no_trainer.py文件里进行代码修改。当然,修改需要对transfomers库的架构很熟,例如搞懂设计库时model、data、trainer是怎幺组织和解耦的,需要深入学习transfomers库的源码,这个目前我也在学习中。

 

值得注意的是,由于英文和中文的不同,具体的英文的字母对应的是中文的字,英文的词对应的是中文的词,因为直接按这个教程来运行是不work的…

 

这里官方还给了另外一个中文的例子,地址在https://github.com/huggingface/transformers/tree/master/examples/research\_projects/mlm\_wwm [4] ,这个例子做的就是wwm,而且给的例子的backbone model正是哈工大讯飞实验室推出的Chinese-BERT-wwm [5] 。

首先运行run_chinese_ref.py得到中文的词汇切分文件,源码跟作者的一样,只不过可以把刚挖掘的新词加入到LTP分词词典中,如下图所示

然后运行run_mlm_wwm.py做继续预训练,建议直接看作者的链接介绍。

 

实验

 

这里拿新冠肺炎疫情期间网民情绪识别 [6] 比赛的数据作为实验,由于实验资源的问题,用的是哈工大讯飞实验室发布的三层RoBERTa [7] 。

 

这个比赛一共给了10万个labeled语料和90万个unlabeled语料共100万个语料,首先我们做新词挖掘,挖掘到以下这些新词,这里没有做过多的清洗

 

 

然后分成90w条训练数据和10w条验证数据,做继续预训练,训练参数epoch=3,batch_size=40,参数可以根据设备进行调整,这里我在一个3080ti 10g显存上进行训练,大概需要训练2个半小时。

 

由于RoBERTa是用维基百科中文语料进行预训练的,而这个比赛用的是微博帖子数据,可以看到预训练语料和任务语料之间的分布是有颇大差异的,因此我认为 「用微博语料对预训练模型继续做预训练,对下游任务的提升会颇大」 ,这里对10w个labeled数据做0.85比例的训练集和验证集切分,跑了5个随机种子,取平均准确率,实验效果如下

rbt3继续预训练后的rbt3
随机种子10.74700.7504
随机种子20.74360.7462
随机种子30.73600.7427
随机种子40.74750.7504
随机种子50.74900.7514
avg.0.74460.7482

 

总结

 

有个问题, 「多大的语料建议做继续预训练呢?」 这个答案我也说不准,感觉起码要上万的语料,手头没有足够的任务领域语料,建议到网上进行爬取,越多越好。

 

这篇博客给大家介绍了新词挖掘+继续预训练的一套流程,不妨拿你现在手头的任务进行尝试。

一起交流

想和你一起学习进步!『 NewBeeNLP』 目前已经建立了多个不同方向交流群( 机器学习 / 深度学习 / 自然语言处理 / 搜索推荐 / 图网络 / 面试交流 /  等),名额有限,赶紧添加下方微信加入一起讨论交流吧!(注意一定o要 备注信息 才能通过)

 

 

本文参考资料

[1]

一系列中文PTMs: https://github.com/ymcui

[2]

LTP: https://ltp.ai/docs/quickstart.html

[3]

huggingface/transformers库的examples: https://github.com/huggingface/transformers/tree/master/examples/pytorch/language-modeling

[4]

https://github.com/huggingface/transformers/tree/master/examples/research_projects/mlm_wwm: https://github.com/huggingface/transformers/tree/master/examples/research_projects/mlm_wwm

[5]

Chinese-BERT-wwm: https://github.com/ymcui/Chinese-BERT-wwm

[6]

新冠肺炎疫情期间网民情绪识别: https://www.datafountain.cn/competitions/423

[7]

三层RoBERTa: https://huggingface.co/hfl/rbt3

 

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注