知识图谱初步学习暨服外项目复盘
写在前面
主要是为了考研复试回忆以前做过的比赛项目。当时做的时候就对这块知识囫囵吞枣,找了篇综述看,但是看得云里雾里。试图对该知识建立起一个初步的体系并且复盘当时项目的做法与合理性。
项目来源:第十五届服务外包国赛A类【A11】——基于课程教学数据的实时内容推荐和个性化智能问答系统设计。
当时看,理解是基于给的数据建模知识图谱,设计智能问答系统。但是现在回过头看,还需要推荐系统、分布式调用(意味着需要C-S架构,提供服务器端),这个确实是很有难度了。


Part1 知识图谱概述
知识图谱涉及很多知识点,涉及的学科也很多,学起来很乱。但最基本的概念是实体、属性和关系。
因此从知识图谱的研究内容入手,可以分为以下部分。
- 知识图谱表示:用什么东西来表示知识图谱。
知识图谱根据使用场景和应用需求的不同,可以采用有向图、有向标记图、本体描述语言等方式在逻辑层面进行表示。物理层面研究上述逻辑表示的数据模型主要包含:属性图、RDF 图模型、OWL 本体语言等。而随着神经网络研究的兴起,基于向量的表示方法因为计算高效等特点也被广泛使用。 - 知识图谱存储:如何存下知识图谱的表示,用什么来存?
在知识图谱表示的基础上,可以利用传统关系型数据库和原生图数据库实现知识图谱存储和检索。 - 知识图谱构建:怎么从数据中构建知识图谱?
知识广泛存在于自然语言文本、半结构化以及结构化的数据中,知识图谱构建主要目标就是研究如何利用实体识别、关系抽取等信息抽取技术,从自然语言文本中抽取实体、属性、关系、事件等知识图谱要素的方法,以及属性补全、实体链接、实体对齐等知识图谱扩展和融合方法。 - 知识图谱推理:知识图谱的作用之推理。
知识图谱不仅可以提供知识的存储和检索,更重要的是可以根据构建的已知知识进行归纳、推断和预测未知的事实。知识图谱推理的研究主要基于符号逻辑和表示学习,实现演绎、归纳、溯因、类比等类型推理。 - 知识图谱应用:知识图谱可以用来干什么?
在构建了大规模高质量知识图谱后,可以将知识与不同的自然语言处理任务进行深度融合,构建基于知识图谱的智能推荐系统、基于知识图谱的智能问答以及知识增强的自然语言处理算法。
一、知识图谱表示
知识图谱表示方法分为符号表示和向量表示。符号表示中有属性图、RDF图、OWL本体语言等。而向量表示的方法有很多,比如TransE、TransR等方法。
1.1 符号表示
属性图
是一种有向的带标记的图,包含三个元素:节点(对应实体)、边(对应关系)、属性。

这里的绿色圆圈如员工是概念,而到具体的属性图中,会用比如说姓名等来替代。就好比类和对象。
基于该属性图,工业界常用图数据库Neo4j来实现存储。
资源描述框架RDF、RDFS、OWL
资源描述框架RDF,是由国际万维网联盟 W3C 制定,用于描述实体/资源的数据模型。RDF 的基本组成单元为一个 SPO 三元组 < 主体(Subject),谓词(Predicate),客体(Object)>,用来表示一条客观世界的逻辑描述或客观事实,比如<梅西,是,足球运动员>。。然后多个三元组首尾相接就形成了一个图。

但这个图的弊端是无法分清楚概念和对象的区别。
资源描述框架模式 RDFS,定义了Class用来描述类,Domain表示属性属于何种类别,Range用来限制属性的取值,subClassOf用作描述类的父类,subProperty则描述属性的父属性。是对 RDF 图的扩展。OWL语言则是对RDFS的拓展。
1.2 向量表示
随着文本向量化的发展,深度学习模型将文本映射为低维稠密向量,那么这种方法就是将知识图谱中的知识也用向量表示。简单介绍下 TransE 模型。
TransE
从 CBOW 和 Skip-Gram 的文本向量化模型中,发现了词向量空间存在平移不变现象。模型可以捕捉一些词的相对关系。比如 man 的词向量减去 woman 的词向量约等于 king 的词向量减去 queen 的词向量。

该模型将关系看做是表示空间的平移,就是说一个向量(实体)加上一个关系会等于另一个向量(实体)。那么这样就组成了三元组,很多的三元组就会合成一个知识图谱。
二、知识图谱存储
知识图谱存储到计算机上,一般有两种方式:表方式和图方式,对应的就是用关系型数据库(MySQL等)和图数据库(Neo4j等)。
2.1 基于表的存储
用表的形式存储,也即用关系型数据库存储,至于如何存储主要分为四种存储方式。
基于三元组
顾名思义,按三元组为一条记录进行存储。比如一条记录为 <梅西,是,足球运动员> 。这是最简单的存储方式,但是当用到复杂的查询时需要大量的Join操作,代价十分昂贵。

基于属性表
对实体进行分类,并将属性进行归类。简单来说就是按照实体把相关的放到一张表去。感觉和日常设计数据库是差不多的,但是这样的缺陷是一张表可能存在很多空值,造成存储稀疏。

基于垂直表
属性表是按照实体分类,那么垂直表示按照谓词分类,将属性归类。缺陷是不太好查询。

基于全索引
将一个三元组里的实体和谓词全都映射为数字,然后对三元组的全排列分别存储(A33=6A_3^3=6A33=6 种)。映射因为存储数字比字符串空间更小,而全存储相比三元组表存储显然更有优势,但是维护起来成本比较高。

2.2 基于图的存储
用图存储的知识图谱更加直观。工业界一般使用 Neo4j 来进行存储。
关系型数据库如果要进行多跳查询,那么会有大量的连接操作,这样的效率是很低的。图数据库与关系型数据库的不同就是,它的存储就是按照实体和关系进行存储连接的。可以创建不同的概念即不同的类别,对于每个概念可以创建许多实体,实体又可以给予它相应的属性。
对于下图的 neo4j 数据库来说,“《上邪》”就是概念“著作”的实体,描述、时间、评价就是该类别的属性。其类似于关系型数据库的属性表存储,不同的是,你可以用关系将不同的实体进行连接,而关系型数据库无所谓实体连接,只有查询时表与表的连接。
用图数据库存储,只需要找到所需的实体,再用连接的关系进行查找即可找到答案,相比关系型数据库更加直观也更加方便。

Neo4j 数据库的简单介绍:
Neo4j 由节点和边组成,相应的代表知识图谱中的实体和关系。该数据库支持为节点打上标签,即给实体添加属性。并且显然,组成的图是有向图,也可以从图中看出三元组的形式。
Neo4j的查询是Cypher语言,这是一种描述性语言,用户只需要声明“查什么”,而不用关心“怎么查”。Cypher语言与SQL很相似,但是Cypher关键字不区分大小写,属性值、标签、关系类型和变量则区分大小写。当然用起来的时候,其实用不着写Cypher,用相应的高级语言与查询语言映射库就可以了,项目里用的是py2neo。
三、知识图谱构建
该部分关心的是知识图谱的数据来源,并且如何构建知识图谱。并且构建途中还存在着属性抽取、实体链接和实体对齐的问题。
3.1 数据来源
构建知识图谱最简单的方法是人工构建,用不同的专家知识进行构建。
数据的来源可以分为以下三种:
-
结构化数据:来自于关系型数据库,可以直接将某些字段解析为实体和属性。
-
半结构化数据:有一部分可以根据规则解析。比如维基百科的网页,右边的部分就包含了大部分可以直接使用的实体和属性。

-
非结构化数据:即纯文本数据。而大部分遇到的都是这样的数据。因此需要获得实体,关系和属性。需要运用NLP中的命名实体识别NER,关系抽取RE,属性抽取AE等技术。
3.2 属性抽取
类似于命名实体识别,将任务转换为序列标注任务。不过还需要考虑标签数量巨大以及非封闭世界假设的问题。
该任务的解决方案有基于属性理解的属性抽取方法AVEPT。
3.3 实体链接
主要考虑两个问题,实体的歧义性(比如“苹果”可能指水果,也可能指苹果公司)和实体的多样性(同一实体的不同表述,比如一个人的名字可以是全名、小名、英文名、缩写,这些都指同一个人)。

解决方案有端到端的实体链接方法ENEL,同时建模实体识别和语义消歧的任务,避免了分别建模的错误传播。
3.4 实体对齐
主要研究表示相同含义的实体之间的对齐,比如几种不同语言表示的知识图谱的对齐。
解决方案有基于平移的实体对齐方法MTransE,基于图神经网络的实体对齐方法GCNAlign等。
四、知识图谱推理
基于知识图谱的知识推理,旨在从图谱中已存在的关联关系或事实推断出未知的关系或事实。推理得到的知识又可以反过来丰富知识图谱,为下游的应用提供更好的支持。
由于我现在了解到的知识图谱经常与大模型结合,而大模型具备了推理能力,因此这一部分仅简单了解了一下。
主要方法有:
- 基于符号逻辑的知识图谱推理。
该方案中,主要有本体推理、Datalog推理、产生式规则推理。有部分其实和离散数学中的逻辑谓词可以进行的推理是相同的。 - 基于表示学习的知识图谱推理。
该方案中,主要有基于神经张量网络NTN进行知识图谱推理的方法、基于复空间关系旋转的知识图谱推理。
五、知识图谱应用
知识图谱的应用主要是问答,主要分为语义解析和信息检索的方法。
5.1 基于语义解析的知识图谱问答
将自然语言组织的问句转化为知识库可以识别的结构化查询语句,然后在知识库中查询得到答案。而这个问句到结构化查询的映射,可以通过规则定义的模板或者训练过的自然语言解析器来完成。
语义解析可以分为短语检索、资源映射、语义组合和逻辑表达式生成这四个步骤。
- 短语检索用于识别出问句中的实体、关系等各种短语。
- 资源映射的目标是建立问句和知识图谱之间的映射,这包括实体链接、概念匹配和关系分类。
- 将问句中实体、关系和知识图谱中概念相匹配后,还需要做句法分析、组合模型训练等工作。
- 最终组合生成一个可执行的逻辑表达式,比如SQL语句,然后在知识图谱中获取最终答案。
5.2 基于信息检索的知识图谱问答
通过对问句中的实体和关系识别锁定问题的主题实体,然后根据主题实体得到知识图谱中的候选实体,最后对候选进行排序得到最终答案。随着深度学习技术的成熟,深度学习技术也开始在知识图谱问答中广泛应用,用来提升语义解析和检索排序方法的效果。
这里主要介绍一下信息检索的方法,深度学习的方法有多列卷积神经网络的知识图谱问答与EmbedQGKA算法。
信息检索主要有四个步骤,问题图构建、主题图构建、特征生成、关系映射。
问题图构建
这一步将自然语言问题转换为结构化的图表示,捕捉问题的语义结构。个人理解有点像意图识别。比如问题:“What is the name of Justin Bieber’s brother?” 则可以将其建模为下图,可以表示依存关系。

主题图构建
这一步是从知识图谱中检索与问题相关的子图。设计特定算法来找到知识图谱中的特定子图。比如找到了如下的图:

特征生成
该步骤目的是为问题图和主题图生成对比特征,用于关系映射。通常需要构建人工特征用于表示问题与答案之间的关系。
对于该例,通过问题图中边 prep_of(qfocus=name,brother) 可以提取以下特征:qfocus=name,brother,qfocus=name|brother,qfocus=name|prep_of|brother。(源节点,目标节点,源节点|目标节点,源节点|关系|目标节点)
候选答案特征:一个节点的关系和属性对区分答案非常重要,对于一个问题对应的主题图,提取每个节点的所有关系和属性作为特征。然后将问题的一个特征和候选答案的一个特征组合在一起,这样可以捕捉问题模式和答案节点之间的关系。例如,问题-答案组合特征:qfocus=name|node_type=person。
关系映射
该步骤是将问题中的关系映射到知识图谱中的具体关系路径,得到最有可能为答案的路径。即构建知识图谱中的关系与自然语言单词之间的映射,发现问题所对应的关系。
Part2 项目相关复盘
回忆一下当时的情景,没学过机器学习、自然语言处理,对知识图谱的知识为零,仅有一些零碎的对算法和模型的了解,对问答系统的了解基本为零。一开始是不知道怎么做的,指导老师指导过后也是云里雾里,不知道从哪里着手。做项目的过程中也是不断优化思路,推倒重来了两三版设计路线。
设计思路
整个系统的完成很宏大,当时也不知道知识图谱有啥用,和问答的关系是什么。但是比赛明确提出的技术指标来看,一个重点是知识图谱构建,一个重点是问答系统,反倒是推荐系统没有强调。
正如前文所说,不太清楚知识图谱的用途,但经过调研之后得出的一个显然的结论是可以作为知识库。至少可以根据给出的数据集建立起知识图谱。那么串起的一条线就是构建知识图谱,根据知识图谱提供的信息来对用户进行回答,由于已经碰到足够的问题了,所以在下意识里接下来的思路都是基于单轮对话的。
确定了大致思路,那么就可以细分问题了。(当时没有现在这么清晰,很多都是做到那一步了才发现问题,然后回过头来重新调整)
- 用什么表示和存储知识图谱?
- 怎么构建知识图谱,用什么样的规则构建?
- 用户提出问题后,怎么进行意图识别,怎么根据问题来进行查询?
- 查询获得答案后仅回答答案吗,要不要用自然语言生成?如果要,怎么生成?
下图是几乎最后一版思路,接下来根据每个问题进行具体分析与实现。

问题1:知识图谱表示和存储
当时不甚了解知识图谱还能用关系型数据库存储,但是想着既然是图谱,那么可视化之后的图应该很重要,一定要体现出实体、关系和属性之间的联系。简单调研之后,发现用的比较多的是 Neo4j,既然用的多,那么讨论和资料就相对多,遇到问题有办法去查。冷门的技术如果遇到问题都不一定能解决。
Neo4j 作为一个数据库,具备增删改查的功能,查询-回答的基本问题已经可以解决,那么要解决的就是如何设计数据库内容了。
问题2:知识图谱构建 & 问题3:查询意图对应
该赛题的主题是中华传统文化,不可避免的会碰到文言文之类的东西。如果一些大众的领域,可以去 OpenKG 上一些开源的知识图谱寻找。这个主题当时团队找了一会发现不太容易找到,所以直接从零开始了。
数据初步清洗
要构建图谱,首先需要明确给出的数据集。

该数据集很杂乱,内容里包含 html 标签,无法直接使用。字段也包含很多:目录、题目类型、题干、答案、解析、难度、选项等。可以明确的是,除了数据清洗,数据中还包含很多空值,需要进行舍弃字段或者知识补充。
当时也没有具体的思路,只知道先把数据清洗出来。团队讨论后,分工进行清洗。html 标签清洗很容易,写正则表达式就可以解决。获得文本数据后,清洗过程中遇到很多问题,题目类型不同导致无法用一个pyhton程序进行清洗。而考虑到如果对于一个知识,要问的话,不一定是仅考虑数据集里的填空题形式的,比如说第五行填空题“己欲立而立人,___ ”,到时候问的时候完全可能是 “ ___,己欲达而达人”。但这两种必定是同一种知识,因此索性清洗的时候,就直接将答案填充到题干里去,把问句全部变成陈述句,到时候再根据相关规则进行建图谱。
经过清洗后数据大概会长这样

规则建立探索
最初的想法是,一切从简,按照流程,问啥从知识图谱里找啥,但怎么找是个问题,首要的就是意图识别。比如说用户问一个“床前明月光”的下一句,就不能给这一句进行翻译而是要给出后一句。其次是怎么找的问题,给出自然语言文本,怎么定位到知识图谱里去。这两个问题按照理论里对应着上文的短语检索和资源映射。因此大致的问答方法应该是对应着基于语义解析的知识图谱问答。
按照流程,需要对问句进行命名实体识别、属性抽取和关系抽取,然后对应到建立的知识图谱上。但问题是,当时的命名实体识别模型就算有,也处理不了古文的内容。这意味着需要自主标注数据并训练模型,使得可以处理古文的内容。而且谁也说不好到底怎么识别好,比如“学而时习之”,这里有两个文言虚词,如果按照单字进行识别,工作量大不说,效果也未必见得好。
先找出可能会包含哪些类型的问题。人工初筛后,大概获悉有问古诗词的来源、前后句、作者、翻译、默写,对传统文化知识的提问比如对《史记》的评价,孔子是哪一派别的人物之类的问题。从命名实体识别的角度来看,客观事实类的,就用得上常见模型,比如说可以建模出“人名”,“著作”之类的实体;而文言整句,就无从下手了。因此将这些问题分为两类,一类是难以建模的文言类,另一类是客观事实类。这一步其实是建立不同的子图过程。
基于该思路制作的demo中,问答根据规则而对应到不同的问题类别中去,从而实现系统功能。

实体关系构建
前面提到,将问题分类就是因为实体不好一劳永逸,因此每类问题的实体和关系以及属性需要自行探索并且为了训练模型,团队还需要进行人工标注。
首先确立一个概念“题目类型”,里面存在两个实体:文言类和事实类。再按照两个类型进行实体细分,其他实体与该实体之间的关系为“题型”。
文言类
按照上图,题目类型包含上下句、来源、作者、翻译、原文填空。每个类别的实体和关系不相同,但是属性可以相同,属性均含有 [作者、来源、注释、翻译] (注释是对其的评价等内容,因此有很大一部分实体该属性为空值)
实体 [分句、全句] ,关系 [上一句、下一句、部分] 。
该类问题思前想后,单个字或者像大模型那样token拿出来组合并不合适,因此直接按照句子建模,即完整的句子作为一个概念,其中的每一个分句作为另一个概念。那么显然,句子之间的关系有前一句和后一句的关系,分句属于整句,因此有“部分”的关系。而每个实体均有 [作者、来源、注释、翻译] 的属性,可以用来解决来源、作者、翻译的问题。而天然的就能解决上一句下一句的问题。
还剩一个句中填空的问题,如果可以对应到具体知识的分句,那么按照这样规定的实体也可以解决问题。
事实类
事实类问题的特点就是杂,概念繁多。经过了解数据以及常见的实体识别,“人名”、“地点”之类的实体很轻易就可以得到,而经过对数据的讨论和标注后,下图中除了文言类之外的实体和关系均属于事实类。

为了得到上图的结果,接下里的工作就是对数据进行标注。团队用的标注工具为 Label Studio。下载python库即可标注。

知识图谱构建
标注完成后获取到数据集,稍加调整获得相对规格化的数据。接下来能做的工作是训练命名实体识别、关系抽取模型以及构建知识图谱。
该过程中,如果仅利用 neo4j 提供的可视化平台 Neo4j Browser 来写 Cypher 语言存储数据就有点效率太低了。因此同样借助于 python ,虽然 py2neo 库在21年已经停止维护,不过对简单的需求应该问题不大。团队使用py2neo 构建知识图谱。有了规格化数据,这一步便不难。当然中间的摸索过程和 demo 运行免不了一番功夫。

练模型做优化
训练命名实体识别和关系抽取模型,便于在用户给出的问题中提取相关的实体和关系。但事实情况是,这一过程异常艰难,并且时间有限。最后只训练出了命名实体识别模型,使用模型为 RoBERTa 。

说实话,效果其实一般般。但是当时已经过去好久了,距离比赛结束已经没有很充裕的时间了。没有办法回到数据构建重新考虑。因此必须考虑一种保底的算法来获取实体和关系。
由于已经对问题进行分类,因此想到按照关键字匹配来对应问题。这样考虑其实已经省去了关系的查找,只需要获得实体并对应到图谱中就可以了。打个比方说,如果问题是作者问题,那么问句中会不可避免包含一些关键词“作者”、“谁写的”、“作家”等,那么到这里就可以建立出几种问题的关键词。然后根据检索到的关键词,分类问题,制定回答的制定模板就可以实现问答。

接下来只需要获取实体。最简单的方法获取一个实体列表存在内存里,里面包含了所有的实体。对问句进行循环,查询实体是否在问句中出现。但这种方法的弊端显而易见,循环的次数很多,耗费的时间几乎不能忍受。因此想到了字符串匹配算法,用 AC 自动机进行模式匹配。Python 中也包含了实现AC 自动机的库,团队用的是 ahocorasick。以下代码给出了一个简单的例子。
import ahocorasick
def make_AC(word_set):
AC = ahocorasick.Automaton()
for idx,word in enumerate(word_set):
AC.add_word(word,(idx,word))#第二个参数为击中返回的结果
AC.make_automaton()
return AC
def ac_entity(word_list,sentence):
'''
ahocosick:自动机的意思
可实现自动批量匹配字符串的作用,即可一次返回该条字符串中命中的所有关键词
'''
AC_KEY = make_AC(word_list)
name_list =set()
for item in AC_KEY.iter(sentence):#将AC_KEY中的每一项与content内容作对比,若匹配则返回
name_list.add(item[1][1])
name_list = list(name_list)
name_list.sort(key=word_list.index)
#if len(name_list) > 0:
#print(sentence, "--->命中的关键词有:", "\t".join(name_list))
return name_list
sentence="夫运筹帷幄之中,决胜千里外吾不如子房,填国家抚百姓给饷馈绝粮道萧何连万众战必攻取韩信这句话出自哪里?"
word=ac_entity(fenju,sentence)#fenju是对应的实体列表
'''
输出结果为:['夫运筹帷幄之中', '决胜千里外吾不如子房', '填国家抚百姓给饷馈绝粮道萧何连万众战必攻取韩信']
'''
运用该算法可以较快获得句子中想要的实体,还剩下最后一小类问题,就是句内填空,如何根据“__而实习之”获取“学而时习之”呢?
既然都用了模式匹配了,那联想到字符串的匹配,用模糊匹配解决。团队使用 python 库 fuzzywuzzy 解决该类问题。
再对各类问题建立各种规则,就基本解决了问答。
问题4:自然语言生成
如果仅做到上述这一步,虽然也不容易,但最后的回答问题就和查表一样,简直是个笨蛋机器人,团队并不满足于这一点。当然也想过设计多种回答模板,每次回答随机取一种回答,应对了那句“越人工越智能”。正当无从下手时,指导老师发力,给出了提示学习的方向,团队调研尝试理清逻辑后,几乎是对上述思路进行了大改。
重新理解问答
提示学习是大语言模型兴起之后的一种方式,用 prompt 的方式规格化统一了 NLP 任务。当时的 T5 模型比较著名,也在论文中提出了 Text-to-Text 的想法。既然都是输入文本,输出文本,那么问答其实也可以用这样的模型解决。而由于预训练的关系,模型本身就具备输出自然语言的能力,这样就完美解决了自然语言生成的问题。而且也无须进行模式匹配来进行意图识别,模型可以在训练中识别用户的意图,更加灵活。
同时,如果按照上述流程解决该问题,那么显然是一个封闭世界假设的问题,对于没有在知识图谱里的知识就回答不了。但大模型是预训练微调范式,由于预训练的知识领域很广,也一定程度上能缓解这样的局面。
那么接下来的思路就是,制作新的问答数据集,对 Flan-T5 模型进行微调。不过又出现了新的问题,如果仅仅这样,那建立知识图谱的目的是什么?整个流程仅需要大语言模型的输入输出,而无须经过知识图谱。
重新思考后,团队将问答系统理解为一个阅读理解的 NLP 任务。完整的来说,前一套流程依旧适用,只不过目的由直接提供答案变成了提供相应的知识,而大模型的任务就是根据给出的知识和问题进行解答。比如说,如果问到“学而时习之”的下一句是什么,那么经过知识图谱,可以检索到的知识为“学而时习之,不亦说乎。出自《论语》,翻译为…”,随后将这些知识贴到问题前,将知识和问题一起作为大模型的输入,然后由大模型做出最后的解答。
流程回溯优化
至此,系统的总体思路已经完善,不过暴露出的许多小问题仍未解决。并且其实做的时候并没有列出来这么清晰,上述流程很多都是到这一步才发现并实施的。
- 如何保证问句中识别的实体对应到图谱中相应的实体?(如何确保主题图构造正确?)
- 大模型需要微调,成本不低,数据集需要重新制作。
- 经过大模型后,整体等待的问答时长稍微有些长,如何尽量缩短等待时间?
优化策略如下:
AC自动机所用的实体列表由知识图谱的实体得来,其中有许多重复值,这降低了效率,同时导致有些重复实体没有正确连接,也就是没有正确添加关系。因此需要回到制作数据集那一步进行精细去重。这样之后才得到2800个实体。
同时,在内存中,建立字典(哈希表)存储简单的实体关系从而减少知识图谱的查询。考虑到进行了问题大类的分类,建立起两个大类的字典,这样能有效减少查询时长。此时,如果有些实体在两个大类的问题中均出现,那么均取,将各自的知识都贴上交给大模型处理。这样就解决了实体链接的问题。

模型训练调优
由于 Flan-T5 仅支持英语,因此团队在 huggingface 社区上寻找变体即mt5,支持多语言的T5。
有了预训练模型,接下来就是准备数据集了。之前的数据需要重新处理,由于将任务设置为阅读理解,需要先将问题过一遍知识图谱,获得知识,然后将知识贴到问题前面。获得(知识+问题,答案)这样形式的数据。而如何处理问题,是根据不同类型的问题,经过chatgpt生成获得不同的问法,再将所有问题分布到不同的问法中去。最后获得总的数据。

至于微调部分,基于 transformers 库。但当时并没有了解到 PEFT 高效微调以及 Lora 微调,因此是基于预训练模型的基础上进行继续训练的,这就要求有足够的显存去存下整个模型。因此最后仅使用了 Large 规模的模型,印象里似乎参数数量是 780M。
Web应用实现
最后一步,就是搭建前后端和可视化界面。由于知识图谱和大模型端都是Python,因此 Web 端也基于 Python 实现。选择不多,Django 比较常用,因此基于 Django 实现。
需要实现的功能至少是:登录注册以及首页(推荐的样式起码要有)、问答界面、知识图谱展示界面以及数据分析界面。
首页:
登录注册框,基础的导航栏加上主体轮播图,最后是推荐传统文化样式。

问答界面:
时间有限,当时仅做了能够实现一问一答的功能,历史记录仅是个样式,无法链接。借助了Ajax异步刷新实现。
这里的缺陷比较大,流式输出也没能实现。

知识图谱展示界面:
主要是将建立的图谱在网页进行可视化,其实 Neo4j 自带的会更好。但是移植不过来。因此基于百度开源的 Echarts 做了知识图谱可视化以及实体查询功能。值得一说的是,这一步需要实体不重复,如果重复就会渲染失败,还好前面做了高度去重,这一步没花多少时间就调试好了。赛后评委给出了如果换成立体图会更好的建议。

数据分析界面:
主要是词云图,以及一些数据的展示。同样使用 Echarts,词云图部分使用 python 库 opencv 和 wordcloud。

赛后复盘
诚然,在当时的水平下做成这样已经是非常不错了,团队也已经尽了全力。
首先按照赛题技术要求对比一下。
- 数据收集与清洗。
这个可太完成了,超额巨额完成。清洗整理结构化,花了非常多的时间在数据上。 - 知识图谱构建。
同样完美完成,从表示到存储上都完全符合赛题要求。 - 问题匹配与答案生成。
从结果上看,应该也是完成的比较好。
语义匹配和理解技术:应该可以包含规则化的理解,这一步做到了。
对问题进行解析和匹配找到知识:完成,基于实体找到子图,获取所有知识。
答案以亲用户形式展示:完成,并不仅仅包含答案。 - 交互和追问。
可以说基本没完成。仅有基本的交互,无法进行追问和深入。 - 验证和优化。
略,这个评估必定是完成了的,不完成也得说自己完成了。
然而,赛题上还给出了任务清单。

推荐系统:一点没做,首页给出了四个推荐框,技术文档以及 ppt 中没有一点提到推荐系统。仿佛团队和指导老师都没有看到这一点一样。
问答系统:问答有了,但是未必推理性,未必智能。
可以作为独立功能运行,也可作为服务运行,支持分布式任务调用:基本没完成。仅有独立功能,服务器的负担也不小,更谈不上分布式了。
总结评价
作为复试简单介绍,这样进行总结:完成了一个基于知识图谱和大模型的智能问答系统,主题为中华传统文化。该项目以知识图谱为知识库,用 T5 模型作为输出大模型。首先,用户提出问题系统会进行命名实体识别和语义解析,抽取出相关的实体,随后对应到知识图谱中,获取相应子图的知识。获取知识后,将其传递给大模型,大模型依据给出的知识进行具体问答。
这样基本可以说到所有的点,复试老师如果对其中某一点问起来也可以对应到上文的细节。
用现在的眼光来看, 对该项目进行评价,或者说复试老师提问你这个有什么可以改进的地方:从赛题要求上看,企业偏向的技术手段为推荐系统+RAG+生成式大模型。T5 模型虽然在 NLP 任务上表现优异,但是并不擅长生成自然语言文本与对话,因此从模型选择上就可以进行改进。而知识图谱的作用在项目中作为知识库,而知识库就可以使用 RAG 技术,将知识进行向量化之后再进行对应,比直接字符串查找可能会更好一点,同时 RAG 要求的材料也不用建立知识图谱那么苛刻,虽然是赛题要求的。至于推荐系统,至今还没有进行调研。再者,需要进行压力测试等方式优化 Web 端,现在粗糙的系统肯定是不能承受很多访问的,问答系统的实现也有待提高。
深度QA
-
Q1:RoBERTa模型训练效果一般是如何一般,有没有做量化指标?原因是具体是数据量不足、标注质量、还是模型参数或训练策略问题?有没有进行过简单的错误分析?
A1:验证的准确率和 f1 值为86.6%和85.1%,对于要求很高的企业级项目来说是比较一般的,有些数据难免会出现错误。原因可能是因为对古文做命名实体识别我们也不确定思路是不是正确的,有些划分可能有争议。而roberta模型应该是在现代预料下做预训练的,对古文的效果可能受限。可以尝试用古文对模型做预训练来提高效果。

-
Q2:引入AC自动机后,系统的准确率和召回率大概是多少?怎么做的测评,测评的粒度是怎么样的,只是看答案是否相关,还是逐字核对生成的文本是否严格基于检索到的子图、没有引入外部知识?模糊匹配的阈值如何设定的?
A2:当时做过整个系统获取知识的准确率,一共几乎5000数据,最后有120条左右的数据有误,这个准确率可以接受。测评的时候我们对所有数据进行问题的评测,然后统计T5回答错误的问题。接下来人工进入数据中查看原因,如果是知识没贴好导致的错误,那和标答的差别会很大,就显然是资源映射的问题。模糊匹配没有设置阈值,返回5条有匹配分数的实体,取分数最高的那一条。 -
Q3:微调mt5时,构造的数据集规模有多大?微调后有没有在测试集上评估过BLEU或ROUGE指标?直观感受上,模型“编造”答案的情况多吗?
A3:大概五千条数据,评估的BLEU-4指标大概是30,rouge-1有50左右。对开放领域生成来说,这样的指标还可以,但对于文本摘要任务来说,稍微有些偏高。直观感受的话,模型几乎没怎么编造答案,回答的比较准确,不过可能对自身内在的知识利用率不大。 -
Q4:用AC自动机做实体匹配,主要是为了解决NER模型效果不好的问题。但AC自动机只能做“完全匹配”,对于“同义词”(如“孔子”和“仲尼”)或“上位词”(如“诗仙”和“李白”)就无能为力了。如果当时时间更充裕,你们会怎么优化这个问题?
A4:在知识图谱实体定义的时候,有定义过“别名”这样的实体,在一定程度上有部分缓解作用。但是以现在的思路可以做短文本语义相似度计算,用sentence-BERT做向量相似度比较。 -
Q5:你提到“流式输出也没能实现”。如果现在要优化这个体验,让大模型的回答一个字一个字地显示出来,在前端和后端分别需要做什么?
A5:后端需要将HTTP协议从普通的短连接改为SSE(Server-Sent Events,服务器推送事件) 或WebSocket,让Django视图可以逐字地Yield模型的输出结果。前端JavaScript则需要通过EventSource API或WebSocket客户端接收这些流式数据,并动态地追加到页面上。 -
Q6:数据标注过程中,知识图谱Schema是怎么样的?如何确保数据标注的质量?
A6:实体类型有分句、全句、事件、人名、思想、思想内容、派别、著作、地点等,关系类型有上一句、下一句、部分、提出者、著有、内容、来源、事件等。数据标注由两个人完成,但只做了初期的一致性检测。从数据集里随机抽了少部分样本大概50条的样子,然后做了一致性检测,Fleiss’ Kappa为0.8左右,就让他们继续做了。然后遇到不确定的或者标注不一致的就交给团队统一处理了。 -
Q7:基于NER和AC自动机共同进行短语检索与资源映射,是如何共同进行的?
A7:AC自动机可以完全获取预先定义的实体,大部分情况下都是与NER的识别结果一致的,少部分NER识别结果有遗漏,它可以进行补充。但也有些情况是指向同一个东西的不同表述,AC自动机无法获得,但NER有泛化能力可以识别出,这样也可以对应到知识图谱(李白,李太白)。当然还有一种情况NER是获得的实体在知识图谱中无法找到,这要不就是知识图谱的数据不够充分,要不是NER出现了错误;就最后实际使用来看,出现这样的情况是比较少的(5000条数据,120条有误)。 -
Q8:你这个系统是搭在本地还是在云端的,如果在云端有没有做过并发测试?
A8:当时我们还不太清楚上云的流程,并且在本地端用cpu推理就比较缓慢了,上云之后如果没有gpu实例可能会效果更差,因此只有本地部署。不过我认为,在云端搭建系统是比较合理的,可以基于阿里云或者腾讯云来租借实例和服务器,把模型部署在云端,前端通过API访问。
那么如果搭载云端,肯定是要考虑并发性的问题。因此打算对架构进行拆分,整体功能选取两个服务器,一个搭建web系统,用Django支持;另一个专门负责大模型推理,可以用fastapi封装接口,也可以用ONNX Runtime等方法加速推理。两个服务器之间通过内网http调用通信,web端开放公网接口。并且可以考虑加入消息队列的异步处理,使用redis和Celery:用户请求进来之后先存入redis队列然后worker从队列中拉取任务,获得结果后存入数据库,前端可以用后端返回的task_id进行轮询或者websocket来获取信息。做完之后,考虑用Jmeter进行压力测试然后获得吞吐量和响应时间等指标。
(大模型推理优化三种方式的适用情况:
vllm:PageAttention像操作系统一样将kvcache按页表的方式存储,适合高并发的情景。但为参数较大的模型设计,在参数较小的模型上提升不大。
ONNX Runtime:可跨平台,稳定提升推理速度。
TensorRT:仅适用于NVIDIA显卡,做算子融合和精度校准。
在这个任务中,vllm并不适用,应该考虑另外两种。) -
Q9:怎么保证问答对知识图谱的依赖是必要的,而不是模型直接背答案?为什么微调的时候是以(知识+问题),答案的形式,而不是直接(问题,答案)呢?
A9:当时有做过简单的测试,直接用预训练版本的去进行回答,基本是答非所问的状态。这说明预训练的时候模型在这方面的知识是很欠缺的。并且从图谱中获取的知识并不是直接可输出的答案,而是包括相关的知识,这就要求模型理解所问,然后简单推理再回答,而不是机械回答。另一个简单的小测试是,在给模型贴了错误的知识之后,它回答的问题也是错误的,这能说明模型是依赖知识图谱的知识。
两种方法的要求不一样,前者是阅读理解任务或者说基于知识的生成任务,后者是开放领域生成任务。这两者问题的难度是不一样的,由于只能使用相对小参数的模型,因此首先考虑的是降低任务难度,使得模型可以准确生成答案。其次,如果是后者,那么知识图谱的构建就难以发挥作用了。最后,考虑到解耦的问题,如果是后者,那么如果训练到了一条错误数据,就面临着重新训练模型的问题,而且更新知识起来很不方便;但如果是前者的话,模型只需要学到根据知识回答的推理能力,这个能力在不同领域直接可以迁移。并且对于系统来说可以方便地更新知识。 -
Q10:你说‘用 NER + AC 自动机双路召回’。如果压力测试发现 Neo4j 的查询成了瓶颈,你会怎么优化? 是加索引、调查询语句,还是引入缓存?如果引入缓存,你打算缓存在哪一层(Django 层还是 Neo4j 层),缓存的 key 怎么设计?
A10:可以在Django层加一层Redis缓存;缓存的key可以设计为{三元组:预设答案}的形式,对于一些高频的简单问题,命中缓存可以直接返回,跳过全流程。至于缓存更新机制,可以采用TTL+主动更新的策略:当数据库更新时,采用信号机制(发布订阅或者消息队列)自动对对应的缓存进行更新。 -
Q11:命名实体识别和ac自动机获得实体之后,没有做关系抽取,你们是如何组成知识的呢?
A11:我们当时确实没有做独立的关系抽取模块,而是采用了一种基于规则的直接知识组装策略。这个选择主要是基于我们对数据的分析和对任务的理解。根据对任务的观察,我们将任务分为了两类,一类是文言类,这类问题通常围绕单句展开,比如上下句、作者、翻译、出处;另一类是事实类,就比较复杂,什么都可能问。当匹配到文言类时,直接从知识图谱中取出该实体的所有预定义属性(作者、翻译、注释、出处)然后拼接;当匹配到事实类时,按照周围链接的实体获取关联的关系和实体组成知识;当有些是两者都属于的,就都会做。这样做的好处是简单,并且速度快。缺点就是当遇到多跳关系关系时,就难以形成有效知识了。当然,之后才发现,neo4j是可以直接用查询来获取实体周围1-2条的邻居的,那么如果这样,就省力多了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)