【生成AI】 LLMが学習してないことを回答させる「RAG」とは

## はじめに

この記事は、[NTTテクノクロス Advent Calendar 2024 シリーズ1](https://qiita.com/advent-calendar/2024/ntt-tx)の3日目の記事になります。

こんにちは、NTTテクノクロスのトムです。
数か月前までRAG(Retrieval-Augumented Generation、検索拡張生成)という言葉すら知らなかったのですが、業務で触れる機会があったので仕組みをまとめてみました。この記事を通して、RAGを知らない方に少しでも知っていただけたら幸いです。

## 対象読者

- RAGをこれから学習する方
- RAG初学者

## 要約
- LLM(大規模言語モデル)は学習していない情報(社内限定の情報など)を正確に答えることができないが、解決策の1つとしてRAGがある
- RAGはLLMに質問を投げる際、関連情報をデータベースから検索し、質問と共にLLMに渡し、回答を生成させる技術
- データベースから検索した関連情報をLLMに渡すことで、LLMが学習してない内容も踏まえて回答させることができる

## LLMの課題と解決策
まずChatGPT(インターネット検索機能なし)を例に質問を投げてLLM(大規模言語モデル)が回答を生成するまでの流れを整理してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328838/3b250c10-53ad-623c-ec80-114e1c085d2c.png)
上記のように質問をそのままLLMに渡すため、回答できる内容はLLMに依存します。

例えば、下記のようにLLMは社内情報など学習していない情報を正確に答えることができません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328838/27b535cf-99ca-843c-21cd-6116028f4954.png)

では、**LLMに学習していない情報を回答させたい場合どうすれば良いでしょうか。**

解決策として、**RAG(Retrieval-Augumented Generation、検索拡張生成)** を使うことが挙げられます。

:::note info
LLMは人間の脳と同じで知っていることしか正確に回答することができない。
LLMに知らない情報を回答させる手段の1つとしてRAG(Retrieval-Augmented Generation、検索拡張生成)が挙げられる。
:::


## RAGとは

RAGは、Retrieval Augmented Generation（検索補強型生成）の略称で、外部情報を参照して入力としてLLMに渡す技術です。この技術を使うことでLLMが学習していない内容も踏まえて回答させることができます。

RAGは以下の3つのステップで構成されます。

1. Retriveval(検索)
    - 質問に関連する情報を事前に用意したデータベースから検索する
1. Augmented(増強)
    - 検索で得られた情報をLLMの入力に加える
1. Generation(生成)
    - 質問と検索結果を入力としてLLMが回答を生成する

図で表すと下記のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328838/b43437ae-5b4c-4fc2-06a2-45b337c617c2.png)


ここで注意すべきポイントは、データベースに登録されている全ての情報をLLMの入力とするのではなく、質問に関連する情報のみを入力とする点です。そのため、学習していない内容も踏まえて回答できる範囲は検索で取得した情報に限定されます。

:::note info
RAGとはデータベースから質問に関連する情報を検索し、LLMに入力として質問と検索で取得した情報を渡す技術。その仕組みによってLLMが知らないことも回答させることができる。
:::

## RAGで使うデータベース

これまでの例では、データベースに生のドキュメントがそのまま格納されているように見えましたが、実際はそうではありません。RAGでは、ドキュメントを小さな単位（チャンク）に分割し、それをデータベースに格納します。検索時には、このチャンク単位でデータを取得します。これにより、質問に関連する情報だけを効率的に検索し、LLMに過剰な情報を渡さずに済みます。

また、チャンク化したデータを検索する際には、RAGの検索手法に適したデータベースが必要なります。ここでは、[RAGのドキュメント検索](#ragのドキュメント検索)で後述する「キーワード検索」と「ベクトル検索」において、それぞれで使用されるデータベースについて解説します。

### キーワード検索用のデータベース

キーワード検索にはいくつか手法がありますが、ここでは[転置インデックス](https://www.lifull.blog/entry/2021/09/29/100000)を用いたキーワード検索を例にデータベースを説明します。

転置インデックスは、キーワード検索の効率を高めるためのデータ構造で、単語とその単語が含まれる文書を関連付けて記録します。これにより、特定の単語が含まれる文書を迅速に検索することができます。次に転置インデックスを作成するまでの流れを説明します。

例えば、3つのチャンクがあるとします。

- **チャンクA**: "猫が好きです"
- **チャンクB**: "犬と猫が遊んでいる"
- **チャンクC**: "鳥と猫は友達"

転置インデックスを作成する前に、まずチャンクに出てくる単語を抽出し、各単語がどのチャンクに登場するかを記録します。この処理を経て、下記の転置インデックスが作成されます。(※抽出した単語は例のため、実際とは異なる場合があります)

| 単語     | 登場するチャンク         |
|----------|----------------------|
| 猫       | A, B, C               |
| が       | A, B                  |
| 好き     | A                    |
| です     | A                    |
| 犬       | B                    |
| と       | B, C                 |
| 遊んでいる | B                    |
| 鳥       | C                    |
| は       | C                    |
| 友達     | C                    |

たとえば、「犬」を含むチャンクを検索したい場合、転置インデックスを利用すれば即座にチャンクBのみ該当するということがわかります。このように、転置インデックスを作成することで、検索時にチャンクを1つ1つ確認せず、必要なチャンクを迅速に見つけることができます。

### ベクトル検索用のデータベース

ベクトル検索用には各チャンクを[エンべティング](https://atmarkit.itmedia.co.jp/ait/articles/2401/18/news023.html)と呼ばれる技術で意味的な特徴を数値的に表現した数値ベクトルに変換し保持します。検索時には、質問も同様にベクトル変換し、類似度計算を用いて、関連性の高いチャンクを取得します。

下記の図は質問との関連性の高いチャンクを検索するときのイメージ図です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328838/d60d84ec-342e-901c-4e86-d6b14b5c68e7.png)

## RAGのドキュメント検索

RAGのドキュメント検索では質問とデータベースの各チャンクとの関連を評価し、評価が高かったチャンクを取得します。ここからは、質問と関連するチャンクの検索手法(キーワード検索、ベクトル検索、ハイブリッド検索)を紹介します。

なお、下記の前提で進めることとします。
- LLMには日本昔話に関する情報は学習されていない
- 質問ごとに日本昔話に関する情報がデータベースに登録されている

### キーワード検索

キーワード検索はユーザが入力した単語やフレーズと文書内の単語の一致する情報を検索する手法です。ここではキーワードの一致度の評価([BM25](https://learn.microsoft.com/ja-jp/azure/search/index-similarity-and-scoring)など)の詳細には触れずに検索イメージのみお伝えします。

#### キーワード検索の例

データベースには以下のチャンクが登録されていて、検索時には検索結果の上位1位しか取得しないこととします。

 :::note info
 **チャンク1**
「桃太郎は日本の昔話に登場する主人公で、桃から生まれた男の子です。彼は強い正義感を持ち、鬼退治に出発します。」
:::

:::note info 
**チャンク2**
「桃太郎の仲間には犬、猿、キジがいます。それぞれは桃太郎からもらった黍団子を食べ、仲間として鬼退治に協力します。」
:::

下記のように桃太郎に関する質問をしてみます。

:::note warn
**サンプル質問**
「桃太郎の話で、黍団子をあげて仲間にしたメンバーを教えてください」
:::

キーワード検索では、質問に含まれる単語と一致するチャンクをデータベースから探します。

今回は質問から「桃太郎」「黍団子」「仲間」「メンバー」というキーワードが抽出されたと仮定します。これらのキーワードが一致するキーワードの検索を行い、結果としてキーワードがより一致してるチャンク2を取得します。

このように、キーワード検索は質問内に含まれたキーワードと一致する情報を検索します。

#### キーワード検索の課題

ただし、キーワード検索には課題があります。今回は質問とチャンクのキーワードが一致したので、期待するチャンクを取得できましたが、もしチャンクの「黍団子」が「おだんご」などといった同義語で入っていたとした場合に検索で引っかからない可能性が高くなります。


### ベクトル検索

ベクトル検索は、ユーザが入力した単語や文脈に類似度を重視し、意味的に関連する情報を検索する手法です。ここでは[類似度の評価方法](https://learn.microsoft.com/ja-jp/azure/search/vector-search-ranking)には触れずに検索イメージのみお伝えします。([こちら](https://atmarkit.itmedia.co.jp/ait/articles/2402/07/news021.html)の記事も合わせて参照するとイメージしやすいです)

#### ベクトル検索の例

データベースには以下のチャンクが登録されていて、検索時には検索結果の上位1位しか取得しないこととします

:::note info
**チャンク1**
「浦島太郎は、日本の昔話に登場する漁師です。彼は浜辺でいじめられていた亀を助けた後、龍宮城に招待されます。」
:::

:::note info 
**チャンク2**
「浦島太郎は龍宮城で乙姫から歓待を受け、玉手箱を渡されます。この玉手箱には大切な秘密が隠されています。」
:::

下記の質問をしてみます。

:::note warn
**サンプル質問**
「浦島太郎が亀を助けた後に行った場所と、そこで何をもらったのか教えてください。」
:::

ベクトル検索では質問の意味に近い情報を探すため、質問をベクトル変換し、チャンクとの関連性を計算します。この場合、質問「浦島太郎が亀を助けた後に行った場所は？」は「場所」という意味を持つベクトルに変換されます。その後、データベース内にあるチャンクと比べて、意味的に最も関連するチャンク2を返します。

ここで重要なのは、ベクトル検索が単語そのもの（「浦島太郎」や「亀」など）ではなく、質問全体の意味を理解し、関連する情報を探し出す点です。キーワード検索では、「浦島太郎」という言葉が含まれていなければ情報が出てこないかもしれませんが、ベクトル検索は文全体の意味を解析して適切な情報を見つけ出します。

#### ベクトル検索の課題

ただし、ベクトル検索にも課題があります。特に、チャンクに不要な情報が含まれている場合、関連が低い情報を検索結果として返す可能性が高まります。例えば、以下のチャンクが登録されていると仮定します。

:::note info
**チャンク1**
「浦島太郎は龍宮城で乙姫から歓待を受け、玉手箱を渡されます。この玉手箱には、大切な秘密が隠されています。乙姫は浦島太郎に、この玉手箱を絶対に開けてはいけないと警告しました。玉手箱は美しく装飾されており、その中には何か重要なものが入っていることが示唆されていますが、乙姫はその中身については一切明かしませんでした。浦島太郎は帰国後、好奇心にかられて玉手箱を開けてしまいますが、そこで驚くべきことが起こるのです。」
:::

:::note info
**チャンク2**
「浦島太郎は浜辺でいじめられていた亀を助けた後、龍宮城に招待されます。龍宮城では乙姫から豪華なごちそうを振る舞ってもらいました。」
:::

先ほどと同様に下記の質問をします。

:::note warn
**サンプル質問**
「浦島太郎が亀を助けた後に行った場所と、そこで何をもらったのか教えてください。」
:::

チャンク1では亀を助けた後に竜宮城へ行き、玉手箱をもらったことが内容に含まれているため取得されることを期待しますが、それ以外にも「玉手箱」に関する説明が多く含まれていることから、質問に対して関連性が低いと判断されてチャンク2を取得し、期待した回答を得られない可能性が高まります。

このように、ベクトル検索はチャンク内の文脈全体を理解して検索しますが、データベース内のチャンクに不要な情報が含まれていると、他の適切な情報と比較して関連性が低くなり、間違った情報を引き出してしまうことがあります。そのため、各チャンクは文脈的に意味のある単位で分割できるように調整することが重要です。

### ハイブリッド検索

ハイブリッド検索は、キーワード検索とベクトル検索の結果を組み合わせ、キーワードが一致する情報と文脈的に近い情報を取得し、最適な回答を導きだせるようにする手法です。他と同様に[ハイブリッド検索の評価方法](https://learn.microsoft.com/ja-jp/azure/search/hybrid-search-ranking)については触れずに検索イメージのみお伝えします。

#### ハイブリッド検索の例

データベースには以下のチャンクが登録されていて、検索時にはキーワード検索、ベクトル検索でそれぞれの検索結果で上位1位しか取得しない(合計2件取得する)こととします。

:::note info
チャンク1
「一寸法師は、日本の昔話に登場する小さな主人公です。彼は親の元を旅立ち、都で活躍します。」
:::

:::note info
チャンク2
「一寸法師は都で姫を助け、鬼退治に成功しました。鬼の持っていた打ち出の小槌を手に入れ、それを使って自分の体を普通の大きさにしました。」
:::

:::note info
チャンク3
「打ち出の小槌は、不思議な力を持つ道具で、願ったものを実現することができます。鬼が使っていたものを一寸法師が奪い、自分の体を大きく変えるのに使用しました。」
:::

下記の質問をしてみます。

:::note warn
サンプル質問
「一寸法師が体を普通の大きさにするのに使った道具について教えてください。」
:::

#### ハイブリッド検索の手順

1. キーワード検索
   - 質問から「一寸法師」「体」「普通」「大きさ」「道具」というキーワードを抽出し、データベースを検索します。この結果、キーワードに一致する情報を多く含むチャンク2が選ばれます

2. ベクトル検索
   - 質問をベクトル化し、チャンクとの関連性を計算します。この場合、体を普通の大きさにする」という具体的な行動を支える背景情報として道具の詳細が重要だと判断され、チャンク3が選ばれます

#### ハイブリッド検索の利点

ハイブリッド検索では、キーワード検索での固有名詞の検索で得られる高精度な結果と、ベクトル検索による文脈や意味の理解し検索した結果を組み合わせます。これにより、キーワード検索では見逃されがちな同義語や文脈に基づいた情報も補完され、より正確な回答をLLMにさせることができます。

### 検索手法まとめ

最後にこれまで紹介してきた各検索手法のまとめです。

| 検索手法 | 特徴 | 同義語の検索 | 単語や固有名詞の検索|
| --- | --- |---| --- |
| キーワード検索 | ユーザが入力した単語やフレーズと文書内の単語の一致する情報を検索する。一致する情報を探す特性のため、同義語の検索に対しては弱い。 | × | ◎ |
| ベクトル検索 | ユーザが入力した単語や文脈に類似度を重視し、意味的に関連する情報を検索する。ただし、単語や固有名詞などの検索がキーワード検索ほど高くない。 | ◎ | × |
| ハイブリッド検索 | キーワード検索とベクトル検索の両方を使う手法。双方の強みを活かし、双方の弱みを補う。| ◯ | ◯ |

### おわりに

以上、RAGの仕組みについて概念的な部分を中心に整理してみました。まだ触れられていないドキュメント検索時の具体的な評価方法などがありますが、引き続き学習を進め、別の機会で共有していけたらと思います。この記事がRAGに興味を持つ方のお役に立てば幸いです。最後までお読みいただき、ありがとうございました。

---

NTTテクノクロス Advent Calendar 2024、明日は @Daha さんの記事です！ぜひお楽しみください！