一文でまとめると: トークン埋め込みは「この単語が何であるか」を、位置エンコーディングは「この単語がどこにあるか」を伝えます。Transformerは両者を連結ではなく加算で組み合わせます。なぜなら、加算はモデルの幅を増やすことなく、両方のシグナルを保持できるからです。
14.1 すべてに先立つ二つの入力、一つの工程
第4章と第5章では、Transformerに入力を準備する二つのコンポーネントを取り上げました。
| 章 | コンポーネント | 目的 |
|---|---|---|
| 第4章 | Embedding | トークンIDをベクトルに変換する(意味情報) |
| 第5章 | Positional Encoding | 各ベクトルに位置情報を加える |
本章ではさらに一段深く踏み込みます。なぜこの二つのシグナルは連結ではなく加算で組み合わされるのでしょうか?
その答えには、いくつかの幾何学的直観、パラメータ数に関する議論、そしてニューラルネットワークが表計算ソフトではないという率直な認識が含まれます。
14.2 それぞれのシグナルの本質
14.2.1 トークン埋め込み: 意味情報
第4章で扱った埋め込みのルックアップテーブルを思い出してください。
各トークンには [vocab_size, d_model] の形をしたテーブル内の対応するベクトルがあります。このベクトルはトークンの意味をエンコードします。
- 「agent」と「workflow」は近い場所にある(どちらもエージェント計算の領域)
- 「agent」と「integer」は遠く離れた場所にある(意味的領域が異なる)
埋め込みは次の問いに答えます。このトークンは何を意味するのか?
14.2.2 位置エンコーディング: 位置情報
第5章の位置エンコーディングは、シーケンス内の各位置に固有のベクトルを割り当てます。
- 位置0は固有のベクトルを持つ
- 位置1は別のベクトルを持つ
- 位置15はさらに別のベクトルを持つ
位置エンコーディングは次の問いに答えます。このトークンはシーケンス内のどこに現れるのか?
14.2.3 なぜどちらも重要なのか
次の二つのプロンプトを考えてみましょう。
"The agent reviewed the PR."
"The PR reviewed the agent."
トークンは同じ。順序は異なる。意味はまったく違います。
Transformerは両方のシグナルを同時に必要とします。
- 各トークンが何であるか (Embedding)
- 各トークンがどこにあるか (Positional Encoding)
問題は、両者をどう組み合わせるかです。
14.3 加算 vs 連結
14.3.1 二つの選択肢
二つのベクトルを組み合わせる直観的な方法は二つあります。
選択肢1: Concat(連結)
input = [Embedding; Positional Encoding]
結果の次元: d_model + d_model = 2 × d_model
選択肢2: Add(加算)
input = Embedding + Positional Encoding
結果の次元: d_model (変わらず)
Transformerは加算を採用しています。なぜでしょうか?
14.3.2 連結への反論
連結はベクトルの幅を倍にしてしまいます。
仮に d_model = 512 なら、連結によって1024次元のベクトルが生まれます。それ以降のすべての演算 — Q、K、V射影、FFNレイヤー、出力射影 — はすべて、512次元ではなく1024次元の入力を扱う必要があります。これは次のことを意味します。
- すべての重み行列のサイズが倍になる
- すべての行列乗算のコストが4倍になる(計算量は入力次元と出力次元の積に比例するため)
- 各パラメータのメモリ使用量がおおよそ倍になる
その後で512次元に射影し直すこともできますが、それでは連結を取り消すためだけに余分な線形レイヤーを追加することになります。差し引きすると、複雑さが増し、パラメータが増え、明示的な利点はありません。
加算はインターフェースをシンプルに保ちます。
[d_model] + [d_model] → [d_model]
下流のすべては、想定どおりの形状を受け取ります。
14.3.3 加算の擁護
1. 次元が膨らまない
モデル幅 d_model は、埋め込みレイヤーからTransformerスタック全体を通じて固定されたままです。すべてのブロック、すべての射影、すべての正規化レイヤーは同じ形状で動作します。この一貫性はアーキテクチャ的に美しいものです。
2. 高次元では情報が共存できる
512次元のベクトル空間では、二つの独立したシグナルを加算しても、必ずしも互いを破壊するわけではありません。幾何学的に考えてみてください。各次元は意味シグナルの一成分と位置シグナルの一成分を同時に運ぶことができます。
具体例:
embedding = [0.5, 0.3, -0.2, 0.8, ...] # 意味シグナル
position = [0.1, 0.0, 0.1, -0.1, ...] # 位置シグナル
combined = [0.6, 0.3, -0.1, 0.7, ...] # 両方が存在する
合成ベクトルはどちらの入力単独とも同じではありませんが、両方のシグナルがそこに影響を与えています。
3. Attentionレイヤーが両者を分離することを学べる
学習される重み行列 Wq、Wk、Wv は、合成ベクトルを異なる部分空間に射影します。学習が進むにつれて、射影の一部の次元は意味的なパターンに敏感になり、他の次元は位置的なパターンに敏感になることを学習できます。モデルは私たちが分離されたシグナルを与えなくても、必要なものを抽出することを自分で学べるのです。
14.3.4 ある類似
各コミットが次の情報を持つ、バージョン管理されたコードベースを思い浮かべてください。
- 内容 (コードで何が変わったか)
- タイムスタンプ (いつそれが起こったか)
これらを別々の二つのデータベースに保存して、いつも両方を参照する方法もあります。あるいは、両方をエンコードする結合レコードを設計し、一回のルックアップから両方を解釈できるようにクエリシステムを訓練することもできます。
Transformerは結合レコード方式を選びました。下流の「クエリシステム」(Attention)はそれを十分に活用できるほど強力です。
14.4 具体的な計算
14.4.1 ステップごとに
この図は、訓練ステップを通して特定の値の動きを追跡しています。
訓練前 (順伝播):
embedding_value = 0.9 # トークン埋め込みのある一次元
positional_value = 0.1 # 位置ベクトルの同じ次元
combined_value = 0.9 + 0.1 = 1.0
訓練中 (誤差逆伝播による埋め込みの更新):
new_embedding_value = old_embedding_value - lr * gradient
= 0.9 - 0.1 * (-0.4)
= 0.9 + 0.04
= 0.94
次の順伝播:
new_combined_value = new_embedding_value + positional_value
= 0.94 + 0.1
= 1.04
14.4.2 重要な観察
このトレースから、三つの点が浮かび上がります。
- トークン埋め込みは学習可能 — 各訓練ステップで誤差逆伝播により更新される
- 位置エンコーディングは固定 (元のTransformerの正弦波方式では) — 訓練中に変化しない
- 加算は順伝播のたびに行われる — 一度きりの前処理ではない
14.5 なぜこの設計が妥当なのか
14.5.1 ベクトル空間の視点
高次元ベクトル空間では、方向が概念を表します。二つのベクトルの加算は次のように考えられます。
- 埋め込みベクトルはこのトークンの「意味の方向」を指している
- 位置エンコーディングベクトルは「位置のオフセット」を加える
結果として得られるベクトルは、両方の情報を運ぶ場所を指しています。Attention機構の学習された射影は、各ヘッドをそのタスクに必要な種類の情報の方へと導いていきます。
14.5.2 直交性の直観
加算がうまくいく理由は、緩やかに直交性の仮定に基づいています。意味情報と位置情報は、高次元空間内では異なる「方向」に存在しがちであり、加算しても互いに打ち消し合わない、というものです。
これは定理ではなく、経験的な観察です。しかし実際には成り立ちます。この方法で訓練されたモデルは、単語の同一性と単語の位置を区別することを学習します。
14.5.3 Attentionは合成シグナルで何をするか
Attentionでは:
Q = (Embedding + PE) @ Wq
K = (Embedding + PE) @ Wk
訓練が進むにつれて、Wq と Wk は主に意味内容に応答する次元(QとKは意味に基づいて一致する)と、主に位置に応答する次元(QとKは位置に基づいて一致する)を発展させていけます。あるヘッドは前者に特化し、別のヘッドは後者に特化することもありえます。
これがAttentionの前にシグナルを分離する必要がない理由です。Attention自身が分離を行えるのです。
14.6 バリエーション: さまざまな位置エンコーディング手法
14.6.1 元の手法: 固定された正弦波エンコーディング
input = Embedding(token_ids) + PositionalEncoding(positions)
2017年の元のTransformerは、固定された正弦波関数を使用しました。位置に関するパラメータは学習されません — エンコーディングは位置インデックスから決定論的に計算されます。
14.6.2 学習可能な位置埋め込み
GPT系のモデルは学習可能な位置埋め込みを使用します。位置ベクトルは、トークン埋め込みと同様に、訓練可能なパラメータです。
class TransformerInput(nn.Module):
def __init__(self, vocab_size, d_model, max_len, dropout=0.1):
super().__init__()
self.token_embedding = nn.Embedding(vocab_size, d_model)
self.position_embedding = nn.Embedding(max_len, d_model) # 学習される
self.dropout = nn.Dropout(dropout)
self.scale = d_model ** 0.5
def forward(self, x):
# x: [batch_size, seq_len] トークンID
token_emb = self.token_embedding(x) # [batch, seq, d_model]
token_emb = token_emb * self.scale # スケーリング(任意)
positions = torch.arange(x.size(1), device=x.device)
pos_emb = self.position_embedding(positions) # [seq, d_model]
combined = token_emb + pos_emb # [batch, seq, d_model]
return self.dropout(combined)
学習可能な位置埋め込みは、固定的な構造を押し付けるのではなく、どの位置パターンがタスクに有用かをモデル自身に決めさせます。
14.6.3 RoPE: Rotary Position Embedding
より新しいモデルではRoPE (Rotary Position Embedding) を使うことがあり、これは異なるアプローチを採ります。位置ベクトルをトークン埋め込みに加算するのではなく、RoPEは位置に基づいてQとKベクトルに回転を適用します。
Q_rotated = rotate(Q, position)
K_rotated = rotate(K, position)
RoPEの利点:
- 相対位置をより明示的にエンコードできる(回転されたベクトル同士の内積は位置の差に依存する)
- 訓練時に見たことのないシーケンス長への汎化が良好
- LLaMA、GPT-NeoX、Mistral、そしてほとんどの現代的なオープンソースLLMで採用されている
RoPEには第25章で改めて触れます。今のところ重要なのは、インターフェースは変わらないという点です。モデルは内容と順序の両方を必要とし、設計上の問いは順序をいかに効率的にエンコードするかにあるのです。
14.6.4 アプローチの比較
| 種類 | 例 | 利点 | 欠点 |
|---|---|---|---|
| 固定正弦波 | 元のTransformer | 理論上は任意の長さに外挿できる | すべてのタスクに最適とは限らない |
| 学習型絶対位置 | GPT-2、GPT-3 | タスク固有の位置パターンを学習する | 訓練時に見ていない長さに汎化できない |
| RoPE | LLaMA、Mistral | 相対位置に強く、長さ汎化が優秀 | 実装がやや複雑 |
| ALiBi | BLOOM | Attentionスコアへの加算バイアス | 標準的な加算とはインターフェースが異なる |
14.7 次元の追跡
入力 token_ids: [4, 16] # 4シーケンス、各16トークン
トークン埋め込み:
ルックアップ: token_embedding([4, 16])
出力: [4, 16, 512] # 各トークン → 512次元ベクトル
位置埋め込み:
位置: [0, 1, 2, ..., 15]
ルックアップ: position_embedding([16])
出力: [16, 512] # 各位置 → 512次元ベクトル
ブロードキャスト: [4, 16, 512] # バッチ方向に拡張
加算:
[4, 16, 512] + [4, 16, 512] = [4, 16, 512]
最終出力: [4, 16, 512] # 意味 + 位置、形状は同じ
形状は決して変わりません。下流のすべてのコンポーネントは [batch, seq, d_model] を見ます。
14.8 よくある質問
14.8.1 加算は情報を失わないのか?
実用上は失いません。主な理由は次のとおりです。
d_model = 512(あるいは768、4096) は、意味シグナルと位置シグナルが破滅的に干渉しないだけの十分な次元を提供する- AttentionのWq、Wk、Wv射影は、いずれのシグナルも抽出できるように学習できる
- 複数層のTransformerブロックが、両シグナルの使い方を段階的に洗練できる
14.8.2 なぜ位置エンコーディングは小さくなければならないのか?
位置エンコーディングの値がトークン埋め込みの値より大幅に大きいと、意味シグナルを圧倒してしまいます。
embedding = [0.5, 0.3, -0.2] # 意味
position = [10, 20, -15] # 位置 — 大きすぎる!
combined = [10.5, 20.3, -15.2] # ほとんど位置、意味がかき消される
正弦波エンコーディングは [-1, 1] の範囲の値を生み、典型的な埋め込みベクトルの大きさに合致します。学習可能な位置埋め込みも、通常の訓練ダイナミクスを通じて同様の範囲に保たれます。
14.8.3 学習型 vs 固定型: どちらが優れるか?
| 種類 | 利点 | 欠点 |
|---|---|---|
| 固定型(正弦波) | 任意のシーケンス長に対応 | 必ずしも最適ではない |
| 学習型 | タスクに最適な位置パターンを学習できる | 訓練長を超えて外挿できない |
実用上は、学習可能な位置埋め込みのほうが標準的なベンチマークでわずかに優れた性能を示す傾向があり、これがGPTとBERTの両方が採用している理由です。長文脈モデルではRoPEとALiBiがますます好まれています。
14.9 章のまとめ
14.9.1 重要な概念
| シグナル | 出所 | 表すもの | 学習可能? |
|---|---|---|---|
| トークン埋め込み | 埋め込みルックアップテーブル | トークンの意味 | はい |
| 位置エンコーディング | 位置埋め込み / 正弦波関数 | シーケンス内の位置 | 手法による |
| 合成入力 | 両者の加算 | 意味 + 位置 | — |
14.9.2 なぜ加算で、連結ではないのか
- 次元の安定性:
d_modelがアーキテクチャ全体で固定される - 両シグナルの共存: 高次元ベクトルは複数のシグナルを収める余地を持つ
- Attentionが分離できる: 学習されたWq、Wk、Wv射影がシグナルごとに特化できる
14.9.3 核心の要点
トークン埋め込みと位置エンコーディングは、要素ごとの加算で組み合わされます。この設計はモデルの幅を一定に保ち、両方のシグナルが高次元空間に共存することを許し、各関係にとって重要なシグナルの抽出をAttentionの学習された射影に任せます。これは制約ではなく、コンパクトで効果的な設計上の選択なのです。
章末チェックリスト
本章を終えたら、次のことができるようになっているはずです。
- トークン埋め込みと位置エンコーディングがそれぞれ何を表しているかを説明できる。
- 連結がモデルの幅を増やすこと、そしてそれがなぜ問題なのかを説明できる。
- 加算がなぜ高次元空間で両方のシグナルを保持できるのかを説明できる。
- Attentionが合成シグナルをどう使うかを説明できる。
- 位置エンコーディングのバリエーションを少なくとも三つ挙げ、それぞれのトレードオフを述べられる。
次章でお会いしましょう
これで、必要なピースはすべて揃いました。
- トークン埋め込み (第4章)
- 位置エンコーディング (第5章、本章で再訪)
- Q、K、V を備えたAttention (第9〜12章)
- 残差接続とDropout (第13章)
- 内容と位置を組み合わせる加算操作 (本章)
第15章ではこれらを組み立てて完全な順伝播を構成します — 生のテキストのシーケンスから出力確率に至るまでを、次元ごとに丁寧にトレースしていきます。それではまた次の章で、お会いしましょう。