知识增强预训练简介
近年来,预训练语言模型(包括BERT,RoBERTa,XLNET等)作为NLP基础模型在各个下游NLU任务取得了前所未有的性能表现,具有很强的通用性。然而,主流的预训练语言模型都是基于公开的文档,从非结构化文档中学习一般的语言知识,忽略了对大量知识信息的学习,特别是结构化的知识图谱信息。这将导致模型无法真正理解现实中的实体和它们之间的关系等一系列知识,难以拥有真正的NLU能力。比方说,GPT模型输出关于“太阳有两只眼睛”,“巴拉克奥巴马是一个Muslim”等论断。此类知识的缺失,将会产生一些反事实的内容,也会大大减弱模型的小样本学习能力,领域知识的迁移能力,一般知识的归纳能力等等。因此,PAI团队很早就开始了知识预训练的技术积累,致力于提升预训练模型的常识性和知识性,避免出现反常识和反事实的模型结果。
DKPLM算法概述
DKPLM算法
DKPLM模型 (Decomosable Knowledge-injected PLM),可以在预训练阶段注入知识,训练完的模型在下游场景里不需要注入知识直接finetune就可以取得很好的效果。在公开数据上效果显示,DKPLM比常用的KEPLM和KGBERT效果更好。模型使用方式如下图所示:
由此可见,传统的知识注入的预训练语言模型需要在下游任务的时候做显式的知识注入,增加了使用成本。新版的知识注入模型DKPLM可以跟普通的预训练模型(例如BERT)同样使用,在下游任务里直接Finetune就可以取得不错的效果。值得一提的是,我们提出的DKPLM模型在医疗数据上相比其他预训练模型平均提升了3个点。DKPLM的架构如下所示:
由上图可见,DKPLM基于传统的PLM,提出了三个技术创新点来做知识注入:
- 面向长尾实体的知识注入:最近很多研究结果已经显示,对于常见的三元组实体,预训练语言模型通过大规模无监督语料的训练,已经能够将相应的知识三元组建模出来,知识注入会带来冗余信息。因此,DKPLM采取面向长尾实体的知识注入机制,利用实体频率、实体语义重要性和目标实体周围邻居数量检测长尾实体,作为知识注入的目标。
- 实体知识注入:为了将实体的知识注入到预训练模型中,同时尽可能不要引入其他额外参数,保持backbone与预训练模型对齐。我们引入了伪知识表示注入方法。具体地,如果训练语句中的目标实体是头实体,那么将其对应的预训练输入的表示替换成尾实体减去关系的表示。如果是尾实体,就进行相反的操作。
基于关系知识的知识解码验证:最后,将注入的三元组知识显示地解码出来,我们将这一任务作为DKPLM的预训练任务之一,加入预训练流程中。
完整流程示例
输入数据格式
输入文本
{‘text’: ‘通常来说,人类想获得针对某种的[ENT]特异性抗体[ENT]有两种方式,要么是通过自然感染,要么是通过[ENT]>疫苗接种[ENT]。但是,我们显然不会让婴幼儿冒着生病的危险去主动感染某个病毒,而对于 3 岁以下婴幼儿,目前各国尚 没有[ENT]新冠疫苗[ENT]获批使用。’, ‘relation_id’: [1, 2, 3], ‘replced_entity_id’: [1, 2, 3]}
每条文本用一个python字典类型保存,其中有text文本数据,需要注入的实体需要提前识别出来,用[ENT]实体字符串[ENT]标注,relation id和 replaced id分别是关系的索引和需要被替换的实体索引,两者一一对应。
- entity embedding文件
文件的行号对应的是实体的id(例如上面replaced_entity_id),其中“0”表示PAD,缺省的实体表示。每一行的embedding内容是提前准备好:用实体关系三元组形成一个句子(如果有更多的该实体相关的文本数据可以concat上去),如果这个该注入的实体是尾实体,那么可以用预训练模型输出的“头实体+关系”去表示出来尾实体,同时如果有其他的尾实体相关知识文本,表示以后也一样concate上去(例如paper中的实体描述文本)
DKPLM算法的训练
#! /bin/bashexport CUDA_VISIBLE_DEVICES=$1if [ ! -f ./train_corpus.txt ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/language_modeling/dkplm/train_corpus.txtfiif [ ! -f ./dev_corpus.json ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/language_modeling/dkplm/dev_corpus.txtfiif [ ! -f ./entity_emb.txt ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/language_modeling/dkplm/entity_emb.txtfiif [ ! -f ./rel_emb.txt ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/language_modeling/dkplm/rel_emb.txtfiMASTER_ADDR=localhostMASTER_PORT=6009GPUS_PER_NODE=1NNODES=1NODE_RANK=0DISTRIBUTED_ARGS="--nproc_per_node $GPUS_PER_NODE --nnodes $NNODES --node_rank $NODE_RANK --master_addr $MASTER_ADDR --master_port $MASTER_PORT"python -m torch.distributed.launch $DISTRIBUTED_ARGS ../examples/application_tutorials/language_modeling/main.py \--mode=train \--worker_gpu=1 \--tables=train_corpus.txt,dev_corpus.txt \--learning_rate=1e-4 \--epoch_num=1 \--logging_steps=100 \--save_checkpoint_steps=500 \--sequence_length=128 \--train_batch_size=32 \--checkpoint_dir=./lm_models \--app_name=language_modeling \--user_defined_parameters='pretrain_model_name_or_path=alibaba-pai/pai-dkplm-medical-base-zh entity_emb_file=entity_emb.txt rel_emb_file=rel_emb.txt'
DKPLM算法的微调
DKPLM模型在fine-tune阶段使用与BERT相同,只需要修改上述训练出来的模型即可,下面以文本分类为例:
export CUDA_VISIBLE_DEVICES=$1if [ ! -f ./train.tsv ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/easynlp/tutorials/classification/train.tsvfiif [ ! -f ./dev.tsv ]; thenwget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/easynlp/tutorials/classification/dev.tsvfimode=$2if [ "$mode" = "train" ]; theneasynlp \--mode $mode \--worker_gpu=1 \--tables=train.tsv,dev.tsv \--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \--first_sequence=sent1 \--second_sequence=sent2 \--label_name=label \--label_enumerate_values=0,1 \--checkpoint_dir=./classification_model \--learning_rate=3e-5 \--epoch_num=3 \--random_seed=42 \--save_checkpoint_steps=50 \--sequence_length=128 \--micro_batch_size=32 \--app_name=text_classify \--user_defined_parameters='pretrain_model_name_or_path=alibaba-pai/pai-dkplm-medical-base-zh'elif [ "$mode" = "evaluate" ]; theneasynlp \--mode=$mode \--worker_gpu=1 \--tables=dev.tsv \--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \--first_sequence=sent1 \--second_sequence=sent2 \--label_name=label \--label_enumerate_values=0,1 \--checkpoint_dir=./classification_model \--sequence_length=128 \--micro_batch_size=32 \--app_name=text_classifyelif [ "$mode" = "predict" ]; theneasynlp \--mode=$mode \--worker_gpu=1 \--tables=dev.tsv \--outputs=dev.pred.tsv \--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \--output_schema=predictions,probabilities,logits,output \--append_cols=label \--first_sequence=sent1 \--second_sequence=sent2 \--checkpoint_path=./classification_model \--micro_batch_size=32 \--sequence_length=128 \--app_name=text_classifyfi
KGBERT
算法概述
KGBERT Finetune示例
export CUDA_VISIBLE_DEVICES=$1
if [ ! -f ./train.tsv ]; then
wget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/easynlp/tutorials/classification/train.tsv
fi
if [ ! -f ./dev.tsv ]; then
wget http://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/easynlp/tutorials/classification/dev.tsv
fi
mode=$2
if [ "$mode" = "train" ]; then
easynlp \
--mode $mode \
--worker_gpu=1 \
--tables=train.tsv,dev.tsv \
--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \
--first_sequence=sent1 \
--second_sequence=sent2 \
--label_name=label \
--label_enumerate_values=0,1 \
--checkpoint_dir=./classification_model \
--learning_rate=3e-5 \
--epoch_num=3 \
--random_seed=42 \
--save_checkpoint_steps=50 \
--sequence_length=128 \
--micro_batch_size=32 \
--app_name=text_classify \
--user_defined_parameters='
pretrain_model_name_or_path=alibaba-pai/pai-kgbert-insurance-base-zh
'
elif [ "$mode" = "evaluate" ]; then
easynlp \
--mode=$mode \
--worker_gpu=1 \
--tables=dev.tsv \
--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \
--first_sequence=sent1 \
--second_sequence=sent2 \
--label_name=label \
--label_enumerate_values=0,1 \
--checkpoint_dir=./classification_model \
--sequence_length=128 \
--micro_batch_size=32 \
--app_name=text_classify
elif [ "$mode" = "predict" ]; then
easynlp \
--mode=$mode \
--worker_gpu=1 \
--tables=dev.tsv \
--outputs=dev.pred.tsv \
--input_schema=label:str:1,sid1:str:1,sid2:str:1,sent1:str:1,sent2:str:1 \
--output_schema=predictions,probabilities,logits,output \
--append_cols=label \
--first_sequence=sent1 \
--second_sequence=sent2 \
--checkpoint_path=./classification_model \
--micro_batch_size=32 \
--sequence_length=128 \
--app_name=text_classify
fi
