电影知识图谱问答(五)| 基于微信公众号的电影知识图谱问答Demo
通过【电影知识图谱问答(一)|爬取豆瓣电影与书籍详细信息、电影知识图谱问答(二)|生成298万条RDF三元组数据、电影知识图谱问答(三)|Apache Jena知识存储及SPARQL知识检索、电影知识图谱问答(四)| 问句理解及答案推理】四篇文章的介绍,我们已经了解如何从豆瓣官网中爬取数据;如何将爬取的数据转换得到可用的三元组数据,并存储至Apache Jena之中;如何利用SPARQL查询语言进行知识检索和答案推理;如何理解问句所表达的深层语义信息,即获取问句实体和目标属性信息;如何利用问句的深层语义信息,结合规则和表示学习方法,推理得到问题答案。结合上面几篇文章,已经能够从零开始构建一个电影知识图谱问答系统,有兴趣的朋友可以尝试搭建。本篇文章,将介绍如何将电影知识图谱问答系统部署至微信公众平台,部署完成后能够通过微信公众号进行知识问答。
本项目相关代码已经发布至 GitHub,项目地址为weizhixiaoyi/DouBan-KGQA,欢迎Star。
1. 服务器
将代码部署至微信公众平台需要一个服务器,如果只是用于Demo展示的话,推荐购买腾讯云学生服务器或者阿里云学生服务器,价格十分优惠。个人购买的是腾讯云服务器,配置为1核CPU、2G内存、1Mbps带宽、50GB高性能云盘,价格6个月60元。
购买好服务器之后,需要安装系统环境,个人安装的是Ubuntu16.04系统。系统安装成功之后,安装Anaconda3,配置清华源。然后配置微信公众号,将服务器和微信公众号进行关联。
2. 微信公众号开发
根据微信公众平台开发文档接入指南,将服务器接入至微信公众号。微信官方文档已经比较详细,此处不再进行介绍,接入过程中有相关问题直接在文章下方评论即可。下图是个人微信公众号配置截图,有兴趣的话,也可以关注下个人微信公众号谓之小一。
目前只是将服务器成功接入到微信公众号,但是还不能处理用户发送过来的请求,因此需编写用户请求处理方面的代码。处理方法可参考微信官方Demo,包括微信公众号处理用户请求的流程,如何接收消息,如何发送消息等问题。
熟悉官方Demo之后,开始编写豆瓣电影知识图谱(BM-KGQA)对话处理的代码。首先创建query_server.py文件,用于响应用户经微信公众号端发送过来的请求,并返回相应答案。代码如下所示,其中from query_main import Query是单条问句处理的接口,import receive, reply是定义的微信消息接收和返回函数。
# query_server.py
# -*- coding:utf-8 -*-
import web
import hashlib
from query_main import Query
import receive, reply
query = Query()
class Handle(object):
def __init__(self):
# 初始化query
# self.query = Query()
pass
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "hello, this is handle view"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
# 和公众平台官网-->基本配置中信息填写相同
token = "douban_kgqa"
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
map(sha1.update, list)
hashcode = sha1.hexdigest()
print("handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return "I don't Know"
except Exception as err:
print('ERROR: ' + str(err))
return err
def POST(self):
try:
webData = web.data()
# 后台打印日志
print('Handle Post webdata is ', webData)
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg):
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
if recMsg.MsgType == 'text':
# result = "彩虹屁屁"
question = recMsg.Content
result = query.parse(question)
replyMsg = reply.TextMsg(toUser, fromUser, result)
return replyMsg.send()
if recMsg.MsgType == 'image':
mediaId = recMsg.MsgId
replyMsg = reply.ImageMsg(toUser, fromUser, mediaId)
return replyMsg.send()
else:
return reply.Msg().send()
else:
print('暂且不处理')
return reply.Msg().send()
except Exception as err:
print('ERROR: ' + str(err))
return err
urls = (
'/douban_kgqa', 'Handle'
)
if __name__ == '__main__':
douban_kgqa_web = web.application(urls, globals())
douban_kgqa_web.run()
receive.py是接收用户经微信公众号发送过来的请求,解析后获取问句详细信息。
# receive.py
# -*- coding:utf-8 -*-
import xml.etree.ElementTree as ET
def parse_xml(web_data):
if len(web_data) == 0:
return None
xmlData = ET.fromstring(web_data)
msg_type = xmlData.find('MsgType').text
if msg_type == 'text':
return TextMsg(xmlData)
elif msg_type == 'image':
return ImageMsg(xmlData)
class Msg(object):
def __init__(self, xmlData):
self.ToUserName = xmlData.find('ToUserName').text
self.FromUserName = xmlData.find('FromUserName').text
self.CreateTime = xmlData.find('CreateTime').text
self.MsgType = xmlData.find('MsgType').text
self.MsgId = xmlData.find('MsgId').text
self.Content = xmlData.find('Content').text
class TextMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Content = xmlData.find('Content').text.encode("utf-8")
class ImageMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.PicUrl = xmlData.find('PicUrl').text
self.MediaId = xmlData.find('MediaId').text
reply.py是将需要返回的答案封装成微信要求的数据类型。
# reply
# -*- coding:utf-8 -*-
import time
class Msg(object):
def __init__(self):
pass
def send(self):
return "success"
class TextMsg(Msg):
def __init__(self, toUserName, fromUserName, content):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['Content'] = content
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{Content}]]></Content>
</xml>
"""
return XmlForm.format(**self.__dict)
class ImageMsg(Msg):
def __init__(self, toUserName, fromUserName, mediaId):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['MediaId'] = mediaId
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[{MediaId}]]></MediaId>
</Image>
</xml>
"""
return XmlForm.format(**self.__dict)
代码编写完成之后,将所有代码移到服务器端,安装项目所需要的python依赖包,最后利用python query_server.py 80
开启服务。开启成功之后,便能够利用微信公众号进行问答。
3.微信问答Demo
通过上面的配置,已经能够通过微信公众号处理电影,书籍方面的问题,下面来看看书籍-电影知识图谱问答系统(BM-KGQA)的最终效果,下图为BM-KGQA能够处理的问句类型。
针对电影类信息,可提问其主演、导演、编剧、海报、上映地区、上映时间、时长、其他名字、简介、详细信息、评分、评分人数等内容。比如提问“流浪地球的主演是谁?”、“流浪地球的上映时间是什么时候?”、“流浪地球的评分是多少?”、“流浪地球的详细信息是什么”等。需要注意的是,针对问句“流浪地球的评分是多少?”,因“流浪地球”既有书籍也有电影,所以返回两种答案,并对其进行标注区分。
针对电影人物类信息,可提问其照片、性别、星座、生日、出生地、职业、其他名字、详细信息、介绍等内容。比如提问“吴京的生日是什么时候?”、“吴京的出生地是在哪儿?”、“吴京的职业是什么?”、“吴京的星座是什么?”、“吴京个人的详细信息和我说一下”、“吴京主演了哪些电影”、“吴京指导了哪些电影”等。
针对书籍类信息,可提问其图片、出版社、出版日期、页数、目录、简介、评分、评价人数、详细信息等内容。比如提问“《追风筝的人》的出版社是哪儿?”、“《追风筝的人》的出版日期是什么时候?”、“《追风筝的人》总共多少页?”、“《追风筝的人》的作者是谁呢”、“《追风筝的人》的详细信息?”。
针对书籍类人物信息,可提问其图片、性别、生日、出生地、其他名称、介绍、详细信息等内容。比如提问“杨绛的图片内容?”、“杨绛写作了哪些书?”、“杨绛的出生地是哪儿?”、“杨绛的其他名字叫做什么?”、“杨绛的详细信息和我说一下?”。
以上,便是书籍-电影知识图谱智能问答系统(BM-KGQA)的最终效果,能够通过微信公众号来精确回答用户关于书籍-电影方面的问题。当然,目前该知识图谱问答系统仅能够处理书籍,电影领域的问题,处理过程也需要依赖大量规则模版,功能还不是很完善。但通过此项目,能够给初学者提供一个解决问题的完整流程,即如何利用知识图谱来进行特定领域知识问答。后续,将继续进行完善,包括但不限于将特定领域内知识问答推广至百科类知识问答;构建端到端的问句理解模块,直接从中抽取出问句三元组;能够提供复杂问句理解和复杂答案推理功能。
欢迎关注公众号谓之小一阅读更多内容。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
请问作者这个问答响应时间在十几秒是正常的吗
不正常,答案检索时间不超过1s。
云服务器上只要安装Anaconda3吗?我用的是neo4j存储知识图谱,需要安装neo4j吗?希望大佬不要嫌弃本菜鸡的问题
需要安装neo4j的。
锤子!!!