文献检索


文档摘要

文献检索 本节摘要:假设廉价;知道是否有人已证明它才是贵的部分。在运行器起沙盒前构建回答这个问题的检索层。关键词搜摘要返与查询共享词汇的论文,覆盖了大部分表面,但漏两种情况:奠基论文用不同词汇(查「稀疏注意力」漏了标题「transformer 路由中的块选择」)、相关论文是引已知锚点的后续(找锚点向前走比暴扫摘要池高效)。本节建两遍:仅用 stdlib 数据结构在摘要上建 BM25 索引抓词法命中,引用图遍历从种子集前后扩一两跳。两者按论文 id 去重合并,按小组合分排序。把两个 mock 外部 API 藏在单一客户端后,使上游调用点在真实端点落地时不变。 对应原课程:Phase 19 · Lesson 51 · (原英文 )。本节属「AI Scientist」赛道第二节。

文献检索

本节摘要:假设廉价;知道是否有人已证明它才是贵的部分。在运行器起沙盒前构建回答这个问题的检索层。关键词搜摘要返与查询共享词汇的论文,覆盖了大部分表面,但漏两种情况:奠基论文用不同词汇(查「稀疏注意力」漏了标题「transformer 路由中的块选择」)、相关论文是引已知锚点的后续(找锚点向前走比暴扫摘要池高效)。本节建两遍:仅用 stdlib 数据结构在摘要上建 BM25 索引抓词法命中,引用图遍历从种子集前后扩一两跳。两者按论文 id 去重合并,按小组合分排序。把两个 mock 外部 API 藏在单一客户端后,使上游调用点在真实端点落地时不变。

对应原课程:Phase 19 · Lesson 51 · literature-retrieval(原英文 phases/19-capstone-projects/51-literature-retrieval/docs/en.md)。本节属「AI Scientist」赛道第二节。

学习目标

阅读完本节,你应当能够:

  1. 用循环会读的字段建模小论文记录。
  2. 仅用 stdlib 数据结构在摘要上建 BM25 索引。
  3. 走引用图浮现词法搜索漏掉的论文。
  4. 按稳定论文 id 跨词法与图遍历去重命中。
  5. 把两个 mock 外部 API 藏在单一客户端后,使上游调用点不变。

一、问题与直觉

为什么两遍检索

关键词搜摘要返与查询共享词汇的论文,覆盖大部分表面。它漏两种情况。第一,奠基论文用不同词汇——查「稀疏注意力」漏了标题「transformer 路由中的块选择」的论文。第二,相关论文是引已知锚点的后续——找锚点向前走比暴扫摘要池高效。

检索客户端拥有两遍与合并。调用方递查询,得排好序列表,每条带每论文分字段(bm25_score/graph_distance/recency_score/final_score)解释排序。

Paper 的形状

Paper id : str (稳定标识, mock 语料用 "p001") title : str abstract : str year : int authors : list[str] references : list[str] (此论文引的论文 id) citations : list[str] (引此论文的论文 id) source : str (哪个 mock api 供的, "arxiv" 或 "s2")

referencescitations 构成有向引用图。两 mock API 返重叠但不同的字段,语料加载器在 id 上并。

二、从零实现

从零 BM25

实现是标准 Okapi BM25,默认参数 k1=1.5b=0.75。索引是两个字典:term -> doc_frequencyterm -> list of (doc_id, term_count)。文档长是摘要 token 数,平均文档长在索引建时算一次。打分查询是查询词项的 idf * tf_norm 之和,tf_norm 是标准 BM25 长度归一项频。分词是 lower 后按非字母数字切,无词干还原(生产换小词干还原器,接口不变)。

idf(t) = log((N - df + 0.5) / (df + 0.5) + 1.0) tf_norm(t) = (f * (k1 + 1)) / (f + k1 * (1 - b + b * dl / avgdl)) score(d, q) = sum over t in q of idf(t) * tf_norm(t)

引用图遍历

图从语料建一次。前向边从论文到其 references,后向边从论文到其 citations。遍历是以 BM25 top 命中为种子的广度优先搜索,封顶两跳。两跳是刻意上限:一跳太浅(代理常要直接祖或后裔);三跳在连通图上炸结果规模、易跑题。hop 上限作配置旋钮暴露。

去重与排序

两遍返重叠集。合并按论文 id 键。每论文终分是加权混合:

final_score = w_bm25 * bm25_score_norm + w_graph * graph_score + w_recency * recency_score

bm25_score_norm 是 BM25 分除以合并集最大 BM25 分(使字段落零到一)。graph_score 直接词法命中一、一跳 0.6、两跳 0.3、否则零。recency_score 是从语料最小年到最大年的线性斜坡。默认权重 0.5/0.3/0.2,作配置;陈题调低 recency,快题调高。

mock 语料与 API

语料是 build_corpus() 生成的百篇论文,每篇手写标题与摘要,覆盖五主题:注意力稀疏、检索增强、低秩适配器、数据集蒸馏、评估框架。references 与 citations 接线使每主题成连通子图、带几条跨主题边。两 mock API 客户端(ArxivMockClientSemanticScholarMockClient)读同语料但暴露不同字段:Arxiv 返 title/abstract/year/authors,Semantic Scholar 加 references/citations。检索客户端在 id 上并。

code/main.py 定义 PaperArxivMockClientSemanticScholarMockClientBM25IndexCitationGraphRetrievalClient 与确定性 demo。BM25 实现一类约 60 行,图遍历一方法。code/tests/test_retrieval.py 覆盖词法路径、图路径、合并、去重、空查询。

设计要点:检索客户端返 RetrievalResult 含排好序列表与每查询指标(命中数、均分、top 分、总墙时间),运行器日志这些使下游可观测遍能绘质量随时间。第 52 节运行器读 paper.idpaper.title 与摘要前三句作实验上下文;第 53 节评估器读 paper.yearpaper.references 把基线归到具体论文。检索客户端是四阶段中最廉的,在编排器中最先跑。

三、框架对比

真实学术检索用 Semantic Scholar API、arXiv API、Google Scholar(无官方 API,需爬)。BM25 是经典(1994,Elasticsearch/Lucene 默认),生产常配 dense 检索(FAISS + 句嵌入)做混合检索——词法抓精确匹配,dense 抓语义相关。引用图遍历是 Connected Papers、ResearchGate、Inciteful 的核心。本节手写让你看清 BM25 的 idf * tf_norm、图遍历的 BFS hop、混合排序的加权。换真 API 只需 mock 客户端替成 HTTP 调用,接口不变。RAG 系统(第 64~69 节)的检索层用同模式,只是语料是文档而非论文。

四、可复用产物

code/main.py + code/tests/test_retrieval.pyRetrievalClient.search(query) -> RetrievalResult 是编排器入口,产排好序列表供第 50/51 节消费。BM25IndexCitationGraph 可独立复用——任何「词法 + 图」混合检索场景。换真 API(Semantic Scholar/arXiv HTTP),只需替两 mock 客户端,检索客户端与排序逻辑不动。

五、练习

  1. dense 检索:加句嵌入 + 余弦相似度作第三遍,与 BM25 混合,对比召回。
  2. 词干还原:加分词器词干还原(Porter),对比 BM25 命中率。
  3. hop 扫描:扫 hop 上限 1/2/3,绘结果规模与相关性。
  4. 真 API:把 ArxivMockClient 换成真 arXiv API(免费、无需 key),对真实查询跑检索。
  5. recency 调权:对快题(如 LLM)调高 w_recency,对陈题(如 SVM)调低,观察排序变化。

本节要点回顾

  1. 两遍检索:BM25 词法 + 引用图遍历,各抓一类漏网。
  2. BM25 从零:idf * tf_norm,纯 stdlib 两字典索引,默认 k1=1.5/b=0.75
  3. 图遍历两跳:BFS 从 BM25 top 命中扩,两跳是刻意上限防跑题。
  4. 加权混合排序:bm25_norm/graph/recency 三分,权重作配置。
  5. mock API 藏后:两客户端读同语料暴露不同字段,检索客户端在 id 上并。
  6. 最廉最先跑:检索客户端是四阶段中最廉,在编排器中最先。

下一节,我们做「实验运行器」——把实验编成类型化 spec,在沙盒子进程里执行,发评估器可信任的 json 指标 blob。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U