前情提要

折腾了两周,写写对 RAG 中检索阶段的一些实验总结。

数据集的选择:本实验采用 C-MTEB/T2Retrieval 数据集,以及 C-MTEB/T2Retrieval-qrels 是其对应的问题-答案对。

核心指标:recall@5、recall_cap@5 用于评判召回质量,因为一个问题对应原始文档过多,所以下文都将以 recall_cap@5 为主要参考指标,其中 cap=5。

其他指标:mrr@5、ndcg@5、precision@1 用于评判排序质量。

Embedding 模型:BAAI/bge-m3 可以同时输出 sparse 和 dense 向量,在训练时两个向量有互补,所以查找时效果也会更好,实测混合检索用 sparse 替代 bm25 可以提升 ~3%。

Reranker 模型:BAAI/bge-reranker-v2-m3 很好的模型。

实验结果

下面是 C-MTEB/T2Retrieval 数据集的一些测试结果,指标说明在图下方,测试数据库为 Milvus,原始语料库 ~12w 条,分块后存入数据库 ~36w 条数据,测试时使用前 2000 条 query 测试:

其中各个环境说明如下:

  • bm25:通过 bm25 检索

  • embedding:通过 bge 生成的 dense 向量检索,数据库索引为 HNSW

  • sparse:通过 bge 生成的 sparse 向量检索

  • fixed_*:混合检索后通过固定权重聚合 0.6*dense + 0.4*sparse(milvus会在聚合时对分数进行归一化

    为什么叫 fixed,当然是因为还有 dynamic,后续会讲

  • rrf_*:混合检索后通过 rrf 聚合,k=60

  • rerank_*:rrf 混合检索后再经过 rerank 模型重排序,重排的候选集为 50 条

实验分析

目前在实验中表现最好的方案为使用 sparse+dense 混合检索,rrf 聚合后通过 reranker 重排,整套方案有提升的部分如下:

  • 使用混合检索替代单独 dense embedding 检索可提升 ~10%
  • 混合检索使用 sparse 替代 bm25 可提升 ~3%
  • 扩大检索召回数据量,增加重排序,最高可提升 ~5%

其中 rerank 部分需要注意:增加了 rerank 后,耗时从 ~60ms 增加到 ~360ms,各项指标均提升 ~3%(sparse+dense+rrf+rerank 方案为例),整个 RAG 系统中这个耗时可以接受,所以引入 rerank 是值得的。另外需要注意的是:

  • 增加了 rerank 后,用 sparse 代替 bm25 的优势减小。因为召回池变大,rerank 模型效果也不错,完全可以把好答案排在前面
  • 在混合检索时,按照权重混合的方式和rrf的效果差不多,甚至会更好一些,但是分别增加了 rerank 后,权重的效果(提升<1%)反而不如 rrf。猜测权重方式会更偏向于 dense 向量的结果,候选池同质化严重,这会导致 rerank 发挥不了太大作用,而 rrf 更加均衡,候选池更加多元化,reranker 的作用更大,所以效果提升更好

综上,从之前的实验以及分析可见,给 bm25+dense+rrf 加 rerank 最能体现出 rerank 提升的效果。

最终结论,以单纯的 dense 向量检索为 baseline,本实验通过增加 sparse 混合检索和 rerank,将 recall_cap@5 从 68% 提升到了 81.2%,ndcg@5 提升了10个百分点,mrr@5 和 precision@1 也各提升了5个百分点。

一些思考

尝试过提升指标的方法:

  • 优化数据分块方法。开始使用固定 chunk 大小+overlap 的方式分块,效果不太好(不过后面发现了是使用的数据集有问题)。现在最终版实验是采用了 递归分块+固定 chunk 大小+overlap 的方式,即:优先根据段落、句末标点分块,无法分割的再回退到简单切割的版本。

  • 调参。混合检索时可以调整权重或者rrf的k值,经过测试来看,默认的 0.6/0.4 和 60 效果最好。

  • 增大 Rerank 候选池。因为引入了重排序,于是思考扩大候选池是否对结果有提升。答案是:有的,但是会有边际效益。当候选集从 10 增大到 50 时,会有正向提升,但是 50 增大到 200 甚至 500 时,时延增加了十几倍,指标反而下降了。由此可见,过多的候选集不仅会增大模型的计算负担,过多的噪声还会让模型迷惑。

  • Dynamic weight 聚合:原本想过是否可以通过计算句子复杂度动态调整权重,语气词/连词/长词越多则 dense 权重更大,但实际测试效果不如 fixed,因为有些专业词语虽然是长词,但是显然更应该用 bm25 这种稀疏向量去硬匹配

  • Query 扩展:试过本地起一个 Qwen/Qwen3.5-35B-A3B 模型用更加标准的句子对query进行扩展以更好地命中数据库中的数据,例子如下:

    原句为:

    我想知道现在大模型在机器翻译中是怎么使用的?可以结合实际例子说明吗?

    扩展为:

    • 我想知道现在大模型在机器翻译中是怎么使用的?可以结合实际例子说明吗?
    • 大语言模型在机器翻译中的应用方式及案例分析
    • 当前大型预训练模型如何用于机器翻译,有哪些实际案例
    • 机器翻译任务中集成大模型的技术路径与实例说明

    但是实际测试中效果并未提升太多(<1%),同时因为引入了大模型,耗时从 ~360ms 飙升至了 ~70s,这对系统的影响将是巨大且不可接受的。

未尝试但或许对指标有提升的方法:

  • 最终结果不完全依靠 reranker 的分数,将 reranker 的分数和原始分数加权相加,根据这个加权后的最终分数再排序。听起来比较有道理,当为了降低时延使用了效果较差的 rerank 模型时,感觉可以使用此方法弥补一些 rerank 模型对语义理解的偏差。
  • 优化数据分块。可以使用更好的数据分块方法,如通过模型使用语义分块,增加章节结构按主题分块(这种方法对书籍比较友好)等等。不过之前也有试过用语义分块,结果看起来也并没有太好,有些比较长的专业术语反而会分开。
  • 依旧引入大模型:
    • 让大模型提取问题关键词用于关键词匹配
    • 让大模型先生成一个预设答案(HyDE),连着问题带答案去检索
    • 数据入库时使用大模型对对应 chunk 上下文进行一个总结,后续检索排序时都带上这个总结
    • 一些其他思考:是否可以用自有数据对一个小模型进行微调,既能保证生成速度不会太慢,又能让生成内容更好地命中数据库。但这需要考虑各种成本,对于我们这小项目来讲无论是训练还是推理成本都太高,ROI 太低
  • 领域内微调。将领域内的专业名词聚合成一个词表,用于提高分词和 bm25 检索的准确度。或者是用领域内数据对 embedding、rerank 模型微调,同样因为成本问题没有尝试,但是想来应该也会有一些提升。

后续实验

后来用比较新的 jinaai/jina-embeddings-v5-text-smalljinaai/jina-reranker-v3 两个模型又跑了一次实验,因为 jina 没有生成 sparse 向量,所以使用 bm25 代替,最终结果最好的方案为 bm25+dense+rrf+rerank,并且结果与上面用 bge 模型的结果相同,recall_cap@5 均为 81.2%。

在对这次实验结果分析后,我有了一个猜想:bge-reranker-v2-m3 的性能或许要比 jina-reranker-v3 要好,于是使用 jina-embedding 与 bge-reranker 配合重新又跑了一次实验,最终得出了本次的最高分:82.2%,详细实验结果如下: