GPTの動作原理 — 自己回帰モデル
ChatGPTに「今日の天気は」と入力すると、次のように単語を1つずつ生成します。
入力: "今日の天気は"
↓
予測: "晴れ" ← 1単語生成
↓
入力に追加: "今日の天気は晴れ"
↓
予測: "で"
↓
入力に追加: "今日の天気は晴れで"
↓
予測: "す"
↓
... 終端トークンが出るまで繰り返す このように 前のトークンから次のトークンを予測することを繰り返す モデルを 自己回帰モデル(Autoregressive Model) と呼びます。GPT、Claude、Gemini、Llama……現代のチャット系LLMはすべて自己回帰モデルです。
graph LR A[今日の天気は] --> M1[Model] M1 --> B[晴れ] B --> A2[今日の天気は晴れ] A2 --> M2[Model] M2 --> C[で] C --> A3[今日の天気は晴れで] A3 --> M3[Model] M3 --> D[す] style A fill:#3b82f6,stroke:#1d4ed8,color:#fff style A2 fill:#3b82f6,stroke:#1d4ed8,color:#fff style A3 fill:#3b82f6,stroke:#1d4ed8,color:#fff style B fill:#14b8a6,stroke:#0d9488,color:#fff style C fill:#14b8a6,stroke:#0d9488,color:#fff style D fill:#14b8a6,stroke:#0d9488,color:#fff
問題:訓練中に「未来をカンニング」してはダメ
自己回帰モデルを訓練するとき、「次の単語を当てる」という問題を出して正解を教えます。たとえば「今日の天気は晴れです」という訓練データなら、こんな問題を作ります。
# 訓練データ: "今日の天気は晴れです"
# 問題群:
位置1の「今日」: 入力「<開始>」→ 正解「今日」
位置2の「の」: 入力「今日」→ 正解「の」
位置3の「天気」: 入力「今日 の」→ 正解「天気」
位置4の「は」: 入力「今日 の 天気」→ 正解「は」
位置5の「晴れ」: 入力「今日 の 天気 は」→ 正解「晴れ」
位置6の「です」: 入力「今日 の 天気 は 晴れ」→ 正解「です」 ここで重要な制約があります。位置5の「晴れ」を予測するとき、未来の位置6「です」を見てはいけない。当然ですよね、推論時はまだ「です」を生成していないのですから。
ところが、Self-Attentionは すべてのトークン間で内積を計算してしまう 仕組みです。何の工夫もしないと、位置5は位置6を見てカンニングできてしまいます。
解決策:Causal Mask(因果マスク)
そこで登場するのが Causal Mask(因果マスク) です。「因果」とは「原因 → 結果」、つまり「過去 → 未来」の方向のみを許可する、という意味です。
仕組みは驚くほどシンプル。Attentionスコアの行列のうち、上三角部分(=未来側)に -∞ を入れる だけです。
# QK^T の行列(5×5、5単語の文)
# 行 = クエリ位置、列 = キー位置
今日 の 天気 は 晴れ
今日 [ 0.5 -∞ -∞ -∞ -∞ ]
の [ 0.3 0.8 -∞ -∞ -∞ ]
天気 [ 0.4 0.6 0.7 -∞ -∞ ]
は [ 0.2 0.5 0.9 0.4 -∞ ]
晴れ [ 0.1 0.3 0.8 1.2 0.6 ]
↑ 上三角を -∞ で埋めた なぜ -∞ なのか? ここで第5章のsoftmaxの式を思い出してください。
softmax(xᵢ) = e^xᵢ / Σⱼ e^xⱼ
x = -∞ なら、e^(-∞) = 0
つまり -∞ を入れると、softmax後にその位置の重みが 完全に0 になります。未来の情報がまったく混ざらない、というわけです。
graph LR A[QK^T 行列\n上三角に -∞] --> B[softmax 適用] B --> C[未来側は重み0\n過去だけ参照] C --> D[× V で加重平均] D --> E[各位置は\n自分以前の情報だけ\n取り込んだベクトル] style A fill:#3b82f6,stroke:#1d4ed8,color:#fff style C fill:#f97316,stroke:#ea580c,color:#fff style E fill:#14b8a6,stroke:#0d9488,color:#fff
並列訓練の魔法
Causal Maskには素晴らしい副産物があります。「次の単語を予測する」という6つの問題を、1回のAttentionで同時に解ける のです。
RNNだったら「位置1を計算→位置2を計算→…→位置6を計算」と6回逐次的に処理する必要がありました。Transformerは、Causal Mask付きAttentionを1回計算するだけで、6つの位置すべての出力(=次トークン予測)が並列に得られます。
| RNN/LSTM(訓練時) | Transformer(訓練時) | |
|---|---|---|
| 処理方式 | 時刻 t は t-1 を待つ(逐次) | 全位置を1回で並列処理 |
| n=1000のとき | 1000回のステップが必要 | 1回のAttentionで全位置 |
| GPU効率 | 低い | 極めて高い |
| 訓練速度 | 遅い | 速い(Transformerの強みの1つ) |
KVキャッシュ — 推論を高速化する仕組み
推論時に1トークンずつ生成するとき、毎回ゼロから全体を計算するのは大変な無駄です。「今日の天気は晴れ」→「で」を予測するとき、「今日」「の」「天気」「は」「晴れ」のKeyとValueは 前ステップで計算済み のはず。
そこで、計算したK・Vを保存して再利用する仕組みが KVキャッシュ(KV Cache) です。
graph TD S1[ステップ1\n今日 → の] --> CACHE1["K, V を保存\n[今日のK, 今日のV]"] CACHE1 --> S2[ステップ2\n今日 の → 天気] S2 --> CACHE2["追加保存\n[今日, の]のK,V"] CACHE2 --> S3[ステップ3\n今日 の 天気 → は] S3 --> CACHE3["追加保存\n[今日, の, 天気]のK,V"] CACHE3 --> S4[新しいトークンのQだけ\n計算して既存K,Vと内積] style CACHE1 fill:#f97316,stroke:#ea580c,color:#fff style CACHE2 fill:#f97316,stroke:#ea580c,color:#fff style CACHE3 fill:#f97316,stroke:#ea580c,color:#fff style S4 fill:#14b8a6,stroke:#0d9488,color:#fff
補足:BERTは因果マスクを使わない
ここまでGPT系(Decoder-only)の話をしましたが、BERT系(Encoder-only)モデルは 因果マスクを使いません。
| GPT系(Decoder-only) | BERT系(Encoder-only) | |
|---|---|---|
| Attention | Causal Mask あり | マスクなし(双方向) |
| 用途 | 文章生成 | 文章理解・分類 |
| 訓練タスク | 次トークン予測 | Masked Language Modeling(穴埋め) |
| 代表モデル | GPT-4, Claude, Llama | BERT, RoBERTa |
BERTは「文章の穴埋め問題」を解くため、前後両方の文脈を見られた方が有利です。一方GPTは「次の単語を予測する」ため、未来を見てはいけません。同じTransformerでも、マスクの設計がモデルの目的を決める のです。
この章のまとめ
GPT系の自己回帰モデルは Causal Mask(上三角を-∞で埋める)により、訓練中の「未来カンニング」を防いでいます。-∞ → softmax → 0 のメカニズムで未来情報を完全に遮断し、しかも全位置を1回で並列に訓練できる優れた設計です。
推論時は逐次生成になりますが、KVキャッシュ で過去のK・Vを再利用し、高速化を図ります。
次の第9章では、Attentionの「隠れた弱点」を補う仕組み——Positional Encoding(位置エンコーディング)——を扱います。Attentionは順序を見ないので、位置情報を別途注入する必要があるのです。
理解度チェック
GPT系のような自己回帰モデルで「因果マスク」が必要な理由は何ですか?
キーボード: 1〜4 で選択、Enter で回答