混合搜索:向量 + 关键词

当向量搜素不够用时,你可以搭配BM25关键词搜素。

混合搜索:向量 + 关键词
微信 ezpoda免费咨询:AI编程 | AI模型微调| AI私有化部署
AI模型价格对比 | AI工具导航 | ONNX模型库 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

向量搜索很强大。你将查询和文档块转换为密集Embedding,测量余弦相似度,并返回最匹配的结果。它捕获语义含义——搜索"输出电压"会找到关于"Vout规格"的块,即使没有共享关键词。

但它有一个关键的盲点。

问:"第34页上R_COMP的值是多少?"

向量搜索会找到语义相似的关于补偿、电阻和组件的块。但R_COMP是一个特定的技术符号。它在像all-MiniLM-L6-v2这样的通用模型中的Embedding可能无法准确表示它——模型从未见过这个特定IC的文档。你可能会错过 exact page。

这就是BM25的用武之地。

1、两种搜索范式

┌─────────────────────────────────────────────────────────────────┐
│                   搜索范式比较                    │
│                                                                 │
│  向量搜索(FAISS)          BM25关键词搜索             │
│  ─────────────────────          ──────────────────              │
│                                                                 │
│  查询──► Embed──► [0.2, 0.8, ...]    查询──► 分词      │
│                           │                           │         │
│  块──► Embed──► 矩阵            块──► 词频    │
│                           │                           │         │
│  余弦相似度 ◄──────┘             BM25分数 ◄┘         │
│                                                                 │
│  发现:语义相似            发现:精确术语匹配 │
│  遗漏:稀有精确术语               遗漏:改写    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
  • BM25——智能关键词搜索

BM25(最佳匹配25)是大多数经典搜索算法(包括Elasticsearch默认)的算法。它根据三个因素对文档进行排名:

  • 因素1——词频(TF)

查询词在这个块中出现多少次?更多出现=更高相关性(有递减回报)。

  • 因素2——逆文档频率(IDF)

这个词在所有块中稀有吗?稀有术语获得更高权重。像"the"、"is"、"a"这样的常见词获得接近零的权重。

  • 因素3——文档长度归一化

长块被惩罚,以防止它们仅因有更多文本而获胜。

BM25公式:

Score(query, doc) = Σ IDF(t) × [TF(t,d) × (k1+1)] / [TF(t,d) + k1 × (1 - b + b × |d|/avgdl)]

其中:

  • k1 = 1.5——控制TF饱和
  • b = 0.75——控制长度归一化
  • |d|——文档长度
  • avgdl——语料库中平均文档长度

2、仅使用单一搜索的问题

让我们用真实查询示例来具体说明:

查询:"软启动期间的静态电流是多少?"

  • 向量搜索:找到关于"启动行为"、"电流限制"、"上电序列"的块——好的语义匹配
  • BM25:"静态"可能不会出现在附近块中——差的关键词匹配
  • 获胜者:向量搜索

查询:"ISL81801 UVLO阈值是多少?"

  • 向量搜索:ISL81801是特定零件号,UVLO是特定首字母缩写。这些稀有token的Embedding可能很弱
  • BM25:找到ISL81801UVLO的精确匹配——直接关键词命中
  • 获胜者:BM25

没有一个能总是获胜。你需要两者。

3、倒数排名融合(RRF)

RRF将两个排名列表合并为一个,无需跨不同系统标准化分数。

def reciprocal_rank_fusion(rank_lists, k=60):
    scores = {}
    for rank_list in rank_lists:
        for rank, chunk in enumerate(rank_list):
            chunk_id = chunk["chunk_id"]
            # RRF分数: 1 / (k + rank_position)
            # k=60是防止排名1占主导的平滑常数
            score = 1 / (k + rank + 1)
            if chunk_id not in scores:
                scores[chunk_id] = {"chunk": chunk, "score": 0}
            # 从所有排名列表累积分数
            scores[chunk_id]["score"] += score
    # 按累积的RRF分数排序
    sorted_chunks = sorted(
        scores.values(),
        key=lambda x: x["score"],
        reverse=True
    )
    return [x["chunk"] for x in sorted_chunks]

为什么RRF有效?

示例:3个块,2个搜索系统(k=60):

FAISS 排名:         BM25 排名:
  排名1: 块A         排名1: 块B
  排名2: 块B         排名2: 块A
  排名3: 块C         排名3: 块D
RRF分数 (k=60):
  块A: 1/(60+1) + 1/(60+2) = 0.01639 + 0.01613 = 0.03252
  块B: 1/(60+2) + 1/(60+1) = 0.01613 + 0.01639 = 0.03252
  块C: 1/(60+3) + 0         = 0.01587 + 0       = 0.01587
  块D: 0        + 1/(60+3)  =       + 0.01587  = 0.01587
最终排名:
  1. 块A(并列)
  2. 块B(并列)
  3. 块C / 块D

两个列表中出现的块获得双倍积分。只在一个列表中的块仍然获得部分积分——它们不会被丢弃。

4、完整的混合检索流程

用户查询:"UVLO阈值电压是多少?"
                    │
         ┌──────────┴──────────┐
          │                     │
          ▼                     ▼
   FAISS向量搜索           BM25关键词搜索
   (top_k=5)              (top_k=5)
          │                     │
          │  [第3页]           │  [第3页]   ←两者一致
          │  [第12页]          │  [第7页]
          │  [第7页]           │  [第15页]
          │  [第19页]          │  [第19页]
          │  [第34页]          │  [第34页]  ←两者一致
          │                     │
          └──────────┬──────────┘
                     │
                     ▼
             RRF融合 (k=60)
                     │
               [第3页]   ←顶部(两者都出现,高排名)
               [第34页]  ←强(两者,中排名)
               [第7页]
               [第12页]
               [第19页]
               [第15页]
                     │
                     ▼
           Cross-encoder重排器
           (最终top-3选择)

在两个搜索运行之前,查询使用领域特定的同义词进行扩展:

SYNONYMS = {
    "input voltage":    "vin input supply voltage operating voltage",
    "output voltage":   "vout output regulation output range",
    "switching frequency": "fsw oscillator frequency",
    "efficiency":       "efficiency power conversion efficiency",
}
for key, value in SYNONYMS.items():
    if key in query:
        query += " " + value

这为BM25提供更多匹配术语,为向量编码器提供更丰富的语义输入。

5、结束语

关键洞察:混合搜索在术语查询上比纯向量搜索提供约15%的MRR改进。BM25处理那些让Embedding模型犯错的精确缩写和零件号。


原文链接:Hybrid Search: Why Vector Search Alone Is Not Enough汇智网翻译整理,转载时请标明出处