Masato Izumi Portfolio

Self-Attention 詳細ガイド:Q, K, V から Multi-Head まで

  • Transformer
  • 機械学習
  • Self-Attention

本記事では、TransformerのSelf-Attention機構を詳しく解説します。「なぜその仕組みが必要なのか」という動機から、具体的な計算の流れまでを丁寧に説明していきます。

Self-Attentionの目的

各トークンは独立したベクトルとして入力されるため、文脈(周囲のトークン)の情報がありません。「bank」という単語が銀行なのか土手なのかは、周囲の単語を見なければわかりません。

問題 Self-Attention による解決
トークンが孤立他のトークンの情報を取り込む
文脈がわからない文脈依存の意味を表現
多義語の曖昧さ周囲から正しい意味を判断

全体の流れ

入力: X(トークン列、各トークン d次元)
      [x₁, x₂, x₃, ...]  ← 位置エンコーディング済み

Step 1: Q, K, V を生成(線形変換)
  Q = X × Wq    (Query: 「何を探しているか」)
  K = X × Wk    (Key: 「自分は何を持っているか」)
  V = X × Wv    (Value: 「実際の情報」)

Step 2: RoPE回転を適用(位置情報の付与)
  Q' = RoPE(Q)
  K' = RoPE(K)
  V はそのまま(回転しない)

Step 3: Attention Score を計算
  Score = Q' × K'ᵀ / √d

Step 4: Softmax で正規化
  Attention Weight = softmax(Score)

Step 5: Value の重み付け和
  Output = Attention Weight × V

出力: 文脈を考慮した新しいベクトル列

Q, K, V の詳細

なぜ Q, K, V に分けるのか

単純にトークン同士の内積を取るだけでは、自分自身との類似度が常に最大になり、柔軟な注目パターンを学習できません。役割を分離することで柔軟な情報交換が可能になります。

  • Query: 「私は何を探している?」← 探す視点
  • Key: 「私は何を提供できる?」← 提供する視点
  • Value: 「私の実際の情報」← 渡す情報

同じ入力から異なる重み行列で分ける

同じ入力ベクトル X から:

Q = X × Wq   ← 重み行列 Wq
K = X × Wk   ← 重み行列 Wk(Wq とは別の値)
V = X × Wv   ← 重み行列 Wv(Wq, Wk とは別の値)

Wq, Wk, Wv はすべて学習パラメータ(異なる値)

学習で獲得されること

Wq が学ぶこと: 「何を探しているか」への変換
  "bank" → 「金融 or 地理に関連するものを探している」

Wk が学ぶこと: 「何を提供できるか」への変換
  "money" → 「金融に関連する情報を提供できる」
  "river" → 「地理に関連する情報を提供できる」

マッチング:
  Q_bank · K_money = 高い(金融 ↔ 金融)
  Q_bank · K_river = 高い(地理 ↔ 地理)
  → 文脈で「どちらに注目するか」が決まる

Multi-Head Attention

なぜ Multi-Head が必要か

1つのヘッド(Single Head)では1種類の関係しか見れませんが、言語・画像には多様な関係があります。複数のヘッドで同時に観察することで、構文、意味、位置など多角的な情報収集が可能になります。

"彼女は銀行に行った"

必要な関係性:
  - 構文: 「彼女」→「行った」(主語-述語)
  - 参照: 「彼女」→ 文脈の人物
  - 意味: 「銀行」→ 金融機関 or 土手?
  - 位置: 隣接する単語の関係

1つのヘッドで全部は無理
→ 128次元でも「1種類の関係」なら十分
→ 16種類あれば多くの関係をカバー

次元の分割

入力: 1トークン = 2048次元ベクトル

Q = X × Wq で Query を生成(2048次元)
         ↓
16個のヘッドに分割(2048 ÷ 16 = 128次元/ヘッド)

[d₀, d₁, ..., d₁₂₇, d₁₂₈, ..., d₂₅₅, ..., d₁₉₂₀, ..., d₂₀₄₇]
 └─── Head 1 ───┘  └─── Head 2 ───┘      └─── Head 16 ──┘
     (128次元)         (128次元)              (128次元)

分身チームの比喩

入力トークン「今日」(2048次元)
         │
         ↓ 16分割
┌────────┼────────┬────────┬─────┬────────┐
↓        ↓        ↓        ↓     ↓
分身1    分身2    分身3    ...   分身16
(128dim) (128dim) (128dim)       (128dim)
│        │        │              │
↓        ↓        ↓              ↓
「構文的に   「意味的に   「位置的に      「別の
関係ある    関係ある    近いトークン    視点で
トークンは?」トークンは?」は?」        は?」
│        │        │              │
└────────┴────────┴──────────────┘
         │
         ↓ 連結(合体)
「今日」の新しいベクトル (2048次元)
= 16人の分身が集めた情報の統合

なぜ128次元でも十分か

128次元 = $2^{128}$ 通りの方向を区別可能 = $10^{38}$ 通り(宇宙の原子数より多い)。「構文関係」だけを表現するには十分すぎるため、次元数の問題より「何を見るか」の多様性が重要です。

Attention Score の計算

なぜスケーリング($\sqrt{d_k}$ で割る)が必要か

次元数が増えると内積の絶対値が大きくなります。大きい値がSoftmaxに入ると「勝者総取り」になり、1位以外の勾配がほぼ0になって学習が進みません。

$$ \text{Score} = \frac{Q \times K^T}{\sqrt{d_k}} $$

統計的な理由

Q, K の各要素が平均0、分散1の分布だと仮定すると、$d_k$ 個の項の和の分散は $d_k$ に比例します。標準偏差 $\sqrt{d_k}$ で割ると分散が1に正規化されます。

Softmax と重み付け

なぜ Softmax を使うのか

  • 全て正の値になる
  • 合計が1.0になる(確率分布として扱える)
  • 差を強調しつつ、全員に少しは重みを残す

Softmax の計算例

Score = [2.1, 0.5, -0.3]  ← 3トークンへの注目度(正規化前)

Step 1: exp を取る
  exp([2.1, 0.5, -0.3]) = [8.17, 1.65, 0.74]

Step 2: 合計で割る
  合計 = 8.17 + 1.65 + 0.74 = 10.56

  Weight = [8.17/10.56, 1.65/10.56, 0.74/10.56]
         = [0.77, 0.16, 0.07]

→ 合計 = 1.0(確率分布)
→ 大きいスコアほど大きい重み

Value の重み付け和

Weight = [0.77, 0.16, 0.07]  ← トークン1の注目度

V₁ = [0.2, 0.8, ...]  ← トークン1のValue
V₂ = [0.5, 0.3, ...]  ← トークン2のValue
V₃ = [0.1, 0.6, ...]  ← トークン3のValue

出力₁ = 0.77×V₁ + 0.16×V₂ + 0.07×V₃

→ 重みが大きいトークンの情報が多く混入

FFN(Feed Forward Network)

なぜ FFN が必要か:線形と非線形

Self-Attentionの最終出力は、Valueベクトルの重み付き平均です。これは足し算と掛け算だけの「線形結合」であり、活性化関数を使いません。

あるトークンの出力 = 0.7×V₁ + 0.2×V₂ + 0.1×V₃
                    ↑
              線形結合(足し算と掛け算のみ)

線形変換だけでは、何層重ねても1層と同じです:

層1: y = W₁x
層2: z = W₂y = W₂(W₁x) = (W₂W₁)x = Wx

→ 結局1つの行列Wと同じ
→ 層を深くする意味がない

FFNは活性化関数(GELU)で非線形性を加えることで、複雑なパターンを学習可能にします。

FFN の式と各記号の意味

$$ \text{FFN}(x) = \text{GELU}(x W_1 + b_1) W_2 + b_2 $$
記号 意味 サイズ例
$x$入力ベクトル(Attentionの出力)2048次元
$W_1$第1層の重み行列2048 × 8192
$b_1$第1層のバイアス8192次元
$W_2$第2層の重み行列8192 × 2048
$b_2$第2層のバイアス2048次元
GELU活性化関数-
入力 x (2048次元)
    ↓ × W₁ + b₁
隠れ層 (8192次元)  ← 4倍に拡張
    ↓ GELU
活性化後 (8192次元)
    ↓ × W₂ + b₂
出力 (2048次元)    ← 元に戻す

GELU(活性化関数)の役割

GELU(Gaussian Error Linear Unit)は「柔らかいReLU」です:

$$ \text{GELU}(x) = x \times \Phi(x) $$

$\Phi(x)$ は正規分布の累積分布関数(「xが正である確率」)です。

x が大きい正 → ほぼ x をそのまま出力
x が大きい負 → ほぼ 0 を出力
x が 0 付近  → 滑らかに遷移
x ReLU(x) GELU(x)
-20-0.05
-10-0.16
000
110.84
221.95

なぜ ReLU ではなく GELU か

  • 滑らかさ:ReLUは x=0 で角があるが、GELUは全域で滑らか → 勾配が安定
  • 負の値も少し通す:ReLUは x<0 で勾配0(学習が止まる)、GELUは小さい勾配が残る
  • 確率的解釈:「この入力は重要か?」を確率的に判断

重みとバイアスはどう決まるか

$W_1$, $W_2$, $b_1$, $b_2$ は学習で獲得されるパラメータです。

【初期化】ランダムな小さい値
  W₁, W₂ = 平均0、標準偏差0.02程度のランダム値
  b₁, b₂ = 0 または小さい値

【学習中】勾配降下法で更新
  1. 入力を与えて出力を計算(順伝播)
  2. 正解との誤差(Loss)を計算
  3. 誤差を逆方向に伝播(逆伝播)
  4. 各重みの勾配(∂Loss/∂W)を計算
  5. 重みを更新:W ← W - 学習率 × 勾配

  これを何億回も繰り返す

バイアスの役割

y = Wx + b
        ↑
      バイアス = 「基準点の調整」

b が正 → GELUの閾値が下がり、より通しやすくなる
b が負 → GELUの閾値が上がり、より抑制しやすくなる

FFN = 知識の保存場所

研究(Geva et al., 2021)によると、FFNは連想記憶として機能します:

W₁の各行 = Key(パターン検出器)
W₂の各列 = Value(対応する出力)

例:
  W₁のある行が「首都に関する文脈」を検出
    ↓ GELU で活性化
  W₂の対応する列が「首都の知識」を出力に加える

FFN vs Attention の役割分担

Self-Attention FFN
何をするトークン間の情報を混ぜる各トークンを個別に変換
学ぶもの「誰を見るか」のパターン「何を出力するか」の知識
「bank」は「money」を見る「Tokyo」→「日本の首都」
線形性重み付き平均(線形)GELU(非線形)

RoPE との関係

Self-Attentionは全トークンを一度に比較するため、順番の情報がありません。「犬が猫を追いかけた」と「猫が犬を追いかけた」は同じトークン集合のため、区別できません。

RoPE の適用タイミング

1. Q, K, V を生成
   Q = X × Wq
   K = X × Wk
   V = X × Wv

2. Q と K に RoPE を適用 ← ここで位置情報が入る
   Q' = RoPE(Q, position)
   K' = RoPE(K, position)

3. Attention Score を計算 ← ここで位置情報が効果を発揮
   Score = Q' × K'ᵀ / √d_k
   (この内積計算で相対位置 (m-n) がスコアに反映される)

4. Softmax → 重み付け和
   Output = softmax(Score) × V  ← V は回転しない

なぜ Q と K だけに適用するのか

  • Q, K の役割: 「誰に注目するか」を決める → 位置によって注目パターンを変えたい
  • V の役割: 「実際の情報」を渡す → 情報そのものは位置で変わらない

InfoNCE Loss(対照学習)

なぜ InfoNCE を使うのか

Embeddingは「類似度」を学習したいですが、クラス数が膨大で相対的な順序が重要です。InfoNCEは正解との類似度を高く、不正解との類似度を低くする相対的な差を学習します。

InfoNCE の数式

$$ L = -\log \frac{\exp(\text{sim}(q, d^+) / \tau)}{\exp(\text{sim}(q, d^+) / \tau) + \sum_i \exp(\text{sim}(q, d_i^-) / \tau)} $$
要素 役割
$\text{sim}(q, d^+)$正解との類似度を高くしたい
$\text{sim}(q, d^-)$不正解との類似度を低くしたい
$\exp(\cdot/\tau)$差を強調($\tau$小→シャープ)
$-\log$確率→損失に変換

温度パラメータ $\tau$ の役割

【τ が大きい(高温)】
softmax: [0.32, 0.30, 0.22, 0.16]  ← なだらか
  - 分布が均等に近い
  - 多くのネガティブから学べる
  - 学習が安定

【τ が小さい(低温)】
softmax: [0.70, 0.26, 0.03, 0.00]  ← シャープ
  - 正解が際立つ
  - Hard Negative に集中
  - 識別力が高い

まとめ

Self-Attention の核心:

1. Q, K, V への変換
   → 「探す」「提供」「情報」を分離

2. 内積で類似度計算
   → どのトークンに注目するか決定

3. スケーリング(√d_k)
   → 勾配を安定させる

4. Softmax で正規化
   → 確率分布として扱える

5. Value の重み付け和
   → 関連トークンの情報を取り込む

6. Multi-Head で並列化
   → 多角的な注目パターン(分身チーム)

7. RoPE で位置情報
   → 相対位置が内積に反映

8. FFN で知識処理
   → 非線形変換と知識の保存

参考文献

記事一覧に戻る