大規模言語モデルの基礎：トークナイゼーションと日本語の扱い



## 目次
1. はじめに
2. バイト対符号化（BPE）
   - 単語とその頻度
   - 語彙の初期化
   - 単語の分割状態
   - 最も頻度の高い隣接サブワードの組を計算
   - サブワードの組を結合
   - BPEの適用
3. 日本語の扱い
   - 必要なライブラリのインストール
   - 多言語BERTトークナイザー
   - XLM-RoBERTaトークナイザー
   - 日本語BERTトークナイザー
4. まとめ

## 1. はじめに

大規模言語モデル（LLM）の重要な要素の1つに「トークナイゼーション」があります。トークナイゼーションは、テキストをモデルが処理可能な単位（トークン）に分割するプロセスです。特に日本語のような形態素解析が必要な言語では、トークナイゼーションの手法が重要になります。本記事では、トークナイゼーションの基礎となる「バイト対符号化（BPE: Byte Pair Encoding）」と、日本語のトークナイゼーションの実践的な例を紹介します。

## 2. バイト対符号化（BPE）

バイト対符号化（BPE）は、テキストをサブワード単位に分割する手法です。これにより、未知語や稀な単語を効果的に扱うことができます。以下に、BPEの基本的な実装例を示します。

### 2.1 単語とその頻度

まず、単語とその出現頻度を定義します。

```python
word_freqs = {
    "たのしい": 6,
    "たのしさ": 2,
    "うつくしい": 4,
    "うつくしさ": 1,
}
```

### 2.2 語彙の初期化

語彙を文字単位で初期化します。

```python
vocab = sorted(set([char for word in word_freqs for char in word]))
print(vocab)
```

**出力:**

```
['い', 'う', 'く', 'さ', 'し', 'た', 'つ', 'の']
```

### 2.3 単語の分割状態

各単語を文字単位に分割した状態を保持します。

```python
splits = {word: [char for char in word] for word in word_freqs}
print(splits)
```

**出力:**

```
{'たのしい': ['た', 'の', 'し', 'い'],
 'たのしさ': ['た', 'の', 'し', 'さ'],
 'うつくしい': ['う', 'つ', 'く', 'し', 'い'],
 'うつくしさ': ['う', 'つ', 'く', 'し', 'さ']}
```

### 2.4 最も頻度の高い隣接サブワードの組を計算

隣接するサブワードの組の頻度を計算し、最も頻度の高い組を見つけます。

```python
from collections import Counter

def compute_most_frequent_pair(splits: dict[str, list[str]]) -> tuple[str, str]:
    pair_freqs = Counter()
    for word, freq in word_freqs.items():
        split = splits[word]
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    pair, _ = pair_freqs.most_common(1)[0]
    return pair
```

### 2.5 サブワードの組を結合

最も頻度の高いサブワードの組を結合します。

```python
def merge_pair(target_pair: tuple[str, str], splits: dict[str, list[str]]) -> dict[str, list[str]]:
    l_str, r_str = target_pair
    for word in word_freqs:
        split = splits[word]
        i = 0
        while i < len(split) - 1:
            if split[i] == l_str and split[i + 1] == r_str:
                split = split[:i] + [l_str + r_str] + split[i + 2 :]
            i += 1
        splits[word] = split
    return splits
```

### 2.6 BPEの適用

上記の関数を用いて、BPEを繰り返し適用します。

```python
for step in range(9):
    target_pair = compute_most_frequent_pair(splits)
    splits = merge_pair(target_pair, splits)
    vocab.append(target_pair)
print(vocab)
```

**出力:**

```
['い', 'う', 'く', 'さ', 'し', 'た', 'つ', 'の', ('し', 'い'), ('た', 'の'), ('たの', 'しい'), ('う', 'つ'), ('うつ', 'く'), ('うつく', 'しい'), ('し', 'さ'), ('たの', 'しさ'), ('うつく', 'しさ')]
```

## 3. 日本語の扱い

日本語のトークナイゼーションは、形態素解析やサブワード分割が複雑であるため、事前学習済みのトークナイザーを利用することが一般的です。以下に、`transformers`ライブラリを用いた日本語のトークナイゼーションの例を示します。

### 3.1 必要なライブラリのインストール

まず、必要なライブラリをインストールします。

```bash
!pip install transformers[ja,sentencepiece,torch]
```

### 3.2 多言語BERTトークナイザー

多言語BERT（mBERT）を用いたトークナイゼーションの例です。

```python
from transformers import AutoTokenizer

mbert_tokenizer = AutoTokenizer.from_pretrained("bert-base-multilingual-cased")
print(mbert_tokenizer.tokenize("自然言語処理"))
print(mbert_tokenizer.tokenize("自然言語処理にディープラーニングを使う"))
```

**出力:**

```
['自', '然', '言', '語', '処', '理']
['自', '然', '言', '語', '処', '理', 'に', '##ディ', '##ープ', '##ラー', '##ニング', '##を', '使', 'う']
```

### 3.3 XLM-RoBERTaトークナイザー

XLM-RoBERTaを用いたトークナイゼーションの例です。

```python
xlmr_tokenizer = AutoTokenizer.from_pretrained("xlm-roberta-base")
print(xlmr_tokenizer.tokenize("自然言語処理にディープラーニングを使う"))
print(xlmr_tokenizer.tokenize("私は日本で生まれました"))
print(xlmr_tokenizer.tokenize("本日はよろしくお願いいたします"))
```

**出力:**

```
['▁', '自然', '言語', '処理', 'に', 'ディー', 'プラ', 'ー', 'ニング', 'を使う']
['▁私は', '日本で', '生まれ', 'ました']
['▁本', '日は', 'よろしくお願いいたします']
```

### 3.4 日本語BERTトークナイザー

日本語に特化したBERTトークナイザーの例です。

```python
bert_ja_tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese-v3")
print(bert_ja_tokenizer.tokenize("自然言語処理にディープラーニングを使う"))
print(bert_ja_tokenizer.tokenize("私は日本で生まれました"))
```

**出力:**

```
['自然', '言語', '処理', 'に', 'ディープ', 'ラー', '##ニング', 'を', '使う']
['私', 'は', '日本', 'で', '生まれ', 'まし', 'た']
```

## 4. まとめ

本記事では、大規模言語モデルのトークナイゼーション手法であるバイト対符号化（BPE）の実装と、日本語のトークナイゼーションの実践的な例を紹介しました。日本語のような複雑な言語を扱う際には、事前学習済みのトークナイザーを活用することが効果的です。ぜひ、これらの手法を活用して、自然言語処理タスクに取り組んでみてください。

---

参考資料：

- [https://github.com/ghmagazine/llm-book](https://github.com/ghmagazine/llm-book)