0Pricing
AI Engineering Academy · レッスン

密検索と疎検索の比較:トレードオフ

密な埋め込みが完全一致するキーワードを見落とす場合と、BM25が意味的な言い換えを見落とす場合を理解し、両者を組み合わせると単独の場合を一貫して上回る理由を学びます。

「密検索と疎検索の比較:トレードオフ」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。

このレッスンの一部はまだ翻訳されておらず、英語で表示されています。

Two Fundamentally Different Retrieval Signals

Modern retrieval systems rely on two distinct signals: dense retrieval encodes meaning into continuous vector spaces, while sparse retrieval counts exact term occurrences. These signals are complementary, not interchangeable. Understanding their individual strengths and weaknesses is the first step toward building a system that uses both effectively.

How Dense Embeddings Work

Dense retrieval maps both the query and each document into a high-dimensional vector using a neural encoder. Similarity is measured by cosine distance or dot product between vectors. Because the encoder was trained on large text corpora, semantically related phrases end up near each other in vector space even if they share no common words — this is the key advantage of dense retrieval.

from openai import OpenAI
import numpy as np

client = OpenAI()

def embed(text: str) -> list[float]:
    resp = client.embeddings.create(
        model='text-embedding-3-small',
        input=text,
    )
    return resp.data[0].embedding

def cosine_similarity(a, b):
    a, b = np.array(a), np.array(b)
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

q = embed('How do I cancel my subscription?')
d = embed('Steps to unsubscribe from the service')
print(cosine_similarity(q, d))  # high similarity despite different words

Where Dense Retrieval Fails

Dense models struggle with rare terms that were underrepresented during encoder training. A query containing a specific product model number like RTX-4090-Ti-OC, a medical drug name, or a proprietary internal identifier will often fail to match the correct document because the encoder has no learned representation for that token sequence. The vector just lands somewhere unhelpful in embedding space.

How Sparse BM25 Retrieval Works

BM25 (Best Matching 25) is a probabilistic ranking function that scores documents based on how often query terms appear in the document, normalized for document length and dampened by term frequency saturation. It produces a sparse score vector — most dimensions are zero because documents contain only a small fraction of the vocabulary.

# BM25 scoring formula (conceptual)
# score(D, Q) = sum over query terms t of:
#   IDF(t) * (tf(t,D) * (k1 + 1)) / (tf(t,D) + k1 * (1 - b + b * |D|/avgdl))

# k1 controls term frequency saturation (typically 1.2-2.0)
# b controls document length normalization (typically 0.75)
# IDF(t) = log((N - df(t) + 0.5) / (df(t) + 0.5))

# N = total documents, df(t) = documents containing term t
# tf(t,D) = frequency of t in document D, |D| = doc length, avgdl = average doc length

BM25 Strengths: Exact Terms and Jargon

BM25 excels at queries involving exact technical terms, product names, error codes, and numeric identifiers that should match precisely. A query for ORA-01017 (an Oracle error code) will rank documents containing that exact string far above documents that merely discuss database authentication in general terms. This is impossible for a dense model that has never seen that specific code.

from rank_bm25 import BM25Okapi

corpus = [
    'Oracle database ORA-01017 invalid username or password logon denied',
    'Database authentication and connection troubleshooting guide',
    'How to resolve login errors in Oracle and MySQL databases',
]

tokenized_corpus = [doc.lower().split() for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)

query = 'ORA-01017 error fix'
scores = bm25.get_scores(query.lower().split())
print(dict(zip(range(len(corpus)), scores)))
# doc 0 scores highest because it contains ORA-01017

Where BM25 Fails: Paraphrases and Synonyms

BM25 is blind to semantic paraphrasing. A document about 'automobile engine repair' will score zero for a query about 'car motor maintenance' because none of the exact words overlap. This vocabulary mismatch problem, sometimes called the lexical gap, means pure keyword search misses huge amounts of relevant content that simply uses different words to express the same idea.

from rank_bm25 import BM25Okapi

corpus = [
    'automobile engine repair and maintenance tips',
    'car motor maintenance guide for beginners',
    'vehicle powertrain service intervals',
]
tokenized = [doc.split() for doc in corpus]
bm25 = BM25Okapi(tokenized)

scores = bm25.get_scores(['car', 'motor', 'maintenance'])
print(scores)
# doc 1 scores high, doc 0 and 2 score lower despite being semantically related

Benchmark Evidence: Hybrid Wins Consistently

Benchmarks on BEIR, MS MARCO, and enterprise Q&A datasets consistently show that hybrid retrieval outperforms either dense or sparse alone by 5-15 percent on NDCG@10. The improvement is largest on datasets with a mix of factual lookups (where BM25 helps) and paraphrase queries (where dense embeddings help). No single retrieval method dominates across all query types.

Query Type Analysis: Which Retriever Wins

You can predict which retriever will perform better by analyzing the query type. Dense retrieval wins on conceptual questions, paraphrases, and broad topic queries. BM25 wins on queries containing proper nouns, version numbers, code snippets, acronyms, and rare technical terms. Hybrid always wins when query type is unknown in advance — which is almost always true in production.

# Query type heuristics
def predict_retriever_advantage(query: str) -> str:
    tokens = query.split()
    has_numbers = any(t[0].isdigit() for t in tokens)
    has_uppercase_acronyms = any(t.isupper() and len(t) > 2 for t in tokens)
    is_short = len(tokens) <= 4

    if has_numbers or has_uppercase_acronyms:
        return 'BM25 likely wins (exact terms)'
    elif is_short:
        return 'Dense likely wins (semantic matching needed)'
    else:
        return 'Hybrid recommended (mixed signals)'

Score Incompatibility Problem

Combining dense and sparse results is non-trivial because their scores are on incompatible scales. Cosine similarity produces values between -1 and 1, while BM25 produces unbounded positive scores that depend on corpus size. You cannot simply add them. The standard solution is to use rank-based fusion rather than score-based fusion — merging ranked lists instead of raw scores.

Practical Decision: When to Use Each

Use dense-only retrieval when your corpus is in a narrow domain with consistent vocabulary and you need semantic generalization across paraphrases. Use BM25-only when queries are primarily lookup-style with exact identifiers and your dataset is small enough that brute-force is feasible. Use hybrid in all production RAG systems where query types vary — the overhead is modest and the recall improvement is significant.

Performance and Infrastructure Trade-offs

Dense retrieval requires GPU-accelerated approximate nearest neighbor search or a vector database, which adds infrastructure cost. BM25 runs entirely on CPU with an inverted index and is extremely fast. Hybrid retrieval requires both infrastructure components plus a fusion step. The added complexity is justified by the recall improvement for most production use cases, but must be weighed against your infrastructure budget.

Quick Check

Test your understanding of dense versus sparse retrieval trade-offs from this lesson.

Lesson Recap

In this lesson you learned: dense retrieval captures semantic meaning but fails on rare exact terms, BM25 sparse retrieval handles exact keywords but misses paraphrases, and hybrid retrieval consistently outperforms either method alone across diverse query types. Their scores are incompatible and must be merged via rank fusion rather than score addition. Next up we implement BM25 keyword search in Python.

よくある質問

「密検索と疎検索の比較:トレードオフ」レッスンは無料ですか?

はい。「密検索と疎検索の比較:トレードオフ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。

「密検索と疎検索の比較:トレードオフ」で何を学びますか?

密な埋め込みが完全一致するキーワードを見落とす場合と、BM25が意味的な言い換えを見落とす場合を理解し、両者を組み合わせると単独の場合を一貫して上回る理由を学びます。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Engineering Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「密検索と疎検索の比較:トレードオフ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Engineering Academyレッスンでコードを書いて実行できますか?

はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 密検索と疎検索の比較:トレードオフ
  2. BM25キーワード検索を実装する
  3. スコア統合のための逆順位融合
  4. Pineconeとpgvectorでハイブリッド検索
← AI Engineering Academyに戻る