2017 年 6 月、Google Brain と Toronto 大学が "Attention is All You Need" を発表。RNN / CNN を一切使わず、Self-Attention だけで翻訳タスクを SOTA 越え という衝撃の論文。これが GPT / BERT / Claude / Gemini を含む現代 LLM 全ての出発点となった。
1. これで何が動いているか
- ChatGPT / GPT-4 / GPT-5 (OpenAI)
- Claude (Anthropic)
- Gemini / Bard (Google)
- Llama / Mistral / DeepSeek (オープン LLM)
- ViT / DALL-E / Stable Diffusion (画像)
- Whisper (音声), AlphaFold (タンパク質構造予測)
2. 仕組みのざっくり
- 1入力 X (n トークン × d 次元) を 3 つの行列 Wq, Wk, Wv で射影
- 2Q = X·Wq, K = X·Wk, V = X·Wv (それぞれ n × d_k)
- 3Attention scores = Q·K^T / √d_k (n × n の類似度行列)
- 4Softmax で各行を確率分布に正規化 (= attention weights)
- 5Output = weights·V (各トークンが他トークンの V を加重平均で取込み)
RNN は「左から順に処理 → 並列化できない」 という根本的限界を持つ。Self-Attention は系列の全位置を 行列演算 1 発 で処理できるため、GPU 並列化が完全に効く。これが「巨大モデル + 巨大データ」 の道を開いた最大の貢献。
3. Python 実装 (numpy のみ・動作確認済)
import numpy as np
def softmax(x, axis=-1): e = np.exp(x - x.max(axis=axis, keepdims=True)) return e / e.sum(axis=axis, keepdims=True)
def self_attention(X, Wq, Wk, Wv): ''' X: (n, d) 入力 (n トークン x d 次元) Wq: (d, dk) Query 射影 Wk: (d, dk) Key 射影 Wv: (d, dv) Value 射影 ''' Q = X @ Wq # (n, dk) K = X @ Wk # (n, dk) V = X @ Wv # (n, dv)
d_k = K.shape[-1] scores = Q @ K.T / np.sqrt(d_k) # (n, n) weights = softmax(scores, axis=-1) # (n, n) output = weights @ V # (n, dv) return output, weights
# 4 トークン × 8 次元の入力np.random.seed(0)n, d = 4, 8X = np.random.randn(n, d)Wq = np.random.randn(d, d) / np.sqrt(d)Wk = np.random.randn(d, d) / np.sqrt(d)Wv = np.random.randn(d, d) / np.sqrt(d)
output, weights = self_attention(X, Wq, Wk, Wv)print(f'Output shape: {output.shape}')print(f'\nAttention weights (4x4):')print(np.round(weights, 3))print(f'\n各行の和 (softmax 確認): {weights.sum(axis=1).round(2)}')
# 実機実行例:# Output shape: (4, 8)# Attention weights:# [[0.115 0.468 0.268 0.150] ← トークン 0 はトークン 1 に最も注目# [0.199 0.277 0.232 0.292] ← トークン 1 はやや均等# ...# 各行の和: [1.0 1.0 1.0 1.0]4. Multi-Head Attention
def multi_head_attention(X, Wq_list, Wk_list, Wv_list, Wo): ''' H 個の head を並列に計算し、結合してから Wo で射影 ''' heads = [] for Wq, Wk, Wv in zip(Wq_list, Wk_list, Wv_list): out, _ = self_attention(X, Wq, Wk, Wv) heads.append(out) # 結合 (concat) して最終射影 concat = np.concatenate(heads, axis=-1) return concat @ Wo
# 8 head の Multi-Head AttentionH = 8d_k = d // H # 各 head の次元 = 1Wq_list = [np.random.randn(d, d_k) for _ in range(H)]Wk_list = [np.random.randn(d, d_k) for _ in range(H)]Wv_list = [np.random.randn(d, d_k) for _ in range(H)]Wo = np.random.randn(d, d)
output = multi_head_attention(X, Wq_list, Wk_list, Wv_list, Wo)print(f'Multi-Head Output shape: {output.shape}')5. Causal Mask (GPT 系の自己回帰)
def causal_self_attention(X, Wq, Wk, Wv): '''GPT の自己回帰生成で必須: 未来トークンを 0 にマスク''' Q = X @ Wq; K = X @ Wk; V = X @ Wv n = Q.shape[0] d_k = K.shape[-1]
scores = Q @ K.T / np.sqrt(d_k) # 上三角マスク (未来は -inf) mask = np.triu(np.ones((n, n)) * -1e9, k=1) scores = scores + mask
weights = softmax(scores, axis=-1) return weights @ V, weights
output, weights = causal_self_attention(X, Wq, Wk, Wv)print(f'\nCausal Mask 後の weights:')print(np.round(weights, 3))# 上三角部分 (未来) は全て 0 になっているはず6. 計算量とスケーリング
| 項目 | Self-Attention | RNN / LSTM |
|---|---|---|
| 並列化 | 完全並列 | 逐次 (左から右) |
| Path Length | O(1) (任意 2 トークン間) | O(n) |
| 計算量 | O(n² × d) | O(n × d²) |
| 長文の弱点 | n² が膨大 (改良で O(n log n)) | 勾配消失 |
7. 改良アルゴリズム (長文対応)
- FlashAttention (2022): SRAM 局所性を最大化、2-4x 高速
- Sliding Window Attention (Mistral): 局所窓のみ、O(n × w)
- Sparse Attention (GPT-3 / Longformer): 一部のペアのみ計算
- Linear Attention / Performer: カーネルトリックで O(n)
- Mamba / RetNet (2023-): 状態空間モデル / RNN 復権
8. 関連リソース
- 論文: "Attention is All You Need" (Vaswani et al. 2017)
- Annotated Transformer (Harvard NLP): コードと論文を並列に説明
- nanoGPT (Andrej Karpathy): GPT を 600 行で再現
- HuggingFace Transformers: 業界標準ライブラリ
- llm.c (Andrej Karpathy): C で GPT-2 を再現
9. ここまでの到達点
現代アルゴリズム図鑑シリーズはここまでで 15 回。HNSW (ベクトル検索) から Self-Attention (LLM) まで、現在のクラウド・AI・分散システムを動かす中核アルゴリズムを Python で実装してきた。今後も Vector Quantization / Reed-Solomon / Differential Privacy / Diffusion Model など、随時追加予定。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。