稀疏检索与 BM25
稀疏嵌入与稠密嵌入有什么不同?
- 渊源: 稀疏嵌入(Sparse Embedding)根植于传统信息检索,核心是精确的关键词匹配;
- 形态: 文档被表示为极高维度的向量,绝大多数维度为零,只有文档中出现的词汇对应维度有非零值;
- 理论基石: 词袋模型(Bag of Words, BoW)——把文本看作“装满词的袋子”,只关心哪些词出现、出现几次,完全忽略词序;
- 后果: “猫追狗”和“狗追猫”在词袋模型中完全相同;
- 演进: 在词袋之上逐步发展出更复杂的词项加权与排序算法;
TF-IDF 的核心直觉与公式是什么?
- 直觉: 一个词在当前文档中出现得越多、在整个语料库中越少见,它对检索越重要;
- 例子: 100 篇文章中 60 篇包含“模型”,只有 3 篇包含“蒸馏”,则“蒸馏”更能区分哪些文章真正与“模型蒸馏”相关;
- 公式:
TF-IDF(t, d) = TF(t, d) × IDF(t),其中IDF(t) = ln(N / DF(t)),DF(t)是包含该词的文档数,N是文档总数; - 局限一: 原始词频随出现次数线性增长,出现 10 次会得到出现 5 次的两倍词频;
- 局限二: 没有校正文档长度,长文档容易仅因字数更多而获得高分;
BM25 如何修正 TF-IDF?
- 定位: 保留 IDF 对稀有词的加权,同时引入词频饱和与长度归一化;
- 评分公式:
- IDF 公式:
IDF_BM25(t) = ln((N - DF(t) + 0.5) / (DF(t) + 0.5)); - 与 TF-IDF 的差别: 分子从文档总数
N换成“不含该词的文档数N - DF(t)”,比值直接反映“不含该词的文档是含它的文档的多少倍”;分子分母各加 0.5 做平滑; - 已知代价: 当一个词出现在超过半数文档中(
DF(t) > N/2)时 IDF 取值会变成负数,实现中通常给它设一个下限; - 参数 : 控制词频饱和速度,使重复出现的边际贡献逐渐降低——一个词出现 10 次通常不会比出现 5 次贡献整整两倍;
- 参数 : 控制长度归一化强度,使相同词频在较长文档中权重更低、不同长度文档更公平地比较;
从零实现 BM25 的实验揭示了什么?
- 教学步骤: 文本预处理(分词并去除“的”“了”这类几乎不携带检索价值的停用词)→ 构建倒排索引 → 计算 TF 与 IDF;
- 倒排索引(Inverted Index): 从词到文档的反向映射表——普通索引是“给定文档,列出它包含的词”,倒排索引是“给定一个词,立刻找到所有包含它的文档”;
- 类比: 好比书后的术语索引页,查“TCP”就告诉你第 45、112、203 页提到了它;
- 示例语料:
N=10篇文档,k1=1.5、b=0.75、avgdl=250词; - 运行日志:
查询分词: ["模型", "蒸馏"]
词 "模型" → 倒排索引命中 3 篇文档 (df=3, IDF=ln((10−3+0.5)/(3+0.5))=0.76):
doc_1: TF=5, 文档长度=200词, BM25贡献=1.52
doc_3: TF=2, 文档长度=500词, BM25贡献=0.82
doc_7: TF=8, 文档长度=150词, BM25贡献=1.68
词 "蒸馏" → 倒排索引命中 2 篇文档 (df=2, IDF=ln((10−2+0.5)/(2+0.5))=1.22, 比"模型"更稀有):
doc_1: TF=3, 文档长度=200词, BM25贡献=2.15
doc_5: TF=1, 文档长度=250词, BM25贡献=1.22
最终排序: doc_1 (3.67) > doc_7 (1.68) > doc_5 (1.22) > doc_3 (0.82)
- 关键观察: doc_1 中“蒸馏”的词频(TF=3)低于“模型”(TF=5),但因 IDF 更高,对得分的贡献(2.15)反而超过“模型”(1.52)——这正是 BM25 的核心逻辑;
- 关键观察: doc_1 同时命中两个查询词、总分 3.67 遥遥领先,印证了多词命中对排序的叠加效应;
- 优势: 凭借精确的关键词匹配,在技术代码、人名等查询上表现极佳;
- 局限: 读不懂同义表达——查一个词只能匹配到字面相同的文档,对策见 混合检索与重排序;