はじめに
この記事はAIで作成されています。multi-modal LLM関連のメモを記載しています。
目次
Positonal Encoding
Transformer の Self-Attention は、トークンの順序をそのままでは捉えられないため、位置情報を与える仕組みが必要になる。代表的な方式には、次の3つがある。
| 方式 | 位置情報の扱い | 学習可能な位置パラメータ | 特徴 |
|---|---|---|---|
| ①Sinusoidal Positional Encoding | 位置ベクトルをトークン埋め込みに加算 | なし | 固定された sin / cos の数式で位置を表現する |
| ②Relative Position Encoding | 相対距離に応じた情報を Attention に組み込む | あり(代表的な方式) | トークン同士の距離や前後関係を直接扱う |
| ③RoPE | Query と Key を位置に応じて回転 | なし | Attention の内積に相対位置の情報を反映する |
現在の主流
近年の大規模言語モデル(LLM)では、RoPE が広く採用されている代表的な方式である。例えば、Llama 系列などで使われている。
ただし、すべてのモデルが RoPE を採用しているわけではない。学習済みの絶対位置埋め込みや、RoPE 以外の位置情報の扱いを採用するモデルもある。
①Sinusoidal Positional Encoding
論文名: Attention Is All You Need
URL: https://arxiv.org/abs/1706.03762
発表日: 2017-06-12
✅ ポイント
- Transformer の Self-Attention には、トークンの順序を直接表す仕組みがないため、位置情報をトークン埋め込みに加算する。
- 位置情報には、異なる周波数の sin 関数と cos 関数を使用する。各位置が異なるベクトルで表現される。
- 位置情報は学習可能なパラメータではなく、固定された数式で計算する。学習時も推論時も同じ方法で生成できる。
🧪 学習の疑似コード
# トークン埋め込みと位置情報を準備
initialize(token_embedding, transformer)
while not converged:
token_ids, targets = sample_from_dataset()
# トークンの意味をベクトル化
X = token_embedding(token_ids)
# 各位置の位置エンコーディングを計算
PE = sinusoidal_encoding(sequence_length, d_model)
# トークン埋め込みに位置情報を加算
X = X + PE
# Transformer で予測
predictions = transformer(X)
# 損失を計算してモデルを更新
loss = compute_loss(predictions, targets)
update(loss)
位置エンコーディング自体は学習せず、トークン埋め込みや Transformer のパラメータを学習する。
🔎 推論時の疑似コード
token_ids = input_sequence()
# トークン埋め込みを取得
X = token_embedding(token_ids)
# 入力系列の位置情報を計算
PE = sinusoidal_encoding(sequence_length, d_model)
# 位置情報を加算して Transformer に入力
X = X + PE
output = transformer(X)
🧮 位置エンコーディングの計算式
位置 pos と次元 i に対して、次の式で位置情報を計算する。
$$
PE(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)
$$
$$
PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)
$$
偶数次元には sin、奇数次元には cos を使用する。次元ごとに周波数が異なるため、位置ごとに異なるパターンのベクトルが生成される。
❓ よくある質問
Q. なぜ sin と cos を使うの?
- 周波数の異なる波を組み合わせることで、各位置を異なるベクトルで表現できる。
- sin と cos の組み合わせには、位置のずれを相対的な変化として扱いやすいという性質もある。
Q. 位置エンコーディングも学習するの?
- 学習しない。位置と次元から数式で計算する固定値である。
- 一方、トークン埋め込みや Transformer の重みは通常どおり学習する。
Q. なぜトークン埋め込みに加算するの?
- トークンの意味情報と位置情報を同じ次元のベクトルにまとめ、Transformer に入力するため。
- これにより、Self-Attention がトークンの内容と位置の両方を利用できるようになる。
Q. 学習時より長い系列にも対応できるの?
- 固定された数式から位置情報を計算するため、学習時に使っていない位置についても生成できる。
- ただし、学習時より長い系列で十分な性能が得られるとは限らない。
Q. Relative Position Encoding との違いは?
- Sinusoidal Positional Encoding は、各トークンの絶対位置を表すベクトルを入力埋め込みに加算する。
- Relative Position Encoding は、トークン同士の相対位置を Attention のスコア計算や出力の集約に直接組み込む。
②Relative Position Encoding
論文名: Self-Attention with Relative Position Representations
URL: https://arxiv.org/abs/1803.02155
発表日: 2018-03-06
✅ ポイント
- Sinusoidal Positional Encoding ではトークンの絶対位置を使うが、Relative Position Encoding ではトークン同士の相対距離を使う。
- 相対位置ごとに学習可能なベクトルを用意し、Attention のスコア計算と出力の集約に組み込む。
- 相対距離が一定の範囲を超えた場合は同じ距離として扱い、学習するパラメータ数を抑える。
🧪 学習の疑似コード
# 相対位置ごとの学習可能なベクトルを初期化
initialize(relative_key, relative_value)
while not converged:
x, targets = sample_from_dataset()
# Query / Key / Value を計算
Q, K, V = compute_qkv(x)
# トークン間の相対距離を計算
relative_positions = clip(
positions_j - positions_i,
-max_distance,
max_distance
)
# 相対位置に対応する学習可能なベクトルを取得
R_key = relative_key[relative_positions]
R_value = relative_value[relative_positions]
# Key に相対位置ベクトルを加えてスコアを計算
scores = Q @ (K + R_key).T / sqrt(d_k)
attention = softmax(scores)
# Value にも別の相対位置ベクトルを加えて出力を集約
output = attention @ (V + R_value)
# 損失を計算してモデル全体を更新
loss = compute_loss(output, targets)
update(loss)
🔎 推論時の疑似コード
x = input_sample()
Q, K, V = compute_qkv(x)
# トークン間の相対距離を計算
relative_positions = clip(
positions_j - positions_i,
-max_distance,
max_distance
)
# 相対位置に対応する学習済みベクトルを取得
R_key = relative_key[relative_positions]
R_value = relative_value[relative_positions]
# Key に相対位置ベクトルを加えてスコアを計算
scores = Q @ (K + R_key).T / sqrt(d_k)
attention = softmax(scores)
# Value にも相対位置ベクトルを加えて出力を集約
output = attention @ (V + R_value)
relative_key と relative_value は別々の学習可能なパラメータであり、相対位置に応じたベクトルをそれぞれ Key と Value に加える。学習時には誤差逆伝播によって更新し、推論時には学習済みの値を使用する。
❓ よくある質問
Q. なぜ絶対位置ではなく相対位置を使うの?
- Attention では、トークン同士の距離や前後関係が重要になるため。
- 相対位置を直接組み込むことで、「1つ前のトークン」や「3つ後ろのトークン」といった関係を利用できる。
Q. 相対位置ベクトルは Key と Value の両方に必要なの?
- 論文では、Key 側で Attention のスコア計算に相対位置を反映し、Value 側でも出力の集約に相対位置を反映する。
- ただし、相対位置を組み込む方法はモデルの設計によって異なる。
Q. なぜ相対距離をクリップするの?
- 距離ごとに異なるベクトルを用意すると、必要なパラメータ数が増えるため。
- 一定以上の距離をまとめることでパラメータ数を抑えられるが、遠い位置同士の距離の違いは区別できなくなる。
Q. Sinusoidal Positional Encoding との違いは?
- Sinusoidal Positional Encoding は、絶対位置を表すベクトルをトークン埋め込みに加算する。
- Relative Position Encoding は、トークン同士の相対位置を Attention のスコア計算や出力の集約に直接組み込む。
③RoPE(Rotary Position Embedding)
論文名: RoFormer: Enhanced Transformer with Rotary Position Embedding
URL: https://arxiv.org/abs/2104.09864
発表日: 2021-04-20
✅ ポイント
- Sinusoidal Positional Encoding は位置情報をトークン埋め込みに加算するが、RoPE は Query と Key のベクトルを位置に応じて回転させる。
- 回転角度はトークンの位置と次元ごとの周波数によって決まる。Attention の内積に、トークン同士の相対位置の情報が自然に反映される。
- 位置情報を固定された数式で計算するため、位置エンコーディング自体の学習は不要。ただし、学習時より長い系列での性能が保証されるわけではない。
🧪 学習の疑似コード
# RoPE の回転角度を計算するための周波数を準備
initialize_frequencies()
while not converged:
token_ids, targets = sample_from_dataset()
# トークン埋め込みから Query / Key / Value を計算
X = token_embedding(token_ids)
Q, K, V = compute_qkv(X)
# 各トークンの位置に応じて Query と Key を回転
Q_rot = apply_rope(Q, positions)
K_rot = apply_rope(K, positions)
# 回転後の Query と Key で Attention を計算
scores = Q_rot @ K_rot.T / sqrt(d_k)
attention = softmax(scores)
# Value を重み付きで集約
output = attention @ V
# 損失を計算してモデルを更新
loss = compute_loss(output, targets)
update(loss)
RoPE 自体は学習可能なパラメータを持たず、位置に応じた回転を計算する。学習対象となるのは、主にトークン埋め込みや Query / Key / Value の射影などのモデルパラメータである。
🔎 推論時の疑似コード
token_ids = input_sequence()
# トークン埋め込みから Query / Key / Value を計算
X = token_embedding(token_ids)
Q, K, V = compute_qkv(X)
# 位置に応じて Query と Key を回転
Q_rot = apply_rope(Q, positions)
K_rot = apply_rope(K, positions)
# Attention を計算
scores = Q_rot @ K_rot.T / sqrt(d_k)
attention = softmax(scores)
# Value を集約
output = attention @ V
🧮 回転の計算式
RoPE では、ベクトルの隣り合う2次元を1組として扱い、位置 pos に応じて回転させる。
x'_{2i} = x_{2i}\cos(pos\theta_i) - x_{2i+1}\sin(pos\theta_i)
x'_{2i+1} = x_{2i}\sin(pos\theta_i) + x_{2i+1}\cos(pos\theta_i)
ここで、$x$ は回転前のベクトル、$x'$ は回転後のベクトル、$\theta_i$ は次元ごとに設定される周波数である。
各次元ペアは異なる周波数で回転するため、位置に応じた情報をベクトル全体に反映できる。この回転を Query と Key に適用することで、Attention のスコアに相対位置の情報が反映される。
❓ よくある質問
Q. なぜベクトルを回転させるの?
- 位置に応じて Query と Key の向きを変えることで、Attention の計算に位置情報を反映するため。
- 2つの回転後ベクトルの内積には、それぞれの絶対位置だけでなく、トークン同士の相対位置が反映される。
Q. Value にも RoPE を適用するの?
- 基本的な RoPE では、Query と Key に回転を適用する。
- Attention のスコア計算を通して位置情報を反映するため、Value には通常、RoPE を適用しない。
Q. Sinusoidal Positional Encoding との違いは?
- Sinusoidal Positional Encoding は、位置を表すベクトルをトークン埋め込みに加算する。
- RoPE は、Query と Key を位置に応じて回転させ、Attention の内積に相対位置の情報を反映する。
Q. Relative Position Encoding との違いは?
- Relative Position Encoding は、相対距離ごとの学習可能なベクトルを Attention の計算に加える。
- RoPE は、位置に応じた回転によって相対位置の情報を反映する。相対距離ごとの学習可能なベクトルを用意する必要がない。
Q. 学習時より長い系列にも対応できるの?
- 回転角度は位置から計算できるため、学習時に登場しなかった位置にも適用できる。
- ただし、長い系列での性能が保証されるわけではなく、実際のモデルでは系列長に応じた工夫が必要になる場合がある。