有意义的实体在自由运行的情况下,LLM提取的实体类别可能过于多样化。它错误地将抽象概念标记为实体。例如,在“比尔博·巴金斯庆祝他的生日并将戒指留给弗罗多”这段文本中,LLM可能会提取“比尔博·巴金斯庆祝他的生日”或“庆祝他的生日”作为“动作”。但如果它提取“生日”作为“事件”,可能更有用。一致的实体它也可能在不同的上下文中错误地标记相同的实体。例如:‘Sauron’、‘the Dark Lord Sauron’和‘the Dark Lord不应该被提取为不同的实体。或者如果它们被提取为不同的实体,它们应该通过等价关系连接。解析的弹性LLM的输出本质上是不可预测的。要从大文档中提取知识图谱,我们必须将语料库拆分成较小的文本块,然后为每个块生成子图。要构建一致的图谱,LLM必须根据给定的模式一致地输出JSON对象,即使丢失一个也可能会对整个图谱的连接性产生不利影响。尽管LLMs在生成格式良好的JSON对象方面变得越来越好,但仍然远非完美。具有有限上下文窗口的LLMs也可能生成不完整的响应。实体的分类LLMs在识别实体时可能会大量出错。当上下文是特定领域时,或者当实体未用标准英语命名时,这个问题更为严重。NER模型在这方面可能表现更好,但它们也仅限于它们接受训练的数据。此外,它们不能理解实体之间的关系。迫使LLM在类别上保持一致是一种提示工程的艺术。隐含关系关系可以是明确提到的,也可以是由上下文暗示的。例如:“比尔博·巴金斯庆祝他的生日并将戒指留给弗罗多”暗示了以下关系:比尔博·巴金斯 → 拥有者 → 戒指比尔博·巴金斯 → 继承人 → 弗罗多弗罗多 → 拥有者 → 戒指我认为,LLMs在某些时候将比任何传统方法更好地提取关系。但目前,这仍是一个需要巧妙提示工程的挑战。Graph Maker我在这里分享的Graph Maker库,通过在严格性和易用性之间,以及在结构化和非结构化之间找到平衡,改进了之前的方法。在应对上述大多数挑战方面,它比我之前讨论的方法表现得更好。与之前的方法不同,在之前的方法中,LLM可以自行发现本体,而Graph Maker试图强制LLM使用用户定义的本体。我们可以通过一个简单的pip命令安装知识图谱生成器库:
pip install knowledge-graph-maker
使用给定的本体从任意文本中创建知识图谱以下是它的工作方式,分为五个简单步骤。这些步骤已经编码在一个笔记本中,我将在本文末尾分享。1. 定义图谱的本体该库理解以下本体的模式。在幕后,本体是一个pydantic模型。ontology = Ontology( labels=[ {"Person": "没有任何形容词的人的名字,记住一个人可能会用名字或代词引用"}, {"Object": "在对象名称中不要添加定冠词'the'"}, {"Event": "涉及多个人的事件。不要包括限定词或动词,如给予、留下、工作等。"}, "Place", "Document", "Organisation", "Action", {"Miscellaneous": "任何无法分类为其他给定标签的重要概念"}, ], relationships=[ "实体对之间的关系", ],)
我已经调整了提示,使其生成与给定本体一致的结果。我认为它在这方面做得相当不错。然而,它仍然不是100%准确。准确性取决于我们选择的生成图谱的模型、应用、本体和数据质量。2.将文本拆分成块我们可以使用尽可能大的文本语料库来创建大型知识图谱。然而,LLMs目前有一个有限的上下文窗口。所以我们需要适当地将文本分块,并一次创建一个块的图谱。我们应该使用的块大小取决于模型的上下文窗口。这个项目中使用的提示大约占用500个token。其余的上下文可以分为输入文本和输出图谱。根据我的经验,较小的200到500个token的块会生成更详细的图谱。3.将这些块转换成文档文档是一个pydantic模型,具有以下模式: ## Pydantic文档模型class Document(BaseModel): text: str metadata: dict
我们在这里添加到文档的元数据将标记到从文档中提取的每个关系中。我们可以将关系的上下文,例如页码、章节、文章名称等添加到元数据中。通常,每对节点在多个文档中有多个关系。元数据有助于将这些关系情境化。4.运行Graph MakerGraph Maker直接接受文档列表,并遍历每个文档,为每个文档创建一个子图。最终输出是所有文档的完整图谱。以下是实现此目标的简单示例。from knowledge_graph_maker import GraphMaker, 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)## -> 从文档列表中创建图谱。graph = graph_maker.from_documents(docs)## 结果:边的列表。print("边的总数", len(graph))## 1503
Graph Maker通过LLM运行每个文档,并解析响应以创建完整的图谱。最终的图谱是边的列表,其中每个边都是一个pydantic模型,如下所示。class Node(BaseModel): label: str name: str class Edge(BaseModel): node_1: Node node_2: Node relationship: str metadata: dict = {} order: Union[int, None] = None
我已经调整了提示,使其现在生成相当一致的JSON。如果JSON响应解析失败,图谱生成器还会尝试手动将JSON字符串拆分成多个边字符串,然后尽可能地恢复。5.保存到Neo4j我们可以将模型保存到Neo4j,以创建RAG应用程序、运行网络算法,或者只是使用Bloom[3]可视化图谱。from knowledge_graph_maker import Neo4jGraphModelcreate_indices = Falseneo4j_graph = Neo4jGraphModel(edges=graph, create_indices=create_indices)neo4j_graph.save()
图谱的每条边都作为一个事务保存到数据库中。如果您是第一次运行此代码,请将create_indices设置为true。这将通过在节点上设置唯一性约束来准备数据库。5.1 可视化,只是为了好玩在上一篇文章中,我们使用networkx和pyvis库对图谱进行了可视化。在这里,由于我们已经将图谱保存到Neo4J,我们可以直接利用Bloom进行可视化。为了避免重复,让我们生成一个与上一篇文章不同的可视化。假设我们想看看书中角色之间的关系是如何演变的。我们可以通过跟踪图谱生成器遍历书籍时如何逐渐添加边来实现这一点。为了实现这一点,Edge模型有一个名为“order”的属性。这个属性可以用于为图谱添加时间或时间顺序维度。在我们的示例中,图谱生成器自动将特定文本块在文档列表中出现的顺序号添加到它从该块提取的每条边中。因此,要查看角色之间的关系如何演变,我们只需按边的顺序截取图谱。这里有一个这些截面的动画。动画由作者生成图谱与RAG这种知识图谱的最佳应用可能是在RAG(图谱检索增强生成)中。有很多文章讨论了如何用图谱增强RAG应用程序。本质上,图谱提供了多种不同的知识检索方式。根据我们设计图谱和应用程序的方式,其中一些技术可能比简单的语义搜索更强大。最基本的是,我们可以将嵌入向量添加到节点和关系中,并对向量索引运行语义搜索进行检索。然而,我觉得图谱在RAG应用程序中的真正力量在于将Cypher查询和网络算法与语义搜索混合使用。我自己也在探索其中的一些技术。我希望在我的下一篇文章中写到这些内容。代码这里是GitHub仓库。请随意试用。我还在仓库中包含了一个示例Python笔记本,可以帮助你快速入门。请注意,在开始之前,你需要在.env文件中添加你的GROQ凭证。GitHub - rahulnyk/graph_maker[4]另外,如果你觉得可以为这个开源项目做出贡献,请这样做并使其成为你自己的项目。我希望你觉得图谱生成器有用。感谢阅读。更多信息本文由三人行AI花费较多时间翻译整理,更多详细内容见:https://medium.com/towards-data-science/text-to-knowledge-graph-made-easy-with-graph-maker-f3f890c0dbe8,如对您有帮助,请帮忙点赞、转发、关注。References[1] arxiv.org: https://arxiv.org[2] 来源: https://arxiv.org/abs/2403.11996[3] Bloom: https://neo4j.com/product/bloom/[4] GitHub - rahulnyk/graph_maker: https://github.com/rahulnyk/graph_maker(图片来源网络,侵删)
0 评论