RAGにおけるベクトル+BM25ハイブリッド検索の性能比較および評価指標の解説
RAGにおけるベクトル+BM25ハイブリッド検索の性能比較および評価指標の解説
はじめに
RAG (Retrieval-Augmented Generation) とは、
日本語では「検索拡張生成」と言われる手法で、
外部の知識ベースから関連情報を検索し、その情報を基にLLMの応答を生成するという技術です。
RAGを活用することで、LLMが事前に学習した知識に加えて、
最新の情報や特定のドメイン知識を参照した応答が可能となります。
本記事ではRAGにおける検索手法として広く用いられているベクトル検索と、
キーワード検索の代表的手法であるBM25、
そしてそれらを組み合わせたハイブリッド検索の理論について簡単に解説し、
検索性能の比較評価を行います。
評価に用いる指標については具体的な計算例を示して感覚的に理解できるよう説明します。
RAGのアーキテクチャと検索の重要性
RAGは主に以下の3つのステップで構成されます。
- クエリ生成: ユーザーからの質問や要求を受け取り、検索クエリを生成する。
- 情報検索: 生成されたクエリを用いて、外部の知識ベースから関連情報を検索する。
- 応答生成: 検索で得られた情報をコンテキストとしてLLMに提供し、最終的な応答を生成する。
したがって、RAGの応答内容は情報検索の精度に大きく依存します。
LLMが回答に必要な情報を持っていなければ答えられない質問が当然あるため、
検索ステップで適切な情報を拾えなければなりません。
そのため、検索手法の選択と最適化はRAGシステムの性能にとって極めて重要です。
以下、RAGにおける主要な検索手法であるベクトル検索とBM25について説明します。
ベクトル検索とは
ベクトル検索を理解するにあたって、まず「埋め込み(Embedding)」の概念を押さえる必要があります。
「埋め込み」とはテキストや画像などのデータを高次元の数値ベクトルに変換することです。
数値化・ベクトル化することにより、コンピュータがデータの意味的な類似性を計算できるようになります。
「意味的な類似性」というのは、高校数学でもよく扱われる以下の式で表現されます(cosine類似度と呼ばれます)。
として、
です。ベクトル化によってこのような計算が可能となります。
埋め込みが具体的にどのように行われているかの説明はかなり専門的になるためここでは割愛します。
ベクトル検索は埋め込みによりベクトル化されたデータの中から、
クエリ(LLMへの質問)と類似したドキュメントを検索する手法です。
ベクトル検索の長所として、以下のような点が挙げられます。
- 単語の表面的な一致ではなく、意味的な類似性に基づいて検索できるため、同義語や関連語を含むドキュメントも検出可能。例えば、「自動車」と「クルマ」は異なる単語だが、意味的には非常に近いため、ベクトル検索では両者を関連付けて検索できる。
- 文脈を考慮した検索が可能。曖昧なクエリに対しても意図を理解し、関連する情報を抽出できる。
一方、短所としては以下の点が挙げられます。
- 埋め込みの次元数が高くなると計算コストが増大し、検索速度が低下する可能性がある。精度と速度のトレードオフが存在する。
- 固有名詞や専門用語などのキーワード検索に弱い。
補足: 数学用語の「埋め込み」との違い・関連性について
「埋め込み」という用語をすんなり受け入れられますでしょうか?
私は大学で数学を専攻していたので、数学用語としての埋め込みと比較することで違和感はなくなりましたが、
はじめ「データを数値(ベクトル)化する」ことをなぜ「埋め込み」と呼ぶのかピンときませんでした。
機械学習やLLMの文脈では埋め込みという言葉は数学的に厳密な意味では使われていないと思いますが、
近い感覚で捉えることはできると思うので、以下に私の解釈を記載します。
機械学習で埋め込みという言葉が用いられるようになった実際の経緯については気になるところですので、
ご存知の方がいれば教えていただけると嬉しいです。
数学用語の埋め込みとは、ざっくり言うと数学的な構造を保つ単射(1対1)写像を指します。
定式化はそのとき考えている数学的対象(集合になんらかの構造を考慮したもの)によって異なります。
ある数学的な対象Aから別の対象Bへの埋め込みが存在すれば、AはBの一部分とみなせます。
例えば、足し算とかけ算(通常の意味の)を定義できる集合のことを「環」と呼びますが、
整数全体や有理数全体の集合は環の構造を持ちます。
整数全体の集合はそれ自体で環の構造を持ちますが、
整数全体から有理数全体への包含写像が環の構造を保つ単射(埋め込み)であるため、
整数全体のなす環は有理数全体のなす環の部分環とみなせます。
整数全体は有理数全体の部分集合であるため、ここではみなすというより直接的に部分環なのですが、
集合として包含関係のない環同士の場合でも、演算のふるまいが同じになる場合があります。
そのようなときに一方の環を埋め込みを通じて他の環の部分環とみなすことで、取り扱いが容易になることがあります。
また、単射(定義域の集合の中の相異なる要素は必ず値域の異なる要素に写される)という条件により、
値域の方で演算した結果に対応する定義域の要素が特定できます。
LLMの文脈での埋め込みは、トークン(単語)の集合からn次元実数ベクトル空間への写像と解釈できます。
トークンの集合に数学的な構造を定義しているのを見たことがなくベクトル化の過程も複雑なため、
数学的に厳密な意味での埋め込みとは捉えられませんが、
トークンの集合をベクトル空間の一部とみなしている点、ベクトル空間での演算結果から出力トークンを決定する点で「埋め込み」と呼ばれることに納得がいきました。
BM25とは
BM25 (Best Matching 25) は情報検索におけるキーワードベースの検索手法です。
LLMの登場以前から存在する伝統的な技術であり、
Google検索エンジンなど多くの情報検索システムで採用されてきました。
BM25は、文書とクエリの間の関連性を以下の指標で評価します。
-
単語の出現頻度 (Term Frequency, TF): クエリ内の単語が文書内にどれだけ頻繁に出現するかを測定する。頻度が高いほど、その文書はクエリに関連していると見なされる。
-
逆文書頻度 (Inverse Document Frequency, IDF): クエリ内の単語が全体の文書集合においてどれだけ希少であるかを測定する。希少な単語ほど、その単語が含まれる文書はクエリに関連していると見なされる。
例えば、「犬」より「柴犬」の方が通常は文書内に出現頻度が低いため、IDFは「柴犬」の方が高くなります。
これらの指標は文書の長さにも影響を受けるため、BM25では文書長も考慮してスコアを調整します。
例えば、1000語の文書で「AI」が5回出現する場合と、100語の文書で「AI」が2回出現する場合では、後者の方が「AI」に関して関連性が高いと評価されます。
参考: BM25スコアの計算式
各項の意味は以下の通りです。
-
:クエリ中のq_i 番目の単語i -
:単語f(q_i,d) が文書q_i 中に出現する回数(TF: Term Frequency)d -
:文書|d| の長さ(単語数)d -
:コーパス全体の平均文書長\text{avgdl} -
:調整パラメータ(通常k_1, b )k_1=1.5, b=0.75
このように、BM25は単語の一致と統計的な重要性に基づく検索手法であり、ベクトル検索と異なり意味的な類似性は考慮しません。
ベクトル検索では「犬」と「ペット」が関連づけられますが、BM25では「犬」と「ペット」は異なる単語として扱われます。
ベクトル検索とBM25を組み合わせたハイブリッド検索
ベクトル検索とBM25は異なるアプローチで情報検索を行っており、それぞれ強みと弱みがあります。
2つの手法を組み合わせることで、
ベクトル検索におけるキーワード検索の弱さとBM25の同義語・文脈理解に弱い点を補完し合い、
検索精度の向上が期待できます。
本記事の比較実験にあたっては、以下の方式で実装しました。
それぞれの検索手法で得られたスコアを正規化し、
重み付き和を取り最終スコアとするシンプルな方法です。
-
ベクトル検索とBM25をそれぞれ全件に対して実行し、スコア
,s^{(v)} を得る。s^{(b)} -
クエリごとにmin-max正規化:
\hat{s}^{(v)}_i = \frac{s^{(v)}_i - s^{(v)}_{\min}}{s^{(v)}_{\max} - s^{(v)}_{\min} + \epsilon},\quad \hat{s}^{(b)}_i = \frac{s^{(b)}_i - s^{(b)}_{\min}}{s^{(b)}_{\max} - s^{(b)}_{\min} + \epsilon} (
と設定)\epsilon=10^{-6} -
重み付き和でハイブリッドスコアを計算:
s^{(h)}_i = \alpha \hat{s}^{(v)}_i + (1-\alpha)\hat{s}^{(b)}_i (
と設定)\alpha = 0.6
ハイブリッド検索の懸念として、計算コストの増加があります。
重みの設定に関係なく両方の検索を全文書に対して実行するため、
それぞれの検索手法の計算コストがそのまま合算されます。
この点が問題となるかどうかはデータセットの規模やリアルタイム性の要件など様々な要因があると思いますが、
今回の実験で検索にかかった時間も計測してみます。
検索精度の評価指標について
本記事の比較では、以下の指標を用いて検索精度を評価します。
Precision@k / Recall@k / F1@k
単一のクエリに対する取得文書のうち上位k件に対する指標で、いずれも情報検索(IR)における基本指標です。
- Precision@k: 上位k件のうち、正解文書が占める割合(適合率)。「無駄な検索結果がどの程度含まれるか」を測る指標。
- Recall@k: 全正解文書のうち、上位k件に含まれる割合(再現率)。「本当に必要な情報のうち何件を上位kで拾えたか」を測る指標。
- F1@k: Precision@k と Recall@k の調和平均。
調和平均は算術平均と比べて低い値の影響が大きく反映されます。
例(Precision@k = 0.9, Recall@k = 0.1)のとき
つまりF1スコアは両方の指標がバランスよく高い場合にのみ高くなるため、検索システムの総合性能をより適切に評価できます。
Mean Average Precision (MAP)
RAGでは検索結果の上位の情報ほどLLMの回答生成に与える影響が大きいため、
単に正解文書を拾えたかどうかだけでなく、正解が早い段階で出現しているかどうかも重要です。
MAPはその観点を評価できる指標で、複数クエリに対してランキング品質を測ります。
計算方法の流れとしては、
まず各クエリに対して「正解が検索結果のより上位に来ているか」を評価し、
その後テストクエリ全体でそのスコアの平均を取るという形になっています。
-
クエリごとのスコア (AP: Average Precision)
まず、1つのクエリに対するスコア(AP)を考えます。
APは、「正解文書が見つかるたびに、その時点でのPrecisionを計算し、平均したもの」です。\boxed{ \text{AP}(q)=\frac{1}{|R_q|}\sum_{i=1}^{k} \text{Precision@}i \cdot \mathbf{1}_{\{d_i\in R_q\}} } 記号の意味:
-
: クエリR_q の正解文書の集合q -
: 検索結果のd_i 位の文書i -
: 評価に用いるランキングの長さ(Precision@k などの「k」と同じ意味合い)k -
:\mathbf{1}_{\{d_i\in R_q\}} が正解文書なら1、そうでなければ0の指示関数d_i
これにより、同じ正解数でも順位によってスコアが大きく変わります。
例えば、文書全体の中に正解文書が2つあるクエリで、上位5件の結果が以下の2パターンだった場合を比較します。
Precision@5 はどちらも ですが、APは異なります。2/5 -
パターンA(優秀):
[正解, 正解, ミス, ミス, ミス]1位と2位で即座に正解が出ている場合です。
AP = \frac{\tfrac{1}{1} + \tfrac{2}{2}}{2} = 1.0 -
パターンB(惜しい):
[ミス, ミス, ミス, 正解, 正解]4位と5位まで見ないと正解が出てこない場合です。
AP = \frac{\tfrac{0}{1} + \tfrac{0}{2} + \tfrac{0}{3} + \tfrac{1}{4} + \tfrac{2}{5}}{2} = 0.325
このように、APは上位に正解が多いパターンを高く評価します。
また検索漏れが多い(Recallが低い)ほどAPも低くなります。 -
-
全体平均 (Mean)
MAPは、上記のAPを評価対象の全クエリについて計算し、平均したものです。
したがって、特定のクエリだけでなくあらゆる質問に対して安定して上位に正解を出せるシステムほど、MAPの値は高くなります。\boxed{ \text{MAP}=\frac{1}{|Q|}\sum_{q\in Q}\text{AP}(q) } 記号の意味:
-
: 評価に用いるクエリ集合Q -
: 評価に使うクエリ総数|Q|
-
ベクトル検索にBM25を加えハイブリッド検索とすることで情報の拾い漏れを減らすこと、
また正解が上位に来るケースを増やすことが目的であるため、
今回の比較実験ではこれらの指標のうちRecall@kやMAPに注目します。
実験設定
今回の実験のために以下の処理を行うpythonスクリプトを作成しました。
(コードとデータセットはGitHubリポジトリで公開予定です。)
評価フロー
- インデックス作成: Wikiコーパス全件を読み込み、ベクトル化およびBM25インデックスを構築。
- 検索実行: 100件のテストクエリに対し、各手法で上位3件を取得。
- 評価指標計算:
- MAP: 正解文書がランキングの上位に含まれているか。
- Recall@3: 上位3件の中に正解文書が含まれている割合。各クエリで算出したスコアの平均を取る。
- レイテンシ: 検索部分にかかった時間を計測。
以下、スクリプト実行にあたっての詳細設定を記載します。
データセット
評価用データとして、Wikipedia日本語版の記事から抽出・加工したドキュメントとQAペアを使用しました。
(CC BY-SA 3.0: Wikipediaのデータを二次利用するためのライセンス規約に基づき記載。)
- ドキュメント総数: 100件
- 歴史、科学、地理など多様なジャンルの短い解説文。
- 評価クエリ数: 100問
- 各ドキュメントに対応する事実確認的な質問を作成。
- 各クエリには正解となる「参照元ドキュメントID」が紐付けられている。
検索手法とパラメータ
ベクトル検索単体、BM25単体、ベクトル+BM25のハイブリッド検索の3手法について以下の設定で計測しました。
- BM25
- ライブラリ: rank_bm25
- トークナイザ: 文字N-gram (bi-gram)
- ベクトル検索
- モデル: sentence-transformers/all-MiniLM-L6-v2
- 次元数: 384次元
- ハイブリッド検索
- スコア結合方式: ベクトル検索とBM25のスコアをそれぞれ min-max 正規化し、重み付き和で算出。
- 重みパラメータ:
\alpha = 0.6
- 取得件数 (
): 上位3件と上位5件の2パターンで評価。k
回答生成設定
今回は検索性能のみを比較し、回答性能の考察は行いませんが、
スクリプトでは以下の設定で回答生成も実装しています。
-
モデル: Azure OpenAI gpt-4o-mini
-
パラメータ: temperature=0.2, max_tokens=256
-
プロンプト:
以下のコンテキストのみを根拠に日本語で簡潔に答えてください。 {context} 質問: {question} 回答:
実験結果
単一クエリに対する指標であるPrecision, Recall, F1については100問の平均値を出力しています。
k=3の場合
| 検索方法 | Precision | Recall | F1 | MAP | 検索レイテンシ |
|---|---|---|---|---|---|
| ベクトル | 0.3033 | 0.91 | 0.455 | 0.8483 | 0.055s |
| ハイブリッド | 0.3333 | 1.00 | 0.50 | 1.00 | 0.063s |
| BM25 | 0.3333 | 1.00 | 0.50 | 0.995 | 0.001s |
k=5の場合
| 検索方法 | Precision | Recall | F1 | MAP | 検索レイテンシ |
|---|---|---|---|---|---|
| ベクトル | 0.1860 | 0.93 | 0.310 | 0.8533 | 0.058s |
| ハイブリッド | 0.2000 | 1.00 | 0.333 | 1.00 | 0.062s |
| BM25 | 0.2000 | 1.00 | 0.333 | 0.995 | 0.001s |
考察
k=3,5いずれの場合でも、ベクトル検索だけではRecallが他の手法に比べて低くなっており、
回答生成に必要な情報を拾いきれていないケースがあることが分かります。
今回のデータセットは比較的短いドキュメントで構成されており、
内容も一問一答的な単純なものが多かったためBM25に有利な条件となっていたとは思いますが、
少なくとも「ベクトル検索単体では漏らしてしまう情報で、ハイブリッド検索では拾えるようになるものがある」ということは実際に確認できました。
「そもそも回答に必要な情報が取得できていない」というケースは限りなく0に近づけたいので、
この結果だけでもハイブリッド検索の導入を検討する価値が大いにあると言えます。
また、MAPについてはハイブリッド検索がベクトル検索単体・BM25単体の両方を上回り、
今回のような小規模データセットでも2手法を組み合わせることでの相乗効果が目に見える形で現れました。
ランキングの上位に正解が現れやすいということは、
LLMに渡す文書の数を減らしても必要な情報が揃っている可能性が高いということなので、
参照情報数を絞ることでノイズ・トークンを削減し性能・コスト両方の改善も狙えます。
レイテンシについては理論通りハイブリッド検索がベクトル検索より遅くなってはいるものの、
そもそもBM25の計算が非常に高速であるため大きな差にはなっていません。
大規模データセットの場合やシステムの要件次第では注意が必要になるかもしれませんが、
大抵の場合は精度向上のメリットが遥かに大きいのではないかと思います。
ベクトル検索単体で拾えなかった文書
ちなみに、k=3でベクトル検索単体で正解文書を拾えなかったクエリは以下の9つです。
{"id": "qa-102", "question": "桜は何科の植物?", "answer": "バラ科サクラ属の落葉高木。", "citations": ["doc-102"]}
{"id": "qa-105", "question": "味噌の主原料は?", "answer": "大豆を発酵させた調味料。", "citations": ["doc-105"]}
{"id": "qa-115", "question": "相対性理論を提唱した科学者は?", "answer": "アインシュタイン。", "citations": ["doc-115"]}
{"id": "qa-116", "question": "遺伝情報を担う分子は?", "answer": "DNAの二本鎖。", "citations": ["doc-116"]}
{"id": "qa-130", "question": "過学習とは何か?", "answer": "訓練データに適合しすぎ汎化が落ちる。", "citations": ["doc-130"]}
{"id": "qa-146", "question": "電源構成の割合を指す用語は?", "answer": "エネルギーミックス。", "citations": ["doc-146"]}
{"id": "qa-147", "question": "再生可能エネルギーの例を1つ挙げて。", "answer": "太陽光(他に風力・地熱など)。", "citations": ["doc-147"]}
{"id": "qa-151", "question": "ピラミッドはどこの国で建造された?", "answer": "古代エジプトで建造された。", "citations": ["doc-151"]}
{"id": "qa-157", "question": "世界最大のサンゴ礁は?", "answer": "グレートバリアリーフ。", "citations": ["doc-157"]}
k=5ではこれらのうち、
{"id": "qa-130", "question": "過学習とは何か?", "answer": "訓練データに適合しすぎ汎化が落ちる。", "citations": ["doc-130"]}
{"id": "qa-146", "question": "電源構成の割合を指す用語は?", "answer": "エネルギーミックス。", "citations": ["doc-146"]}
は拾えるようになりました(他7件は拾えないまま)。
拾えるようになった2件については専門用語ではあるものの、
単語と意味の関係が比較的わかりやすいと思うので、
取得件数を増やしたことで拾えるようになったのは感覚的には納得できます。
拾えなかった7件については、確かに固有名詞は多いですが、
固有名詞でもベクトル検索で拾えているものがあるためその差がよくわかりませんでした。
この点についてはGemini 3 Proの考察を補足として載せておきます。
Geminiの見解
単純に「固有名詞だからダメ」というわけではなく、以下の2つの要因が複合的に影響しているという仮説が得られました。
-
「主題」には強く、「属性」に弱い説 ベクトル検索は文書全体の意味(トピック)を捉えるのが得意です。
成功例: 「富士山」「徳川家康」のように、質問対象がその文書の主題(メインテーマ)そのものである場合、ベクトルは強く反応します。
失敗例: 「桜の科」「味噌の主原料」「相対性理論の提唱者」のように、主題(桜)に付随する特定の属性情報(バラ科)を問う質問では、ベクトルの向きが「桜(春・花・日本)」という大きなトピックに引っ張られ、「科」という細かい情報の重みが埋もれてしまった可能性があります。
-
モデルの言語特性(Global vs Local)説 今回使用した all-MiniLM-L6-v2 は英語圏のデータが中心のモデルです。
成功例: Sushi, Samurai (Ieyasu), Bitcoin, DNA など、英語圏でもそのまま通じる(グローバルな)概念は、日本語でも精度が高くなる傾向があります。
失敗例: 「バラ科 (Rosaceae)」「大豆 (Soybean)」のような植物分類や、「エネルギーミックス」のような専門用語の結びつきは、日本語特有の漢字表現や文脈において、このモデルでは知識として弱かった(ベクトルが近づききらなかった)可能性があります。
結論: こうした「属性情報の埋没」や「モデルの知識バイアス」に左右されず、キーワードさえ合っていれば理屈抜きで拾えるのがBM25(およびハイブリッド検索)の強みである、と再認識できました。
おわりに
今回の比較実験で、検索手法によって実際に検索性能に差が出ることが確認でき、
アプローチ次第でRAGシステム全体の改善が見込める、
逆に言えば不適切な手法の選択がボトルネックになる可能性も大きいため、
今回扱った以外の手法も含めて検証していく必要があると感じました。
また適切な評価指標を選ぶことの重要性も実感できました。
MAPのように、単に精度など特定の側面を測るだけでなくシステム全体の改善に繋がる数値があるため、
まずよく調べてどういった指標があるのかをピックアップし、1つ1つ考察をじっくり行うことが重要だと思います。
今後の展望
検索部分の性能改善という観点に限っても、
LLMの応答を利用して検索精度を向上させるというRAGならではの手法もあり、
その代表的なものとしてHyDE (Hypothetical Document Embeddings)などを検証してみたいです。
HyDEの大まかな流れ
-
ユーザーの質問に対し、LLMが「仮想的な回答(Hypothetical Document)」を生成する。
-
その「仮想回答」をベクトル化して検索を行う。
-
検索結果を使って本当の回答を生成する。
また今回の結果からBM25が想像以上に速い(ベクトル検索に比べて)ということもわかり、
BM25をもっと活用したアプローチにも可能性を感じました。
考えてみると検索部分では要は適切な情報をLLMに渡せればよいため、
必ずしもベクトル検索を行う必要もないのではと思いますが、
そういったアプローチが可能なのかどうかも調査・検討してみたいです。
参考文献
- [情報検索の基礎・評価指標] Manning, C. D., Raghavan, P., & Schütze, H. (2008). Introduction to Information Retrieval. Cambridge University Press.
- ※ 本記事で解説したベクトル空間モデル、BM25、およびMAP(Mean Average Precision)などの評価指標について体系的に解説された標準的な専門書です。
Discussion