深入淺出RAG經典論文

Yu-Ting Ye
15 min readJun 24, 2024

--

前言

上次撰寫了適合普羅大眾的《RAG技術揭密:推動各大專業領域AI發展的關鍵》,今天則是針對RAG原論文進行詳解。

本篇建議有些許資料科學背景,或者不排除數理公式的讀者進行閱讀

《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》是2020年Facebook AI Research發表的論文,至今仍極具影響力。

RAG的架構

要完整解釋RAG在做什麼,直接看下方架構圖,以較白話的方式說明,首先從左方3個範例問題開始,問題(Define “middle ear”)輸入後會跟多個預先建立好的文檔(Documents)進行相似度比對,比對出相似程度最高的前k個文檔,也就是試圖找出跟”middle ear”有關的文檔,將問題跟k個文檔一起提供給語言模型生成答案(The middle ear includes the tympanic cavity and the three ossicles.),透過這個方式,語言模型每次在回覆一個問題時,就能及帶著這些小文檔一起思考,具備輔助也同時限制語言模型的思考範圍,進而減少AI思考時產生幻覺(hallucination)

Overview of RAG approach

上述對於實作上仍過於籠統,將輸入的問題(Query)定義為x,此時問題x先經過一個Query Encoder q得到q(x)q(x)也就是Query的Embedding,目的就是為了將非結構化的文字資訊轉換成可以量化的向量型態,簡言之將文字映射到向量空間;這個Query Encoder論文採用BERT,Query Encoder用的BERT定義為BERTq,將x送進這個BERTq的結果就是q(x)

Query Encoder and Document Encoder

另一方面,事先準備好一些文檔(Document),論文採用Wikipedia,然而,要一口氣將Wikipedia所有文字輸入給LM是不切實際的,即使單頁的文字量也很龐大,實務上會對Document進行切分(Split),後續問題其實是跟切分後的段落(Chunk)進行比對,切分的方法有很多,論文採取每100個單字(word)作為一個小段落(Chunk)定義為z,每個小段落z會各別投入Document Encoder d得到d(z)d(z)也就是Chunk的Embedding,目的一樣是將每個小段落映射到向量空間;這個Document Encoder論文採用BERT,Document Encoder用的BERT定義為BERTd,將每一個小段落z1, z2, z3, z4, …分別送進BERTd就會得到d(z1), d(z2), d(z3), d(z4), …等向量,此時這些文檔向量的數目非常龐大,會特別建立一個向量資料庫(Vector Store)系統來存放,並統稱為Document Index,由於這些Document Index資料並不存放在Generator本身的參數記憶(Parametric Memory)之中,因此又稱為非參數記憶(Non-Parametric Memory)。

MIPS — 搜尋演算的兵家必爭之地

方才提到這些文檔向量數目很龐大,想想每當有一個用戶,問了一個問題,你就得將一整棟名為Wikipedia的圖書館把每一本書每一個段落全部讀過一遍,大海撈針般把最相關的數個文章段落從不同的書籍給提取出來,以現在晶片的算力所需的時間,還沒辦法用簡單暴力的方法靠硬體直接符合商用場景,勢必得靠搜尋演算法優化

給大家參考一下Google網站核心體驗指標(Core Web Vital)

Google網站核心體驗指標

拿ChatGPT介面來舉例,你詢問一個問題,等到完整答案全部列出,大概要10–20秒不等,GPT-4則需要更長的時間來得到更高品質的回覆,這也是為什麼在ChatGPT的介面一定要用串流(Streaming)的方式來呈現AI回覆結果,因為要盡可能在4秒之內,給出回覆的第一個字,否則使用者等的不耐煩,很有可能會直接離開頁面,想像你在現實中跟一個人講話,每次你都要等他4秒他才開始講話,如非特殊狀況,很可能會想盡早結束對話,而如今遇到的問題更艱難:你跟AI對話,他還必須先去Open Book翻完一堆書找答案,才能回答你的問題,時間更加急迫,而AI在翻一堆書找答案的過程,就是MIPS,所以一定要優化MIPS流程,才能解決回答效率問題!

到底MIPS怎麼做?

MIPS全名為Maximum Inner Product Search,中文可稱「最大內積搜尋」,要完整理解專有名詞,最基本要理解內積(Inner Product)在做什麼,先針對一些數學代名詞做一些舉例,比較好理解:

x = "中耳是什麼?" # 輸入問題 
z1 = "中耳就是位於鼓膜後面的那個充滿空氣的空間,連接外耳和內耳的結構" # 文檔段落1
z2 = "中腦在腦幹的最前方,和視覺、聽覺、運動控制、睡眠、甦醒、警覺、及溫度調控有關" # 文檔段落2

將輸入問題(x)轉換成Query的Embedding得到q(x),另一方面也將文檔段落(z)轉換為Chunk的Embedding得到d(z),接著要計算q(x)跟所有的d(z1), d(z2), d(z3), d(z4), …個別的內積,也就是 q(x) ・d(z1) , q(x) ・d(z2) , q(x) ・d(z3) , …,以下舉例:

Embedding範例

一個好的Query Encoder將文字映射到向量空間後,能夠得到優質的Embedding並很好的量化其文字潛在的語意性質,以下舉例:

Inner Product範例

現實場景中能達到的效果是:「中耳是什麼?」跟「中耳就是位於鼓膜後…」的關聯性比較高,相反地與「中腦在腦幹的最前方…」關聯性比較低,我們則能期望能得到q(x) ・d(z1) > q(x) ・d(z2) ,這就是內積作用在優質Embedding的奇效。

MIPS方法就是從透過從q(x)・d(z1), q(x)・d(z2), d(z3), ….,找到能夠最大化內積結果的z ,實務上會採用Top-k的策略,也就是找到前k個內積最大值的z,如此就能從輸入問題(x),進而找到關聯性最高的前k的文檔段落(z)。

等等!你前面提到文檔用Wikipedia,那麼z豈不是個超過上千億以上個段落,d(z)則是數千億以上的高維度向量,MIPS如果每次都要暴力計算完所有d(z)跟q(x)內積,在全部進行由大到小排序取得Top-k,對算力不足的中小企業來說未免太不切實際了!

[補充] ANN — 暴力不能解決問題

如果現今硬體還無法任由我們透過Brute-Force Search進行有效率的MIPS,那我不要全局最佳解,追求局部最佳近似解總行了吧!而MIPS的近似解法常使用ANN(Approximate Nearest Neighbor, 近似最鄰近),這個方法的概念是這樣,每次要q(x)要跟所有d(z)計算太冗長的,既然d(z)是基於文本的Embedding,就可以事先運算好所有d(z),然後對d(z)先進行分群(Clustering),也就是對d(z)整個向量空間進行分割(partition)成多個子空間,具體一點來說,將d(z)的整個宇集合分割成多個子集合,今天當問題(x)輸入後,先快速匹配最合適的子集合,接著只要對子集合進行Brute-Force Search就好了。

https://ann-benchmarks.com/

ANN又可以再細分成Tree-base、Hash、Vector Quantization三種面向的搜尋方法,像是Meta(Facebook)團隊提出的Faiss、Google團隊提出的ScaNN,都是基於Vector Quantization的方式來優化MIPS,並沒有說哪一個ANN搜尋演算就一定比較好,根據任務或需求不同,建議可以到ANN的Benchmark先參考比較 https://ann-benchmarks.com ,拿glove-100-angular benchmark 來說,像ScaNN曾在Recall@10指標直上SOTA(state-of-the-art),不過Faiss在各大任務的速率跟準確率上也都達到不錯的平衡。

Retriever — RAG可不光一種用法

接下來這小節需要一點機率背景,這個給定問題(x)計算檢索文本(z)的機率分佈Pη(z|x)就是Retriever。生成器(Generator)則是給定輸入(x)跟檢索文本(z)去計算生成Token的機率分佈Pθ(y|x,z),論文使用BART是BERT結合GPT的語言模型,仍為token by token的輸出方式。根據Retriver與Generator之間的不同搭配,又可再細分為「RAG-Sequence Model」以及「RAG-Token Model」。

RAG-Sequence 模型

RAG-Sequence模型在生成完整的序列前都使用相同的檢索文本。它將檢索的文件視為單一的隱變量,通過 top-K 近似來邊際化(marginalize)以獲取 seq2seq 機率 p(y|x)。使用檢索器檢索前 K 個文件,然後生成器為每個文件生成輸出序列機率,再被邊際化,近似公式如下:

RAG-Token 模型

在 RAG-Token 模型中,每個目標 token 都能抽取不同的檢索文件並相應地邊際化。這允許生成器在生成答案時從多個文件中選擇內容。使用檢索器(Retriever)檢索前 top-k 個文件,然後生成器為每個文件的下一個輸出 token 生成分佈,接著邊際化,並重複這個過程生成後續的輸出 token。近似公式如下:

至於兩種不同方法的效果比較,不如直接來看Benchmark。

Benchmark — RAG怎麼打出效果拔群

1. 真實性、特異性

先看最好理解的Table 4:人類評估BART和RAG模型在Jeopardy問題生成任務中的表現。評估標準包括真實性(Factuality)和特異性(Specificity)。

RAG模型在真實性和特異性上都遠勝於BART,分別達到42.7%和37.4%懸殊的優勢比例,這讓RAG在更適合在知識密集任務下提供更具體的回答,而真實性的顯著提升也讓消除幻覺的困境多了一絲希望。

2. 多樣性

在生成任務中,不同模型生成的文本中獨特三元組佔總三元組的比例。測試數據集包括MSMARCO和Jeopardy QGen。

Table 5為生成任務中獨特三元組(tri-grams)佔總數比例,RAG模型在生成Jeopardy問題時,RAG-Seq在生成文本的多樣性上表現出色,比BART和RAG-Token更接近於Gold標準答案。這些結果顯示RAG模型(特別是RAG-Seq)在生成自然且多樣化的文本上有顯著優勢。

3. 準確性、相關性

Table 1展示了開放領域問答(Open-Domain QA)測試成績。對於TQA,左列使用標準測試集進行測試,右列使用TQA-Wiki測試集。具體分數如下:

(1) Closed Book 模型:不使用外部知識,只基於內部參考生成答案。T5–11B 和 T5–11B+SSM 都屬於這類模型。

(2) Open Book 模型:使用外部知識庫來輔助生成答案。REALMDPR 屬於這類模型。這些模型通常在開放領域問答測試中表現較好,因為它們可以訪問更多的知識。

(3) RAG 模型:結合檢索和生成的模型,分為 RAG-Token 和 RAG-Sequence。

RAG-Token 和 RAG-Seq 在大多數測試中均表現良好,顯示出在開放領域問答上的優勢。特別是 RAG-Sequence 在 NQ 和 TQA 測試中表現最好,顯示了其在整體序列生成上的優勢。RAG 模型無論是在開放領域問答還是生成和分類任務中,RAG 模型(特別是 RAG-Token)均表現優異。這是因為它能夠靈活地從多個文檔中選取內容進行生成,從而提高答案的準確性和相關性。

4. 消融實驗

Table 6展示了不同RAG模型變體的消融實驗(Ablation Study)。消融實驗是為了觀察不同模型組件對整體性能的影響。例如:可以懷疑生成器有沒有訓練、參數更不更新說不定沒有顯著差異。具體測試指標包括NQ、TQA、WQ、CT、Jeopardy-QGen、MSMarco R-L和B-1、以及FVR-3和FVR-2分類準確率。

  1. RAG-Token-BM25/RAG-Sequence-BM25:使用BM25檢索技術。
  2. RAG-Token-Frozen/RAG-Sequence-Frozen:使用凍結的生成器參數。
  3. RAG-Token/RAG-Sequence:標準RAG模型。

Jeopardy-QGen 測試中,RAG-Token在B-1和QB-1測試中表現最好,分別達到17.9和22.6分。FVR-3和FVR-2 分類準確率測試中,RAG-Token和RAG-Sequence的表現較為接近,但RAG-Sequence在FVR-2上的準確率略高,達到90.6%。整體來說,RAG標準模型仍比其他表現更優異,其模型組件對整體性能接有一定程度的提升

結論

RAG模型在多種基準測試中展現了其強大的性能。相比Closed Book模型,RAG模型能夠結合檢索系統的優勢,有效提升答案的準確性和相關性特別是RAG-Sequence在開放領域問答、生成和分類任務中均表現出色。RAG-Token則在生成特定內容時表現更為靈活,能夠動態選取最相關的文檔來生成準確的答案。

在人類評估中,RAG模型的真實性和特異性均優於BART,顯示出更高的答案準確性和特異性。在生成文本的多樣性上,RAG-Seq接近於Gold標準答案,而RAG-Token在MSMARCO數據集上的生成多樣性也表現良好。

消融實驗結果顯示,標準的RAG模型在大多數測試中均表現更佳。RAG模型結合了檢索和生成的優勢,能夠在多樣化的應用場景中提供高品質的文本生成,特別是RAG-Sequence在生成和分類任務中顯示出其強大的能力。

這些結果都說明了RAG模型在不同的應用場景中具有顯著的優勢,能夠生成更準確、相關且多樣化的答案,為自然語言處理領域提供了一個強大的工具,也具備潛力消除大型語言模型的幻覺問題。

[補充] 過往與現今的RAG怎麼實現?

RAG論文發表於2020年,當初使用的BART約莫400M參數量,然而現今(2024)已經有許多超過20B以上的大型語言模型(LLM)可以直接作為生成器(Generator),再考量到算力及MIPS效率問題,大多RAG架構以RAG-Sequence模型實現,且Retriver和Generator會採納獨立不同來源的模型

2020年舊的RAG,使用的是BART(~400M)

https://huggingface.co/facebook/rag-sequence-base

https://huggingface.co/facebook/rag-token-base

2023後較新的RAG可參考Langchain框架搭配HuggingFace來實現,以下範例是用本地端可運行的Phi-3-mini-4k-instruct (~4B),你也可以改用ChatGPT的API,詳細用法可參考以下兩個連結:

後記

寫文章實在是相當花時間的事情,通常我是在空閒時在手機上邊撰寫部分想法跟內容,然後下班後或週末另外撥時間補上細節。但林林總總來說還是相當費時,更佩服過往所有願意貢獻在網路公開資源上的前輩們,希望這些精神也有感染到您。

如果您喜歡這邊文章的話,還請不吝於拍50下手👏👏🏻👏🏼👏🏿👏🏽,您的支持是我繼續撰寫的動力!

--

--

Yu-Ting Ye

Data Scientist, familiar with Python, interested in Statistics, ML, DL and crazy about IMDB’s 250 top rated movies.