#gStore-weekly | ​gAnswer源码分析:基于通用数据集的NE、RE服务开发

PART1

简 介

目前基于知识图谱的问答模式有两种,一种是基于信息检索的方式,一种是基于语义分析的方式。前者较之于后者,没有真正关心语义,主要是ranker算法,擅于处理简单问题,后者则是从语义的角度将用户的自然语言问题转化为逻辑形式,再在KG中执行查询。gAnswer 就是基于语义分析的方法,区别于传统的语义解析的方法,它是一种新颖的面向知识图谱的自然语言问答系统,以图数据驱动的视角回答RDF知识库上的自然语言问题。


 

采用基于语法分析的方法,大致分为两个阶段:其一为问题理解,即将问题转换为 SPARQL 类型的结构化查询;其二为查询评分,即对产生的结构化查询进行置信度评分。在问答系统中,重点是解决第一阶段中的歧义性问题,即解决:第一,短语链接问题,即如何将自然语言问句中的短语链接到正确的实体/类/关系/属性上;第二,复合问题,即一个自然语言问题可能转换为多个知识图谱三元组,而这多个三元组如何组合,才正确表达了问题的意图,并由此得到正确答案。


 

因此,为了解决第一阶段的两个问题,gAnswer基于图匹配的方法,建立一个与自然语言问句意图充分匹配的查询图Qs,这个查询图中可以存在具有歧义性的实体(以节点表示)或关系(以边表示)。当这个查询图被确定下来时,对应的结构化查询也被唯一确定。算法将解决歧义问题与查询评分这两个阶段融合在一起,即当得到自然语言问题的一个正确匹配的查询子图时,歧义问题也已经同时解决了。


 

图谱学苑社区weekly分别在:

  • 70期,介绍了句法依存树、句法依存分析工具以及它们在gAnswer中的使用;

  • 73期,介绍了NE即节点提取模块;

  • 76期,介绍了RE即关系提取模块;

  • 79期中,介绍了查询图生成。

这四期是gAnswer几个重要的基础模块。

大多数现有项目主要集中于提高模型在单一基准数据集上的效果,忽视了在现实场景(如多用户平台)中重新训练、重新部署和调整系统适应不同数据集所带来的高成本。因此,我们的团队在gAnswer中加入了ADMUS,一个渐进式知识库问答框架,旨在适应各种各样的数据集,包括多种语言、多样的基础知识库和不同的问答数据集。为了提高ADMUS的易用性,我们设计了一个渐进式框架,包括三个阶段,从执行精确查询、生成近似查询到检索大型语言模型引用的开放领域知识。我们的框架支持轻松集成新数据集,只需付出极小的努力和成本,通过添加NE和RE服务来支持新的数据集,创建一个与数据集相关的微服务。下面就解析在birdDB添加的NE和RE服务。

PART2

bird数据集

NE:

首先使用模型分词和词性标注,提取出四种类型节点Entity、Type、Literal、Variable(bird数据集只需要提取Entity和Type),并将结果存储在相应的数据结构中。源码通过调用Extract(question)函数,可以提取给定问题中的相关信息。

def Extract(question):
    word_list = model(question)
    word_list = word_list[0]
    print("parsing result: ", word_list)

    ret = {}
    ret["mergedQuestionList"] = [question]
    mWordList = []
    length = len(word_list)
    vis = np.zeros(length, dtype=bool)
    for sta in range(0, length):
        for l in range(length - sta, 0, -1):
            flg1 = False
            for j in range(sta, sta + l):
                if vis[j] == True:
                    flg1 = True
            if flg1 == True:
                break

            word = ""
            flg = False
            for j in range(sta, sta + l):
                word += word_list[j]

            mWord = Word(word)
            pred = check_predicate(word)
            if pred != None:
                continue
            emList = check_entity(word)
            if emList != None and len(emList) > 0:
                for x in emList:
                    addEntity(mWord, Entity_candidate(x[0], x[1], x[2]))
                flg = True
            if check_type(word) != None:
                addType(mWord, Type_candidate(word, -1, 100.0, -1))
                flg = True

            if flg == True:     # 如果有候选,就加入mWodList
                mWordList.append(mWord)
                for j in range(sta, sta + l):
                    vis[j] = True

主函数Extract(question):

  • 使用model即使用FastHan模型对传入的问题进行分词和词性标注,得到一个词语列表。

  • 创建一个结果返回字典ret,包含一个合并的问题列表和一个空的单词列表。

  • 遍历词语列表,对每个词语进行处理:

    构建一个表示词语的字典mWordmWord的结构如下:

    def Word(name):     
     mWord = {}     
     name = name.replace("_", " ")     
     mWord["name"] = name     
     mWord["mayCategory"] = False     
     mWord["mayEnt"] = False     
     mWord["mayType"] = False     
     mWord["mayLiteral"] = False     
     return mWord
    

    检查词语是否是谓词,如果是则跳过。

    检查词语是否是实体,如果是则将实体候选项添加到mWordemList中,并将mayEnt标记为True。

    检查词语是否是类型,如果是则将类型候选项添加到mWordtmList中,并将mayType标记为True。

    将被标记词语的mWord添加到单词列表mWordList中,后续不再重复访问。

  • 将单词列表mWordList添加到字典ret中。并将字典ret转换为JSON字符串并返回。

对于如何检验是否是实体或者类型,NE模块的方法是提取输入问题中的mentions,然后通过实体链接将其和特定数据集匹配,将其映射到知识图谱中的一组实体(类型)名称。这个方法的复杂性取决于知识库的规模和复杂性。诸如DBpedia等大型知识库,通常采用离线基于词典的链接器或利用第三方实体链接服务。在相对较小的知识库中,所有实体可以加载到内存缓存中,通过精确匹配、子串匹配或神经链接器方法来实现实体的链接。此处使用的是基于最长公共子序列(Longest Common Subsequence)匹配算法。

for i, s in enumerate(entities):         
 score = SequenceMatcher(None, s, word).ratio()         
 temp.append((s, -1, score * 100))     
temp.sort(key=lambda x: x[2], reverse=True)     
res = []     
for i, x in enumerate(temp):         
 if i == candidate_num or x[2] < sqMatcher_thrs:             
  break         
 res.append(x)

RE:

关系提取服务的目标是识别连接查询图中每对节点的谓词。给定查询图作为输入,与数据集相关的RE服务将为节点对生成与知识库中谓词对齐的关系以及相应的分数。

主函数RE_explicit用于解析文本数据中的语义关系。下面是对该函数的解析:

def RE_explicit(data):    
    posResult = data["posResult"]
    dependencyResult = data["dependencyResult"]
    SemanticUnitList = data["simplifiedSemanticUnitList"]
    print("posResult", posResult)
    
    nodes.clear()
    temp = ["_", "_", "_"]
    nodes.append(Node(temp))
    for i, u in enumerate(dependencyResult):
        nodes.append(Node(u))
        if u[2] == "root": root = i + 1
    for i, u in enumerate(nodes):
        if i == 0: continue
        u.token = posResult[u.position - 1]
        if u.fa_position != 0:
            u.fa = get_idx(nodes, u.fa_position)
            nodes[u.fa].vertices.append(i)
            u.vertices.append(u.fa)
    
    print("=====nodes=====")
    for i, u in enumerate(nodes):
        if i == 0: continue
        print("i: ", i, end="\t")
        print("token: ", u.token, end="\t")
        print("father: ", u.fa, end="\t")
        print("head label: ", u.head_label, end="\t")
        print("vertices: ", u.vertices)
    simplifiedSemanticRelations = {}
    simplifiedSemanticUnitList = []
    cnt = 2333
    for i, SemanticUnit in enumerate(SemanticUnitList):
        u = SemanticUnit["centerWordIndex"]
        simplifiedSemanticUnit = {}
        simplifiedSemanticUnit["centerWordIndex"] = u
        new_neighborUnitList = []      
        neighborUnitList = SemanticUnit["neighborUnitList"]
        for j, neighborUnit in enumerate(neighborUnitList):
            ret = calc_relations(u, neighborUnit)
            if ret != None:
                simplifiedSemanticRelations[str(cnt)] = ret
                cnt += 1
                new_neighborUnitList.append(neighborUnit)
                    
        simplifiedSemanticUnit["neighborUnitList"] = new_neighborUnitList
        simplifiedSemanticUnitList.append(simplifiedSemanticUnit)
    
    ret = {}
    ret["simplifiedSemanticUnitList"] = simplifiedSemanticUnitList
    ret["simplifiedSemanticRelations"] = simplifiedSemanticRelations
    solve_steadyEdge(ret)
    json_ret = json.dumps(ret)
    print("return result(json): ", json_ret)
    return json_ret
  • 遍历 dependencyResult,对于每个依存结果 u,创建一个节点对象,并将其添加到 nodes 列表中。如果该节点是根节点,则记录其索引。

  • 遍历所有节点,为每个节点设置词性标记 token,并处理节点间的连接关系。如果节点有父节点,则将节点与父节点之间建立连接。

  • 遍历简化的语义单元列表 SemanticUnitList,对每个语义单元进行处理。提取其中的中心词索引 u,以及邻居单元列表 neighborUnitList

  • 遍历邻居单元列表,对每个邻居单元计算语义关系,并将结果存储到 simplifiedSemanticRelations 中。同时更新计数器 cnt

  • 将新的邻居单元列表更新到当前语义单元中,并将当前语义单元添加到 simplifiedSemanticUnitList 中。

  • 构建返回结果 ret,将简化语义单元列表和语义关系添加到其中。调用 solve_steadyEdge 函数处理稳定边的情况。solve_steadyEdge 函数如下:

    def solve_steadyEdge(ret):
        vis = {}
        num_nodes = 0
        simplifiedSemanticUnitList = ret["simplifiedSemanticUnitList"]
        for semanticUnit in simplifiedSemanticUnitList:
            x = semanticUnit["centerWordIndex"]
            if x not in vis:
                num_nodes += 1
                vis[x] = 1
            xs = semanticUnit["neighborUnitList"]
            for x in xs:
                if x not in vis:
                    num_nodes += 1
                    vis[x] = 1
        num_edges = len(ret["simplifiedSemanticRelations"].keys())
        print("steadyEdge check:num_edges ", num_edges, "num_nodes:", num_nodes)
        if num_edges > num_nodes - 1:
            relations = ret["simplifiedSemanticRelations"]
            for key in relations:
                rel = relations[key]
                rel["isSteadyEdge"] = False
    

与NE服务类似,RE服务的选择取决于目标知识库的属性。对于结构简单、谓词数量较少的知识库,可以采用直接的策略。两个节点之间的谓词通过确定它们的语义结构(如依存树)中的共同祖先节点来确定。对于像DBpedia这样更复杂的知识库,仅依赖上述简单策略可能会导致较大的错误。

在经过NE、QGG和RE处理后,我们获得了一个粗粒度匹配的查询图的超集,其中包含必要的查询结构和节点。ADUMS将通过近似匹配和在目标知识库上搜索子图来处理这样的查询图,然后交互地生成预期的SPARQL查询。

gAnswer在DBpedia,bird,film三个数据集的demo演示可以公开访问:https://answer.gstore.cn/pc/index.html

[1] Sen Hu, Lei Zou, Jeffrey Xu Yu, Haixun Wang, DongyanZhao: “Answering Natural Language Questions by Subgraph Matching over KnowledgeGraphs”. IEEE Trans. Knowl. Data Eng (TKDE) 2018: 824-837

[2] ADMUS: A Progressive Question Answering Framework Adaptable to Multiple Knowledge Sources

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/178178.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数环通入选中国信通院《高质量数字化转型技术方案集(2023)》,积极推动企业数字化转型

近日&#xff0c;中国信息通信研究院“铸基计划”发布《高质量数字化转型技术方案集&#xff08;2023&#xff09;》&#xff0c;数环通《数字化协同管理解决方案》成功入选。 随着科技的快速发展和市场竞争的日益激烈&#xff0c;数字化转型已成为企业持续发展和提升竞争力的关…

JetLinks设备接入的认识与理解【woodwhales.cn】

为了更好的阅读体验&#xff0c;建议移步至笔者的博客阅读&#xff1a;JetLinks设备接入的认识与理解 1、认识 JetLinks 1.1、官网文档 官网&#xff1a;https://www.jetlinks.cn/ JetLinks 有两个产品&#xff1a;JetLinks-lot和JetLinks-view 官方文档&#xff1a; JetLi…

WPF树形控件TreeView使用介绍

WPF 中的 TreeView 控件用于显示层次结构数据。它是由可展开和可折叠的 TreeViewItem 节点组成的&#xff0c;这些节点可以无限嵌套以表示数据的层次。 TreeView 基本用法 例如实现下图的效果&#xff1a; xaml代码如下&#xff1a; <Window x:Class"TreeView01.Mai…

优秀智慧园区案例 - 上海世博文化公园智慧园区,先进智慧园区建设方案经验

一、项目背景 世博文化公园是上海的绿色新地标&#xff0c;是生态自然永续、文化融合创新、市民欢聚共享的大公园。作为世博地区的城市更新项目&#xff0c;世博文化公园的建设关乎上海城市风貌、上海文化展示、城市生态环境、市民游客体验、上海服务品牌等&#xff0c;被赋予…

防火墙部署模式 -- 镜像流量(旁路模式)

镜像流量&#xff08;旁路模式&#xff09; 如图&#xff0c;与单臂路由模式不同&#xff0c;旁路模式中&#xff0c;PC的流量不会流经防火墙&#xff0c;就算防火墙宕机也不会影他们之间的数据传输。 镜像的原理是交换机把被镜像端口的流量复制一份&#xff0c;发到监听端口&…

打不开clickonce问题解决过程

1.用户电脑user文件夹下有xx和xx.1两个账户,原先安装在xx账户下,后修了电脑原数据保留在xx.1,新创建XX,之后clickonce就打不开了表现为没有反应,,删除注册表和appdata都只能正常安装,但是不能打开,没有任何报错,发现在我的电脑下打开有这样的提示,,在用户电脑上没有 尝试通过修…

了解CCC认证流程,确保产品合规通过

CCC认证是指中国强制性产品认证制度&#xff0c;也是中国国家质量监督检验检疫总局实施的一项重要认证制度。对于想要在中国市场销售的产品来说&#xff0c;CCC认证是必不可少的步骤。本文将详细介绍CCC认证的流程&#xff0c;帮助您了解并确保产品顺利通过认证。 第一步&#…

智能监控,高效观测 IT 系统瓶颈

前言 云原生时代的监控系统贯穿于移动端、前端、业务服务端、中间件、应用层、操作系统等&#xff0c;渗透 IT 系统的各个环节。因此&#xff0c;在构建 IT 系统之初&#xff0c;就需要考虑如何打造一个完善的监控系统。当面临大量业务流量数据时&#xff0c;借助监控进行问题…

FreeRTOS列表和列表项

FreeRTOS内核调度使用了大量的列表&#xff08;list&#xff09;和列表项&#xff08;listitem&#xff09;数据结构。它的源码中涉及到很多列表的操作&#xff0c;对于FreeRTOS来说&#xff0c;列表就是它最基础的一部分&#xff0c;列表被用作FreeRTOS调度器使用&#xff0c;…

C语言--判断年月日是否合理

一.题目描述 比如输入2001&#xff0c;2&#xff0c;29&#xff0c;输出&#xff1a; 不合理 。因为平年的二月只有28天 比如输入2000&#xff0c;6&#xff0c;31&#xff0c;输出&#xff1a;不合理。因为6月是小月&#xff0c;只有30天。 二.思路分析 本题主要注意两个问…

Android : ListView + BaseAdapter-2简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 实体类 News.java package com.example.mylistviewadapter2.entity;public class News {private String title;private String content;private int img;public News(String title, String conte…

CentOS 7 使用pugixml 库

安装 pugixml Git下载地址&#xff1a;https://github.com/zeux/pugixml 步骤1&#xff1a;首先&#xff0c;你需要下载pugixml 的源代码。你可以从Github或者源代码官方网站下载。并上传至/usr/local/source_code/ 步骤2&#xff1a;下载完成后&#xff0c;需要将源代码解压…

【MySQL】多表查询、子查询、自连接、合并查询详解,包含大量示例,包你会。

复合查询 前言正式开始一些开胃菜多表查询自连接子查询单行子查询多行子查询in关键字all关键字any关键字多列子查询在from中使用子查询 合并查询union 和 union all 前言 我前面博客讲的所有的查询都是在单表中进行的&#xff0c;从这里开始就要专门针对查询这个话题进行进一步…

STM32-标准库和HAL库-不同容量系列的代码移植

使用STM32单片机过程中经常会涉及到不同芯片间的代码转换&#xff0c;手头上熟悉的工程需要稍作处理才能用到新的板子上。常见的是STM32F103xE、STM32F103xC&#xff08;大容量&#xff09;和STM32F103x8、STM32F103xB&#xff08;中容量&#xff09;的转换。这里做一下总结&am…

93.STL-系统内置仿函数

目录 算术仿函数 关系仿函数 逻辑仿函数 C 标准库中提供了一些内置的函数对象&#xff0c;也称为仿函数&#xff0c;它们通常位于 <functional> 头文件中。以下是一些常见的系统内置仿函数&#xff1a; 算术仿函数 功能描述&#xff1a; 实现四则运算其中negate是一元…

PTA-6-45 工厂设计模式-运输工具

题目如下&#xff1a; 工厂类用于根据客户提交的需求生产产品&#xff08;火车、汽车或拖拉机&#xff09;。火车类有两个子类属性&#xff1a;车次和节数。拖拉机类有1个子类方法耕地&#xff0c;方法只需简单输出“拖拉机在耕地”。为了简化程序设计&#xff0c;所有…

依托数据、平台、知识增强等优势 夸克大模型大幅降低问答幻觉率

“大模型时代&#xff0c;夸克有巨大机会创造出革新性搜索产品。”11月22日&#xff0c;夸克大模型公布了其面向搜索、生产力工具和资产管理助手的大模型技术布局。数据显示&#xff0c;夸克千亿级参数大模型登顶C-Eval和CMMLU两大权威榜单&#xff0c;夸克百亿级参数大模型同样…

Linux-编译器

编译器 gcc-arm-linux-gnueabihf gcc-arm-linux-gnueabihf 是一个针对 ARM 架构 Linux 系统的交叉编译工具链&#xff0c;它包括了 C、C、Objective-C 和 Fortran 编译器以及一些辅助工具&#xff0c;用于将源代码编译成可在 ARM 架构的 Linux 系统上运行的二进制程序。arm架…

2023年,人工智能在医疗行业领域的应用场景

本期行业洞察将带领大家了解人工智能在医疗行业领域的应用&#xff0c;主要了解在患者治疗和运营中的应用、人工智能作为预防工具以及大型医院目前如何使用人工智能。未来的智慧医疗时代已经悄然到来。 人工智能在患者治疗和机构运营中的应用 人工智能有望彻底改变医疗护理的…

基本的弹层,点击弹出

<div class"role"><el-button type"primary" size"mini" click"showDialog true">添加角色</el-button></div><!--控制弹框的显示隐藏visible .sync可以点击X关闭弹框 --> <el-dialog width"…