トークン化
1. BPE によるトークン化
基本的な流れ
BPE はテキストを 単語単位ではなく隣接ペアの頻度 でマージします。 初期状態では各文字(バイト)が1つのトークンです。
入力: t h i s _ i s _ a _ p e n
1. "is" が2回出現 → 最頻出ペア → ID 256 = "is" にマージ
2. t h [is] _ [is] _ a _ p e n
3. 以降も頻出ペアを繰り返しマージ
ID の割り当て
| 範囲 | 内容 |
|---|---|
| ID 0 〜 255 | 基本バイト(固定) |
| ID 256 〜 N | BPE マージで追加されたトークン |
2. UTF-8 によるバイト区別
UTF-8 は Unicode (コードポイント)を 1 - 3 バイトの可変長で符号化します。
UTF-8 はバイトの先頭ビットで役割が重複しない設計になっています。
| バイト種別 | ビットパターン | 範囲 |
|---|---|---|
| ASCII 1バイト文字 | 0xxxxxxx |
0x00〜0x7F |
| 先頭バイト(2バイト文字) | 110xxxxx |
0xC0〜0xDF |
| 先頭バイト(3バイト文字) | 1110xxxx |
0xE0〜0xEF |
| 先頭バイト(4バイト文字) | 11110xxx |
0xF0〜0xF7 |
| 継続バイト | 10xxxxxx |
0x80〜0xBF |
例:cat vs 猫
"cat"
c = 0x63 → 01100011(1バイト文字)
a = 0x61 → 01100001(1バイト文字)
t = 0x74 → 01110100(1バイト文字)
"猫"
0xE7 → 11100111(3バイト文字の先頭)
0xAC → 10101100(継続バイト)
0xAB → 10101011(継続バイト)
※ 0x は 16 進数表記を表します
3つの範囲が完全に重複しないため、BPE がバイト列を扱っても混同されないません。
3. トークン ID が Embedding に渡るまで
入力テキスト
↓
1. トークナイザー( BPE )
テキスト → トークン ID 列
例: "猫は可愛い" → [42567, 11245, 98, 334]
↓
2. Embedding レイヤー
トークン ID → 高次元ベクトル
例: 42567 → [0.23, -0.41, 0.87, ...] (768次元など)
↓
3. Transformer ブロック
Embedding レイヤーの実体
語彙数 × 埋め込み次元 の巨大な参照テーブル(行列)です。 トークン ID は単なる 行番号(インデックス) として使われます。
役割の分離
| 役割 | 内容 |
|---|---|
| BPE | 「どの ID を割り振るか」を決める(学習前に決定) |
| Embedding | 「その ID がどんな意味か」を学習で獲得する |
4. 語彙数
$語彙数 = 256 + マージ回数$ です。
マージ回数は事前に決めるハイパーパラメータです。
| モデル | 語彙数 |
|---|---|
| GPT-2 | 50,257 |
| LLaMA 3 | 128,256 |
| cl100k(GPT-4) | 100,277 |
トレードオフ
| 語彙数 | メリット | デメリット |
|---|---|---|
| 大きい | 1トークンで長い単語を表現できる | Embedding テーブルが大きくなりメモリ増加 |
| 小さい | メモリ節約 | 1つの単語が多くのトークンに分割される |
5. 未知語の処理
学習時に存在しなかった語彙が推論時に入力された場合の優先順位:
入力文字列
↓
1. BPE の既知トークンに一致するか? → 一致すればそのID
↓ 一致しない
2. より短い BPE トークンに分解できるか? → 分解して ID 列
↓ それも無理
3. 1バイトずつ分解 → ID 0〜255 の基本バイト
Byte-level BPE であれば、どんな入力でも必ず 0〜255 のバイトに分解できるため未知語が原理的に発生しません。
ただし意味的には
| 状況 | 品質 |
|---|---|
| 既知トークン “東京” | 学習で意味を獲得済みなので良い |
| バイト分解 [0xE7, 0xB4, …] | 文脈から推測するしかないので悪い️ |
学習時に十分出現していた語彙ほど良い Embedding を持ちます。