ディープラーニングAI_人工知能TED AI LabHCI/AI

大規模自然モデル(LLM)で適切なトークナイザーを利用することの影響について

みなさん、こんにちは
CerebrasプリセールスエンジニアのNakadaです。
前回のブログではLLMに重要なトークナイザーについて投稿していましたが、今回は、LLMトークナイザーが与える影響について実際にトークナイザーを作成し、その結果を元に共有させて頂きます。また、作成したトークナイザーの性能確認を行う方法についても触れさせて頂きます。

■トークナイザーの影響

前回はLLMにて日本語トークナイザーを使うメリットについて説明させていただきましたが、日本語に対応していないLLMで利用されているトークナイザーでは入力した文章を1文字ずつトークン化してしまうため、以下の例のように、同じ文章でもトークンの数が増え、トークン数による従量課金を行っているサービスではコストが上がってしまったり、学習・推論(文章生成時)の両方でパフォーマンスに影響が出てしまう場合があります。

例:「東京エレクトロンデバイスは、日本の会社です」という文章を2種類のトークナイザーでトークン数を比較

■日本語トークナイザーの作成

日本語トークナイザーを作成するには、日本語でトークナイズできるように学習させるための日本語データが必要です。そして、そのデータを元にトークナイザーをゼロから学習させることで作成することができます。

ただ、準備する日本語データは構築するLLMをどのようなタスクで利用するかに依存するため、利用するデータや学習手法は様々です。今回は、「JIS X 0208 日本漢字(約6800文字)」および、当社で利用している日本語データセットを使いました。また、HuggingfaceのtransformerライブラリにあるBPE(Bytes Pair Encoding)を使って、日本語トークナイザーとしてトークナイザーを学習させました。

従来のNLPで日本語を扱うためには、形態素解析ソフトを使うのが普通でしたが、ChatGPTを代表とするGPTモデルでは形態素解析ではなくBPEがよく使われていることは前回のブログで触れさせていただきました。

次のコードは日本語トークナイザーを学習させるために作成したコードの一部です。

## Prepare tokenizer
tokenizer = tokenizers.Tokenizer(tokenizers.models.BPE(unk_token=”<unk>”, byte_fallback=True))
trainer = tokenizers.trainers.BpeTrainer(
    vocab_size=50000,
    show_progress=True,
    limit_alphabet=8000,
    min_frequency=10,
    special_tokens=[“<s>”, “</s>”, “<unk>”]  + [f”<0x{i:02X}>” for i in range(256)],
    initial_alphabet=japanese_alphabet
 
#Set pre_tokenizer/decoder
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
tokenizer.decoder = decoders.ByteFallback()
 
# Run tokenization
tokenizer.train_from_iterator(batch_iterator(), trainer=trainer)
tokenizer.save(“./tokenizers/jpn_bpe_tokenizer”)
 

コードの中身について解説します。

## Prepare tokenizer

最初にBPEトークナイザーを定義します。Vocab_sizeで作成する語彙リストの登録数を設定し、limit_alphabetで、1文字の語彙の数を定義します。Initial_alphabetには実際の日本語文字を代入します。(japanese_alphabetは、JIS X 0208のすべての文字を定義しています)

なお、min_frequency=10はBPEの学習ルールの1つです。BPEの学習は、入力したデータセットを1文字ごとにチェックし、もっとも頻度の多い隣接文字を結合します。さらに結合した文字列に対しても頻度の多い隣接文字を結合していきます。最終的には指定した回数分結合するか、Vocab_sizeで設定した語彙リスト数に達すると完了するのですが、min_frequency=10は隣接する頻度として最低10回隣接していないと結合しないように制限することができます。これにより頻度が少ないペアの結合を防ぐことができ、利用するデータセットに対する適正な語彙リストを作成することが可能になります。

#Set pre_tokenizer/decoder

入力した文の前処理方法をプレトークナイザーとして指定します。

Whitespace()は、正規表現(\w+|[^w)を使って単純に分割する方法です。他にも手法がありますが、当社のデータセットではこの方法が適していました。

ByteFallback()はトークナイザー内で<unk>トークンをUnicode byteを利用するために設定しました。

# Run tokenization

当社で用意したデータセットをイテレータにセットし、# Prepare tokenizerで設定したパラメータを元に学習を実行しています。学習完了後は、jpn_bpe_tokenizerというファイル名でトークナイザーを保存します。

■作成したトークナイザーのチェック

無事学習が完了したトークナイザーですが、期待した性能が出るのかチェックする必要があります。単純な確認方法としては、前回のブログの中で記載させて頂いたように、実際にトークナイザーをロードし文章を入力し、期待したトークンに分割されていることを確認することです。以下が簡単なコードです。

## Prepare from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(“./tokenizers/jpn_bpe_tokenizer “)
text = “東京エレクトロンデバイスは、日本の会社です”
encoded_text = tokenizer(text)
print(encoded_text)

ただ、このチェックでは心もとないので、トークナイザーの性能指標について簡単にご紹介します。(詳細は別のブログで書きたいと思います)

まず、テスト用にトークナイザーで利用したデータセットとは別に日本語コーパスを準備します。当社では、「Universal Dependencies日本語コーパス」と「wordfreq日本語コーパス」を利用しました。このコーパスにある全文を作成したトークナイザーで分割し、以下の指標を計算します。

豊度(Fertility) 入力した文の分割比。マルチリンガルLLMの場合、1から1.3が良い
未登録トークン出現度 入力した文に含まれた未登録トークン数(unkトークン)の平均。未登録トークン数が少ないほど良い
パリティスコア 同じ意味の英語文と日本語文をトークン化した際に同じ数のトークンが使われるかどうか。マルチ言語の場合に、 100%に近いほど言語転移しやすい。

これらの結果をもとに作成したトークナイザーの性能をチェックすることができます。

■作成した日本語トークナイザーの本当の性能

今回、利用するデータセットを元にトークナイザーを作成しましたが、これをLLMで利用するとどうなるのでしょうか。

Meta社がリリースしたLlama3-8Bでこのトークナイザーを使ってみましたので、結果をお知らせします。結論から言うと、トークナイザーを変更するだけで精度が上がっています。

※注 Llama3-8Bは英語用のLLMですので、そもそも日本語は得意ではありません。また、作成したトークナイザーをそのまま利用せずに、Llama3-8Bのトークナイザーにマージする形で利用しました。

精度のチェックにはStability-AI様が公開しているlm-evaluation-harnessを利用しました。

いかがでしょうか。トークナイザーを変えるだけで、Averageが約5%上がっています。※Averageは右6つの平均精度です。

このように適切なトークナイザーはLLMの精度向上に寄与することができるということを実際の評価で確認することができました。

■最後に

今回は、適切なトークナイザーを利用することで下流タスクの精度も向上できることを共有させていただきました。また、作成したトークナイザーの評価方法と評価指標についても簡単にお話させていただきました。

最後に、ブログをお読みになり、LLM構築及びAIアクセラレータ製品等にご興味がある方は当社までお問い合わせ頂ければ幸いです。

この記事に関連する製品・サービス