搜索“吃 苹果”时,系统需要判断哪些文档更相关。TF-IDF 和 BM25
都利用词频与文档频率,但它们处理重复词与文档长度的方式不同。
统一示例和计算口径
以下空格表示已完成分词,长度按词项数量计算:
D1:我 喜欢 吃 苹果 长度 4
D2:我 今天 吃 了 一个 苹果 长度 6
D3:苹果 是 一种 水果 啊 长度 5
查询:吃 苹果
文档总数 N=3,平均长度 avgdl=5。“吃”出现在 2 篇文档中,“苹果”出现在 3
篇文档中。自然对数记为 ln。
TF-IDF:既看出现次数,也看词的区分度
为了把例子算清楚,采用最基础的一种定义:
TF(t,d) = 词 t 在文档 d 中出现的次数
IDF(t) = ln(N / DF(t))
weight(t,d) = TF(t,d) × IDF(t)
“吃”的 IDF 为 ln(3/2)≈0.405465;“苹果”的 IDF 为
ln(3/3)=0。后者出现在每一篇文档中,在这个小语料里没有区分能力。
若仅将查询词对应的文档权重相加,D1 和 D2 均为 0.405465,D3 为
0。这是为了演示权重的简化评分,不是所有 TF-IDF 检索实现的统一公式。TF-IDF
权重说明
实际系统可能使用平滑 IDF、对数
TF、向量归一化与余弦相似度。因此,“TF-IDF
完全不考虑长度”并不准确:采用归一化的向量空间实现可以减弱长文档的影响。
BM25:重复出现的收益逐渐饱和
本文使用带正值平滑 IDF 的常见 BM25 形式:
IDF(t) = ln(1 + (N − DF(t) + 0.5) / (DF(t) + 0.5))
score(q,d) = Σₜ IDF(t) × TF(t,d) × (k₁+1)
/ [TF(t,d) + k₁ × (1−b+b×|d|/avgdl)]
取 k₁=1.2、b=0.75。“吃”的 IDF≈0.470004,“苹果”的 IDF≈0.133531。Lucene
的 BM25 文档给出了这类 IDF
和参数说明;具体引擎实现的分值可能与手算公式存在缩放差异。Lucene
BM25Similarity
| 文档 | 长度 | “吃”的贡献 | “苹果”的贡献 | 总分 |
|---|---|---|---|---|
| D1 | 4 | 0.511885 | 0.145430 | 0.657315 |
| D2 | 6 | 0.434457 | 0.123432 | 0.557890 |
| D3 | 5 | 0 | 0.133531 | 0.133531 |
D1 与 D2 的查询词频相同,但 D1 更短,关键词更集中,所以该例中 D1
得分更高。BM25 不会因为长文档“上下文更多”就自动给它加分。
两个参数怎样影响结果
k₁
控制词频饱和的程度。固定文档长度时,同一个词不断重复,收益不会无限线性增长。
b 控制长度归一化强度。b=0 时,这一项不再惩罚文档长度;b=1
时,长度调整最充分。参数是否合适,应放到自己的分词、文档切分与查询分布中验证。
| 维度 | 本文基础 TF-IDF | 本文 BM25 |
|---|---|---|
| 词频处理 | 原始 TF 线性增加 | 词频收益逐渐饱和 |
| 稀有词作用 | 通过 IDF 增强 | 通过 IDF 增强 |
| 长度处理 | 此简化例未归一化 | 显式使用文档长度和平均长度 |
| 需要先约定的细节 | TF、IDF、相似度定义 | IDF 变体、k₁、b、分词和长度口径 |
自测:为什么三篇文档都有“苹果”,它在这里的基础 TF-IDF
中权重为零,在平滑 BM25 中却仍大于零?因为两者采用了不同的 IDF
定义。