位置エンコーディングとは?同じ単語の並び順を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が、各位置からどの情報を集めるかを計算します。