跳到主要內容

RAG整理

RAG(檢索增強生成)課程重點整理

RAG,全名為檢索增強生成(Retrieval-Augmented Generation),有時也稱作檢索增強語言模型(Retrieval-Augmented Language Model, RALM)。它是一種旨在透過允許語言模型從外部來源檢索資訊來增強其能力的技術。

一、 RAG 的基本框架與必要性

1. RAG 是什麼?

RAG 是一種提升語言模型(LM)能力的方法,使其能「查閱」外部知識庫,而非僅依賴訓練時所學的靜態資訊。

2. RAG 為何必要?語言模型的局限性:

  • 長尾知識與幻覺 (Hallucination):語言模型難以準確回憶訓練資料中不常出現的「長尾」資訊,容易產生看似合理但實際上錯誤的「幻覺」。例如,模型能正確識別蔡英文,但可能對陳縕儂的細節產生幻覺。RAG 透過查詢外部知識來補充這些「小眾」知識。
  • 知識過時 (Outdated Knowledge):語言模型的知識固定在其訓練時間點,無法獲取即時或頻繁更新的資訊(如即時股價、最新選舉結果)。雖然「知識編輯」是更新LM知識的研究領域,但RAG提供了一種透過即時資料檢索來解決過時知識的方法。
  • 缺乏溯源性與可驗證性 (Lack of Traceability and Verifiability):傳統語言模型通常不提供資訊來源,難以驗證其答案。RAG 可以為檢索到的文件提供明確的來源,提升答案的可驗證性。

3. 基本 RAG 的實作流程 (早在2020年就已存在)

最基礎的 RAG 方法包含在生成過程開始時進行單次檢索:

  1. 查詢輸入 (Query Input):使用者提供一個查詢。
  2. 檢索 (Retrieval):檢索器元件從外部知識來源(如網路或內部資料庫)搜尋相關的文本片段或文件,這就像是當你不確定時「打開書本」查找資訊。
  3. 拼接 (Concatenation):將檢索到的資訊直接與原始查詢拼接起來。
  4. 生成 (Generation):這個組合後的輸入(查詢 + 檢索到的上下文)隨後被送入語言模型的輸入層,LM 根據這些資訊生成回應。這種方法能顯著減少幻覺並解決資訊過時問題。WebGPT 模型就是一個例子,它模仿人類搜尋行為,生成搜尋查詢,選擇相關結果,並引用來源來回答知識密集型問題。

二、 進階 RAG 技術:提升性能

進階 RAG 專注於優化 RAG 流程的各個階段,以實現更好的性能、效率和靈活性。這些改進可以根據 檢索什麼 (What to Retrieve)如何使用檢索到的資訊 (How to Use)何時檢索 (When to Retrieve) 來分類。

A. 提升檢索性能 (預檢索與後檢索)

Pre-Retrival

  • 查詢重寫 (Query Rewriting):在檢索之前,可以重寫或擴展使用者的查詢,使其更適合搜尋。
  • 上下文豐富 (Context Enrichment):不僅檢索單個文本片段,還檢索其周圍的上下文(例如前後段落),以提供更豐富的資訊供 LM 理解。
  • 混合檢索 (Hybrid Retrieval):結合不同的檢索方法,利用各自的優勢並彌補其劣勢:
    • 稀疏檢索 (Sparse Retrieval):依賴精確的關鍵字匹配(傳統搜尋引擎行為),當查詢詞直接出現在相關文件中時很有效,例如:BM25。
    • 密集檢索 (Dense Retrieval):使用語義相似性(如嵌入向量)來查找與查詢概念相關的文件,即使它們沒有共享精確的關鍵字。

Post-Retrival

  • 重排序 (Reranking):在從大量文件進行初步高效檢索後,對潛在相關文件的一個較小子集應用重排序步驟。這使用更複雜(通常更大)的模型來精確評估每個文件與查詢的相關性,確保最相關的資訊優先傳給語言模型。

    • 逐點重排序 (Pointwise Reranking):每個檢索到的片段單獨評分其與查詢的相關性(例如,從1到5分)。
    • 成對重排序 (Pairwise Reranking):直接比較兩個檢索到的片段,判斷哪個更相關。這通常更準確,提供更清晰的相對比較,但計算成本更高(N平方複雜度)。
    • 零樣本重排序 (Zero-Shot Reranking):大型語言模型(LLM)無需特定訓練資料即可執行重排序,只需透過提示來評分或比較片段的相關性,實驗證明其性能可與監督式重排序器媲美。
    • 級聯重排序 (Cascaded Reranking):結合逐點和成對方法的實用方法。例如,初步的逐點排序可以篩選候選,然後對較小的頂部子集應用成對排序以精煉順序,降低總體成本同時保持準確性。
  • 上下文壓縮 (Context Compression):如果多個檢索到的文件包含冗餘資訊,可以使用技術來壓縮或總結它們,為語言模型提供更緊湊和易於理解的輸入,使其更容易吸收資訊並生成更好的回應。

A.1 查詢重寫 (Query Rewriting)

  • 方式一:LLM 作為 Rewriter

  • 使用 OpenAI / Azure OpenAI API 呼叫模型(如 gpt-3.5-turbo)

  • 將改寫後的查詢送進向量 Embedding 模型(如 text-embedding-3-small)

範例:

prompt: 你是一個查詢重寫助手。請將下列問題改寫成適合搜尋知識庫的完整問題:

原始問題:「哪個方法能用來縮小資料?」

→ 改寫後:「在 C# 中,有哪些方法可以用來縮小大量資料的大小?」

註:HuggingFace 提供許多 Query Rewriting 模型:castorini/t5-base-canard 、facebook/bart-large

  • 方式二:Rule-based(適合限定範圍) 例如針對某些關鍵詞進行同義詞替換或語意展開:
if (query.Contains("壓縮"))
    query = query.Replace("壓縮", "資料大小壓縮方法");
  • 方式三:使用 Chat History 做 Contextual Rewriting

記錄使用者的歷史查詢紀錄,改寫成具語意的完整句子再透過LLM來完成,問題改寫

  • 以下由GPT整理的方法的呦
方法 精度 成本 多語支援 建議情境
Rule-based ⭐⭐ 小型系統、規則明確場景
Classification + 模板 ⭐⭐⭐ 有查詢意圖分類邏輯時
Seq2Seq 模型(T5/BART) ⭐⭐⭐⭐ ✅✅ 多樣查詢、語境模糊、需要通順回應
Embedding + 相似查詢 ⭐⭐⭐ 有歷史查詢語料
Prompt-based LLM ⭐⭐⭐⭐ 中~高 ✅✅ 使用 GPT/LLM 的高效場景

A.2 上下文豐富 (Context Enrichment)

  • 方法 1:Sliding Window Chunking(滑動視窗分段) (1) 在文件建構向量索引時,就把每段「滑動」形成交疊的 context。 (2) 查詢時即便命中的是中間段,也能涵蓋上下文。 (3) 每 chunk 約 100~300 字元,可考慮重疊 20~30% 提高連貫度

A.3 混合檢索 (Hybrid Retrieval)

  • 向量檢索(Vector Search) + 🔤 關鍵字檢索(Keyword / Lexical Search)
  • | 技術 | 優點 | 缺點 | | ----- | ------------------- | ---------------------- | | 向量檢索 | 語意相似度高,可容錯、理解不同表達方式 | 無法精確處理專有名詞、拼字錯誤時效果差 | | 關鍵字檢索 | 精確、高查全率,擅長處理短語、代碼等 | 容易遺漏語意相似但字面不同的內容(如近義詞) |

  • 方式 :Parallel Hybrid Search(並列混合) 將查詢同時送入: (1) 向量資料庫(語意檢索,例如:pgvector, FAISS, Qdrant)\ (2) 關鍵字資料庫(全文檢索,例如:Elasticsearch、Lucene、PostgreSQL + tsvector) 兩邊的結果合併後:去掉重複 -> 重排序(Reranking)->Top K 取前幾筆

A.4 重排序 (Reranking)

  • 整理
類型 準確性 成本 適合場景
Pointwise ⭐⭐ 大量初步過濾用
Pairwise ⭐⭐⭐⭐ 少量候選、高精度任務
Zero-Shot ⭐⭐⭐⭐ 無需資料集、快速部署
Cascaded ⭐⭐⭐⭐ 中~高 結合準確率與效能的最佳化策略

(1) 逐點重排序(Pointwise Reranking) 每個片段單獨對照查詢評分(如 1~5 分),用 BERT 得到 score = sim(query, doc)或是對每個 doc individually 使用 prompt 讓 LLM 打分。

(2)成對重排序(Pairwise Reranking) 比較兩個片段 A 與 B,哪個對查詢更相關。使用RankNet、LambdaMART或是用用 prompt engineering. 計算與推理成本高:複雜度 O(N²),若排序 20 筆資料,需比較 190 對

(3)零樣本重排序(Zero-Shot Reranking) 無需訓練資料,直接使用 LLM 提示(prompt)來比較或打分,比較像監督學習模型相當

(4) 級聯重排序(Cascaded Reranking 先快速進行逐點評分,篩選前 10,再進行成對比較,得到更準確排序 例如:初步檢索 Top-100 -> Pointwise Rerank(Top 10) -> Pairwise Rerank -> 送入 LLM(Top-3)

A.5 上下文壓縮(Context Compression)

  • 為什麼需要上下文壓縮?
問題 上下文壓縮解法
Token 數超過上限 壓縮長文本,精選精要段落
檢索到的資訊重複太多 做資訊去重、合併重點
檢索結果不夠聚焦 摘要後僅保留回答查詢需要的核心內容
多個來源資訊不一致 用摘要或整合方式消除矛盾,提供一致上下文
# 技術名稱 方法說明 優點 缺點 範例 Prompt
✅ 1 抽取式摘要
(Extractive Summarization)
使用 TextRank、BERTSum、或 LLM 依據句子得分抽取 2~3 句原文中最具資訊量的句子。 - 保留原句,無虛構
- 資訊完整性高
- Token 成本低
- 可讀性不佳
- 無法整合語意
請從以下段落中挑選出最重要的 2~3 句:
「段落內容...」
✅ 2 生成式摘要
(Abstractive Summarization)
使用 GPT、T5、BART 等 LLM 生成摘要,重寫原文以強調核心概念。 - 語意自然
- 可濃縮長內容
- 可能產生虛構
- Token 成本高
請將下列段落摘要為一段 150 字的自然語言說明:
「段落內容...」
✅ 3 主題融合
(Topic Collapsing)
將多段主題相近的段落進行分群(K-means、LDA)後摘要、合併。 - 聚合多段、消除重複
- 適合多來源資料整合
- 需先做主題建模或聚類 請將下列段落合併為一段描述「壓縮技術現況」的段落:
「段落一…段落二…」
✅ 4 答案導向壓縮
(Answer-Focused Compression)
根據查詢,僅保留與問題直接相關的片段。可用 LLM 做 zero-shot 過濾,或 fine-tuned classifier 預測相關性。 - 精準對齊問題
- 壓縮比例高
- 可能漏掉潛在有用背景 根據問題「如何壓縮大檔案」,請從以下段落中選出有幫助的句子:
「段落內容...」
✅ 5 重排序 + 摘要(混合)
(Hybrid: Rerank + Summarize)
先用 reranker(如 BGE, GPT)排序段落,再摘要前 N 個段落合併成 context。 - 效果佳
- 可控性高
- 搭配 RAG 易整合
- 成本高(需兩階段) 請將以下前幾段中最重要的資訊摘要為 300 字內的段落:
「段落集合...」
類型 工具 / 方法
抽取式摘要 TextRank, Sumy, GPT extractive prompt
生成式摘要 OpenAI GPT-3.5/4, T5, BART
主題融合 KMeans, LDA + LLM
答案導向壓縮 LLM Prompt, Zero-shot classifier, RAG Retriever filter
混合壓縮 BGE-reranker + GPT summarize top-K

B. 何時檢索 (Retrieval Timing)

  • 初始檢索 (Initial Retrieval):最基本的方法,檢索在生成過程的開始階段只發生一次。
  • 迭代檢索 (Iterative Retrieval):檢索過程可以重複多次。語言模型可能會生成初步答案或根據先前的檢索結果細化其查詢,然後執行另一個檢索步驟,以獲得更準確的最終答案。
  • 上下文檢索 (Retrieval in Context, RiC):檢索可以在生成過程中多次發生。例如,每生成 'N' 個標記就觸發一次檢索,這確保了語言模型的知識保持更新,特別是對於較長的回應。然而,這種頻繁檢索會顯著增加計算成本並減慢生成速度。
  • 適應性/動態檢索 (Adaptive/Dynamic Retrieval, 例如 FATE 方法):為了降低持續檢索的高成本,系統可以動態決定何時進行檢索。語言模型評估其對即將生成文本的信心或不確定性。如果模型有信心,它會繼續生成而不進行檢索;如果它不確定(例如,關於某個特定事實),它會觸發檢索以驗證或獲取準確資訊,然後再繼續。這模仿了人類只有在不確定時才查找資訊的行為。
檢索方式 🧠 特徵說明 ✅ 優點 ❌ 缺點 📌 適用情境 🔧 實作備註
初始檢索
Initial Retrieval
檢索發生一次,位於生成前,最簡單也最常見。 - 成本低
- 整合簡單
- 無法因應查詢修正
- 不適合複雜問答
- 單輪問答
- 明確查詢
標準 RAG:查詢 → top-K → 給 LLM
迭代檢索
Iterative Retrieval
模型先生成初步輸出或修正查詢,再重新檢索(可能多次)。 - 適應性強
- 精準度提升
- 計算成本上升
- 多輪邏輯需設計
- 多輪問答
- 精確回答需校正的任務
可使用 CoT(Chain of Thought)→ 再檢索
上下文檢索(RiC)
Retrieval-in-Context
每生成一段(如每 50 tokens)就重新觸發一次檢索。 - 回應始終根據最新資訊
- 支援長輸出任務
- 成本極高
- 速度慢
- 長文件生成
- 高知識密集內容(如報告、解釋)
Streaming generation + retrieval callback
動態/適應性檢索
Adaptive Retrieval
模型判斷「是否需要」檢索(依不確定性/信心)。 - 模仿人類行為
- 成本與精度平衡佳
- 判斷邏輯需模型支援或微調 - 多段事實型生成
- 高成本任務需最小化檢索次數
參考 FATE 方法:用 token-level uncertainty 觸發檢索

C. 如何使用檢索到的資訊 (融合方法)

  • 輸入層融合 (Input Layer Fusion):這是最直接的方法,檢索到的文本與原始查詢拼接後直接送入語言模型的輸入層。雖然簡單,但如果上下文窗口有限或需要更深層次處理,語言模型可能難以充分整合資訊。
  • 中間層融合 (Intermediate Layer Fusion):檢索到的資訊可以融合到語言模型的中間層(例如,注意力層和前饋層之間)。這使得語言模型能夠透過交叉注意力等機制更有效地吸收知識。然而,這種方法通常需要特定的訓練才能學習如何有效地整合這些資訊。
  • 輸出層融合 (Output Layer Fusion, 例如 kNN-LM):資訊在語言模型的解碼過程中於標記層面進行檢索和融合。當語言模型生成每個後續標記時,它會考慮來自相似上下文的檢索到的標記,以影響下一個標記的概率分佈。這種方法非常精細,並且每個標記的計算效率可能很高,但所有潛在標記的存儲(空間)成本可能非常高。kNN-LM 的一個高級版本可以自適應地決定是使用語言模型的內部概率還是檢索到的標記概率來提高效率。

D. 檢索什麼 (多樣化的資源類型)

  • 結構化數據(structured Data) :文本片段/文件 (Text Chunks/Documents)最常見的形式,來源於網頁或內部數據集。
  • 半結構化數據 (Semi-structured Data):具有某些內在結構的數據,例如PDF文件中的表格。雖然表格可以轉換為純文本進行檢索,但這通常會導致豐富結構資訊的丟失。
  • 知識圖譜 (Knowledge Graphs, KGs):這些是表示實體及其關係的結構化數據庫(例如,「美國總統」--「唐納德·川普」)。
    • 優點:知識圖譜非常適合複雜的推理任務,並且比通用文本語料庫更容易更新以保持最新知識,通常透過手動或半自動維護(如Google的知識圖譜)。
    • 缺點:知識圖譜的覆蓋範圍通常比通用文本窄,因為它們只包含精確定義的資訊。
    • 與LM整合:檢索到的KG實體和關係(子圖)在輸入到語言模型之前可以轉換為各種文本格式(例如,三元組、自然語言句子、代碼、表格格式)。
    • 混合方法:將基於KG的檢索與傳統的基於文本的RAG結合起來可能是有益的,利用兩者的優勢以實現更廣泛的覆蓋和精確的推理。
  • 多模態數據 (Multimodal Data):對於主要基於文本的語言模型,非文本數據(如圖像)可以轉換為文本描述(例如,圖像標題)作為生成器的檢索上下文。

E. 檢索器訓練與評估

  • 雙編碼器架構 (Dual Encoder Architecture):為了訓練檢索器,通常使用兩個獨立的編碼器——一個用於查詢,一個用於文件(一種「雙塔」架構),並且通常能產生更好的性能。這是因為查詢和文件的長度和特性通常差異很大,受益於專門的編碼器。如果檢索器未經訓練(現成模型),通常會對兩者使用相同的編碼器。
  • 訓練
    • 檢索器可以以監督學習的方式,使用查詢-文件對進行訓練。
    • 也可以透過強化學習(RL)根據最終答案的性能來更新檢索器。
    • 語言模型也可以自我生成內部「檢索來源」以防止自相矛盾,儘管這會引入自我生成來源中存在幻覺的風險。
  • 評估:RAG系統的評估主要涉及兩個方面:
    • 檢索性能 (Retrieval Performance):評估檢索器是否能找到確實相關且有助於回答查詢的文件。這可以使用現有的基準數據集進行,這些數據集包含查詢-文件相關性標籤。
    • 生成性能 (Generation Performance):評估檢索增強型語言模型生成的最終答案的品質。這通常需要帶有真實答案(問答對)的人工標註數據集來比較RAG輸出。語言模型通常不適合評估RAG中最終答案的品質,因為RAG的目的正是克服LM本身的局限性。

參考的網址: 以上教學來源於台大 陳縕儂 Vivian NTU MiuLab

留言

這個網誌中的熱門文章

GSON基礎教學

GSON 前言 JSON是很常見的資料交換格式,在JAVA領域常用處理JSON的函式庫:GSON、FastXML和JSON-B,本章節會以GSON為主,學習目標如下 JSON格式說明 GSON 套件函式 GSON: 物件轉換JSON字串 GSON: JSON字串轉換物件 JSON 格式說明 JSON全名為JavaScript Object Notation,它是一種輕量級的資料交換格式,會大為流行的理由,主要是他比傳統用xml更輕巧且容易處理, JSON表達方式物件會用大括弧{},陣列則是用中括號[]。 用JSON字串來表達Employee的物件內容,由JSON字串可以知道物件name、age、sex和salary屬性。 JSON表示員工資料方式: {“name”:”Jack Bryant”, “age”:18, “sex”:”M”,”salary”:3500.00} JSON陣列表示方式: 跟我們使用JAVA的陣列方式類似,內容值可以是數字’、文字、布林、陣列、物件、null等等。 範例: 字串: [“紅”、”橙”、”黃”、”綠”、”青”、”藍”、”紫”} 布林: [true, true, false, false, true, true] GSON 套件函式 Gson為google所發布的函式庫,主要將物件與json字串之間的轉換時方便使用。當我們將JAVA物件轉換成JSON字串稱為 序列化 ,JSON字串轉換至JAVA物件稱為 反序列化 。 GSON: 物件轉換JSON字串 有了JSON基本概念後,我們進入本章重點,首先我們需要建立員工類別(Employee),定義如下 物件 屬性 員工類別 Employee name 名字 age 年紀 sex 性別 salary 薪水 /** * name:員工類別 */ public class Employee implements Serializable { //constructor public Employee(String name, double salary){ this.name = name; this.sala...

JavaBean 和POJO

前言 今天介紹JavaBean和POJO的不同,這兩個名詞在JAVA文章常常被拿來使用以及討論。在JDK1.1時候釋出才有的一個標準架構,很多時候常常被搞混,所以我們特別開闢一章來加以討論。POJO規範在企業級應用已經廣大的被使用的規範。 解釋 POJO : 全名為Plain-old-Java-object,只需要繼承Object就可以,沒有特定規定,只要建立的類別有setter/getter方法都可以稱為POJO JavaBean: JavaBean通常用來封裝多個物件成為單獨物件使用,規範比較嚴格,規則如下 規則 說明 1 需要實作序列(Serializable/Externalizable) 2 不能有參數的建構子( no-arg constructor) 3 需要有公用setter/getter 4 屬性必須要私人(private) 5 屬於特定POJO規則 比較 所有的JavaBean都為POJO,但是所有的POJO不一定為JavaBean 都可以當作重複元件 都必須序列化 特性都為可用性、易用性和持久化使用 - 應用 由圖我們可以知道POJO在應用程式中,主要用來存取資料庫資料達到持久化的目的,並提供給商業邏輯流程處理使用。這種POJO的架構提供程式人員開發時的可以很有規則將資料封裝並加以使用。 範例1. JavaBean(以員工為實例) JavaBean建立員工物件,可以發現Employee物件建構子沒有任何參數,屬性為私有化並setter/getter的命名方式。 //實作序列化 public class Employee implements java.io.Serializable{ private int id; private String name; //無參數建構子 public Employee(){} //以下實作setter/getter public void setId(int id){this.id=id;} public int getId(){return id;} public void setName(String ...

PHP與Python搭配

今天介紹如何利用php網頁呼叫目錄下的python程式工作或是資料交換,本人整理的方法有兩種 使用system()、exec()、shell_exec()呼叫程式 (1) string system ( string return_var ] ) 參考網址 官網解釋system()用來執行外部命令,返回為印出的結果,passthru()跟system()類似但是它不會返回結果。 範例1. 利用system執行ls指定並顯示在網頁上,無法使用變數保留ls的結果 檔案名稱: psystem.php $jsondata= system("ls -al", $result); 結果: (2) exec() : string exec ( string output [, int &$return_var ]] ) 參考網址 範例2. 利用exec執行python程式並可以回傳json格式給前端網頁做處理並顯示。我們ptopy.php就是可以看到callpy()為執行py的函式,它執行完pyEx01.py會將結果給$jsondata變數,做後面json解析。 檔案名稱: ptopy.php function callpy() { $jsondata= exec("/usr/bin/python pyEx01.py"); return $jsondata ; } $jsondata= callpy(); echo $jsondata ; echo " " ; $obj = json_decode($jsondata) ; echo "name:".$obj-> { 'name'} .',' ; echo "id:".$obj-> { 'id'} ; 檔案名稱: pyEx01.py import sys ...