深夜的刑警队办公室,老陈把手里仅有的一张纸条拍在桌上。纸条上歪歪扭扭地写着两个字:“桥头”。这是一起流窜盗窃案唯一的线索。徒弟小周拿着纸条跑了一圈,带回了十个“在桥头出现过的人”——但其中只有两三个真正与案件有关。老陈没有气馁,反而眼睛一亮:既然第一轮排查能带回候选人,那我为什么不从这几个“最可疑”的人身上再挖点线索?他盯着名单里最像的三个,从他们的衣着、鞋底泥土、口音习惯里继续找信息,然后带着新线索回到街上,展开了更大范围的排查。这,就是“伪相关反馈”的核心心智模型:先用手中仅有的线索跑一次检索,假装返回的前几个结果都是“相关”的,再从中学习新词、改写查询、重新检索。整个过程无需用户多动一下手指,机器自己就能把模糊的查询“越扩越准”。

岗位关联

掌握 PRF,能让你在用户不愿多打字、查询词极其匮乏的真实场景下,用系统自动追问大幅提升检索召回率;它也是理解 RM3、KL 散度等现代检索扩展技术的必修基石。

目录
  1. 可视化演示
  2. 1. 问题与直觉:检索的困境
  3. 2. 核心流程:先抓一批“嫌疑人”
  4. 3. 词项加权:判断线索的价值
  5. 4. 查询扩展:组装新的抓捕令
  6. 5. 风险与漂移:被“假线索”带偏
  7. 6. 实现方案:RM3与KL散度扩展
  8. 7. 工程实践:从理论到破案系统
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

1. 问题与直觉:检索的困境

老陈第一次排查靠的是“桥头”两个字。可那天傍晚在桥头附近出现的人少说也有上百,而他要找的罪犯很可能用的是“渡口”“河边”这种同义词。只按字面去找,真正相关的人压根没进前十。徒弟小周不服气:“我能挨个问啊!”老陈摇头:全城几十万人,你问得过来吗?搜索引擎面临同样的处境——用户只肯打三五个词,文档库却有千万篇,一次机械的词匹配,就像拿着两个字满城乱问。

所以传统检索模型的全部功夫,就花在“怎么给文档打分排名”上。一个朴素的打分函数是 TF-IDF:先看查询词在文档里出现多少次(词频),再看这个词在全库里是不是稀罕货(逆文档频率)。稀罕的词对不上号,烂大街的词又没有区分度,两者相乘,就得到了文档与查询的“表面相似度”。

但表面相似度有个致命弱点:只能命中查询词的字面替身。查询说“苹果”,文档聊“乔布斯和 iPhone”,一个字都对不上,相关文档就被排在十名开外。这类单轮检索的召回瓶颈,正是 PRF 想解决的。

老陈看着徒弟打回来的分数单想:分数只能说明“字面上像不像”,说明不了“真的相关”。要抓住真凶,得换个思路——先按这单子抓一批人,再从人身上找线索。这正是伪相关反馈的起点。

score(q,d)=∑t∈qtf(t,d)×idf(t)\mathrm{score}(q, d) = \sum_{t \in q} \mathrm{tf}(t, d) \times \mathrm{idf}(t)
这个公式就是老侦探的第一张排查令:把每条线索的价值加总,给每个候选人打分。打分只能说明“字面像不像”,说明不了“真不真相关”。
  1. q(查询词集合)老陈手里仅有的线索清单——“桥头”。
    q={桥头}q = \{ \text{桥头} \}
  2. tf(t, d)(词频)某个线索在街坊嘴里重复了几遍。一个词在文档里重复越多,说明它与这篇文档关系越紧密。
    tf(t,d)=词 t 在文档 d 中的出现次数\mathrm{tf}(t, d) = \text{词 } t \text{ 在文档 } d \text{ 中的出现次数}
  3. idf(t)(逆文档频率)线索越稀缺,价值越高。满世界都在说的“东西”几乎不提供任何信息。
    idf(t)=log⁡Ndf(t)\mathrm{idf}(t) = \log \frac{N}{\mathrm{df}(t)}
  4. 逐词求和把每条线索的价值加总,得到这篇文档与查询的整体匹配度。
    ∑t∈qtf(t,d)×idf(t)\sum_{t \in q} \mathrm{tf}(t, d) \times \mathrm{idf}(t)

2. 核心流程:先抓一批“嫌疑人”

老陈把“桥头”交给线人,线人回报了十个人的名字。老陈赌了一把:这十个人里就算只有两个沾边,也已经比“满城乱找”强得多。他圈出最像的三个,开始翻他们的通讯录、查聊天记录、嗅鞋底——每一处细节,都可能是下一轮排查的新线索。关键在于,整个过程不需要回头问报案人“你觉得谁可疑”,系统自己就能从 Top-K 结果里挖信息。

PRF 的“PR“是 Pseudo Relevance(伪相关),”F“是 Feedback(反馈)。所谓”伪”,是说系统假装排名前 K 的文档就是相关文档——这个假设未必成立,但大多数时候够用。整个过程分三步:先用原始查询 q₀ 跑一遍倒排索引,拿到 Top-K 文档;再从这 K 篇文档里统计高频词,挑出候选扩展词;最后把扩展词并回查询,组成新查询做第二轮检索。

第二步的“挑词”不是随便挑。每个候选词都要计算它在这 K 篇文档里的出现强度,淘汰那些只出现一两次的词。第三步等价于把查询从“约在桥头”扩展成“约在桥头河边夜市口音外地人”——字变多了,能匹配的文档也自然变多了。

老陈把这套流程画在黑板中央:“记住,关键假设只有一个——Top-K 里只要有几篇真相关,新线索就有价值;如果全抓错了,后面全白搭。” 这正是 PRF 既惊艳又危险的根源。

F={d∈D:rankq0(d)≤K},Q′=Q0∪EF = \{ d \in D : \mathrm{rank}_{q_0}(d) \le K \}, \qquad Q' = Q_0 \cup E
前一个集合是“伪相关的嫌疑人名单”,后一个是“合并了新线索的抓捕令”。PRF 的全部秘密,就藏在这两个集合的先后关系里。
  1. D(全量文档库)全城所有人的名单,也就是搜索引擎里千万篇文档的集合。
    D={d1,d2,…,dN}D = \{ d_1, d_2, \dots, d_N \}
  2. rank_{q₀}(d)(按原始查询的排名)每份名单与“桥头”的字面匹配排名。排得越靠前,嫌疑越大。
    rankq0(d)=按 q0 打分后的名次\mathrm{rank}_{q_0}(d) = \text{按 } q_0 \text{ 打分后的名次}
  3. F(Top-K 集合)排名不超过 K 的那部分文档,是系统假设“相关”的嫌疑人名单。
    F={d:rankq0(d)≤K}F = \{ d : \mathrm{rank}_{q_0}(d) \le K \}
  4. E(扩展词集合)从 F 的词频统计中筛出的新词,是老陈从嫌疑人身上挖出的新线索。
    E={w:score(w,F)≥τ}E = \{ w : \mathrm{score}(w, F) \ge \tau \}
  5. Q'(新查询)原查询与新线索的并集,构成第二轮检索的“全新排查令”。
    Q′=Q0∪EQ' = Q_0 \cup E

3. 词项加权:判断线索的价值

嫌疑人 A 说“我在河边待过”,嫌疑人 B 说“东西挺多”。老陈记下线索后要称重:“河边”可能直指作案地点,价值极高;“东西”是全城人民张口就来的词,几乎不值钱。权重,就是侦探给每条线索标的可信度。

在 PRF 里,扩展词的权重来自“这批文档中的词频分布”。一个词在 K 篇文档里反复出现,说明它是这批文档的“标签词”;但它越是全库都常见的通用词,区分度就越低。于是,统计词频之后,还要看它对全库的稀有程度。

常用的候选词打分是把词频和逆文档频率相乘。更形式化的做法是估计该词在 F 集合中的最大似然概率:分子是该词在 F 所有文档中出现的总次数,分母是 F 中所有词的次数总和。这样就得到一个“文档集合上的词分布模型”,也就是嫌疑人群体口供的“词频画像”。

老陈把称重结果排在桌上,划掉了“东西”“那个”等虚词,留下的全是“河边、泥脚印、夜班车站”。他知道,只有高区分度的线索,才能让第二份排查令变得锋利。

P^(w∣F)=∑d∈Ftf(w,d)∑d∈F∑ttf(t,d)\hat{P}(w|F) = \frac{\sum_{d \in F} \mathrm{tf}(w, d)}{\sum_{d \in F} \sum_{t} \mathrm{tf}(t, d)}
这个比值衡量的是“词 w 在嫌疑人名单里的浓度”。浓度越高,越可能是这批人独有的黑话;浓度太低,就不配写进抓捕令。
  1. 分子(词 w 在 F 中的总出现次数)新线索在嫌疑人群体里被提及的总次数。
    ∑d∈Ftf(w,d)\sum_{d \in F} \mathrm{tf}(w, d)
  2. 分母(F 中所有词的总量)嫌疑人群体聊天记录的总字数,用来归一化,消除文档长短差异。
    ∑d∈F∑ttf(t,d)\sum_{d \in F} \sum_{t} \mathrm{tf}(t, d)
  3. 比值(最大似然概率)词 w 在这锅汤里的浓度,浓度越高越可能是特征词。
    P^(w∣F)∈[0,1]\hat{P}(w|F) \in [0, 1]

4. 查询扩展:组装新的抓捕令

新线索备齐后,老陈拿出最初的纸条。“桥头”是唯一经过报案的权威线索,不能丢;但“河边”“夜班车”这些新词,也得让它们占一席之地。他给老线索和新线索分别标好权重,拼成一张全新的抓捕令。

查询扩展在实现上就是给“查询词表”重新加权。原查询词虽然少,但语义最可信;扩展词来自 Top-K 文档,能带来同义表达和上下文词,但可能混入噪音。一个稳健的方案是线性插值混合模型。

设原始查询里的词按指示函数取值,扩展词按上一节算出的经验概率取值,再用 λ 控制两边的权重。λ 越接近 1,系统越保守,只相信原始查询;λ 越小,越依赖新线索。工程上,λ 通常取 0.5~0.8 之间——既保留原意,又大胆吸收新表达。

新抓捕令贴满全城:“桥头、河边、外地口音、夜班车站”。老陈的二次排查范围比第一轮大了好几倍,那些原本用同义词藏起来的嫌疑人,终于浮出水面。

P(w∣Q′)=λ δQ0(w)+(1−λ) P^(w∣F)P(w|Q') = \lambda \, \delta_{Q_0}(w) + (1 - \lambda) \, \hat{P}(w|F)
这是“老线索 + 新线索”的配比公式。λ 就像老侦探的“信任档位”:信多少手头的纸条,信多少嫌疑人的供词,由这一个旋钮控制。
  1. δ_{Q₀}(w)(原始词指示函数)w 是不是原始查询里的词,是则为 1,否则为 0。
    δQ0(w)={1w∈Q00w∉Q0\delta_{Q_0}(w) = \begin{cases} 1 & w \in Q_0 \\ 0 & w \notin Q_0 \end{cases}
  2. P̂(w|F)(扩展词经验概率)上一节算出的候选词在 Top-K 文档中的浓度。
    P^(w∣F)=∑d∈Ftf(w,d)∑d∈F∑ttf(t,d)\hat{P}(w|F) = \frac{\sum_{d\in F} \mathrm{tf}(w,d)}{\sum_{d\in F}\sum_t \mathrm{tf}(t,d)}
  3. λ(混合系数)信多少老线索,这是整个模型里最敏感的手感参数。
    λ∈[0,1]\lambda \in [0, 1]
  4. 加权求和把“老线索是否出现”和“新线索的出现强度”合成一个综合权重。
    P(w∣Q′)=λ×δQ0(w)+(1−λ)×P^(w∣F)P(w|Q') = \lambda \times \delta_{Q_0}(w) + (1-\lambda) \times \hat{P}(w|F)

5. 风险与漂移:被“假线索”带偏

不是每次赌注都能赢。如果 Top-K 抓回来的三人全是被冤枉的路人,他们嘴里挖出的“河边”“夜班车”就全是巧合。老陈拿着错误的线索满城转悠,案件方向被彻底带偏。这就是 PRF 世界里最凶狠的敌人——查询漂移。

查询漂移的本质:扩展词来自“被假设为相关”的文档,如果这些文档里混入噪音,高频词就会把新查询往无关方向拉扯。更糟的是,扩展会放大初始错误——第一轮的偏差在第二轮被几何级放大,结果可能比不扩展还差。

缓解手段主要有三:限制 Top-K 的 K 值(通常 10~30);限制扩展词数量(通常 10~50);引入 KL 散度等信息论判据,只保留“在 F 中显著高于全库背景”的词,过滤掉泛化词。三者合力,才能把漂移的缰绳勒住。

老陈在黑板角落记下一行忠告:“线索必须经过与全城常识的比较才可信,人人都在说的词,永远不该出现在抓捕令上。”

DKL(PF∥PC)=∑wP(w∣F)log⁡P(w∣F)P(w∣C)D_{KL}(P_F \| P_C) = \sum_{w} P(w|F) \log \frac{P(w|F)}{P(w|C)}
KL 散度是一把“识别假线索”的尺子:凡是全城人都在说的词,和嫌疑人名单里的浓度差不多,就会被这把尺子量出来并淘汰掉。
  1. P(w|F)(文档集合中的浓度)词 w 在嫌疑人名单里的出现概率。
    P(w∣F)=P^(w∣F)P(w|F) = \hat{P}(w|F)
  2. P(w|C)(全库背景浓度)词 w 在全城人口里“人人都在说”的普遍程度。
    P(w∣C)=∑d∈Ctf(w,d)∑d∈C∑ttf(t,d)P(w|C) = \frac{\sum_{d\in C}\mathrm{tf}(w,d)}{\sum_{d\in C}\sum_t \mathrm{tf}(t,d)}
  3. log 比值(偏离程度)如果这个数接近 0,说明 w 在全库和名单里一样普通,没有特征价值。
    log⁡P(w∣F)P(w∣C)\log \frac{P(w|F)}{P(w|C)}
  4. 加权求和(总偏离度)把所有词的偏离度累加,值越大,说明这份“嫌疑人名单”确实有独特话题。
    ∑wP(w∣F)log⁡P(w∣F)P(w∣C)\sum_{w} P(w|F) \log \frac{P(w|F)}{P(w|C)}

6. 实现方案:RM3与KL散度扩展

老陈有两本办案手册。第一本叫 RM3,风格稳健:不轻易相信任何一根新线索,把原案卷和嫌疑人供词按固定比例掺在一起看。第二本叫 KL 散度扩展,风格激进:每条线索都要和全城背景做对比,只有那些“全城没人提、唯独嫌疑人圈子里反复出现”的怪词,才配写进新抓捕令。

RM3 是语言模型框架下最经典的 PRF 实现,它把第 4 节的混合模型落到了具体运算上:用平滑后的文档语言模型估计 P_ML(w|θ_d),再以文档在初始检索中的概率加权求和,得到集合模型;最后与原始查询模型做插值。它实现简单、数值稳定,是工程中的默认武器。

KL 散度扩展则走另一条路:它对每个候选词计算集合模型与全库背景模型的 KL 差异,只保留差异大的特征词。工程上两者常常结合使用——先 KL 筛词,再 RM3 混合,鲁棒性最好。

老陈合上两本手册:“激进派和保守派吵了很多年,但真破案时,先用 KL 识破假线索、再用 RM3 稳步推进,是我交班前能传给你的最好经验。”

PRM3(w)=(1−λ) PML(w∣Q0)+λ∑d∈FPML(w∣θd) P(d∣Q0)P_{\mathrm{RM3}}(w) = (1-\lambda)\,P_{\mathrm{ML}}(w|Q_0) + \lambda \sum_{d \in F} P_{\mathrm{ML}}(w|\theta_d)\,P(d|Q_0)
RM3 公式是一份“两份供词按比例混合”的操作手册:一份来自原查询,一份来自 Top-K 文档集合,λ 决定信任哪边多一点。
  1. P_ML(w|Q₀)(原查询词模型)老案卷里本来就有的话,它的权重由 1-λ 守着。
    PML(w∣Q0)P_{\mathrm{ML}}(w|Q_0)
  2. P_ML(w|θ_d)(单篇文档词模型)嫌疑人 d 个人的“供词分布”,用平滑手段避免出现 0 概率。
    PML(w∣θd)=tf(w,d)+μP(w∣C)∣d∣+μP_{\mathrm{ML}}(w|\theta_d) = \frac{\mathrm{tf}(w,d) + \mu P(w|C)}{|d| + \mu}
  3. P(d|Q₀)(文档对查询的权重)嫌疑人在第一轮排查中排名越靠前,他的供词参考价值越高。
    P(d∣Q0)=score(q0,d)∑d′∈Fscore(q0,d′)P(d|Q_0) = \frac{\mathrm{score}(q_0, d)}{\sum_{d' \in F}\mathrm{score}(q_0, d')}
  4. λ 整体插值手册的“信任档位”,决定多少比例听老案卷、多少比例听新供词。
    λ∈(0,1)\lambda \in (0, 1)

7. 工程实践:从理论到破案系统

把这一套方法论写进系统前,老陈把徒弟们叫到一起:“你们不能光说破案率提高了,得拿出数来。” 工程化的第一课,是找到一把好尺子。

信息检索里最常用的尺子是 MAP(平均精度均值)和 NDCG(归一化折损累计增益)。MAP 关心“相关文档整体排得靠不靠前”;NDCG 关心“越相关的文档是不是排得越靠前”。PRF 效果好不好,要在标准测试集上跑这两把尺子,而不是靠感觉拍板。

调参经验也已相当成熟:Top-K 取 10~30,扩展词数 10~50,λ 取 0.5~0.8。查询极短、数据稀疏时 PRF 收益最明显;查询本身已经很详细时,收益会明显递减。部署时还要注意:索引更新频繁的场景,扩展词要加缓存,防止二次检索耗时翻倍。

老陈在会议室白板上写下最后一行字:“方法不怕旧,怕的是你不知道它什么时候会失效。” 伪相关反馈的理论到今天已走过二十多年,但它面对模糊查询时的“自动追问”能力,依然在无数搜索系统里安静地工作着。

MAP=1∣Q∣∑q=1∣Q∣AP(q),AP(q)=∑k=1nP(k)⋅rel(k)#rel\mathrm{MAP} = \frac{1}{|Q|} \sum_{q=1}^{|Q|} \mathrm{AP}(q), \qquad \mathrm{AP}(q) = \frac{\sum_{k=1}^{n} P(k)\cdot \mathrm{rel}(k)}{\#\mathrm{rel}}
MAP 是一份“总破案率”报表:先算每个案子的平均精度,再把所有案子的平均精度取平均,得到系统整体战斗力。
  1. P(k)(前 k 篇的精确率)抓捕令前 k 名里,真相关者占几成。
    P(k)=前 k 篇中相关文档数kP(k) = \frac{\text{前 } k \text{ 篇中相关文档数}}{k}
  2. rel(k)(第 k 篇是否相关)第 k 名是真人犯还是冤枉路人,相关为 1,否则为 0。
    rel(k)∈{0,1}\mathrm{rel}(k) \in \{0, 1\}
  3. AP(q)(单查询平均精度)把所有“抓到真人犯的位置”上的精确率求平均。
    AP(q)=∑k:rel(k)=1P(k)#rel\mathrm{AP}(q) = \frac{\sum_{k: \mathrm{rel}(k)=1} P(k)}{\#\mathrm{rel}}
  4. 对全部查询求均值(MAP)把所有案子的 AP 加起来取平均,得到系统整体战斗力。
    MAP=1∣Q∣∑q=1∣Q∣AP(q)\mathrm{MAP} = \frac{1}{|Q|} \sum_{q=1}^{|Q|} \mathrm{AP}(q)

AI 应用场景

电商搜索的长尾词拓展

阶段:查询理解与召回优化

解决的问题:用户搜索“运动鞋”时,很多商品标题写的是“跑步鞋”“休闲鞋”,字面不匹配导致漏召回。传统检索只看词面重合,PRF 从点击率最高的 Top 文档中提取“跑步、透气、轻便”等词扩展查询,把大量长尾商品重新捞回结果页。

具体案例:某电商平台上线 PRF 后,搜索“运动鞋”的第一轮结果只覆盖爆款,扩展词加入后,“轻便透气跑步鞋”等长尾商品进入 Top-20,搜索引导的 GMV 提升了约 12%。

学术文献检索的术语补齐

阶段:检索增强生成的前置召回

解决的问题:研究者用口语化句子描述想法(如“怎么让模型学得少也能分类”),学术数据库返回结果稀疏。PRF 从初检论文中提取专业术语(“少样本学习、对比学习、元学习”)扩展查询,补齐口语与学术黑话之间的鸿沟。

具体案例:一位博士生在论文检索系统中输入模糊想法,PRF 自动扩展出 6 个专业术语,第二轮检索的相关论文数量翻了一倍,其中两篇直接成为论文 related work 的核心引用。

企业知识库问答的模糊查询对齐

阶段:FAQ检索与 RAG 召回

解决的问题:员工问“报销要多久”,知识库文档标题是“财务审批流程时效说明”,字面差异导致答非所问。PRF 从初检 FAQ 中提取“审批、发票、合规”等词,把口语化查询与书面化文档对齐。

具体案例:某企业内部助手接入 PRF 后,短查询的命中率提升了 23%,员工不再需要记住“标准术语”才能搜到制度文档。

Python 演示

python76 行
# -*- coding: utf-8 -*-"""PRF pseudo relevance feedback demo with pure Python."""import mathimport refrom collections import Counter DOCS = [    "苹果公司发布了新款手机",    "苹果是一种常见的水果",    "手机市场今年增长迅速",    "苹果树在秋天结果",    "公司财报显示营收增长",    "水果富含维生素",]  def tokenize(text):    return [w for w in re.split(r"[^\u4e00-\u9fa5a-zA-Z0-9]+", text.strip()) if w]  def build_index(docs):    df = Counter()    doc_tf = []    for doc in docs:        tokens = tokenize(doc)        tf = Counter(tokens)        doc_tf.append(tf)        for w in set(tokens):            df[w] += 1    n = len(docs)    idf = {w: math.log((n + 1) / (c + 1)) + 1.0 for w, c in df.items()}    return doc_tf, idf  def score_query(query, doc_tf, idf):    q = tokenize(query)    scores = []    for tf in doc_tf:        s = 0.0        for w in q:            s += tf.get(w, 0) * idf.get(w, 0.0)        scores.append(s)    return scores  def top_docs(scores, k):    order = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)    return order[:k]  def expand(query, doc_tf, idf, top_k, max_terms=3):    counter = Counter()    for i in top_k:        for w, c in doc_tf[i].items():            counter[w] += c    valid = [w for w in counter if w not in tokenize(query)]    ranked = sorted(valid, key=lambda w: counter[w] * idf.get(w, 0.0), reverse=True)    return query + " " + " ".join(ranked[:max_terms])  def main():    doc_tf, idf = build_index(DOCS)    query = "苹果"    s1 = score_query(query, doc_tf, idf)    k = 2    top = top_docs(s1, k)    print("initial:", [DOCS[i] for i in sorted(top, key=lambda i: s1[i], reverse=True)])    q2 = expand(query, doc_tf, idf, top)    print("expanded query:", q2)    s2 = score_query(q2, doc_tf, idf)    rank2 = sorted(range(len(s2)), key=lambda i: s2[i], reverse=True)    print("refined:", [DOCS[i] for i in rank2])  if __name__ == "__main__":    main()
目的:用 6 篇微型文档复现 PRF 全流程——先按 TF-IDF 做初始检索,取 Top-2 文档,从其中提取扩展词,再以扩展后的查询重新打分排序,直观看到召回改善。
  1. idf = {w: math.log((n + 1) / (c + 1)) + 1.0 for w, c in df.items()}计算逆文档频率,词越稀有,idf 越大,就像老侦探给稀罕线索标的更高价值。
  2. s += tf.get(w, 0) * idf.get(w, 0.0)对每个查询词累加 TF×IDF,完成第一轮“表面相似度”打分。
  3. order = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)按分数从高到低排序,得到第一轮排查的“嫌疑人名单”。
  4. counter[w] += c把 Top-2 文档中所有词的出现次数累加,统计“嫌疑人圈子里反复出现的词”。
  5. ranked = sorted(valid, key=lambda w: counter[w] * idf.get(w, 0.0), reverse=True)用词频乘 idf 给候选扩展词称重,筛掉泛化词。
  6. return query + " " + " ".join(ranked[:max_terms])把 Top 扩展词拼回原查询,组装第二轮的“抓捕令”。

常见误区

误区伪相关反馈是不是让用户点几个“我喜欢”的按钮,然后系统再搜一次?那我做推荐系统时是不是也这么干?

正解不是。需要用户标注的是“相关性反馈”(Relevance Feedback);伪相关反馈的“伪”字指系统自作主张地把 Top-K 当成相关文档,全程零用户参与。用户只输入一次查询,剩下的自动完成,这也是它能大规模部署的根本原因。

为何常见:名字里带着“反馈”二字,容易让人联想到需要用户参与;很多入门资料又把两类反馈放在同一章讲,概念边界很容易糊掉。

误区既然是假设相关,那把 K 设成 50、100,样本多一点,统计出来的扩展词肯定更准吧?

正解恰恰相反。K 越大,排名靠后的文档相关性越弱,噪音比例越高。全库通用词在高 K 下更容易混进扩展词,把查询带偏。经验值 K 取 10~30,要在统计稳定性和纯净度之间取平衡。

为何常见:直觉上样本越大越好,但忽略了 PRF 的样本是“按排名截断”的,排名越靠后越不可信。

误区扩展词数量没有上限,多加点词总能覆盖到更多文档吧?

正解扩展词太多,查询向量会被泛化词淹没,检索结果退化成“所有文档都得分”。一般扩展词限制在 10~50 个,且要用权重或 KL 散度筛掉低区分度的词。检索的核心是区分度,而不是覆盖度。

为何常见:用“覆盖=好”的直觉推倒,忘了检索系统要的是“把相关和不相关分开”,而不是“把全部文档都捞上来”。

误区反正都是把第一次检索结果拿去改查询,PRF 只是自动化了而已,原理上没区别。

正解原理区别很大。相关性反馈使用真相关文档(用户标了“这个有用”),反馈信号干净;PRF 使用“假装相关”的文档,信号含噪。因此 PRF 需要 K 值、词数、插值权重等更多约束来对抗噪音,而相关性反馈直接加权即可。

为何常见:两者在流程图上有相似的三段式结构,只看流程不看假设本质,就容易混淆。

误区短查询才需要扩展,用户都打了十几个词了,PRF 还有什么用?

正解长查询信息量足,扩展收益确实递减;但 PRF 的另一个作用是“翻译”——把文档中出现的同义表达融入查询,消解长查询里个别词的歧义。只是工程实践上,它主要被用在短查询场景,因为那里边际收益最大。

为何常见:把 PRF 单纯理解成“加词”,忽略了它还承担“把用户语言换成文档语言”的翻译功能。

面试题

Q1. PRF 中“伪”的含义是什么?

单选题 · easy

  • A.系统故意使用假文档
  • B.假设顶部文档都相关← 正确答案
  • C.检索词被人为篡改
  • D.反馈过程纯属虚构

答案:B。“伪”指系统未经用户确认,就假装排名前 K 的文档是相关文档,并从中提取扩展词。A/D 把“伪”理解成恶意或造假,C 理解成篡改查询,都不符合 PRF 的本意。

Q2. PRF 的正确操作顺序是?

单选题 · easy

  • A.扩充查询词再首轮检索
  • B.首轮检索后提取扩展词← 正确答案
  • C.等待用户手动标注反馈
  • D.直接跳过检索去建索引

答案:B。PRF 的流程是“初始检索 → 假设 Top-K 相关 → 提取候选扩展词 → 用新查询二次检索”。A 把扩展放在检索前,C 是相关性反馈,D 完全偏离检索流程,均不正确。

Q3. 查询漂移的主要成因是什么?

单选题 · mid

  • A.用户查询词拼写错误
  • B.噪音文档把查询带偏← 正确答案
  • C.文档库规模实在太大
  • D.索引更新速度太缓慢

答案:B。查询漂移源于 Top-K 文档中混入不相关文档,其高频词把扩展查询拉向无关方向。A 是用户输入问题,C/D 是工程环境因素,都不会直接导致扩展词方向偏移。

Q4. RM3 中 λ 参数的作用是什么?

单选题 · mid

  • A.决定扩展词项的个数
  • B.调节新旧线索的权重← 正确答案
  • C.决定首轮返回的数量
  • D.决定平滑参数的大小

答案:B。λ 控制原始查询模型与 Top-K 文档集合模型的混合比例,λ 大则更信任原查询,λ 小则更依赖扩展词。扩展词数量由 max_terms 控制,K 值控制首轮返回数,平滑参数由 μ 控制,均与 λ 无关。

Q5. 以下哪些措施能缓解查询漂移?

多选题 · mid

  • A.调小首轮返回的K值← 正确答案
  • B.控制扩展词的总数← 正确答案
  • C.用KL散度筛特征词← 正确答案
  • D.增加文档分词数量

答案:ABC。调小 K 值可减少噪音文档混入,限制扩展词数量可防止查询被泛化词淹没,KL 散度能筛掉与全库背景差异小的词。D 是分词层面的预处理,与纠正漂移没有直接关系。

Q6. 逆文档频率(IDF)越大的词意味着什么?

单选题 · easy

  • A.该词在文档中出现频繁
  • B.该词在全库中稀少独特← 正确答案
  • C.该词的长度一定更短
  • D.该词在全库毫无作用

答案:B。IDF 衡量词的稀有度,在全库中越少出现的词,IDF 越大、区分度越高。A 说的是词频,C 与长度无关,D 把“稀有”误读成“无用”,都不对。

Q7. 相比用户相关性反馈,PRF 的优势有哪些?

多选题 · hard

  • A.整个流程无需用户标注← 正确答案
  • B.系统自动完成两轮检索← 正确答案
  • C.必定能完全杜绝漂移
  • D.成本低且适合大规模用← 正确答案

答案:ABD。PRF 最大的优势是零用户参与、全自动完成、适合大规模部署。但“伪相关”假设决定了它必然有漂移风险,C 说“必定杜绝”过于绝对,PRF 恰恰需要额外机制来抑制漂移。

Q8. MAP 指标衡量的是什么?

单选题 · mid

  • A.全部查询平均精度的均值← 正确答案
  • B.一次检索过程的总时长
  • C.数据集中全部文档个数
  • D.每个查询词项的数量

答案:A。MAP 先对每个查询计算平均精度 AP,再对所有查询的 AP 取均值,反映系统整体把相关文档排在前面的能力。B/C/D 分别描述耗时、文档规模和查询长度,均不属于 MAP 定义。

Q9. 为什么极短查询场景下 PRF 收益最大?

单选题 · hard

  • A.短查询信息少扩展增益高← 正确答案
  • B.短查询的检索耗时更少
  • C.短查询更不容易发生漂移
  • D.短查询必定是正确的查询

答案:A。短查询本身携带的语义信息极少,Top-K 文档补充的扩展词能提供大量缺失的上下文,边际增益最高。B 只说明速度快,C/D 与事实相反——短查询歧义大、漂移风险高,也不存在“必定正确”。

Q10. 关于 K 值的说法,正确的是?

多选题 · hard

  • A.K过小统计结果不稳定← 正确答案
  • B.K过大引入更多噪音文档← 正确答案
  • C.K取值依赖具体语料库← 正确答案
  • D.K值越大效果一定越好

答案:ABC。K 太小,扩展词的词频统计方差大,不稳定;K 太大,排名靠后的文档噪音比例升高;最优 K 随语料和查询分布变化,需要在验证集上调。D 说“越大越好”完全违背工程经验。