エンベディング
Embedding
概要(サマリー)
エンベディングとは、単語や文章の意味を、数値の並び(ベクトル)に変換したもののことである。日本語では「埋め込み」「分散表現」とも呼ばれる。
コンピュータは文字そのものの意味を理解できない。そこで、意味を座標として表す。地図の上で、東京と横浜が近く、東京とロンドンが遠いのと同じように、「犬」と「猫」を近い座標に、「犬」と「経理」を遠い座標に置く。こうすると、意味の近さを距離の計算で求められるようになる。
この変換によって、文字が一致しなくても「意味が似ている」文章を探せるようになる。RAGや検索機能の土台になっている技術である。
以下は、あらかじめ計算しておいた意味の近さを、単語を選んで確かめられるデモである。
基準の単語を選ぶと、他の単語との「意味の近さ」が並び替わる。
詳細解説
意味を数値にするとはどういうことか
エンベディングは、1つの単語や文章を数百〜数千個の数値の並びに変換する。たとえば次のようなイメージである。
"犬" → [0.21, -0.85, 0.44, 0.09, ... ] ← 1536個の数値
"猫" → [0.19, -0.81, 0.47, 0.12, ... ] ← 犬とよく似た並び
"サーバー" → [-0.63, 0.28, -0.11, 0.77, ... ] ← 全然違う並び
数値ひとつひとつに人が理解できる意味があるわけではない。「1番目は動物らしさ、2番目は大きさ」といった具合には分かれていない。重要なのは並び全体としての位置関係であり、意味が近いものは近い場所に、遠いものは遠い場所に配置される、という点だけである。
この数値の個数を「次元数」と呼ぶ。1536次元なら、1536個の数値で1つの意味を表している。次元が多いほど細かい違いを表現できるが、そのぶん計算も保存も重くなる。
近さの測り方
2つのベクトルがどれくらい近いかは、計算で求められる。もっともよく使われるのがコサイン類似度で、2つの向きがどれだけ揃っているかを −1〜1 の数値で表す。
1.0に近い: ほぼ同じ意味0あたり: 無関係-1.0に近い: 逆の意味(実務ではあまり出てこない)
上のデモで表示している数値がこれにあたる。「犬」と「猫」が 0.82、「犬」と「サーバー」が 0.06 というように、意味の近さが数値になる。ここが肝心な点で、意味の比較が単なる算数になるため、何万件の文書でも高速に比較できる。
文字の一致で探す検索との違い
従来のキーワード検索は、文字が一致するかどうかで探す。そのため、次のような取りこぼしが起きる。
- 「休みを取りたい」で検索 → 「年次有給休暇の申請について」が見つからない
- 「PCが重い」で検索 → 「パソコンの動作が遅い場合の対処」が見つからない
エンベディングを使った検索なら、文字がまったく違ってもどちらも見つかる。意味の座標が近いからだ。この性質を「セマンティック検索(意味検索)」と呼ぶ。
ただし万能ではない。型番や人名のような、意味ではなく文字そのものが重要なものは逆に苦手である。「XR-2200」と「XR-2100」は意味的にはほぼ同じ座標に来てしまう。実務ではキーワード検索と併用することが多い。
使いどころ
- RAG: 質問に関係する資料を探し出す。もっとも代表的な用途である
- セマンティック検索: サイト内検索で、言い回しが違っても目的の記事にたどり着けるようにする
- 関連記事の表示: 記事同士の近さを計算して、内容の近いものを並べる
- 分類・グループ分け: 問い合わせ内容を自動で仕分ける
- 重複の検出: 表現は違うが同じ内容の投稿を見つける
実際の作り方
エンベディングは自分で計算するものではなく、専用のモデルにテキストを渡して受け取る。多くはAPI経由で使う。
# 文章を渡すと、意味を表す数値の並びが返ってくる
res = client.embeddings.create(
model="text-embedding-3-small",
input="有給休暇はいつから取れますか"
)
vec = res.data[0].embedding # [0.013, -0.027, ... ] 1536個の数値
重要な約束事がひとつある。保存するときと検索するときで、同じモデルを使わなければならない。 モデルが違えば座標系そのものが違うので、比較しても意味のない数値しか出てこない。モデルを新しいものに変えるなら、保存済みの全データを作り直す必要がある。
計算したベクトルはベクトルデータベースに保存して、高速に検索できるようにするのが一般的である。
生成AIのトークンとの違い
トークンと混同されやすいが、役割がまったく違う。
- トークン: 文章を細かく区切った「単位」。「有給休暇」を「有給」「休暇」の2つに分ける、といった分割の話
- エンベディング: その単位や文章全体の「意味」を数値にしたもの
トークンは切り分け、エンベディングは意味の座標化である。トークン数はAPIの料金や入力量の目安に使われるが、エンベディングは検索や分類に使われる。
AIコーディングとの関係
エンベディングをAIに実装してもらうこと自体は難しくない。API呼び出しが数行で済むためだ。ただし、AIが黙って決めてしまいがちな箇所があるので、先に指定しておくとよい。
- どのモデルを使うか(次元数と費用が変わる)
- 何を1件として保存するか(記事単位か、段落単位か)
- どこに保存するか(ベクトルデータベースか、ファイルか)
- 正規化するかどうか(コサイン類似度を使うなら通常は正規化する)
たとえば「Markdownの記事200本を見出し単位で分割し、text-embedding-3-small でベクトル化して保存する処理を書いて。あとから検索できるよう、元の見出しとファイル名も一緒に保存して」と伝えると、実用的なものが出てくる。
生成後に確認したいのは、同じモデルで保存と検索をしているかと、元テキストを一緒に保存しているかである。ベクトルだけ保存して元の文章を捨ててしまうと、検索で見つけても何が見つかったのか分からない、という状態になる。AIが書いたコードで意外とやりがちな抜けである。
よくある勘違い
エンベディングは文章を圧縮したもの?
圧縮ではない。エンベディングから元の文章を復元することはできない。意味の座標だけを取り出したもので、元の言葉は捨てられている。だから検索システムでは、ベクトルとは別に元テキストも必ず保存しておく必要がある。
数値の1つ1つに意味がある?
「3番目の数値は感情の強さ」といった具合に、人が読み取れる意味は割り当てられていない。意味を持つのは並び全体の位置関係だけである。個々の数値を見ても何も分からない。
次元数が多いほど高性能?
次元が多いほど細かい違いを表せるが、保存容量も計算量も増える。用途によっては小さいモデルで十分なことが多く、実際に検索精度を比べてから決めるのがよい。「大きいほうが良い」とは限らない。
エンベディングとトークンは同じもの?
別物である。トークンは文章を区切った単位、エンベディングはその意味を数値化したものだ。「1000トークンの文章を1536次元のベクトルに変換する」というように、両方が同時に登場することもある。
まとめ
- エンベディングは、単語や文章の意味を数値の並び(ベクトル)に変換したものである
- 意味が近いものは近い座標に置かれ、近さをコサイン類似度などの計算で求められる
- 文字が一致しなくても意味で探せるが、型番や固有名詞の完全一致は苦手である
- 保存時と検索時で同じモデルを使う必要がある。モデルを変えるなら全件作り直す
- ベクトルからは元の文章を復元できないので、元テキストも必ず一緒に保存する
情報ソース
より詳しくAIに聞いてみよう
- エンベディングとは何かを、意味を座標にするというたとえで初心者向けに説明してください。
- コサイン類似度がどう計算されるのかを、簡単な例つきで教えてください。
- キーワード検索とベクトル検索の得意・不得意を比較して整理してください。
- エンベディングのモデルを変更するとき、既存データにどんな影響があるか教えてください。
- AIに検索機能のコードを生成してもらうとき、元テキストとベクトルをどう保存させるべきか教えてください。