前回まで Token と Token ID についていくつか実験してきた。
その結果を見ていて、自分の中でこんなイメージができてきた。
Token と Token ID の関係は、辞書のようなものではないか。
Token ID は「何ページの何行目」のような番号で、Token はそこに書かれている内容。
ただし、Token は必ずしも単語とは限らない。そして LLM は、ユーザーが入力した普通の文字を Token ID に変換し、その Token ID を使って予測する。
予測した結果も Token ID として出てきて、最後に何らかの方法で Token に戻し、人間が読める文字として表示しているのではないか。
図にすると、今の自分のイメージはこんな感じだ。
ユーザーが入力した文字
↓
Token ID
↓
LLM が予測
↓
Token ID
↓
何らかの変換
↓
Token / 文字
↓
ユーザーに表示
もちろん、これはまだ自分の想像だ。
今までの実験で分かったことと、まだ想像しているだけのことが混ざっている。
そこで今回は、まずこの仮説を、これまでの実験結果と実際の動作から確かめてみることにした。
まず、今までに分かっていること
"Hello" を Tokenize すると、
"Hello" → 9707
になった。
さらに GGUF の中を覗いてみると、Tokenizer の Vocabulary に、
9707 'Hello'
という対応も見つかった。
つまり、少なくともここまでは実際に観察できている。
"Hello" → 9707
では、自分の「辞書と番号」というイメージが近いのであれば、逆に 9707 から "Hello" という対応も見えるのだろうか。
Token IDを直接渡してみる
/completion の prompt に "Hello" ではなく、Token ID 9707 を直接渡してみた。
curl http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": [9707],
"n_predict": 20,
"temperature": 0,
"cache_prompt": false,
"return_tokens": true
}'
レスポンスを見ると、
{
...
"tokens_evaluated": 1,
...
"prompt": "Hello"
}
となった。
入力したのは、
9707
だけだった。
それなのに prompt には、
"Hello"
と表示されている。
9707 → "Hello"
という逆方向の対応も見えた。
ただ、これだけでは 9707 と "Hello" だけの話かもしれない。
そこで、以前の実験ですでに対応を確認している別の Token ID でも試してみる。
別のToken IDでも試す
まず 16。
curl http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": [16],
"n_predict": 20,
"temperature": 0,
"cache_prompt": false,
"return_tokens": true
}'
結果:
{
...
"prompt": "1",
...
}
16 → "1"
次に 10。
curl http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": [10],
"n_predict": 20,
"temperature": 0,
"cache_prompt": false,
"return_tokens": true
}'
結果:
{
...
"prompt": "+",
...
}
10 → "+"
17 では、
curl http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": [17],
"n_predict": 20,
"temperature": 0,
"cache_prompt": false,
"return_tokens": true
}'
結果:
{
...
"prompt": "2",
...
}
17 → "2"
最後に、以前、
14990 'hello'
という対応を確認していた 14990 も試した。
curl http://localhost:8080/completion \
-H "Content-Type: application/json" \
-d '{
"prompt": [14990],
"n_predict": 20,
"temperature": 0,
"cache_prompt": false,
"return_tokens": true
}'
結果:
{
...
"prompt": "hello",
...
}
14990 → "hello"
結果を並べてみる
今回の結果をまとめると、
| Token ID |
prompt に表示された内容 |
|---|---|
16 |
1 |
10 |
+ |
17 |
2 |
9707 |
Hello |
14990 |
hello |
となった。
前回までに確認した結果と合わせると、
"1" ↔ 16
"+" ↔ 10
"2" ↔ 17
"Hello" ↔ 9707
"hello" ↔ 14990
となる。
最初に考えた、
Token は辞書の内容、Token ID はその内容を指す番号のようなものではないか。
というイメージは、少なくとも今回試した範囲では、実験結果と矛盾しなかった。
ただし、「辞書」や「何ページの何行目」というのは、あくまで今の自分が理解するためのイメージだ。
でも、仮説の全部を確認できたわけではない
最初に立てた仮説をもう一度見る。
ユーザーが入力した文字
↓
Token ID
↓
LLM が予測
↓
Token ID
↓
何らかの変換
↓
Token / 文字
↓
ユーザーに表示
今回までの実験で、Token と Token ID の間に対応があることはかなり見えてきた。
しかし、
LLM は本当に Token ID を使って予測している?
これはまだ確認していない。
さらに、
予測結果も本当に Token ID なのか?
これもまだ確認していない。
そして今回、もう一つ気になることが出てきた。
Token ID を直接入力しただけなのに、
9707 → "Hello"
16 → "1"
10 → "+"
17 → "2"
14990 → "hello"
と、人間が読める文字が prompt に表示された。
最初の仮説では、
Token ID
↓
何らかの変換
↓
Token / 文字
という処理があるのではないかと考えていた。
今回の結果を見ると、確かにそのような「逆方向」の動きがどこかにありそうに見える。
ただし、今回は /completion が返した prompt を見ただけだ。
Token ID を Token に戻す処理そのものを確認したわけではない。
では、
Token ID から Token に戻す処理は、本当に存在するのだろうか?
次は、この疑問を確かめてみたい。