表記について
この記事では、以下の表記に統一する。
| 表記 | この記事での意味 |
|---|---|
| Token | Tokenizer が文字列を分割した単位 |
| Token ID | Token を識別する整数値。Hello は 9707
|
token_embd.weight |
モデルが持つ weight の一つ |
n_vocab |
Token ID の総数。今回のモデルでは 151936
|
n_embd |
1つの Token ID に対応する値の数。今回は 2048
|
f32 |
32 bit 浮動小数点数 |
| Q4_K | weight の量子化形式 |
| dequantize | 量子化された値を浮動小数点値へ変換する処理 |
実験
前回までに、
"Hello"
↓ Tokenize
Token ID = 9707
まで確認した。
今回は llama-eval-callback を使い、その 9707 がモデルに入った直後を観察する。
/opt/llama.cpp/build-rocm/bin/llama-eval-callback \
-m /data/models/Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf \
-p "Hello" \
-n 1
入力は、
number of input tokens = 1
9707
その直後に、
embd = (f32)
GET_ROWS(
token_embd.weight{2048, 151936, 1, 1},
inp_tokens{1, 1, 1, 1}
) = {2048, 1, 1, 1}
が観察できた。
つまり、
Token ID = 9707
↓
GET_ROWS(...)
↓
2048 個の f32
何が起きているのか、callback とソースコードをつなげて整理する。
ここまでの流れ
今回の疑問:
Token ID は LLM に入ると、最初に何になるのか?
"Hello"
│
│ Tokenize
▼
Token ID = 9707
│
│ Q:9707 は何?
│ A:"Hello" を識別する Token ID。
│
▼
┌─────────────────────────────────────────────┐
│ callback │
│ │
│ number of input tokens = 1 │
│ 9707 │
│ │
│ embd = (f32) │
│ GET_ROWS( │
│ token_embd.weight{2048,151936,1,1}, │
│ inp_tokens{1,1,1,1} │
│ ) = {2048,1,1,1} │
└──────────────────┬──────────────────────────┘
│
│ Q:inp_tokens は何?
│ A:モデルに渡された Token ID。
│ 今回は 9707。
▼
inp_tokens
┌───────┐
│ 9707 │
└───┬───┘
│
│ Q:token_embd.weight は何?
│ A:モデルが持つ weight の一つ。
▼
┌───────────────────────────┐
│ token_embd.weight │
│ {2048,151936} │
└─────────────┬─────────────┘
│
│ Q:151936 は何?
│ A:n_vocab。
│ Token ID の総数。
│
│ Q:2048 は何?
│ A:n_embd。
│ 1つの Token ID に
│ 対応する値の数。
▼
ggml_get_rows(ctx, a, b)
│
│ ctx → GGML context
│ 今回は追わない
│
│ a → token_embd.weight
│ b → inp_tokens
▼
src0 = token_embd.weight
src1 = inp_tokens
│
│ src1 から int32 を取得
▼
i01 = 9707
│
│ Q:9707 はここで何をする?
│ A:token_embd.weight から
│ 取得する位置を指定する。
▼
token_embd.weight
Token ID
────────────────────────
0 → [2048 個]
1 → [2048 個]
...
9707 → [2048 個] ← 今回
...
151935 → [2048 個]
│
│ GET_ROWS
▼
Token ID 9707 に対応するデータ
│
│ Q4_K
│
│ dequantize
▼
2048 個の f32
│
▼
┌─────────────────────────────────────────────┐
│ callback │
│ │
│ embd = (f32) {2048,1,1,1} │
│ │
│ [-0.0021, 0.0273, -0.0070, ..., │
│ -0.0360, 0.0022, 0.0022] │
└─────────────────────────────────────────────┘
用語メモ
token_embd.weight
モデル全体の weight ではなく、モデルが持つ weight の一つ。
今回のモデルでは、
n_vocab = 151936
n_embd = 2048
なので、人間向けに単純化すると、
Token ID 0 → 2048 個の値
Token ID 1 → 2048 個の値
...
Token ID 9707 → 2048 個の値
...
Token ID 151935 → 2048 個の値
と考えられる。
ctx
ggml_get_rows(ctx, a, b) の GGML context。
今回調べている「Token ID 9707 がどのデータを取得するか」には直接関係しないため、追わない。
Q4_K
weight を小さく保存するための量子化形式の一つ。
今回の token_embd.weight は Q4_K。
dequantize
量子化されたデータを、計算で使用する浮動小数点値へ変換する処理。
Q4_K
↓ dequantize
f32
f32
32-bit floating point(32 bit 浮動小数点数)。
今回の、
2048 個の f32
は、
2048 → 値の数
f32 → 値のデータ型
という意味。
今回の結果
"Hello"
↓
Token ID = 9707
↓
token_embd.weight
↓
Token ID 9707 に対応するデータを取得
↓
Q4_K → dequantize
↓
2048 個の f32
これで、
Token ID = 9707
↓
???
だった部分を、
Token ID = 9707
↓
2048 個の f32
まで観察できた。