埋め込み検索はTF-IDFより強い?日本語RAG 20問をEmbeddingGemmaで実測

実験日: 2026-08-24
環境: RTX 5070 Ti 16GB / WSL2 / Ollama 0.32.15 / EmbeddingGemma / Qwen 3 8B

RAGを作るとき、「TF-IDFは古い、embeddingなら意味で検索できる」と考えがちです。では、日本語と英数字が混ざった実験記事から正確な測定値を探す場合、本当にembeddingが勝つでしょうか。

embeddingの方が新しい手法でも、この検索条件でTF-IDFより強いとは限りません。

前回と同じ13記事、198 heading chunk、20問を固定し、character 2~4 gram TF-IDFと768次元のEmbeddingGemmaを比較しました。結果はTF-IDFがsource hit@3で20/20、embeddingは19/20。RAG回答はTF-IDF 19/20、embedding 16/20でした。

EmbeddingGemmaが弱いmodelという意味ではありません。今回のqueryはbatch 819211,244 tokensのようなexact identifier・数値検索が多く、lexical matchが強いtaskです。「意味が近いchunk」と「答えの数値が書かれたchunk」は同じとは限りません。

今回はTF-IDFが勝った

metric TF-IDF EmbeddingGemma
期待source hit@1 17/20 17/20
期待source hit@3 20/20 19/20
source-level MRR 0.925 0.9125
平均query時間 1.03 ms 496.2 ms
index作成 0.189秒 12.45秒
同じQwen 3 8BのRAG正解 19/20 16/20
TF-IDFとEmbeddingGemmaの日本語ローカルRAG検索精度、query latency、回答正解数の比較
TF-IDFとEmbeddingGemmaの日本語ローカルRAG検索精度、query latency、回答正解数の比較

この結果からの実務的な判断です。

  • model名、数値、error code、parameter名が重要ならlexical baselineを必ず測る
  • 自然な言い換えや概念検索はembeddingが有利な可能性がある
  • どちらか一方と決めず、scoreを融合するhybrid検索を候補にする
  • source fileが当たっただけで成功とせず、answerを支持するchunkを確認する
  • embedding APIの計算costをlatency budgetへ含める

前回と同じ評価集合を使う

比較で最も大切なのは、retriever以外の条件を変えないことです。

documents       13
heading chunks 198
questions       20
top-k            3
generator       Qwen 3 8B Q4
temperature      0
seed            42

質問は、このPCで測った未知の数値です。

12BモデルのGPU peak使用量は何GiBだった?
Digitsの小型NNではCPUとCUDAのどちらが何倍速かった?
augmentationありDataLoaderで0 workerのthroughputはいくつ?

TF-IDF用とembedding用でquestionやchunk sizeを変えると、公平な比較になりません。今回は前回のPython moduleからARTICLE_NAMESQUESTIONS、chunking関数、採点関数をimportしました。

embeddingは文章をvectorへ変える

embedding modelは、文章を固定長の実数vectorへ写像します。今回、Ollamaへ次のrequestを送りました。

{
  "model": "embeddinggemma",
  "input": ["検索対象の文章"],
  "truncate": true
}

/api/embedのresponseを実測すると、1入力につき768個のfloatが返りました。

embedding shape = (number of chunks, 768)

corpus 198 chunkを16件ずつ13 batchで変換しました。downloadしたmodel payloadは621 MBです。ここでの621 MBはpull時の表示であり、runtime VRAMやparameter数ではありません。

cosine similarityを計算する

document vectorとquery vectorをL2 normalizeします。

arr /= np.maximum(
    np.linalg.norm(arr, axis=1, keepdims=True),
    1e-12,
)

単位vector同士なら、dot productがcosine similarityです。

scores = document_embeddings @ query_embedding
top_idx = np.argsort(scores)[::-1][:3]

cosine similarityはvectorの向きを比べ、長さの影響を除きます。値が大きいほどembedding空間で近いと見なします。ただし「近い」が何を意味するかはmodelのtraining objectiveに依存します。

TF-IDFは文字列の一致を数える

baselineは日本語tokenizerなしのcharacter n-gramです。

TfidfVectorizer(
    analyzer="char_wb",
    ngram_range=(2, 4),
    sublinear_tf=True,
)

例えばqueryにCUDA peak allocatedがあれば、その連続文字を含むchunkが強くなります。意味の言い換えには弱い反面、model名、version、数値、関数名をそのまま探すtaskでは強力です。

実装と計算の性質も違います。

方式 表現 index
TF-IDF 語彙数次元のsparse vector corpus統計から計算
embedding 768次元dense vector neural modelで推論

198 chunk程度なら両方ともmemory上に置けます。大規模化するとTF-IDFはinverted index、embeddingはapproximate nearest neighbor indexが一般的です。

retrieval結果はほぼ同点、TF-IDFがわずかに上

期待source fileが最初に現れる順位を測りました。

                 hit@1   hit@3   MRR
TF-IDF           17/20   20/20   0.9250
EmbeddingGemma   17/20   19/20   0.9125

MRRはMean Reciprocal Rankです。正解sourceがrank 1なら1、rank 2なら1/2、rank 4なら1/4を与え、20問で平均します。上位へ来るほど高くなります。

差は20問で1件なので、一般的な優劣を断定する規模ではありません。しかし「embeddingへ変えれば自動的に改善する」という仮説は、このtaskでは支持されませんでした。

embeddingが改善した質問

次の質問ではTF-IDFの期待sourceがrank 2、embeddingはrank 1でした。

12BモデルのGPU peak使用量は何GiBだった?

TF-IDF top 1はQ4/Q8のquantization記事でした。GPUpeakGiBが強く一致したためです。EmbeddingGemmaは4B・8B・12B比較記事を1位へ上げました。

RAG回答も正解です。

12BモデルのGPU peak使用量は約9.7GiBだった。
[article_ollama_4b_8b_12b.md#4]

語の重なりだけでなく「12B modelのresource measurement」という意味を捉えた可能性があります。ただし1例から内部表現を断定はできません。

embeddingが落とした質問

次の質問では、TF-IDFの期待sourceはrank 1、embeddingはrank 4でした。

Digitsの小型NNではCPUとCUDAのどちらが何倍速かった?

embedding top 3にはFashionMNIST CNNのCPU/GPU比較とNumPy NN記事が入り、期待したPyTorch Digits記事は外れました。意味的にはどれも「small neural network、CPU/GPU、speed comparison」に近い文書です。

幸いFashionMNIST記事の比較tableにDigitsの4.38倍が再掲されていたため、answer自体は正解しました。

CPUはCUDAに対して約4.38倍速かった。
[article_fashion_cnn_cpu_gpu.md#7]

expected source citationは不合格ですが、別sourceが同じ測定値を支持しています。これはevaluation設計上の重要点です。正解sourceを1ファイルに限定すると、重複した正しい根拠を誤って罰する場合があります。

source hitでもevidence chunkを外す

Embedding RAGが不正解だった4問のうち、代表例です。

質問:
code生成で4Bの修正後合格数はいくつ?

retrieval:
article_llm_codegen_pytest.md#2
article_llm_codegen_pytest.md#8
article_llm_codegen_pytest.md#12

回答:
資料内に記載なし

source fileはrank 1です。しかし7/10がある#1を選べませんでした。source-level hit@1は成功でも、evidence retrievalは失敗です。

DataLoader質問も同様でした。

augmentationありDataLoaderで0 workerのthroughputはいくつ?

期待sourceはrank 2に入りましたが、上位chunkには8,830 images/sがありません。LLMは資料内に記載なしを返しました。

RAGをdebugするときはdocument filenameだけでなく、top-kの本文を保存しなければ原因を特定できません。

回答正解は19/20対16/20

retrievalした上位3 chunkを同じQwen 3 8Bへ渡しました。

TF-IDF RAG       19 / 20
Embedding RAG    16 / 20

source hit@3の差は1問ですが、answer正解は3問差です。source fileの中でどのheadingを取ったかが効いたためです。

Embedding RAGの期待source引用は18/20、平均generation時間は0.640秒でした。前回TF-IDF RAGは0.376秒でしたが、promptに入ったchunkの文字量が同じとは限らないため、generator speedの厳密な比較にはしません。

query latencyは1.0 ms対496 ms

このPCでの平均値です。

TF-IDF transform + sparse score   1.03 ms
Ollama EmbeddingGemma API       496.2 ms

embeddingは約480倍ですが、実装経路が公平ではありません。TF-IDFは同じPython process内、embeddingはHTTPでOllamaへrequestし、neural modelを実行しています。cold/warm状態、batching、GPU schedulingも含みます。

したがって「embeddingは常に480倍遅い」と一般化できません。ここから言えるのは、この単発query API構成では約0.5秒をlatency budgetへ追加した、ということです。

index作成もTF-IDF 0.189秒、embedding 12.45秒でした。corpus embeddingは文書更新時に一度計算してcacheできます。query embeddingはrequestごとに必要です。

hybrid検索で両方式を補完する

exact tokenに強いTF-IDFと言い換えに強いembeddingは補完関係にあります。

単純なrank fusionなら、score scaleを直接混ぜず順位を使えます。

RRF(d) = Σ 1 / (k + rank_retriever(d))

Reciprocal Rank Fusionは、各retrieverで上位のdocumentへ加点します。kは上位1件が過度に強くなるのを和らげるconstantです。

実験順序としては次が堅実です。

TF-IDF baseline
    ↓
embedding単体
    ↓
hybrid / RRF
    ↓
reranker
    ↓
answer + citation評価

いきなり複雑な構成へ進むと、どの追加が改善したか分かりません。

この比較の限界

20問は小さく、exact numeric lookupに偏っています。自然なparaphrase question、長文要約、概念横断検索では結果が変わる可能性があります。

また、EmbeddingGemmaのprompt prefix、chunk長、title付与、query expansionを調整していません。defaultのOllama modelをそのまま使うbaselineです。model固有の推奨形式を適用すると改善する余地があります。

評価はsource-file levelです。同じfactが複数記事へ再掲された場合、期待source以外でも正しい根拠になりえます。より厳密に評価するなら、answer span単位のground truthが必要です。

再現手順

ollama pull embeddinggemma

cd /mnt/c/Users/user/Documents/Codex/2026-08-24/ko/work/ai_lab
/opt/ai-lab/venv/bin/python -u experiment_embedding_search.py
/opt/ai-lab/venv/bin/python plot_embedding_search.py

artifactです。

work/ai_lab/experiment_embedding_search.py
work/ai_lab/results/embedding_search.json
work/ai_lab/plot_embedding_search.py
work/figures/tfidf-vs-embeddinggemma.png

JSONには両retrieverのrankとtop 3 ID、embedding側のchunk全文とscore、query time、Qwen回答、正誤、citationを保存しました。

検索方式の判断基準

retrieverを選ぶ前に、質問の性質を見ます。

質問の性質 最初に試すもの
型番、数値、error code、固有名詞 TF-IDF / BM25
言い換え、概念、自然文の意味 embedding
両方が混在 hybrid
候補は取れるが順序が悪い reranker

このPCのexact measurement検索では、621 MBのembedding modelを追加してもTF-IDFを超えませんでした。これは失敗ではなく、baselineを置いたから得られた判断です。

「新しい手法だから採用」ではなく、固定question set、retrieval metric、downstream answer accuracy、latencyで選ぶ。それが研究としてもsystem開発としても、再現可能なRAG改善になります。

関連記事