40 · 倒排、布尔与位置约束
单元:S07。先修:39。
来源:王树森固定目录;Manning等《Introduction to Information Retrieval》(补充原始教材;章节1、2、6、8)。这是 Lab 自编实验;王树森后四模块只有公开目录,不称原作者完整讲义。
证据边界:四文档小索引与穷举;非分布式引擎。
学习方式:先预测每个对照,再只改一个变量,运行后解释差异。
import numpy as np
import pandas as pd
from IPython.display import display
from reclab.learning.search_curriculum import *倒排表
键是(field,term),值为doc→positions。位置是字段内token下标;标题和正文不能拼成跨字段短语。
idx=PositionalIndex(DOCUMENTS)
print(dict(idx.postings[('title','repair')]))
assert idx.postings[('title','repair')]['a']==[1]Notebook 保存的输出 · 本页未执行
{'a': [1], 'c': [2], 'd': [1]}
AND/OR
固定query,只改布尔算子;与逐文档穷举完全核对。
for mode in ['AND','OR']:
got=idx.retrieve('apple battery',mode=mode)
expected=sorted(d for d,f in DOCUMENTS.items() if (set(terms('apple battery'))<=set(terms(f['title'])) if mode=='AND' else bool(set(terms('apple battery'))&set(terms(f['title'])))))
assert got==expected
print(mode,got)Notebook 保存的输出 · 本页未执行
AND [] OR ['a', 'b', 'c', 'd']
短语与字段
命中两个词不等于邻接。对照标题/正文与AND/phrase。
for field in ['title','body']:
print(field,'AND',idx.retrieve('apple repair',field,mode='AND'),'phrase',idx.retrieve('apple repair',field,phrase=True))
for q in ['apple repair','battery repair','repair apple']:
assert idx.retrieve(q,phrase=True)==sorted(d for d,f in DOCUMENTS.items() if any(terms(f['title'])[i:i+len(terms(q))]==terms(q) for i in range(len(terms(f['title'])))))Notebook 保存的输出 · 本页未执行
title AND ['a', 'd'] phrase ['a', 'd'] body AND ['d'] phrase []
更新与删除
此机制选择重建换代,不宣称增量合并。删除a后旧索引仍可见a,展示缓存一致性前置问题;26章在此后学习BM25。
updated={d:f for d,f in DOCUMENTS.items() if d!='a'}
new=PositionalIndex(updated)
assert 'a' in idx.retrieve('apple') and 'a' not in new.retrieve('apple')
print('old',idx.retrieve('apple'),'new',new.retrieve('apple'))Notebook 保存的输出 · 本页未执行
old ['a', 'b', 'd'] new ['b', 'd']
本次保存结果的解读
AND与OR结果通过逐文档穷举核对;短语检查额外要求相邻位置,正文命中与标题命中分开。重建后的索引不再返回a,旧索引仍可见它,为下一缓存课提供明确的版本边界。
改参数后应以新输出重新判断,不把这段固定解读当作新的测量。
复盘与练习
写下输入、输出、保持不变的量与本章不能支持的结论。选一个上面的单变量参数先预测再重跑;保留失败例。回到搜索学习路径继续下一语义单元。