基于 Mistral 7B 的知识图谱构建方法
几个月前,基于知识的问答(KBQA)还算是一个新鲜概念。
如今,借助检索增强生成(RAG) ,实现 KBQA 对于任何一位 AI 爱好者来说都已经变得轻而易举。
LLM 的出现极大地拓宽了自然语言处理(NLP)的可能性领域,而且每天都在不断进步。
这次,我想分享另一个可以与递归式 RAG 结合,从而打造“超级研究 Agent”的思路。
这个想法来源于我在小型 LLM 上做递归 RAG 实验时的一些探索,同时也受到了 Medium 上其他文章的启发,特别是知识图谱增强生成(KGAG) 的概念。
知识图谱(Knowledge Graph, KG) 或其他类型的图结构由节点(Nodes) 和边(Edges) 组成:
•节点代表一个概念•边表示两个概念之间的关系
在本文中,我将分享一种将任意文本语料转换为“概念图(Graph of Concepts, GC)” 的方法。
这里我会将 “GC” 与 “KG” 交替使用,以更好地描述本项目的目标。
技术特点
•完全本地化运行 : 所有组件都可以在本地搭建,因此该项目可以在个人电脑上轻松运行。•无 GPT 依赖 : 我选择了小型开源模型,如 Mistral 7B OpenOrca instruct 与 Zephyr ,并通过 Ollama 在本地部署。•简化存储结构 : 尽管 Neo4j 等数据库非常适合存储与检索图数据,但为了保持简洁,我这里使用了内存中的 Pandas DataFrame 和 NetworkX Python 库 。
项目目标
我们的目标是:
1.将任意文本语料转化为概念图(GC) ;2.以交互式的方式可视化图结构(可拖动节点和边、缩放、调整物理属性等);3.实现如同本文封面图那样的美观可视化效果。
项目演示与结果请参考 GitHub 页面:
🔗 https://rahulnyk.github.io/knowledge\_graph/
接下来,我们将先回顾知识图谱的基本概念及其必要性。
如果你对这些内容已经熟悉,可以直接跳过这一节 。
知识图谱
来看下面这段文字:
Mary had a little lamb,
You’ve heard this tale before;
But did you know she passed her plate,
And had a little more!
针对这段文字,可以将其转化为如下其中一种可能的知识图谱(KG) 表示:
IBM 对知识图谱的解释
IBM 的一篇文章**《What is a Knowledge Graph?》** 对知识图谱的核心概念进行了简洁而准确的阐述:
知识图谱(Knowledge Graph),又称语义网络(Semantic Network),表示的是由真实世界中的实体(如对象、事件、情境或概念 )组成的网络,并展示它们之间的关系。
这些信息通常存储在图数据库中,并以图结构的形式进行可视化,因此得名“知识图谱”。
📎 原文链接:What is a Knowledge Graph? | IBM[1]
为什么要使用知识图谱?
知识图谱的用途非常广泛,例如:
•运行图算法与中心性分析
通过计算任意节点的中心性(centrality),我们可以判断某个概念在整体知识结构中的重要程度。•分析概念的连通性与社群结构
可以研究哪些概念是相互关联的、哪些是孤立的,还可以计算概念社区(community)以更深入地理解主题。•发现隐藏关联
能够揭示表面上毫无关系的概念之间的潜在联系。
知识图谱与增强生成
知识图谱还可以用来实现图检索增强生成(Graph Retrieval Augmented Generation, GRAG 或 GAG) ,让我们能够与文档对话 。 这种方法往往能比传统的 RAG(Retrieval Augmented Generation) 取得更好的效果,因为传统 RAG 存在一些不足:
•仅依赖语义相似度检索并不总是有效
当查询语句未能提供足够的上下文,或上下文分散在大规模语料中时,RAG 很难找到最相关的内容。
示例
查询:
“请介绍一下《百年孤独》中何塞·阿尔卡蒂奥·布恩迪亚(José Arcadio Buendía)的家族树。”
《百年孤独》记录了何塞·阿尔卡蒂奥·布恩迪亚家族 7 代 的历史,而且半数角色都叫这个名字。
在这种情况下,单纯依靠 RAG 管道想要精确回答几乎是不可能的任务。
RAG 的另一大不足
RAG 不能告诉你该问什么问题 。 而在很多场景中,提出正确的问题比得到答案更为重要。
GAG 的优势
•图增强生成(GAG) 可以在一定程度上解决 RAG 的局限性。•更理想的做法是将二者结合,构建 图增强 + 检索增强生成 的混合管道,兼得两者优势。
构建概念图
如果你向 GPT 询问:“如何从一段文本中创建知识图谱?”,它可能会给出类似这样的流程:
1.抽取概念和实体 —— 这些将作为节点(nodes)。2.提取概念之间的关系 —— 这些将作为边(edges)。3.将节点和边填充到图数据结构或图数据库中 。4.进行可视化 —— 即便只是为了美观展示。
其中 步骤 3 和 4 较容易理解,但 步骤 1 和 2 如何实现才是关键问题。
我的方法
下面是我为从任意文本语料中提取概念图 而设计的流程图(draw.io 绘制)。
它与上面 GPT 给出的方法类似,但做了一些细化和改进。
步骤解析
1.切分文本语料
将语料分割成多个文本块(chunk),并为每个块分配一个 chunk\_id
。2.提取概念与语义关系
对每个文本块使用 LLM 提取概念 及其语义关系 ,为这种关系分配一个权重 W1 。•同一对概念之间可能存在多种关系•每种关系都作为一条边记录3.引入上下文邻近度关系
如果两个概念出现在同一文本块中,即便它们未显式关联,也因上下文接近而建立一种关系,并为其分配权重 W2 。•同一对概念可能在多个文本块中重复出现4.合并相似边
将相同的概念对进行归并,累加权重 ,并将关系描述拼接在一起。•最终每对不同的概念之间只保留一条边•边包含总权重和关系列表(名称)
实现说明
该方法的完整 Python 实现可在我分享的 GitHub 仓库中查看。
在接下来的章节中,我会简要介绍实现的核心思路。
为了演示这个方法,我选用了一篇发表在 PubMed/Cureus 的综述文章(遵循 Creative Commons Attribution License),并在本文末尾对作者致以感谢。 文章:
India's Opportunity to Address Human Resource Challenges in Healthcare[2]
近年来,印度的健康指标有所提升,但仍落后于同类国家。该文探讨了印度在医疗卫生人力资源方面所面临的挑战与可借机应对的机会。
Mistral 与提示词设计
在上面的流程图中,步骤 1 相对简单:
可以直接使用 LangChain 提供的多种文本分割器(text splitters)将长文本切分为若干 chunks 。
步骤 2 才是核心:为抽取概念及其关系 ,我使用了 Mistral 7B 系列模型。在确定最合适的变体之前,我尝试过如下模型:
•Mistral Instruct •Mistral OpenOrca •Zephyr (Hugging Face 社区基于 Mistral 的衍生版本)
为避免本地资源压力,我使用了这些模型的 4-bit 量化版本 ,并通过 Ollama 在本地托管运行(这样我的 Mac 不会“生气”)。
Ollama
本地快速运行各类大语言模型:https://ollama.ai
这些模型都是 指令微调(instruction-tuned) 的,支持 system prompt 和 user prompt 。 只要在提示词中明确指定,它们通常都能很好地遵循指令 ,并将结果整洁地输出为 JSON 。
在多轮试验(hit & trial)后,我最终选择了 Zephyr ,并搭配如下提示词。
SYS_PROMPT =(
"You are a network graph maker who extracts terms and their relations from a given context. "
"You are provided with a context chunk (delimited by ```) Your task is to extract the ontology "
"of terms mentioned in the given context. These terms should represent the key concepts as per the context. \n"
"Thought 1: While traversing through each sentence, Think about the key terms mentioned in it.\n"
"\tTerms may include object, entity, location, organization, person, \n"
"\tcondition, acronym, documents, service, concept, etc.\n"
"\tTerms should be as atomistic as possible\n\n"
"Thought 2: Think about how these terms can have one on one relation with other terms.\n"
"\tTerms that are mentioned in the same sentence or the same paragraph are typically related to each other.\n"
"\tTerms can be related to many other terms\n\n"
"Thought 3: Find out the relation between each such related pair of terms. \n\n"
"Format your output as a list of json. Each element of the list contains a pair of terms"
"and the relation between them, like the follwing: \n"
"[\n"
" {\n"
' "node_1": "A concept from extracted ontology",\n'
' "node_2": "A related concept from extracted ontology",\n'
' "edge": "relationship between the two concepts, node_1 and node_2 in one or two sentences"\n'
" }, {...}\n"
"]"
)
USER_PROMPT = f"context: ```{input}``` \n\n output: "
如果把那首(并不适合幼儿园的)童谣作为输入并使用上述提示词,模型可能给出如下结果:
[
{
"node_1":"Mary",
"node_2":"lamb",
"edge":"owned by"
},
{
"node_1":"plate",
"node_2":"food",
"edge":"contained"
}
// ...
]
请注意,模型甚至猜出了“food(食物)”这个概念 ,尽管它并未在该文本块中被明确提及。是不是很妙?
如果我们把整篇示例文章逐块处理 ,并将模型返回的 JSON 转成 Pandas DataFrame ,大致会得到如下效果(示意图):
•表中每一行 代表一对概念之间的一条关系 ;•换到图结构的语境里:每一行就是图中的一条边 (连接两个节点);•同一对概念之间可能存在多条边/多种关系 ;•表中的 count 列是我人为设为 4 的权重 (示例用)。
上下文邻近
我假设:在语料中彼此靠得近 的概念是相关 的,我们称这类关系为 “上下文邻近(contextual proximity)” 。
计算“上下文邻近”边的步骤:
1.“熔化(melt)”数据表
将 node\_1
与 node\_2
折叠为一列(统一为“节点”列),保留它们所属的 chunk\_id
。2.按 chunk\_id
自连接(self-join)
基于相同的 chunk\_id
做自连接,让同一文本块 中的所有节点两两配对 生成行。3.去除自环(self-loop)
上一步会产生“节点与自身配对”的行,即自环 (边从某节点出发又回到该节点)。
通过删除 node\_1 == node\_2
的行 来移除这些自环。
处理完成后,我们会得到一个与原始 DataFrame 结构相似 的新表(示意图):
•其中 count 表示:node\_1
与 node\_2
共同出现 的文本块数量;•chunk_id 列保存了所有共现的文本块 ID 列表 。
融合两类关系,生成网络图数据表
现在我们有两张表:
1.语义关系表 (由 LLM 抽取的概念关系,带权重 W1);2.上下文邻近表 (由共现统计得到的关系,带权重 W2)。
将这两张表进行合并/聚合 (按概念对分组,合并关系描述、累加或加权处理 count/权重等),即可得到用于绘图的网络图 DataFrame 。
下一步:可视化
至此,我们已经基于文本构建完概念图 。 但如果止步于此,未免略显意犹未尽——我们的目标是把它可视化 ,做出与本文开头封面示意图 类似的交互式网络图。
好消息是:离目标已经不远了 !
构建概念网络
NetworkX 是一个让图结构处理变得极其简单的 Python 库。
如果你还不熟悉它,可以点击下方链接了解更多:
•NetworkX - 官方文档[3]
将我们的 DataFrame 加入到 NetworkX 图中,只需要几行代码:
import networkx as nx
G = nx.Graph()
# 添加节点
for node in nodes:
G.add_node(str(node))
# 添加边(从关系表 dfg 中逐行读取)
for index, row in dfg.iterrows():
G.add_edge(
str(row["node_1"]),
str(row["node_2"]),
title=row["edge"],# 关系名称(可用于可视化时的标签/提示)
weight=row["count"]# 权重(出现次数或合并后的权值)
)
现在,我们可以开始发挥网络图 的威力了。NetworkX 内置了大量可直接调用的图算法 ,可用于你的概念网络分析。可参考算法列表:
•Algorithms — NetworkX 3.2.1 文档[4]
用社群检测为节点着色
这里我使用了一种社群检测 算法,为不同社群的节点赋予不同颜色。
社群(Community) 指的是:在图中彼此连接更紧密 的一组节点,相比之下它们与图中其它节点的连接更稀疏。
在“概念图”场景下,概念社群 可以帮助我们快速把握文本中讨论的宏观主题 与知识板块 。
在本文使用的综述文章上,Girvan–Newman 算法检测出了 17 个概念社群 。下面是其中一个社群的示例:
[ "digital technology", "EVIN", "medical devices", "online training management information systems", "wearable, trackable technology" ]
有了上面的社群检测结果,我们立刻就能把握这篇综述中健康技术(health technologies) 的宏观主题,并据此提出更精准的问题——随后再交给 RAG 管道 去检索与回答。很棒,对吧?
接下来计算每个概念在图中的度(degree) 。 度 指一个节点连接的边的数量 。在我们的语境里,度越高 说明该概念对全文主题越核心/关键 。 在可视化时,我们将用度来决定节点大小 :度越大,节点越大。
图可视化
可视化是整套流程中最有趣 的一环,也最具“艺术满足感”。
这里我使用 PyVis 来创建交互式 网络图(PyVis 是用于网络图可视化的 Python 库[5])。
这篇 Medium 文章展示了它的易用与强大:
《Pyvis: Visualize Interactive Network Graphs in Python》 (towardsdatascience.com)
PyVis 内置了 NetworkX Helper ,可以把 NetworkX 的图直接转换 为 PyVis 可视化对象,几乎无需额外编码 ,省心省力。
回顾一下我们已准备好的映射规则:
•边权重(weight) → 用于控制边的粗细 •节点社群(community) → 用于控制节点着色 •节点度(degree) → 用于控制节点大小
将这些“花里胡哨”(bells and whistles)统统接上,我们就能得到最终的交互式概念网络图 (如图所示)。
在线体验地址 :https://rahulnyk.github.io/knowledge\_graph/
你可以随意缩放 、拖拽节点与边 ,底部还有滑块面板 可以调整图的物理效果(弹性、引力、排斥力等)。
亲自体验一下,这张图是如何帮助我们提出更精准的问题 、并更好地理解主题的!
展望与总结
我们还可以进一步探讨,如何将此图用于图增强检索(Graph Augmented Retrieval) ,并借此构建一个更强大的 RAG(检索增强生成) 管道。
不过这部分的细节就留到下次再聊吧——因为本篇的目标已经圆满完成 🎯
项目仓库
GitHub 项目地址:
rahulnyk/knowledge_graph[6]
将任意文本转换为知识图谱,可用于 Graph Augmented Generation 或基于知识图谱的问答系统。
https://medium.com/data-science/how-to-convert-any-text-into-a-graph-of-concepts-110844f22a1a
References
[1]
What is a Knowledge Graph? | IBM:https://www.ibm.com
[2]
India's Opportunity to Address Human Resource Challenges in Healthcare:https://www.cureus.com
[3]
NetworkX - 官方文档:https://networkx.org
[4]
Algorithms — NetworkX 3.2.1 文档:https://networkx.org/documentation/stable/reference/algorithms/index.html?source=post\_page-----110844f22a1a---------------------------------------
[5]
PyVis 是用于网络图可视化的 Python 库:https://github.com/WestHealth/pyvis/tree/master#pyvis---a-python-library-for-visualizing-networks
[6]
rahulnyk/knowledge_graph:https://github.com/rahulnyk/knowledge\_graph
[7]
10.7759/cureus.40274: https://doi.org/10.7759/cureus.40274