Tech カテゴリのイメージ

位置エンコーディングとは?同じ単語の並び順をTransformerへ伝える

埋め込み表からベクトルを取り出すだけでは、トークンが文の何番目にあるか分かりません。「猫が犬を追う」と「犬が猫を追う」では登場する語が似ていても、誰が追う側かは逆です。Transformerが語順を扱うには、トークンの種類とは別に位置の手がかりが必要です。

その手がかりを表現へ加える方法が位置エンコーディングです。2017年のTransformer原論文では、トークン埋め込みと同じ次元の位置ベクトルを作り、両者を足して最初の層へ渡しました。

Self-Attentionだけでは順番が入らない

Self-Attentionは、各位置のQueryとKeyの相性を計算し、Valueを重み付きで集めます。しかし位置の情報が入力に一切なければ、同じベクトル集合を並べ替えても、並べ替えに対応して出力が移るだけです。どちらが先で、どれだけ離れているかを計算から知る材料がありません。

そこで入力位置0、1、2といった番号ごとに異なるベクトルを用意します。同じ「猫」の埋め込みでも、位置0の情報を足したものと位置3の情報を足したものは別の入力になります。

Transformerへの入力 = トークン埋め込み + 位置の表現

この加算が可能なのは、二つのベクトルの次元が同じだからです。原論文では埋め込みと各層の出力を d_model=512 次元にそろえています。

sinとcosで位置ごとの波を作る

原論文の固定位置エンコーディングは、偶数次元にsin、奇数次元にcosを使います。位置を pos、次元の組を示す番号を i とすると、次の式です。

PE(pos, 2i) = sin(pos / 10000^(2i / d_model))
PE(pos, 2i + 1) = cos(pos / 10000^(2i / d_model))

次元ごとに波の周期が異なるため、各位置は複数の速さで動く値の組として表されます。単純に位置番号を全次元へ足すのではなく、滑らかに変化する複数の波で位置を区別します。

4つの位置をPythonで計算する

6次元の位置エンコーディングを4位置ぶん作ります。NumPyだけで実行できます。

import numpy as np
def positional_encoding(length, d_model):
if d_model % 2 != 0:
raise ValueError("この例ではd_modelを偶数にしてください")
positions = np.arange(length)[:, None]
frequencies = np.exp(
-np.log(10000.0) * np.arange(0, d_model, 2) / d_model
)
pe = np.zeros((length, d_model))
pe[:, 0::2] = np.sin(positions * frequencies)
pe[:, 1::2] = np.cos(positions * frequencies)
return pe
pe = positional_encoding(length=4, d_model=6)
print(np.round(pe, 4))
token_embedding = np.array([0.2, -0.1, 0.4, 0.3, -0.2, 0.5])
print("位置0:", np.round(token_embedding + pe[0], 4))
print("位置3:", np.round(token_embedding + pe[3], 4))

出力:

[[ 0. 1. 0. 1. 0. 1. ]
[ 0.8415 0.5403 0.0464 0.9989 0.0022 1. ]
[ 0.9093 -0.4161 0.0927 0.9957 0.0043 1. ]
[ 0.1411 -0.99 0.1388 0.9903 0.0065 1. ]]
位置0: [ 0.2 0.9 0.4 1.3 -0.2 1.5]
位置3: [ 0.3411 -1.09 0.5388 1.2903 -0.1935 1.5 ]

位置0ではsin側が0、cos側が1から始まります。位置が進むと各次元が異なる速さで変化します。後半では同じトークン埋め込みへ位置0と位置3を足しました。トークンが同じでも、Transformerへ渡る入力は異なります。

表示上は最後のcos次元がどちらも 1.5 に丸められていますが、内部の値はわずかに異なります。周期の長い次元は、短い位置差ではゆっくり変わります。

位置ベクトルだけで文法が決まるわけではない

位置エンコーディングは「3番目だから目的語」といった文法規則を直接書き込むものではありません。各位置を区別し、距離や順序を利用できる材料を与えます。その情報をどう使うかはAttentionなどの学習済みパラメータが決めます。

また、同じ位置番号のベクトルは文章が違っても同じです。文脈に応じた意味表現は、トークン埋め込みと位置情報を入力にしてTransformerの層を通る中で作られます。

現在のLLMがすべてsin・cos加算とは限らない

原論文では固定のsin・cos方式と学習可能な位置埋め込みを比較し、当時の実験ではほぼ同等の結果だったと報告しています。その後は相対位置を扱う方式や、QueryとKeyへ回転を加える方式なども登場しました。

したがって「位置情報が必要」という考え方と、「位置ベクトルを入力へ足す」という一つの実装は分けて理解する必要があります。モデルの位置表現を確認するときは、そのモデル自身の技術資料や実装を参照します。

ここまでで、トークンIDは埋め込みベクトルになり、位置の手がかりも加わりました。次はSelf-Attentionが、各位置からどの情報を集めるかを計算します。

講座の前後記事

参照資料