本記事では、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) |
|---|---|---|
| -2 | 0 | -0.05 |
| -1 | 0 | -0.16 |
| 0 | 0 | 0 |
| 1 | 1 | 0.84 |
| 2 | 2 | 1.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 で知識処理
→ 非線形変換と知識の保存
参考文献
- Vaswani, A., et al. (2017). Attention Is All You Need. NeurIPS 2017.
- Geva, M., et al. (2021). Transformer Feed-Forward Layers Are Key-Value Memories. EMNLP 2021.