💡 一个使用 Llama 3、Mixtral 等开源 LLM 从文本语料构建知识图谱的开源库
在本文中,我将分享一个 Python 库——Graph Maker ,它能根据给定本体(Ontology)从文本语料中创建知识图谱。Graph Maker 使用 Llama3、Mistral、Mixtral 或 Gemma 等开源 LLM 来抽取知识图谱。
我们将了解 Graph Maker 的“为什么(Why)”和“是什么(What)”,简要回顾上一篇文章,以及当前方法如何解决其中的一些挑战。文章末尾会附上 GitHub 仓库链接。
相关文章:
基于知识图谱的RAG——如何用固定的实体架构在Knowledge Graph上进行RAG检索
从非结构化文本到互动知识图谱的构建:使用大语言模型(LLMs)
用 OpenAI Functions 从文本构建知识图谱实战篇
前言
下面是一张简明阐释知识图谱核心思想的示意图:
来源:(https://arxiv.org/abs/2403.11996)
构建知识图谱(KG)需两大要素:
1.知识库(Knowledge Base) •文本语料•代码库•文章集合•…2.本体(Ontology)
定义我们关注的实体类别及它们之间的关系类型。示例:•实体 :Person
、Place
•关系 :•Person — related to → Person
•Person — lives in → Place
•Person — visits → Place
有了上述两部分信息,就可以从提到“人”和“地点”的文本中构建知识图谱。
示例
如果我们的知识库是关于“处方药及其相互作用”的临床研究,那么本体可能包括:•Compound (化合物)•Usage (用法)•Effects (效果)•Reactions (反应)
虽然这种方法不及传统构建方式那样严谨,但它能:
•更灵活地处理非结构化数据;•生成更丰富的信息;•非常适合图检索增强生成 (Graph Retrieval Augmented Generation,GRAG)类应用。
为什么要使用 Graph Maker?
让我列举一些我在上一篇文章中收到的反馈中的挑战和观察,这有助于我们理解使用 LLM 构建知识图谱时遇到的问题。我们以《指环王》书籍的维基百科摘要为例——毕竟谁能不爱《指环王》呢!
有意义的实体
•问题 :在无任何约束时,LLM 往往会将过于宽泛或抽象的短语识别为实体。例如,文本中出现 “Bilbo Baggins celebrates his birthday and leaves the Ring to Frodo”(比尔博·巴金斯庆祝生日并将魔戒留给佛罗多),LLM 可能会将 “Bilbo Baggins celebrates his birthday” 或 “Celebrates his birthday” 标记为 Action
(动作)。•期望 :但如果它能提取 “Birthday”(生日)并将其归为 Event
(事件),或许更有用。
实体的一致性
•问题 :LLM 也可能在不同上下文中对同一实体进行不同标注。例如:Sauron
、the Dark Lord Sauron
、 the Dark Lord
。•期望 :它们不应被当作不同实体提取。如果确实被识别为不同实体,也应在图中通过等价关系(equivalence)将它们关联起来。
解析的鲁棒性
•问题 :LLM 输出具有非确定性,且上下文窗口有限。 a.我们需要先将大文档拆分成小块,再分别为每块生成子图。 b.要保证最终全局图谱的一致性,每个子图的 LLM 响应都必须严格符合预定义的 JSON 模式。 c.任何字段的缺失——即便是一处——都可能破坏图的连通性。
尽管 LLM 在生成规范 JSON 方面已有显著进步,但仍会出现格式错误或信息遗漏,尤其是在使用上下文窗口较小的模型时。
•期望 :即使出现解析失败,也能恢复部分边数据,保证图谱连通性。
实体的分类
•问题 :LLM 在识别实体时容易出错。对于领域特定的上下文,或实体名称不符合标准英语时,这个问题尤为严重。传统的命名实体识别(NER)模型在这方面表现更好,但也受限于训练数据,而且它们无法理解实体之间的关系。•期望 :要让 LLM 在类别上一致地输出结果,通过 Prompt 工程,引导 LLM 坚守用户定义的类别。
隐含关系
关系可以是显式提到的,也可以通过上下文隐含提取。例如:
“Bilbo Baggins celebrates his birthday and leaves the Ring to Frodo” 隐含了以下关系:
•Bilbo Baggins → 拥有者(Owner)→ Ring•Bilbo Baggins → 继承人(heir)→ Frodo•Frodo → 拥有者(Owner)→ Ring
我认为,未来 LLM 在关系抽取方面终会超越任何传统方法,但目前仍需要巧妙的 Prompt 工程来解决这一挑战。
Graph Maker 库
我在此分享的 Graph Maker 库,通过在严谨与灵活、结构化与非结构化之间取一个平衡点,显著改进了之前的方法,并在上述大多数挑战上表现更佳。
与之前让 LLM 自行发现本体不同,Graph Maker 会强制 LLM 使用用户定义的本体。
我们可以通过以下简单的 pip 命令安装知识图谱构建器:
pip install knowledge-graph-maker
以下就是它的五个简易使用步骤:
下面的步骤都已在本文末尾附带的 Jupyter Notebook 中实现。
1. 定义图谱本体(Ontology)
Graph Maker 库支持如下格式的本体定义,底层使用 Pydantic 模型:
from knowledge_graph_maker importOntology
ontology =Ontology(
# 要抽取的实体标签。可以是字符串,也可以是包含描述的字典。
labels=[
{"Person":"Person name without any adjectives, Remember a person may be referenced by their name or using a pronoun"},
{"Object":"Do not add the definite article 'the' in the object name"},
{"Event":"Event event involving multiple people. Do not include qualifiers or verbs like gives, leaves, works etc."},
"Place",
"Document",
"Organisation",
"Action",
{"Miscellaneous":"Any important concept can not be categorised with any other given label"},
],
# 你关心的关系类型,作为 LLM 的提示引导,模型会优先提取这些关系。
# 虽无法完全保证只提取这些关系,但大多数模型都能较好地遵循。
relationships=[
"Relation between any pair of Entities",
],
)
我已经调优了 Prompt,使其输出能与给定本体保持一致,效果相当不错。不过准确率仍未达到 100%,它会受到所选模型、应用场景、本体设计和数据质量的影响。
2. 将文本拆分为若干块
我们可以使用任意规模的语料来生成大型知识图谱,但目前 LLM 的上下文窗口有限。因此,需要将文本按模型上下文窗口大小切分,然后逐块构建子图。
•本项目所用的 Prompt 大约占用 500 个 token;•剩余部分既要放在输入文本中,也要容纳输出的图谱;•实践中,将文本切分为 200–500 token 的小块,往往能生成更为详尽的图谱。
3. 将文本块转换为文档(Document)
文档在底层由 Pydantic 模型定义,其模式如下:
from pydantic importBaseModel
classDocument(BaseModel):
text: str
metadata: dict
•text
:文本块内容•metadata
:与本段落中提取的每条关系绑定的上下文信息,如页码、章节名、文章标题等。
通常,同一对节点在不同文档中可能会出现多条关系,
metadata
有助于为这些关系提供背景语境。
4. 运行 Graph Maker
Graph Maker 接收一个 Document
列表,对每个文档生成一张子图,最后汇总为整体图谱。
下面是一个简单示例:
from knowledge_graph_maker importGraphMaker,Ontology,GroqClient
# 选择 Groq 支持的模型
model ="mixtral-8x7b-32768"
# 也可选:
# model = "llama3-8b-8192"
# model = "llama3-70b-8192"
# model = "gemma-7b-it" # 速度最快,但准确度略低
# 初始化 Groq 客户端
llm =GroqClient(model=model, temperature=0.1, top_p=0.5)
graph_maker =GraphMaker(ontology=ontology, llm_client=llm, verbose=False)
# 从 Document 列表生成图谱(返回 Edge 列表)
graph = graph_maker.from_documents(docs)
print("Total number of Edges", len(graph))
# 示例输出:Total number of Edges 1503
Graph Maker 会将每个文档通过 LLM 处理并解析响应,最终生成完整的图谱。图谱以边(Edge)列表的形式表示,每条边对应如下的 Pydantic 模型:
classNode(BaseModel):
label: str
name: str
classEdge(BaseModel):
node_1:Node
node_2:Node
relationship: str
metadata: dict ={}
order:Union[int,None]=None
•我已调优 Prompt,使其输出的 JSON 更加一致。•若 JSON 解析失败,Graph Maker 会尝试手动拆分 JSON 字符串为多条边记录,然后尽可能地恢复数据。
5. 保存到 Neo4j
可以将生成的图谱保存到 Neo4j,以便用于 RAG 应用、运行网络算法,或使用 Bloom 可视化:
from knowledge_graph_maker importNeo4jGraphModel
create_indices =False
neo4j_graph =Neo4jGraphModel(edges=graph, create_indices=create_indices)
neo4j_graph.save()
•每条边都作为一个事务写入数据库。•如果是首次运行,请将 create\_indices=True
,以在 Neo4j 中创建节点唯一性约束。
5.1 可视化(仅供演示)
在上一篇文章中,我们使用 networkx
和 pyvis
进行可视化。既然已经将图谱保存到 Neo4j,这里可以直接用 Bloom。
假设我们想观察角色关系在整本书中的演进过程,可以借助 Edge 中的 order
属性,将时间或顺序维度加入图谱。Graph Maker 在处理文本块时,会自动记录该块在文档列表中的序号到每条边的 order
字段。要查看关系演变,只需按 order
对图谱进行分段切片即可。
图谱与 RAG
此类知识图谱最理想的应用场景或许是 RAG(检索增强生成)。已有大量文章介绍如何将图谱用于强化 RAG 应用。
•多样化检索途径
图谱提供了多种不同的知识检索方式。根据图谱结构和应用设计,有些方法可能比简单的语义搜索更为强大。•向节点和关系添加 Embedding 向量
在最基础的实现中,我们可以为每个节点和关系添加向量表示,并在向量索引上执行语义搜索以进行检索。•混合 Cypher 查询与网络算法
我认为,图谱在 RAG 应用中真正的威力在于将 Cypher 查询、网络算法与语义搜索相结合。
我自己也在探索这些技术,计划在下一篇文章中分享更多实践心得。
代码示例
以下是 GitHub 仓库链接,欢迎大家试用。
GitHub 仓库:
rahulnyk/graph_maker[1]
仓库中也包含了示例 Python Notebook,可帮助你快速上手。
注意 :使用前需在项目根目录的
.env
文件中填写你的 GROQ 凭证。
如果你在项目中使用了本库,欢迎与我分享用例,我很乐意了解大家的实际需求。
希望 Graph Maker 对你有所帮助,感谢阅读!
References
[1]
rahulnyk/graph_maker: https://github.com/rahulnyk/graph\_maker
[2]
更多信息可查看:https://medium.com/data-science/text-to-knowledge-graph-made-easy-with-graph-maker-f3f890c0dbe8