トークンとトークナイザ
モデルは単語も文字も見ない。見るのはトークンID——バイトペア符号化(BPE)かその近縁で構築された、サブワード片の固定語彙を指す整数。トークン化はあらゆるものの関門である。プロンプト、応答、コンテキストウィンドウ、トークン毎秒メーター、API請求。トークンで考えれば、LLMの挙動に関する俗説の半分は溶ける。
語彙の作られ方
BPEは生バイトから始め、語彙が予算に達するまで最頻出の隣接ペアを繰り返しマージする。結果:よく使う単語は1トークン、珍しい単語は2〜5片、表現できない文字列はない——バイトフォールバックがタイポも絵文字も含めあらゆる文字列をカバーする。語彙サイズは約3万(旧世代モデル)から約25万(現代の多言語モデル)まで。
| トークナイザ | 語彙サイズ | 備考 |
|---|---|---|
| GPT-2 | 約50k | 英語・ウェブ中心 |
| Llama 2 | 約32k | 韓国語が痛いほど分割される |
| Llama 3 | 約128k | 大規模多言語アップグレード |
| Qwen 2 / 2.5 / 3 | 約151k | 強力なCJKマージ |
| Gemma | 約256k | 最大級の一般的語彙 |
大半が英語で学習されたトークナイザは、英語断片を激しくマージする。韓国語の音節ペアに有用なマージはほとんど回ってこない。モデル自体の韓国語の巧拙よりずっと前の段階で、語彙があなたの「韓国語税」を決定する。
実例:英語
"Tokenization turns text into pieces."
→ ["Token", "ization", " turns", " text", " into", " pieces", "."]
→ 37 characters → 7 tokens ≈ 5.3 chars/token
細部に注目。頻出単語「Token」は1片、接尾辞は独立、先行スペースは次のトークンに属する(「 turns」であって「turns」ではない)。だから「hello」「 hello」「Hello」は別トークン——スペース1つ、大文字1つ違うプロンプトは別のトークン列になる。
実例:韓国語
同じ文、より悪い経済性。ハングルは音節ブロックで書かれ、英語中心の語彙では韓国語のマージはまれ(以下の分割は妥当な例で、厳密ではない)。
"안녕하세요, 만나서 반가워요." # 17文字
強力な多言語語彙(約15万エントリ):
["안녕", "하세요", ",", " 만나", "서", " 반", "가", "워", "요", "."]
→ 約10トークン ≈ 1.7 chars/token
脆弱な語彙(約3.2万エントリ、字母単位の分割):
["안", "녕", "하", "세", "요", ",", " 만", "나", "서", ...]
→ 15+トークン ≈ 1.1 chars/token
英語は平均約4文字/トークン。韓国語は通常1〜1.5。同じ意味でトークンは3〜4倍。理由は次のとおり。
- CJKプロンプトはコンテキストウィンドウを警戒すべき速度で消費する——8kトークンのウィンドウに入る英語は韓国語の約3〜4倍。
- 同一tok/sでも韓国語は生成が「遅く感じる」。文あたりトークンが多く、トークンあたりのテキストが少ない。
- トークン課金APIでは韓国語は高くつき、韓国語で書かれた推論トレースも同じように予算を燃やす。
文字数えの質問が失敗する理由
「strawberryにrはいくつ?」と聞くと、大きなモデルでもつまずく。理由:モデルは文字を見ない。「strawberry」は概ね["straw", "berry"]——2個の整数として到着する。整数の中に文字数は入っていない。ページ番号にそのページの綴りが書かれていないのと同じ。モデルは記憶した綴りを暗唱するか段階的に推論するが、どちらも当てにならない。本当に必要なら、まず1行1文字で単語を綴らせてから行数を数えさせること。
語彙はモデルに同梱される
- トークナイザファイル(語彙、マージ)はリリースの一部。GGUFは埋め込み、safetensorsリポジトリは重みの隣に同梱。
- エンジン(llama.cpp、LM Studio、vLLMなど)は正しいトークナイザを自動ロードする。自分で選ぶことはない。
- トークン数はモデル相対。同じテキストが、あるファミリーの語彙で1,000トークン、別の語彙で1,150トークンになる。モデルをまたいでトークン予算をハードコードするな。
- 語彙の品質とモデルの品質は別の軸。3.2万語彙のモデルも韓国語を扱える(自分のトークンを理解している)。文あたりのトークンが増えるだけ。
すべてはトークン建て
| 関心事 | 単位 |
|---|---|
| コンテキストウィンドウ(4k → 128k+) | 入出力トークンの合計 |
| 生成速度 | トークン毎秒 |
| KVキャッシュメモリ | コンテキストのトークンごとに増大(KVキャッシュ) |
| API価格 | 100万トークンあたり |
| 出力長の上限 | 最大出力トークン数 |
| 推論・thinking予算 | 回答前に消費できるトークン数 |
| RAG用の文書埋め込み | チャンク化され100万トークンあたりで計数 |
特殊トークンとチャットテンプレート
テキスト片に加え、語彙は制御IDを予約する。シーケンス開始、シーケンス終了、ロールマーカー。チャットUIがメッセージを送るとき、生テキストは送らない——<|im_start|>user ... <|im_end|>のような特殊トークンをあなたの言葉の周囲に織り交ぜるチャットテンプレートを埋める。このラップがあるから、エンジンが報告するトークン数は常に本文より少し多く、異なるファミリーのテンプレートを混ぜるとモデルが混乱する。
見積もりルール:英語は文字数÷4。韓国語は文字数÷1.0〜1.5——最悪を仮定すること。シミュレータは約4文字/トークンの近似を使い英語で較正されている。韓国語は頭の中で割り増すこと。
自分でトークンを数える
pip install tiktoken
python -c "import tiktoken as t; e = t.get_encoding('o200k_base'); print(len(e.encode('hello world')))"
- tiktoken(https://github.com/openai/tiktoken)はOpenAI系のエンコーディングをカバー。他ファミリーは異なるが、英語では概ね±15%以内に収まる。
- Hugging Faceのモデルページにはトークナイザビューアがあり、トークナイザプレイグラウンドではテキストを貼って分割をライブで見られる。
- ローカルエンジンはログにプロンプトのトークン数を出力する。実際のプロンプトで1回確認し、あとは比率で見積もる。
関連: transformerの内部 · プリフィルとデコード · KVキャッシュ