第六天学习进度--(KBQA)初接触知识图谱之动态知识提取(三)
昨天通过networkx自己构建了一个简单的知识图谱,但是遇到了一个问题,就是昨天构建的知识网络只适用于静态的知识提取,相对应的那种动态的知识需要额外进行一个设定。今天就学习如何提取动态的知识并将其添加到对应的问答之中进行测试。
个人暂定的方法(等待分析得到较优的解):
1.利用全局替换,当检测到某个字符串的时候,将该字符串替换成为对应函数的返回
2.额外设定函数的插入方式,重新构建一个动态知识图谱的规则。
第一种方法实现起来相对比较简单,但是在面对复杂问题的处理上,效率来说相对比较低。
第二种方法实现起来相对较复杂,实现起来较难,对于框架的设计都有着较高的要求,但是如果能够实现的话,那么在处理复杂问题的效率上就能得到极大的提升。
目前看来第二种虽然是比较优的方案,但是在这种利用networkx来了解知识图谱的构建的原理的基础上(目的是了解知识图谱的构建而不是优化),首先使用简单第一种方法实现,这样在设计结构和了解知识图谱的构建的时间比上就不会有太大的出入。
有了上述的简单分析之后,就可以开始设计动态知识图谱的构建框架了。对应的,首先需要定义一个函数对应库,函数对应库能根据用户回答的问题抽取出对应的数据,并能根据问答判断需要根据那一个函数来进行回答,依据的函数代入的数据是用户提问的问题主体或者知识图谱的实体后继。为了解决上述的抽取出对应的数据的问题,需要设定问题的文本分类器,将用户提问的问题匹配到一个模板上面,然后能根据匹配完后的模板,将其利用对应的函数进行计算并返回答案来代入对应的问答中。这样就解决了第一个问题。
具体究竟是如何实现的?这一部分主要是在用户提问的环节进行判断的过程。那这个部分就要设定一个文本的分类器,将其归类为动态回答问题还是静态回答问题,这个部分我打算利用前天学习到的知识,用文档向量来构建一个问题分类器(此处可以尝试构建多个问题分类器,用来细化问题的判断及提问问题判断的时候问题归类的准确性):文本情感分类
这样就解决了第二个问题,静动态问题的归属(有了静动态问题归属,那是否需要静动态知识图谱分离)
解决了上述的两个问题后,就开始着手开始研究代码的修改部分了。第一步就是要完成静动态问题回答的时候的变化情况,即在添加完动态的回答后,不会影响静态问题的回答。(隐约觉得这一步是关键)
静态问题的回答是简单的,一般来说拥有特定的矩形,而动态回答的问题则需要对整体部分做一定程度上的修改,例如回答今天揭阳的天气怎么样这类问题,首先不能直接提取知识图谱中的数据,因为这一部分的数据需要自己去做分析,这样的一个分析的过程肯定首先要被执行,因此我们最终做出的回答应该是动态变化的而不是静态变化的。
那么有了上面的思路之后我们开始对先前的代码进行一部分的修改。
添加函数的对应字典:
# 根据实体名称添加函数对应关系(仅替代节点),function带有参数 G,node1,node2,relation_info,relation 返回更换句子数据str def add_function(NodeOrRelation, function): global dictfunction global dictfunction try: if not dictfunction: dictfunction = {} except: dictfunction = {} dictfunction[NodeOrRelation] = function # 删除函数对应关系 def del_function(NodeOrRelation): global dictfunction try: if not dictfunction: dictfunction = {} except: dictfunction = {} dictfunction[NodeOrRelation] = None
对原子处理部分进行修改:
# 原子信息处理 def nlp_atom_handle(digraph: nx.DiGraph, node1, node2, relation="relation"): ptype = get_nodes_relation(digraph, node1, node2, relation) n_2 = str(node2) n_1 = str(node1) n_relation = str(digraph[node1][node2][relation]) global dictfunction try: if not dictfunction: dictfunction = {} except: dictfunction = {} if (dictfunction): if (node1 in dictfunction): return dictfunction[node1](digraph, node1, node2, n_relation, relation) elif (relation in dictfunction): return dictfunction[n_relation](digraph, node1, node2, n_relation, relation) elif (node2 in dictfunction): return dictfunction[node2](digraph, node1, node2, n_relation, relation) if (ptype == 4): return "是" + n_2 + "的" + n_relation elif (ptype == 3): return n_relation + n_2 + ";" + n_2 + n_relation + n_1 elif (ptype == 2): return n_2 + n_relation + n_1 elif (ptype == 1): return n_relation + n_2 else: return None
在先前的知识图谱的基础上,添加对应的天气节点和揭阳(地方)节点。
将对应的地方节点的属性指向对应的天气节点的属性
对应的代码如下:
# 动态知识提取测试 # 添加地方节点 add_node_attribute(G, ['揭阳'], '地方') # 添加函数映射 add_node_attribute(G, ['天气'], 'function') # 在知识图谱中添加揭阳的天气对应 add_edge(G, "揭阳", "天气", "link") # 添加自定义动态句型(句型的开头默认都存在一个node1节点) def jy_weather(G, node1, node2, info, relation): return "%s很好"%(node2) add_function('天气', jy_weather) print("动态知识提取:",nlp_nodes(G,'揭阳','天气'))
纳闷现在已经对处理的部分进行了动态的句型设置,我们来看看最终的实现效果如何。
可以看到最终调用最终的回答的时候,对应的句型已经变成了我们设置好的返回句型了(在这个部分可以对其句型进行处理,这样就形成了一个动态知识提取的过程)