
ハイブリッド検索:BM25 vs ベクトル検索(両方が必要な理由)
サポート担当者がRAGチャットボットに「SKU-4471」と入力しました。結果は4件返ってきます。しかし、どれも自信満々に間違っています。汎用の埋め込みモデルには、あのexactな文字列をベクトル空間内で自分自身の近くに配置する理由がありません。このたった一つの失敗パターンこそがハイブリッド検索が存在する理由であり、各チームが同じ問いを繰り返し投げかける理由でもあります。BM25とベクトル検索を実際にどう統合すれば、チューニングつまみを永遠に面倒見なくて済むのか、という問いです。
RAGツール全般を比較検討している段階なら、おすすめRAGツールまとめで周辺スタック全体を俯瞰できます。
この記事のポイント
- BM25は完全一致のキーワード(SKU、エラーコード)を見つけ、ベクトル検索は概念的に似たテキストを見つけるもので、同一文字列の一致を探すものではありません。
- ハイブリッド検索は通常Reciprocal Rank Fusion(RRF)で両者を統合し、混在するクエリ負荷では単独のどちらよりも高い精度を出します。
- WANDSベンチマークでは、素のRRFが0.7068 NDCGを記録(BM25単独は0.6983)。チューニング済みなら0.7497まで伸び、7.4%の改善です。
- Postgres/pgvectorなら
ts_rank+pgvectorでハイブリッド検索をネイティブ実行でき、専用ベクトルデータベースは不要です。
ハイブリッド検索とは?(BM25+ベクトル検索の統合)
ハイブリッド検索は、同じクエリに対してBM25とベクトル検索を2つの独立した検索パスとして実行し、2つのランキング結果リストを融合アルゴリズム(最も一般的なのはReciprocal Rank Fusion)で1つの出力にマージします。これは第三の検索手法ではなく、既存の2手法の上に載るオーケストレーション層です。
この区別は重要です。このトピック周辺の検索トラフィックのかなりの部分が、BM25とベクトル検索を同じもののように混同しているからです。両者は別物です。BM25は1970年代の情報検索にルーツを持つ、スパースでキーワードベースのスコアリング関数です。ベクトル検索は密な埋め込みベースの類似度検索で、大規模環境で実用的になったのはこの10年のことです。ハイブリッド検索は両者を競合する手法ではなく相補的な入力として扱い、最初に勝者を選ぶのではなく、出力を融合します。
BM25 vs ベクトル検索 vs ハイブリッド:早見表
| 観点 | BM25(スパース/語彙一致) | ベクトル検索(密/意味) | ハイブリッド |
|---|---|---|---|
| 得意なこと | 完全一致の用語、希少トークン、ID | 言い換え、同義語、概念 | 両方のクエリタイプ |
| 苦手なこと | 言い換え質問、同義語 | SKU、エラーコード、略語 | どちらのパターンもないコーパス |
| 完全一致(SKU、ID、エラーコード)への対応 | 可 | 不可 | 可 |
| 言い換え・同義語への対応 | 不可 | 可 | 可 |
| 埋め込みモデルの要否 | 不要 | 必須 | 必須 |
| 必要なチューニング | k1、bパラメータ | チャンキング、モデル選定 | 融合手法(RRF/alpha) |
| 典型的なレイテンシ | 1ミリ秒未満〜数ミリ秒 | 数〜十数ミリ秒(ANN依存) | 両者の合計+融合オーバーヘッド |
| ネイティブ対応の例 | Elasticsearch、Postgres ts_rank | Qdrant、Pinecone、pgvector | Weaviate、Qdrant、Elasticsearch |
WANDS eコマースベンチマークでは、BM25単独が0.6983 NDCG、ベクトル検索単独が0.6953(ほぼ同率)でした。コーパスごとのチューニングなしの素のRRF融合は0.7068に到達し、BM25単独より1.2%の小幅な改善です。Doug Turnbullのベンチマークでは、RRFの上に商品名ブーストを重ねたチューニング済みバリアントもテストされ、そのバージョンは0.7497、つまり7.4%の改善を記録しました。どの数字を引用しているかには正直であるべきです。RRF単独では、そのまま使える小幅だが実在する優位性が得られます。一方、7.4%というより大きな数字には、ほとんどのチームが初日には飛ばすドメイン固有の追加チューニングが必要でした。BM25もベクトル検索も、単独では支配的になりません。両者は異なる失敗モードをカバーしており、融合すれば両方のギャップを同時に塞げます。
BM25の仕組み:キーワード検索は関連性をどうスコアリングするか
BM25は、文書内の用語の出現頻度を、その用語がコーパス全体でどれだけ希少かで重み付けし、文書長で正規化してスコアを算出します。RobertsonとZaragozaは、2009年の論文"The Probabilistic Relevance Framework: BM25 and Beyond"でこの形式化を提示しました。これはTF-IDFの改良であり、TF-IDFの置き換えではありません。
BM25の挙動の大部分は、2つのパラメータで決まります。k1(通常1.2〜2.0)は用語頻度の飽和を制御します。単語の繰り返しによるスコア上昇に上限を設けることで、「invoice」を40回含む文書が、より引き締まった関連パッセージ内で4回含む文書に自動で勝つことを防ぎます。b(デフォルト0.75)は文書長の正規化を制御します。長い文書が自然により多くの用語一致を含むことに対するペナルティの厳しさを決めます。
bの設定ミスは、実際にありがちなチューニング上の失敗です。短い技術文書(エラーログ、商品タイトル)は長さの分散が小さいためbを低くすべきで、長文コンテンツ(ドキュメントページ、記事)は通常デフォルトに近いbが適切です。BM25の本質的な弱点は語彙の不一致です。ユーザーが「どうやってお金を返してもらえるの?」と尋ね、文書に「返金ポリシー」としか書かれていない場合、BM25は共有トークンゼロと判定し、有用なものを何も返しません。
ベクトル検索の仕組み(そして限界点)
ベクトル検索は、モデルを使ってテキストを固定次元の埋め込みに写像し、コサイン類似度や内積で近傍ベクトルを検索します。通常は近似最近傍インデックスで高速化します。HNSWはWeaviate、Qdrant、Milvus全体で支配的なアルゴリズムで、大規模環境での大幅な速度向上と引き換えに、再現率をわずかに犠牲にします。
これこそがBM25の語彙不一致問題を解決するものです。「お金を返してもらう」と「返金ポリシー」は、共有トークンがゼロでも埋め込み空間内で近くに配置されます。モデルが表面的な形ではなく意味を捉えるからです。ここで正しいモデルを選ぶことが大きく効いてきます。適切な埋め込みモデルの選び方ガイドと、Voyage、OpenAI、Cohereの埋め込みをどう比較したかの詳細も、選択肢を比較検討しているなら参考にしてください。
しかし、密な検索にも固有の盲点があり、それはBM25の盲点の鏡像です。私たちがクライアント向けにRAGシステムを構築するとき、最も頻繁に遭遇する完全一致の失敗は、特殊なものではありません。サポート担当者が特定の注文番号やSKUを尋ね、ベクトルインデックスが意味的には似ているが間違っている何かを自信満々に返す、というケースです。汎用の埋め込みモデルには、「SKU-4471」や「ERR_CONN_RST」を、関連しているが間違っているトークンよりも自分自身の近くにベクトル空間内で配置する強い理由がありません。そのような文字列は、訓練データ内で独立した孤立した概念として登場することがほとんどないからです。BigData Boutiqueも、まさにこの失敗パターンを自社SKUとエラーコードの例とともに記録しています。これはRAGデプロイ全体で広く確認され、独立して裏付けられている現象であり、一度きりの特殊事例ではありません。
BM25とベクトル検索の統合方法:RRF vs アルファ重み付き融合
BM25とベクトルの結果を融合する方法は実質2つありますが、ハイブリッド検索について書く人のほとんどは、この2つを明確に対比していません。Cormack、Clarke、Buettcherの2009年SIGIR論文に由来する**Reciprocal Rank Fusion(RRF)**は、順位に対して作用します。各結果リストにわたりscore = sum(1 / (k + rank_i))を計算し、kは通常60に設定します。生のスコアではなく順位だけを気にするため、RRFはBM25の上限なしスコアとコサイン類似度の0〜1レンジのスケール不一致を問題なく処理でき、コーパスごとのチューニングも不要です。
アルファ重み付き(凸)融合は異なる仕組みです。final = alpha * dense_score + (1 - alpha) * sparse_scoreで、順位ではなく正規化されたスコアに対して作用します。信頼度の大きさをよりよく反映できます(類似度0.95のベクトルヒットは、0.61のそれより純粋に強く見えます)。しかし、コーパスごとにalphaのチューニングが必要で、スコア分布が変わると(新しい埋め込みモデル、コーパスの再インデックス、異なるクエリミックス)そのチューニングはサイレントに壊れます。
実際には、この選択はスコアのキャリブレーションをどれだけ信頼できるかに行き着きます。単一の安定した埋め込みモデルに対して素のBM25を走らせているなら、アルファ重み付きは実際のスコア差を使うため、わずかに良いランキングを絞り出せます。しかし、そのキャリブレーションは人が思うより頻繁にずれます。新しい埋め込みモデルのバージョンに差し替えたり、文書のチャンキングをし直したり、上流にリランキングパスを追加したりすれば、密なスコアの分布は変わります。alpha=0.6が正しい値でなくなったとき、誰もページングを受けません。ランキングはただ静かに少しだけ悪くなり、検索の評価を定期的に回していなければ見逃しがちです。RRFはこれを完全に回避します。生のスコアを一切見ず、順位の位置だけを見るため、再インデックスやモデル差し替えがアルファ重み付きを壊すようにサイレントに壊れることはありません。
RRFはコーパスごとのチューニング不要。アルファ重み付きは、データが変わるたびに絶えず面倒を見る必要がある。
エンジンのデフォルトは分かれています。WeaviateはRRFと、明示的に設定するalphaパラメータの両方を公開しています。Elasticsearchはretriever API経由でネイティブRRFを搭載しています(デプロイでの正確なバージョン条件は要確認。この機能は8.x系で入りました)。QdrantはQuery API経由でRRFをネイティブサポートします。Pineconeのハイブリッド機能は、通常RRFを直接公開するのではなく、アルファ重み付きの凸結合に依存します。どちらを使うか確信が持てないなら、RRFから始めてください。メンテナンス負荷が低いデフォルトです。
RRFをスクラッチ実装:ベンダー非依存のPythonサンプル
競合ガイドで見つかるRRFコードサンプルは、すべてどれか1社のSDKに固定されています。Weaviateのクライアント、Qdrantのクライアント、Pineconeのクライアント、という具合です。以下は、どのスタックにもそのまま入れられるフレームワーク非依存版です。k=60を標準デフォルトとします。
def reciprocal_rank_fusion(result_lists, k=60):
"""
result_lists: list of ranked lists, each a list of document IDs
ordered from most to least relevant.
k: RRF constant (60 is the standard default from Cormack et al., 2009).
Returns: list of (doc_id, fused_score) sorted descending by score.
"""
scores = {}
for result_list in result_lists:
for rank, doc_id in enumerate(result_list, start=1):
scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (k + rank)
return sorted(scores.items(), key=lambda item: item[1], reverse=True)
# Example usage:
bm25_hits = ["doc_9", "doc_2", "doc_14", "doc_1"]
vector_hits = ["doc_2", "doc_1", "doc_9", "doc_30"]
fused = reciprocal_rank_fusion([bm25_hits, vector_hits])
for doc_id, score in fused:
print(f"{doc_id}: {score:.4f}")これでアルゴリズム全体です。SDKもベンダーロックインもなく、2つのランキングリストがElasticsearchとFaissインデックスから来ようが、Postgresのts_rankとpgvectorから来ようが動作します。APIを叩く代わりにモデルを自前で動かしているなら、Ollamaで埋め込みモデルをローカル実行する方法も参照してください。
Postgres+pgvector:専用ベクトルDB不要のハイブリッド検索
ハイブリッド検索の実行に専用ベクトルデータベースは不要です。開発者Pedro Alonsoによるpg_textsearch/pgvectorベンチマーク(Postgres 17、pg_textsearch 1.3.0、pgvector 0.8.2、nomic-embed-text埋め込み、BEIR SciFactデータセット)によると、ネイティブts_rankを動かした単一のPostgresインスタンスはわずか0.07 NDCG@10で、BM25の0.69、pgvectorの0.66、ハイブリッドの0.70に大きく水をあけられました。すべて同一インスタンス内で、ハイブリッドRRFのレイテンシ中央値は約11.5msです。
この0.07という数字が全てを物語っています。Postgres組み込みのts_rankはカバー密度ランカーであり、真のBM25ではありません。Postgresで本物のBM25スコアリングが欲しければ、拡張機能が必要です。pg_textsearch、VectorChord、ParadeDBはいずれも、ネイティブts_rankが提供しない適切なBM25スタイルのランキングを追加します。そのどれかとpgvector(密な類似度担当)を組み合わせ、上記のRRF関数で2つのランキングリストを融合すれば、別途動かすインフラなしで、単一のPostgresインスタンス内にハイブリッド検索が完成します。
その組み合わせが単一クエリでどうなるかの大まかな姿が以下です。BM25対応拡張による語彙一致ランクと、pgvectorによるベクトル距離を結合します。
WITH lexical AS (
SELECT id, ts_rank_cd(body_tsv, query) AS rank
FROM documents, plainto_tsquery('english', 'refund policy') query
WHERE body_tsv @@ query
ORDER BY rank DESC LIMIT 50
),
semantic AS (
SELECT id, embedding <=> '[0.012, -0.045, ...]' AS distance
FROM documents
ORDER BY distance LIMIT 50
)
SELECT * FROM lexical
FULL OUTER JOIN semantic USING (id);両方の結果セットを上記のRRF関数に流し込めば、1つのPostgresインスタンスでハイブリッド検索の出来上がりです。正直な限界もあります。この構成は数百万行規模の前半までは十分持ちますが、Postgresは専用検索エンジンとして作られていません。インデックスのチューニングは自己責任で、素のts_rank_cdは拡張なしでは真のBM25ではなく、WeaviateやMilvusがネイティブ搭載する組み込みリランキングやマルチベクトルサポートもありません。コーパスが小〜中規模で、すでにPostgresを運用しているなら、インフラの2台目を丸ごと節約できます。数千万文書を超える場合や、高度なリランキングが必要な場合は、専用エンジンがそのコストに見合います。
スタック全体でQdrant、Chroma、pgvectorのどれにするかを比較検討しているなら、それは融合手法そのものとは別の判断です。トレードオフはQdrant、Chroma、pgvectorの比較を参照してください。
ネイティブハイブリッド検索に対応するベクトルDB
最新のベクトルデータベースの大半は、今やハイブリッド検索を標準搭載しています。ただし、デフォルトの融合手法は意味のある差があります。
| エンジン | ネイティブハイブリッド対応 | 融合手法 | 備考 |
|---|---|---|---|
| Weaviate | 可 | RRFまたはアルファ重み付き | 両方を公開、クエリごとに選択可 |
| Qdrant | 可 | RRF | Query API経由 |
| Elasticsearch | 可 | RRF | retriever API経由 |
| OpenSearch | 可 | 正規化+重み付き和 | 「normalization processors」を使用 |
| Vespa | 可 | ネイティブ融合 | 最古参の対応エンジンの一つ |
| Milvus | 可 | マルチベクトル+スパースBM25 | combined search API経由のハイブリッド |
| pgvector+Postgres | 可(拡張が必要) | 手動RRF(上記参照) | 真の語彙一致スコアリングにはts_rank/BM25拡張が必要 |
採用を決める前に、正確なバージョン条件を検証してください。2026年を通じてこれらのエンジンにハイブリッド機能が次々と搭載されており、APIの形はリリースごとに変わります。融合の仕組みを超えたより広い選定判断には、おすすめベクトルデータベース完全まとめを参照してください。
ハイブリッド検索はこの複雑さに見合う価値があるか?
ハイブリッド検索がアーキテクチャとして正しいのは、コーパスに完全一致パターン(SKU、ID、希少用語)と概念的な言い換えクエリの両方がある場合です。コーパスにどちらもない場合(純粋なナラティブコンテンツで、誰も文字列そのものでは検索しない、識別子を持たないもの)、融合の複雑さを追加しても、ほとんど気づかない程度の改善しか得られないかもしれません。
「ナラティブのみ」が実際どういうものか考えてみましょう。企業のブログアーカイブ、散文だらけのランブックが並ぶ社内エンジニアリングWiki、誰もプロダクトIDやチケット番号で検索しないドキュメントサイトです。こうしたコーパスでは、ベクトル検索単独で価値の大部分を得られることが多く、融合ステップはただ2回目の検索パスと、誰かが今度から責任を持つパラメータを増やすだけで、改善幅は誤差に丸められます。これをサポートのチケットシステムやeコマースカタログと比較してください。そこではSKU、注文番号、モデルコードが実際のユーザークエリに絶えず登場します。これが本当のテストです。自社ログから実際のクエリを10件抽出し、言い換えベースの埋め込みモデルが正しく配置できない完全な識別子を含むものがいくつあるか数えてください。ゼロなら、ハイブリッドは飛ばして構いません。1、2件より多いなら、構築してください。
ハイブリッド検索は万能のアップグレードではない。コーパスにSKU、ID、希少用語の検索がなければ、気づきもしない改善のために融合の複雑さを足しているだけかもしれない。
コストは実在しますが、限られています。2回目の検索パス、融合ステップ、そして誰かが今度から責任を持つ重みパラメータ。ここで意図的にレイテンシの数値を引用していません。出回っている数字は、名前もないセットアップで名前もないハードウェアから出てきたもので、あなたの環境では異なるからです。判断の前に、自分のコーパスで測定してください。Hacker Newsの2つのスレッドが、現場の実践者の緊張感をうまく捉えています。"Hybrid Search Is Just the Beginning: Optimizing the R in RAG"と"Better RAG Results with Reciprocal Rank Fusion and Hybrid Search"です。両スレッドとも、コーパスにそもそも解決対象のクエリパターンがあるかを確認せずに、ハイブリッドをカーゴカルト的なベストプラクティスとして採用することに異を唱えています。構築の前に、検索品質を実際にどう測定するかを理解しておく価値があります。NDCGやrecall@kの数値は、ベンチマークデータセットに対してではなく、自分のコーパスに対してのみ意味を持ちます。
私たちの見解です。ユーザー対応のサポート、eコマース、チケット対応のクエリを捌くRAGシステムでは、デフォルトでハイブリッドを選んでください。これらのワークロードは、ほぼ常に識別子と自然言語を混ぜ合わせます。ナラティブのみのコーパス(長文ドキュメント、ナラティブWiki)では、単一手法の検索が残す実際のギャップを測定するまで、ハイブリッドは見送ってください。
著者について
Mert BaturはTechsy.ioの共同創業者で、チームはB2Bクライアント向けにAIエージェント、自動化システム、音声/SDRパイプラインを提供しています。Techsyチームが実際に本番環境で使っているLLMツールスタックについて執筆しています。LinkedInでつながる。
よくある質問
RAGにおけるハイブリッド検索とは?
ハイブリッド検索は、同じクエリに対してBM25(キーワード)とベクトル(意味)の検索を別々のパスとして実行し、2つのランキングリストを融合アルゴリズム(通常はReciprocal Rank Fusion)でマージします。完全一致クエリと言い換えられた概念クエリの両方を捉え、これはどちらの手法単独でも処理できません。
BM25はベクトル検索と同じ?
いいえ。BM25は用語の完全な重複と希少性をスコアリングするスパースな語彙一致検索です。ベクトル検索は埋め込みと類似度計算を使う密な意味検索です。両者は正反対の強みを持つ2つの異なる検索手法であり、ハイブリッド検索はどちらかを置き換えるのではなく統合します。
BM25とベクトル検索はどう統合する?
同じクエリに対して両方の検索手法を独立して実行し、2つのランキング結果リストを融合します。最も一般的なのはReciprocal Rank Fusionで、各リストにわたり1 / (k + rank)を合計します。アルファ重み付きのスコア結合が代替手法ですが、RRFが不要とするコーパスごとのチューニングが必要です。
Reciprocal Rank Fusion(RRF)とは?
RRFはCormack、Clarke、Buettcherの2009年SIGIR論文に由来する融合アルゴリズムで、各文書の1 / (k + rank)を合計して複数のランキングリストを統合します。kは通常60です。生のスコアではなく順位の位置に対して作用するため、検索手法間のスケール不一致があっても安定します。
RRFとアルファ重み付き融合の違いは?
RRFは順位を統合し、コーパスごとのチューニングは不要です。アルファ重み付き融合は、チューニング可能なalphaパラメータで正規化スコアを結合します。信頼度の大きさをよりよく反映できますが、再インデックスやモデル変更などでスコア分布が変わるたびに、継続的な再チューニングが必要です。
ベクトル検索単体ではなくハイブリッド検索を使うべき場面は?
クエリが完全な識別子(SKU、注文番号、エラーコード)と自然言語の概念的な質問を混ぜ合わせる場合、つまりサポート、eコマース、チケットシステムが典型的にそうである場合に、ハイブリッド検索を使ってください。識別子のないナラティブのみのコンテンツでは、追加する融合の複雑さが測定可能な改善として表れない可能性が高いため、見送ってください。
ベクトル検索がSKUやエラーコードの完全一致を取りこぼす理由は?
埋め込みモデルは汎用的な言語パターンから学習しており、「SKU-4471」や「ERR_CONN_RST」のような文字列は、訓練データ内で独立した孤立した概念として登場することがほとんどありません。モデルには、そのexactな文字列を、意味的に関連しているが間違っているトークンより自分自身の近くに配置する強い理由がありません。
ハイブリッド検索にネイティブ対応するベクトルDBは?
2026年時点で、Weaviate、Qdrant、Elasticsearch、OpenSearch、Vespa、Milvusはいずれもネイティブハイブリッド検索を搭載しています。ただし、デフォルトの融合手法は異なります(RRF、アルファ重み付き、正規化)。Postgres+pgvectorでもハイブリッド検索は実行できますが、ネイティブのts_rankは真のBM25ではないため、BM25拡張が必要です。
ハイブリッド検索は追加の複雑さに見合う?
完全一致クエリと概念クエリが混在するコーパスなら、見合います。WANDSベンチマークでは、素のRRFですでに単一手法のどちらかを上回っており(0.7068、BM25の0.6983に対して)、チューニング済みバリアントは7.4%の改善(0.7497)に到達します。識別子のないナラティブのみのコーパスでは、2回目の検索パスとその融合チューニングが、気づきもしない改善を上回るコストになるかもしれません。採用の前に測定してください。
Postgres/pgvectorだけでハイブリッド検索は可能?
可能です。密な類似度担当のpgvectorと、pg_textsearch、VectorChord、ParadeDBのような真のBM25拡張を組み合わせ(Pedro Alonsoのpg_textsearch/pgvectorベンチマークでは、ネイティブts_rank単独はわずか0.07 NDCG@10、ハイブリッドは0.70)、2つのランキングリストをRRFで融合すれば、すべて1つのPostgresインスタンス内で完結します。
どちらの検索手法も、単独で実行すると実際のギャップを残します。BM25は言い換えを取りこぼし、ベクトル検索は完全な識別子を取りこぼします。RRFで両者を融合するのが、メンテナンス負荷の低い両ギャップの塞ぎ方です。これを自前で構築するか、RAG検索を本番環境で運用してきたチームを呼ぶかを比較検討しているなら、RAGアプリケーション構築の完全ガイドが次のステップをカバーしています。Techsyと一緒に構築したい場合は、お問い合わせください。