AI活用

RAGのリランキングとは?Cross-Encoderの実装とtop-k評価

RAGの一次検索候補をリランキングする方法を解説。Cross-EncoderとRRFの役割、候補数top-k、Python実装、導入前後の比較方法、遅延が増える場合の対処まで整理します。

RAGの検索候補を再評価して関連度順に並べ替えるイメージ
この記事の目次
  1. 結論:回収と精査を二段に分ける
  2. RAGのリランキングは何を改善する?
  3. Cross-Encoderとバイエンコーダーの違いは?
  4. RRFはCross-Encoderの前段に置く
  5. 手順1:一次検索の候補を固定する
  6. 手順2:Cross-Encoderで候補を再採点する
  7. 長いチャンクをそのまま切り捨てない
  8. 手順3:再順位付け前後の上位k件を比較する
  9. 再順位付け前後の上位k件をどう比較する?
  10. 候補数上位k件はどう決める?
  11. 遅延が増えすぎたらどうする?
  12. リランキングの失敗をどう切り分ける?
  13. 導入前チェックリスト
  14. よくある質問
  15. RRFだけでもリランキングになりますか?
  16. Cross-Encoderを全文書へ使ってはいけませんか?
  17. 再順位付け スコアが低い候補は捨ててよいですか?
  18. 日本語RAGでも英語のリランカーを使えますか?
  19. 再順位付けで回答のハルシネーションはなくなりますか?
  20. まとめ
  21. 公式資料・原論文

RAGのリランキングは、一次検索が集めた候補だけを、質問との関連度で並べ直す二段目の処理です。まず広めに候補を回収し、Cross-Encoderでクエリと各チャンクを同時に採点して、生成モデルへ渡す上位だけを選びます。

この記事は一次検索後の候補再順位付けに限定し、Cross-Encoder、候補数上位k件、遅延を測る実装を扱います。ベクトル検索とキーワード検索の統合やRRFの実装は、役割が重ならないようRAGのハイブリッド検索を実装する方法へ分けています。RAG全体の評価データセット設計や生成回答の品質評価も扱いません。

情報確認日:2026年8月17日(日本時間)

結論:回収と精査を二段に分ける

標準的な流れ

  1. 一次検索:ベクトルまたはキーワード検索で候補を広めに取得する
  2. 候補準備:一次検索の候補ID・順位・テキストを固定する
  3. 再採点:Cross-Encoderへ質問と各候補のペアを渡す
  4. 絞り込み:再採点後の上位だけを生成モデルへ渡す
  5. 比較:同じ質問セットで、正解チャンクの順位とp95遅延を導入前後で測る
項目 最初の基準 完成条件
一次候補 上位 20〜50から検証 正解チャンクが候補内へ入る
再順位付け後 上位3〜8件を生成へ渡す 正解チャンクの順位が上がる
品質 固定した実質問セット 導入前より悪化していない
性能 p50 / p95 遅延 サービスの応答時間の上限内

上の候補数は開始点であり、推奨値の保証ではありません。コーパス、文書長、言語、リランカー、CPU/GPU、同時リクエスト数によって最適値は変わります。

スポンサーリンク

RAGのリランキングは何を改善する?

一次検索は大量のチャンクから候補を素早く回収する役目です。ベクトル検索は言い換えに強い一方で、意味が近いだけの一般説明を上位へ出すことがあります。キーワード検索は製品名や番号に強い一方で、語が一致しない質問を落とすことがあります。

リランカーは候補を増やせません。候補内に正解チャンクがあるとき、その順位を上げ、生成モデルへ渡すコンテキストを選びやすくします。正解が一次候補に入っていない場合は、リランカーではなく一次検索、クエリ、フィルター、チャンク設計へ戻ります。

全corpus
  │ fast retrieval: 広く回収
  ▼
候補 30件
  │ optional RRF: 複数順位を統合
  │ Cross-Encoder: queryとの一致を再採点
  ▼
上位 5件
  │
  └─ generation modelへ渡す

Cross-Encoderとバイエンコーダーの違いは?

バイエンコーダーは質問と文書を別々にベクトル化します。文書ベクトルを事前計算できるため、大きなコーパスから近い候補を高速に探せます。Cross-Encoderは質問と文書を一つのペアとして入力し、トークン同士の関係を見てスコアを返します。

比較 バイエンコーダー / ベクトル検索 Cross-Encoder
入力 クエリと文書を別々にエンコード 質問と文書の組を一緒にエンコード
事前計算 文書ベクトルを保存できる クエリごとに各ペアを計算する
役割 大規模コーパスから候補回収 少数候補の精査・並べ替え
コスト 候補取得が速い 候補数にほぼ比例して増える
使いどころ 一次検索 二次検索

Sentence Transformersの公式ガイドも、バイエンコーダーで候補を取得し、Cross-Encoderで再採点するRetrieve & Re-Rank構成を示しています。Cross-Encoderをコーパス全件へ実行するとペア数が膨らむため、一次候補へ限定します。

RRFはCross-Encoderの前段に置く

RRFはベクトル検索とBM25など、複数の順位表を一つへ統合する処理です。本文を読んでクエリとの一致を再採点するCross-Encoderとは役割が違います。複数検索を使う場合だけ、次のようにRRFを前段へ置けます。

vector top 30 ─┐
               ├─ RRFで重複排除・順位統合 ─ Cross-Encoder ─ final top 5
BM25 top 30 ───┘

検索方式が一つだけならRRFは不要です。順位統合の式、Python実装、単独検索との比較手順は、ハイブリッド検索の記事で確認してください。ここからは、統合済みの候補をCross-Encoderで精査する工程だけを扱います。

手順1:一次検索の候補を固定する

リランカー導入前に、質問、期待する正解チャンク、一次候補のID・順位・スコアを保存します。質問を変えながら比較すると、改善がリランカーによるものか判断できません。

{
  "query_id": "q-014",
  "query": "返品の受付期限は?",
  "expected_chunk_ids": ["policy:returns:deadline"],
  "retriever_version": "embed-v3+index-20260817",
  "candidate_limit": 30,
  "candidates": [
    {"id": "faq:shipping:delay", "rank": 1, "score": 0.83},
    {"id": "policy:returns:deadline", "rank": 6, "score": 0.78}
  ]
}

ここで正解チャンクが上位 30に入らない質問が多ければ、候補数を増やすだけでなく、クエリ表現、メタデータ フィルター、ハイブリッド検索、チャンクの境界を確認します。

手順2:Cross-Encoderで候補を再採点する

PythonのSentence Transformersでは、CrossEncoder.rank()へクエリと文書の一覧を渡し、スコア順の結果を取得できます。次は環境差を減らすためにバージョンを固定した再現用の例です。

python -m venv .venv
source .venv/bin/activate
python -m pip install "sentence-transformers==5.6.1"
python -c "import sentence_transformers; print(sentence_transformers.__version__)"
# 5.6.1

この記事のコードは、再現時の環境差を減らすため5.6.1へ固定しています。2026年8月17日にPyPIで確認した最新版は5.7.0です。導入時はPyPIのリリース履歴と利用中のPython・PyTorchとの互換性を確認し、採用バージョンをロックファイルへ残してください。初回実行時はモデルファイルを取得します。公開環境ではモデルのリビジョンやアーティファクトのハッシュも固定し、取得元、ライセンス、保存先を記録してください。次はAPIと出力形式を確認する最小例です。

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")

query = "返品の受付期限は?"
candidates = [
    {"id": "faq:shipping:delay", "text": "配送遅延が発生した場合は..."},
    {"id": "policy:returns:deadline", "text": "返品は商品到着後14日以内に..."},
    {"id": "policy:exchange", "text": "交換を希望する場合は..."},
]

ranked = reranker.rank(
    query,
    [item["text"] for item in candidates],
    return_documents=False,
    top_k=3,
)

results = [
    {
        **candidates[row["corpus_id"]],
        "rerank_score": float(row["score"]),
    }
    for row in ranked
]

for rank, item in enumerate(results, start=1):
    print(rank, item["id"], item["rerank_score"])

正常なら3行がスコア降順で表示され、各行は順位 chunk_id scoreの形になります。スコアはlibrary・モデル 仕様リビジョン・実行環境で変わるため固定値との一致ではなく、policy:returns:deadlineが1位になることと3件が返ることを確認します。この記事の環境にはSentence Transformersを導入していないため、数値を実測値としては掲載しません。

モデル名は英語検索向けの例です。日本語または多言語コーパスでは、対象言語とドメインに対応するリランカーを選び、自分の質問セットで確認してください。モデルカードのライセンス、学習ドメイン、最大入力長、コード実行要件も確認します。

長いチャンクをそのまま切り捨てない

Cross-Encoderには最大入力長があります。クエリとチャンクの合計が超えると末尾が切られ、結論や例外が消えることがあります。長い候補は見出し・段落単位で分け直すか、titleと該当セクションだけをリランカーへ渡します。

再順位付け用テキストと生成用テキストを分ける場合も、同じchunk_idで結びます。短い検索テキストのスコアを、別の無関係な長文へ付け替えないようにします。

手順3:再順位付け前後の上位k件を比較する

ここで比較するのは、同じ一次候補に対する導入前の順位とCross-Encoder導入後の順位です。複数検索の統合条件まで同時に変えると、改善要因を切り分けられません。ハイブリッド検索を使う場合も、RRFの設定を固定してからリランカーだけを有効・無効にします。

再順位付け前後の上位k件をどう比較する?

次の表は形式を示す架空例です。実測ベンチマークではありません。正解チャンクが候補内にあるか、再順位付け後に何位へ動いたか、誤った上位候補がどう下がったかを一問ずつ確認します。

候補 一次順位 再順位付け順位 判定理由
配送遅延FAQ 1 4 「期限」はあるが返品の答えではない
返品規約:受付期限 6 1 質問と条件が直接一致
交換規約 3 3 近い手続きだが返品期限ではない
返品規約:送料 8 2 同じ文書だが答えは一部のみ

順位が改善しても、同じ文書の似たチャンクが上位を占有すると情報量は増えません。生成へ渡す直前に、文書ごとの最大チャンク数や重複テキストの除去を検討します。ただし重要な連続セクションまで落とさないよう、評価例で確認します。

候補数上位k件はどう決める?

一次候補を増やすほど正解を回収できる可能性は上がりますが、Cross-Encoderのペア計算、メモリ、遅延も増えます。最終コンテキストを増やしすぎると、生成モデルへ不要情報が入ります。

調整値 小さすぎる場合 大きすぎる場合
一次候補数 正解がリランカーへ届かない 再順位付けの遅延とコストが増える
再順位付け後件数 複数根拠や例外が欠ける ノイズとコンテキスト量が増える
チャンク長 答えが分断される 入力切り捨て、主題の混在
  1. 再順位付けなしで一次上位 10、20、30、50の正解包含率を比べる
  2. 包含率の伸びが小さくなる範囲を候補にする
  3. その範囲で再順位付け後上位 3、5、8の正解順位と生成回答を比べる
  4. 同時リクエストを含むp50、p95 遅延とタイムアウト率を測る
  5. 品質と応答時間の上限を満たす最小の候補数を採用する

遅延が増えすぎたらどうする?

原因 確認 対策
候補が多い 候補数別のペア数 一次検索を改善し、必要最小数へ減らす
バッチが効かない バッチサイズとデバイス使用率 メモリ内でバッチを調整する
モデルが大きい モデル別の品質・p95 小型モデル、ONNX、OpenVINO等を比較する
長文が多い トークン長分布・切り詰め チャンク境界と再順位付け用テキストを見直す
毎回同じクエリ クエリ正規化後のヒット率 バージョン付きキャッシュを検討する

速度のためにリランカーを外す前に、質問の種類で分岐する方法もあります。型番の完全一致はキーワード結果を優先し、曖昧な自然文だけ再順位付けする構成です。分岐を入れたら、どのルートが選ばれたかをログへ残します。

リランキングの失敗をどう切り分ける?

回答に必要なchunkが出ない
├─ 一次候補に正解がある?
│  ├─ No  → retriever・query・filter・chunkへ戻る
│  └─ Yes
├─ rerank後に順位が下がった?
│  ├─ Yes → 対象言語・domain・入力切り捨て・modelを確認
│  └─ No
├─ final top-kで落ちた?
│  ├─ Yes → final件数・重複除去条件を確認
│  └─ No
└─ generationが根拠を使わない
   └─ prompt・citation・回答評価へ切り替える

再順位付け スコアの絶対値はモデルごとに意味が異なる場合があります。別モデルのスコアを同じしきい値で比較せず、バージョンごとの順位と評価結果を保存します。

導入前チェックリスト

  • 実際の質問と正解チャンクを持つ固定テストセットがある
  • 一次候補内に正解が入るかを再順位付け前に測っている
  • RRFとCross-Encoderの役割を混同していない
  • 対象言語・ドメイン・ライセンスに合うモデルを選んだ
  • 最大入力長と切り詰めを確認した
  • 候補数と最終件数を設定値としてバージョン管理している
  • 導入前後の正解順位、p50、p95、エラー率を記録する
  • タイムアウト時に一次検索結果へフォールバックできる

よくある質問

RRFだけでもリランキングになりますか?

広い意味では順位の再構成ですが、Cross-Encoderのような本文再採点とは役割が違います。RRFは複数の順位付き一覧を順位で統合し、Cross-Encoderはクエリと候補テキストのペアをスコアします。

Cross-Encoderを全文書へ使ってはいけませんか?

小さなparagraph集合なら可能です。ただしコーパスが増えるとクエリごとのペア数が増えるため、通常は高速な一次検索で候補を絞ります。

再順位付け スコアが低い候補は捨ててよいですか?

しきい値はモデルとドメインで校正が必要です。まず固定件数で評価し、根拠不足時に拒否する設計と合わせてしきい値を決めます。

日本語RAGでも英語のリランカーを使えますか?

英語専用モデルでは品質を保証できません。日本語またはmultilingual対応がモデルカードで確認できる候補を選び、日本語の実質問セットで比較してください。

再順位付けで回答のハルシネーションはなくなりますか?

なくなりません。根拠チャンクの順位は改善できますが、生成時の誤りは別に起きます。根拠限定プロンプト、引用、回答評価、人への引き継ぎを組み合わせます。

まとめ

RAGのリランキングは、大量コーパスを直接精査するのではなく、一次検索が回収した候補へ限定して行います。複数検索の順位付き一覧はRRFで統合でき、クエリと本文の関連度はCross-Encoderで再採点できます。

導入効果は、正解チャンクが一次候補に含まれたか、再順位付け後に何位へ動いたか、p95 遅延が応答時間の上限内かで判断します。候補数を増やすだけで解決せず、検索、再順位付け、生成を別々の観測点として改善してください。

公式資料・原論文

スポンサーリンク