埋め込み(Embedding)とは、テキストや単語の意味を数値のリスト(ベクトル)として表現する技術です。RAGでは、文書も質問も同じベクトル空間に変換することで、「意味的に近い文書」を数値計算で高速に見つけられるようにします。
埋め込みとは何か
Section titled “埋め込みとは何か”埋め込みを直感的に理解するために、「意味の座標」という比喩が役立ちます。
地図上で「東京」と「大阪」の座標を調べると、2つの距離を数値で計算できます。埋め込みでは、テキストに対して同じことを高次元の空間で行います。たとえば「猫」と「犬」は意味的に近いため、ベクトル空間での距離も近くなります。「猫」と「宇宙船」は意味的に遠いため、距離も大きくなります。
実際の埋め込みベクトルは、数百〜数千個の数値で構成されます。たとえば OpenAI の text-embedding-3-small は1536次元、つまり1536個の数値で1つのテキストを表します。[1]
# Python 3.11以上
# pip install openai
from openai import OpenAI
client = OpenAI() # OPENAI_API_KEY 環境変数を使用
response = client.embeddings.create(
model="text-embedding-3-small",
input="RAGとは検索拡張生成のことです"
)
vector = response.data[0].embedding
print(f"次元数: {len(vector)}") # 出力例: 次元数: 1536
print(f"最初の5要素: {vector[:5]}") # 出力例: [-0.021, 0.034, ...]テキストからベクトルへの変換フロー
Section titled “テキストからベクトルへの変換フロー”RAGでは、文書の登録時と質問の検索時の両方で埋め込みが使われます。
graph TD
A["テキスト(文章・質問)"] --> B["埋め込みモデル"]
B --> C["ベクトル(数値のリスト)"]
C --> D{"用途"}
D -->|文書登録時| E["ベクトルDBに保存"]
D -->|質問検索時| F["類似度計算"]
F --> G["近いベクトルを取得"]
G --> H["関連文書をLLMへ渡す"]文書を登録するとき、各チャンク(分割された文書片)をベクトルに変換してベクトルDBに保存します。ユーザーが質問するとき、その質問文もベクトルに変換し、保存済みのベクトルとの距離を計算して、最も近い(意味的に似ている)チャンクを取り出します。
重要: 文書の登録時と質問時で、必ず同じ埋め込みモデルを使う必要があります。異なるモデルで作ったベクトルは、互いに比較できません。
コサイン類似度の仕組み
Section titled “コサイン類似度の仕組み”ベクトル同士の「近さ」を測る最もよく使われる方法がコサイン類似度(Cosine Similarity)です。
コサイン類似度は、2つのベクトルの「向き」の近さを -1 から 1 の数値で表します。
- 1.0 に近い: 非常に似ている(例:「犬の飼い方」と「犬を飼う方法」)
- 0 に近い: 関係が薄い(例:「犬の飼い方」と「宇宙開発の歴史」)
- -1 に近い: 反対の意味(実際のテキストではほとんど発生しない)
import numpy as np
def cosine_similarity(vec_a, vec_b):
"""2つのベクトルのコサイン類似度を計算する"""
dot_product = np.dot(vec_a, vec_b)
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
return dot_product / (norm_a * norm_b)
# 例:類似度計算
similarity = cosine_similarity(vector_doc, vector_query)
print(f"類似度: {similarity:.4f}") # 出力例: 0.8732ほとんどのベクトルDBは、類似度計算を内部で行い、スコアの高い順に結果を返してくれます。コサイン類似度の計算式を覚える必要はありませんが、「スコアが高い = 意味的に近い」という感覚を持っておくことが重要です。
代表的な埋め込みモデル
Section titled “代表的な埋め込みモデル”OpenAI Embeddings
Section titled “OpenAI Embeddings”OpenAI が提供する text-embedding-3-small と text-embedding-3-large は、日本語を含む多言語に対応した埋め込みモデルです。[1]
text-embedding-3-small: 1536次元、低コスト、多言語対応、RAGのプロトタイプに最適text-embedding-3-large: 3072次元、精度重視の本番環境向け
Cohere Embed
Section titled “Cohere Embed”Cohere が提供する embed-multilingual-v3.0 は、多言語検索向けの埋め込みモデルで、input_type によって文書用・検索クエリ用などの入力種別を指定できます。[2]
- 1024次元
- 100以上の言語をサポート
input_typeパラメータで文書用・検索クエリ用を区別できる点が特徴
# pip install cohere
import cohere
co = cohere.Client() # COHERE_API_KEY 環境変数を使用
# 文書登録時: input_type="search_document"
doc_response = co.embed(
texts=["RAGとは検索拡張生成のことです"],
model="embed-multilingual-v3.0",
input_type="search_document" # 文書登録用
)
# 質問時: input_type="search_query"
query_response = co.embed(
texts=["RAGの仕組みを教えて"],
model="embed-multilingual-v3.0",
input_type="search_query" # 検索クエリ用
)multilingual-e5(多言語対応オープンソース)
Section titled “multilingual-e5(多言語対応オープンソース)”multilingual-e5(intfloat/multilingual-e5-large)は、xlm-roberta-large を初期値として多言語データで継続学習されたオープンソースの多言語埋め込みモデルです。100言語をサポートするため、日本語を含む多言語検索を自前でホスティングしたい場合の選択肢になります。[4]
# pip install sentence-transformers
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/multilingual-e5-large")
# Passage(文書)は "passage: " プレフィックスが必要
docs = ["passage: RAGとは検索拡張生成のことです"]
# Query(質問)は "query: " プレフィックスが必要
queries = ["query: RAGの仕組みを教えて"]
doc_vectors = model.encode(docs)
query_vectors = model.encode(queries)埋め込みモデル比較
Section titled “埋め込みモデル比較”| モデル | 提供元 | 次元数 | 多言語対応 | 日本語精度 | コスト | 向いている用途 |
|---|---|---|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | あり | 良好 | 低 | プロトタイプ・本番小規模 |
| text-embedding-3-large | OpenAI | 3072 | あり | 良好 | 中 | 精度重視の本番環境 |
| embed-multilingual-v3.0 | Cohere | 1024 | 優れる | 優秀 | 中 | 多言語・日本語重視 |
| multilingual-e5-large | Microsoft/OSS | 1024 | 優れる | 優秀 | 無料(自前ホスト) | コスト最適化・プライベート環境 |
| embed-multilingual-light-v3.0 | Cohere | 384 | あり | 良好 | 低 | 低レイテンシ重視 |
用途に合ったモデルの選び方
Section titled “用途に合ったモデルの選び方”言語・ドメインで選ぶ
Section titled “言語・ドメインで選ぶ”- 日本語文書が中心:
embed-multilingual-v3.0またはmultilingual-e5-largeが有力 - 英語中心またはグローバル:
text-embedding-3-smallで十分なことが多い - 専門用語が多いドメイン: 汎用モデルで精度が出ない場合、ドメイン特化モデルや fine-tune を検討する
コスト・運用で選ぶ
Section titled “コスト・運用で選ぶ”- API コストを最小にしたい:
text-embedding-3-small(OpenAI の中で最安)またはmultilingual-e5の自前ホスト - 外部 API に依存したくない(オンプレ・プライベートクラウド):
multilingual-e5などのオープンソースモデル - とにかく早く試したい:
text-embedding-3-smallで始め、精度が足りなければ切り替える
MTEB(Massive Text Embedding Benchmark)は、埋め込みモデルの精度を言語・タスク別に評価する公開ベンチマークです。モデルを選ぶ際は、日本語タスクでのランキングを参照することで、客観的な根拠として使えます。[3]
よくある失敗パターン
Section titled “よくある失敗パターン”登録時と検索時でモデルが異なる
Section titled “登録時と検索時でモデルが異なる”文書登録時に text-embedding-3-small を使い、検索時に text-embedding-3-large を使うと、ベクトル空間が異なるため正しい類似度が計算できません。必ず同じモデルを使い続けてください。
モデルを変更した場合は、すべての文書をベクトル化しなおす(再インデックス)が必要です。
ベクトルの正規化を忘れる
Section titled “ベクトルの正規化を忘れる”コサイン類似度を使う場合、ベクトルの長さ(ノルム)を1に揃える正規化が必要です。多くのベクトルDBは自動で処理しますが、独自に計算する場合は注意が必要です。
import numpy as np
def normalize(vector):
"""ベクトルを正規化する(長さを1にする)"""
norm = np.linalg.norm(vector)
if norm == 0:
return vector
return vector / norm
normalized_vector = normalize(np.array(vector))チャンクと質問のプレフィックス不一致(E5系モデル)
Section titled “チャンクと質問のプレフィックス不一致(E5系モデル)”multilingual-e5 などのE5系モデルは、文書に "passage: " を、質問に "query: " プレフィックスを付ける必要があります。モデルカードでも、プレフィックスを付けない場合は性能低下が起きると説明されています。[4]
- 埋め込みは、テキストを「意味の座標」であるベクトルに変換する技術
- RAGでは文書も質問も同じモデルで変換し、コサイン類似度で意味的に近い文書を探す
- 日本語中心の用途では
embed-multilingual-v3.0やmultilingual-e5-largeが精度面で有利 - プロトタイプは
text-embedding-3-smallから始め、精度・コストを見て切り替えるのが実務的なアプローチ - 登録時と検索時で必ず同じモデルを使うことが、最も重要なルール
よくある質問
Section titled “よくある質問”Q: 埋め込みの数学(線形代数)を理解しないと使えませんか?
A: 実務での利用において、数式を理解する必要はありません。「テキストを数値のリストに変換し、数値同士の距離で意味の近さを測る」という概念を把握していれば、ライブラリやAPIを通じて利用できます。精度チューニングの段階では、ベクトルの次元数や正規化の概念が役立ちますが、数式の導出は不要です。
Q: 埋め込みの品質はRAGの結果にどう影響しますか?
A: 埋め込みの品質は、RAGの検索精度に直結します。埋め込みモデルが「この質問とこの文書は意味的に近い」と正しく判断できなければ、いくらリランカーやプロンプトを工夫しても、関連文書をそもそも取得できません。特に日本語の専門用語・固有名詞を含む文書では、多言語対応モデルの選定が重要です。
Q: 埋め込みモデルは更新されることがありますか?
A: あります。OpenAI は text-embedding-3-small と text-embedding-3-large を提供しています。[1] モデルを変更した場合、以前のベクトルとの互換性はないため、全文書の再インデックスが必要です。本番環境ではモデルバージョンを固定し、変更時の影響範囲を計画しておくことが重要です。
Q: チャンクサイズと埋め込みモデルの関係は?
A: 埋め込みモデルには入力長の上限があります。text-embedding-3-small は最大8192トークンまで入力できます。[1] 上限を超えると末尾が切り捨てられるため、チャンクサイズはモデルの上限内に収める必要があります。