← 機械学習テキスト 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:プロンプティングとIn-context learning | 関連:表現学習と埋め込み(埋め込み検索)

要点(BLUF)


動機:パラメトリック知識の限界

LLM は事前学習で大量のテキストを読み、その知識をモデルの重みの中に圧縮して持っています。これを パラメトリック知識(parametric knowledge) と呼びます。便利ですが、限界があります。

重みを焼き直す(ファインチューニング)という手もありますが、知識が変わるたびに再学習するのは高コストで、出典の問題も残ります。「知識をモデルの外に置き、必要なときだけ引いてくる」 ——これが RAG の発想です。


RAG の流れ:検索 + 生成

RAG は名前のとおり Retrieval(検索)と Generation(生成)の2段構えです。ユーザーの質問が来たら、まず外部の知識ベースから関連しそうな文書を引いてきて、それを In-context(プロンプトに入れて与える) の形で LLM に渡し、その文書を根拠に答えさせます

flowchart LR
    Q["ユーザーの質問<br/>(クエリ)"] --> E["埋め込みモデルで<br/>クエリをベクトル化"]
    E --> S["ベクトルDBで<br/>近似最近傍検索 (ANN)"]
    KB[("知識ベース<br/>(文書をチャンク化し<br/>埋め込み済み)")] --> S
    S --> R["関連文書 (上位k件) を取得"]
    R --> RR["リランキングで<br/>精度よく並べ替え"]
    RR --> P["プロンプトに注入<br/>(質問 + 根拠文書)"]
    P --> G["LLM が文書を根拠に生成"]
    G --> A["出典つきの回答"]

ポイントは、LLM 自体は学習し直していないことです。同じ重みのまま、入力(プロンプト)に「正解の手がかり」を差し込むことで、ふるまいだけを変えています。これは プロンプティングとIn-context learning の in-context learning をそのまま応用した形です。

知識ベース側(図の KB)は事前に作っておきます。社内文書・マニュアル・論文などをチャンクに分割し、各チャンクを埋め込みベクトルに変換してベクトルDBに格納しておく、という準備工程(インデックス構築)です。


RAG の心臓部は「質問に意味的に近い文書をどう見つけるか」です。ここで使うのが 埋め込み(embedding)による検索です。

コサイン類似度で「意味の近さ」を測る

文書もクエリも、埋め込みモデルで高次元のベクトルに変換します(埋め込みの一般論は 表現学習と埋め込み)。意味が近いテキストどうしはベクトル空間で近くに配置されるよう学習されているので、ベクトルの近さ=意味の近さとみなせます。

近さの指標には コサイン類似度(cosine similarity) がよく使われます。ベクトル q\mathbf{q}(クエリ)と d\mathbf{d}(文書)について:

cos(q,d)=qdqd\cos(\mathbf{q}, \mathbf{d}) = \frac{\mathbf{q} \cdot \mathbf{d}}{\|\mathbf{q}\| \, \|\mathbf{d}\|}

要するに:2つのベクトルの「向きがどれだけ揃っているか」だけを見る指標です。値は 1-111 で、11 に近いほど意味が似ています。分母でノルムを割っているので、文書の長さ(ベクトルの大きさ)に左右されず、向き=意味の方向だけで比べられるのが利点です。

これがキーワード一致(同じ単語が含まれるか)と決定的に違う点です。「自動車の燃費」と「クルマの燃料効率」は共通単語がほぼ無くても、意味ベクトルとしては近い。だから言い換えや同義語をまたいで正しい文書を引けます。

ベクトルDBと近似最近傍(ANN)

文書が数百万チャンクあると、クエリとの類似度を全件計算するのは現実的ではありません。そこで ベクトルDB近似最近傍探索(Approximate Nearest Neighbor, ANN) を使います。

ANN は「わずかな取りこぼし(再現率の数%低下)を許す代わりに、計算量を桁違いに減らす」アルゴリズム群です。完全な最近傍を諦めることで、数十億ベクトルでもミリ秒で「だいたい一番近いもの」を返せます。代表格の HNSW(Hierarchical Navigable Small World) は、ベクトルを多層のグラフでつなぎ、上の層を粗くたどってから下の層で細かく探す構造で、対数オーダーの探索量を実現します。

高次元ベクトルの最近傍探索は、次元が高いほど「全部が同じくらい遠い」状態に近づいて難しくなります(次元の呪い)。実務では、埋め込みの次元削減(主成分分析と次元削減)で次元を落として検索を軽くすることもあります。「意味を保ったまま圧縮された表現の上で近さを測る」という点で、RAG の検索は次元削減と地続きの発想です。


前処理:チャンク分割とリランキング

検索の質は前処理で大きく変わります。「RAG は検索の品質が律速」 という言葉のとおり、ここが本丸です。

チャンク分割(chunking)

長い文書を丸ごと1ベクトルにすると、話題が混ざって「何にでもそこそこ近いが、どれにもピッタリ来ない」ベクトルになってしまいます。そこで文書を意味のまとまりごとの断片(チャンク)に分割してから埋め込みます。

リランキング(reranking)

ANN による検索は「速いが粗い」ので、2段構えにするのが定石です。

  1. 粗く広く(recall重視):ANN で候補を多め(例:数十〜100件)に取る。取りこぼしを防ぐのが目的。
  2. 精度よく並べ替え(precision重視):取った候補を、より重い高精度モデル(クエリと文書を一緒に読むタイプ)で関連度を測り直して並べ替える。これが リランキング です。

「速い検索で当たりをつけ、丁寧な評価で順位を確定する」という分業です。最終的にプロンプトへ入れるのは上位の少数だけなので、ここの精度が回答の根拠の質を決めます。


なぜ効くのか:知識を外部化する利点

RAG の本質は 「知識をモデルの重みではなく、外部のインデックスに置く」 ことです。これがそのまま3つの利点になります。

利点理由
知識更新が容易重みを再学習せず、ベクトルDBの中身を差し替えるだけで最新化できる
出典提示ができるどのチャンクを根拠にしたかが分かるので、引用・リンクを示せる(検証可能)
幻覚(ハルシネーション)が減る生成を「引いてきた文書の内容」に縛る(grounding)ので、でっち上げが起きにくい

ファインチューニングとの使い分け

ファインチューニング は知識やふるまいを重みに焼き込みます。RAG は知識を外に置きます。両者は対立ではなく役割分担です。

実際、知識集約的なタスクでは「教師なしの追加学習で知識を詰め込む」より「RAG で引いてくる」ほうが正確になりやすい、という比較報告が複数あります(新規知識でも既存知識でも)。新しい事実を覚えさせたいなら、重みに焼くより外から渡すほうが筋が良い、と覚えておくとよいです。


⚠️ よくある誤解・落とし穴


対応するシミュレーション

simulations/rag_retrieval.py:いろんな話題の文書集合と質問を同じベクトル空間(ここでは簡易に TF-IDF)に埋め込み、コサイン類似度で関連文書を取り出します。植物・光合成の質問に対し関連文書が上位に来て無関係な文書が下位になることを可視化します。検索した文書を文脈に渡せば、重みを再学習せず知識を更新でき出典も示せて幻覚を減らせること(実際は密な埋め込み+近似最近傍探索)を示します(評価・ハルシネーション・安全性)。

埋め込み類似度で文書検索(RAG)

関連ノート