快速构建知识图谱不再是难题!本文教你如何利用LLM,结合Langchain和Neo4J,轻松提取信息并构建实用知识图谱。
原文标题:几分钟掌握!用 LLM 快速构建实用知识图谱
原文作者:图灵编辑部
冷月清谈:
怜星夜思:
2、文中提到的预处理方法对于提高知识图谱的质量至关重要,除了预处理,还有哪些方法可以提升 LLM 提取信息的准确性?
3、文章中提到的 GraphRAG,除了知识图谱,还有什么其他方式可以优化 RAG 的检索策略?
原文内容
我曾经尝试过构建知识图谱,结果以失败告终——那时候还没有 LLM 这样的工具!
照片由 Unsplash 上的 detait 拍摄最初听到“知识图谱”这个词,感觉有点吓人——倒不是因为它的原理复杂,而是构建过程看起来非常繁琐。
我以前试过自己动手构建知识图谱,结果失败了。
图是表达复杂关系的最佳方式之一,被广泛应用在推荐系统、欺诈检测等领域。但让我最感兴趣的是它在信息检索上的应用潜力。
后来,我开始尝试利用知识图谱来改进 RAG(检索增强生成,Retrieval-Augmented Generation)。
事实上,构建 RAG 不一定非得用知识图谱,甚至不需要数据库。只要能从大量信息中提取出相关内容,传递给 LLM,就可以实现 RAG 的功能。
比如,你可以通过网页搜索作为信息检索策略,也可以用向量数据库的语义搜索功能来提升效果。
如果你用图数据库来检索信息,那么这种方式被称为 GraphRAG,它是知识图谱在 RAG 中的具体应用。
这篇文章并不是专门介绍 GraphRAG 的,而是讨论如何用 LLM 构建知识图谱。但既然提到,还是有必要简单说说为什么知识图谱可以作为内容存储,帮助改进 RAG(Retrieval-Augmented Generation)。
为什么 RAG 需要知识图谱?
知识图谱提供了更智能的信息检索方式,而单靠向量存储有时无法满足所有需求。
从向量存储中获取信息的主要方法是通过编码文本的语义相似度。具体流程是这样的:
我们使用一个向量嵌入模型,例如 OpenAI 的 text-embedding-3,将文本转换为向量表示。即使 “Apple” 和 “Appam”(一种印度食物)在字母上有很多相同之处,它们的向量表示仍然大不相同。这些向量随后会被存储在像 Chroma 这样的向量数据库中。
在检索阶段,我们使用同样的嵌入模型对用户输入进行编码,然后通过计算距离矩阵(比如余弦相似度)从向量存储中检索信息。
这种方法是我们从向量存储中检索信息的唯一方式。如你所猜测,嵌入模型的选择在检索准确性中起着关键作用。而数据库的类型通常对准确性影响不大,但可能会涉及并发、速度等其他问题。
举个例子:
假设你有一份关于各大公司领导团队的大型文档。
如果问题是“John Doe 先生是哪个公司 CEO?”这种简单的事实性问题,向量嵌入系统可以轻松处理,因为答案通常可以直接从嵌入的文档片段中找到。
但如果问题变成“John Doe 先生和哪些人在多个董事会里一起担任董事?”这种更复杂的问题,就会比较难处理。
向量相似性检索依赖于知识库中的明确提及,只有明确提到的信息才会被检索出来。而知识图谱则能通过整体数据来推理出更多信息。
例如,最近邻搜索就需要依赖知识库中的直接提及。如果缺少这种直接总结,基于向量嵌入的系统就难以跨越多个数据源进行推理或整合信息。
相比之下,知识图谱可以在整个数据集的层面进行推理。例如,它能把“国家节点”和“战略节点”按关系靠近分组,运行一个简单的查询就能得到我们需要的信息。
现在我们理解了知识图谱的重要性,接下来让我们来看看构建一个知识图谱时会面临哪些挑战。
构建知识图谱曾经是个难题(但现在不再是了)
几年前,一位同事向我介绍了知识图谱的概念。他希望为我们所有的项目创建一个统一、可搜索的知识图谱。
在花了一个周末学习 Neo4J 后,我觉得这个工具有很大的潜力。
但当时的难点在于如何从大量的 PDF、PPT 和 Word 文档中提取节点和边(即实体和关系)。
我们尝试过一些方法,但效果不理想。我们只能手动将这些非结构化的文档转化为图数据模型。
我们曾经试图用 PyPDF2 来读取 PDF 文件并进行关键字搜索,但并没有取得很好的效果。最终,我们只能放弃这个方法,认为它“不值得花时间”。
但随着 LLM(大语言模型)的广泛应用,情况发生了变化。
在接下来的部分,我们将借助 LLM 来构建一个简单的(或许是最简单的)知识图谱。
几分钟内构建知识图谱
现在,从文本和图像中提取信息不再像过去那么困难了。
虽然处理非结构化数据还可以进一步改进,但过去几年,尤其是大语言模型(LLM)的发展,给我们带来了很多新机会。
在这一部分,我们将介绍如何使用 LLM 来构建一个简单的知识图谱,并讨论如何进一步优化,让它可以应用于企业级场景。
我们将使用 Langchain 的实验功能 LLMGraphTransformer,同时采用 Neo4J Aura 作为图数据库的云端存储解决方案。
如果你使用的是 LlamaIndex,可以尝试它的 KnowledgeGraphIndex,这是一个与我们使用的工具类似的 API。当然,你也可以选择其他图数据库,像 Neo4J 一样使用。
接下来,我们先来安装必要的依赖包。
pip install neo4j langchain-openai langchain-community langchain-experimental
在这个例子中,我们将把一份包含高管和他们所属组织等信息的列表映射到图数据库。如果你想跟着一起操作,可以在这里找到我使用的示例数据。这个数据是我自己创建的虚拟数据(当然,使用了 AI 技术)。
接下来是将非结构化文档转化为知识图谱的简单代码,实际上,这段代码非常简洁:
import os
from langchain_neo4j import Neo4jGraph
from langchain_openai import ChatOpenAI
from langchain_community.document_loaders import TextLoader
from langchain_experimental.graph_transformers import LLMGraphTransformer
graph = Neo4jGraph(
url=os.getenv("NEO4J_URL"),
username=os.getenv("NEO4J_USERNAME", "neo4j"),
password=os.getenv("NEO4J_PASSWORD"),
)
# ------------------- Important --------------------
llm_transformer = LLMGraphTransformer(
llm= ChatOpenAI(temperature=0, model_name="gpt-4-turbo")
)
# ------------------- Important --------------------
document = TextLoader("data/sample.txt").load()
graph_documents = llm_transformer.convert_to_graph_documents(document)
graph.add_graph_documents(graph_documents)
这段代码非常简单明了。
我特别强调了最关键的部分:如何构建图数据库。LLMGraphTransformer 类使用我们传入的 LLM,从文档中提取图谱数据。
你现在可以将任何类型的 Langchain 文档传入 convert_to_graph_documents
方法来提取知识图谱。数据源可以是文本、Markdown 文件、网页内容,甚至是从其他数据库查询的结果。
如果是手动操作,这项任务几年前可能需要几个月时间才能完成。
你还可以访问 Aura db 控制台来查看生成的图谱,它看起来可能是这样的:
Neo4J Aura 云端通过 LLMGraphTransformer 模块生成的图谱 — 作者截图在后台,API 使用 LLM 来提取相关信息,并构建 Neo4J 的 Python 对象来表示节点和边。
正如我们所看到的,使用提取器 API 构建知识图谱已经变得非常简单。接下来,我们讨论一下如何将其优化,做到企业级应用。
如何让知识图谱适应企业级需求
曾经我们放弃了构建知识图谱的想法,因为它太复杂了。如今,借助 LLM 的帮助,我们能够更快速、低成本地构建知识图谱。然而,刚刚创建的这个图谱并不适合用于稳定的实际应用。
虽然构建知识图谱的效率得到了极大提升,但初步生成的图谱通常还不足以满足企业级应用的需求。以下是两个关键问题及其解决方案:
1. 更好地控制图谱提取过程
如果你按照这个示例操作,你会注意到生成的图谱只包含“人物”和“组织”类型的节点。实际上,提取的内容仅限于这些人物及其所担任的公司董事职位。
注意:使用 LLM 提取图谱是一种概率性技术,结果可能与你的不同。
不过,我们可以从文本文件中提取更多信息。例如,我们可以查明每位高管曾就读的大学以及他们的工作经历。
那么,我们能否指定需要提取的实体类型和它们之间的关系呢?幸运的是,LLMGraphTransformer 类支持这一功能。
以下是另一种启动实例的方式:
llm_transformer = LLMGraphTransformer(
llm=llm,
allowed_nodes=["Person", "Company", "University"],
allowed_relationships=[
("Person", "CEO_OF", "Company"),
("Person", "CFO_OF", "Company"),
("Person", "CTO_OF", "Company"),
("Person", "STUDIED_AT", "University"),
],
node_properties=True,
)
在上面的版本中,我们明确告诉 Transformer 要查找“人物”、“公司”和“大学”类型的实体,并指定它们之间可能的关系。这是帮助 LLM 提取信息的关键步骤。
另外,第三个参数 node_properties
让 Transformer 可以获取那些没有关系的实体的所有属性。
通过明确指定节点和关系来构建的知识图谱通常更完整、准确。不过,为了确保提取的关键信息更精准,接下来的一些方法可能会有所帮助。
2. 在图谱转换前进行预处理(Propositioning)
文本本身就是一种复杂的数据形式,而编写这些文本的人思维方式更为复杂。
有时候,我们并不会把所有的内容直接表达出来。即使是正式或技术性的写作,也常常会在不同地方提到相同的概念。比如,在这篇文章中,我多次使用了“Knowledge Graph”和“KG”这两个词。
这种方式让 LLM 很难准确理解上下文。
实际上,Graph Transformer 在后台会将文本分割成多个小块,并在每一块内独立处理。因此,文本中的其他部分信息无法在分块后关联到当前的块。
例如,文章开头提到某人的职位是“首席投资官(CIO)”,但在分割文本时,这个定义可能会在后续块中丢失。此时,LLM 无法理解“CIO”中的“I”究竟是指投资、信息还是其他。
为了解决这个问题,我们使用了预处理。在对文本进行分块或处理之前,我们先进行预处理,确保每一块都能被正确理解。
下面是实现这一目标的方法。
obj = hub.pull("wfh/proposal-indexing")
# You can explore the prompt template behind this by running the following:
# obj.get_prompts()[0].messages[0].prompt.template
llm = ChatOpenAI(model="gpt-4o")
# A Pydantic model to extract sentences from the passage
class Sentences(BaseModel):
sentences: List[str]
extraction_llm = llm.with_structured_output(Sentences)
# Create the sentence extraction chain
extraction_chain = obj | extraction_llm
# Test it out
sentences = extraction_chain.invoke(
"""
On July 20, 1969, astronaut Neil Armstrong walked on the moon .
He was leading the NASA's Apollo 11 mission.
Armstrong famously said, "That's one small step for man, one giant leap for mankind" as he stepped onto the lunar surface.
"""
)
>>['On July 20, 1969, astronaut Neil Armstrong walked on the moon.',
"Neil Armstrong was leading NASA's Apollo 11 mission.",
'Neil Armstrong famously said, "That\'s one small step for man, one giant leap for mankind" as he stepped onto the lunar surface.']
)
>>['On July 20, 1969, astronaut Neil Armstrong walked on the moon.',
"Neil Armstrong was leading NASA's Apollo 11 mission.",
'Neil Armstrong famously said, "That\'s one small step for man, one giant leap for mankind" as he stepped onto the lunar surface.']
这段代码使用了 Langchain 提供的提示(prompt)模板。从结果来看,每个文本块都是自我解释的,即使不参考其他文本块,LLM 也能独立理解。
在创建知识图谱之前先进行这种预处理,能够帮助 LLM 避免因失去参考信息而丢失节点或关系。
最后的思考
我曾经尝试过构建知识图谱,但没有成功。对企业或团队而言,构建知识图谱的好处远远不值付出的巨大努力。不过,那时 LLM 还没有出现。
当我得知 LLM 可以从纯文本中提取图谱信息,并将其存储到像 Neo4J 这样的数据库时,我非常震惊。但这些实验性的功能仍然不够完善。
我认为它们目前还不适合直接用于生产环境,至少需要进行一些调整和优化。
在这篇文章中,我介绍了两种帮助我提升知识图谱构建质量的技术。希望对你也有所帮助!
学习技术不孤单,扫一扫入群,一起进步~