ひとことで言うと
GPT系は、大規模なコーパスで事前学習した自己回帰言語モデルを、入力された文脈の続きとして使う方式です。GPT-3の論文では、タスクの説明と数個の例をテキストに含めるだけで、タスクごとの勾配更新やfine-tuningなしに推論する few-shot 設定を検証しています。例を見せることは重みを更新することではなく、その1回の入力の文脈を変えることです。
RAG(Retrieval-Augmented Generation)は、モデルのパラメータに蓄えた知識だけでなく、外部の検索可能な記憶から取得した文書も生成に使う構成です。検索器が候補文書を取り出し、生成器が質問と文書を条件に回答を作ります。
GPT単体は、長く訓練した技術者が記憶から答える状態です。few-shot はその場で数個の回答例を渡して作業形式を合わせる操作、RAG はさらに資料棚から関連ページを探して机に置く操作に相当します。前者は重みを変えず文脈を変え、後者は外部記憶を追加します。
なぜ必要か
従来の言語モデル利用では、タスクごとにラベル付きデータを集め、モデルをfine-tuningするのが基本でした。GPT-3論文が示したのは、モデルを大きくすることで、翻訳や質問応答などを、タスク固有の勾配更新なしに few-shot で扱える場合が増えることです。したがって試験では、few-shot を「少数例で重みを再学習する方法」と読まないことが要点です。
ただし、パラメータに知識を持たせる方式には、知識への正確なアクセス、出典の提示、世界知識の更新という問題が残ります。RAG論文はこの切り分けを、事前学習済みseq2seq生成器をパラメトリック記憶、Wikipediaの密ベクトル索引をノンパラメトリック記憶として組み合わせる形で扱いました。検索対象を更新すれば外部記憶を差し替えられるので、知識の変更をモデル全体の再学習と同一視しません。
| 課題 | パラメータ中心の生成 | RAGで分ける場所 |
|---|---|---|
| 新しい知識を参照する | 学習済み重みに依存する | 外部文書・索引を検索する |
| 根拠をたどる | パラメータから直接は取り出しにくい | 取得した文書を生成条件に残す |
| 知識を更新する | モデル側の更新が必要になりやすい | 検索対象を更新する経路を持つ |
仕組み
RAGの処理を、質問 、検索器を 、取得文書を 、生成器を と書きます。 は検索器のパラメータ、 は生成器のパラメータ、 は生成する系列です。検索器は質問に近い文書を選び、生成器はその文書を条件に出力を作ります。
ここで は検索器が返す上位 件、 は質問に対する出力系列の確率です。つまり、生成器だけにすべてを覚えさせるのではなく、候補文書ごとの検索確率を重みとして生成結果を組み合わせます。RAG論文では、系列全体で同じ検索文書に条件づける形式と、トークンごとに異なる文書を使える形式を比較しています。
GPT系の few-shot は、この検索の段階を必須にしません。入力を (タスク説明・例・質問を連結した文脈)、次に出すトークンを とすると、推論時に行うのは文脈から次のトークンを選ぶことです。例は の一部であり、few-shot のたびに を更新するわけではありません。RAGはそこへ検索結果 を加え、生成条件を にします。
| 段階 | GPT系 few-shot | RAG |
|---|---|---|
| 知識の主な所在 | 学習済みパラメータ | パラメータと外部索引 |
| 推論時の追加処理 | 文脈に例を並べる | 検索器で文書を取得する |
| 勾配更新 | few-shotごとは行わない | 論文のRAG実験ではfine-tuningする |
試験でどう問われるか
| 問われ方 | 正解に寄る条件 | 引っかけ |
|---|---|---|
| GPT系の few-shot の説明 | 少数の実例を入力文脈に含め、推論時の勾配更新なしで適用する | 例を与えるたびに重みを更新する |
| GPT系とRAGの知識の違い | パラメータ内の知識と検索可能な外部記憶を分けて考える | RAGは外部文書だけで生成し、モデルの知識を使わない |
| RAGの構成要素 | 検索器が文書を取得し、生成器が質問と文書を条件に生成する | 生成器だけを大きくすればRAGになる |
| RAGで更新しやすい対象 | 検索対象の索引・文書を差し替える発想 | 外部知識を変えるには必ず生成器を再学習する |
| RAGの論文上の比較 | 系列全体で同じ文書を使う形式と、トークンごとに文書を変える形式 | 2方式は検索器を使わない別手法である |
実装で確かめる
次のコードは、文書ベクトルとの内積で上位文書を検索し、取得文書を生成器へ渡す直前の境界を確認する最小例です。生成器そのものを実装していないため、RAGの「検索」と「生成」を混同しません。
import numpy as np
docs = np.array([[1., 0., 0.], [0., 1., 0.], [1., 1., 0.]])
query = np.array([.8, .6, 0.])
scores = docs @ query / (np.linalg.norm(docs, axis=1) * np.linalg.norm(query))
top = np.argsort(scores)[::-1][:2]
context = ["文書A", "文書B", "文書C"]
print(top.tolist(), [context[i] for i in top])
出力は [2, 0] ['文書C', '文書A'] です。ここで scores は検索器の候補順位、context は生成器に渡す外部記憶です。query や文書ベクトルを変えても、文書を差し替えるだけなら生成器のパラメータは変わりません。一方、取得結果を使わず質問だけを生成器に渡せば、それはこの構成のRAGではなく、パラメータ中心の生成です。
取り違えやすいもの
| 用語 | GPT系・RAGとの切り分け |
|---|---|
| fine-tuning | データで勾配を計算し、パラメータを更新する。GPT-3のfew-shot推論とは異なる |
| few-shot | 入力文脈に例を置く推論設定。例の提示だけでは学習済み重みは変わらない |
| パラメトリック記憶 | モデルのパラメータに分散して保持された知識。RAGでも生成器側に残る |
| ノンパラメトリック記憶 | 文書やベクトル索引のようにモデル外で検索できる記憶。RAGの検索対象になる |
| 検索・抽出型QA | 文書から答えを抜き出す構成。RAGは検索結果を条件に系列を生成する |
| プロンプトへの文書貼付 | 外部文書を文脈に渡す点は似るが、RAG論文の構成では検索器と生成器を分けて扱う |
想起チェック
few-shot の例はモデルの重みをどう変えるか
その推論の入力文脈を変えるだけで、few-shotごとの勾配更新やfine-tuningは行いません。
RAGでパラメトリック記憶とノンパラメトリック記憶は何か
前者は事前学習済み生成器のパラメータ、後者は検索可能な文書・密ベクトル索引です。検索器が後者から文書を取り出し、生成器が両方を使って出力します。
知識を更新しやすくするRAGの設計上の分離は何か
外部文書やその索引をモデルのパラメータから分離しておくことです。検索対象を更新する経路と、生成器を再学習する経路を分けられます。
RAG論文で比較された検索文書の使い方は何か
生成系列全体で同じ検索文書に条件づける形式と、トークンごとに異なる文書を使える形式です。