Essential GraphRAGを読んだ
Essential GraphRAGという本を実装しながら読んだので、学んだ内容をまとめた。 実装したコードは以下においてある。本ではOpenAIのAPIを使用していたが、コストをかけたくなかったのでOllamaで実装してみた。
https://github.com/takatori/essential-graph-rag
章ごとの紹介
ここからは章ごとに本から学んだことをまとめている。
1章
1章は導入的な内容で、知識カットオフ問題やハルシネーション等のLLMの限界について。それらを克服するための、従来のRAGについての紹介。従来のベクトル検索を用いたRAGは主に非構造化データを対象としており、知識グラフを使うことで、構造化データと非構造化データを統合し、RAGの性能を向上させることができる。
2章
2章は、通常のベクトルRAGを実装していくパート ベクトルインデックスと全文検索のハイブリット検索をNeo4jで実装していく。 Neo4jのデータベース上にベクトルインデックスと全文検索用のインデックスを構築し、それぞれで検索。 高い方のスコアを返すシンプルな実装する。
Neo4jだけでハイブリッド検索を実現できるのは、簡単に使えて便利そう。
3章
3章はベクトルRAGの検索精度を上げる手法の紹介。
ユーザのクエリから直接生成される埋め込みは、用語や文脈の違いにより文書の埋め込みと必ずとも一致しない可能性がある。クエリの書き換えドキュメントの埋め込み戦略の変更によってユーザのクエリとドキュメントの間のギャップを埋め、検索精度を向上させることができる。
クエリの書き換えとして、以下ような手法がある。
- hypothetical document retriever(Gao et al., 2022)
- ユーザのクエリから、「クエリの答えうる理想的な仮説上のドキュメント」をLLMで生成し、この理想的なドキュメントの埋め込みとの類似度で検索する
- step-back prompting (Zheng et al., 2023)
- 与えられた具体的な質問に直接答えるのではなく、まずその質問の背後にある高レベルの概念や基本原理に関する、より一般的で抽象的な質問(ステップバック・クエスチョン)をLLMに生成させる
また、ドキュメントの埋め込み戦略として、例えば、以下のような手法がある。
- 仮説的な質問戦略
- ドキュメントが答えられる仮想的な質問を生成しそれを埋め込む
- 親ドキュメント検索戦略
- 親となる元の文章を、子チャンクと呼ばれる小さなユニットに分割し、チャンクごとに埋め込みを計算する
- 検索時には、クエリ埋め込みとチャンクの埋め込みの類似度で関連チャンクを取得する
- マッチしたチャンクだけを返すのではなく、元の親文書全体を返すことで完全なコンテキストをLLMに渡す
この章の内容はGraphDBを使わなくても実現できるが、親文書とチャンクをグラフに保存しておけば、チャンク同士の類似度などから関連ドキュメントの取得などもできそうだなと思った。
4章
4章は自然言語の質問からCypherクエリを生成する手法の紹介。
クエリの言語生成は、ベクトル検索だけでは回答できない、何らかの集計が必要な場合やグラフから特定のデータを取得したい場合などに有効。
自然言語による質問からCypherクエリを生成するワークフローは以下のようになっている。
- ユーザからの質問を取得する
- 知識グラフのスキーマを取得する
- 用語のマッピング、フォーマットの指示、few-shot exampleなど、そのほかの有用な情報を定義する
- LLMのプロンプトを生成する
- プロンプトをLLMに私、Cypherクエリを生成する
また、適切なCpyherクエリを生成するために有用なプラクティスが紹介されていた。
- few-shot examples
- ここでプロンプトに埋め込む例は、知識グラフに特有なものなので、各知識グラフに対して手動で作成する必要がある
- プロンプトにデータベースのスキーマを埋め込む
- 利用可能な、ノードのラベルおよびプロパティ、リレーションの種別およびプロパティ、リレーションそのものをプロンプトで明示する
- Neo4jからスキーマを推論するにはAOPCライブラリを使う
- ユーザの質問をスキーマに意味的にマッピングさせる用語マッピングの追加
- ユーザの質問でよく使われる用語を、スキーマで使われる用語にどのように対応づけるかを、プロンプトで明示する
- 例えば、
ユーザが人について尋ねる際は、Personというラベルを持つノードを参照する
- フォーマット命令
- LLMにCypherクエリのみを出力させ、それ以外は出力させないようにする
CypherではなくSQLなどを生成する場合にも同じワークフローやテクニックが有効だと思われる。
5章
5章ではエージェント型のRAGを構築する。
エージェントRAGは、ユーザーの質問に答えるため、さまざまな検索ツール(エージェント)を使い分けることができるシステム。
エージェントRAGが必要なのは、さまざまなデータソースがあり、最適なデータソースを業務に使用したい場合や、データソースが非常に広範であったり複雑で必要なデータを取得するために専門的な検索が必要な場合。
エージェントRAGには以下のコンポーネントが必要になる。
- retriever router
- ユーザの質問から、最適なretriverを取得する
- この本ではLLMを使って、どのレトリーバを使うか決定する
- retriever agents
- ユーザの質問へ答えるために必要なデータを取得する、実際のretriever
- answer critic
- retrieverからの回答が、元の質問に正しく答えられているかチェックする
- 不完全な回答の場合は、正しい答えを検索するための新しい質問を生成し、再検索をできるようにすべき
6章
6章では知識グラフの構築方法を学ぶ。
知識グラフの構築フローは以下。
- LLMでデータから構造化情報を抽出
- 抽出した情報をグラフにインポート
- エンティティの解決
LLMでデータから構造化情報を抽出
LLM以前にもデータから情報抽出は行われていたが、高いコストと技術的な障壁により簡単ではなかった。
LLMは非構造化テキストから構造化されたデータを抽出し、使用可能な形式に変換するのに効果的であり、簡単にデータ抽出できるようになった。
OpenAIのAPIで提供されているStructred Outputs機能を使うことで、Pydanticのようなライブラリで定義したデータスキーマに従ってモデルの応答を出力させることができる。
抽出した情報をグラフにインポート
抽出したデータをグラフにインポートする際、クエリの完全性の保証およびパフォーマンス向上のために、可能な限りの制約とインデックスを定義しておく。
エンティティの解決
インポート後にエンティティの解決し、データ一貫性と精度を向上させる必要がある。 エンティティ解決とは、同じ実世界のエンティティの異なる表現を特定し、統語するプロセス。大規模なデータセットの場合、スペルのバリエーション、異なる命名規則、データ形式のわずかな不一致のような矛盾のために、同じエンティティが複数の形で現れることが一般的。 エンティティ解決のための技術としては、文字列マッチング、クラスタリングアルゴリズム、機械学習による手法などがあるが、ユースケースやドメインに大きく依存する。
7章
7章ではMircosoftのGraphRAG(Edge et al., 2024) を実装する。
この論文では従来のベクトルRAGの問題点を克服するためにGraphRAGを提案している。 従来のベクトルRAGでは「過去10年間で、科学的発見は学際的研究からどのような影響を受けてきたか、その主要な傾向は何か?」といった、データセット全体の包括的な理解を必要とするクエリには対応できない。
GraphRAGでは、知識グラフを使ってこの問題に対処する。具体的には、まず、元のドキュメントからエンティティとリレーションシップを抽出し知識グラフを構築する。次に、知識グラフからコミュニティを検出し、コミュニティの要約を生成する。検索時には、各コミュニティの要約を使って中間の回答を生成し、最終的にそれらをまとめてグローバルな回答をユーザに返す。
Graph RAGの詳細なステップは以下になる。
インデックス時
インデックス構築は大きく分けて2段階で行われる。 第一段階では知識グラフを構築する。第二段階では構築した知識グラフ上でコミュニティを検出し、各コミュニティの要約を生成する。
第一段階
- ソースドキュメント -> テキストチャンク
- ドキュメントをチャンクに分割する
- チャンクのサイズが小さいほど、多くのエンティティを抽出できる可能性が高まるが、LLMの呼び出し回数が増えてしまう
- テキストチャンク -> エンティティとリレーションシップ
- LLMを使ってエンティティとリレーションシップを抽出する
- アウトプットに含める要素は以下が必要となる
- エンティティ
- エンティティ名
- エンティティタイプ(どのようなタイプがあるかはプロンプトに埋め込んで渡す)
- エンティティの説明
- リレーションシップ
- ソースエンティティ名
- ターゲットエンティティ名
- リレーションシップの説明(なぜ関連しているかの説明)
- リレーションシップの強度(関連の強さを示す数値スコア)
- エンティティ
- エンティティとリレーションシップ -> 知識グラフ
- 抽出されたエンティティとリレーションシップから知識グラフを構築する
- 通常、1つの要素が複数のドキュメントから何度も検出・抽出されるため、複数のインスタンスが生成されてしまう
- 抽出された知識の矛盾、冗長性、断片化を避けるため、LLMを用いて同じエンティティやリレーションシップの複数の記述をマージし、要約を生成する
第二段階
- 知識グラフ -> グラフコミュニティ
- コミュニティ(グラフの残りの部分よりも互いに密に接続されているノードのグループ)の検出
- コミュニティ検出アルゴリズムであるLouvain法などを使う
- 元の論文では、Leidenコミュニティ検出(Traag et al., 2019)を階層的に使用
- 検出された各コミュニティ内で、分割できなくなるリーフコミュニティに到達するまでサブコミュニティを再帰的に検出
- グラフコミュニティ -> コミュニティサマリー
- 検出したコミュニティの要約をLLMで生成する
検索時
グローバルな検索とローカルな検索の両方に対応できる。
グローバルな検索は、関連性の高い情報を検索するためにグラフ全体を考慮する。 具体的には、中間応答としてコミュニティの要約を使用する。 グローバルな検索では以下の2つのステップを踏む。
- mapステップ
- 各コミュニティの要約から中間結果を生成する
- 中間結果は、キーポイントのリストで構成され、数値的な重要度も返す
- reduceステップ
- すべての中間結果のうち、重要なものをフィルタリングして残す
- それらを集約して、最終的にユーザに返す回答を生成する
ローカル検索は、特定のエンティティに焦点を当てたクエリに有効。 ローカル検索では、ユーザがクエリを入力すると、まず、ベクトル検索で最も意味的に関連するエンティティを特定する。 つぎに、特定されたエンティティに関連する情報を知識グラフから取得する。 これには、テキストチャンク、リレーションシップ、接続されているエンティティ、関連するコミュニティの要約を含む。 これらの情報を関連度の高い順にランキングし、コンテキストウィンドウに収まるようにフィルタリングする。 最後に、LLMが最終的な応答を生成する。
8章
8章はRAGアプリケーションの評価について。 この章では5章で実装した、エージェント型RAGを評価していく。
RAGパイプラインでの各ステップで評価するポイント
- ツール選定
- LLMがどのツールを選択し、クエリのニーズにどの程度合致しているかを評価することは、検索性能を最適化する上で重要
- 検索されたコンテキストとユーザの質問との関連性
- 回答の評価
- 生成された応答の一貫性と正確さ
- LLMが正しいコンテキストを与えられたときに正しい答えを生成するかどうか
- モデルの推論能力と合成能力を測定
ベンチマークデータセットの設計
以下のようなさまざまな側面をテストする入力クエリを設計する必要がある。
- ツール選択の評価
- 正しい検索方法を選択するかを評価するためのクエリ
- 最も関連性の高い情報源を特定することを保証する
- エンティティと値のマッピング
- ユーザ入力から抽出したエンティティや値をデータベースのエントリーに正しくマッピングできているかどうかを評価する
- 多段階の検索シナリオ
- 最初に検索されたデータが2回目の検索の入力となる多段階の検索システムの場合の評価
- 最初の検索を改良または拡張できているかどうか
- エッジケースと機能カバレッジ
- 曖昧なクエリ、ロングテール、複数の検索方法が適用できるシナリオの対応など
- 会話の使いやすさ
- 挨拶への対応、曖昧なクエリの明確化、自らの機能を効果的に説明できるか
評価指標
ここでは、RAGASを使って評価する方法を紹介している。 RAGASを含めたLLMの評価指標については、LLMの精度ってどう測るの?評価指標を調べてみたの方が参考になる。
合わせて読んだ本
- 初めての知識グラフ構築ガイド
- グラフデータベースのNeo4jで使われるクエリ言語であるcypherは初見だと理解が難しかった
- この本では、cypherをがっつり使って知識グラフの構築から活用までを学べるので、Essential GraphRAGに出てくるcypherを簡単に理解できるようになった
- 先に読んでおいたのでスムーズに読み進められた
- データのつながりを活かす技術
- ネットワークデータ分析について幅広く学べる本
- Essential GraphRAGでは、グラフ(ネットワーク)自体の分析や学習については出てこなかったが、この本に書かれている手法を組み合わせることで更なる精度の改善ができそう
- つくりながら学ぶ!LLM 自作入門
- LLM自体について実装しながら学習できる本
- 実際にGPT-2相当のモデルを実装して動かすことができて楽しい
- 中身を知らなくてもLLM活用して開発はできるが、知っているとより応用が効くはず
- LLMのプロンプトエンジニアリング
- プロンプトだけというよりコンテキストエンジニアリングについて書かれている
- Essential GraphRAGを実装している際に、プロンプトが非常に重要だとあらためて感じた
- 問題に対応するための必要な情報を含んだ適切なプロンプトを構築できないと、意図した通りのアウトプットが出せない
まとめ
Essential GraphRAGを読んで学んだことをまとめた。実際に手を動かすことでかなり理解が進んだ。 ベクトル検索だけでは対応できないクエリでは、グラフを活用することで対処できることを学んだ。 さらなる発展はナレッジグラフを活用するGraphRAGを俯瞰するに詳しくまとまっていそうなので、参考にしたい。 また、Graph Neural Networkも学ぶ必要がありそうなので、Graph Neural Networks in Actionも気になっている。