利用层次聚类来提升知识检索的性能
从大型数据集中检索信息是具有挑战性的,尤其是当共享的概念跨越多个来源而没有明确的链接的时候。假设有一堆想要查询的文档,并且需要可靠的的软件来从这些文档中检索相关数据。然而,随着所拥有的文档数量大大增加,以至于我们不知道如何引导大模型找到那些包含答案的文档。
更糟糕的是,跨文档的相关概念在回答的问题时可以提供相当多的信息,例如: 假设在你的文件1中说,A是B的老板,而在文件23 中说,在A的公司里,有 2 个雇员。在文档108 中提到张三和李四有同一个老板。你问软件 “列出A的所有员工”,我们的软件怎么知道查看文档108 和23 以及文档1,而A的名字甚至没有在前 2 个文档中提到过!
1. 一般的解决方案
在典型的检索增强生成(RAG)流程中,系统首先将文档内容分割为多个文本块(chunks),然后通过嵌入模型将这些文本块转换为向量表示,并存储在一个称为“索引”的结构中。当用户提出问题时,系统也会将问题转化为向量,并计算其与索引中所有向量的相似性,从而检索出最相关的内容作为上下文,最终输入给大语言模型用于生成答案。
然而,在面对复杂查询场景时,这种标准的 RAG 方法可能会遇到一些挑战。
例如,当我们有五个不同来源、不同类型的信息文档,而用户的问题需要从中综合提取信息时,就会面临一个关键问题:是否应将所有文档统一向量化并构建一个单一索引?如果这样做,虽然实现简单,但检索过程会把所有文档视为一个整体,缺乏结构化的语义区分,导致难以从每个文档中精准获取相关信息。结果往往是只能检索到部分相关内容,而遗漏了其他必要的信息源。
另一种思路是为每个文档单独建立索引,并分别进行检索,之后将结果汇总传递给 LLM。这种方法确实在一定程度上提高了检索的准确性,尤其是在文档间语义差异较大的情况下。然而,它也带来了明显的可扩展性问题——当文档数量增加到数百甚至上千时,逐一检索不仅效率低下,而且增加了系统的复杂性和资源消耗。
因此,真正的问题核心在于:我们如何让检索器智能地定位到与当前问题最相关的文本块子集?
换句话说,我们需要一种更高级的检索策略,能够根据问题的内容动态路由到正确的知识片段集合。这可能涉及引入元数据标签、主题分类、混合检索策略(如关键词 + 向量联合检索)、或使用专门的路由模型来识别哪些文档或块最有可能包含所需信息。
2. 当前的解决方案
解决上述问题的关键在于提升检索阶段的“语义理解”能力,使其不仅仅依赖于向量相似性的匹配,而是具备一定的逻辑判断和上下文感知能力,从而实现更高效、准确的知识提取与整合。
近年来,一种日益流行的解决方案是构建一个独立的概念嵌入图(embedding graph)。这种图结构专门用于建模文本中实体和概念之间的关系,不再仅仅将文档切分为文本块进行独立索引,而是通过图的形式捕捉不同文档内部及彼此之间提及的实体之间的关联。这样一来,检索的目标不再是孤立的文本片段,而是蕴含丰富语义关系的图结构中的一部分。
然而,即使是对仅有五个文档构建的单一图索引,在实际应用中也面临可扩展性挑战。随着图中节点和边数量的增长,遍历整个图以收集相关上下文并进行跨文档推理的计算开销会迅速上升。当图中包含成千上万的节点和边时,这种查询方式将变得效率低下,难以满足实时或准实时检索的需求。
为应对这一挑战,我们需要在正式开始检索之前,有效地缩小搜索空间。换句话说,我们希望对相关的文本块(即图中的节点)进行合理的聚类或分组,使得后续的检索算法能够聚焦于图中高度相关的区域,而非在整个图中盲目搜索。这种方法不仅能显著提升检索效率,还能增强结果的相关性和语义连贯性。
因此,未来的检索系统不仅要关注如何表示信息,更要思考如何组织和索引这些信息,使其既保留语义结构,又具备高效的访问路径。这将是推动 RAG 系统迈向更高智能水平的重要一步。
3. 进一步的解决方案
正如人类大脑通过强化神经通路来优化记忆检索一样,一种有效的策略是通过实现层次聚类来简化信息检索过程。这种方法不仅能够提高检索效率,还能增强结果的相关性和语义一致性。
此方法分为两个主要步骤:
社区检测(第一级聚类)
首先进行的是社区检测,即第一级聚类。在这个阶段,系统使用类似于 K 近邻(K-Nearest Neighbors, KNN)的算法,基于主题或上下文的紧密度对文本块进行初步分组。这些初步形成的簇被称为“社区”,每个社区内的节点通常共享相似的主题或内容特征。例如,在一个包含多个文档的知识库中,所有讨论特定技术的文章段落可能会被归入同一个社区。
超级社区检测(第二级聚类)
接下来是超级社区检测,即第二级聚类。在此步骤中,系统会分析第一级社区的核心节点或中心点,并根据更高层次的主题相似性将这些社区进一步聚合为更大的群组——“超级社区”。这种高级别的聚类有助于识别出跨多个社区的共同主题或概念,使得检索时可以快速定位到最相关的知识区域,而不是在细粒度的单个社区内逐一搜索。
通过这种方式,层次聚类不仅能够有效地减少检索空间,提升查询速度,还能够在保持高精度的同时提供更加连贯和全面的答案。这种方法模仿了人类认知过程中从具体细节到抽象概念的理解路径,使得机器学习模型能够更智能地理解和处理复杂的信息结构。最终,这种策略为构建高效、可扩展的知识检索系统提供了坚实的基础。
3.1 社区探测的实现方法
为了有效缩小图检索的搜索空间,我们可以结合使用kNN(k-最近邻)算法和Leiden 社区发现算法,分别实现文本块的相似性连接和社区结构划分。以下是具体实现步骤:
首先,借助Tavily等工具,我们可以获取多个文档,并将其分割为大小适中的文本块,以便后续处理。
接下来,使用sentence-transformer模型(如all-MiniLM-L6-v2)将每个文本块嵌入到一个高维向量空间中,从而将其语义信息转化为可计算的数值表示。
在这一过程中,每一个文本块都会被表示为图中的一个节点,并标记为Chunk类型。
随后,应用k-Nearest Neighbors (k-NN) 算法,根据向量之间的余弦相似度,为每个文本块找到最相近的 4 个邻居,并在它们之间建立连接。这些连接以图中的边表示,并统一标记为SIMILAR,用于表达块与块之间的语义相关性。
通过上述步骤构建出的图结构,不仅保留了文本内容的局部相似性,还为后续使用 Leiden 算法进行更高级别的社区划分打下了基础。这种分层组织方式有助于显著缩小检索范围,使系统能够更高效地定位与查询最相关的知识区域。
# Pseudo-visual of the first-level community detection nbrs = NearestNeighbors(n_neighbors=4, metric="cosine") nbrs.fit(X) # X is the array of embeddings distances, indices = nbrs.kneighbors(X) # Build edges based on similarity edges = [] for i in range(len(X)): for j_idx, dist in zip(indices[i], distances[i]): if i != j_idx: similarity = max(0, 1 - dist) edges.append((i, j_idx, similarity))使用 igraph 库,脚本应用 Leiden 算法在所有 Chunk 节点中查找第一级社区。在 Neo4j 中,为每个节点分配一个社区标签。
# Run Leiden g = igraph.Graph(n=len(X), edges=[(i, j) for i, j, _ in edges], directed=False) g.es['weight'] = [w for _, _, w in edges] partition = leidenalg.find_partition( g, leidenalg.RBConfigurationVertexPartition, weights=g.es['weight'], resolution_parameter=1.0 ) community_labels = partition.membership在完成文本块的图结构构建后,下一步是赋予每个节点和社区更具语义可读性的标签,以便后续检索时能够更直观、高效地定位信息。
具体而言,大语言模型会为每一个文本块节点生成一个简短而具有描述性的名称。这个名称概括了该块的核心内容,使图中的节点不再只是抽象的标识符,而是具备实际意义的语义单元。
在一级社区(First-level Community)形成之后,LLM 会进一步综合该社区内所有文本块的内容,生成一个更高层次的主题性命名。例如,“足球偶像”、“摇滚乐队”等名称就是通过对社区内部共性主题的归纳得出的摘要式标签。这些名称不仅有助于人类理解社区内容,也为后续的自动化处理提供了结构化的语义基础。
当一级社区被成功识别并命名后,我们往往会发现:某些社区之间存在明显的主题相似性。比如,“足球偶像” 和 “世界杯历史” 都属于体育领域。这自然引发了一个问题:我们是否可以将这些相关的一级社区进一步聚类,形成更高层级的组织结构?
答案是肯定的——这就是“超级社区(Super-Community)”的概念。超级社区作为多个相关一级社区的上层聚合,起到了一种“分类伞”的作用,能够将语义相近的社区归为一类,如“体育世界”或“音乐文化”。
引入超级社区机制后,整个图结构就形成了一个多层次、等级化的知识组织体系。这种结构极大地优化了检索效率——系统在进行查询时,首先会在超级社区或一级社区层级进行剪枝,快速锁定最相关的区域,再深入到具体的文本块中提取信息。
这样一来,即使是跨文档、跨社区的信息整合也变得更加高效。通过层级化过滤,检索器能够在保证准确性的前提下显著减少搜索范围,从而提升整体响应速度与推理质量。这种分层检索策略,正是实现大规模 RAG 系统智能化、可扩展化的关键一步。
3.2 方案特点
通过将嵌入向量聚类为社区(Community)和超级社区(Super-Community),我们能够显著缩小检索器在处理查询时需要考察的节点范围,从而大幅提升查询解析的速度与系统的整体可扩展性。这种分层聚类机制不仅优化了检索效率,还为构建更智能、更结构化的知识图谱奠定了基础。
为了增强图结构的可解释性和可用性,我们可以利用大语言模型(LLM)为每一个文本块(Chunk)、每个社区以及超级社区生成简洁而富有语义的名称。例如,“足球偶像”、“世界杯历史”或“摇滚乐队”等标签,使得用户即使面对大规模的知识网络,也能轻松理解其内容结构,并进行高效导航。
这种“人性化”的命名方式不仅提升了用户体验,也为后续构建基于语义的代理路由器(Agent Router)提供了可能。该路由器可以建立在检索器之上,根据查询的主题自动定位到相关的社区或超级社区,从而进一步压缩搜索空间。
无论用户的查询是局限于单个社区内部,还是涉及多个超级社区之间的信息整合,整个检索流程都能保持高度精简,有效节省运行时间和计算资源。
构建一阶(一级社区)和二阶(超级社区)的图结构,本质上是对知识空间的一种语义化组织方式。它不仅大幅减少了潜在的检索范围,更为实现快速响应、高并发、可扩展的 RAG 系统打下了坚实基础。
更重要的是,这种基于主题相似性的分层聚合机制,显著提高了从海量数据中获取正确上下文以回答问题的可能性。换句话说,系统不仅能更快地找到答案,还能更准确地找到那个答案。
因此,这一方法不仅是性能优化的关键,更是迈向智能化、语义驱动的检索系统的重要一步。
4. 小结
结合一级社区发现和二级超级社区形成的两步解决方案为限制搜索空间,特别是多文档和多领域环境下的搜索空间提供了一种稳健的方法。查询不是在一个单一的整体索引中搜索所有嵌入或所有关系,而是从基于主题统一性创建的图和超级社区中获取。这种方法平衡了性能和语义深度,确保了速度和准确性!
PS: 如果你正在写一本书,如果你也认可“大家帮助大家”,我们欢迎你的加入。如果你想读一本好书, 我相信,这里作译者们推广的图书中应该会有你喜欢的那一本。作译者互助群活动——
【参考资料与关联阅读】
https://github.com/DenizAskinIBM/Knowledge-Graph
大模型应用的10种架构模式
7B?13B?175B?解读大模型的参数
万字揭秘:生成式AI浪潮中的架构模式
抽象的进化:AgentOps
拆解OpenAI最大对手的杀手锏:为什么会是MCP?
智能体间协作的"巴别塔困境"如何破解?解读Agent通信4大协议:MCP/ACP/A2A/ANP
大模型应用系列:从Ranking到Reranking
大模型应用系列:Query 变换的示例浅析
从零构建大模型之Transformer公式解读
如何选择Embedding Model?关于嵌入模型的10个思考
解读文本嵌入:语义表达的练习
解读知识图谱的自动构建
“提示工程”的技术分类
大模型系列:提示词管理
提示工程中的10个设计模式
解读:基于图的大模型提示技术
大模型微调:RHLF与DPO浅析
Chunking:基于大模型RAG系统中的文档分块
大模型应用框架:LangChain与LlamaIndex的对比选择
解读大模型应用的可观测性
大模型系列之解读MoE
在大模型RAG系统中应用知识图谱
面向知识图谱的大模型应用
让知识图谱成为大模型的伴侣
如何构建基于大模型的App
Qcon2023: 大模型时代的技术人成长(简)
论文学习笔记:增强学习应用于OS调度
《深入浅出Embedding》随笔
LLM的工程实践思考
大模型应用设计的10个思考
基于大模型(LLM)的Agent 应用开发
解读大模型的微调
解读向量数据库
解读向量索引
解读ChatGPT中的RLHF
解读大模型(LLM)的token
解读提示词工程(Prompt Engineering)
解读Toolformer
解读TaskMatrix.AI
解读LangChain
解读LoRA
解读RAG
大模型应用框架之Semantic Kernel
浅析多模态机器学习
大模型应用于数字人
深度学习架构的对比分析
老码农眼中的大模型(LLM)
