第5章 Query、Key、Valueの生成:自己注意の三つの役割

こんにちは。ゆうせいです。

第4章までで、入力された文章は、各トークンが768次元のベクトルとして表現された状態になりました。ここから、Transformerの中核である自己注意の処理に入ります。Transformer Explainerを開くと、画面の中央に、Query、Key、Valueと書かれた三つの列が並んでいるはずです。同じ入力ベクトルから、なぜ三種類の異なるベクトルを作る必要があるのでしょうか。一種類では足りないのでしょうか。第5章では、この三つの役割の違いと、それがどのように生成されるのかを解説します。ここを曖昧にしたまま先に進むと、以降の章の計算がすべて意味不明になりますので、丁寧に進めます。

自己注意が解決しようとしている問題

Query、Key、Valueの説明に入る前に、そもそも自己注意が何のためにあるのかを確認します。

第3章の最後で触れたとおり、埋め込みの段階では、単語の意味は文脈と無関係に決まっています。「bank」という英単語は、金融機関を指す場合も、川の土手を指す場合もありますが、埋め込みの段階では、どちらの意味かを区別できません。

「I went to the bank to deposit money」という文であれば、「deposit」や「money」という周辺の単語から、金融機関の意味だと判断できます。

つまり、あるトークンの意味を確定させるには、周囲のどのトークンを参照すべきかを判断し、その情報を取り込む必要があります。

この「どのトークンを参照すべきか」を計算し、「実際にその情報を取り込む」処理が、自己注意です。

Transformerの中核となる革新は、この自己注意機構にあります。系列全体を処理し、長距離の依存関係を、従来のアーキテクチャよりも効果的に捉えられるようにするものです。

三つの役割を、図書館の検索に例える

Query、Key、Valueという名前は、情報検索の分野から借りてきたものです。図書館で本を探す場面に例えると、役割の違いが理解しやすくなります。

Query(クエリ、問い合わせ)は、「私はこういう情報を探している」という検索条件です。図書館の検索窓に入力するキーワードにあたります。

Key(キー、見出し)は、「この本はこういう内容です」という目印です。本の背表紙に書かれたタイトルや、カードカタログの見出しにあたります。

Value(バリュー、中身)は、実際に取り出される情報の中身です。本そのものの内容にあたります。

検索の手順は、次のようになります。

まず、自分のQuery(探している条件)と、並んでいる本のKey(見出し)を照らし合わせます。

次に、一致度の高い本ほど、強く注目します。

最後に、注目した本のValue(中身)を、注目度に応じた重みをつけて取り込みます。

自己注意も、まったく同じ手順で動いています。

自己注意における三つの役割

図書館の例を、実際のトークンの処理に置き換えます。

各トークンは、次の三つの役割を同時に果たします。

第一に、自分が情報を集める側として、「私は今、こういう情報を必要としている」というQueryを出します。

第二に、他のトークンから参照される側として、「私はこういう情報を持っています」というKeyを提示します。

第三に、参照されたときに実際に渡す情報として、Valueを保持します。

具体例で確認します。

「The animal didn't cross the street because it was too tired」という文における「it」というトークンを考えます。

「it」は代名詞であり、それ自体には具体的な意味がありません。何を指しているのかを知る必要があります。

このとき、「it」のQueryは、おおよそ「私が指している名詞を探している」という内容になります。

一方、「animal」のKeyは、「私は名詞であり、生物である」という内容を持っています。「street」のKeyは、「私は名詞であり、場所である」という内容を持っています。

「it」のQueryと、それぞれのKeyを照合すると、「tired(疲れている)」という文脈から、生物である「animal」のほうが一致度が高いと判断されます。

そして、「animal」のValue、つまり「animal」が持っている実際の意味情報が、「it」のベクトルに強く取り込まれます。

この処理により、「it」のベクトルは、単なる代名詞のベクトルから、「animalを指している代名詞」という文脈を反映したベクトルへと更新されます。

なぜ三種類に分ける必要があるのか

ここで、多くの初学者が抱く疑問に答えます。

「入力のベクトルをそのまま使って、トークンどうしの類似度を計算すればよいのではないか」という疑問です。

一種類では表現できないこと

入力ベクトルをそのまま使うと、次の問題が起きます。

一つ目の問題は、「探している情報」と「持っている情報」が、必ずしも同じではないという点です。

「it」というトークンが探しているのは、名詞、特に生物を指す名詞です。しかし、「it」自身が持っている情報は、代名詞であるという情報です。この二つは、まったく異なります。

一つのベクトルで両方を表現しようとすると、どちらかが犠牲になります。QueryとKeyを分けることで、「何を探しているか」と「何を持っているか」を、独立に表現できるようになります。

二つ目の問題は、「照合に使う情報」と「実際に渡す情報」も、必ずしも同じではないという点です。

図書館の例で言えば、本の見出しと本の中身は別物です。見出しは検索のために簡潔である必要があり、中身は詳細である必要があります。

KeyとValueを分けることで、「見つけてもらうための目印」と「見つけられたときに渡す実質的な情報」を、それぞれ最適な形で表現できます。

三つ目の問題は、自分自身との類似度が常に最大になってしまう点です。

入力ベクトルをそのまま使って類似度を計算すると、あるトークンと自分自身の類似度は、必ず最大になります。同じベクトルどうしですから、当然です。

これでは、すべてのトークンが自分自身にばかり注目し、周囲の情報を取り込めなくなります。QueryとKeyを別々の変換で作ることで、この問題を回避できます。

Query、Key、Valueはどう生成されるのか

三種類のベクトルは、いずれも、同じ入力ベクトルから、それぞれ異なる行列を掛けることで生成されます。

Q = X W_Q

K = X W_K

V = X W_V

Xは入力ベクトル(トークン埋め込みと位置埋め込みを足したもの)です。

W_QW_KW_V は、それぞれQuery、Key、Value を作るための重み行列です。これらは学習によって獲得されるパラメータです。

処理としては、単なる線形変換(行列の掛け算)にすぎません。同じ入力に、異なる三つの行列を掛けることで、三つの異なる「視点」からのベクトルが得られる、という仕組みです。

三つの行列が学習で獲得するもの

W_Q は、「入力ベクトルから、探すべき情報の条件を抽出する」変換を学習します。

W_K は、「入力ベクトルから、他者に提示すべき目印を抽出する」変換を学習します。

W_V は、「入力ベクトルから、実際に渡すべき情報の中身を抽出する」変換を学習します。

これらの変換ルールは、人間が設計するのではなく、大量のテキストで次のトークンを予測する訓練を通じて、自動的に獲得されます。

行列の大きさとパラメータ数

GPT-2(small)では、これらの行列は、いずれも768行768列です。

入力の768次元を、同じ768次元に変換する形です。次元数が変わらないため、一見すると何もしていないように見えますが、値の並びは大きく変わっています。空間の中で、ベクトルを回転させたり伸縮させたりしているイメージです。

パラメータ数を計算します。

1つの行列につき、次のパラメータ数です。

768 \times 768 = 589824

三つの行列を合わせると、次のようになります。

589824 \times 3 = 1769472

約177万個です。これが、1つのTransformerブロックあたりの数です。GPT-2(small)にはブロックが12個ありますから、モデル全体では、この12倍にあたる約2124万個のパラメータが、Query、Key、Valueの生成に使われていることになります。

Transformer Explainerで観察できること

Transformer Explainerでは、入力ベクトルからQuery、Key、Valueが生成される様子が、三つの列として可視化されています。

次の点を確認してみてください。

一つ目は、三つのベクトルが、いずれも同じ入力から作られているにもかかわらず、値がまったく異なるという点です。同じ材料に、異なる変換を施した結果であることが、視覚的に確認できます。

二つ目は、トークンの数だけ、Query、Key、Valueがそれぞれ存在するという点です。5トークンの文章を入力すれば、Queryが5本、Keyが5本、Valueが5本、生成されます。

三つ目は、この段階では、まだトークンどうしの相互作用が起きていないという点です。各トークンは、それぞれ独立に三つのベクトルへ変換されているだけです。相互作用が始まるのは、次章で扱う内積の計算からです。

初学者がつまずきやすい点

つまずき1:Query、Key、Valueが別々の入力から来ると誤解する

自己注意(Self-Attention)の「自己」という言葉は、Query、Key、Valueがすべて同じ入力系列から作られることを意味しています。

一方、機械翻訳などで使われる交差注意(Cross-Attention)では、Queryは翻訳先の系列から、KeyとValueは翻訳元の系列から作られます。

GPT-2で使われているのは、自己注意のほうです。すべて同じ文章から作られている、と押さえてください。

つまずき2:三つの名前を、意味のある英単語として深読みしすぎる

Query、Key、Valueという名前は、情報検索やデータベースの用語からの借用です。データベースのキーバリューストアと、名前は同じですが、動作はまったく異なります。

データベースの検索では、キーが完全に一致したものだけを取り出します。自己注意では、すべてのKeyとの一致度を計算し、その度合いに応じて、すべてのValueを重み付きで合成します。

「完全一致ではなく、重み付きの合成である」という点が、決定的な違いです。この点は、第9章で詳しく扱います。

つまずき3:ベクトルの次元数が変わると思い込む

「Queryは検索条件だから、短いベクトルなのではないか」といった直感を持つ人がいますが、実際には、入力と同じ768次元です。

ただし、次章以降で扱うマルチヘッドの仕組みでは、この768次元が複数のヘッドに分割されます。第10章で詳しく説明しますが、GPT-2(small)では12個のヘッドに分割されるため、1ヘッドあたりは64次元になります。

\frac{768}{12} = 64

この64という数字は、第7章のスケーリングで再び登場しますので、覚えておいてください。

演習

演習1:役割を対応させる

次の文における「it」というトークンについて、Query、Key、Valueがそれぞれどのような情報を持つべきかを、自分の言葉で説明してください。

「The trophy doesn't fit in the suitcase because it is too large」

なお、この文の「it」が何を指すかは、人間でも判断が分かれる有名な例です。「large(大きい)」という語から、どちらを指すと考えられるかも、あわせて検討してみてください。

演習2:Query、Key、Valueを実際に取り出す

from transformers import GPT2Tokenizer, GPT2Model
import torch

tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2Model.from_pretrained("gpt2")

# 最初のTransformerブロックのQKV生成用の重みを確認
# GPT-2では3つの行列がまとめて1つのLinear層になっている
qkv_weight = model.h[0].attn.c_attn.weight
print(f"QKV結合行列の形: {qkv_weight.shape}")

# 入力を用意
text = "The cat sat on the mat"
inputs = tokenizer(text, return_tensors="pt")

# 埋め込みまでを計算
token_emb = model.wte(inputs["input_ids"])
position_ids = torch.arange(inputs["input_ids"].shape[1]).unsqueeze(0)
position_emb = model.wpe(position_ids)
x = token_emb + position_emb

print(f"入力ベクトルの形: {x.shape}")

# QKVを生成
qkv = model.h[0].attn.c_attn(x)
print(f"QKV生成後の形: {qkv.shape}")

# 3分割してQ、K、Vに分ける
q, k, v = qkv.split(768, dim=2)
print(f"Queryの形: {q.shape}")
print(f"Keyの形: {k.shape}")
print(f"Valueの形: {v.shape}")

# 同じトークンのQ、K、Vが異なることを確認
cos = torch.nn.CosineSimilarity(dim=0)
print(f"\n最初のトークンの QとK の類似度: {cos(q[0][0], k[0][0]):.4f}")
print(f"最初のトークンの QとV の類似度: {cos(q[0][0], v[0][0]):.4f}")

演習の解答例と解説

演習1について、期待される回答は次のようなものです。

「it」のQueryは、「私が指し示している名詞を探している。特に、大きさに関する性質を持つ名詞を探している」という内容になるべきです。

各名詞のKeyは、「私は名詞であり、こういう性質を持つ」という目印を提示します。「trophy」は物体であり大きさを持つ、「suitcase」も容器であり大きさを持つ、という情報です。

Valueは、それぞれの名詞が持つ実際の意味情報です。

「large」という語から、この文の「it」は「trophy」を指すと解釈するのが自然です。トロフィーが大きすぎるからスーツケースに入らない、という因果関係になります。

この例は、ウィノグラード・スキーマと呼ばれる問題群の一つであり、常識的な推論が必要であることから、言語モデルの能力を測る題材としてよく使われます。「large」を「small」に変えると、指す対象が「suitcase」に変わる点も、あわせて考えてみてください。

演習2では、同じトークンから作られたQueryとKeyの類似度が、必ずしも高くないことが確認できます。これが、三種類に分ける意義を示す、直接的な証拠です。

講師向けの補足

この章は、Transformerの理解における最大の関門です。時間配分を厚めに取ってください。

図書館の例えは有効ですが、例えだけで終わらせないことが重要です。例えを説明した直後に、必ず実際の文章(「it」を含む文など)で、Query、Key、Valueが具体的に何にあたるかを対応づけてください。

受講者から「なぜ三つも必要なのか」という質問が出たら、それは良い兆候です。「一種類だとどうなるか」を一緒に考える時間を取ると、理解が深まります。特に、「自分自身との類似度が常に最大になってしまう」という問題は、実感を伴って納得されやすい説明です。

理解度を確認する問い

第5章の内容を、次の問いで確認してみてください。

Query、Key、Valueの三つの役割を、図書館での本の検索に例えて説明してみてください。

入力ベクトルをそのまま使って、トークンどうしの類似度を計算してはいけない理由を、三つ挙げてください。

Query、Key、Valueは、どのような計算によって生成されるでしょうか。

まとめ

自己注意は、あるトークンの意味を確定させるために、周囲のどのトークンを参照すべきかを判断し、その情報を取り込む仕組みです。

この処理のために、各トークンは、Query、Key、Valueという三つのベクトルを持ちます。Queryは「探している情報の条件」、Keyは「他者に提示する目印」、Valueは「実際に渡す情報の中身」を表します。

三種類に分ける理由は、「探している情報」と「持っている情報」が異なること、「照合に使う情報」と「渡す情報」が異なること、そして一種類では自分自身との類似度が常に最大になってしまうことの三点です。

三つのベクトルは、同じ入力ベクトルに、それぞれ異なる重み行列を掛けることで生成されます。GPT-2(small)では、いずれも768行768列の行列であり、1ブロックあたり約177万個のパラメータを持ちます。これらの変換ルールは、学習によって自動的に獲得されます。

次の第6章では、生成されたQueryとKeyを使って、トークンどうしの一致度を計算する処理を扱います。なぜ内積という計算で「似ている度合い」が測れるのか、その計算結果が何を意味するのかを解説します。

第6章に進む前に、Transformer Explainerで、Query、Key、Valueの三つの列を見比べてみてください。同じトークンの行であっても、三つの値がまったく異なっていることを、目で確認しておくと、次章以降の計算がつながりやすくなります。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。