Tech カテゴリのイメージ

Self-Attentionとは?3トークンの重みをPythonで計算する

埋め込みと位置情報によって、各トークンは順番を含むベクトルになりました。次に必要なのは、ある位置の表現を作るとき、文中のどの位置からどれだけ情報を集めるかです。Self-Attentionは、同じ系列の各位置どうしの関係を点数化し、情報を重み付きで混ぜます。

「Attentionは重要な単語を見つける仕組み」とだけ覚えると、計算の一部が抜けます。重みは文全体に一つではなく、情報を受け取る位置ごとに作られます。同じ単語でも、どの位置から問い合わせるかによって参照の配分は変わります。

Query、Key、Valueの役割

各トークンの入力ベクトルから、学習可能な線形変換でQuery、Key、Valueを作ります。

Queryは「この位置がどんな情報を探すか」、Keyは「各位置がどんな照合用の特徴を持つか」、Valueは「選ばれたときに渡す情報」に相当します。これは理解のための言い換えで、各次元に人が役割を指定するわけではありません。

あるQueryとすべてのKeyの内積を取ると、参照先ごとのスコアができます。スコアをsoftmaxで合計1の重みに変え、その重みでValueを平均します。

Attention(Q, K, V) = softmax(QK^T / √d_k) V

d_k はKeyの次元数です。内積を √d_k で割るのは、次元が大きいと内積の大きさが増え、softmaxが極端な領域に入りやすくなるためです。

3トークンを一度に計算する

説明を短くするため、すでに作られたQ、K、Vを直接使います。実際のモデルでは、入力行列へ別々の重み行列を掛けて作ります。

import numpy as np
Q = np.array([
[1.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
])
K = np.array([
[1.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
])
V = np.array([
[10.0, 0.0],
[0.0, 10.0],
[5.0, 5.0],
])
scores = Q @ K.T / np.sqrt(K.shape[1])
shifted = scores - scores.max(axis=1, keepdims=True)
weights = np.exp(shifted)
weights /= weights.sum(axis=1, keepdims=True)
output = weights @ V
print("スコア:\n", np.round(scores, 3))
print("Attention重み:\n", np.round(weights, 3))
print("出力:\n", np.round(output, 3))
print("行ごとの重み合計:", np.round(weights.sum(axis=1), 3))

出力:

スコア:
[[0.707 0. 0.707]
[0. 0.707 0.707]
[0.707 0.707 1.414]]
Attention重み:
[[0.401 0.198 0.401]
[0.198 0.401 0.401]
[0.248 0.248 0.503]]
出力:
[[6.017 3.983]
[3.983 6.017]
[5. 5. ]]
行ごとの重み合計: [1. 1. 1.]

重み行列の各行は、一つのQueryが3個のValueをどの比率で混ぜるかを示します。1行目は1番目と3番目へ約0.401ずつ、2番目へ約0.198を割り当てました。その結果、1行目の出力は3個のValueの重み付き平均 [6.017, 3.983] になります。

softmaxを行ごとに計算するため、各行の合計は1です。ただしAttention重みをそのまま、人間が説明する「重要度」や因果関係と同一視はできません。Valueの内容、後続の線形変換、残差接続、ほかの層も最終出力へ影響します。

なぜSelfと呼ぶのか

Query、Key、Valueの元が同じ系列だからSelf-Attentionと呼びます。原論文のエンコーダーでは、一つ前の層の出力からQ、K、Vを作り、各位置が同じ系列の全位置を参照します。

一方、エンコーダーとデコーダーの間のAttentionでは、QueryとKey・Valueの出どころが異なります。Attentionという計算がすべてSelf-Attentionなのではありません。

文章生成では未来を見せない

次トークンを予測するデコーダー型の処理で、現在位置が後ろの正解トークンを参照できると、学習課題の答えを先に見てしまいます。そのため未来側のスコアをsoftmax前に無効化する因果マスクを使います。

原論文のデコーダーも、現在位置より後ろへAttentionを向けられないようにマスクしています。推論時にはまだ未来のトークン自体が存在しませんが、訓練では系列をまとめて計算するため、この制約が必要です。

一つのAttentionだけではない

実際のTransformerでは、複数のAttentionヘッドを並行して計算するMulti-Head Attentionが使われます。ヘッドごとに異なる射影を持つため、同じ入力から異なる関係を捉えられます。各ヘッドの出力を結合し、もう一度線形変換して層の出力にします。

今回の例は一つのヘッドと、すでに用意されたQ、K、Vだけに絞りました。次にQuery・Key・Valueを入力から作る過程を分けて見ると、Attention重みが固定の規則ではなく、学習された射影によって変わることが分かります。

講座の前後記事

参照資料