はじめに
LLMとは何でしょうか。
定義を調べれば、すぐにそれらしい説明は見つかります。
しかし今回は、先に答えを調べるのではなく、
実際にLLMを動かして、観察できた事実から考えてみる
ことにします。
最初の問いは単純です。
LLMとは何か?
まだ内部の仕組みについては考えません。
まず、文字を入力すると何が起きるのかを観察します。
実験環境
今回使用したモデル:
Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf
実行環境:
llama.cpp / llama-server
ローカルの llama-server は以下で待ち受けています。
localhost:8080
実験
LLMに次の文字列を与えます。
日本の首都はどこ?
予想としては、
東京です。
のような回答が返ってくることを期待します。
実際に実行したコマンドはこちらです。
curl -N http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": "日本の首都はどこ?",
"stream": true,
"n_predict": 30,
"temperature": 0
}'
条件は次の通りです。
prompt = 日本の首都はどこ?
stream = true
n_predict = 30
temperature = 0
結果
出力は、一度にまとめて返ってきませんでした。
実際には次のように少しずつ返ってきます。
data: {"index":0,"content":"また","tokens":[52800],"stop":false,"id_slot":-1,"tokens_predicted":1,"tokens_evaluated":5}
data: {"index":0,"content":"、","tokens":[5373],"stop":false,"id_slot":-1,"tokens_predicted":2,"tokens_evaluated":5}
data: {"index":0,"content":"日本の","tokens":[131888],"stop":false,"id_slot":-1,"tokens_predicted":3,"tokens_evaluated":5}
data: {"index":0,"content":"国旗","tokens":[115799],"stop":false,"id_slot":-1,"tokens_predicted":4,"tokens_evaluated":5}
data: {"index":0,"content":"と","tokens":[19182],"stop":false,"id_slot":-1,"tokens_predicted":5,"tokens_evaluated":5}
data: {"index":0,"content":"国","tokens":[28404],"stop":false,"id_slot":-1,"tokens_predicted":6,"tokens_evaluated":5}
data: {"index":0,"content":"歌","tokens":[99678],"stop":false,"id_slot":-1,"tokens_predicted":7,"tokens_evaluated":5}
data: {"index":0,"content":"は何","tokens":[136045],"stop":false,"id_slot":-1,"tokens_predicted":8,"tokens_evaluated":5}
data: {"index":0,"content":"ですか","tokens":[131938],"stop":false,"id_slot":-1,"tokens_predicted":9,"tokens_evaluated":5}
data: {"index":0,"content":"?\n\n","tokens":[26850],"stop":false,"id_slot":-1,"tokens_predicted":10,"tokens_evaluated":5}
data: {"index":0,"content":"日本の","tokens":[131888],"stop":false,"id_slot":-1,"tokens_predicted":11,"tokens_evaluated":5}
data: {"index":0,"content":"首都","tokens":[106114],"stop":false,"id_slot":-1,"tokens_predicted":12,"tokens_evaluated":5}
data: {"index":0,"content":"は","tokens":[15322],"stop":false,"id_slot":-1,"tokens_predicted":13,"tokens_evaluated":5}
data: {"index":0,"content":"東","tokens":[102356],"stop":false,"id_slot":-1,"tokens_predicted":14,"tokens_evaluated":5}
data: {"index":0,"content":"京","tokens":[46553],"stop":false,"id_slot":-1,"tokens_predicted":15,"tokens_evaluated":5}
data: {"index":0,"content":"です","tokens":[37541],"stop":false,"id_slot":-1,"tokens_predicted":16,"tokens_evaluated":5}
data: {"index":0,"content":"。","tokens":[1773],"stop":false,"id_slot":-1,"tokens_predicted":17,"tokens_evaluated":5}
data: {"index":0,"content":"日本の","tokens":[131888],"stop":false,"id_slot":-1,"tokens_predicted":18,"tokens_evaluated":5}
data: {"index":0,"content":"国旗","tokens":[115799],"stop":false,"id_slot":-1,"tokens_predicted":19,"tokens_evaluated":5}
data: {"index":0,"content":"は","tokens":[15322],"stop":false,"id_slot":-1,"tokens_predicted":20,"tokens_evaluated":5}
data: {"index":0,"content":"「","tokens":[12881],"stop":false,"id_slot":-1,"tokens_predicted":21,"tokens_evaluated":5}
data: {"index":0,"content":"日の","tokens":[130635],"stop":false,"id_slot":-1,"tokens_predicted":22,"tokens_evaluated":5}
data: {"index":0,"content":"丸","tokens":[106256],"stop":false,"id_slot":-1,"tokens_predicted":23,"tokens_evaluated":5}
data: {"index":0,"content":"」","tokens":[10429],"stop":false,"id_slot":-1,"tokens_predicted":24,"tokens_evaluated":5}
data: {"index":0,"content":"、","tokens":[5373],"stop":false,"id_slot":-1,"tokens_predicted":25,"tokens_evaluated":5}
data: {"index":0,"content":"国","tokens":[28404],"stop":false,"id_slot":-1,"tokens_predicted":26,"tokens_evaluated":5}
data: {"index":0,"content":"歌","tokens":[99678],"stop":false,"id_slot":-1,"tokens_predicted":27,"tokens_evaluated":5}
data: {"index":0,"content":"は","tokens":[15322],"stop":false,"id_slot":-1,"tokens_predicted":28,"tokens_evaluated":5}
data: {"index":0,"content":"「","tokens":[12881],"stop":false,"id_slot":-1,"tokens_predicted":29,"tokens_evaluated":5}
data: {"index":0,"content":"君","tokens":[101956],"stop":false,"id_slot":-1,"tokens_predicted":30,"tokens_evaluated":5}
30個生成したところで終了しました。
tokens_predicted = 30
tokens_evaluated = 5
stop_type = limit
期待していた単純な、
東京です。
という回答とはかなり違います。
実際の出力をつなげると、
また、日本の国旗と国歌は何ですか?
日本の首都は東京です。
日本の国旗は「日の丸」、国歌は「君...
となりました。
なぜこのような結果になるのかは、今の時点ではまだ分かりません。
ここでは理由を推測せず、観察結果として残しておきます。
出力をもう少し詳しく見る
ストリーミングされたデータを見ると、興味深い情報が含まれていました。
例えば、
{"content":"東","tokens":[102356],"tokens_predicted":14}
次に、
{"content":"京","tokens":[46553],"tokens_predicted":15}
さらに、
{"content":"です","tokens":[37541],"tokens_predicted":16}
そして、
{"content":"。","tokens":[1773],"tokens_predicted":17}
と続きました。
整理すると、
| 生成順 | content | token |
|---|---|---|
| 14 | 東 | 102356 |
| 15 | 京 | 46553 |
| 16 | です | 37541 |
| 17 | 。 | 1773 |
となっています。
「東京」は一つではなかった
人間から見ると、
東京
は自然に一つの単語として見えます。
しかし今回のログでは、
東 → 102356
京 → 46553
となっていました。
一方で、
日本の → 131888
首都 → 106114
という結果もあります。
つまり、今回観察した範囲では、
日本の
は一つの単位として現れ、
東京
は、
東
京
に分かれて現れました。
さらに、
日の → 130635
丸 → 106256
という例もあります。
この単位はログ上では token と呼ばれています。
同じ文字には同じ数字が現れた
今回の短いログの中でも、同じ内容が複数回登場しています。
例えば、
日本の → 131888
は後でも再び、
日本の → 131888
として登場しました。
同様に、
国旗 → 115799
国旗 → 115799
や、
は → 15322
は → 15322
も確認できました。
少なくとも今回の実験では、
特定の文字列と特定の数字の対応
を観察することができました。
入力側にもTokenがある?
最後のログには、
tokens_evaluated = 5
とあります。
入力した文字列は、
日本の首都はどこ?
です。
つまり今回のログでは、この入力に対して 5 個の evaluated tokens が記録されています。
ログから推測すると、以下になります。
| tokens | content |
|---|---|
| 1 | 日本の |
| 2 | 首都 |
| 3 | は |
| 4 | どこ |
| 5 | ? |
一方、出力側では、
tokens_predicted = 1
tokens_predicted = 2
tokens_predicted = 3
...
tokens_predicted = 30
と増えていきました。
観察したものをそのまま並べると、
日本の首都はどこ?
↓
tokens_evaluated = 5
↓
tokens_predicted = 1
↓
tokens_predicted = 2
↓
tokens_predicted = 3
↓
...
↓
tokens_predicted = 30
となります。
今回分かったこと
今回の実験から直接観察できたのは、次のような動きです。
文字を入力
↓
tokens_evaluated = 5
↓
新しい内容が少しずつ出力される
↓
content と token ID が現れる
↓
tokens_predicted が増えていく
↓
最終的に文章になる
例えば、
東 → 102356
京 → 46553
です → 37541
。 → 1773
という対応を実際のログから確認できました。
また、
日本の首都はどこ?
と入力したからといって、単純に、
東京です。
だけが返ってくるわけではないことも確認できました。
しかし、まだ何も説明できていない
ここまで観察すると、むしろ疑問が増えました。
Tokenとは何なのか?
なぜ文字に、
102356
46553
37541
のような数字が対応しているのでしょうか。
そして、なぜ、
東京
は、
東
京
に分かれるのに、
日本の
は一つなのでしょうか。
さらに大きな疑問があります。
なぜ次に「東」が出て、その次に「京」が出たのでしょうか?
今回の実験では、それを説明するものはまだ観察できていません。
そしてもう一つ。
なぜ「日本の首都はどこ?」と入力したのに、予想していなかった国旗や国歌の話まで生成されたのでしょうか?
これもまだ分かりません。
まとめ
最初の問いは、
LLMとは何か?
でした。
今回、一度の実験だけでその答えが分かったわけではありません。
ただし、最初は、
質問
↓
LLM
↓
回答
程度にしか見えていなかったものから、
実際のログを観察することで、
文字
↓
Tokenらしき単位
↓
数字のID
↓
少しずつ新しい内容が生成される
↓
文章になる
という、もう一段内側の現象を見ることができました。
そして新しい疑問が残りました。
Tokenとは何か?
なぜ文字が数字になるのか?
なぜTokenの区切り方が違うのか?
なぜ次に生成するTokenを決められるのか?
なぜ予想外の文章まで生成されたのか?
まだ答えは調べません。
次も、この実験から生まれた疑問の一つを選び、実際に観察できる方法を考えてみます。
Experiment 001 — 完了