RAGの世界観・メリットをまとめる
はじめに
チーム内でRAGの世界観について共有することになり、まとめ記事を作成しました。
粒度の異なる情報が同一記事内にあるため、混乱する点もあるかと思いますが、参考になれば幸いです。
RAG(Retrieval-Augmented Generation:検索拡張生成)とは何か
LLMを使う際に、外部のデータベースから情報を検索して、その情報に基づいて回答を生成させる手法です。
社内マニュアルチャットボットを例とするとイメージがつきやすいでしょう。
大量のマニュアルをデータベースに保存しておき、質問と関連性が高い部分を検索して取り出して、その情報に基づいて回答を生成します。
RAGが必要な理由
LLMは「知らない情報」について答えられない
LLMは莫大な学習データに基づいて学習されているので、大半の内容には正しく回答できます。しかしながら、LLMが絶対に知らない内容については、どれだけ優秀なLLMであっても正しく回答することは原理的に不可能です。
知らない情報とは、以下のような内容です。
- 社内マニュアルのような内部文書
- 専門知識(医療、工学、化学など)
- 最新情報
- ChatGPT4oでは、2023年10月までの情報しか知らない
ファインチューニングで正確に情報を与えるのは難しい
前段を読み、ファインチューニングではダメなのか?と疑問に思う方がいたかもしれません。
深層学習の分野では、既存のモデルに情報を足す手法として、ファインチューニングが有名です。
しかしながら、LLMにファインチューニングを行う場合、時間と計算コストがかかります。さらに、ßせっかく実施したとしても、このような実験結果からファインチューニングでLLMに「事実」を教えるのは難しいのです。
上記の記事では「ロミオとジュリエット」の脚本の知識における「ロミオ」を「ボブ」に置換する実験を行いました。結果はうまく置換させることはできませんでした。
そこでRAGの検索結果の情報を、回答の根拠としてプロンプト自体に組み込んでしまえば、事実に基づいた回答が得られます。具体的には、検索結果とともに「必ず検索結果の情報に基づいて回答しなさい。」と指示を入れておくと、誤情報の生成を抑えられます。
ファインチューニングは高コスト
先ほどの話と被りますが、ファインチューニングは高性能なPCを用意するなどのコストがかかります。RAGはただ単にプロンプトに取得した情報を足すだけなので、低スペックなPC環境であっても実行できます。
また、最新情報を追加したい場合でも、ファインチューニングは再学習が必要なのに対し、RAGではデータベースを更新するだけで完結します。
【補足】 追加・変更できる内容と対応する手法
こちらの記事の焼き直しになりますが解説します。
上の項目ほど低コストで実現できます。下の項目ほど実現が難しい内容です。
追加・変更できる項目 | 内容 | 例 |
---|---|---|
役割・立場・内容 | プロンプト | 「〜金融の専門家の立場で回答して」 |
口調・スタイル | 小〜中規模なファインチューニング | 語尾を「〜でござる」にする/応答をチャット形式にする |
知識・ナレッジ | RAG | 社内マニュアルに基づいて回答させる |
文法・言語・論理的思考 | 大規模な再学習 | 英語モデルを日本語対応させる |
必ずしも常にこの表が正しいわけではないことに留意してください。
例えば語尾を「〜ござる」にするだけであれば、プロンプトで指示するだけで簡単に対応できます。(ござるデータセットはあくまでもファインチューニングの実験用です。)
他には、入力可能トークンが大きいモデルを使って、プロンプト内に回答根拠となる事実の情報を大量に入れることで、RAGなしでも知識に基づく正しい回答をさせることができるかもしれません。
RAGの仕組み
ここでは最も一般的であるベクトル検索のRAGを例とします。
(事前準備)データベースの作成:
マニュアルの文章をチャンクごとに分けて、埋め込みモデルを使いベクトルにします。
ChromaやFAISSなどのベクトルデータベースに保存します。
クエリの入力:
ユーザーの質問や指示を入力します。
ベクトル化:
クエリを埋め込みモデルを使ってベクトル形式に変換します。
OpenAIのtext-embedding-ada-002
やtext-embedding-3-small
がよく使われます。上記の埋め込みモデルではテキストの長さにかかわらず文章の意味を1536次元のベクトルに変換します。
情報検索:
クエリのベクトルを用いて、データベース内のドキュメントベクトルと類似度計算を行い、関連するドキュメントを検索します。
類似度の指標にはユークリッド距離やコサイン類似度がよく使われます。
ドキュメント抽出:
類似度の高いドキュメントを高い順にK個を選び出します。
生成モデルへ入力:
抽出したドキュメントと元のクエリを、LLMに入力します。
最も単純な形は、取得されたドキュメントをプロンプトに組み込む方法です。
下記の情報に基づいてユーザーの質問に回答してください。
{db_content}
ユーザーの質問: {question}
回答生成:
生成モデルが入力された情報に基づいて、回答を生成します。
RAGの検索手法
単純にそのドキュメント内にキーワードが存在するかどうかだけで検索をする全文検索に対して、クエリとドキュメントの意味的な関連性を考慮して検索する方法をセマンティック検索と言います。セマンティック検索の最もわかりやすい例がベクトル検索です。
全文検索
クエリに含まれる単語やフレーズを直接データベース内のドキュメントと一致させる方法です。一般的には、クエリとドキュメントの中で同じ単語が出現するかを基に関連性を判断します。
ベクトル検索
クエリとドキュメントをベクトル形式に変換し、これらのベクトル間の類似度(例えばコサイン類似度)を計算して関連するドキュメントを検索する方法です。これにより、文の意味を捉えた検索が可能になります。
ナレッジグラフ
ナレッジグラフは、実体(エンティティ)とそれらの関係を図形式で表現したデータベースです。情報の検索や質問応答において、関連する実体や関係を効率的に探索するために使用されます。
ハイブリット型
「ベクトル検索+キーワード検索」や「ベクトル検索+ナレッジグラフ」のように、複数の手法を並行して行い、それらの結果を組み合わせてLLMのプロンプトに入れます。それぞれの方法の利点を活かし、より精度の高い検索結果を得ることができます。
ベクトルデータベースの比較
以下の記事で解説が参考になります。
LangChainのGet startedにはChroma, FAISS, Lanceの三つが紹介されていますが、それ以外のベクトルデータベースも使用できます。
ChatGPTに上記3つを比較してもらった表です。
肌感として、ZennやQitaの記事ではChromaやFAISSを使っている方が多いです。
商用サービスとして作り込む場合は、スケーラビリティやパフォーマンスを踏まえて選定する必要がありますが、実験的に動かす場合はご自身が慣れているものを使う形で良いと思います。
(私が手元で試したわけではありませんが、この記事によると大きな差があるわけではないようです。)
項目 | Chroma | FAISS | Lance |
---|---|---|---|
特徴 | AIネイティブのオープンソースベクトルデータベース。埋め込みの保存とクエリの高速化に特化。PythonおよびJavaScriptのサポート。 | Facebook AIが開発したオープンソースのベクトル検索ライブラリ。大規模データセットに対して高速な近似最近傍検索(ANN検索)が可能。 | データサイエンスと機械学習向けのオープンソースのベクトル検索ライブラリ。データの効率的な検索と分析をサポート。 |
利点 | 簡単なセットアップ、埋め込みとメタデータの管理が容易、高速な検索。 | 高速検索、複数のインデックス手法(IndexFlatL2、IndexIVFFlat、IndexIVFPQ、IndexHNSW)をサポート、Python APIが使いやすい。 | 高速で効率的なベクトル検索、スケーラブルなインデックス、Python APIが使いやすい。 |
欠点 | 他のデータベースほどの大規模スケーリング機能がない場合がある。 | 設定が複雑、大規模データセットでのメモリ消費が多い場合がある。 | 比較的新しいプロジェクトのため、コミュニティとサポートがまだ発展途上。 |
主な用途 | 機械学習モデルの埋め込み管理、小規模から中規模のデータセットでの検索アプリケーション。 | 研究、プロトタイプ開発、特に高速な類似度検索が必要なアプリケーション。 | データサイエンスと機械学習のためのベクトル検索と分析。 |
インデックス作成手法 | HNSWインデックスを使用。 | IndexFlatL2、IndexIVFFlat、IndexIVFPQ、IndexHNSWなどの多様な手法をサポート。 | 高度に最適化されたスケーラブルなインデックス。 |
言語サポート | Python、JavaScript。 | Python、C++。 | Python。 |
スケーラビリティ | 中程度のスケーラビリティ。 | 高いスケーラビリティ。 | 高スケーラビリティ。 |
設計目標 | シンプルさと開発者の生産性を重視。 | 高速で大規模なデータセットに対する効率的な検索。 | データサイエンスと機械学習の効率的なデータ検索と分析。 |
【補足】FAISSのインデックス作成手法
万単位のデータベースを扱う場合、図の上1つ目のIndexFlatL2の手法で、総当たりの類似度計算をすると莫大な時間がかかります。
そこで図の下3つのような高速化のためのインデックスの作成手法があります。
このような手法は近似最近傍探索と言われます。
近似なので最近傍探索とは異なる結果が得られる場合があります。
インデックス名 | 原理 | メリット | デメリット |
---|---|---|---|
IndexFlatL2 | 全ベクトルのL2距離を直接計算 | 簡単で実装が容易、精度が高い | 計算コストが高い |
IndexIVFFlat | ベクトル空間をクラスタに分割し、クラスタ内で検索 | 検索速度が速い、大規模データセットに適している | クラスタリングの精度に依存、構築コストが高い |
IndexIVFPQ | クラスタリング後、各クラスタ内でベクトルを量子化 | メモリ効率が良い、検索速度が非常に速い | 量子化による誤差、複雑な実装 |
IndexHNSW | グラフベースの階層的な近傍探索 | 非常に高速な検索、高精度 | メモリ消費が多い、インデックス構築コストが高い |
↓参考
ベクトル量子化
ベクトルを部分空間に分けて、それらを代表値で置き換えることで、データを表すのに必要なビット数を減らす。
HNSW
エッジの個数が異なる階層構造のグラフを作成して、上位レイヤーで近いノードを見つけたら、より詳細なグラフの下位レイヤーに移って探索を続ける。
RAGの拡張手法
一旦参考になった記事のリンクをおいておきます。
精度改善のための戦略
拡張手法と同様に、一旦こちらの記事のリンクをおいておきます。
その他の参考記事
↓RAGの概要
↓RAGについてのリンク集 ↓精度を上げるためのTips ↓各手法の整理
Discussion