模型答错,很多时候不是不会,是记错了,或者记的还是旧的。
RAG 让它回答前先查一遍资料:把外部文档当成一本随时能翻的书,查完再写。
原文:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
2020 年 5 月发布,NeurIPS 2020。作者是 Patrick Lewis 等,来自 Facebook AI Research(今天的 Meta AI)、伦敦大学学院和纽约大学。
一、知识被压在参数里
问模型「法国首都是哪」,它答「巴黎」。
这个知识不在任何文件里。训练时,它被压进了模型的参数。论文管这叫参数化记忆。
好处很直接:快。不用翻任何东西,一次计算就能答。
代价有四个,而且都不是调参能解决的:
- 改不了。 训练截止之后发生的事,模型不知道。要更新,只能重新训练。
- 说不清出处。 参数里没有「这段话来自维基百科的哪一行」这种信息。
- 记混了也不自知。 冷门事实记错了,它照样答得很肯定。这就是幻觉。
- 想多记,就得做大。 知识量和参数量绑在一起,模型越大,训练和推理越贵。
「压进参数」是什么意思?前面讲训练时说过:训练就是做一道道填空题,每做一道,把参数拧一点,让正确答案的概率变大。
「法国的首都是___」这类题做得多了,模型看到「法国首都」,就越来越倾向于接「巴黎」。
但这份知识没有存在某一个数字里。它散在几亿甚至几千亿个参数中,每个参数只沾一点点,同时还沾着成千上万条别的知识。
上面四个代价都从这里来。想改「巴黎」,没有一个单独的数字可以改;想问出处,参数里也没留下是哪道题教的;想多装知识,就得有更多参数。
顺便说一句,「训练和推理越贵」里的「推理」,指的是模型每回答一次要做的那一遍计算(英文 inference),不是「动脑想问题」。后面讲 ReAct 时说的「推理」,才是动脑想的那个意思。
论文开头讲的就是这件事:预训练模型存了很多知识,但访问和修改这些知识的手段很弱。
一个自然的想法是开卷考试。但开卷的难点不在「有书」,在「知道该翻哪一页」。翻错页,比不翻还糟。
二、先查,再写
RAG 是 Retrieval-Augmented Generation 的缩写,中文叫「检索增强生成」。
拆开看:Retrieval 是检索,Augmented 是增强,Generation 是生成。
连起来就是:先检索资料,再让模型带着资料生成答案。
一次问答分两步:
- 查:把问题交给检索器,从资料库里取回最相关的几段。论文取 5 段或 10 段。
- 写:把问题和这几段资料拼在一起,交给生成模型,让它写出答案。

论文里的配置很具体:检索器用 DPR,生成器用 BART-large,资料库是 2018 年 12 月的英文维基百科。整部维基切成 2100 万段,每段 100 个词。
切段是必须的。论文用的 BART 一次只能读一千个 token 上下,整本维基百科塞不进去。今天上下文窗口长了很多,但一次塞几百万字依然又慢又贵。资料一多,模型还容易漏看中间的部分。这个后面再说。
BART 是 Facebook(今天的 Meta)在 2019 年发布的模型,读一段话、再写一段话,摘要、问答都能干。large 是它的大号版本,后面讲成绩时会说到它约有 4 亿参数。DPR 是检索器,后面细讲。
2100 万段、每段 100 个词,加起来是 21 亿个词。一本书按 10 万词算,相当于两万多本书。
token 就是前面讲过的词块。就算一个词只算一个 token,21 亿 ÷ 1000 = 210 万,整部维基也比 BART 一次能读的长两百多万倍。
在 RAG 之前,「先查再答」已经有了,比如 REALM 和 DPR 自己。它们的做法是抽取:查到资料后,从里面挑一段原文当答案。
RAG 把最后一步换成了生成。模型读完资料,用自己的话写。答案不必在原文里逐字出现,几段资料的信息还可以拼成一个回答。
这个改动听起来不大,但它把检索和生成放进了同一个训练目标。这是这篇论文最关键的地方。
「同一个训练目标」,可以想成小组作业:一个同学查资料,一个同学写报告,两人拿同一个分数。
报告得分低,写报告的要改,查资料的也得反思找的资料行不行。没人告诉查资料的同学该找哪本书,他只能从分数里慢慢琢磨。
RAG 里,检索器是查资料的同学,生成器是写报告的同学,分数是「最后答案对不对」。这件事怎么做到,后面会算给你看。
三、怎么知道该翻哪一页
先看字面匹配错在哪。
问:「iPhone 是哪家公司做的?」
资料库里有两句话:
- 「苹果公司发布了新手机。」
- 「这个苹果又脆又甜。」
第一句一个「iPhone」都没有,却正好能回答;第二句有「苹果」,却和手机无关。
按字面匹配(比如 BM25 这类词频方法),第一句可能因为「手机」得一点分,第二句因为「苹果」得一点分,分数拉不开。词频方法不知道「iPhone」和「苹果公司」指的是同一家公司,也不知道两个「苹果」不是一回事。
要按意思匹配,就得把「意思」变成能计算的数字。
一个只有两个分数的玩具
假设每句话只打两个分:
- 手机分:跟「手机」有多大的关系,0 到 10 分;
- 水果分:跟「水果」有多大的关系,0 到 10 分。
四句话的分数是:
| 句子 | 手机分 | 水果分 |
|---|---|---|
| 苹果公司发布了新手机 | 9 | 1 |
| iPhone 上市了 | 8 | 0 |
| 这个苹果又脆又甜 | 0 | 9 |
| 香蕉放久了会变黑 | 1 | 8 |
把手机分当横轴、水果分当纵轴,四句话就成了纸上的四个点:

讲手机的两句挤在右下角,讲水果的两句挤在左上角。距离不是人摆出来的,是分数本身决定的。
提问时,问题也打这两个分。「iPhone 是哪家公司做的?」手机分高、水果分低,点落在右下角附近。离它最近的两段,正是讲手机的那两句:

到这一步,有个地方容易讲糊:分数是人打的,所以「意思近,位置就近」看起来像一句废话。
真实系统里,分数不是人打的。这句话得展开说。
真实系统:768 个数字
真实系统里,每段文字不是一个二维点,而是一串 768 个数字。这串数字叫向量。
每个数字对应一个维度。但这些维度没有「手机分」「水果分」这样的名字,人也说不清第 37 个数字代表什么。它们是模型在训练里自己学出来的,不是人设计的。
768 这个数是哪来的?DPR 用的是一个叫 BERT 的模型(下面会讲),BERT 交出来的每串数字正好 768 个,DPR 就沿用了这个长度。
还有一点和前面讲的词嵌入不一样。词嵌入是一个 token 一串数字;这里是一整段 100 个词,最后只交出一串 768 个数字。
做法是:BERT 在每段文字最前面放一个特殊 token,再把整段读一遍。读完以后,这个特殊位置上的那串数字已经「看过」全段,就拿它当整段的数字摘要。问题也一样,整个问题只交一串。
两段文字的向量有多接近,用点积算:对应位置的数字相乘,再全部加起来。两个向量在多数维度上方向一致,点积就大;方向拧着,点积就小。
用玩具版的分数试一下。假设问题也是(手机 9,水果 1):