この回のゴール
- 文書コレクションを 埋め込みベクトル に変換して保存する
- FAISS で大規模ベクトル検索を実装する
- Top-k 検索 で関連文書を自動で取り出す
- 検索結果の 質 を自分で評価できるようになる
1. 全体像 — インデックスと検索
【事前準備: インデックス構築】
┌─ FAISS index ─┐
┌────────┐ ┌──────────┐ │ [v1][v2]... │
│ 文書 1 │──▶│ 埋め込み │───────▶│ [v10000] │
│ 文書 2 │──▶│ モデル │ │ │
│ ... │ └──────────┘ └─────┬─────────┘
└────────┘ │
│ O(log N) or O(√N)
【検索時】 │ で高速検索
┌──────┐ ┌──────────┐ │
│ 質問 │──▶│ 埋め込み │──コサイン類似度─▶ top-k 文書
└──────┘ └──────────┘
2. 埋め込みモデルの選び方
2026 年時点でよく使われるモデル:
| モデル | 提供 | 次元 | 用途 |
|---|---|---|---|
paraphrase-multilingual-MiniLM-L12-v2 |
HF | 384 | 軽量・多言語 |
text-embedding-3-small |
OpenAI | 1536 | バランス |
text-embedding-3-large |
OpenAI | 3072 | 高精度 |
voyage-3 |
Voyage AI | 1024 | 2024 年時点の最強クラス |
注意: Claude API 自体は埋め込み専用 API を提供していません(2026 年現在)。RAG の検索部分は別途用意します。
3. FAISS — なぜベクトル DB が必要か
ナイーブ実装(小規模向け)
similarities = query_vec @ corpus_vecs.T # [1, N]
top_k = np.argsort(-similarities)[0, :k]
- 文書数 N が小さい(〜1 万)なら問題なし
- 大規模では遅くなる
FAISS の役割
- 大量のベクトルを高速検索(100 万件で数ミリ秒)
- 近似最近傍探索 (IVF, HNSW) で $O(\sqrt{N})$ や $O(\log N)$
- 正確検索(
IndexFlatIP,IndexFlatL2)も可能
import faiss
index = faiss.IndexFlatIP(dim=384) # 内積で検索
index.add(corpus_vecs) # corpus_vecs は [N, 384]
D, I = index.search(query_vec, k=3) # Top-3
4. メタデータと組み合わせる
ベクトル DB は通常「ベクトル → ID」しか返しません。メタデータ は別途管理:
docs = [
{"id": 0, "title": "有給", "body": "...", "category": "HR"},
{"id": 1, "title": "リモート", "body": "...", "category": "HR"},
]
vectors = embed_model.encode([d["body"] for d in docs])
# 検索後
_, indices = index.search(query_vec, k=3)
results = [docs[i] for i in indices[0]]
5. 正規化とコサイン類似度
vectors = model.encode(texts, normalize_embeddings=True) # L2 正規化
正規化すれば、内積 = コサイン類似度 になります:
$$ \cos(\vec{a}, \vec{b}) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|} \xrightarrow{\text{正規化後}} \vec{a} \cdot \vec{b} $$
だから FAISS の IndexFlatIP(内積)が cos 類似度検索になります。
6. 検索品質の評価
- Recall@k: 本当に関連する文書が、top-k に含まれている割合
- Precision@k: top-k のうち関連文書の割合
- MRR (Mean Reciprocal Rank): 関連文書が何番目に出たか
7. Top-k の選び方
| k | 効果 | 欠点 |
|---|---|---|
| k=1 | トークン少、ピンポイント | 関連文書を逃すと即アウト |
| k=3〜5 | 最もバランス(実務) | 適度なトークン量 |
| k=10+ | 取りこぼしは少ない | トークン肥大、ノイズ混入 |
実務の定番は k=3〜5 です。
まとめ
- ベクトルストア(FAISS など)で 大量文書の高速類似検索 を実現
- 埋め込みベクトルを L2 正規化 すれば内積 = コサイン類似度
- メタデータは別管理(出典・更新日・カテゴリなど)
- 検索品質は Recall@k / Precision@k / MRR で評価
- 実務の定番は Top-3〜5
この回の限界(次への動機)
長文を「1 つの文書」としてそのまま埋め込むと、意味が平均化されて検索ヒットしない。長文をどう区切る? 👉 次回「チャンク分割戦略」で、固定長・段落・再帰・セマンティックの 4 戦略を比較します。
参考文献
- FAISS 公式ドキュメント
- sentence-transformers
- Reimers & Gurevych (2019) Sentence-BERT