2、信息检索基础:信息检索模型与倒排索引

各位同学,今天咱们来聊聊信息检索的根基。说实话,这部分内容看起来有点理论,但你要是真搞懂了,后面写搜索引擎代码的时候会特别顺手。我当年刚入行时,就是在这块吃了不少亏,所以今天我把自己的经验揉碎了讲给你听。

2.1 信息检索模型:三种主流思路

信息检索模型,说白了就是回答一个问题:「用户搜了一个词,我怎么判断哪些文档跟它相关?」 不同的模型给出了不同的答案。我习惯把它们分成三类:布尔模型、向量空间模型、概率模型。

2.1.1 布尔模型

这是最古老的模型,也是逻辑最清晰的。它的核心思想就一句话:文档要么匹配,要么不匹配,没有中间状态。

举个例子:用户搜「苹果 AND 手机」,那只有同时包含「苹果」和「手机」的文档才会被返回。如果是「苹果 OR 手机」,那包含任意一个词的都算。如果是「苹果 NOT 手机」,那就只要苹果不要手机。

核心特点:

  • 匹配是二值的:1 或 0
  • 不支持排序,所有匹配文档地位平等
  • 查询语法简单,适合精确检索

我在项目中遇到过什么情况呢?有一次做法律文档检索系统,用户要求必须精确匹配法条编号。这时候布尔模型就特别合适——「民法典第104条 AND 合同效力」,多一个词少一个词都不行。但你要是拿它来做网页搜索,那就完蛋了,因为用户搜「苹果手机」时,你没法区分哪篇文档更相关。

2.1.2 向量空间模型

这个模型就聪明多了。它把每个文档和每个查询都看作高维空间里的一个向量。维度就是词典里的所有词,每个维度上的值就是这个词在文档中的权重。

你想想看,这样一来,文档和查询之间的相似度就可以用向量夹角来计算了。夹角越小,说明越相关。常用的计算方式是余弦相似度

我的经验:向量空间模型最大的好处是支持排序。你可以给用户返回「最相关的前10条」,而不是一堆无序的结果。这在搜索引擎里是刚需。

但这里有个坑——维度灾难。如果词典里有10万个词,那每个文档就是一个10万维的向量。计算起来非常慢。我曾经在一个项目中直接拿全量词典做向量,结果查询响应时间飙到了3秒多。后来用了降维和稀疏矩阵存储,才把时间压到100毫秒以内。

2.1.3 概率模型

概率模型的思路更接近人类直觉:给定一个查询,我们计算每篇文档是「相关」的概率有多大。然后按概率从高到低排序。

最经典的是 BM25 算法,它是在概率检索框架下发展出来的。BM25 考虑了三个因素:

  • 词频(TF):这个词在文档里出现了几次
  • 逆文档频率(IDF):这个词在整个文档集合里有多稀有
  • 文档长度归一化:长文档天然包含更多词,需要做惩罚

我个人习惯在中小型搜索引擎里用 BM25,因为它效果好、参数少、调起来方便。Elasticsearch 的默认相关性算法就是 BM25,这足以说明它的地位。

2.2 倒排索引原理

好,模型讲完了。但模型再好,没有高效的数据结构也白搭。搜索引擎里最核心的数据结构就是倒排索引

什么叫倒排?正常我们想「这篇文档里有哪些词」,这叫正排索引。但搜索引擎需要的是反过来——「这个词出现在哪些文档里」。这就是倒排索引。

举个例子,假设有三篇文档:

  • 文档1:我喜欢苹果
  • 文档2:苹果手机很好用
  • 文档3:我喜欢手机

倒排索引长这样:

词项 倒排列表(文档ID + 位置)
文档1(位置1), 文档3(位置1)
喜欢 文档1(位置2), 文档3(位置2)
苹果 文档1(位置3), 文档2(位置1)
手机 文档2(位置2), 文档3(位置3)
很好用 文档2(位置3)

当用户搜「苹果手机」时,搜索引擎先查倒排索引找到「苹果」和「手机」的倒排列表,然后做交集运算,就能快速定位到文档2。

注意:倒排索引的构建过程需要做分词、去停用词、词干提取等预处理。我曾经因为分词没做好,导致「北京大学」被拆成了「北京」和「大学」,搜索结果完全乱套了。嗯,这里要注意,中文分词是倒排索引的命门。

2.3 TF-IDF 算法详解

TF-IDF 是信息检索里最经典的权重计算算法。虽然现在有 BM25 等更先进的算法,但 TF-IDF 的思想是所有后续算法的基础。我建议你把它吃透。

2.3.1 TF(词频)

TF 衡量的是一个词在文档中出现的频繁程度。直觉上,一个词出现次数越多,它越能代表这篇文档的主题。

计算公式:

TF(t, d) = 词 t 在文档 d 中出现的次数 / 文档 d 的总词数

为什么要除以总词数?因为长文档天然词多,不做归一化的话,长文档的 TF 会普遍偏高。这就像你不能因为一本厚书里「爱情」这个词出现了100次,就说它比一本薄书(出现50次)更关于爱情——得看比例。

2.3.2 IDF(逆文档频率)

IDF 衡量的是一个词的稀有程度。如果一个词在很多文档中都出现,那它的区分能力就很弱。比如「的」、「是」、「在」这些词,几乎每篇文档都有,它们对判断相关性没什么帮助。

计算公式:

IDF(t) = log(文档总数 / 包含词 t 的文档数 + 1)

注意分母加1是为了防止除零错误。log 的作用是压缩数值范围,让 IDF 不会因为文档数差异过大而失控。

2.3.3 TF-IDF 的计算

TF-IDF 就是 TF 和 IDF 的乘积:

TF-IDF(t, d) = TF(t, d) × IDF(t)

这个值越大,说明词 t 对文档 d 越重要。举个例子:

  • 「苹果」在文档1中出现了3次,文档1总词数100,TF=0.03
  • 总共有1000篇文档,其中10篇包含「苹果」,IDF=log(1000/11)≈1.96
  • TF-IDF = 0.03 × 1.96 ≈ 0.059

而「的」在文档1中出现了20次,TF=0.2,但几乎每篇文档都有它,IDF≈0,所以 TF-IDF≈0。你看,高频但无意义的词就被过滤掉了。

避坑指南:我曾经在一个项目中直接用原始 TF 做排序,结果「的」、「了」这些停用词因为出现频率高,反而排在了前面。后来加上 IDF 权重,效果立竿见影。所以记住:TF 负责找高频词,IDF 负责找稀有词,两者缺一不可。

2.4 三者如何配合

你可能会问:模型、索引、算法,它们到底是什么关系?

我打个比方你就明白了:

  • 倒排索引是搜索引擎的「书架」,告诉你每本书放在哪个位置
  • TF-IDF是「评分卡」,给每本书和每个关键词打分
  • 检索模型是「决策规则」,决定怎么用这些分数来排序和筛选

实际工作中,我们通常用倒排索引快速定位候选文档,然后用 TF-IDF 或 BM25 计算相关性分数,最后按模型规则排序输出。这三者环环相扣,缺一不可。

好了,这一章的内容就到这里。下一章我们会进入实战环节,手把手教你用 Python 实现一个迷你搜索引擎。到时候你会亲眼看到倒排索引是怎么构建的,TF-IDF 是怎么计算的——那才是真正有意思的部分。