1,536次元の「意味の地図」を描くための数理、Embedding API の内部で起きていること

に公開

はじめに

セマンティック検索を実装し、僕のメモたちが「意味」で繋がり始めたとき、一つの大きな疑問が残った。

https://zenn.dev/sal9000/articles/6c97ed2ba26250

テキストを OpenAI の API に投げ、返ってきた 1,536 個の数値の束。これらは一体、どのような処理を経て、僕たちの曖昧な言葉を座標へと変換しているのだろうか。

「AI が文脈を理解している」という言葉で片付けるのは、この面白い仕組みを理解する好奇心を満たす上でもったいない。
今回は、Transformer 内部で実行されている行列演算のプロセスを、その数理的な背景とともに理解したい。

1. 埋め込み行列による初期配置

最初のステップは、離散的な記号である「単語(トークン)」を、連続的な数値の世界へ投影することだ。

モデルの内部には、語彙数 V(100,256件 [1])と次元数 d(1,536)を持つ巨大な重み行列 W_{emb} \in \mathbb{R}^{V \times d} が鎮座している。
「バナナ」という単語が入力されると、AI はその ID w_i を使って、行列の特定の行を抜き出す。[2]

\mathbf{x}_i = \text{one-hot}(w_i) \cdot W_{emb}

これは単に行列積だが、意味合いとしては「巨大な辞書(Lookup Table)から初期ベクトルを引く」作業に他ならない。

しかし、この段階ではまだ文脈は存在しない。
ひらがなの日本語で言うならば、「はし(箸)」も「はし(橋)」も、この時点では全く同じ 静的な初期座標 に置かれたままだ。ここから文脈という色を染み込ませるプロセスが始まる。

2. Self-Attention による文脈の織り込み

Embedding が「文脈」を理解する仕組みは、Self-Attention(自己注意機構) にある。
各トークンは、3つの役割(Query, Key, Value)を持って他のトークンと「対話」し、自らの位置を微調整する。

各トークンベクトル \mathbf{x}_i に対し、線形変換行列 W_Q, W_K, W_V を掛け、以下の3つを生成する。

\mathbf{q}_i = \mathbf{x}_i W_Q, \quad \mathbf{k}_i = \mathbf{x}_i W_K, \quad \mathbf{v}_i = \mathbf{x}_i W_V

ここで、「重い荷物を持って、はしを渡る」という文を考えてみる。
「はし」トークンの Q は、周囲にある「渡る」トークンの K と強く反応(内積計算)する。

\alpha_{ij} = \frac{\exp(\mathbf{q}_i \cdot \mathbf{k}_j / \sqrt{d_k})}{\sum_{k} \exp(\mathbf{q}_i \cdot \mathbf{k}_k / \sqrt{d_k})}

この数式は Scaled Dot-Product Attention と呼ばれる。一言で言えば、「文章内の全単語の中から、誰の意見をどれくらい重視すべきか」という配分率を決定するための演算だ。

  • \mathbf{q}_i \cdot \mathbf{k}_j (内積): 「はし」の問い(Query)と、周囲の単語(Key)の相性を測る。高次元空間でベクトルの向きが近いほど大きな値となる。
  • / \sqrt{d_k} (スケーリング): 1,536次元という巨大な次元数ゆえに、内積の値が大きくなりすぎて計算が不安定になるのを防ぐため、次元数の平方根で割って数値をマイルドに調整する。
  • Softmax演算 (Exp / Sum): 各単語への注目度の合計がちょうど 1 (100%) になるように正規化する。

この計算の結果、たとえば「はし」という言葉に対し、周囲の「渡る」という単語に 60% の注目を向ける、といった 「情報の配分率」 が決定される。

この Attention Score \alpha_{ij} に基づき、周囲の「意味(Value)」を自分に足し合わせる。

\mathbf{x}'_i = \sum_{j} \alpha_{ij} \mathbf{v}_j

「渡る」の持つ「建造物」という成分を 60% の濃度で吸収した結果、「はし」は 1,536 次元の宇宙の中で、食器のエリアから 建造物のエリアへと幾何学的に移動 するのだ。[3]

3. 情報の凝縮:Pooling と重ね合わせの数理

バラバラのトークンベクトルを、たった1本の「文章ベクトル」に凝縮するのが Pooling だ。
特に OpenAI 等で使われる [CLS] [4] トークン は、会議における「書記」のような役割を果たす。

\mathbf{e} = \mathbf{x}'_{\text{CLS}} = \sum_{j=1}^{n} \alpha_{\text{CLS},j} \mathbf{v}_j

この式は、いわば「全単語のエッセンスを、重み付きで一つの器(CLS)に流し込む」プロセスを表している。

  • \alpha_{\text{CLS},j}: 書記である [CLS] が、各単語 j の話をどれくらい重視すべきかという情報の配分率。
  • \mathbf{v}_j: 各単語が持つ具体的な意味ベクトル。

この計算によって、文章全体の情報はたった一つの [CLS] ベクトルへと凝縮される。これは単なる平均ではなく、モデルが学習した「何が要約に重要か」というルールに基づいた、意味加重平均的な要約プロセスと言える。

[CLS]ベクトルは文章の先頭に座り、Self-Attentionを通じて全単語から要約に必要な情報を吸い上げることに特化している。

ここで面白いのは、1,536 次元という 「情報の重ね合わせ」 の性質だ。
高次元空間では、異なる概念(ベクトル)は統計的にほぼ直交する。そのため、複数の意味を1本のベクトルに足し合わせても、それぞれの成分は干渉しにくく共存できる。
個別の単語という情報を捨て、文章全体の「意味の方向性」へと情報を濃縮するこの不可逆な要約こそが、検索を可能にする。

4. 計算可能性の向上のための正規化

そしてAPI が最後に出力する直前、ベクトル \mathbf{e} の長さを 1 に揃える L2 正規化が行われる。

\mathbf{e}_{final} = \frac{\mathbf{e}}{\|\mathbf{e}\|_2}

これにより、全データは半径 1 の超球面上に射影される。
これがなぜ重要かと言えば、検索時の「コサイン類似度」の計算が、単純な「内積」と等価になる からだ。

\text{Similarity}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \|\mathbf{b}\|} = \mathbf{a} \cdot \mathbf{b} \quad (\text{if } \|\mathbf{a}\|=\|\mathbf{b}\|=1)

この最後の手続きがあるからこそ、FAISS などの検索エンジンは、爆速で「意味の近さ」を判定できる。

5. 言葉が「座標」へと昇華されるまでの全プロセス

ここまで見てきた4つのプロセスを振り返ると、Embedding API の内部では以下のような意味合いを持つ計算が一気通貫で行われていることがわかる。

  1. 初期配置
    巨大な意味のカタログから、単語ごとの種となるベクトルを引き出す。この時点ではまだ、記号の羅列に過ぎない。
  2. 文脈の織り込み(Self-Attention)
    全単語間での総当たりの行列演算により、周囲の単語の意味を自分に染み込ませる。静的な単語が、文脈色に染まった動的な意味へと変質する。
  3. 情報の凝縮 (Pooling)
    全単語のエッセンスを、書記である [CLS] ベクトルという一つの器に流し込む。1,536 次元の情報の重ね合わせを活かし、文章全体の要約を一本の数値の束に封じ込める。
  4. 最終調整(計算可能性向上のための正規化):
    完成したベクトルを半径1の超球面上に射影する。この正規化により、のちの検索プロセスにおける爆速の内積演算が保証される。

これら重厚なプロセスを経て生成された 1,536 次元の座標は、単なる数値の羅列ではなく、人類の知性を模した計算技術によって、自然言語が持つ文脈が幾何学的に表現されたものだと言える。

まとめ:脳の模倣と知の幾何学

Embedding のプロセスを俯瞰すると、それは脳がバラバラの刺激から「意味」を構成するゲシュタルト的な働きを、数学的に定式化したものに見える。

  • 学習: 数兆回の「穴埋めクイズ」を通じ、特定のベクトルが宇宙のどこに座るのが最も矛盾が少ないかを探り当てる。
  • 推論: 固定された「宇宙の法則(重み行列)」に言葉を通し、瞬時に座標を特定する。

そして、ここの計算プロセスに着目すると、僕が当時学生だった頃、2014年から2018年頃にかけて機械学習ブームの中で、その分野で積み上げられてきた知見がうまく統合されていることがわかる。

過去の理論的・実践的な方法論が、今1本のAPIの背後で、脳の認知機構を模倣した機構として動き、僕たちが記号として扱っていた言葉を、ベクトル空間における点の移動と集約という計算可能な手続きへと翻訳する。
この意味の幾何学を理解することは、AI という知性がいかにして世界を捉えているのかを覗き見ることに他ならないのだ。

参考文献


脚注
  1. OpenAIのトークナイザー cl100k_base の仕様を参照。詳細は OpenAI Cookbook の How to count tokens with tiktoken を参照。 ↩︎

  2. 特定の要素(単語IDに対応する位置)だけが 1 で、他が 0 であるようなベクトル。これを行列 W_{emb} に掛ける操作は、「特定の行を抽出する」ことと等価になる。 ↩︎

  3. この Self-Attention は、文章内の全トークンが総当たりで計算を行う。そのため、その計算量はトークン数 N の2乗(O(N^2))に比例して増大する。OpenAI の API に「最大トークン数(8,192トークン等)」の制限があるのは、主にこの幾何級数的な計算爆発を避けるためだ。しかし、これほど重厚な演算を GPU による並列処理でねじ伏せ、API という形で一瞬で提供していることには、改めて驚かされる。僕たちはこの重厚な演算の恩恵を受けることで、「単語同士の網羅的な関係性」という、解像度の高い意味の地図を手に入れているのである。 ↩︎

  4. 括弧付きの表記は、文章中の一般単語と区別するためのスペシャルトークンであることを示す慣習的な表記。 ↩︎

Discussion