稀缺即正义:搜索引擎的评分哲学

孙博 1 次阅读 技术分享
搜索引擎 TF-IDF BM25

经济学里有个概念叫"稀缺性"——用来衡量价值。黄金比铁贵,钻石比玻璃贵。

但在搜索技术中,"稀缺性"却能带来另外一种重要作用——消除歧义

想想看:为什么有些词一搜就能精准定位,有些词一搜却冒出一堆无关结果?

答案就藏在"稀缺性"里。


从一个直觉问题开始

搜"拙政园"时,我们立刻就能想到苏州那个园林——全中国只有一个拙政园,没有任何歧义。但搜"西湖"时,我们可能会愣一下:扬州还有个瘦西湖,名字里也带着"西湖"两个字。到底是杭州的西湖,还是扬州的瘦西湖?

搜"人民广场"时,我们更懵了:全国几乎每个城市都有一个人民广场,到底想找哪一个?

为什么同样是搜索,有的词能让我们秒定位,有的词却让我们犯难?

这个问题的答案,藏在"区分度"三个字里,而区分度本身,则与文本的稀缺性息息相关。


区分度:一个连续的光谱

假设你手里只有全国 5A 级景区名录,大概三百多个。"拙政园"和"西湖"的区分度都很高。但"西湖"略输一筹——扬州的瘦西湖也是 5A 级,名字里带着"西湖",多少带来一丝模糊。而"拙政园"独一无二,没有这个问题。

把资源池扩大到 4A + 5A 级景区,几千个。"西湖"的区分度进一步下降:安徽阜阳的"颍州西湖"、杭州的"西湖龙坞茶镇",名字里都带着"西湖"。光是这个级别,"西湖"就已经被好几个景区共享了。"拙政园"依然稳如泰山。

再扩展到地图上的全部 POI——几千万个。周边 POI 大量出现:"拙政园苏博站"、"如家酒店拙政园店"、"拙政园停车场"……但即便如此,"拙政园"仍保持较高区分度——这些周边 POI 全都围绕在苏州拙政园附近。"西湖"就没这么幸运——瘦西湖的 POI 分词时可能被拆成"瘦"+"西湖",再加上"西湖文化广场"、"西湖大学"……区分度被进一步稀释。而"人民广场"呢?上千条 POI 分布在全国各地,已经完全无法定位到任何一条。

这就是区分度:一个词在总资源池中出现得越多,反而越不聚焦。

IDF 就是区分度的数学表达——一个词在资源池中出现的范围越窄,IDF 越高,区分度越强。

搜索引擎用"文档频率"来近似衡量一个词的区分度——一个词出现的文档越少,说明它的区分度越强。TF-IDF 就是为了解决这个问题而生的,它用 IDF 量化了词的区分度,用 TF 筛选出最专注的文档。


TF-IDF

怎么用数学来量化这种区分度?评分由两部分组成——词本身的专属性(IDF)和文档的专注度(TF)。TF-IDF 分别量化了这两个维度。

假设我们的语料库是全国所有大小景点的介绍文章,总共有几万篇。你希望搜一个词,就能从里面找出最相关的那篇。

怎么判断哪篇最相关?这里主要依靠两个直觉。

第一个直觉:这个词在多少篇文章里出现过?

当你搜"拙政园"时,为什么能秒定位?因为"拙政园"这个名字,几乎只属于苏州那一个地方——提到它的文章可能有几百篇。而"人民广场"呢?几乎每个城市都有一个,成千上万篇文章都可能提到了它。两相对比,"拙政园"有一种独一无二的确定性——我们此前说过:一个词出现的地方越少,说明它的专属性越强,指向性越明确

看看这几个词的专属性梯度:

  • "拙政园"只出现在几百篇文章中——这个名字几乎专属于苏州那一个地方

  • "西湖"出现在几千篇中——虽然最知名的是杭州西湖,但瘦西湖、颍州西湖也在分享这个名字

  • "人民广场"、"人民公园"出现在上万篇中——这个名字太泛了,谁都能叫

这种不需要人工标注,不需要知识图谱,只靠统计词在文档中的分布,就能自动衡量一个词的"专属性",就是"逆文档频率"(IDF,Inverse Document Frequency):

IDF(t)=ln(1+Ndf+0.5df+0.5)IDF(t) = \ln\left(1 + \frac{N - df + 0.5}{df + 0.5}\right)

说明:这是 Lucene 实际使用的平滑版本,与经典版本 log(N/df)\log(N/df) 相比更稳定,避免了除零问题。

第二个直觉:这个词在某篇文章里出现了多少次?

光有专属性还不够。提到了"拙政园"的文章里,都是真的与"拙政园"紧密相关吗?拙政园官方的景区介绍,可能一千字里会反复提及"拙政园"好多次;而它旁边的苏州博物馆的介绍里可能只是顺带说一句"与拙政园相邻"。很显然拙政园官方的景区介绍才是更符合我们预期的——它在某篇文章中出现得越多,说明这篇文章越专注于这个词

而这就是"词频"(TF,Term Frequency)的直觉:

TF(t,d)=词 t 在文档 d 中出现的次数TF(t, d) = \text{词 } t \text{ 在文档 } d \text{ 中出现的次数}

专门介绍拙政园的文章,"拙政园"可能出现 20 次——TF 很高;苏州博物馆的文章可能只出现 1 次——TF 很低。

补充说明:实际应用中,TF 还有归一化版本(词数 / 文档总词数),可以避免长文档天然占优势。但为了简化理解,本文使用基础版本。

TF-IDF = TF × IDF

当我们把两个值相乘,就得到了 TF-IDF 的结果:

TF-IDF(t,d)=TF(t,d)×IDF(t)TF\text{-}IDF(t, d) = TF(t, d) \times IDF(t)

通过这个值很好地表达出了这种数学关系:一个词在某篇文章中出现得越多(TF 高),且在整个语料中出现的地方越少(IDF 高),那它和这篇文章的"相关性"就越强。

这就是为什么,搜索引擎天然偏爱那些"只此一家"的名字。

有了 TF-IDF 就一定能在所有场景下,帮我们找到最相关的文档吗?让我们来看一个反直觉的例子。

假设有一篇 5000 字的苏州旅游攻略,介绍了苏州各种好玩的地方,里面可能提到了"拙政园"10 次;而另一篇 50 字的拙政园简介,只提到"拙政园"3 次。根据我们的直觉认知,后者应该与拙政园更相关,但如果按照 TF-IDF 的逻辑,前者的 TF 更高,反而应该排在前面。

问题出在哪?

其实这就是 TF-IDF 的问题:因为它只看"出现次数",不看"浓度"。而这个问题,自有更完善的算法会解决。


BM25

1994 年,Stephen Robertson 等人提出了 BM25(Best Matching 25),针对 TF-IDF 的弱点做出了改进。

改进一:TF 饱和

TF-IDF 让 TF 线性增长,按照算法,文档中出现 10 次的得分将是 1 次的 10 倍。但出现 10 次真的比 1 次要更加"相关 10 倍"吗?也许文章只是在堆砌关键词罢了。

BM25 用一个带饱和效应的函数替代了原始 TF(这里展示的是简化形式,假设文档长度等于平均长度):

TF 饱和项=tf×(k1+1)tf+k1\text{TF 饱和项} = \frac{tf \times (k_1 + 1)}{tf + k_1}

其中 tf 是词在这篇景点介绍中的出现次数,k1k_1 是控制饱和速度的参数(通常取 1.2 ~ 2.0)。

相比原始的 tf 值可能无限增长不同,TF 的值将会随着 tf 的增长逐渐收敛,通过计算不难得出:

limtftf×(k1+1)tf+k1=k1+1\lim_{tf \to \infty} \frac{tf \times (k_1 + 1)}{tf + k_1} = k_1 + 1

也就是说,当 k1=1.2k_1 = 1.2 时,TF 饱和项的最大值约为 2.2。

让我们来简单计算一下:当 tf 从 1 涨到 5 时,分数从 1.00 涨到 1.77,涨了将近一倍;但从 10 涨到 100 时,分数只从 1.96 涨到 2.17 —— 几乎到顶了。

这就是"饱和":词频越高,额外增加一次出现带来的收益越小。 一篇景点介绍提到"拙政园" 3 次确实比 1 次更相关,但提到 100 次并不会比 10 次多出多少。

词频的收益会饱和,但区分度永远不会 —— 这是 BM25 对 TF-IDF 的核心修正。

k1k_1 参数如何选择?

k1k_1 控制 TF 饱和的速度。让我们看看不同 k1k_1 值下的曲线差异:

图片:不同 k1 值下的 TF 饱和曲线

  • k1=0.5k_1=0.5(蓝色):饱和最快,tf=5tf=5 时就接近上限,适合短文本场景

  • k1=1.2k_1=1.2(红色):工业界默认值,平衡了区分度和饱和速度

  • k1=2.0k_1=2.0(绿色):饱和最慢,tf=10tf=10 时还在快速增长,适合长文档场景

实际经验:如果你的语料以短文本为主(如商品标题、POI 名称),k1k_1 可以设小一点(0.5~1.0);如果以长文本为主(如文章、攻略),k1k_1 可以设大一点(1.5~2.0)。

改进二:描述长度归一化

想象两篇景点介绍:一篇 50 字,提到"拙政园" 3 次;另一篇 500 字,也提到"拙政园" 3 次。你直觉上会觉得哪篇更相关?

显然是那篇短的——50 字里 3 次提到,浓度极高,整篇都在讲拙政园;500 字里 3 次提到,可能只是一笔带过。

BM25 的长度归一化就是来做这件事的:

长度归一化=(1b)+b×dlavgdl\text{长度归一化} = (1 - b) + b \times \frac{dl}{avgdl}

其中 dl 是当前文章的字数,avgdl 是所有文章的平均篇幅,b 是控制惩罚力度的参数(通常取 0.75)。

  • 篇幅等于平均长度时:不增不减

  • 篇幅是平均长度的 2 倍时:TF 项被除以 1.75,分数被压低

  • 篇幅是平均长度的一半时:TF 项被除以 0.875,分数被放大

篇幅长的文章里词频高,可能是"水"出来的,要打折;篇幅短的文章里词频高,说明浓度更高,要加分。

想象一个极端案例:一篇 5000 字的攻略提到"拙政园"5 次,一篇 50 字的介绍提到 3 次——BM25 会告诉你,50 字那篇更相关。为什么?因为 5000 字里 5 次提到,浓度只有 0.1%;50 字里 3 次提到,浓度高达 6%。

浓度才是真相,总量会骗人。

bb 参数如何选择?

bb 控制文档长度对分数的影响程度。让我们看看不同 b 值下的曲线差异:

图片:不同 b 值下的长度归一化因子

  • b=0b=0(蓝色):完全忽略文档长度,所有文档一视同仁

  • b=0.5b=0.5(橙色):适度惩罚长文档

  • b=0.75b=0.75(红色):工业界默认值,平衡了长短文档的差异

  • b=1.0b=1.0(紫色):完全按长度比例惩罚,长文档被大幅压分

实际经验:如果你的语料长度比较均匀(如都是商品标题),bb 可以设小一点(0~0.3);如果长度差异很大(如混合了标题、简介、长文),bb 可以设大一点(0.75~1.0)。

BM25 完整公式

把两个改进合在一起:

Score(D,Q)=IDF(qi)×tf(qi,D)×(k1+1)tf(qi,D)+k1×((1b)+b×dlavgdl)Score(D, Q) = \sum IDF(q_i) \times \frac{tf(q_i, D) \times (k_1 + 1)}{tf(q_i, D) + k_1 \times \left((1 - b) + b \times \frac{dl}{avgdl}\right)}

看着复杂,但和 TF-IDF 的 TF × IDF 对比,其实也就只是把 TF 换成了 TF 饱和项 / 长度归一化项

用具体例子算一下

假设文章的平均篇幅 avgdl=100avgdl = 100 字,参数 k1=1.2,b=0.75k_1 = 1.2, b = 0.75。用户搜索"拙政园",有 3 篇景点介绍命中:

文章 篇幅 (dl) "拙政园"出现次数 (tf)
A(苏州拙政园景区介绍) 80 字 4 次
B(拙政园苏博站介绍) 150 字 2 次
C(如家酒店拙政园店介绍) 200 字 1 次

假设语料有 100 篇文章,"拙政园"只出现在其中 2 篇中:

IDF("拙政园")=ln(1+1002+0.52+0.5)=ln(40.4)3.70IDF(\text{"拙政园"}) = \ln\left(1 + \frac{100 - 2 + 0.5}{2 + 0.5}\right) = \ln(40.4) \approx 3.70

以文章 A 为例:篇幅 80 字,"拙政园"出现 4 次。

长度归一化=0.25+0.75×80100=0.85(短于平均,加分)\text{长度归一化} = 0.25 + 0.75 \times \frac{80}{100} = 0.85 \text{(短于平均,加分)}Score(A)=3.70×4×2.24+1.2×0.856.49Score(A) = 3.70 \times \frac{4 \times 2.2}{4 + 1.2 \times 0.85} \approx 6.49

用同样的方法计算 B 和 C,结果分别是 4.46 和 2.63。

文章越短、词频越集中,BM25 给分越高——浓度胜过总量。

注意看趋势:文章越短、词频越高,分数越高。长度归一化在背后默默调节——短的加分,长的打折。

苏州拙政园景区的介绍虽然只有 80 字,但"拙政园"出现了 4 次,而且因为篇幅短、浓度高,长度归一化反而给了加分。如家酒店的介绍虽然也提到了"拙政园",但篇幅长(200 字),浓度低,被大幅压分了。

BM25 没有改变区分度的计算方式(IDF 保持不变),但它修复了区分度的两个干扰因素——不再被词频堆砌和文章长度误导。

BM25 的局限性:虽然 BM25 解决了 TF-IDF 的核心问题,但它仍有不足——比如不考虑关键词的位置(标题中的关键词和正文中的关键词权重相同),也不考虑语义关联("人工智能"和"AI"被视为两个不同的词)。这些是后续算法(如 BM25F、向量检索)要解决的问题。


两个算法的对比

TF-IDF 和 BM25 的核心差异在于 TF 的处理方式:TF-IDF 让 TF 线性增长,BM25 用饱和函数限制了 TF 的上限,同时引入了长度归一化。

用一张图来理解 TF 处理的差异:

图片:TF 贡献值对比:TF-IDF(线性)vs BM25(饱和)

TF-IDF 是一条直线,无限增长。BM25 是一条渐近线,趋近于某个上限。


写在最后

从 TF-IDF 到 BM25,我们看到的不是两个算法的简单替换,而是搜索引擎对"区分度"理解的不断深化:

  • TF-IDF 发现了区分度,用 IDF 量化了它

  • BM25 修复了区分度,让 IDF 不再被 TF 堆砌和文档长度干扰

这两个算法不是"新的替代旧的",而是"新的在特定场景下更好"。BM25 至今仍是工业界的主流选择,TF-IDF 作为最简单的基线仍有教学价值。

理解了这一点,你就掌握了搜索引擎评分逻辑的本质。