Skip to main content

Tokenization

Tokenization は、生 text を LLM の入力単位 (token) に変換する処理です。語彙設計は、モデルの効率、多言語性能、コード理解、推論精度すべてに影響します。

なぜ単語単位ではないのか

単語単位の tokenizer には、

  • 語彙が爆発する (固有名詞、活用、新語)
  • OOV (out-of-vocabulary) が出る
  • 言語ごとに大きく違う

という問題があります。一方、文字単位は語彙は小さいですが、sequence が長くなりすぎます。

実用上は、subword tokenization (単語と文字の中間) が標準です。

BPE (Byte-Pair Encoding)

BPE は、文字 pair の中で頻出するものを順次 merge して、語彙を構築する手法です。

例:

"low" -> l o w
"lower" -> l o w e r
"newest"-> n e w e s t
"widest"-> w i d e s t

merges: (e,s) -> es, (es,t) -> est, ...

GPT-2、GPT-3、GPT-4、Llama などは BPE 系を使います。

Byte-Level BPE

GPT-2 以降は byte-level BPE が主流です。任意の Unicode を扱えるため、

  • 多言語
  • 絵文字、記号
  • バイナリに近い data

も losslessly に token 化できます。

SentencePiece

SentencePiece は、空白を陽に扱わず、生 text を直接 unigram LM や BPE で tokenize するライブラリです。日本語のように空白で区切らない言語にも自然に適用できます。Llama、T5、PaLM などで採用されています。

何が問題になるか

問題
多言語の効率差英語は 1 token / 単語、日本語や中国語は文字単位になりやすい
数値演算数字の分割の仕方で計算精度が変わる
コードインデント、空白の扱いが重要
長い識別子不自然な split が CoT を壊す
Prompt injection特殊 token の悪用

近年の Llama 3 / GPT-4o などは、語彙を大幅に拡張して多言語・コード効率を改善しています。

数式で見る BPE の merge

BPE は、corpus 内で最も頻繁に隣接して現れる token pair を順番に merge して語彙を作ります。現在の token sequence 集合を D\mathcal{D} とすると、次に merge する pair は次のように選べます。

(a,b)=argmax(a,b)countD(a,b)(a^*,b^*)=\arg\max_{(a,b)}\mathrm{count}_{\mathcal{D}}(a,b)

選ばれた pair (a,b)(a^*,b^*) は新しい token c=[ab]c=[a^*b^*] に置き換えられます。

(a,b)c(a^*,b^*)\to c

この式の気持ちは、「よく一緒に出る文字列を一つの単位にまとめることで、語彙数と sequence length の妥協点を作る」というものです。頻出語や頻出 subword は短い token 列になり、珍しい語は細かい token に分かれます。

Tokenization は、LLM の計算量にも直接影響します。文字列 ss の token 数を T(s)T(s) とすると、self-attention の計算量は概念的に次の量に比例します。

O(T(s)2d)O(T(s)^2d)

ここで、dd は hidden dimension です。同じ文章でも tokenizer によって T(s)T(s) が変わるため、多言語や code では tokenizer の効率が context length と推論コストに大きく効きます。

関連ページ

主なソース