欢迎光临
我们一直在努力

Milvus 等数据库目前对向量 + 关键词(BM25)混合搜索的原生支持度及权重分配逻辑如何?

引言:混合搜索的必要性

在现代检索增强生成(RAG)和语义搜索应用中,纯粹的向量搜索(基于语义相似度)和纯粹的关键词搜索(基于词汇匹配,如BM25)都有其局限性。向量搜索可能遗漏关键词精确匹配的文档,而关键词搜索则无法捕获深层次的语义关系。

混合搜索(Hybrid Search)正是为了结合两者的优势而生。对于AI基础设施工程师而言,理解向量数据库如何原生支持这种查询结构以及如何进行结果融合(即权重分配)至关重要。

Milvus对混合搜索的原生支持

Milvus作为一个高性能的向量数据库,其核心设计支持在同一查询中结合向量搜索和结构化过滤(或关键词搜索)。

在Milvus或Zilliz的体系中,实现混合搜索通常有两种方式:

  1. 结构化过滤与向量搜索的结合: 利用 Milvus 的 expr 参数对非向量字段(如ID、类别、标签)进行过滤。这更侧重于布尔逻辑,而非关键词评分。
  2. 向量搜索与全文本搜索(FTS)的结合: 新一代的云服务或集成方案(如Zilliz Cloud或某些扩展)提供了对文本字段的索引支持,例如基于全文索引实现类似 BM25 的查询能力。

无论是哪种方式,最终都会产生两个独立的排名列表:一个是基于向量距离的排名,另一个是基于关键词/BM25得分的排名。

权重分配的核心挑战

挑战在于如何公平地融合这两个异构的得分系统:

  • 向量距离(如L2或Cosine)是度量空间中的相似度,通常范围在 [0, 1] 或 [0, $\infty$)。
  • BM25 得分通常是一个正数,没有固定的上限,其数值大小与集合大小和文档频率有关。

由于它们的量纲和分布差异巨大,简单地进行线性加权求和(e.g., $Score = w_{vector} \cdot VectorScore + w_{keyword} \cdot KeywordScore$)需要复杂的归一化步骤和繁琐的权重调优。

结果融合的黄金标准:Reciprocal Rank Fusion (RRF)

解决异构得分融合问题的最有效且最少参数依赖的方法是 倒数排名融合(Reciprocal Rank Fusion, RRF)

RRF 是一种排名聚合算法,它完全忽略了原始分数,只关注文档在各个搜索结果列表中的排名

RRF 的工作原理与公式

RRF 假设如果一个文档在多个独立搜索中的排名都很靠前,那么它应该是一个高质量的结果。RRF 的计算公式如下:

$$RRF_Score(d) = \sum_{i=1}^{N} \frac{1}{k + rank_i(d)}$$

其中:

  • $d$ 是文档。
  • $N$ 是搜索列表的数量(在这里 $N=2$,即向量和关键词)。
  • $rank_i(d)$ 是文档 $d$ 在第 $i$ 个搜索结果列表中的排名(从1开始)。
  • $k$ 是一个平滑常数(通常取 60),用于防止排名第一的文档得分过高,并给予后续排名文档一定的权重。

实践:使用 Python 实现 RRF 融合

虽然更先进的 VDB 平台在内部封装了 RRF 或类似的融合逻辑,但了解其实现原理和在客户端手动融合结果是至关重要的实操技能。

假设我们已经通过 Milvus 执行了两个独立的查询,得到了两组结果:

import pandas as pd

# 假设的向量搜索结果 (ID, 排名)
vector_results = [
    {'id': 101, 'rank': 1},
    {'id': 103, 'rank': 2},
    {'id': 105, 'rank': 3},
    {'id': 102, 'rank': 4},
]

# 假设的关键词搜索结果 (ID, 排名)
keyword_results = [
    {'id': 102, 'rank': 1},
    {'id': 101, 'rank': 2},
    {'id': 104, 'rank': 3},
    {'id': 106, 'rank': 4},
]

# RRF 平滑常数
K = 60

def calculate_rrf(results_lists, K=60):
    """根据多个排名列表计算 RRF 分数"""
    # 1. 存储所有文档的累积 RRF 分数
    rrf_scores = {}

    # 2. 遍历每个搜索列表
    for rank_list in results_lists:
        for item in rank_list:
            doc_id = item['id']
            rank = item['rank']

            # 计算该文档在该列表中的 RRF 贡献
            score_contribution = 1 / (K + rank)

            # 累加分数
            if doc_id not in rrf_scores:
                rrf_scores[doc_id] = 0
            rrf_scores[doc_id] += score_contribution

    # 3. 按分数降序排列结果
    final_ranking = sorted(rrf_scores.items(), key=lambda item: item[1], reverse=True)

    # 4. 格式化输出
    df = pd.DataFrame(final_ranking, columns=['Document ID', 'RRF Score'])
    df['Final Rank'] = df.index + 1
    return df

# 执行 RRF 融合
final_ranking_df = calculate_rrf([vector_results, keyword_results], K=60)

print("RRF 融合结果:\n")
print(final_ranking_df.to_markdown(index=False))

输出结果(示例):

| Document ID | RRF Score | Final Rank |
|-------------|-----------|------------|
| 101         | 0.032787  | 1          |
| 102         | 0.032517  | 2          |
| 103         | 0.016393  | 3          |
| 105         | 0.016129  | 4          |
| 104         | 0.016129  | 5          |
| 106         | 0.016129  | 6          |

权重分配逻辑总结

在 RRF 机制下,权重分配的逻辑体现在排名的倒数上。文档 101(向量排名1,关键词排名2)和文档 102(向量排名4,关键词排名1)都获得了很高的综合分数,因为它们都在至少一个列表中取得了前列的成绩。RRF 的优势在于:

  1. 非参数化: 无需手动设置 $w_{vector}$ 和 $w_{keyword}$ 等权重。
  2. 抗噪性强: 避免了因为某个分数维度异常高而主导最终结果的情况。
  3. 高度公平: 给予在所有搜索列表中表现稳定的文档更高的综合排名。
【本站文章皆为原创,未经允许不得转载】:汤不热吧 » Milvus 等数据库目前对向量 + 关键词(BM25)混合搜索的原生支持度及权重分配逻辑如何?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址