来源:互联网 更新时间:2026-08-25 14:05
在大语言模型火起来之前的很长一段时间里,信息检索这个领域其实主要靠TF-IDF和BM25这类方法撑着。别看它们资历老,效果经过了时间的检验,至今依然很能打。到了大模型时代,一个常见的做法是把BM25这种稀疏检索和向量检索结合起来,取长补短,往往能带来检索效果的明显提升。

为什么叫“稀疏检索”?简单来说,是因为文档和查询是用稀疏向量来表示的。这种向量的维度有多大呢?通常等于所有文档中不重复词(也就是词表)的总数,其中绝大部分维度都是0,所以叫“稀疏”。与之对应的,是深度学习时代流行的Word Embedding、向量模型产出的“稠密向量”。稠密向量的维度通常只有几百或几千,而且几乎所有维度上都有非零值——跟TF-IDF、BM25那种动辄上万维的向量比起来,确实算得上低维了。
需要特别提醒一下。用Langchain默认的BM25检索器处理中文,效果会非常惨。一个常见的坑是:做项目时没单独检查一下稀疏检索的表现,直接上了混合检索,调整权重后觉得最终效果比纯向量检索好了一点点就收工了。实际上,语义检索的优势确实明显,混合检索能有小幅提升也算合理。但事后仔细一分析才发现,默认参数下的BM25在中文场景下根本就不可能有好效果。具体原因,在核心代码部分会详细解释。
这部分公式比较多,如果不喜欢看公式,可以直接跳到代码部分,不影响后续使用。
提前说明一下:下面提到的“文档”,在我们的场景里指的就是知识片段,而“语料库”则对应所有文档片段的集合。
BM25是一个用来评估查询和文档相关性的重要函数,全称是Best Matching 25。它的评分公式如下:
(公式略)
其中:
这里顺带提一下TF-IDF,因为BM25正是在它的基础上改进而来的。TF-IDF的公式为:
(公式略)
其中,TF(q_i, D)是词q_i在文档D中的占比,比如一篇文档共20个词,“数据”出现了3次,那么TF就是3/20。IDF的公式同样是:IDF(q_i) = log((N - n(q_i) + 0.5) / (n(q_i) + 0.5) + 1)(注:部分实现中略有差异,但核心思路一致)。
简单解释一下TF-IDF在做什么。它衡量的是某个词在某个文档中的重要性。其实你也能猜到,如果只看TF,那么得分最高的肯定是“的”、“了”这类停用词。引入IDF就是为了平衡这部分高频词的影响。两者一综合,像“的”、“了”这些词的得分自然就低了,而那些在特定文档里经常出现、又在其他文档中很少出现的词——也就是能代表这篇文档主题的词——反而会被突出出来。
相比TF-IDF,BM25的改进主要体现在以下三个方面。
(公式略)
直观理解一下:当词频比较小的时候,主导这个公式值的主要是分子中除词频之外的部分,所以随着词频增加,分数会增长。当词频大到一定程度,增长的边际收益就被分母稀释了。
用一组实验数据来说明:假设k1=2.0,b=0.75,a vgdl=20,文档初始有5个词,某个词出现了1次。之后每次往文档中增加一个同样的词,观察TF_adj的变化和增量(diff):
| TF | TF_adj | diff |
|---|---|---|
| 1 | 1.600000 | |
| 2 | 2.033898 | 0.433898 |
| 3 | 2.236025 | 0.202127 |
| 4 | 2.352941 | 0.116916 |
| 5 | 2.429150 | 0.076209 |
| 6 | 2.482759 | 0.053609 |
| 7 | 2.522523 | 0.039764 |
| 8 | 2.553191 | 0.030669 |
| 9 | 2.577566 | 0.024374 |
| 10 | 2.597403 | 0.019837 |
可以清楚看到,随着TF越来越大,diff越来越小。这就完美解释了“饱和效应”——越到后面,词频带来的提升越有限。
(公式略)
当文档比较长时,|D|较大,分母变大,从而降低了词频的影响。当文档较短时,|D|较小,分母变小,词频的影响被放大。这个标准化是通过参数b来调节的。
混合检索的流程如下图所示。整个过程分为两路:一路使用稠密向量检索,另一路使用稀疏检索(BM25),分别检索知识片段。然后对两路的检索结果,采用RRF算法(具体可参考本系列关于RAG Fusion的文章)进行重排序,截取Top N的知识片段送入大语言模型,最后由大模型结合用户问题和知识片段生成答案。
从下面的表格可以看到,单独使用中文分词后的BM25检索,或者使用混合检索(Embedding微调 + BM25),检索命中率都超过了之前所有的方案。而混合检索的整个问答流程的准确率,也达到了目前的最好成绩。
(表格数据略)
本文对应的代码已经开源,地址是:https://github.com/Steven-Luo/MasteringRAG/blob/main/retrieval/04_bm25_hybrid.ipynb
用Langchain实现BM25检索其实很简单,直接把切分好的知识库列表splitted_docs传进去就能构建检索器:
from langchain.retrievers import BM25Retriever
vanilla_bm25 = BM25Retriever.from_documents(splitted_docs)
chunks = vanilla_bm25.get_relevant_documents(question)
但前面也说过了,这样直接用,效果非常差。针对本系列构建的测试集,Top1到Top8的命中率表现如下:
| retriever | top_k | hit_rate |
|---|---|---|
| vanilla_bm25 | 1 | 0.000000 |
| vanilla_bm25 | 2 | 0.000000 |
| vanilla_bm25 | 3 | 0.032258 |
| vanilla_bm25 | 4 | 0.064516 |
| vanilla_bm25 | 5 | 0.064516 |
| vanilla_bm25 | 6 | 0.064516 |
| vanilla_bm25 | 7 | 0.064516 |
| vanilla_bm25 | 8 | 0.064516 |
原因在哪里?打开Langchain的源码(https://github.com/langchain-ai/langchain/blob/master/libs/community/langchain_community/retrievers/bm25.py)就能看到。在创建BM25检索器时,会用一个preprocess_func来处理文档列表,而如果不指定这个参数,默认使用的是default_preprocessing_func——它就是按空格来切分的。对中文来说,按空格切分,结果可想而知。
解决方法也很直接:加入中文分词器。
import jieba
chz_cut_bm25_retriever = BM25Retriever.from_documents(splitted_docs, preprocess_func=lambda text: list(jieba.cut(text)))
k = chz_cut_bm25_retriever.k = k
chz_cut_bm25_retriever.get_relevant_documents(question)
看看修改后的召回效果,正常多了:
| retriever | top_k | hit_rate |
|---|---|---|
| jieba_cut_bm25 | 1 | 0.666667 |
| jieba_cut_bm25 | 2 | 0.784946 |
| jieba_cut_bm25 | 3 | 0.838710 |
| jieba_cut_bm25 | 4 | 0.870968 |
| jieba_cut_bm25 | 5 | 0.870968 |
| jieba_cut_bm25 | 6 | 0.870968 |
| jieba_cut_bm25 | 7 | 0.870968 |
| jieba_cut_bm25 | 8 | 0.870968 |
创建混合检索器的方式如下:
from langchain.retrievers import BM25Retriever, EnsembleRetriever
chz_cut_bm25_retriever = BM25Retriever.from_documents(splitted_docs, preprocess_func=lambda text: list(jieba.cut(text)))
def get_ensemble_retriever(k, weights=[0.5, 0.5]):
chz_cut_bm25_retriever.k = k
return EnsembleRetriever(
retrievers=[vector_db.as_retriever(search_kwargs={"k": k}), chz_cut_bm25_retriever],
weights=weights
)
ensemble_retriever = get_ensemble_retriever()
ensemble_retriever.get_relevant_documents(question)
这里有一个需要调节的超参数:稀疏检索和向量检索各自的权重。针对本系列的数据,我们对不同的权重配比进行了实验,不同权重下的检索性能对比如下图所示:
(图略)
腾讯ima怎么把微信内容一键导入知识库?
腾讯ima怎么创建共享知识库?
Celestia价格预测2026-2032:TIA币能否引领山寨币上涨行情?历史价格回顾
新浪互联网热点小时报丨2026年07月26日16时_今日实时互联网热点速递
比特币(BTC)核心周期指标复刻历史走势 价格或跌破5.8万美元关键支撑位
比特币 2025 年价格预测:BTC 的未来走势
新浪机器学习热点小时报丨2026年07月25日18时_今日实时机器学习热点速递
WorkBuddy微信版怎么获得积分?
新浪人工智能热点小时报丨2026年07月30日18时_今日实时人工智能热点速递
短剧《史上最强洪荒修为》剧情介绍
海尔消毒柜自动消毒如何中止
5000元起的鼠标哪个最值得入手?
男生高性价比充电头?
博世壁挂炉关闭暖气怎么操作
腾讯ima知识库怎么分类管理?
车载冰箱重置到出厂设置几步?
Windy卫星云图怎么看?云层变化识别技巧
5000-6000元鼠标有什么推荐?
管线机怎么接云米净水器
笔记本移动电源推荐哪款?
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc