NLP(20)--知识图谱+实体抽取

前言

仅记录学习过程,有问题欢迎讨论

基于LLM的垂直领域问答方案:

  • 特点:不是通用语料;准确度要求高,召回率可以低(转人工);拓展性和可控性(改变特定内容的回答);确切的评价指标

实现:
传统方法:

  • 知识库+文本匹配 (问题转向量找知识库的相似度最高的问题来回答)

基于LLM:

1.直接生成:

  • 直接使用LLM获答案,然后使用SFT微调
  • 缺点:fine-tune的成本高;模型的泛用性下降;不易调整数据

2.RAG思路(推荐):

  • 段落召回+阅读理解(基于获取的信息给模型提示,期望获取一个更好的答案)
  • 召回的就是你垂直领域的内容,去给llm提示
  • 缺点:对LLM要求高,受召回算法限制(如果正确答案被丢弃,LLM无法挽回);生成结果不可控

3.基于知识体系(图谱)

  • 树形结构设置知识体系结构,然后给LLM去匹配最可能的知识点选择项,一级一级往下走
  • 缺点:需要大量标注数据,标注数据需要人工标注,标注成本高

知识图谱:

  • 知识图谱是图数据库的一种,用于存储和表示知识。
  • 如:姚明-身高-226cm(三元组)
  • 知识图谱的构建取决于你想要完成的任务,如果想要获取实体之间的关系,就要构建实体关系的图谱
以下为知识图谱的具体内容:

实体抽取:ner任务获取实体属性
关系抽取:

  • 限定领域:
    • 文本+实体送入模型 预测关系(本质还是分类任务)
    • 可以同时训练实体抽取和关系抽取,loss为二者相加
  • 开放领域:
    基于序列标注 (NER)

属性抽取:同关系抽取

知识融合:

  • 实体对齐:通过判断不同来源的属性的相似度
  • 实体消歧:根据上下文和语义关系进行实体消歧
  • 属性对齐:属性和属性值的相似度计算

知识推理:通过模型来推断两个实体的关系
知识表示:实体关系属性都转化为向量,都可以用id表示某个信息

图数据库的使用:

noe4j
使用NL2SQL把输入的文字变为sql查询

  • 方法1:基于模版+文本匹配,输入的文本去匹配对应的问题模版–再去匹配SQL(依赖模版,易于拓展,可以写复杂sql)
  • 方法2:semantic parsing(语义解析)–通过训练多个模型来获取sql(不易于拓展)
  • 方法3:用LLM写sql

代码展示

构建基于neo4j的知识图谱问答:
这里采用的是方法1,依赖问题模版。

import re
import json
import pandas
import itertools
from py2neo import Graph

"""
使用neo4j 构建基于知识图谱的问答
需要自定义问题模板
"""


class GraphQA:
    def __init__(self):
        # 启动neo4j neo4j console
        self.graph = Graph("http://localhost:7474", auth=("neo4j", "password"))
        schema_path = "kg_schema.json"
        templet_path = "question_templet.xlsx"
        self.load(schema_path, templet_path)
        print("知识图谱问答系统加载完毕!\n===============")

    # 对外提供问答接口
    def query(self, sentence):
        print("============")
        print(sentence)
        # 对输入的句子找和模板中最匹配的问题
        info = self.parse_sentence(sentence)  # 信息抽取
        print("info:", info)
        # 匹配模板
        templet_cypher_score = self.cypher_match(sentence, info)  # cypher匹配
        for templet, cypher, score, answer in templet_cypher_score:
            graph_search_result = self.graph.run(cypher).data()
            # 最高分命中的模板不一定在图上能找到答案, 当不能找到答案时,运行下一个搜索语句, 找到答案时停止查找后面的模板
            if graph_search_result:
                answer = self.parse_result(graph_search_result, answer)
                return answer
        return None

    # 加载模板
    def load(self, schema_path, templet_path):
        self.load_kg_schema(schema_path)
        self.load_question_templet(templet_path)
        return

    # 加载模板信息
    def load_question_templet(self, templet_path):
        dataframe = pandas.read_excel(templet_path)
        self.question_templet = []
        for index in range(len(dataframe)):
            question = dataframe["question"][index]
            cypher = dataframe["cypher"][index]
            cypher_check = dataframe["check"][index]
            answer = dataframe["answer"][index]
            self.question_templet.append([question, cypher, json.loads(cypher_check), answer])
        return

    # 返回输入中的实体,关系,属性
    def parse_sentence(self, sentence):
        # 先提取实体,关系,属性
        entitys = self.get_mention_entitys(sentence)
        relations = self.get_mention_relations(sentence)
        labels = self.get_mention_labels(sentence)
        attributes = self.get_mention_attributes(sentence)
        # 然后根据模板进行匹配
        return {
   "%ENT%": entitys,
                "%REL%": relations,
                "%LAB%": labels,
                "%ATT%": attributes}

    # 获取问题中谈到的实体,可以使用基于词表的方式,也可以使用NER模型
    def get_mention_entitys(self, sentence):
        return re.findall("|".join(self.entity_set), sentence)

    # 获取问题中谈到的关系,也可以使用各种文本分类模型
    def get_mention_relations(self, sentence):
        return re.findall("|".join(self.relation_set), sentence)

    # 获取问题中谈到的属性
    def get_mention_attributes(self, sentence):
        return re.findall("|".join(self.attribute_set), sentence)

    # 获取问题中谈到的标签
    def get_mention_labels(self, sentence):
        return re.findall("|".join(self.label_set), sentence)

    # 加载图谱信息
    def load_kg_schema(self, path):
        with open(path, encoding="utf8") as f:
            schema = json.load(f)
        self.relation_set = set(schema["relations"])
        self.entity_set = set(schema["entitys"])
        self.label_set = set(schema["labels"])
        self.attribute_set = set(schema["attributes"])
        return

    # 匹配模板的问题
    def cypher_match(self, sentence, info):
        # 根据提取到的实体,关系等信息,将模板展开成待匹配的问题文本
        templet_cypher_pair = self.expand_question_and_cypher(info)
        result = []
        for templet, cypher, answer in templet_cypher_pair:
            # 求相似度 距离函数
            score = self.sentence_similarity_function(sentence, templet)
            # print(sentence, templet, score)
            result.append([templet, cypher, score, answer])
        # 取最相似的
        result = sorted(result, reverse=True, key=lambda x: x[2])
        return result

    # 根据提取到的实体,关系等信息,将模板展开成待匹配的问题文本
    def expand_question_and_cypher(self, info):
        templet_cypher_pair = []
        # 模板的数据
        for templet, cypher, cypher_check, answer in self.question_templet:
            # 匹配模板
            cypher_check_result = self.match_cypher_check(cypher_check, info)
            if cypher_check_result:
                templet_cypher_pair += self.expand_templet(templet, cypher, cypher_check, info, answer)
        return templet_cypher_pair

    # 校验 减少比较次数
    def match_cypher_check(self, cypher_check, info):
        for key, required_count in cypher_check.items():
            if len(info.get(key, [])) < required_count:
                return False
        return True

    # 对于单条模板,根据抽取到的实体属性信息扩展,形成一个列表
    # info:{"%ENT%":["周杰伦", "方文山"], “%REL%”:[“作曲”]}
    def expand_templet(self, templet, cypher, cypher_check, info, answer):
        # 获取所有组合
        combinations = self.get_combinations(cypher_check, info)
        templet_cpyher_pair = []
        for combination in combinations:
            # 替换模板中的实体,属性,关系
            replaced_templet = self.replace_token_in_string(templet, combination)
            replaced_cypher = self.replace_token_in_string(cypher, combination)
            replaced_answer = self.replace_token_in_string(answer, combination)
            templet_cpyher_pair.append([replaced_templet, replaced_cypher, replaced_answer])
        return templet_cpyher_pair

    # 对于找到了超过模板中需求的实体数量的情况,需要进行排列组合
    # info:{"%ENT%":["周杰伦", "方文山"], “%REL%”:[“作曲”]}
    def get_combinations(self, cypher_check, info):
        slot_values = []
        for key, required_count in cypher_check.items():
            # 生成所有组合
            slot_values.append(itertools.combinations(info[key], required_count))
        value_combinations = itertools.product(*slot_values)
        combinations = []
        for value_combination in value_combinations:
            combinations.append(self.decode_value_combination(value_combination, cypher_check))
        return combinations

    # 将提取到的值分配到键上
    def decode_value_combination(self, value_combination, cypher_check):
        res = {
   }
        for index, (key, required_count) in enumerate(cypher_check.items
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值