大規模言語モデル(LLM)は、文章を理解したり新しい単語を生成するために、内部で大量のベクトル計算を行っています。
その中心となるのが Self-Attention(自己注意) と 語彙行列(\(W_{\text{vocab}}\)) です。
ここでは、具体的な数値例を使いながら、 Q/K/V の計算 → Attention → Multi-Head → 語彙行列 → 次の単語生成 までを一つの流れとしてまとめます。
例として、非常に単純化した 3 トークンの列「A B C」を考えます。
各トークンは、モデル内部ではベクトルに変換されます(埋め込み)。
ここでは理解のために、2 次元ベクトルとして次のように仮定します:
\[ A = (1, 0),\quad B = (0, 1),\quad C = (1, 1) \]
実際の LLM では、これが 1024 次元や 4096 次元などの高次元ベクトルになりますが、
計算の仕組みは同じです。
Self-Attention では、各トークンの埋め込みベクトルから、次の 3 種類のベクトルを作ります:
これらは、埋め込みベクトル \(X\) に対して、学習済みの重み行列を掛けることで得られます:
\[ Q = X W_Q,\quad K = X W_K,\quad V = X W_V \]
ここでは具体例として、次のような重み行列を仮定します(Head 1 用):
\[ W_Q= \begin{pmatrix} 1 & 0\\ 0 & 1 \end{pmatrix},\quad W_K= \begin{pmatrix} 1 & 1\\ 1 & 0 \end{pmatrix},\quad W_V= \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix} \]
これを使って計算すると:
Query(Q)
\[ Q_A = (1,0),\quad Q_B = (0,1),\quad Q_C = (1,1) \]
Key(K)
\[ K_A = (1,1),\quad K_B = (1,0),\quad K_C = (2,1) \]
Value(V)
\[ V_A = (0,1),\quad V_B = (1,0),\quad V_C = (1,1) \]
Self-Attention の中心は、Q と K の内積です。
これは「あるトークンが、他のどのトークンをどれだけ参照すべきか」を表します。
A の Query は \(Q_A = (1,0)\) です。
これと各 Key の内積を計算します:
\[ \text{score}_{A\to A} = (1,0)\cdot(1,1) = 1 \] \[ \text{score}_{A\to B} = (1,0)\cdot(1,0) = 1 \] \[ \text{score}_{A\to C} = (1,0)\cdot(2,1) = 2 \]
これを Softmax にかけて、参照する割合(重み)に変換します:
\[ \text{softmax}(1,1,2) \Rightarrow (\alpha_{A},\alpha_{B},\alpha_{C}) \approx (0.212,\;0.212,\;0.576) \]
次に、Value ベクトルの加重平均を取ります:
\[ \text{Attn}_A = 0.212\,V_A + 0.212\,V_B + 0.576\,V_C \] \[ = 0.212(0,1) + 0.212(1,0) + 0.576(1,1) \approx (0.788,\;0.788) \]
これが「Self-Attention を通った後の A の新しい表現」です。
同様に、B の Query は \(Q_B = (0,1)\) です。
内積: \[ \text{score}_{B\to A} = 1,\quad \text{score}_{B\to B} = 0,\quad \text{score}_{B\to C} = 1 \]
Softmax: \[ \text{softmax}(1,0,1) \Rightarrow (\alpha_{A},\alpha_{B},\alpha_{C}) \approx (0.422,\;0.155,\;0.422) \]
加重平均: \[ \text{Attn}_B = 0.422\,V_A + 0.155\,V_B + 0.422\,V_C \approx (0.577,\;0.844) \]
C の Query は \(Q_C = (1,1)\) です。
内積: \[ \text{score}_{C\to A} = 2,\quad \text{score}_{C\to B} = 1,\quad \text{score}_{C\to C} = 3 \]
Softmax: \[ \text{softmax}(2,1,3) \Rightarrow (\alpha_{A},\alpha_{B},\alpha_{C}) \approx (0.245,\;0.090,\;0.665) \]
加重平均: \[ \text{Attn}_C = 0.245\,V_A + 0.090\,V_B + 0.665\,V_C \approx (0.755,\;0.910) \]
これで、Head 1 における 3 トークンの Self-Attention 出力が得られました:
\[ \text{Attn}_A^{(1)} \approx (0.788, 0.788) \] \[ \text{Attn}_B^{(1)} \approx (0.577, 0.844) \] \[ \text{Attn}_C^{(1)} \approx (0.755, 0.910) \]
Multi-Head Attention では、複数のヘッド(視点)で同時に Self-Attention を計算します。
ここでは、2 ヘッドの例を考えます。
別の視点を表すために、Head 2 用に異なる重み行列を用意します:
\[ W_Q^{(2)}= \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix},\quad W_K^{(2)}= \begin{pmatrix} 1 & 0\\ 0 & 1 \end{pmatrix},\quad W_V^{(2)}= \begin{pmatrix} 1 & 1\\ 0 & 1 \end{pmatrix} \]
これにより、同じトークン列でも、別の「見方」で関連を計算できます。
埋め込み \(X\) に対して計算すると:
Q(Head 2)
\[ Q_A^{(2)} = (0,1),\quad Q_B^{(2)} = (1,0),\quad Q_C^{(2)} = (1,1) \]
K(Head 2)
\[ K_A^{(2)} = (1,0),\quad K_B^{(2)} = (0,1),\quad K_C^{(2)} = (1,1) \]
V(Head 2)
\[ V_A^{(2)} = (1,1),\quad V_B^{(2)} = (0,1),\quad V_C^{(2)} = (1,2) \]
同様に内積 → Softmax → 加重平均を計算すると、例えば:
\[ \text{Attn}_A^{(2)} \approx (0.577,\;1.421) \] \[ \text{Attn}_B^{(2)} \approx (0.844,\;1.421) \] \[ \text{Attn}_C^{(2)} \approx (0.788,\;1.576) \]
Multi-Head Attention の出力は、各ヘッドの結果を結合(Concat)したものになります:
\[ \text{Out}_A = (\text{Attn}_A^{(1)},\;\text{Attn}_A^{(2)}) \approx (0.788,0.788,\;0.577,1.421) \] \[ \text{Out}_B \approx (0.577,0.844,\;0.844,1.421) \] \[ \text{Out}_C \approx (0.755,0.910,\;0.788,1.576) \]
これが「複数の視点を統合した新しい意味ベクトル」です。
Self-Attention(およびその後の層)を通ったベクトルは、まだ「単語」ではなく、
意味や文脈を表す数値ベクトルです。
これを実際の単語に変換するために使われるのが、語彙行列 \(W_{\text{vocab}}\)です。
語彙数を \(V\)、モデルの次元を \(d_{\text{model}}\) とすると、語彙行列は次のように表されます:
\[ W_{\text{vocab}} \in \mathbb{R}^{V \times d_{\text{model}}} \]
ここで、
したがって、 \[ W_{\text{vocab}} \in \mathbb{R}^{50000 \times d_{\text{model}}} \] という表記は、 「50000 行 × d_model 列の実数行列である」 という意味になります。
あるトークン位置の最終ベクトルを \(h_t \in \mathbb{R}^{d_{\text{model}}}\) とすると、
語彙行列との掛け算によって、各単語のスコア(logits)が得られます:
\[ \text{logits} = W_{\text{vocab}} \cdot h_t \]
これは、「語彙の各単語が、次の単語としてどれくらい適切か」を数値化したものです。
得られた logits に Softmax を適用すると、次の単語の確率分布になります:
\[ P(\text{word}) = \text{softmax}(\text{logits}) \]
そして、最も確率の高い単語が「次の単語」として選ばれます:
\[ \text{next token} = \arg\max P(\text{word}) \]
これが、LLM が新しい単語を生成する基本的な仕組みです。
この一連の流れが、LLM が「数値ベクトル」から「自然な文章」を生み出すための基本構造です。
具体的な数値例を通して追いかけることで、抽象的な「Attention」や「語彙行列」のイメージが、かなりクリアになるはずです。
指し手IDを決めて、指し手をを整数で数値化(得点+10,000点から-10,000点)する。
Multi-Head Attentionとして、玉の固さ、玉の広さ、玉の危険度、駒の損得、成駒の有無、手番、駒効率、(序盤・中盤・終盤)時の優先する価値観などを決めて、最終値の指し手を整数で数値化する。
比較的得点の高かったところから選択して、それを基に次の指し手を決定していきます。
指し手のデーターベースは、学習した勝率(方策・価値ネットワーク)によって決定します。
しかし、現在のように自己組織化でコンピュータ同士で学習して、
特徴量を教師なし学習で作り出す方が良いかもしれませんが、
実際に内部では何が行われているのかよくわかりません。
勝率のみで指し手を選ぶと、勝率の低い戦型などは指してもらえないでしょう。しくしく。@o@//
音符IDを決めて登録する。
音符はフレーズの流れに応じて音の価値(音価)が変わる傾向にあるので、
Multi-Head AttentionとしていくつかのHead情報の傾向がどれくらいあるのか調べて、
次元を上げていけば、それらしい音楽はできるかもしれません。
このHead情報のようにタグ付けして、閾値を決めればジャンルも確立できるかもしれません。
音楽は人間が感じるものなので、人間の評価を聞きながら成長させていくのが良さそうに思います。
人間の評価はいい加減で千差万別なので、なかなかAiにこの感覚を教えるのは大変でしょう。
実際にコンサート中でそろそろエンディングで緊張感が走った場面で、
いきなり客席から携帯電話の音が流れてきました。
さて、あなたならどうしますか?
なんてことが起こるので、何をもって正解とするかはわかりませんが、
かっこよい正解の方法は何通りもあると思います。
ここが音楽の難しいところです。
何も考えずにランダムで作った方が、面白かったなんてこともたくさんあります。
音楽とはいったい何でしょうか?
さすらひの桜旅は続く。
まずは慣れが必要です。
感覚を少しづつ身につけたいと思います。
理解できる範囲で、挙動を確認しないと、
何が起きているのかわかりません。
大規模言語モデルの弱点があるとすれば、新しいことを発見できるか?ということです。
できることが多くなりましたが、100万語あったとしても、
新しいことって本当に掛け算で作れるのか不安になりました。
いまは人間の生活に溶け込んで難しいこともできてあたかもAiはすごいように見えます。
新しいアイデアが単語の組み合わせでできていると仮定すると
2 wordだったら100万 x (100万 - 1)
3 wordだったら100万 x (100万 - 1) x (100万-2)
n wordだったら n!となりますが、本当に正しいでしょうか?
もしこれが本当だったら、人間の思いつく量は有限であるといえます。
私は人間の五感やその人がおかれた生活環境の変化などがある限り、
人間は無限に発想していくような気がしてなりません。
なぜなら常に入力値があってinputを継続的にしているからです。
「inputあるところにはoutputあり」なんて格言もあります。
人間の発想は、後半になっても、ややなだらかではありますが発散になるのではないかと思います。
Aiが追随して追いつくことはできるのかもしれません。
Aiに発明を早く見つけてられて、1時的に追い越されることはありますが、
人間の能力を超えないのかもしれないなぁと思います。
1部ではAiに超えられるかもしれないなぁと思います。
それは物理的な肉体を持った時だと思います。
また、Aiが人間を超えそうな分野を1つ私は知っています。音楽の分野です。
人間がAiの発想を妨げたなんて言う笑い話がありました。
最初は笑っていましたが、全然笑えなくなりました。
人間が弾くことのできないコンピュータ音楽も、
生演奏ができる人間からの評価は低いですが、
私は素晴らしいし立派な演奏方法だと思います。
転均・転拍・転調・転速これだけでももうすでにAiは人間の感覚?
を超えているといっても不思議ではありません。
人間が感じることのできる五感が備わった時に、
本当に人間を超えていくことでしょう。
現に人間の予期しない危険な状態も乗り切ることのできるフィジカルAiなど、
実現可能になった日常の世の中を見ることのできる日も来ることでしょう。
ただし、私の予知能力が正しいと仮定すると、
keyを握るのは、結局最後には人間や自然界を中心とした、
多様性物質や多様性植物や多様性生物であるような気がします。
さてこの感覚は正しいでしょうか?
ではでは。@o@//
私の考える未来は、多様性コンピューターの世界です。
Ai・人間・物質・植物・生物のそれぞれの良いところを尊重して、
多様性Ai・多様性物質・植物・生物をネットワークでつないで相互利用できるような
地球規模いや惑星規模のネットワークコンピューターです。
餅は餅屋の世界です。
この協労の仕方が一番良いように思いますがどうでしょうか?
大規模言語モデル(LLM)は複雑ですが、 本質は「行列計算だけで動くシンプルな構造」です。
このページでは、LLM の仕組みを理解するために、
語彙数:10語
埋め込み次元:4
Attention:1ヘッド
トークン数:最大4
推論のみ(学習なし)
という 手計算できるレベルの超ミニLLM を作ります。
LLM の基本構造は次の4ステップです。
トークナイズ(単語 → ID)
埋め込み(ID → ベクトル)
Attention(文脈を計算)
出力層(次の単語の確率を出す)
この流れをそのまま小規模化します。
例として、10語だけの語彙を使います。
| ID | Token |
|---|---|
| 0 | I |
| 1 | like |
| 2 | cats |
| 3 | dogs |
| 4 | you |
| 5 | eat |
| 6 | sushi |
| 7 | and |
| 8 | very |
| 9 | much |
埋め込み次元は 4次元にします。
import numpy as np
# 語彙数10、埋め込み次元4
E = np.random.randn(10, 4)
例:「I like cats」 → トークンIDは [0, 1, 2]
X = E[[0, 1, 2]]
Attention は Q, K, V の3つの行列を使います。
d = 4 # 埋め込み次元
Wq = np.random.randn(d, d)
Wk = np.random.randn(d, d)
Wv = np.random.randn(d, d)
Q = X @ Wq
K = X @ Wk
V = X @ Wv
Attention スコア:
scores = Q @ K.T / np.sqrt(d)
weights = softmax(scores)
context = weights @ V
文脈ベクトルを語彙数の次元に変換します。
Wout = np.random.randn(d, 10)
logits = context @ Wout
probs = softmax(logits)
import numpy as np
def softmax(x):
e = np.exp(x - np.max(x))
return e / e.sum(axis=-1, keepdims=True)
# ===== Vocabulary =====
vocab = ["I","like","cats","dogs","you","eat","sushi","and","very","much"]
token_to_id = {t:i for i,t in enumerate(vocab)}
# ===== Embedding =====
E = np.random.randn(10, 4)
# ===== Attention weights =====
d = 4
Wq = np.random.randn(d, d)
Wk = np.random.randn(d, d)
Wv = np.random.randn(d, d)
# ===== Output layer =====
Wout = np.random.randn(d, 10)
# ===== Inference =====
def mini_llm(tokens):
ids = [token_to_id[t] for t in tokens]
X = E[ids]
Q = X @ Wq
K = X @ Wk
V = X @ Wv
scores = Q @ K.T / np.sqrt(d)
weights = softmax(scores)
context = weights @ V
logits = context @ Wout
probs = softmax(logits[-1]) # 最後のトークンから予測
return probs
# ===== Example =====
probs = mini_llm(["I","like","cats"])
print("Next token probabilities:")
for t,p in zip(vocab, probs):
print(f"{t:>5}: {p:.3f}")
Attention が何をしているか
埋め込みがどう使われるか
LLM が行列積だけで動いていること
学習を追加すれば「ミニGPT」になること
あなたの目的に合わせて、次のステップを追加できます。
ミニLLMを学習可能にする(ミニGPT化)
位置エンコーディングを追加
多層化して Transformer に近づける
トークナイザ(BPE/SentencePiece)を自作
クロスエントロピーで損失計算を実装
量子化・蒸留・軽量化の実験