介绍

代码地址:https://github.com/taishan1994/lol_knowledge_graph_qa

该文介绍了英雄联盟知识图谱的构建以及搭建一个简单的基于知识图谱的英雄联盟问答系统。需要提前安装好以下依赖:

py2neo版本:py2neo-2021.2.3
neo4j版本:neo4j-4.4.5

数据来源于:http://www.openkg.cn/dataset/lol ,其里面获得的数据是英雄联盟宇宙网址:https://yz.lol.qq.com/zh_CN/ 。这里我们简单的去看一下一个地址:https://yz.lol.qq.com/v1/zh_cn/faction-browse/index.json ,请求后我们会返回以下结果:

{
  "id": "faction-browse",
  "name": "Faction Browse",
  "locale": "zh_cn",
  "subheadline": "Take a look at the different worlds in Runeterra",
  "factions": [
    {
      "type": "faction",
      "name": "以绪塔尔",
      "slug": "ixtal",
      "image": {
        "title": "ixtal-splash",
        "subtitle": "",
        "description": "",
        "uri": "https://game.gtimg.cn/images/lol/universe/v1/assets/blt9a01601bbdff5c4a-ixtal_splash.jpg",
        "encoding": "image/jpeg",
        "width": null,
        "height": null,
        "x": null,
        "y": null,
        "featured-champions": []
      },
      "echelon": 0,
      "associated-champions": null
    },
    ......
}

我们通过解析后就可以获得想要的数据了,具体可以参考spider.py里面的(程序实际没使用该文件,直接使用的原始数据)。我们的图谱数据保存在data/mid_data下面。具体先看一个数据:hero_info..csv

0,亚托克斯,暗裔剑魔,符文之地,战士,暗裔,2013/6/13,"亚托克斯曾是恕瑞玛抗击虚空时的伟大战士。但是,他和他的同胞却有可能变成符文之地更大的威胁。最终,他们败给了凡人的狡诈巫术,自身的精魂被锁在了武器之内。数百年的监禁之后,亚托克斯头一个挣脱出来,腐蚀并转化那些胆敢染指的蠢人。现在,他将夺来的血肉模仿着自己曾经的形象粗暴地重塑,渴望着迟来许久的末世复仇。
"
1,阿狸,九尾妖狐,艾欧尼亚,法师,瓦斯塔亚,2011/12/14,天生就与精神领域的魔法存在连接的阿狸,是一名狐狸模样的瓦斯塔亚,在世界上寻找着自己所属的位置。进入凡人社会以后,她成为了一名充满悔意和同情心的掠食者,她喜欢操纵猎物的情绪,然后再吸食他们的生命精魄——每吞噬一个灵魂,都伴着他们生前的记忆片段与领悟洞见。
2,阿卡丽,离群之刺,艾欧尼亚,刺客,人类,2010/5/11,"无论是均衡教派还是暗影之拳的称号,都已被阿卡丽抛弃,如今的阿卡丽独来独往,随时可以成为她的人民所需要的夺命武器。虽然她牢牢铭记着她从宗师慎身上学来的一切,但她效忠保护艾欧尼亚并铲除敌人,每次一条命。或许阿卡丽的出击悄然无声,但她传达的信息将响亮无比:不听命于任何人的刺客最为可怕。
"

存储了和英雄相关的一些信息,接下来就是我们具体的实现了。

代码解读

构建知识图谱

构建知识图谱代码在build_lol_graph.py里面,主要是三个步骤:连接neo4j数据库,创建节点,创建关系,我们一一来看:

初始化的时候:我们连接到neo4j数据库,auth里面设置用户名和密码,我们可以选择是否需要先清空数据库。

self.g = Graph("http://localhost:7474", auth=("gob", "gob"))
self.g.delete_all()  # 先清空数据库,按需执行

在get_data()中我们主要是获取到数据,对于一个属性的东西,我们用列表存储,如果是关系,我们则存储为[(实体1,实体2)]这种形式。

create_xxx_node()是创建图节点的主要函数,我们可以将一个节点,该节点包含一些列的属性,比如:

def create_hero_node(self, data):
        count = 0
        total = len(data["hero_names"])
        for i in range(total):
            node = Node("hero",
                        hero_name=data["hero_names"][i],
                        hero_title=data["hero_titles"][i],
                        hero_race=data["hero_races"][i],
                        hero_role=data["hero_roles"][i],
                        hero_release_date=data["hero_release_dates"][i],
                        hero_info=data["hero_infos"][i],
                        )
            self.g.create(node)
            count += 1
            print(count)
        return

我们创建了英雄hero这个节点,然后这个hero还有属性英雄名、英雄别称、英雄种族等属性。

create_relationship()是创建关系的主要函数,我们在create_graphrels()里面使用它来创建不同的关系,比如:

self.create_relationship(
            "hero",
            "city",
            "hero_name",
            "city_name",
            data["hero_belong_city"],
            "hero_belong_city",
            "英雄所属区域"
        )

我们为hero和city创建hero_belong_city关系,关系名是英雄所属区域,关联的属性是hero_name和city_name。需要注意的是我们必须先在数据库里面有节点,才能去进一步创建关系。最终我们创建的知识图谱如下所示:
基于英雄联盟的知识图谱问答系统-LMLPHP

问答系统的构建

问答系统主要由以下几个部分构成:

  • 1、question_classification.py:给定问题,识别里面的实体以及问题的类型。
  • 2、question_parser.py:根据问题类型生成neo4j的sql语句。
  • 3、answer_search.py:执行sql并构建返回的结果。
  • 4、chatbot_graph.py:程序的主入口。

我们一一来看,在question_classification.py中,我们加载好相关的实体,并构建了一个字典树,接着我们定义了一些问句词语:

self.hero_race_qwds = ["种族"]
self.hero_role_qwds = ["角色"]
self.hero_title_qwds = ["别称", "别名", "称号"]
self.hero_info_qwds = ["基本信息", '简介', '介绍', '信息']
self.city_qwds = ['区域', '城市']
self.env_qwds = ['风景', '景色', '建筑']
self.rel_qwds = entity_dicts['rels']

通过解析问题,我们得到问题对应的类型以及问句中的实体,比如:

input an question:
question = "德玛西亚有什么景色吗"
{'args': {'德玛西亚': ['city_names']}, 'question_types': ['city_has_env']}

我们得到city_names类型以及问题的类型是查询区域的景色。

接着在question_parser.py中我们根据问题的类型来编写查询的neo4j-sql语句,比如上述得到问题类型后,我们可以得到以下语句:

sql = ["MATCH (m:environment)-[r:env_belong_city]->(n:city) where n.city_name = '{0}' return m.env_name, n.city_name".format(i) for i in entities]

该语句的意思是我们查询city_name="德玛西亚"的所有的景色。

然后在answer_search.py中,我们执行该sql语句并最后包装好结果:

 elif question_type == 'city_has_env':
    desc = [i['m.env_name'] for i in answers]
    subject = answers[0]['n.city_name']
    final_answer = '{0}的景色有:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit]))

最后在chatbot_graph.py就是主运行程序了。

结果展示

咨询:盖伦的种族
客服机器人: 盖伦的种族是:人类
咨询:盖伦的角色
客服机器人: 盖伦的角色是:战士
咨询:盖伦的介绍
客服机器人: 盖伦的介绍是:身为一位自豪而且高贵的士兵,盖伦奋战在无畏先锋的最前沿。他深受战友们的爱戴,也受到敌人们的尊敬——同样重要地,他还是冕卫家族的名门之后,肩负着守卫德玛西亚及其理念的重任。他身披抵御魔法的重甲,手持阔剑,时刻准备着用正义的钢铁风暴在战场上正面迎战一切操纵魔法的狂人。
咨询:盖伦的别称
客服机器人: 盖伦的别称是:德玛西亚之力
咨询:孙悟空的徒弟是谁
客服机器人: 孙悟空的徒弟是:易
咨询:德玛西亚区域有哪些英雄
客服机器人: 德玛西亚包含的英雄有:薇恩;嘉文四世;塞拉斯;加里奥;盖伦;菲奥娜;奎因;娑娜;凯尔;赵信;波比;希瓦娜;拉克丝;莫甘娜
咨询:德玛西亚有哪些风景
客服机器人: 德玛西亚的景色有:光明使者神殿;德玛西亚城的宏伟广场;英勇之厅;密银城;黎明城堡
咨询:具有徒弟关系的有哪些
客服机器人: 具有徒弟关系的有:孙悟空|易;布兰德|瑞兹;塔莉垭|亚索
咨询:德玛西亚的介绍
客服机器人: 德玛西亚的介绍是:德玛西亚是一个法理至上的强大王国,战功赫赫,久负盛名。德玛西亚人自古崇尚正义、荣耀和责任,近乎狂热地以自身的传统及底蕴为豪。然而,尽管秉持着这些高尚的原则,在过去的几百年间,刚愎自用的德玛西亚越发与世隔绝,成为了孤立主义的代名词。然而现在,王国中已经出现了变数。德玛西亚雄都以禁魔石——一种可以抑制魔法能量的白色岩石——为基,起初是符文战争之后为了躲避魔法侵害的人们所建立的庇护地。王权由中心向外辐射,守护着边远的城镇、农田、森林和矿产丰饶的山脉。然而,自从嘉文三世国王突然驾崩,各大家族至今仍未赞同他唯一的继承人嘉文王子继位。在王国眼中,重兵把守的边境之外已经是异心遍起,许多原先的附庸在乱世来临之际开始寻求来自别处的庇护。有人私下妄言,德玛西亚的黄金时代已经一去不返,除非臣民能够上下一心,顺应时代的变化——许多人认为他们并没有这样的能力,否则王国的衰败在所难免。再多的禁魔石,也无法阻止德玛西亚由内而外的覆灭。

至此,我们的知识图谱构建和问答系统就全部实现了。通过整个流程,我们就可以去构建自己的知识图谱和问答系统了。

08-03 17:39