高校数学Bの内積を思い出そう
高校数学Bで習った2つのベクトルの内積を覚えていますか? 2つの定義式があります。
定義1(成分表示):a = (a₁, a₂), b = (b₁, b₂) のとき
a · b = a₁b₁ + a₂b₂
定義2(幾何学的):
a · b = |a| |b| cosθ
|a|, |b| はベクトルの長さ、θは2つのベクトルがなす角
この2つは同じものを表しています。そして注目すべきは 定義2 です。
具体例で確認する
簡単な2次元ベクトルで内積を計算してみましょう。
| ベクトル a | ベクトル b | 内積 a·b | 関係 |
|---|---|---|---|
| (1, 0) | (1, 0) | 1·1 + 0·0 = 1 | 完全に同じ方向 (cosθ=1) |
| (1, 0) | (0, 1) | 1·0 + 0·1 = 0 | 直交 (cosθ=0) |
| (1, 0) | (-1, 0) | 1·(-1) + 0·0 = -1 | 反対向き (cosθ=-1) |
| (2, 3) | (1, 4) | 2·1 + 3·4 = 14 | 向きが近い(大きい正の値) |
| (2, 3) | (4, -2) | 2·4 + 3·(-2) = 2 | 少し離れている(小さい正の値) |
パターンが見えますね。内積が大きいほど、2つのベクトルは「同じ方向を向いている」。これがAttentionの第一の発見です。
Attentionへの橋渡し — 「似ている」を数値化する
前章で、単語の意味は Embeddingベクトル として表現されると学びました。「猫」「犬」「車」をベクトルにすると、こんなイメージです(実際は512次元などですが、ここでは2次元で例示)。
vec("猫") = (0.9, 0.4) # 動物・ペット軸が強い
vec("犬") = (0.8, 0.5) # 同じく動物・ペット
vec("車") = (-0.3, 0.7) # 全く別の方向(乗り物)
# 内積を計算
猫 · 犬 = 0.9 * 0.8 + 0.4 * 0.5 = 0.72 + 0.20 = 0.92 ← 大きい
猫 · 車 = 0.9 * (-0.3) + 0.4 * 0.7 = -0.27 + 0.28 = 0.01 ← ほぼゼロ 「猫」と「犬」の内積は0.92と大きく、「猫」と「車」の内積はほぼゼロ。内積を使えば、単語の意味的な近さが数値で測れる のです。
graph TD
A[2つのEmbeddingベクトル] --> B[内積を計算\nΣxi·yi]
B --> C{値の大きさ}
C -->|大| D[似ている\n注目すべき]
C -->|ゼロ| E[無関係]
C -->|負| F[反対方向\n相反する]
style A fill:#3b82f6,stroke:#1d4ed8,color:#fff
style B fill:#8b5cf6,stroke:#6d28d9,color:#fff
style D fill:#14b8a6,stroke:#0d9488,color:#fff
style E fill:#6b7280,stroke:#4b5563,color:#fff
style F fill:#ef4444,stroke:#b91c1c,color:#fffコサイン類似度 — 長さの影響を消す
ところで、内積には1つ困った性質があります。ベクトルの長さに影響される のです。
たとえば (2, 0) と (3, 0) は完全に同じ方向ですが、内積は 2·3 + 0·0 = 6。一方、(1, 0) と (1, 0) も同じ方向ですが内積は1です。「同じ方向」を測りたいのに、長さで値が変わってしまいます。
そこで「長さの影響を消す」工夫として、内積をベクトルの長さで割る方法があります。
コサイン類似度:
cos similarity(a, b) = (a · b) / (|a| · |b|) = cosθ
必ず -1 〜 1 の範囲に収まる
高次元でも式は同じ
ここまで2次元で説明してきましたが、実際のEmbeddingは512次元、4096次元、12288次元……と巨大です。視覚化はできませんが、計算規則は完全に同じ です。
n次元ベクトルの内積:
a · b = Σ aᵢ bᵢ (i=1 〜 n)
= a₁b₁ + a₂b₂ + ... + aₙbₙ
# Python で512次元ベクトルの内積(NumPy使用)
import numpy as np
a = np.random.randn(512) # 512次元のランダムベクトル
b = np.random.randn(512)
# 内積を計算(1行)
score = np.dot(a, b)
print(score) # 例: -3.247...
# Σの式を手で書いてもいい
score_manual = sum(a[i] * b[i] for i in range(512))
print(score_manual) # 上と同じ値 次章への布石 — Q · K というアイデア
Attentionの核心式 softmax(QK^T/√dk)V の中の QK^T の部分が、まさに「すべての(Query, Key)ペアの内積を計算する」操作です。
Qは「私はこの情報を探している」という質問ベクトル、Kは「私はこういう情報を持っている」という鍵ベクトル。その内積が大きい=QとKの相性が良い=注目すべき。これがAttentionの心臓部のロジックです。
graph LR Q[Query\n質問ベクトル\n何を探している?] --> DOT[内積\nQ · K] K[Key\n鍵ベクトル\n何を持っている?] --> DOT DOT --> S[相性スコア\n大きいほど\n注目すべき] style Q fill:#3b82f6,stroke:#1d4ed8,color:#fff style K fill:#8b5cf6,stroke:#6d28d9,color:#fff style DOT fill:#f97316,stroke:#ea580c,color:#fff style S fill:#14b8a6,stroke:#0d9488,color:#fff
ただし、内積で得たスコアをそのまま使うわけではありません。「合計1の確率分布」に変換する必要があります。その魔法の道具が、次章で扱う softmax関数 です。
この章のまとめ
内積は高校数学Bで習う「ベクトルの向きの近さを測る道具」です。a · b = a₁b₁ + a₂b₂ + ... + aₙbₙ の式は次元が増えても変わらず、512次元でも12288次元でも同じ計算で動きます。
Attentionは、この内積を「Q(質問)とK(鍵)の相性スコア」として使います。内積が大きい単語ペアほど、注目すべき関係にある ——これが心臓部の半分です。残りの半分(softmaxで重みに変える、V でValueを集約する)は次章以降で扱います。
理解度チェック
高校数学Bで習う内積の定義 a · b = |a||b|cosθ から、内積の本質を最もよく表すのはどれですか?
キーボード: 1〜4 で選択、Enter で回答