Query、Key、Value
「まあ、そうです。従来のコンピュータは文法に従わない命令を与えられたり、保持していないデータを要求されたときには、エラーを返すだけでした」
「『'ls'は、操作可能なプログラムまたはバッチ ファイルとして認識されていません。』、みたいなやつだな」マルコが言った。
「そのとおりです。Windowsのコマンドプロンプトに UNIX の ls コマンドを投入すれば、そうなりますね」
「ところが GPT は、知らない、あるいは文法として不明確なものを入力しても、なにかしらを返してきます。ここが従来のコンピューターとは大きく異なるところです」
「それがハルシネーション(幻覚)の原因なの」
「ハルシネーションの一因ではあります。それ以外にも、適切ではない文章を学習してしまった場合にも、おかしな回答を返すこともあります」
「デマを大量に学習させられたりすれば、おかしなことを返すようにもなるだろうな」
「そのとおりです。ともかく、なぜそのように、なにがしかを返してくるのか、内部を具体的にみていきましょう」
「これまでに見てきたように、 GPT は入力された文章をトークンID列に変換し、さらに『埋め込み行列』を参照して8192個の実数列 X に変換します」
そして、それをq、k、vのベクトルに写像するのです。写像はマルチアテンションになりますが、ここでは、そのうちの一つだけを見ることにしましょう」
「Xは入力文字列を変換した行列、Wq、Wk、Wvは、ウェイト・パラメータの行列です。それぞれが、このような形をしています」
「Wqは、これだけでも50万以上の実数を持っていて、Xの写像qを作りますが、やっていることは掛け算と足し算だけです」
「これは非常に大きな計算になるので、もっと次元数を少なくして、同じことを表計算ソフトでやってみましょう」
「いま、『吾』『輩』『は』『猫』という文字を入れたとします。4トークンです。先ほどやったように、ここから X ベクトルの束ができます」
原文 →トークンID → Xベクトル
『吾』 → 100 → (3.04. -0.21, 0.09 …… , 1.57, 2,98)
『輩』 → 200 → (1.57. 0.29, -0.58 …… , 3.00, -1,39)
『は』 → 5 → ……
『猫』 → 1234 → ……
これをq、k、vベクトルに変換します。3つのウェイト行列Wq,、Wk、Wv はそれぞれ異なっていますから、元は同じ X ベクトルでも、異なる写像ができます。なお、q、k、v は実際の『生成Ai』では64次元ですが、ここでは理解しやすいように2次元とします」
◆qベクトル束
原文 →トークンID → Xベクトル → qベクトル束
『吾』 → 100 → (3.04. -0.21, 0.09 …… , 1.57, 2,98) → (1.7, 0.5)
『輩』 → 200 → (1.57. 0.29, -0.58 …… , 3.00, -1,39) → (-0.1, 2.3)
『は』 → 5 → …… → (1.1, 1,3)
『猫』 → 1234 → …… → (2.7, 0.9)
◆kベクトル束
原文 →トークンID → Xベクトル → kベクトル束
『吾』 → 100 → (3.04. -0.21, 0.09 …… , 1.57, 2,98) → (1.5, 0.0)
『輩』 → 200 → (1.57. 0.29, -0.58 …… , 3.00, -1,39) → (-0.3, -0.5)
『は』 → 5 → …… → (1.7, 0.5)
『猫』 → 1234 → …… → (-0.6, -0.1)
◆vベクトル束
原文 →トークンID → Xベクトル → vベクトル束
『吾』 → 100 → (3.04. -0.21, 0.09 …… , 1.57, 2,98) → (3.5, 2.1)
『輩』 → 200 → (1.57. 0.29, -0.58 …… , 3.00, -1,39) → (1.9, 4.2)
『は』 → 5 → …… → (2.7, 3.9)
『猫』 → 1234 → …… → (0.9, 5.6)
「です。おなじ X の写像ですが、それぞれ異なります」
「そうね」
「表計算ソフトにこれらの数字を入れてやります。いま、最初の『吾』をqベクトルとして与えます。qは(1.2, 0.5)ですよね。これに対して、kベクトルの束、vベクトルの束によるアテンションの計算はこうなります」
「『吾』のqベクトルは (1.2, 0.6) でした。この q に対する各 k の類似度を計算すると、『は』のスコアが最も高く、次いで『吾』が高くなります。なので 「softmax」列を見ると、『は』が一番大きく 0.57 で、次が『吾』の 0.37 です。確かに、一番に、『吾』、次に『は』を重視しているようです」
「そのようね。でも『輩』、も『猫』も、まったく無視しているわけでもなさそうね、0.03は0じゃないわ」
「それは、とっても大事な事です。そして「softmax」列のそれぞれの重みを、vベクトル束に掛けて合計したところがピンク色の出力です。これが self-Attention の出力(2.91, 3.30)です」
「結果は、『は』のバリュー(2.7, 3.9) に近いけど、『吾』の影響も少しある、ということね」
「そうです。『ハードなディクショナリ』だったら、一致するものがなければ、エラーですが、 self-Attention は、近い物をいくつか見つけてきて、それに対応すバリューを、重みに応じて加重平均する計算(どれくらい近いかの重み計算)を行って、出力としているのです」
「実際には、Attentionはマルチヘッドで複数が並列に接続され、さらにそれにFFN(Feed-Forward Network)という非線形層が続きます」
「そして、マルチヘッドアテンションとFFNが、いくつも積み重なったマルチレイヤになっています」
「けれども、この表計算ソフトによる実験においては、『出力』とよんでいる(2.91, 3.30)ひとつだけが『生成AI』の回答だと思ってください。そして(2.91, 3.30)が『正解』と比較され、差を元に損失関数、つまりLossが計算されて、バックプロパゲーションされます」
「入力された文字列から、一つのクエリとキーの束、それに対応するバリューの束が作られて、回答の先頭の一つをつくる。それを4つのクエリーを入れ替えて、四回繰り返すのね」と、エレナが言った。
「そうです。すこし、クエリの数字を変えてみましょう。キーの『輩』とまったく同じ数字を入れてみます。(-0.3, -0.5)です」
「どうでしょう、『輩』のバリューは(1.9, 4.2)です。それに対して『出力』は (1.94, 4.28)となり、バリューに近い形になりました。この場合、softmax が0.37 と小さいので偶然に近いのですが、それでも『ハードなディクショナリ』に近い結果を出しています」
「それで、『猫っぽい生き物』ってなっちゃうわけね」ミアが言う。よほど気に入ったようだ。
「これだけで、そうなるわけではないと思いますが、少なくとも単語と単語の間の関係性が、このような形で数値化されていく、ということは分かっていただけると思います」アレックスが言った。
「そのq, k, v、つまり q は dic['猫']の『猫』だ。kとv はdicの中の {'猫':1234} のそれぞれ、左と右だ」マルコが言う。
「そのとおりです」
「しかし、ここまでの説明では k:v という構造はなかった。どうやって k:v という構造ができたんだ。プログラムはしていないはずだ」マルコが尋ねる。
「そのとおりです。 q → k:v という構造は、『次の文字を当てる』という学習のなかで、自然にできあがっていきます」
「そうなのか、それは不思議だな」
本文中のEXCELによる計算例は、以下を参考にしています。
「ゼロから作るDeep Learning 6」初版 45ページ 図2-2




