目次
- はじめに
- バイト対符号化(BPE)
- 単語とその頻度
- 語彙の初期化
- 単語の分割状態
- 最も頻度の高い隣接サブワードの組を計算
- サブワードの組を結合
- BPEの適用
- 日本語の扱い
- 必要なライブラリのインストール
- 多言語BERTトークナイザー
- XLM-RoBERTaトークナイザー
- 日本語BERTトークナイザー
- まとめ
1. はじめに
大規模言語モデル(LLM)の重要な要素の1つに「トークナイゼーション」があります。トークナイゼーションは、テキストをモデルが処理可能な単位(トークン)に分割するプロセスです。特に日本語のような形態素解析が必要な言語では、トークナイゼーションの手法が重要になります。本記事では、トークナイゼーションの基礎となる「バイト対符号化(BPE: Byte Pair Encoding)」と、日本語のトークナイゼーションの実践的な例を紹介します。
2. バイト対符号化(BPE)
バイト対符号化(BPE)は、テキストをサブワード単位に分割する手法です。これにより、未知語や稀な単語を効果的に扱うことができます。以下に、BPEの基本的な実装例を示します。
2.1 単語とその頻度
まず、単語とその出現頻度を定義します。
word_freqs = {
"たのしい": 6,
"たのしさ": 2,
"うつくしい": 4,
"うつくしさ": 1,
}
2.2 語彙の初期化
語彙を文字単位で初期化します。
vocab = sorted(set([char for word in word_freqs for char in word]))
print(vocab)
出力:
['い', 'う', 'く', 'さ', 'し', 'た', 'つ', 'の']
2.3 単語の分割状態
各単語を文字単位に分割した状態を保持します。
splits = {word: [char for char in word] for word in word_freqs}
print(splits)
出力:
{'たのしい': ['た', 'の', 'し', 'い'],
'たのしさ': ['た', 'の', 'し', 'さ'],
'うつくしい': ['う', 'つ', 'く', 'し', 'い'],
'うつくしさ': ['う', 'つ', 'く', 'し', 'さ']}
2.4 最も頻度の高い隣接サブワードの組を計算
隣接するサブワードの組の頻度を計算し、最も頻度の高い組を見つけます。
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 サブワードの組を結合
最も頻度の高いサブワードの組を結合します。
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を繰り返し適用します。
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 必要なライブラリのインストール
まず、必要なライブラリをインストールします。
!pip install transformers[ja,sentencepiece,torch]
3.2 多言語BERTトークナイザー
多言語BERT(mBERT)を用いたトークナイゼーションの例です。
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を用いたトークナイゼーションの例です。
xlmr_tokenizer = AutoTokenizer.from_pretrained("xlm-roberta-base")
print(xlmr_tokenizer.tokenize("自然言語処理にディープラーニングを使う"))
print(xlmr_tokenizer.tokenize("私は日本で生まれました"))
print(xlmr_tokenizer.tokenize("本日はよろしくお願いいたします"))
出力:
['▁', '自然', '言語', '処理', 'に', 'ディー', 'プラ', 'ー', 'ニング', 'を使う']
['▁私は', '日本で', '生まれ', 'ました']
['▁本', '日は', 'よろしくお願いいたします']
3.4 日本語BERTトークナイザー
日本語に特化したBERTトークナイザーの例です。
bert_ja_tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese-v3")
print(bert_ja_tokenizer.tokenize("自然言語処理にディープラーニングを使う"))
print(bert_ja_tokenizer.tokenize("私は日本で生まれました"))
出力:
['自然', '言語', '処理', 'に', 'ディープ', 'ラー', '##ニング', 'を', '使う']
['私', 'は', '日本', 'で', '生まれ', 'まし', 'た']
4. まとめ
本記事では、大規模言語モデルのトークナイゼーション手法であるバイト対符号化(BPE)の実装と、日本語のトークナイゼーションの実践的な例を紹介しました。日本語のような複雑な言語を扱う際には、事前学習済みのトークナイザーを活用することが効果的です。ぜひ、これらの手法を活用して、自然言語処理タスクに取り組んでみてください。
参考資料: