赞
踩
本项目是基于知识图谱的问答系统,BERT/BILSTM+CRF做命名实体识别和句子相似度比较,最后实现线上的部署。
项目的分以下步骤进行描述:
NLPCC全称自然语言处理与中文计算会议(The Conference on Natural Language Processing and Chinese Computing),它是由中国计算机学会(CCF)主办的 CCF 中文信息技术专业委员会年度学术会议,专注于自然语言处理及中文计算领域的学术和应用创新。
此次使用的数据集来自NLPCC ICCPOL 2016 KBQA 任务集,其包含 14 609 个问答对的训练集和包含 9 870 个问答对的测试集。 并提供一个知识库,包含 6 502 738 个实体、 587 875 个属性以及 43 063 796 个 三元组。知识库文件中每行存储一个事实( fact) ,即三元组 ( 实体、属性、属性值) 。各文件统计如下:
训练集:14609
开发集:9870
知识库:43063796
知识库样里如下:
空气干燥 ||| 别名 ||| 空气干燥
空气干燥 ||| 中文名 ||| 空气干燥
空气干燥 ||| 外文名 ||| air drying
空气干燥 ||| 形式 ||| 两个
空气干燥 ||| 作用 ||| 将空气中的水份去除
原数据中本只有问答对(question-answer),并无标注三元组(triple),本人所用问答对数据来自该比赛第一名的预处理https://github.com/huangxiangzhou/NLPCC2016KBQA。构造Triple的方法为从知识库中反向查找答案,根据问题过滤实体,最终筛选得到,也会存在少量噪音数据。该Triple之后用于构建实体识别和属性选择等任务的数据集。
<question id=1> 《机械设计基础》这本书的作者是谁?
<triple id=1> 机械设计基础 ||| 作者 ||| 杨可桢,程光蕴,李仲生
<answer id=1> 杨可桢,程光蕴,李仲生
==================================================
<question id=2> 《高等数学》是哪个出版社出版的?
<triple id=2> 高等数学 ||| 出版社 ||| 武汉大学出版社
<answer id=2> 武汉大学出版社
==================================================
<question id=3> 《线性代数》这本书的出版时间是什么?
<triple id=3> 线性代数 ||| 出版时间 ||| 2013-12-30
<answer id=3> 2013-12-30
==================================================
第一步:主要模型构建
基于知识图谱的⾃动问答主要的模型主要有两个:
命名实体识别步骤和属性映射步骤。(就是上述流程的第2、4步)
其中,实体识别步骤的⽬的是找到问句中询问的实体名称,如下图,就是找到“⽯头记”这个实体。
⽽属性映射步骤的⽬的在于找到问句中询问的相关属性,如下图,就是找到这句话问的是“作者”这个属
性。
命名实体识别步骤,采⽤BERT+BiLSTM+CRF⽅法(另外加上⼀些规则映射,可以提⾼覆盖度)。
属性映射步骤,转换成⽂本相似度问题,采⽤BERT作⼆分类训练模型。
第二步:通过实体识别模型检测问句中的实体,得到实体
输⼊任意⼀个问句,⽤上⼀步训练好的命名实体识别模型进⾏预测。
本项⽬⽀持在线预测,即可以⾃⼰输⼊⼀句话,然后进⾏预测,得到实体,预测完后,可以再次输⼊。
第三步:在知识库中检索实体,得到和实体相关的前n个三元组。
完成实体识别模型和属性抽取模型后还需进⾏知识库检索。
由于知识库数据量庞⼤,不可能加载到缓存中遍历检索知识库。所以可以导⼊到mysql数据库中,然后
按上步预测的三元组的实体进⾏检索,得到和实体相关的前n个三元组。
第四步:通过属性抽取模型在n个三元组中挑选最合适的属性,得到唯⼀三元组。
这步采⽤了两种⽅式来返回答案。
命名实体识别和属性映射评价标准:召回率 (Recall),精确率 (Precision) ,F1-Score。⽽对话系统的评价标准以⼈⼯评价为主,以及BLEU和Perplexity。
清洗训练数据、测试数据、知识库过滤属性,去除‘-’,‘•’,空格等噪音符号;同时把每一行lower()转成小写。
https://github.com/huangxiangzhou/NLPCC2016KBQA
构造NER的数据集,我们的数据是有三元组实体信息的,可以反向标注问题,给训练集和测试集的
Question 打标签。我们这⾥采⽤BIO的标注⽅式,因为识别⼈名,地名,机构名的任务不是主要的,我
们只要识别出实体就可以了,因此,我们⽤B-LOC, I-LOC代替其他的标注类型,如下图,这⾥B是句
⾸,I是句中,O是⾮实体。
构造SIM的数据集:一个sample由“问题+属性+Label”构成,原始数据中的属性值置为1
0 你知道计算机应用基础这本书的作者是谁吗? 作者 1
1 你知道计算机应用基础这本书的作者是谁吗? 稀有程度 0
2 你知道计算机应用基础这本书的作者是谁吗? 已离开成员 0
3 你知道计算机应用基础这本书的作者是谁吗? 界面语言 0
4 你知道计算机应用基础这本书的作者是谁吗? 跨度 0
5 你知道计算机应用基础这本书的作者是谁吗? 英文名称 0
6 计算机应用基础这本书的出版社是那个? 出版社 1
7 计算机应用基础这本书的出版社是那个? 投资 0
8 计算机应用基础这本书的出版社是那个? 罗马拼音 0
9 计算机应用基础这本书的出版社是那个? 出身 0
10 计算机应用基础这本书的出版社是那个? 出生时间 0
......
构造数据库的数据:实体+属性+答案
entity,attribute,answer
机械设计基础,作者,杨可桢,程光蕴,李仲生
高等数学,出版社,武汉大学出版社
线性代数,出版时间,2013-12-30
安德烈,国籍,摩纳哥
线性代数,isbn,978-7-111-36843-4
高等数学,书名,高等数学一(微积分)
万达广场,外文名,amoywandaplaza
李明,出生日期,1963.1
韩娱守护力,小说进度,连载
夏想,连载网站,潇湘书院
大学计算机基础,页数,272
城关镇,中文名,城关镇[临澧县]
李明,出生地,青海湟源
大学计算机基础,定价,25元
其中遇到的问题:
未完全匹配的实体样例:部分是识别错误,部分是同义词,部分是噪音
【识别错误】动物地鸠属是属于什么目呀? 地鸠属 动物地鸠
【同义词】《鲁迅全集》这本书是什么时候出版的呀? 《鲁迅全集》 鲁迅全集
【同义词】我想知道永安镇机场叫什么名字? 永安镇 永安镇机场
【同义词】海尔kfrd-35gw/03me-s4空调是什么类型的? 海尔kfrd-35gw/03me-s4 海尔kfrd-35gw/03me-s4空调
【问题噪音】你知道辛夷花粥管治疗什么吗? 辛夷花粥 辛夷花粥管
那么这少部分数据过滤掉。
最后生成插入到数据库中的数据:
然后通过数据的相关处理,输入到模型中的数据如下:
class BertCrf(nn.Module): def __init__(self,config_name:str,model_name:str = None,num_tags: int = 2, batch_first:bool = True) -> None: self.batch_first = batch_first if not os.path.exists(config_name): raise ValueError( "未找到模型配置文件 '{}'".format(config_name) ) else: self.config_name = config_name if model_name is not None: if not os.path.exists(model_name): raise ValueError( "未找到模型预训练参数文件 '{}'".format(model_name) ) else: self.model_name = model_name else: self.model_name = None super().__init__() self.bert_config = BertConfig.from_pretrained(self.config_name) self.bert_config.num_labels = num_tags self.model_kwargs = {'config': self.bert_config} # 如果模型不存在 if self.model_name is not None: self.bertModel = BertForTokenClassification.from_pretrained(self.model_name, **self.model_kwargs) else: self.bertModel = BertForTokenClassification(self.bert_config) # 调用这里 self.crf_model = CRF(num_tags=num_tags,batch_first=batch_first) def forward(self,input_ids:torch.Tensor, tags:torch.Tensor = None, attention_mask:Optional[torch.ByteTensor] = None, token_type_ids=torch.Tensor, decode:bool = True, # 是否预测编码 reduction: str = "mean") -> List: emissions = self.bertModel(input_ids = input_ids,attention_mask = attention_mask,token_type_ids=token_type_ids)[0] new_emissions = emissions[:,1:-1] # [seq,batchs,3] seq = 64-2 =62 [32,62,3] new_mask = attention_mask[:,2:] # [32,62] if tags is None: loss = None pass else: new_tags = tags[:, 1:-1] # shape= [32.62] loss = self.crf_model(emissions=new_emissions, tags=new_tags, mask=new_mask, reduction=reduction) # [1] if decode: tag_list = self.crf_model.decode(emissions = new_emissions,mask = new_mask) return [loss, tag_list] return [loss]
定义优化器和训练策略
no_decay = ['bias', 'LayerNorm.weight','transitions']
optimizer_grouped_parameters = [
{'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],
'weight_decay': args.weight_decay},
{'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
optimizer = AdamW(optimizer_grouped_parameters,lr=args.learning_rate,eps=args.adam_epsilon)
scheduler = WarmupLinearSchedule(optimizer, warmup_steps=args.warmup_steps, t_total=t_total)
参考论文BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
很多博客都有详细的介绍,这里就不多描述。
但是本项⽬的输⼊和输出有点不同,输⼊为分类符[CLS]+句⼦Token+结尾符[SEP],输出也是[CLS]+句
⼦Token+[SEP]对应的最后⼀层的向量,维度为[batch_sizes,seq_len,hidden_sizes]。
我参考的博客:
pytorch BiLSTM+CRF代码详解
pytorch中bilstm-crf部分code解析
最通俗易懂的BiLSTM-CRF模型中的CRF层介绍
Bidirectional LSTM-CRF Models for Sequence Tagging(论文翻译)
pytorch lstm crf 代码理解
我的代码:BERT_CRF.py / CRF_Model.py
global_step = 0 tr_loss, logging_loss = 0.0, 0.0 model.zero_grad() train_iterator = trange(int(args.num_train_epochs), desc="Epoch") set_seed(args) best_f1 = 0. for _ in train_iterator: epoch_iterator = tqdm(train_dataloader, desc="Iteration") for step,batch in enumerate(epoch_iterator): batch = tuple(t.to(args.device) for t in batch) inputs = {'input_ids':batch[0], 'attention_mask':batch[1], 'token_type_ids':batch[2], 'tags':batch[3], 'decode':True }
# 发射概率,每个单词wi->tagj的概率
# emissions.shape [bs,sl,num_labels]
emissions = self.bertModel(input_ids = input_ids,attention_mask = attention_mask,token_type_ids=token_type_ids)[0]
class CRF(nn.Module): def __init__(self,num_tags : int = 2, batch_first:bool = True) -> None: super().__init__() self.num_tags = num_tags # 3 self.batch_first = batch_first self.start_transitions = nn.Parameter(torch.empty(num_tags)) self.end_transitions = nn.Parameter(torch.empty(num_tags)) self.transitions = nn.Parameter(torch.empty(num_tags,num_tags)) self.reset_parameters() def reset_parameters(self): init_range = 0.1 nn.init.uniform_(self.start_transitions,-init_range,init_range) nn.init.uniform_(self.end_transitions,-init_range,init_range) nn.init.uniform_(self.transitions, -init_range, init_range) def forward(self, emissions:torch.Tensor, tags:torch.Tensor = None, mask:Optional[torch.ByteTensor] = None, reduction: str = "mean") -> torch.Tensor: mask=torch.tensor(mask,dtype=torch.uint8) self._validate(emissions, tags = tags ,mask = mask) reduction = reduction.lower() # if reduction not in ('none','sum','mean','token_mean'): # raise ValueError(f'invalid reduction {reduction}') if mask is None: mask = torch.ones_like(tags,dtype = torch.uint8) if self.batch_first: # emissions.shape (seq_len,batch_size,tag_num) emissions = emissions.transpose(0,1) tags = tags.transpose(0,1) mask = mask.transpose(0,1) # numerator shape: (batch_size,) [32] numerator = self._computer_score(emissions=emissions,tags=tags,mask=mask) # 计算s(X,y) # shape: (batch_size,) [32] denominator = self._compute_normalizer(emissions=emissions,mask=mask) # 计算 log(Σe^(x,y')) # log(Σe^(S(X,y))) - S(X,y) llh = denominator - numerator # [batch_size] 32 if reduction == 'none': return llh elif reduction == 'sum': return llh.sum() elif reduction == 'mean': return llh.mean() assert reduction == 'token_mean' return llh.sum() / mask.float().sum() def decode(self,emissions:torch.Tensor, mask : Optional[torch.ByteTensor] = None) ->List[List[int]]: self._validate(emissions=emissions,mask=mask) if mask is None: mask = emissions.new_ones(emissions.shape[:2],dtype=torch.uint8) if self.batch_first: emissions = emissions.transpose(0,1) # [62,32,3] mask = mask.transpose(0,1) # [62,32] return self._viterbi_decode(emissions,mask)
模型分析
总的来说,⽤单独的CRF和单独的BiLSTM都可以做命名实体识别,以下是两个模型的⼀些区别。
LSTM:像 RNN、 LSTM、BILSTM 这些模型,它们在序列建模上很强⼤,它们能够捕捉⻓远的上下⽂信息,此外还具备神经⽹络拟合⾮线性的能⼒,这些都是 CRF ⽆法超越的地⽅,对于t时刻来说,输出层y_t受到隐层h_t(包含上下⽂信息)和输⼊层x_t (当前的输⼊)的影响,但是y_t和其他时刻的y_t是相互独⽴的,感觉像是⼀种point wise,对当前t时刻来说,我们希望找到⼀个概率最⼤的y_t,但其他时刻的y_t对当前y_t没有影响,如果y_t之间存在较强的依赖关系的话(例如,形容词后⾯⼀般接名词,存在⼀定的约束),LSTM⽆法对这些约束进⾏建模,LSTM模型的性能将受到限制。
CRF:它不像LSTM等模型,能够考虑⻓远的上下⽂信息,它更多考虑的是整个句⼦的局部特征的线性加权组合(通过特征模版去扫描整个句⼦)。关键的⼀点是,CRF的模型为p(y|x,w) ,注意这⾥y和x都是序列,它有点像list wise,优化的是⼀个序列y=(y1,y2,…,yn),⽽不是某个时刻的y_t,即找到⼀个概率最⾼的序列y=(y1,y2,…,yn)使得p(y1,y2,…,yn|x,w)最⾼,它计算的是⼀种联合概率,优化的是整个序列(最终⽬标),⽽不是将每个时刻的最优拼接起来,在这⼀点上CRF要优于LSTM。
HMM:CRF不管是在实践还是理论上都要优于HMM,HMM模型的参数主要是“初始的状态分布”,“状态之间的概率转移阵”,“状态到观测的概率转移矩阵”,这些信息在CRF中都可以有,例如:在特征模版中考虑h(y1) , f(yi-1,yi),g(yi,xi) 等特征。
CRF与LSTM:从数据规模来说,在数据规模较⼩时,CRF的试验效果要略优于BILSTM,当数据规模较⼤时,BILSTM的效果应该会超过CRF。从场景来说,如果需要识别的任务不需要太依赖⻓久的信息,此时RNN等模型只会增加额外的复杂度,此时可以考虑类似科⼤讯⻜FSMN(⼀种基于窗⼝考虑上下⽂信息的“前馈”⽹络)。
CNN+BILSTM+CRF:这是⽬前学术界⽐较流⾏的做法,BILSTM+CRF是为了结合以上两个模型的优点,CNN主要是处理英⽂的情况,英⽂单词是由更细粒度的字⺟组成,这些字⺟潜藏着⼀些特征(例如:前缀后缀特征),通过CNN的卷积操作提取这些特征,在中⽂中可能并不适⽤(中⽂单字⽆法分解,除⾮是基于分词后),这⾥简单举⼀个例⼦,例如词性标注场景,单词football与basketball被标为名词的概率较⾼, 这⾥后缀ball就是类似这种特征。
BERT+CRF:其实在整个项目中变动的是,emissions的不同,每个单词到不同tag的发射概率。BERT在特征抽取和上下文语义的理解上来说,是由于BILSTM的。但时间上是没有它快的。
训练集数量:13637
总的epoch:15
batch_sizes:16
累计更新梯度step:4 (相当于64的batch_size)
总的step:6392
运⾏环境:GPU
训练过程如下,最后loss的范围在14左右,可以看到,在3000个step左右,loss降的差不多了。
训练集数量:9016
batch_sizes:8
运⾏环境:GPU
评估指标:这⾥的测试集的评估指标评估的是真实标签值id和预测标签id的差别。
# 计算真实句子的长度,返回真实句子长度的list # batch[3]真实的命名实体识别,batch[1]mask def statistical_real_sentences(input_ids:torch.Tensor,mask:torch.Tensor,predict:list)-> list: # shape (batch_size,max_len) assert input_ids.shape == mask.shape # batch_size assert input_ids.shape[0] == len(predict) # 第0位是[CLS] 最后一位是<pad> 或者 [SEP] new_ids = input_ids[:,1:-1] # [batch_size, seq_len-2] new_mask = mask[:,2:] real_ids = [] for i in range(new_ids.shape[0]): seq_len = new_mask[i].sum() # 计算每句话有多少个字 assert seq_len == len(predict[i]) real_ids.append(new_ids[i][:seq_len].tolist()) return real_ids
precision recall f1-score support
0 0.989159 0.983189 0.986165 47053
1 0.977180 0.975417 0.976298 4434
2 0.965636 0.977964 0.971761 22872
accuracy 0.981119 74359
macro avg 0.977325 0.978857 0.978075 74359
weighted avg 0.981210 0.981119 0.981146 74359
准确率很高,98.11%
运行脚本python test_pro.py
运行中间变量展示:
question entity answer predict
你知道计算机应⽤基础这本书的作者是谁吗? 计算机应⽤基础 作者 秦婉,王蓉 计算机应⽤基础
计算机应⽤基础这本书的出版社是那个? 计算机应⽤基础 出版社 机械⼯业出版社 计算机应⽤基础
告诉我⾼等数学的出版时间是什么时候? ⾼等数学 出版时间 2004年 ⾼等数学
我想知道戴维斯是什么国家的⼈? 戴维斯 国籍 美国 戴维斯
你知道⾼等数学的isbn吗? ⾼等数学 isbn 9.7873E+12 ⾼等数学
你知道mc的外⽂名是什么吗? mc 外⽂名 emcee mc
王平的出⽣⽇期是哪⼀年呀? 王平 出⽣⽇期 1954年 王平
彼岸之界⼩说进度到哪⾥了? 彼岸之界 ⼩说进度 情节展开 彼岸之界
告诉我剑魔传的连载⽹站是哪个? 剑魔传 连载⽹站 天涯读书 剑魔传
.............
最终结果如下:基本都预测正确。
input the test sentence: 中华⼈⺠共和国成⽴于什么时候? # 输⼊的句⼦ [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']]# 预测的实体结果 识别的实体有:中华⼈⺠共和国 time used: 1 sec input the test sentence: 统计学习⽅法的作者是谁? [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O']] 识别的实体有:统计学习⽅法 time used: 0 sec input the test sentence: 机器学习这本书是哪个出版社出版的? [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']] 识别的实体有:机器学习 time used: 0 sec input the test sentence: 统计学习⽅法和机器学习哪本书⽐较好? # 这⾥的两个实体之间的 “和” 字没有预测到 [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'ILOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O', 'O']] 识别的实体有:统计学习⽅法和机器学习 time used: 0 sec input the test sentence: ⽯头记和红楼梦是同⼀本书嘛? [['B-LOC', 'I-LOC', 'I-LOC', 'O', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O', 'O']] 识别的实体有:⽯头记 红楼梦 time used: 0 sec input the test sentence: .............
输入模型的数据格式处理代码:
def sim_convert_examples_to_features(examples,tokenizer, max_length=512, label_list=None, pad_token=0, pad_token_segment_id = 0, mask_padding_with_zero = True): label_map = {label: i for i, label in enumerate(label_list)} features = [] for (ex_index, example) in enumerate(examples): # inputs.keys = ['special_tokens_mask':, 'input_ids', 'token_type_ids'] # special_tokens_mask : 1 00000 1 00 1 1表示CLS或者SEP,前面的零表示问题,后面的零表示正文 # input_ids : 101 问题的idx 102 属性 102 # token_type_ids : 0000 111 其中111表示答案所在的位置 inputs = tokenizer.encode_plus( text = example.question, # 问题 text_pair= example.attribute, # 属性 add_special_tokens=True, max_length=max_length, truncation_strategy='longest_first', # We're truncating the first sequence in priority if True ) input_ids, token_type_ids = inputs["input_ids"], inputs["token_type_ids"] attention_mask = [1 if mask_padding_with_zero else 0] * len(input_ids) padding_length = max_length - len(input_ids) input_ids = input_ids + ([pad_token] * padding_length) attention_mask = attention_mask + ([0 if mask_padding_with_zero else 1] * padding_length) token_type_ids = token_type_ids + ([pad_token_segment_id] * padding_length) assert len(input_ids) == max_length, "Error with input length {} vs {}".format(len(input_ids), max_length) assert len(attention_mask) == max_length, "Error with input length {} vs {}".format(len(attention_mask),max_length) assert len(token_type_ids) == max_length, "Error with input length {} vs {}".format(len(token_type_ids),max_length) # label = label_map[example.label] label = example.label if ex_index < 5: logger.info("*** Example ***") logger.info("guid: %s" % (example.guid)) logger.info("input_ids: %s" % " ".join([str(x) for x in input_ids])) logger.info("attention_mask: %s" % " ".join([str(x) for x in attention_mask])) logger.info("token_type_ids: %s" % " ".join([str(x) for x in token_type_ids])) logger.info("label: %s " % str(label)) features.append( SimInputFeatures(input_ids,attention_mask,token_type_ids,label) ) return features
训练代码就是:BERT分类fine-tine时取出的是BERT模型最后⼀层的CLS隐藏层向量model.get_pooled_output()。然后
在经过⼀层全连接层,就可以输出最终的类别输出概率了。
这部分内容的意思是:我给出一个问题,然后BERT+CRF识别,提取出我的问题中的实体,然后在数据库中找到这个实体,当然这个实体有可能有很多的
属性和答案。那么我们用bert来计算我的问题和哪些属性最相关,然后返回最相关的属性回来。并与正确答案属性ID进行匹配。统计正确率。
model = BertForSequenceClassification.from_pretrained(args.pre_train_model, **model_kwargs)
模型迭代的过程如下图所示:以下是3个epoch,总共7000个step的loss图,可以看到BERT⽐较粗暴,不到⼀个epoch,基本上已经
收敛了。
test
loss 0.0680166557431221
question_acc 0.8998987078666687
label_acc 0.9826409816741943
dev
loss 0.026128364726901054
question_acc 0.9572441577911377
label_acc 0.9926713705062866
train
loss 0.01614166982471943
question_acc 0.9722089171409607
label_acc 0.9953110814094543
这个章节我们主要解决项⽬介绍结束⽅案流程的第三步:在知识库中检索实体,得到和实体相关的前n个三元组。完成实体识别模型和属性抽取模型后还需进⾏知识库检索。知识库进⾏检索⽅式不可能⽤普通的for循环进⾏检索,所以这⾥直接把2个多G的⽂件nlpcc-iccpol-2016-total.kbqa.kb,导⼊mysql数据库中,有4千多万⾏,本地导⼊⼤概7个多⼩时,建议使⽤GPU服务器,我这边使⽤的本地数据库。导⼊数据库前需要下载安装mysql,具体安装⽅式根据⾃⼰的操作系统下载安装。安装完后,运⾏如下命令进⼊mysql数据库中。
mysql -u root -p
create database KB_QA character set utf8mb4 collate utf8mb4_unicode_ci;
use KB_QA;
抽取三元组有两种⽅式。
# 测试第⼀个句⼦ ------------------------------ input the test sentence: 统计学习⽅法的作者是谁? your input is:['统', '计', '学', '习', '⽅', '法', '的', '作', '者', '是', '谁', '?'] [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O']] LOC: 统计学习⽅法 模型识别的实体有:统计学习⽅法 ------------------------------ 返回结果如下: # ⾮语义匹配,速度很快 提出的问题: 统计学习⽅法的作者是谁? 模型识别的实体+属性:统计学习⽅法+作者 知识库中得到的答案: 李航 Time used: 1 sec # 测试第⼆个句⼦ ------------------------------ input the test sentence: 中华⼈⺠共和国成⽴时间? your input is:['中', '华', '⼈', '⺠', '共', '和', '国', '成', '⽴', '时', '间', '?'] [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O']] LOC: 中华⼈⺠共和国 模型识别的实体有:中华⼈⺠共和国 ------------------------------------ # 可以看到中华⼈⺠共和国在知识库中的属性有下⾯这么多 # 属性没有输⼊问题的“成⽴时间”,⾮语义匹配不成功,转到BERT语义匹配 建国时间 0.9826155 国庆⽇ 0.91526103 国庆节 0.067031346 政体 0.013450251 国⼟⾯积 0.007624702 国旗 0.006048701 国⻦ 0.0048175533 国歌 0.003324484 国花 0.002280873 时区 0.0021266777 .................. 最⾼⽴法机构 8.25461e-05 主要节⽇ 8.20243e-05 英⽂名 8.030466e-05 主要宗教 7.857168e-05 官⽅语⾔ 7.641088e-05 货币 7.606106e-05 主要戏曲 7.542803e-05 著名⾼校 7.509416e-05 基尼系数 7.4889205e-05 ⾼等学府 7.221342e-05 国家代码 7.152084e-05 返回结果如下: 提出的问题: 中华⼈⺠共和国成⽴时间? 模型识别的实体+属性:中华⼈⺠共和国+建国时间 知识库中得到的答案: 1949年10⽉1⽇ Time used: 13 sec # 在本地运⾏,有点慢,建议到GPU运⾏,会快很多。 # 第三个句⼦ ------------------------------ 平安大厦的地址在哪? your input is:['平', '安', '大', '厦', '的', '地', '址', '在', '哪', '?'] [['B-LOC', 'I-LOC', 'I-LOC', 'I-LOC', 'O', 'O', 'O', 'O', 'O', 'O']] LOC: 平安大厦 模型识别的实体有:平安大厦 # 知识库中没有平安大厦这个实体 ------------------------------ 返回结果如下: 知识库中没有此实体 input the test sentence:
其实用question和attribute进行一个相似度计算做排序是有缺陷的,毕竟question的句子明显更长,语义明显比attribute更丰富,单拿attribute进行匹配有种断章取义的感觉,所以不提倡,但是也适用。
本项⽬的检索⽅式为完全匹配,⽐如《中国的⾸都在哪?》,假如我们的数据库中没有《中国》这个实体,但是有《中华⼈⺠共和国 ||| ⾸都 ||| 北京》这个三元组,我们可以想办法让数据库检索《中国》时,能同时检索到《中华⼈⺠共和国》作为候选三元组返回。⼀个好的想法是检索包含《中国》的所有实体,但是这样检索数据库时会需要遍历数据库,⾮常耗时间。
https://zhuanlan.zhihu.com/p/62946533
https://github.com/WenRichard/KBQA-BERT?tdsourcetag=s_pctim_aiomsg
https://www.zhihu.com/question/46688107?sort=created
https://www.jiqizhixin.com/articles/2019-01-09-17
https://blog.csdn.net/Jason__Liang/article/details/81772632
https://zhuanlan.zhihu.com/p/59845590
https://github.com/jkszw2014/bert-kbqa-NLPCC2017
https://github.com/WenRichard/KBQA-BERT?tdsourcetag=s_pctim_aiomsg
https://github.com/zhangyunxing37/knowledge_grapy-kbqa_demo?tdsourcetag=s_pctim_aiomsg
https://github.com/huangxiangzhou/NLPCC2016KBQA
https://zhuanlan.zhihu.com/p/44042528
https://blog.csdn.net/Elvira521yan/article/details/88415512
https://blog.csdn.net/appleml/article/details/78579675
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。