美文网首页
构建知识图谱问题系列-3

构建知识图谱问题系列-3

作者: jiarf | 来源:发表于2021-01-04 13:47 被阅读0次

教程依旧是:300行python代码从零开始构建基于知识图谱的电影问答系统3-实验环境和实验数据准备_山不过来,我就过去-CSDN博客

实验数据

可以自己去爬,

image.png
[图片上传中...(image.png-59d978-1607049412302-0)]
image.png
这里的pid指的是personid,mid指的是movieid
image.png
数据地址:链接:https://pan.baidu.com/s/1HgjZFQ7q4V_8EzzjNmMwwQ
提取码:7qv1
//导入节点 电影类型  == 注意类型转换
LOAD CSV WITH HEADERS  FROM "file:///genre.csv" AS line
MERGE (p:Genre{gid:toInteger(line.gid),name:line.gname})
    

//导入节点 演员信息 
LOAD CSV WITH HEADERS FROM 'file:///person.csv' AS line
MERGE (p:Person { pid:toInteger(line.pid),birth:line.birth,
death:line.death,name:line.name,
biography:line.biography,
birthplace:line.birthplace})


// 导入节点 电影信息
LOAD CSV WITH HEADERS  FROM "file:///movie.csv" AS line  
MERGE (p:Movie{mid:toInteger(line.mid),title:line.title,introduction:line.introduction,
rating:toFloat(line.rating),releasedate:line.releasedate})


// 导入关系 actedin  电影是谁参演的 1对多
LOAD CSV WITH HEADERS FROM "file:///person_to_movie.csv" AS line 
match (from:Person{pid:toInteger(line.pid)}),(to:Movie{mid:toInteger(line.mid)})  
merge (from)-[r:actedin{pid:toInteger(line.pid),mid:toInteger(line.mid)}]->(to)
    
//导入关系  电影是什么类型 == 1对多
LOAD CSV WITH HEADERS FROM "file:///movie_to_genre.csv" AS line
match (from:Movie{mid:toInteger(line.mid)}),(to:Genre{gid:toInteger(line.gid)})  
merge (from)-[r:is{mid:toInteger(line.mid),gid:toInteger(line.gid)}]->(to)

首先一个一个的导入:
1.导入节点 电影类型成功


image.png

2.导入节点 演员信息成功


image.png
3,导入节点 电影信息成功
image.png
4,导入关系 actedin 电影是谁参演的 1对多
image.png

5.导入关系 电影是什么类型 == 1对多

image.png
均导入成功
上一篇中提到了问题分类的训练集怎么构造的,也提到了问题模板,github上也有相关数据,可以直接下载,其中A是问题分类的训练数据,B是问题模板。
image.png image.png
image.png

对用户问题的分类

教程:300行python代码从零开始构建基于知识图谱的电影问答系统4-用户问题预处理_山不过来,我就过去-CSDN博客

这篇主要介绍对用户问题的处理,也就是从获取用户问题到明白用户意图这个过程,主要涉及到命名实体识别(这个任务简单,我就用词性标注来代替了),问题分类,以及填充问题模板这几个部分。介绍的时候,可能会用一些代码来说明,但是下面列出来的代码并不完整,完整的代码请参照github。这些代码只是辅助理解整个过程,这样去看代码的时候才容易理清函数之间的来龙去脉。再说明下,这个系统适合那些刚入门没多久,通过实现一个小东西来练练手的同学,而这篇教程,可以看作是代码的说明书,帮助理解代码,主要还是梳理逻辑。

关键信息抽取

这部分就是通过命名实体识别来获取用户问题中的人名,电影名等信息,而在实验过程中,在这一部分我们其实用的词性标注,因为我发现,词性标注工具可以把人名标注出来,如下使用jieba进行词性标注时的含义:

nr 人名 名词代码 n和“人(ren)”的声母并在一起。

所以,只要我们把问题词性标注后,找到对应的nr对应的单词,那就是人名啦,电影名称的识别和这个差不多。这就是这部分的主要思路,接下来就看下代码是怎么实现的。

用户问题词性标注

功能的实现就是下面这个函数:

 def question_posseg(self):
        jieba.load_userdict("./data/userdict3.txt")
        clean_question = re.sub("[\s+\.\!\/_,$%^*(+\"\')]+|[+——()?【】“”!,。?、~@#¥%……&*()]+","",self.raw_question)
        self.clean_question=clean_question
        question_seged=jieba.posseg.cut(str(clean_question))
        result=[]
        question_word, question_flag = [], []
        for w in question_seged:
            temp_word=f"{w.word}/{w.flag}"
            result.append(temp_word)
            # 预处理问题
            word, flag = w.word,w.flag
            question_word.append(str(word).strip())
            question_flag.append(str(flag).strip())
        assert len(question_flag) == len(question_word)
        self.question_word = question_word
        self.question_flag = question_flag
        print(result)
        return result

jieba的词性标注和分词是同步进行的,所以如果分词不准确的话,那么词性标注往往也会出错,比如说电影《卧虎藏龙》,被jieba分词分为:

卧虎 藏龙

这样肯定就不能识别出卧虎藏龙这个实体了啊,人名也是同样的道理,如果把一个演员的姓名分开了,也就不能标注出这样一个人名了,于是我就加了一个自定义字典,把所涉及到的所有电影,所有人名都加到了这个字典中,这样以来,jieba分词的时候就会参考字典里面的词来进行分词和词性标注了,自定义词典的格式如下:

太极气功 15 nm
功夫小子 15 nm
大师 15 nm
...
陈雅伦 15 nr
李修贤 15 nr
黄锦燊 15 nr
潘恒生 15 nr
林熙蕾 15 nr
锺丽缇 15 nr
...
恐怖 15 ng
动作 15 ng
喜剧 15 ng
历史 15 ng

这样涉及到的电影名,人名以及电影类型就不会被jieba分词分开了,此外还需要去掉问题中的特殊字符,这些操作搞完了后就可以进行分词和词性标注了,处理后把结果返回即可。

问题分类和模板填充

接下来是对用户的问题进行分类,获取对应问题模板,从而明白用户的意图,首先要使用前面介绍的根据用户习惯构造的各种各样的问题来训练一个分类器,我这里使用的sklearn里面的贝叶斯分类器。
首先是组织训练数据:

# 获取训练数据
    def read_train_data(self):
        train_x=[]
        train_y=[]
        file_list=getfilelist("./data/question/")
        # 遍历所有文件
        for one_file in file_list:
            # 获取文件名中的数字
            num = re.sub(r'\D', "", one_file)
            # 如果该文件名有数字,则读取该文件
            if str(num).strip()!="":
                # 设置当前文件下的数据标签
                label_num=int(num)
                # 读取文件内容
                with(open(one_file,"r",encoding="utf-8")) as fr:
                    data_list=fr.readlines()
                    for one_line in data_list:
                        word_list=list(jieba.cut(str(one_line).strip()))
                        # 将这一行加入结果集
                        train_x.append(" ".join(word_list))
                        train_y.append(label_num)
        return train_x,train_y

接着是训练多分类贝叶斯分类器模型,并返回:

# 训练并测试模型-NB
    def train_model_NB(self):
        X_train, y_train = self.train_x, self.train_y
        self.tv = TfidfVectorizer()

        train_data = self.tv.fit_transform(X_train).toarray()
        clf = MultinomialNB(alpha=0.01)
        clf.fit(train_data, y_train)
        return clf

利用训练好的模型来对新问题进行分类:

# 预测
    def predict(self,question):
        question=[" ".join(list(jieba.cut(question)))]
        test_data=self.tv.transform(question).toarray()
        y_predict = self.model.predict(test_data)[0]
        # print("question type:",y_predict)
        return y_predict

返回用户问题所属的类别编号,这个编号也就对应一个问题模板:

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 出生日期

数字和模板的对应关系可以提前存到一个字典中,当预测出编号后,直接通过这个编号作为字典的key值,这样就查询出问题模板了。比如预测的结果是2,则对应的问题模板 n m 类 型 nm 类型nm类型,表示询问某部电影的类型,再结合前一阶段获取的电影名字,则可以组成一个新的问题,比如:

卧虎藏龙 类型

上面对涉及到的重点部分进行了介绍,这里小结一下,当遇到一个新问题时:

1、得到原始问题;
2、抽取问题中的关键信息,比如从问题“张学友的个人介绍”中抽取出张学友这个人名;
3、使用分类模型来预测问题类别,预测出模板编号,查询字典得到问题模板;如dict[5]:nnt 介绍;
4、替换模板中的抽象内容,得到:张学友 介绍

问题答案的获取

教程:300行python代码从零开始构建基于知识图谱的电影问答系统5-答案获取_山不过来,我就过去-CSDN博客

在上一篇中,主要介绍了如何从接收到的用户问题中抽取关键信息,以及如何识别用户的意图,那么接下来就将介绍在得到了这些信息后,如何在知识图谱中查询答案。我在处理这个问题时,想得很直接,简单来说,每个问题模板就对应了一个用户意图,那么就按照每个意图来写查询语句,这是一种简单粗暴的方法,优点就是就只有简单了,缺点当然很多了,比如不利于维护以及扩展等。现在要有维护扩展的意识,但是具体实现是,如果想快速实现一个demo的话,可以使用这种简单粗暴方法。下面就对获取答案进行介绍。

首先,我定义了一个问题模板的方法字典,每一个key对应模板的编号,value就是根据该模板来查询答案的方法,结构如下:

self.q_template_dict={
            0:self.get_movie_rating,
            1:self.get_movie_releasedate,
            2:self.get_movie_type,
            3:self.get_movie_introduction,
            4:self.get_movie_actor_list,
            5:self.get_actor_info,
            6:self.get_actor_act_type_movie,
            7:self.get_actor_act_movie_list,
            8:self.get_movie_rating_bigger,
            9:self.get_movie_rating_smaller,
            10:self.get_actor_movie_type,
            11:self.get_cooperation_movie_list,
            12:self.get_actor_movie_num,
            13:self.get_actor_birthday
        }

当我们得到用户问题,以及该问题的模板后需要做那些工作呢?
首先时利用上一篇介绍的方法来获取问题的关键信息,然后就根据模板编号来调用相应的方法,整体框架的代码如下:

 def get_question_answer(self,question,template):
        # 如果问题模板的格式不正确则结束
        assert len(str(template).strip().split("\t"))==2
        template_id,template_str=int(str(template).strip().split("\t")[0]),str(template).strip().split("\t")[1]
        self.template_id=template_id
        self.template_str2list=str(template_str).split()

        # 预处理问题
        question_word,question_flag=[],[]
        for one in question:
            word, flag = one.split("/")
            question_word.append(str(word).strip())
            question_flag.append(str(flag).strip())
        assert len(question_flag)==len(question_word)
        self.question_word=question_word
        self.question_flag=question_flag
        self.raw_question=question
        # 根据问题模板来做对应的处理,获取答案
        answer=self.q_template_dict[template_id]()
        return answer

进入到对应的方法中,利用Cypher语言来构建查询语句,其基本形式是:

match(n)-[r] -(b)

Cypher语言不太懂没关系,也可以直接使用python的库py2neo来操作图数据库neo4j。这里只涉及到查询操作,所以我直接构造了Cypher查询语句,然后使用py2neo库的run方法来查询,数据库的链接和查询我单独的写了一个类,在需要的时候调用即可:

from py2neo import Graph,Node,Relationship,NodeMatcher

class Query():
    def __init__(self):
        self.graph=Graph("http://localhost:7474", username="neo4j",password="123456")

    # 问题类型0,查询电影得分
    def run(self,cql):
        # find_rela  = test_graph.run("match (n:Person{name:'张学友'})-[actedin]-(m:Movie) return m.title")
        result=[]
        find_rela = self.graph.run(cql)
        for i in find_rela:
            result.append(i.items()[0][1])
        return result

比如这里以查询某部电影的演员列表为例,查询答案的代码为:

# 4:nm 演员列表
    def get_movie_actor_list(self):
        movie_name=self.get_movie_name()
        cql = f"match(n:Person)-[r:actedin]->(m:Movie) where m.title='{movie_name}' return n.name"
        print(cql)
        answer = self.graph.run(cql)
        answer_set = set(answer)
        answer_list = list(answer_set)
        answer = "、".join(answer_list)
        final_answer = movie_name + "由" + str(answer) + "等演员主演!"
        return final_answer

主要是构造Cypher语句,然后调用py2neo的run方法,对得到的答案进行处理这几步,得到答案返回即可。

相关文章

  • 构建知识图谱问题系列-3

    教程依旧是:300行python代码从零开始构建基于知识图谱的电影问答系统3-实验环境和实验数据准备_山不过来,我...

  • 构建知识图谱问题系列-4

    本地安装尝试因为那个服务器老有问题登不上neo4j的浏览器界面,所以换成本地试试neo4j本地安装b战的视频很好了...

  • 最全知识图谱介绍:关键技术、开放数据集、应用案例汇总

    1 知识图谱构建技术 本节首先给出知识图谱的技术地图,然后介绍知识图谱构建的关键技术,包括关系抽取技术、知识融合技...

  • 2020-01-09

    自动构建知识图谱 一、知识图谱相关概念 Google公司于2012年提出知识图谱(Knowledge Graph)...

  • 一文了解知识图谱常见的知识表示,图谱存储和查询方法

    对于知识图谱构建,有两个问题是永远绕不掉的。一个是知识图谱的知识表示,一个是知识图谱的存储方式。这两个问题大家也比...

  • 构建红楼梦人物关系知识图谱

    知识图谱构建步骤:数据获取、知识抽取、关系提取、图数据库构建和语义搜索(本质就是把检索的问题转换为查询语句) 知识...

  • 知识图谱的推理

    1.大规模知识图谱的构建、推理及应用 2.文因互联:从「金融数据」到「金融知识」 3.基于知识图谱的厨房领域问答系...

  • 知识图谱入门简介

    一、 知识图谱技术体系 知识图谱的构建主要涉及到知识建模、知识抽取、知识融合、知识存储、知识计算以及知识应用,具体...

  • 从具体案例了解知识图谱构建流程

    经济责任审计知识图谱构建流程总结。     自2012年Google提出“知识图谱”的概念以来,知识图谱就一直是学...

  • 2020-02-03

    语义计算、知识图谱与智能问答(实例) 本文,以医药知识图谱“神机医药”APP为例,简述如何通过“语义计算”构建知识...

网友评论

      本文标题:构建知识图谱问题系列-3

      本文链接:https://www.haomeiwen.com/subject/sevbwktx.html