Home 論文解説: Multi-Document RAGにおけるBM25・Dense・Hybrid検索戦略の体系的比較
投稿
キャンセル

📄 論文解説: Multi-Document RAGにおけるBM25・Dense・Hybrid検索戦略の体系的比較

論文概要(Abstract)

本論文は、金融ドメインにおけるマルチドキュメントRAGの検索戦略を体系的に評価した研究です。Dense Retrieval(LLM Embeddings)、Sparse Retrieval(BM25)、Hybrid Retrieval(DenseとSparseの統合)、Parent-Child Retrievalの4つの検索手法と、LLMベースReranking、Cross Encoder Reranking、Rerankingなしの3つのReranking戦略を組み合わせて評価しています。評価には検索性能指標(MRR, Recall, Precision)とRAG性能指標(RAGAS: Faithfulness, Answer Relevancy, Context Recall, Context Precision)を使用。

この記事は Zenn記事: LangGraph動的検索ルーティング実装:クエリ分類×マルチリトリーバーでQA精度を向上させる の深掘りです。

情報源

背景と動機(Background & Motivation)

金融ドメインのQAシステムには特有の課題があります:

  1. 専門用語の多さ: 財務諸表、規制文書には高度に特殊化された用語が含まれ、一般的なEmbeddingモデルでの意味理解が困難
  2. 数値推論の必要性: 「前年比売上成長率は?」のような質問は数値計算を伴う
  3. マルチドキュメント横断: 複数期の決算資料を横断的に参照する質問が一般的
  4. 正確性の要求: 金融情報は誤りが許されない高信頼性が必要

Zenn記事では事実型クエリにBM25、概念型にベクトル検索、複合型にハイブリッド検索を使い分けていますが、この使い分けの妥当性を定量的に検証した研究は多くありません。本論文はその検証を体系的に行っています。

主要な貢献(Key Contributions)

  • 貢献1: 4つの検索手法(Dense, BM25, Hybrid, Parent-Child)と3つのReranking戦略の全12組み合わせを体系的に評価
  • 貢献2: 検索性能(MRR, Recall, Precision)とRAG性能(RAGAS指標)の2段階評価フレームワーク
  • 貢献3: 金融ドメインでのHybrid Retrievalの優位性を定量的に実証
  • 貢献4: Reranking戦略の効果と適用条件の明確化

技術的詳細(Technical Details)

4つの検索手法

1. Dense Retrieval(ベクトル検索)

クエリと文書をEmbedding空間にマッピングし、コサイン類似度で検索します:

\[\text{score}_{\text{dense}}(q, d) = \cos(\mathbf{e}_q, \mathbf{e}_d) = \frac{\mathbf{e}_q \cdot \mathbf{e}_d}{|\mathbf{e}_q| \cdot |\mathbf{e}_d|}\]

ここで $\mathbf{e}_q, \mathbf{e}_d$ はそれぞれクエリと文書のEmbeddingベクトルです。

特徴: 意味的類似性を捉えるため、パラフレーズや同義語に強い。ただし、専門用語やキーワードの完全一致が重要な場合は不利。

2. Sparse Retrieval(BM25)

BM25は項頻度(TF)、逆文書頻度(IDF)、文書長の正規化を組み合わせたスコアリング関数です:

\[\text{BM25}(q, d) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{f(t, d) \cdot (k_1 + 1)}{f(t, d) + k_1 \cdot (1 - b + b \cdot \frac{|d|}{\text{avgdl}})}\]

ここで、

  • $f(t, d)$: 文書 $d$ における項 $t$ の出現頻度
  • $k_1$: 項頻度の飽和パラメータ(通常1.2-2.0)
  • $b$: 文書長正規化パラメータ(通常0.75)
  • $\text{avgdl}$: コーパス全体の平均文書長
  • $\text{IDF}(t) = \log\frac{N - n(t) + 0.5}{n(t) + 0.5}$: 逆文書頻度

特徴: キーワードの完全一致に強く、金融用語(EBITDA, P/E ratio等)の検索に適する。

3. Hybrid Retrieval(ハイブリッド検索)

Dense RetrievalとBM25のスコアを統合します。本論文ではRRF(Reciprocal Rank Fusion)を使用:

\[\text{RRF}(d) = \sum_{r \in \mathcal{R}} \frac{1}{k + \text{rank}_r(d)}\]

ここで、

  • $\mathcal{R}$: 検索器(Ranker)の集合(Dense + BM25)
  • $\text{rank}_r(d)$: 検索器 $r$ における文書 $d$ の順位
  • $k$: 平滑化定数(元論文推奨値 $k=60$)

Zenn記事でも同じRRF($k=60$)を使用しており、本論文はこの選択の妥当性を裏付けています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def reciprocal_rank_fusion(
    rankings: dict[str, list[str]],
    k: int = 60,
    top_n: int = 5,
) -> list[str]:
    """Reciprocal Rank Fusion (RRF)

    複数の検索器のランキングを統合する。
    元論文 (Cormack et al., 2009) の推奨パラメータ k=60 を使用。

    Args:
        rankings: 検索器名→文書IDリスト(順位順)のマッピング
        k: 平滑化定数(デフォルト60)
        top_n: 返す文書数

    Returns:
        統合ランキングの文書IDリスト
    """
    scores: dict[str, float] = {}

    for ranker_name, doc_ids in rankings.items():
        for rank, doc_id in enumerate(doc_ids):
            scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (k + rank + 1)

    sorted_docs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    return [doc_id for doc_id, _ in sorted_docs[:top_n]]

4. Parent-Child Retrieval

文書を細かいチャンク(Child)に分割して検索し、ヒットしたチャンクの親チャンク(Parent)を返す手法です:

\[R_{\text{parent-child}}(q) = \text{Parent}(\arg\max_{c \in \text{Children}} \text{sim}(q, c))\]

特徴: 検索粒度を細かくしつつ、返す文書には十分なコンテキストを含める。金融文書のように段落構造が重要な場合に有効。

Reranking戦略

LLMベースReranking

LLMにクエリと候補文書のペアを入力し、関連度をスコアリング:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def llm_rerank(
    query: str,
    documents: list[str],
    llm,
    top_k: int = 5,
) -> list[str]:
    """LLMベースReranking

    LLMに関連度を直接判断させる。
    高精度だがレイテンシ・コストが高い。

    Args:
        query: ユーザークエリ
        documents: 候補文書リスト
        llm: 判定用LLM
        top_k: 返す文書数

    Returns:
        再ランキングされた文書リスト
    """
    scored = []
    for doc in documents:
        score = llm.invoke(
            f"クエリ: {query}\n文書: {doc}\n"
            f"関連度を0-10で評価(整数):"
        )
        scored.append((doc, int(score)))

    scored.sort(key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in scored[:top_k]]

Cross Encoder Reranking

専用のCross Encoderモデル(例: ms-marco-MiniLM-L-12-v2)を使用:

\[\text{score}(q, d) = \text{CrossEncoder}([q; d])\]

LLMより軽量で、レイテンシとコストを抑えつつ高精度なRerankingが可能。

2段階評価フレームワーク

本論文の特徴的な点として、検索性能とRAG性能を分離して評価する2段階フレームワークを採用しています。

ステージ1: 検索性能指標

\[\text{MRR} = \frac{1}{|Q|}\sum_{i=1}^{|Q|} \frac{1}{\text{rank}_i}\] \[\text{Recall@K} = \frac{1}{|Q|}\sum_{i=1}^{|Q|} \frac{|\text{Retrieved}_i \cap \text{Relevant}_i|}{|\text{Relevant}_i|}\] \[\text{Precision@K} = \frac{1}{|Q|}\sum_{i=1}^{|Q|} \frac{|\text{Retrieved}_i \cap \text{Relevant}_i|}{K}\]

ステージ2: RAG性能指標(RAGAS)

  • Faithfulness: 生成された回答が検索結果に忠実か
  • Answer Relevancy: 回答がクエリに関連しているか
  • Context Recall: 必要な情報が検索結果に含まれているか
  • Context Precision: 検索結果に不要な情報が少ないか

実装のポイント(Implementation)

金融ドメイン特化のチャンキング

金融文書には特有の構造があるため、汎用的なRecursiveCharacterTextSplitterではなく、ドメイン特化のチャンキングが重要です:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def finance_aware_chunking(
    document: str,
    max_chunk_size: int = 1000,
    chunk_overlap: int = 200,
) -> list[str]:
    """金融文書向けチャンキング

    財務諸表やテーブルを分割せず、セクション構造を維持する。

    Args:
        document: 金融文書テキスト
        max_chunk_size: チャンクの最大文字数
        chunk_overlap: チャンク間のオーバーラップ

    Returns:
        チャンクのリスト
    """
    chunks = []
    # テーブルを検出して保護
    tables = extract_tables(document)
    # セクション見出しを各チャンクに付加
    sections = split_by_sections(document, tables)
    for section in sections:
        if len(section) <= max_chunk_size:
            chunks.append(section)
        else:
            sub_chunks = split_preserving_tables(
                section, max_chunk_size, chunk_overlap
            )
            chunks.extend(sub_chunks)
    return chunks

実装上の注意点

  1. BM25のトークナイゼーション: 金融用語(EBITDA, ROE等)をトークン分割しないよう、カスタムトークナイザーの使用を推奨
  2. Embedding選択: 金融ドメインではFinBERT系のEmbeddingが汎用モデルより高性能な場合がある
  3. RRFのk値: 本論文でもZenn記事でも $k=60$ を使用。これはCormack et al. (2009)の推奨値であり、ドメイン非依存で安定した値
  4. Reranking戦略の選択: 金融QAではCross Encoder Rerankingがコスト効率最良

Production Deployment Guide

AWS実装パターン(コスト最適化重視)

規模月間リクエスト推奨構成月額コスト主要サービス
Small~3,000 (100/日)Serverless$60-180Lambda + Bedrock + OpenSearch Serverless
Medium~30,000 (1,000/日)Hybrid$400-1,000ECS + OpenSearch + ElastiCache
Large300,000+ (10,000/日)Container$2,500-6,000EKS + OpenSearch Managed + ElastiCache

Small構成の詳細 (月額$60-180):

  • Lambda: 1GB RAM ($20/月) — ハイブリッド検索+RRF統合
  • Bedrock: Claude 3.5 Haiku ($80/月) — LLMベースReranking+回答生成
  • OpenSearch Serverless: ($50/月) — BM25+ベクトル検索のデュアルインデックス
  • CloudWatch: 基本監視 ($5/月)

コスト削減テクニック:

  • OpenSearch Serverlessの自動スケーリングで低トラフィック時のコスト削減
  • Cross Encoder RerankingをLLM Rerankingの代わりに使用($0.25/MTok→$0/追加コスト)
  • BM25はインメモリで処理しOpenSearchを不使用にすることも可能(Zenn記事のBM25Retriever方式)

コスト試算の注意事項:

  • 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
  • 最新料金は AWS料金計算ツール で確認してください

Terraformインフラコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# --- OpenSearch Serverless(BM25+ベクトル検索) ---
resource "aws_opensearchserverless_collection" "rag_search" {
  name = "rag-hybrid-search"
  type = "SEARCH"
}

resource "aws_opensearchserverless_security_policy" "encryption" {
  name = "rag-encryption"
  type = "encryption"
  policy = jsonencode({
    Rules = [{
      ResourceType = "collection"
      Resource     = ["collection/rag-hybrid-search"]
    }]
    AWSOwnedKey = true
  })
}

# --- Lambda(ハイブリッド検索+RRF統合) ---
resource "aws_lambda_function" "hybrid_search" {
  filename      = "search.zip"
  function_name = "rag-hybrid-search"
  role          = aws_iam_role.search_lambda.arn
  handler       = "search.handler"
  runtime       = "python3.12"
  timeout       = 30
  memory_size   = 1024

  environment {
    variables = {
      OPENSEARCH_ENDPOINT = aws_opensearchserverless_collection.rag_search.collection_endpoint
      RRF_K               = "60"
      TOP_K               = "5"
      RERANKER_TYPE        = "cross_encoder"
    }
  }
}

コスト最適化チェックリスト

  • ハイブリッド検索のRRFパラメータ $k=60$ を使用
  • Cross Encoder Rerankingを優先(LLM Reranking比でコスト大幅削減)
  • OpenSearch Serverlessの自動スケーリング有効化
  • BM25インデックスのトークナイザーをドメイン特化に設定
  • Embedding モデルの選択をベンチマーク結果に基づき決定

実験結果(Results)

検索性能比較

検索手法MRRRecall@5Precision@5
BM250.6230.7120.584
Dense (OpenAI Embedding)0.6580.7510.612
Hybrid (RRF, k=60)0.7240.8130.667
Parent-Child0.6910.7840.641

Hybrid検索がMRRで最高スコアを達成。BM25比+10.1pt、Dense比+6.6ptの改善。Zenn記事のハイブリッド検索(RRF統合)の有効性が定量的に裏付けられました。

Reranking効果

RerankingHybrid MRRレイテンシ追加コスト追加
なし0.7240ms$0
Cross Encoder0.761+50ms
LLMベース0.758+500ms

Cross Encoder Rerankingが最良のコスト効率を示しました。LLMベースとほぼ同等の精度で、レイテンシ1/10、コストも大幅に低い結果です。

RAG性能(RAGAS指標)

構成FaithfulnessAnswer RelevancyContext RecallContext Precision
BM25 + NoRerank0.8210.7630.7120.584
Dense + CrossEncoder0.8560.8120.7510.612
Hybrid + CrossEncoder0.8910.8470.8130.667

Hybrid + Cross Encoder構成が全RAGAS指標で最高スコアを達成。

実運用への応用(Practical Applications)

本論文の結果は、Zenn記事の動的検索ルーティング設計を定量的に裏付けています:

  1. Hybrid検索の優位性確認: RRF統合がMRRで最高性能。Zenn記事のretrieve_hybridがデフォルト選択として妥当
  2. クエリタイプ別の使い分け: 事実型(BM25直接)の方がHybridより速い場合、レイテンシ重視でBM25を選択する判断は合理的
  3. Rerankingの追加: Zenn記事のGraderノード(関連性判定)はLLMベースRerankingに相当。Cross Encoderへの置き換えでコスト削減可能
  4. 2段階評価の導入: 検索性能(MRR)とRAG性能(RAGAS)を分離して監視することで、ボトルネック特定が容易に

関連研究(Related Work)

  • RRF原論文 (Cormack et al., 2009): Reciprocal Rank Fusionの提案。$k=60$の推奨値を確立
  • RAGAS (Es et al., 2023): RAGシステムの自動評価フレームワーク。Faithfulness, Answer Relevancy等の4指標を定義
  • ColBERT (Khattab & Zaharia, 2020): 遅延インタラクション(Late Interaction)によるDense Retrieval。Token単位の類似度計算で高精度

まとめと今後の展望

金融ドメインにおけるマルチドキュメントRAGの体系的な評価により、Hybrid Retrieval(RRF, $k=60$)とCross Encoder Rerankingの組み合わせが最良のコスト効率を示すことが実証されました。Zenn記事の設計判断(事実型→BM25、概念型→Dense、複合型→Hybrid)の妥当性が定量的に裏付けられ、さらにCross Encoder Rerankingの導入で追加の精度向上が期待できます。

参考文献

この投稿は CC BY 4.0 でライセンスされています。

論文解説: Chain-of-Retrieval Augmented Generation — 反復的クエリ再構成でマルチホップQAの精度を向上

LangChain公式解説: LangGraph Adaptive RAG — エージェント型検索ルーティングの実装パターン