Self-Attentionには本質的な問題があります。入力トークンの順番を入れ替えても、出力が変わらないのです。「猫が犬を追いかけた」と「犬が猫を追いかけた」は全く異なる意味ですが、単語の集合としては同じであり、Self-Attention単体では区別できません。
Self-Attentionの計算 $\text{softmax}(\bm{Q}\bm{K}^\top/\sqrt{d_k})\bm{V}$ は、入力の順列に対して等変(equivariant)です。つまり、入力の順序を入れ替えると出力も同じ順序で入れ替わりますが、各位置の出力値自体は変わりません。
この問題を解決するのが位置エンコーディング(Positional Encoding)です。各トークンの「位置」の情報を入力に追加することで、Self-Attentionが順序を認識できるようにします。
原論文の正弦波とデファクトのRoPEを「なぜ移り変わったか」で比較したい方へ:本記事は各手法を実装まで詳しく扱う詳細記事です。「加算する絶対位置」から「回転する相対位置」への移行の理由を軸にした比較は、以下の記事にまとめています。
位置エンコーディングを理解すると、以下のことが可能になります。
- Transformerの完全理解: なぜ位置情報が必要で、どう実装されているか
- 長文処理の設計: 訓練時より長い系列を処理するための外挿性の理解
- 最新モデルの理解: LLaMAやGPT-4で使われるRoPE、ALiBiの原理
- カスタムモデルの設計: タスクに応じた位置エンコーディングの選択
本記事の内容
- なぜ位置エンコーディングが必要か
- sin/cos 絶対位置エンコーディング(Vaswani et al., 2017)
- 学習可能な位置埋め込み
- RoPE(Rotary Position Embedding)
- ALiBi(Attention with Linear Biases)
- Pythonでの実装と比較
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
- Self-Attentionの理論と実装 — Query・Key・Valueの計算
- Attention機構の基礎 — Attentionの基本概念
Sin/Cos 絶対位置エンコーディング
Vaswaniらの提案
原論文「Attention Is All You Need」(Vaswani et al., 2017)では、sin関数とcos関数を使った位置エンコーディングが提案されました。
位置 $\text{pos}$ の $i$ 番目の次元の値は
$$ \begin{align} \text{PE}(\text{pos}, 2i) &= \sin\left(\frac{\text{pos}}{10000^{2i/d}}\right) \\ \text{PE}(\text{pos}, 2i+1) &= \cos\left(\frac{\text{pos}}{10000^{2i/d}}\right) \end{align} $$
$d$ はモデルの次元、$i = 0, 1, \ldots, d/2-1$ です。
なぜsin/cosなのか
この設計には3つの重要な性質があります。
性質1: 各次元が異なる周波数を持つ
周波数 $\omega_i = 1/10000^{2i/d}$ は次元 $i$ が大きくなるほど小さくなります。$i = 0$ では波長 $2\pi$(最短)、$i = d/2 – 1$ では波長 $2\pi \times 10000$(最長)です。これにより、位置情報が粗い粒度(長距離)から細かい粒度(短距離)まで多スケールで表現されます。
性質2: 相対位置が線形変換で表せる
任意の固定オフセット $k$ に対して、$\text{PE}(\text{pos} + k)$ は $\text{PE}(\text{pos})$ の線形変換で表せます。
$$ \begin{pmatrix} \sin(\omega(\text{pos}+k)) \\ \cos(\omega(\text{pos}+k)) \end{pmatrix} = \begin{pmatrix} \cos(\omega k) & \sin(\omega k) \\ -\sin(\omega k) & \cos(\omega k) \end{pmatrix} \begin{pmatrix} \sin(\omega \cdot \text{pos}) \\ \cos(\omega \cdot \text{pos}) \end{pmatrix} $$
加法定理を適用すると、位置のシフトが回転行列で表現されます。これにより、モデルは相対位置を容易に学習できます。
性質3: 有界性
sin/cos の値は $[-1, 1]$ に収まるため、位置に依存して値が発散することがなく、学習が安定します。
入力への加算
位置エンコーディングは、トークン埋め込みに加算されます。
$$ \bm{z}_i = \bm{x}_i + \text{PE}(i) $$
加算であるため、トークンの意味情報と位置情報が共存します。モデルは学習を通じて、どの次元が位置情報を、どの次元が意味情報を主に担うかを自動的に分離します。
学習可能な位置埋め込み
BERTとGPTのアプローチ
BERT(Devlin et al., 2018)とGPT(Radford et al., 2018)は、sin/cosの代わりに学習可能な位置埋め込みを使用しています。
$$ \text{PE}(\text{pos}) = \bm{E}_\text{pos}[\text{pos}] \in \mathbb{R}^d $$
$\bm{E}_\text{pos} \in \mathbb{R}^{L_\text{max} \times d}$ は位置埋め込み行列で、$L_\text{max}$ は最大系列長です。
sin/cos vs 学習可能な埋め込み
| 性質 | sin/cos | 学習可能 |
|---|---|---|
| パラメータ数 | 0(固定) | $L_\text{max} \times d$ |
| 外挿性 | 理論上は任意長に対応 | 訓練時の最大長に限定 |
| 表現力 | 固定パターン | タスクに適応 |
| 実用的な性能 | 学習可能とほぼ同等 | sin/cosとほぼ同等 |
実験的には、両者の性能差はほとんどないことが報告されています。しかし、最大系列長を超える外挿が必要な場合は、sin/cosの方が有利です。
これらの絶対位置エンコーディングには、長系列への外挿が困難という共通の限界があります。次に、この問題を解決する相対位置エンコーディングの手法を見ていきましょう。
RoPE(Rotary Position Embedding)
相対位置をAttentionスコアに埋め込む
RoPE(Su et al., 2021)は、LLaMA、PaLM、Mistralなど現代の大規模言語モデルで広く採用されている位置エンコーディングです。
RoPEの核心的なアイデアは、QueryとKeyに位置依存の回転を適用することで、Attentionスコアが相対位置のみに依存するようにすることです。
位置 $m$ のQuery $\bm{q}_m$ と位置 $n$ のKey $\bm{k}_n$ に回転行列を適用します。
$$ \begin{align} \tilde{\bm{q}}_m &= R_\Theta(m) \bm{q}_m \\ \tilde{\bm{k}}_n &= R_\Theta(n) \bm{k}_n \end{align} $$
すると、Attentionスコアは
$$ \tilde{\bm{q}}_m^\top \tilde{\bm{k}}_n = \bm{q}_m^\top R_\Theta(m)^\top R_\Theta(n) \bm{k}_n = \bm{q}_m^\top R_\Theta(n – m) \bm{k}_n $$
回転行列の性質 $R(\theta)^\top R(\phi) = R(\phi – \theta)$ により、スコアは相対位置 $n – m$ のみに依存します。
回転の具体的な計算
2次元の場合、位置 $m$ での回転は
$$ R_\theta(m) = \begin{pmatrix} \cos(m\theta) & -\sin(m\theta) \\ \sin(m\theta) & \cos(m\theta) \end{pmatrix} $$
$d$ 次元では、ベクトルを2次元ペアに分割し、各ペアに異なる角速度の回転を適用します。
$$ R_\Theta(m) = \text{diag}(R_{\theta_1}(m), R_{\theta_2}(m), \ldots, R_{\theta_{d/2}}(m)) $$
角速度は $\theta_i = 10000^{-2i/d}$ で、sin/cos位置エンコーディングと同じ周波数スケジュールです。
RoPEの利点
外挿性: RoPEは相対位置を内積に直接エンコードするため、訓練時より長い系列にもある程度対応できます。NTK-awareスケーリングやYaRNなどの拡張手法と組み合わせて、さらに外挿性を改善できます。
入力埋め込みを変更しない: 位置情報はAttentionのQ, Kにのみ適用され、Valueや埋め込み層には影響しません。
ALiBi(Attention with Linear Biases)
スコアにバイアスを加算する
ALiBi(Press et al., 2021)は、位置エンコーディングを使わず、Attentionスコアに距離に比例するバイアスを加算するシンプルな手法です。
$$ \text{score}(i, j) = \bm{q}_i^\top \bm{k}_j – m \cdot |i – j| $$
$m$ はヘッドごとに異なる固定のスロープ(傾き)で、学習パラメータではありません。
$m$ の値は $h$ 個のヘッドに対して幾何数列で設定されます。
$$ m_i = 2^{-8i/h}, \quad i = 1, 2, \ldots, h $$
距離が離れるほどスコアが減少するため、近いトークンへの注目が自然に促されます。スロープが小さいヘッドは遠くまで見え、大きいヘッドは近くに集中します。
ALiBiの利点
外挿性が非常に高い: 学習時の最大長を大幅に超える系列でも性能が維持されます。バイアス項は単に距離に比例するだけなので、新しい距離にも自然に一般化されます。
追加パラメータなし: スロープ $m$ は固定値であり、学習パラメータが一切増えません。
実装の簡潔さ: Attentionスコアにバイアスを加算するだけで、既存のAttention実装をほとんど変更せずに適用できます。
Pythonでの実装と比較
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# --- 各位置エンコーディングの実装と可視化 ---
def sinusoidal_pe(max_len, d_model):
"""Sin/Cos 位置エンコーディング"""
PE = np.zeros((max_len, d_model))
pos = np.arange(max_len)[:, None]
div = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
PE[:, 0::2] = np.sin(pos * div)
PE[:, 1::2] = np.cos(pos * div)
return PE
def rope_rotation(q, k, positions, d_k):
"""RoPE: Query, Keyに回転を適用"""
theta = 10000.0 ** (-2 * np.arange(d_k // 2) / d_k)
q_rot = np.zeros_like(q)
k_rot = np.zeros_like(k)
for i, pos in enumerate(positions):
angles = pos * theta
cos_a = np.cos(angles)
sin_a = np.sin(angles)
q_even = q[i, 0::2]
q_odd = q[i, 1::2]
q_rot[i, 0::2] = q_even * cos_a - q_odd * sin_a
q_rot[i, 1::2] = q_even * sin_a + q_odd * cos_a
k_even = k[i, 0::2]
k_odd = k[i, 1::2]
k_rot[i, 0::2] = k_even * cos_a - k_odd * sin_a
k_rot[i, 1::2] = k_even * sin_a + k_odd * cos_a
return q_rot, k_rot
def alibi_bias(n_heads, seq_len):
"""ALiBi: Attention バイアス行列"""
slopes = 2.0 ** (-8 * np.arange(1, n_heads + 1) / n_heads)
biases = []
for m in slopes:
bias = np.zeros((seq_len, seq_len))
for i in range(seq_len):
for j in range(seq_len):
bias[i, j] = -m * abs(i - j)
biases.append(bias)
return biases, slopes
# --- 可視化 ---
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# (a) Sin/Cos PEのヒートマップ
ax = axes[0, 0]
d_model = 64
max_len = 50
PE = sinusoidal_pe(max_len, d_model)
im = ax.imshow(PE, cmap="RdBu_r", aspect="auto", vmin=-1, vmax=1)
ax.set_xlabel("Dimension", fontsize=11)
ax.set_ylabel("Position", fontsize=11)
ax.set_title("Sinusoidal Positional Encoding", fontsize=13)
plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
# (b) Sin/Cosの内積(位置間の類似度)
ax = axes[0, 1]
similarity = PE @ PE.T
im = ax.imshow(similarity, cmap="viridis", aspect="auto")
ax.set_xlabel("Position j", fontsize=11)
ax.set_ylabel("Position i", fontsize=11)
ax.set_title("PE Similarity: PE(i) . PE(j)", fontsize=13)
plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
# (c) RoPEの効果: 相対位置とスコアの関係
ax = axes[1, 0]
d_k = 16
seq_len = 30
Q = np.random.randn(seq_len, d_k) * 0.5
K = np.random.randn(seq_len, d_k) * 0.5
positions = np.arange(seq_len)
Q_rot, K_rot = rope_rotation(Q, K, positions, d_k)
scores_no_rope = Q @ K.T / np.sqrt(d_k)
scores_rope = Q_rot @ K_rot.T / np.sqrt(d_k)
# 位置0からの各位置へのスコア
ax.plot(range(seq_len), scores_no_rope[0], "o-", linewidth=1.5,
markersize=3, alpha=0.7, label="Without RoPE")
ax.plot(range(seq_len), scores_rope[0], "s-", linewidth=1.5,
markersize=3, alpha=0.7, label="With RoPE")
ax.set_xlabel("Key position", fontsize=11)
ax.set_ylabel("Attention score (position 0)", fontsize=11)
ax.set_title("RoPE: Attention Score from Position 0", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
# (d) ALiBiのバイアスパターン
ax = axes[1, 1]
n_heads = 4
seq_len_alibi = 20
biases, slopes = alibi_bias(n_heads, seq_len_alibi)
for h in range(n_heads):
ax.plot(range(seq_len_alibi), biases[h][0],
linewidth=2, label=f"Head {h+1} (m={slopes[h]:.4f})")
ax.set_xlabel("Key position", fontsize=11)
ax.set_ylabel("Bias (from position 0)", fontsize=11)
ax.set_title("ALiBi: Attention Bias per Head", fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("positional_encoding.png", dpi=150, bbox_inches="tight")
plt.show()
この可視化から、各位置エンコーディング手法の特徴が理解できます。
-
左上(Sin/Cos PE): 低次元(左側)では高周波の正弦波、高次元(右側)では低周波の正弦波が観察されます。各位置が異なるパターンを持つため、位置の識別が可能です
-
右上(PE間の内積): 対角線(同じ位置同士)が最大で、距離が離れるほど内積が減少しています。ただし、sin/cosの周期性により、特定の距離で再び類似度が上がるパターンも見られます
-
左下(RoPEの効果): RoPEなし(青)のスコアはランダムに分布していますが、RoPEあり(オレンジ)のスコアは位置関係を反映した構造を持っています。RoPEにより、距離の近いトークン間のスコアが体系的に影響されています
-
右下(ALiBiのバイアス): 各ヘッドが異なるスロープを持つ線形バイアスを適用しています。Head 1(スロープが最大)は距離に対して急速にペナルティが増加し、Head 4(スロープが最小)は緩やかに増加します。これにより、異なるヘッドが異なるスケールの依存関係を捉えます
まとめ
本記事では、Transformerの位置エンコーディングをsin/cosからRoPE、ALiBiまで体系的に解説しました。
- Self-Attentionは入力の順序に不変であるため、位置情報の明示的な導入が必要
- Sin/Cos PE: 異なる周波数の正弦波で位置をエンコード。相対位置が線形変換で表現される美しい性質を持つ
- 学習可能PE: タスクに適応できるが、訓練長を超える外挿が困難
- RoPE: Q, Kに位置依存の回転を適用し、Attentionスコアが相対位置に依存するようにする。LLaMA, Mistralなどで採用
- ALiBi: スコアに距離比例のバイアスを加算。パラメータ増加なし、高い外挿性
次のステップとして、以下の記事も参考にしてください。
- BERTとGPTの違い — 位置エンコーディングの使い方の違い
- Self-Attentionの理論と実装 — Attentionの計算詳細
- トークナイゼーションの理論 — Transformerの入力処理