基於英雄聯盟的知識圖譜問答系統

2022-08-03 12:00:41

介紹

程式碼地址: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。需要注意的是我們必須先在資料庫裡面有節點,才能去進一步建立關係。最終我們建立的知識圖譜如下所示:

問答系統的構建

問答系統主要由以下幾個部分構成:

  • 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就是主執行程式了。

結果展示

諮詢:蓋倫的種族
客服機器人: 蓋倫的種族是:人類
諮詢:蓋倫的角色
客服機器人: 蓋倫的角色是:戰士
諮詢:蓋倫的介紹
客服機器人: 蓋倫的介紹是:身為一位自豪而且高貴的士兵,蓋倫奮戰在無畏先鋒的最前沿。他深受戰友們的愛戴,也受到敵人們的尊敬——同樣重要地,他還是冕衛家族的名門之後,肩負著守衛德瑪西亞及其理念的重任。他身披抵禦魔法的重甲,手持闊劍,時刻準備著用正義的鋼鐵風暴在戰場上正面迎戰一切操縱魔法的狂人。
諮詢:蓋倫的別稱
客服機器人: 蓋倫的別稱是:德瑪西亞之力
諮詢:孫悟空的徒弟是誰
客服機器人: 孫悟空的徒弟是:易
諮詢:德瑪西亞區域有哪些英雄
客服機器人: 德瑪西亞包含的英雄有:薇恩;嘉文四世;塞拉斯;加里奧;蓋倫;菲奧娜;奎因;娑娜;凱爾;趙信;波比;希瓦娜;拉克絲;莫甘娜
諮詢:德瑪西亞有哪些風景
客服機器人: 德瑪西亞的景色有:光明使者神殿;德瑪西亞城的宏偉廣場;英勇之廳;密銀城;黎明城堡
諮詢:具有徒弟關係的有哪些
客服機器人: 具有徒弟關係的有:孫悟空|易;布蘭德|瑞茲;塔莉埡|亞索
諮詢:德瑪西亞的介紹
客服機器人: 德瑪西亞的介紹是:德瑪西亞是一個法理至上的強大王國,戰功赫赫,久負盛名。德瑪西亞人自古崇尚正義、榮耀和責任,近乎狂熱地以自身的傳統及底蘊為豪。然而,儘管秉持著這些高尚的原則,在過去的幾百年間,剛愎自用的德瑪西亞越發與世隔絕,成為了孤立主義的代名詞。然而現在,王國中已經出現了變數。德瑪西亞雄都以禁魔石——一種可以抑制魔法能量的白色岩石——為基,起初是符文戰爭之後為了躲避魔法侵害的人們所建立的庇護地。王權由中心向外輻射,守護著邊遠的城鎮、農田、森林和礦產豐饒的山脈。然而,自從嘉文三世國王突然駕崩,各大家族至今仍未贊同他唯一的繼承人嘉文王子繼位。在王國眼中,重兵把守的邊境之外已經是異心遍起,許多原先的附庸在亂世來臨之際開始尋求來自別處的庇護。有人私下妄言,德瑪西亞的黃金時代已經一去不返,除非臣民能夠上下一心,順應時代的變化——許多人認為他們並沒有這樣的能力,否則王國的衰敗在所難免。再多的禁魔石,也無法阻止德瑪西亞由內而外的覆滅。

至此,我們的知識圖譜構建和問答系統就全部實現了。通過整個流程,我們就可以去構建自己的知識圖譜和問答系統了。