コンテンツにスキップ
LinkedInX

埋め込みとベクトル表現

約10分

対象読者: RAGの仕組みをより深く理解したい方、埋め込みモデルの選び方を知りたい方
前提知識: RAGとは の基本フローを把握していること

埋め込み(Embedding)とは、テキストや単語の意味を数値のリスト(ベクトル)として表現する技術です。RAGでは、文書も質問も同じベクトル空間に変換することで、「意味的に近い文書」を数値計算で高速に見つけられるようにします。

埋め込みを直感的に理解するために、「意味の座標」という比喩が役立ちます。

地図上で「東京」と「大阪」の座標を調べると、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に保存します。ユーザーが質問するとき、その質問文もベクトルに変換し、保存済みのベクトルとの距離を計算して、最も近い(意味的に似ている)チャンクを取り出します。

重要: 文書の登録時と質問時で、必ず同じ埋め込みモデルを使う必要があります。異なるモデルで作ったベクトルは、互いに比較できません。

ベクトル同士の「近さ」を測る最もよく使われる方法がコサイン類似度(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は、類似度計算を内部で行い、スコアの高い順に結果を返してくれます。コサイン類似度の計算式を覚える必要はありませんが、「スコアが高い = 意味的に近い」という感覚を持っておくことが重要です。

OpenAI が提供する text-embedding-3-smalltext-embedding-3-large は、日本語を含む多言語に対応した埋め込みモデルです。[1]

  • text-embedding-3-small: 1536次元、低コスト、多言語対応、RAGのプロトタイプに最適
  • text-embedding-3-large: 3072次元、精度重視の本番環境向け

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-e5intfloat/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)
モデル提供元次元数多言語対応日本語精度コスト向いている用途
text-embedding-3-smallOpenAI1536あり良好プロトタイプ・本番小規模
text-embedding-3-largeOpenAI3072あり良好精度重視の本番環境
embed-multilingual-v3.0Cohere1024優れる優秀多言語・日本語重視
multilingual-e5-largeMicrosoft/OSS1024優れる優秀無料(自前ホスト)コスト最適化・プライベート環境
embed-multilingual-light-v3.0Cohere384あり良好低レイテンシ重視
  • 日本語文書が中心: embed-multilingual-v3.0 または multilingual-e5-large が有力
  • 英語中心またはグローバル: text-embedding-3-small で十分なことが多い
  • 専門用語が多いドメイン: 汎用モデルで精度が出ない場合、ドメイン特化モデルや fine-tune を検討する
  • API コストを最小にしたい: text-embedding-3-small(OpenAI の中で最安)または multilingual-e5 の自前ホスト
  • 外部 API に依存したくない(オンプレ・プライベートクラウド): multilingual-e5 などのオープンソースモデル
  • とにかく早く試したい: text-embedding-3-small で始め、精度が足りなければ切り替える

MTEB(Massive Text Embedding Benchmark)は、埋め込みモデルの精度を言語・タスク別に評価する公開ベンチマークです。モデルを選ぶ際は、日本語タスクでのランキングを参照することで、客観的な根拠として使えます。[3]

登録時と検索時でモデルが異なる

Section titled “登録時と検索時でモデルが異なる”

文書登録時に text-embedding-3-small を使い、検索時に text-embedding-3-large を使うと、ベクトル空間が異なるため正しい類似度が計算できません。必ず同じモデルを使い続けてください。

モデルを変更した場合は、すべての文書をベクトル化しなおす(再インデックス)が必要です。

コサイン類似度を使う場合、ベクトルの長さ(ノルム)を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.0multilingual-e5-large が精度面で有利
  • プロトタイプは text-embedding-3-small から始め、精度・コストを見て切り替えるのが実務的なアプローチ
  • 登録時と検索時で必ず同じモデルを使うことが、最も重要なルール

Q: 埋め込みの数学(線形代数)を理解しないと使えませんか?

A: 実務での利用において、数式を理解する必要はありません。「テキストを数値のリストに変換し、数値同士の距離で意味の近さを測る」という概念を把握していれば、ライブラリやAPIを通じて利用できます。精度チューニングの段階では、ベクトルの次元数や正規化の概念が役立ちますが、数式の導出は不要です。

Q: 埋め込みの品質はRAGの結果にどう影響しますか?

A: 埋め込みの品質は、RAGの検索精度に直結します。埋め込みモデルが「この質問とこの文書は意味的に近い」と正しく判断できなければ、いくらリランカーやプロンプトを工夫しても、関連文書をそもそも取得できません。特に日本語の専門用語・固有名詞を含む文書では、多言語対応モデルの選定が重要です。

Q: 埋め込みモデルは更新されることがありますか?

A: あります。OpenAI は text-embedding-3-smalltext-embedding-3-large を提供しています。[1] モデルを変更した場合、以前のベクトルとの互換性はないため、全文書の再インデックスが必要です。本番環境ではモデルバージョンを固定し、変更時の影響範囲を計画しておくことが重要です。

Q: チャンクサイズと埋め込みモデルの関係は?

A: 埋め込みモデルには入力長の上限があります。text-embedding-3-small は最大8192トークンまで入力できます。[1] 上限を超えると末尾が切り捨てられるため、チャンクサイズはモデルの上限内に収める必要があります。

  1. OpenAI Embeddings — Official Documentation
  2. Cohere Embed — Official Documentation
  3. MTEB Leaderboard(埋め込みモデルのベンチマーク)
  4. multilingual-e5 — Hugging Face
クイズ