Home 論文解説: Self-RAG — 自己反省トークンによる適応的検索・生成・批評の統合フレームワーク
投稿
キャンセル

📄 論文解説: Self-RAG — 自己反省トークンによる適応的検索・生成・批評の統合フレームワーク

論文概要(Abstract)

Self-RAG(Self-Reflective Retrieval-Augmented Generation)は、LLMに「反省トークン(reflection tokens)」を導入することで、検索の要否判断・文書の関連性評価・生成結果の事実整合性検証を単一モデル内で統合的に実行するフレームワークです。従来のRAGが常に検索を行い、検索結果の品質を評価せずにそのまま生成に渡していた問題を根本から解決します。NeurIPS 2023で発表され、13Bモデルが複数のベンチマークでChatGPTを上回る性能を達成しました。

この記事は Zenn記事: LangGraph×Claude Sonnet 4.6エージェント型RAGの精度評価と最適化 の深掘りです。

情報源

  • arXiv ID: 2310.11511
  • URL: https://arxiv.org/abs/2310.11511
  • 著者: Akari Asai, Zeqiu Wu, Yizhong Wang, Avirup Sil, Hannaneh Hajishirzi(University of Washington, IBM Research AI)
  • 発表年: 2023
  • 分野: cs.CL, cs.AI
  • 会議: NeurIPS 2023

背景と動機(Background & Motivation)

従来のRAGには3つの根本的な問題がありました。第一に、クエリの種類に関わらず常に検索を実行してしまい、創作タスクやLLMの内部知識で回答可能な質問にも不要な検索コストが発生します。第二に、検索された文書の関連性を評価する仕組みがなく、無関係な文書がそのまま生成に渡されてハルシネーションの原因となります。第三に、生成結果が検索文書に基づいているか事実整合性の検証がないため、LLMが検索結果を無視して事実と異なる内容を生成するケースを防げません。

Self-RAGはこれら3つの問題を「反省トークン」という統一的なメカニズムで解決します。モデルが生成の各段階で自律的に判断を行い、検索の要否・文書の関連性・回答の事実整合性を制御します。

主要な貢献(Key Contributions)

  • 反省トークンの導入: [Retrieve], [IsREL], [IsSUP], [IsUSE]の4種類のトークンにより、検索・生成・批評を単一モデルで統合
  • Criticモデルによるスケーラブルなアノテーション: GPT-4で約4,000例をアノテーションし、Criticモデルを訓練して15万例に拡張。人手アノテーション不要
  • 推論時のカスタマイズ: 反省トークンの重み調整により、タスクに応じた精度・引用性・有用性のバランスを再訓練なしで制御可能
  • 大幅な性能向上: 13Bモデルが6つのタスクでChatGPTを3つ上回り、FactScoreで76.4(ChatGPT+RAG: 71.1)を達成

技術的詳細(Technical Details)

反省トークンのアーキテクチャ

Self-RAGの核心は4種類の反省トークンです。これらは通常のテキストトークンと同様に語彙に追加され、生成時にインラインで出力されます。

トークン出力値役割
[Retrieve]yes / no / continue検索を実行するか判断
[IsREL]relevant / irrelevant検索文書の関連性を評価
[IsSUP]fully supported / partially / no support生成が検索文書に支持されるか
[IsUSE]5 / 4 / 3 / 2 / 1生成の有用性を5段階評価

訓練プロセス

訓練は2段階で行われます。

ステップ1: Criticモデルの訓練

GPT-4を使って(query, passage, generation)のペアに対し反省トークンのアノテーションを約4,000例作成します。このデータでLlama 2 7BベースのCriticモデルを通常の次トークン予測で訓練します。

\[\mathcal{L}_{\text{crit}} = -\sum_{t} \log p_\phi(r_t \mid x, d, y)\]

ここで $r_t$ は反省トークン、$x$ はクエリ、$d$ は検索文書、$y$ は生成テキスト、$\phi$ はCriticモデルのパラメータです。

訓練されたCriticモデルは150,000例のアノテーションに使用されます。GPT-4との一致率は[Retrieve]で91.3%、[IsREL]で92.1%、[IsSUP]で84.2%と高精度です。

ステップ2: Generatorモデルの訓練

指示追従データに対し、リトリーバーで上位$k$件の文書を取得し、Criticモデルで反省トークンをアノテーションします。これらをテキストにインライン挿入したデータで、Llama 2 7B/13Bを通常の次トークン予測で訓練します。

\[\mathcal{L}_{\text{gen}} = -\sum_{t} \log p_\theta(y_t \mid x, d_{1:k}, y_{<t}, r_{<t})\]

ここで $y_t$ は通常のテキストトークンと反省トークンの両方を含み、$\theta$ はGeneratorのパラメータです。

推論アルゴリズム

推論時のアルゴリズムは以下の通りです。

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
38
39
40
41
42
43
44
45
def self_rag_inference(query: str, retriever, model) -> str:
    """Self-RAG推論: セグメント単位で検索・生成・批評を実行"""
    output_segments: list[str] = []
    prev_context = query

    while not is_complete(output_segments):
        # ステップ1: 検索判断
        retrieve_token = model.generate_token(prev_context, token_type="Retrieve")

        if retrieve_token == "yes":
            # ステップ2: 検索実行
            passages = retriever.retrieve(prev_context, top_k=5)

            # ステップ3: 各文書に対してスコアリング
            candidates: list[tuple[str, float]] = []
            for passage in passages:
                # 関連性評価
                is_rel = model.generate_token(prev_context, passage, "IsREL")
                if is_rel == "irrelevant":
                    continue

                # セグメント生成
                segment = model.generate_segment(prev_context, passage)

                # 事実整合性評価
                is_sup = model.generate_token(segment, passage, "IsSUP")

                # 有用性評価
                is_use = model.generate_token(segment, query, "IsUSE")

                # スコア計算
                score = compute_score(is_rel, is_sup, is_use)
                candidates.append((segment, score))

            # 最高スコアのセグメントを選択
            best_segment = max(candidates, key=lambda x: x[1])[0]
            output_segments.append(best_segment)
        else:
            # 検索不要: そのまま生成
            segment = model.generate_segment(prev_context)
            output_segments.append(segment)

        prev_context = query + " ".join(output_segments)

    return " ".join(output_segments)

推論時のスコアリング関数

各候補セグメントのスコアは反省トークンの確率を組み合わせて計算します。

\[S(d_i) = \lambda_1 \cdot p(\text{IsREL}=\text{rel} \mid x, d_i) + \lambda_2 \cdot p(\text{IsSUP}=\text{full} \mid x, d_i, y) + \lambda_3 \cdot p(\text{IsUSE}=5 \mid x, y)\]

ここで $\lambda_1, \lambda_2, \lambda_3$ は重みパラメータで、タスクに応じて調整可能です。引用精度を重視するタスクでは $\lambda_2$ を大きく、回答の有用性を重視するタスクでは $\lambda_3$ を大きく設定します。

実装のポイント(Implementation)

Self-RAGの実装にはいくつかの重要な注意点があります。

ベースモデルの選択: Llama 2のbase版(chat版ではない)から訓練します。chat版は既にRLHFで調整されているため、反省トークンの学習に干渉する可能性があります。

Criticモデルの品質が上限を決定: GeneratorのデータはCriticが生成するため、Criticの精度がシステム全体の性能上限になります。[IsSUP]の一致率が84.2%と他より低いため、事実整合性の判定が最も改善の余地があります。

推論レイテンシのトレードオフ: 検索発動時にはK件の文書それぞれに対してforward passが必要です。top-5検索の場合、通常の5倍のforward passが発生します。本番環境ではK=3程度に絞るか、並列処理で対応することを推奨します。

セグメント単位のビームサーチ: 出力をセグメント(おおよそ1-2文)単位で区切り、各セグメントでビームサーチを実行します。beam_size=4がデフォルトですが、レイテンシとのトレードオフで2-3に減らしても性能低下は軽微です。

よくあるバグ: 反省トークンを語彙に追加する際、tokenizerのリサイズとモデルのembedding層のリサイズが必要です。model.resize_token_embeddings(len(tokenizer))を忘れると訓練時にindex out of rangeエラーが発生します。

Production Deployment Guide

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

トラフィック量別の推奨構成:

規模月間リクエスト推奨構成月額コスト主要サービス
Small~3,000 (100/日)Serverless$150-300Lambda + SageMaker Serverless + DynamoDB
Medium~30,000 (1,000/日)Hybrid$800-2,000ECS Fargate + SageMaker + ElastiCache
Large300,000+ (10,000/日)Container$5,000-15,000EKS + Karpenter + GPU Spot Instances

Small構成の詳細 (月額$150-300):

  • SageMaker Serverless: Self-RAG 7Bモデル、コールドスタート~30秒 ($80/月)
  • Lambda: API Gateway連携、リクエスト処理 ($20/月)
  • OpenSearch Serverless: FAISS代替のベクトル検索 ($30/月)
  • DynamoDB: キャッシュ・セッション管理 ($10/月)
  • S3: Wikipediaコーパス格納 ($5/月)

Large構成の詳細 (月額$5,000-15,000):

  • EKS + Karpenter: g5.2xlarge Spot Instances × 2-4台 ($2,000/月)
  • vLLM推論サーバー: PagedAttentionでGPUメモリ効率化
  • OpenSearch: 専用インスタンス、21Mパッセージインデックス ($500/月)
  • ElastiCache Redis: 推論結果キャッシュ ($100/月)

コスト削減テクニック:

  • Self-RAGの適応的検索により、全クエリの約24%は検索なしで回答(PopQA実測値)。検索コストを平均24%削減
  • 反省トークンのスコアをRedisにキャッシュし、同一クエリ・文書ペアの再評価を回避
  • Spot Instances使用で最大90%削減(g5.2xlarge: On-Demand $1.212/h → Spot ~$0.36/h)

コスト試算の注意事項:

  • 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
  • Self-RAGはモデルファインチューニングが前提のため、SageMakerの初期訓練コストが別途発生します(8×A100で約$200-400)
  • 最新料金は AWS料金計算ツール で確認してください

Terraformインフラコード

Small構成 (Serverless): SageMaker Serverless + OpenSearch

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# --- SageMaker Serverless Endpoint ---
resource "aws_sagemaker_model" "self_rag" {
  name               = "self-rag-7b"
  execution_role_arn = aws_iam_role.sagemaker_role.arn

  primary_container {
    image          = "763104351884.dkr.ecr.ap-northeast-1.amazonaws.com/huggingface-pytorch-inference:2.1-transformers4.37-gpu-py310"
    model_data_url = "s3://${aws_s3_bucket.models.id}/self-rag-7b/model.tar.gz"
    environment = {
      HF_MODEL_ID     = "selfrag/selfrag_llama2_7b"
      SM_NUM_GPUS     = "1"
      MAX_INPUT_LENGTH = "2048"
    }
  }
}

resource "aws_sagemaker_endpoint_configuration" "self_rag" {
  name = "self-rag-serverless"

  production_variants {
    variant_name           = "default"
    model_name             = aws_sagemaker_model.self_rag.name
    serverless_config {
      max_concurrency         = 5
      memory_size_in_mb       = 6144
      provisioned_concurrency = 0  # コスト削減: プロビジョニングなし
    }
  }
}

# --- OpenSearch Serverless (ベクトル検索) ---
resource "aws_opensearchserverless_collection" "rag_index" {
  name = "self-rag-passages"
  type = "VECTORSEARCH"
}

# --- DynamoDB (キャッシュ) ---
resource "aws_dynamodb_table" "reflection_cache" {
  name         = "self-rag-reflection-cache"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "query_doc_hash"

  attribute {
    name = "query_doc_hash"
    type = "S"
  }

  ttl {
    attribute_name = "expire_at"
    enabled        = true
  }
}

Large構成 (Container): EKS + vLLM + GPU Spot

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
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "self-rag-inference"
  cluster_version = "1.31"
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnets

  cluster_endpoint_public_access = true
  enable_cluster_creator_admin_permissions = true
}

resource "kubectl_manifest" "karpenter_gpu_provisioner" {
  yaml_body = <<-YAML
    apiVersion: karpenter.sh/v1alpha5
    kind: Provisioner
    metadata:
      name: gpu-spot
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["g5.2xlarge", "g5.4xlarge"]
      limits:
        resources:
          nvidia.com/gpu: "8"
      providerRef:
        name: default
      ttlSecondsAfterEmpty: 60
  YAML
}

運用・監視設定

CloudWatch Logs Insights クエリ:

1
2
3
4
5
6
7
-- 検索発動率の監視(Self-RAGの適応的検索の効果測定)
fields @timestamp, query, retrieve_token
| stats count(*) as total,
        sum(case when retrieve_token = 'yes' then 1 else 0 end) as retrieval_count
| filter total > 0
| eval retrieval_rate = retrieval_count / total * 100
| sort @timestamp desc

CloudWatch アラーム(推論レイテンシ監視):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import boto3

cloudwatch = boto3.client('cloudwatch')

cloudwatch.put_metric_alarm(
    AlarmName='self-rag-latency-p99',
    ComparisonOperator='GreaterThanThreshold',
    EvaluationPeriods=2,
    MetricName='ModelLatency',
    Namespace='AWS/SageMaker',
    Period=300,
    ExtendedStatistic='p99',
    Threshold=10000,  # 10秒(検索込み)
    AlarmDescription='Self-RAG P99レイテンシ異常'
)

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

  • ~100 req/日 → SageMaker Serverless ($150-300/月)
  • ~1000 req/日 → ECS Fargate + SageMaker Endpoint ($800-2,000/月)
  • 10000+ req/日 → EKS + vLLM + Spot Instances ($5,000-15,000/月)
  • GPU: Spot Instances優先(g5.2xlarge: 最大70%削減)
  • 反省トークンキャッシュ: 同一query-docペアのスコアをRedis/DynamoDBにキャッシュ
  • 適応的検索活用: 全クエリの20-50%は検索不要(モデルが自動判断)
  • vLLMのPagedAttention: GPUメモリ効率を2-4倍向上
  • バッチ推論: 非リアルタイム処理はSageMaker Batch Transform使用
  • モデルサイズ最適化: 7Bモデルで十分なケースは13Bを避ける(推論コスト半減)
  • コンテキスト長制限: max_tokens=300でトークン消費を制御

実験結果(Results)

論文の実験結果は、Self-RAGの有効性を明確に示しています。

モデルPopQATriviaQAPubHealthARC-CFactScore
Llama2 13B18.159.555.061.5
ChatGPT29.374.470.575.358.5
ChatGPT + RAG34.378.973.173.071.1
Self-RAG 7B54.966.472.467.374.5
Self-RAG 13B61.269.374.572.376.4

注目すべきは、Self-RAG 13BがPopQAでChatGPT+RAGを+26.9ポイント上回っている点です。これはエンティティ知識が必要なタスクで、適応的検索と反省トークンによる品質管理が極めて有効であることを示しています。

アブレーション分析では、各反省トークンの貢献度が明確になっています。[IsREL](関連性評価)の除去で-5.8ポイント、適応的検索の除去(常に検索)で-4.3ポイント、検索なしで-10.6ポイントと、すべてのコンポーネントが有意に貢献しています。

実運用への応用(Practical Applications)

Zenn記事で紹介されているLangGraph×Claude Sonnet 4.6のCorrective RAG・Self-Reflective RAGは、Self-RAGの設計思想をファインチューニング不要で実現するアプローチです。Self-RAGが専用モデルの訓練を要するのに対し、LangGraphベースの実装は既存のLLM APIをそのまま使えるため、導入コストが大幅に低くなります。

プロダクション視点での選択基準:

  • Self-RAG: 最高精度が必要な場合。専用モデル訓練が可能で、推論インフラを管理できるチーム向け
  • LangGraph + Claude: 迅速な導入が必要な場合。API呼び出しベースで、インフラ管理を最小化したいチーム向け
  • ハイブリッド: LangGraphのDocument GradingノードにSelf-RAGの反省トークン機構を統合し、両方の利点を活かす

関連研究(Related Work)

  • Corrective RAG (CRAG): Self-RAGの反省トークンを軽量リトリーバル評価器に置き換え、Web検索フォールバックを追加。Self-RAGより実装が簡単だが、精度はやや劣る
  • FLARE (Active Retrieval): 生成中のトークン確率が閾値を下回った時に検索を発動。Self-RAGの[Retrieve]トークンに類似するが、明示的な学習ではなく確率閾値に依存
  • ARES: RAG評価フレームワーク。Self-RAGの反省トークンとは異なり、外部評価器としてRAGパイプラインの品質を測定

まとめと今後の展望

Self-RAGは反省トークンという統一的なメカニズムにより、RAGの「検索判断・文書評価・事実検証」を単一モデルで実現した重要な研究です。13Bモデルで複数タスクにおいてChatGPTを超える性能を達成し、RAGシステム設計の新しいパラダイムを提示しました。

今後は、Multi-hop推論への対応、マルチモーダルRAGへの拡張、そしてLangGraphのようなオーケストレーションフレームワークとの統合が研究の方向性として期待されます。特に、推論時のカスタマイズ機構は、プロダクション環境でのタスク別チューニングに直接応用可能です。

参考文献

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

論文解説: SWE-bench — 実世界GitHubイシューでLLMのソフトウェアエンジニアリング能力を評価する

論文解説: Self-Consistency Improves Chain of Thought Reasoning in Language Models