SentencePieceにおける語彙(ボキャブラリー)の構造・管理・最適化手法の総称で、語彙サイズの選定・特殊トークン設計・多言語バランス調整・語彙の分析と評価を包括的に扱う。
SentencePiece語彙管理は、トークナイザーが保持するサブワード語彙の構築・分析・最適化に関する一連のプラクティスを指す。語彙サイズの選定、特殊トークンの設計、多言語モデルにおける言語間バランスの調整、そして学習済み語彙の品質評価が含まれる。LLMの性能はトークナイザーの語彙品質に大きく依存するため、適切な語彙管理は不可欠である。
SentencePieceの学習により出力される.vocabファイルは、タブ区切りのテキスト形式で以下の構造を持つ:
<unk> 0
<s> 0
</s> 0
▁the -2.3456
▁a -3.4567
▁is -3.7891
ing -4.1234
▁機械 -8.9012
学習 -9.0123
各行は「サブワード(タブ)ログ確率」の形式で、確率が高い(値が0に近い)サブワードほど頻出する基本的なトークンである。
| モデル規模 | パラメータ数 | 推奨語彙サイズ | 理由 |
|---|---|---|---|
| Small | 100M〜500M | 16,000〜32,000 | 埋め込み層のパラメータ効率 |
| Medium | 1B〜7B | 32,000〜64,000 | カバレッジと効率のバランス |
| Large | 13B〜70B | 64,000〜128,000 | トークン効率の最大化 |
| XLarge | 70B以上 | 128,000〜256,000 | 多言語効率の最適化 |
| モデル | 語彙サイズ | 年 | 増加の理由 |
|---|---|---|---|
| BERT | 30,522 | 2018 | WordPiece、英語中心 |
| GPT-2 | 50,257 | 2019 | BPE、バイトレベル |
| T5 | 32,000 | 2019 | SentencePiece Unigram |
| LLaMA 1/2 | 32,000 | 2023 | SentencePiece BPE |
| Mistral | 32,000 | 2023 | LLaMA互換 |
| GPT-4 | 100,256 | 2023 | tiktoken、多言語拡張 |
| LLaMA 3 | 128,256 | 2024 | tiktoken、多言語大幅強化 |
| Gemma | 256,128 | 2024 | SentencePiece Unigram、多言語最大 |
2024年以降、語彙サイズは拡大傾向にある。これはモデルパラメータの増大により埋め込み層のコストが相対的に小さくなり、語彙拡張によるトークン効率改善のメリットが上回るためである。
import sentencepiece as spm
sp = spm.SentencePieceProcessor()
sp.load('model.model')
# 語彙サイズの確認
print(f'語彙サイズ: {sp.get_piece_size()}')
# 特定トークンの確認
print(f'ID→トークン: {sp.id_to_piece(100)}')
print(f'トークン→ID: {sp.piece_to_id("▁the")}')
# トークンのスコア(ログ確率)
print(f'スコア: {sp.get_score(100)}')
# 特殊トークンの確認
print(f'UNK ID: {sp.unk_id()}')
print(f'BOS ID: {sp.bos_id()}')
print(f'EOS ID: {sp.eos_id()}')
print(f'PAD ID: {sp.pad_id()}')
テスト用コーパスに対する語彙のカバレッジを以下の指標で評価できる:
| 指標 | 計算方法 | 良好な値 |
|---|---|---|
| 文字カバレッジ | 語彙内文字 / 全文字 | 99.95%以上 |
| トークン/文字比 | 全トークン数 / 全文字数 | 0.3〜0.6 |
| UNK率 | UNKトークン数 / 全トークン数 | 0%(byte_fallback時) |
| 平均トークン長 | 全文字数 / 全トークン数 | 1.5〜4.0文字 |
| 語彙利用率 | 出現語彙数 / 全語彙サイズ | 80%以上 |
def evaluate_vocabulary(sp, test_corpus_path):
total_chars = 0
total_tokens = 0
unk_count = 0
used_ids = set()
with open(test_corpus_path, 'r') as f:
for line in f:
line = line.strip()
total_chars += len(line)
ids = sp.encode(line, out_type=int)
total_tokens += len(ids)
unk_count += ids.count(sp.unk_id())
used_ids.update(ids)
return {
'token_char_ratio': total_tokens / total_chars,
'unk_rate': unk_count / total_tokens,
'vocab_utilization': len(used_ids) / sp.get_piece_size(),
'avg_token_length': total_chars / total_tokens,
}
多言語モデルでは、リソース量の異なる言語間で語彙のバランスを取ることが重要な課題である。
各言語のテキストがトークン化後にどの程度膨張するかを示す指標:
| 言語 | LLaMA 2 (32K) | LLaMA 3 (128K) | Gemma (256K) |
|---|---|---|---|
| 英語 | 1.0x(基準) | 1.0x | 1.0x |
| フランス語 | 1.4x | 1.1x | 1.05x |
| 日本語 | 3.2x | 1.5x | 1.2x |
| 中国語 | 2.8x | 1.4x | 1.15x |
| 韓国語 | 3.5x | 1.6x | 1.3x |
| アラビア語 | 2.1x | 1.3x | 1.1x |
LLaMA 2の32K語彙では日本語テキストが英語の3.2倍にトークン膨張していたが、LLaMA 3の128K語彙では1.5倍に改善された。Gemmaの256K語彙ではさらに1.2倍まで圧縮されている。
P(lang) ∝ size(lang)^α(α=0.3〜0.7)で低リソース言語のサンプリング比率を上げる<|begin_of_text|> # テキスト開始
<|start_header_id|> # ヘッダー開始(system/user/assistant)
<|end_header_id|> # ヘッダー終了
<|eot_id|> # ターン終了
<|end_of_text|> # テキスト終了
<|fim_prefix|> # Fill-in-the-Middle 接頭辞
<|fim_middle|> # Fill-in-the-Middle 中間
<|fim_suffix|> # Fill-in-the-Middle 接尾辞
<|file_sep|> # ファイル区切り
これらはSentencePieceトレーナーのuser_defined_symbolsまたはcontrol_symbolsで予約する。
Q1: 語彙サイズを大きくすると推論速度は遅くなりますか?
A: 語彙サイズの増加は埋め込み層と出力層の行列サイズに影響するが、Transformerの注意機構のコストと比較すると影響は軽微である。32Kから128Kへの拡大でメモリ使用量は約300MB増加するが、トークン数の削減により実効的な推論速度は向上することが多い。
Q2: 学習済みモデルの語彙を事後的に縮小できますか?
A: 語彙の縮小(Vocabulary Pruning)は技術的に可能だが、対応する埋め込みの再学習が必要で実用的でない。代わりに、不要トークンの埋め込みをゼロベクトルに置換し、トークナイザー側で使用不可にする方法が取られることがある。
Q3: 複数のSentencePieceモデルを統合する方法はありますか?
A: 直接的な統合APIは存在しない。実務上は各モデルの.vocabファイルからトークンリストを抽出し、統合コーパスから新しいモデルを再学習するのが標準的なアプローチである。HuggingFace TokenizersのBPE実装では部分的な語彙マージが可能。