作者:Rahul Nayak
来源:https://medium.com/@rahul.nyk
编译:活水智能
几个月前,基于知识的问答(KBQA)还是一个新奇事物。
现在,对于任何AI爱好者来说,带检索增强生成(RAG)的KBQA就像小菜一碟。看到自然语言处理(NLP)的可能性领域由于大型语言模型(LLMs)的发展而如此迅速扩展,真是令人着迷。
而且,它每天都在变得更好。
在我上一篇文章中,我分享了一种递归RAG方法,用于实现具有多跳推理的问答,以回答基于大量文本语料库的复杂查询。
很多人尝试了这种方法并发送了他们的反馈。感谢大家的反馈。自那以后,我整理了这些反馈,并对代码进行了一些改进,以解决原始实现的问题。我计划写一篇单独的文章来讨论。
在这篇文章中,我想分享另一个想法,当与递归RAG结合使用时,它可能有助于创建一个超级研究代理。
这个想法源于我对较小LLMs进行递归RAG的实验,以及我在Medium上读到的其他想法——特别是知识图谱增强生成。
知识图谱(KG)或任何图谱由节点和边组成。知识图谱的每个节点代表一个概念,每条边是这样一对概念之间的关系。
在这篇文章中,我将分享一种将任何文本语料库转换成概念图的方法。我在这里将“概念图”(GC)一词与知识图谱这个术语交替使用,以更好地描述我在这里演示的内容。
我在这个实现中使用的所有组件都可以在本地设置,因此这个项目可以轻松地在个人机器上运行。我在这里采用了无GPT方法,因为我相信较小的开源模型。
我正在使用出色的Mistral 7B Openorca instruct和Zephyr模型。这些模型可以通过Ollama在本地设置。
(编者注:Mistral 7B是一个基于GPT架构的大型语言模型,专门优化以理解和执行用户指令,适用于多种自然语言处理任务)
像Neo4j这样的数据库使得存储和检索图形数据变得容易。在这里,我使用内存中的Pandas数据框和NetworkX Python库,以保持简单。
我们的目标是将任何文本语料库转换成概念图(GC)并像本文的精美横幅图像一样进行可视化。我们甚至将通过移动节点和边缘、放大和缩小以及改变图的物理特性来与网络图进行交互,以满足我们的心愿。
这是Github页面链接,展示了我们正在构建的结果:
https://rahulnyk.github.io/knowledge_graph/
但首先,让我们深入了解知识图谱的基本概念以及我们为什么需要它们。如果你已经熟悉这个概念,请跳过下一节。
考虑以下文本:
玛丽有一只小羊羔,
你以前听说过这个故事;
但你知道她传递了她的盘子,
又多吃了一点!
(我希望孩子们没有读到这个😝)
这是将文本表示为知识图谱的一种可能方式。
本文作者使用draw.io创建的图表
IBM在一篇文章中恰当地解释了知识图谱的基本概念:
知识图谱,也称为语义网络,代表了现实世界实体——即对象、事件、情境或概念——之间的网络,并展示了它们之间的关系。这些信息通常存储在图形数据库中,并以图形结构的形式可视化,因此得名知识“图谱”。
知识图谱在多种方式上都很有用。我们可以运行图算法并计算任何节点的中心性,以了解一个概念(节点)对于整个作品的重要性。
我们可以分析概念的连接集和断开集,或计算概念社区以深入理解主题。我们可以理解看似不相关概念之间的联系。
我们还可以使用知识图谱来实现图形检索增强生成(GRAG或GAG),并与我们的文档进行聊天。
这可以给我们带来比老旧版本的RAG更好的结果,后者有几个缺点。例如,仅使用简单的语义相似性搜索来检索与查询最相关的上下文并不总是有效的。
特别是当查询没有提供足够的上下文来说明其真实意图,或者当上下文跨越大量文本片段时。
例如,考虑这个查询——
告诉我 _《百年孤独》 书中关于José Arcadio Buendía家族史的信息。_
这本书记录了7代人的家族史,其中一半的角色都叫。如果使用简单的RAG管道,即使可能,回答这个查询也将是相当的挑战。
RAG的另一个缺点是它不能告诉你该问什么。很多时候,提出正确的问题比得到答案更重要。
图形增强生成(GAG)可以在一定程度上解决RAG的这些缺点。更好的是,我们可以混合搭配,构建一个图形增强检索增强生成管道,以获得两全其美的效果。
所以现在我们知道图形很有趣,它们可以非常有用,它们看起来也很美。
如果你问GPT如何从给定文本创建知识图谱?它可能会建议如下过程:
从作品中提取概念和实体。这些是节点。
提取概念之间的关系。这些是边。
在图形数据结构或图形数据库中填充节点(概念)和边(关系)。
可视化,至少是为了视觉上的满足感。
第3步和第4步听起来可以理解。但你如何实现第1步和第2步?
这是我设计的从任何给定文本语料库中提取概念图的方法流程图。它类似于上述方法,但有一些小的不同:
1. 将文本语料库分割成块。为每个块分配一个chunk_id。
2. 对于每个文本块,使用LLM提取概念及其语义关系。我们给这种关系分配一个W1的权重。同一对概念之间可以有多重关系。每一种关系都是一对概念之间的边。
3. 考虑在同一文本块中出现的概念,也通过它们的上下文邻近性相关联。让我们为这种关系分配一个W2的权重。请注意,同一对概念可能出现在多个块中。
4. 将相似的对分组,合计它们的权重,并连接它们的关系。因此,现在我们在任何不同的概念对之间只有一条边。这条边具有一定的权重,并带有一系列关系作为其名称。
如下图所示:
本文作者使用draw.io创建的图表 / 活水智能 编译
你可以在本文分享的GitHub仓库中,查看Python代码。
让我们在接下来的几节中简要介绍实现的关键方法。
为了在这里演示该方法,我使用了在PubMed/Cureus上发表的文章(编者注:文章见文末),文章遵循CC BY版权使用协议。
上述流程图的第1步很简单。Langchain提供了大量的文本分割器,我们可以使用它们将文本分割成块。
第2步是真正有趣的开始。为了提取概念及其关系,我使用了Mistral 7B模型。在确定最适合我们目的的模型变体之前,我尝试了以下几种:
Mistral Instruct Mistral OpenOrca Zephyr(从Mistral衍生的Hugging Face版本)
我使用了这些模型的4位量化版本——这样我的Mac就不会开始讨厌我了——通过Ollama在本地托管。
这些模型都是经过指令调整的模型,带有系统提示和用户提示。如果我们告诉它们,它们都能很好地遵循指令,并整齐地以JSON格式格式化答案。
经过几轮尝试和错误后,我最终选择了Zephyr模型 和以下提示:
SYS_PROMPT = ( "你是一个网络图制作者,从给定的上下文中提取术语及其关系。"
"你被提供了一个上下文块(由
限定)你的任务是提取所给上下文中提到的术语的本体。” “这些术语应该根据上下文代表关键概念。\n”
“想法1:在遍历每个句子时,思考其中提到的关键术语。\n” “\t术语可能包括对象、实体、位置、组织、人物、\n”
“\t条件、首字母缩写词、文档、服务、概念等。\n” “\t术语应尽可能原子化\n\n” “想法2:思考这些术语如何与其他术语一对一地关联。\n”
“\t通常在同一句子或同一段落中提到的术语彼此相关。\n” “\t术语可以与许多其他术语相关\n\n”
“想法3:找出每对这样相关的术语之间的关系。\n\n” “将你的输出格式化为json列表。列表的每个元素包含一对术语” “和它们之间的关系,如下所示:\n”
“[\n” “ {\n” ‘ “node_1”: “从提取的本体中提取的一个概念”,\n’ ‘ “node_2”: “从提取的本体中提取的相关概念”,\n’
‘ “edge”: “两个概念之间的关系,node_1和node_2用一两个句子描述”\n’ “ }, {…}\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" }, . . .]
注意,它甚至猜到了“食物”作为一个概念,这在文本块中并没有明确提到。这不是很棒吗!
如果我们将这个过程应用于我们示例文章的每个文本块,并将json转换为Pandas数据框,这是它的样子。
这里的每一行代表一对概念之间的关系。每行是我们图中两个节点之间的一条边,同一对概念之间可以有多条边或关系。
上述数据框中的计数是我任意设置为4的权重。
我假设在文本语料库中彼此靠近出现的概念是相关的。我们称这种关系为“上下文邻近性”。
为了计算上下文邻近性边缘,我们将数据框融化,使得node_1和node_2合并成一个列。
然后我们使用chunk_id作为键对这个数据框进行自连接。因此,具有相同chunk_id的节点将彼此配对,形成一行。
但这也意味着每个概念也会与自己配对。这称为自环,其中边缘起始和结束于同一个节点。为了移除这些自环,我们将从数据框中删除node_1与node_2相同的每一行。
最终,我们得到一个与原始数据框非常相似的数据框。
这里的count列是node_1和node_2一起出现的块数。列chunk_id是所有这些块的列表。
所以我们现在有两个数据框,一个带有语义关系,另一个带有文本中提到的概念之间的上下文邻近性关系。我们可以将它们结合起来形成我们的网络图数据框。
我们已经完成了为我们的文本构建概念图的工作。但在这一点上就结束将是一项相当不令人满意的练习。
我们的目标是像本文开头的特色图像一样可视化图表,而我们离目标不远了。
NetworkX是一个使处理图变得非常容易的Python库。
如果你还不熟悉这个库,请点击链接了解更多:https://networkx.org/?source。
将我们的数据框添加到NetworkX图中只需要几行代码:
G = nx.Graph()## Add nodes to the graphfor node in nodes:
G.add_node(str(node))## Add edges to the graphfor index, row in
dfg.iterrows(): G.add_edge( str(row["node_1"]), str(row["node_2"]),
title=row["edge"], weight=row['count'] )
这是我们可以开始利用网络图的力量的地方。NetworkX为我们提供了大量现成的网络算法供我们使用。
这是我们可以在图上运行的算法列表的链接:https://networkx.org/documentation/stable/reference/algorithms/index.html。
在这里,我使用社区检测算法为节点添加颜色。社区是一组节点,它们彼此之间的连接比与图的其余部分更紧密。概念社区(Communities of concepts)可以让我们很好地了解文本中讨论的广泛主题。
Girvan Newman算法在我们正在处理的审查文章中,检测到了17个概念社区。这是其中一个概念社区。
[ 'digital technology', 'EVIN', 'medical devices', 'online training
management information systems', 'wearable, trackable technology']
这立即让我们了解到审查论文中讨论的健康技术的广泛主题,并使我们能够提出问题,然后我们可以用我们的RAG管道来回答。这不是很棒吗?
让我们也计算图中每个概念的度数。节点的度数是它连接的边的总数。所以在我们的案例中,概念的度数越高,它对我们文本的主题就越中心。我们将在可视化中使用度数作为节点的大小。
可视化是这个练习中最有趣的部分。它有一种特质,能给你带来艺术上的满足感。
我使用PiVis库来创建交互式图表。Pyvis是一个用于可视化网络的Python库。
Pyvis内置了一个NetworkX助手,可以将我们的NetworkX图转换为PyVis对象。所以我们不需要更多的编码……耶!!
记住,我们已经计算了每条边的权重(边的粗细),节点的社区(颜色),以及每个节点的度(大小)。
所以,有了所有这些花哨的东西,这是我们的图。
复制链接打开,即可看到交互式知识图谱:https://rahulnyk.github.io/knowledge_graph/
我们可以随意放大和缩小,移动节点和边缘。页面底部还有一个滑块面板,可以改变图的物理特性。看看图表如何帮助我们提出正确的问题,更好地理解主题内容!
我们可以进一步讨论我们的图如何帮助我们构建图形增强检索,以及这如何帮助我们构建更好的RAG管道。但我认为最好留到另一天。
我们已经实现了本文的目标!
相关资料
我使用了该文章来演示我的代码:Saxena S G, Godfrey T (June 11, 2023) India’s Opportunity to Address Human Resource Challenges in Healthcare. Cureus 15(6): e40274. DOI 10.7759/cureus.40274
Pyvis库介绍:https://towardsdatascience.com/pyvis-visualize-interactive-network- graphs-in-python-77e059791f01