虽然网上代码一大堆,论文一大堆,但是我连一篇实实在在介绍基于知识库的问答系统实现逻辑简单介绍的都找不到。
当然,基于对模板匹配的博客我倒是找到了一篇,见:
https://blog.csdn.net/xyz1584172808/article/details/89319129(写的非常好,整个问答系统的搭建流程都说明白了。)
但是他这篇文章相较于基于知识库的问答来说少了很多东西,我首先总结一下他这篇简单的实现流程,然后再引申到基于知识库的问答系统。
先说明一下:基于模板和基于知识库其实非常相似
阶段主要有:
例如:“刘德华演过哪些电影呀?”
对其进行命名实体识别,就可以得到:“刘德华”,且也能将它标注为“nnt”,nnt实际上就是“人”的词性,这个实体识别过程就相当于一个分词+词性标注过程。(这个过程可以用bert来做,就不会产生用jieba分词把词分错的问题了。比如卧虎藏龙 分成了 卧虎 藏龙两个词)
注意,电影在此处不是个实体,它应该包含在“演过哪些电影”这个问题之中(更简单的理解就是“演过的电影”是一个关系)
例如某dict中保存有如下的模板(nm代表电影,nnt代表人物,ng代表电影类型,nnr我也不知道是啥):
0:nm 评分
1:nm 上映时间
2:nm 类型
3:nm 简介
4:nm 演员列表
5:nnt 介绍
6:nnt ng 电影作品
7:nnt 电影作品
8:nnt 参演评分 大于 x
9:nnt 参演评分 小于 x
10:nnt 电影类型
11:nnt nnr 合作 电影列表
12:nnt 电影数量
13:nnt 出生日期
训练阶段:
对于“刘德华演过哪些电影呀?”这个问题,他可以转化成模板7,也就是nnt 电影作品。我们把这个转化成一个多分类问题,具体流程为:首先可以用cnn(或lstm啥的都行)对“刘德华演过哪些电影呀?”提取特征,接一个全连接层,用这14个分类做一个loss,反向传播。同样的,输入另一段训练语料,比如“刘德华的出生日期”,也可以做训练语料。这样就能训练一个模型,这个模型的作用是:输入一句话,比如“卧虎藏龙的电影类型是什么?”,然后他就会将这句话映射到模板10(也就是nnt 电影类型)上
测试阶段:
此处共有14个问题模板
那么,比如“刘德华演过哪些电影呀?”这个问题,我们将它输入到我们刚刚训练好的模型里,就可以得到这句话对应的模板类型,也就是7了。(当然,预测结果甚至有可能是2、3这类主语是nm而不是nt的,这时我们就可以做一个限制。由于我们通过ner能得到“刘德华演过哪些电影呀?”中刘德华这个实体,同时也能知道“刘德华”所对应的实体类型为nnt,那么我们就可以只在带有nnt的模板中,选择预测概率最高的)
例如“刘德华演过哪些电影呀?”,通过上面的步骤,我们可以得到模板“nnt 电影作品”,也可以得到“刘德华演过哪些电影呀?”这句话中的实体刘德华(nnt),于是可以将nnt所对应的“刘德华”填入到模板“nnt 电影作品”中,就可以得到一条查询语句“刘德华 电影作品”了,用图数据库查询语句就是
SELECT ?x WHERE
{
<刘德华> <参演电影> ?x
}
非常像一条sql语句。
然后就可以查到刘德华参演的所有电影了
至此,已经将这套简单的方案完全介绍完成
下面的方案将解决上面提到的那几个问题
继续使用bert-ner,例如“阿根廷国家的政策是什么?”,可以抽取到“阿根廷国家”这个实体
现在,网络上实际上是有实体链接词典去解决那种“别名的问题了”
说明一下:
第一列是实体名称,第二列是实体名称可能指向的具体(具体到类型)的实体。这样实际上我们就可以解决“别名”(指代消解)问题,但是实体链接问题我们实际上还是没有解决。比如“阿根廷踢了一场好球”,用bert进行ner后拿到了阿根廷,我们人工来看的话很容易就知道这句话里阿根廷代表“阿根廷_(足球国家队)”,但是机器直接看这句话的话是不知道应该从
接下来,我们可以选择
①深度文本匹配模型:首先用cnn去分别扫描“阿根廷红场之役在哪一年踢的?”及“阿根廷_(国家)”这几个字,做一个pairwise(模型),输出一下相似度。
然后再分别扫描“阿根廷踢了一场好球”及“阿根廷_(足球国家队)”,输出相似度,依次类推(关于训练过程的设计我就不说了,可以用esim等模型)
②lgb,也是做文本相似度模型,做特征,详细的特征需要看论文(lgb在论文里是最优的,高于pairwise)
取相似度最高的top-n个实体作为候选实体
论文中采用的是规则+模板匹配的方式,比如1997年八月,用规则将其转成知识库中规范的格式,比如1997年8月,然后对知识库中的所有属性进行匹配(具体看论文吧)
通过实体识别以及实体链接,我们可以分析“徐峥出演的电影有哪些?”,得到“徐峥_(演员)”这个具体的实体,然后我们就可以从知识库中将带有“徐峥_(演员)”的所有的三元组都拿到,例如
(徐峥_(演员),出演,股票的颜色_(电影))
(徐峥_(演员),出演,春光灿烂猪八戒_(电影))
(徐峥_(演员),出演,爱情呼叫转移_(电影))
(徐峥_(演员),出演,夜店_(电影))
(徐峥_(演员),获得奖项,电影频道传媒大奖_(奖项))
(徐峥_(演员),获得奖项,电影金马奖最佳男主角_(奖项))
(徐峥_(演员),导演,我和我的祖国_(电影))
只将"徐峥_(演员)"对应的所有关系拿出来
徐峥_(演员),出演;获得奖项;导演
然后可以用esim(pairwise模型)去将"徐峥出演的电影有哪些"与“徐峥出演了”进行比对**(此处请注意,深度模型的输入一般都是自然语言,像(徐峥,出演)是不可以直接输入进模型的,需要将(徐峥,出演)转换为自然语言,即“徐峥出演了”)**,输出相似度。
接下来依次将(徐峥,获得奖项)、(徐峥、导演)转化为自然语言后和“徐峥出演的电影有哪些”进行比对,输出相似度值,取相似度最高的做为我们的“问题模板”。然后按第一套方案,用数据库查询语言进行查询就可以了。
第二套方案解决了实体链接、指代消解的问题,并且其选择问题模板的方式由多分类换为了相似度比对去选择模板。
但是第二套方案总体来说还是有很多优化的地方,比如实体链接部分可以尝试更多元化的方法(多去寻找特征、试验不同的深度学习模型,尝试ensemble)。还有就是根据文本相似度进行问题模板的选择那里也可以做如上改进,还有将元组转换为自然语言的方法我认为不是100%完美的。
还有的话,对于多跳问题(例如“俄罗斯的首都有多少人口?”,首先需要查俄罗斯的首都是哪个城市,然后还得查这个城市有多少人口)以及实体的属性问题,例如(q610:北京大学出了哪些哲学家
select ?x where { ?x <职业> <哲学家_(基础含义)> . ?x <毕业院校> <北京大学> . })我还没总结,留个坑以后来填~