ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

RAG 检索结果去重与多样性保障:基于 MMR(最大边际相关性)算法实战

RAG 检索结果去重与多样性保障:基于 MMR(最大边际相关性)算法实战 RAG 检索结果去重与多样性保障基于 MMR最大边际相关性算法实战在 RAG检索增强生成系统的向量检索阶段标准的 Top-K 向量相似度检索有一个极易引发回答质量退化的算法盲区“信息茧房与同质化冗余Redundancy Cannibalization”。假设用户提出了一个具有广度的问题“请全面介绍分布式微服务高可用架构的核心设计维度。”向量数据库中排在最前面的前 5 个分块可能全部来自于同一篇文档关于“令牌桶限流算法”的连续段落虽然这 5 个分块与用户提问的余弦相似度都极高如 0.89, 0.88, 0.87...但它们在语义上高度重合这直接挤占了 Top-5 的宝贵名额导致关于“熔断降级”、“超时传播”和“双机容灾”等其他关键维度的分块被彻底挤出最终大模型生成的回答极其狭隘偏颇只能就“限流”喋喋不休完全无法覆盖用户的宏观问题。为了在“与 Query 的相关性Relevance”和“候选结果之间的多样性Diversity”之间取得完美的动态平衡信息检索领域最经典的神级算法就是MMRMaximal Marginal Relevance, 最大边际相关性。今天我们深入拆解 MMR 算法的底层向量距离数学公式并给出基于 NumPy 的生产级高性能实现代码。一、MMR 算法的数学公式与动态贪心选择模型flowchart TD CandidateSet[第一阶段召回的候选分块池: 包含 20 个候选] -- MMR_Loop[MMR 贪心迭代循环 (选择 Top-K 个)] subgraph MMR_Formula [MMR 边际相关性核心得分计算] ScoreCalc[$MMR \arg\max_{d_i \in R \setminus S} \left[ \lambda \cdot \text{Sim}_1(d_i, Q) - (1 - \lambda) \cdot \max_{d_j \in S} \text{Sim}_2(d_i, d_j) \right]$] end MMR_Loop -- ScoreCalc ScoreCalc -- Pick[每一轮挑选边际得分最高的分块加入已选集合 S] Pick -- UpdateDiversity[已选集合 S 扩充 - 惩罚与其语义接近的其他候选分块!] UpdateDiversity -- MMR_Loop MMR_Loop -- 选满 K 个 -- FinalList[输出兼具极高相关性与丰富多样性的 Top-K 结果]核心公式参数物理含义$$\text{MMR}(d_i) \lambda \cdot \text{CosSim}(d_i, \text{Query}) - (1 - \lambda) \cdot \max_{d_j \in S} \text{CosSim}(d_i, d_j)$$$\lambda \in [0, 1]$多样性平衡超参数。当 $\lambda 1.0$ 时完全退化为传统的纯相关性排序当 $\lambda 0.0$ 时完全追求多样性差异最大化工业级生产推荐值$\lambda 0.5 \sim 0.7$在保证 70% 相关性的同时强力驱逐 30% 的同质化冗余。$\max_{d_j \in S} \text{CosSim}(d_i, d_j)$冗余惩罚项。计算当前候选分块与已经入选集合 $S$ 中所有分块的最大相似度。如果当前分块和已经入选的分块长得太像该惩罚项会大幅拉低其最终得分从而让其他新维度的分块脱颖而出二、生产级 Python NumPy 高性能 MMR 算法代码实现import numpy as np from typing import List, Dict, Any class MaximalMarginalRelevanceReranker: def __init__(self, lambda_mult: float 0.6): :param lambda_mult: 平衡因子 (0.0 完全多样性, 1.0 完全相关性, 默认 0.6 黄金比例) self.lambda_mult lambda_mult def _cosine_similarity(self, vec_a: np.ndarray, vec_b: np.ndarray) - float: norm_a np.linalg.norm(vec_a) norm_b np.linalg.norm(vec_b) if norm_a 0 or norm_b 0: return 0.0 return float(np.dot(vec_a, vec_b) / (norm_a * norm_b)) def _cosine_similarity_matrix(self, matrix_a: np.ndarray, matrix_b: np.ndarray) - np.ndarray: 批量计算两个向量矩阵之间的余弦相似度矩阵 norm_a np.linalg.norm(matrix_a, axis1, keepdimsTrue) norm_b np.linalg.norm(matrix_b, axis1, keepdimsTrue) norm_a np.where(norm_a 0, 1e-10, norm_a) norm_b np.where(norm_b 0, 1e-10, norm_b) return np.dot(matrix_a / norm_a, (matrix_b / norm_b).T) def rerank_mmr( self, query_vector: List[float], candidate_chunks: List[Dict[str, Any]], candidate_vectors: List[List[float]], top_k: int 5 ) - List[Dict[str, Any]]: 核心 MMR 重新排序与去重筛选 if not candidate_chunks or top_k 0: return [] q_vec np.array(query_vector, dtypenp.float32) doc_vecs np.array(candidate_vectors, dtypenp.float32) n_candidates len(candidate_chunks) top_k min(top_k, n_candidates) # 1. 预先计算所有候选分块与 Query 之间的余弦相似度 Sim(Doc, Query) query_sims self._cosine_similarity_matrix(doc_vecs, q_vec.reshape(1, -1)).flatten() # 2. 预先计算候选分块相互之间的相似度矩阵 Sim(Doc_i, Doc_j) inter_doc_sim_matrix self._cosine_similarity_matrix(doc_vecs, doc_vecs) selected_indices: List[int] [] unselected_indices list(range(n_candidates)) # 3. 贪心迭代挑选 Top-K 个最优分块 for step in range(top_k): best_score -float(inf) best_idx -1 if not selected_indices: # 第一轮直接挑选与 Query 相关性最高的第一个分块 best_idx int(np.argmax(query_sims)) else: # 后续轮次计算 MMR 得分并选择最大者 for candidate_idx in unselected_indices: # 项 1: 与 Query 的相关性 relevance query_sims[candidate_idx] # 项 2: 与已选集合中所有分块的最大冗余相似度 max_redundancy np.max(inter_doc_sim_matrix[candidate_idx, selected_indices]) # MMR 核心打分计算 mmr_score self.lambda_mult * relevance - (1.0 - self.lambda_mult) * max_redundancy if mmr_score best_score: best_score mmr_score best_idx candidate_idx selected_indices.append(best_idx) unselected_indices.remove(best_idx) # 4. 组装最终结果 final_results [] for rank, idx in enumerate(selected_indices, start1): chunk candidate_chunks[idx].copy() chunk[mmr_rank] rank chunk[query_sim] round(float(query_sims[idx]), 4) final_results.append(chunk) print(f[✓] MMR 多样性重排完成: 从 {n_candidates} 个候选筛选出 {len(final_results)} 个兼具相关性与多样性的黄金上下文) return final_results三、真实业务对比实测同质化击退效果验证我们来看在真实微服务知识库中的对比测试召回算法策略选出的 Top-3 分块主题分布大模型最终回答的宏观完整度传统纯向量相似度 Top-31. 令牌桶限流算法 (相似度 0.89)2. 令牌桶代码实现 (相似度 0.88)3. 漏桶与令牌桶对比 (相似度 0.87)极其狭隘大模型整篇回答只讲了限流完全遗漏了熔断、超时与容灾MMR 多样性重排 ($\lambda0.6$)1. 令牌桶限流算法 (相关性 0.89)2. Sentinel 熔断降级滑动窗口 (相关性 0.82)3. 双机热备与秒级容灾 (相关性 0.79)⭐ 完美覆盖大模型全面系统地回答了高可用的三大核心支柱答题深度拉满四、生产治理选型准则粗筛候选集大小设为 Top-K 的 3 到 5 倍要挑选 Top-5第一阶段向量检索先拉取15~25 个候选分块送入 MMR 矩阵计算仅需 1 毫秒针对问答型 Query 采用 $\lambda0.7$针对探索调研型 Query 采用 $\lambda0.5$精准事实提问保持高相关宏观调研加大多样性权重结合 Qdrant 原生 MMR 支持Qdrant 数据库在服务端已经原生支持 MMR 检索在调用客户端时直接传入search_params即可实现服务端硬件级加速。把 MMR 算法作为向量检索后的标配过滤器RAG 系统的上下文信噪比与视野广度才能真正达到企业级交付的顶级水准。
返回列表