HuggingFace Tokenizers 0.10 : Quicktour (python) (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/27/2021 (Python v0.10.2)
* 本ページは、HuggingFace Tokenizers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
スケジュールは弊社 公式 Web サイト でご確認頂けます。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。
人工知能研究開発支援 | 人工知能研修サービス | テレワーク & オンライン授業を支援 |
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。) |
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ ; Facebook |
HuggingFace Tokenizers : Quicktour (python)
Tokenizers ライブラリの機能を素早く見てみましょう。このライブラリは使いやすく超高速な今日最も利用されているトークナイザーの実装を提供します。
それは事前訓練されたトークナイザーをインスタンス化するために利用できますが、スクラッチから構築することからクイックツアーを始めてそれをどのように訓練するかを見ます。
スクラッチからトークナイザーを構築する
Tokenizers ライブラリがどれほど速いかを示すため、新しいトークナイザーを wikitext-103 (テキストの 516M) の上で数秒間で訓練しましょう。まず最初に、このデータセットをダウンロードして次で unzip する必要があります :
wget https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-103-raw-v1.zip
unzip wikitext-103-raw-v1.zip
トークナイザーの訓練
このツアーでは、Byte-Pair エンコーディング (BPE) トークナイザーを構築して訓練します。様々なタイプのトークナイザーについてのより多くの情報については、 Transformers ドキュメントのこの ガイド を確認してください。ここでは、トークナイザーの訓練は次によりマージルールを学習することを意味します :
- 訓練コーパスに存在する総ての文字からトークンとして始めます。
- トークの最も一般的なペアを識別してそれを一つのトークンにマージします。
- 語彙 (e.g., トークンの数) が望んだサイズに到達するまで繰り返します。
ライブラリのメイン API はクラス Tokenizer で、ここに BPE モデルでそれをどのようにインスタンス化するかがあります :
from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))
トークナイザーを wikitext ファイル上で訓練するには、トレーナー、この場合は BpeTrainer をインスタンス化する必要があります。
from tokenizers.trainers import BpeTrainer
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
vocab_size や min_frequency のような訓練引数を設定できますが (ここでは 30,000 と 0 のそれらのデフォルト値のままです)、最も重要な部分は後で使う予定の special_tokens を与えることです (それらは訓練の間には全く使われません)、その結果それらは語彙に挿入されます。
Note: special トークンリストを書く順序は重要です : ここでは “[UNK]” は ID 0 を得て、”[CLS]” は ID 1 を得ます、等々。
トークナイザーを直ちに訓練できるでしょうが、それは最適ではありません。入力を単語に分割する事前トークナイザーなしでは、幾つかの単語にオーバーラップするトークンを得るかもしれません : 例えば “it is” トークンを得る可能性があります、何故ならばそれら 2 つの単語はしばしば隣り合って出現するからです。事前トークナイザーの使用は事前トークナイザーにより返される単語よりも大きいトークンがないことを確実にします。ここでは subword BPE tokenizer を訓練することを望みます、そしてホワイトスペースで分割することで可能な最も容易な事前トークナイザーを使用します。
from tokenizers.pre_tokenizers import Whitespace
tokenizer.pre_tokenizer = Whitespace()
今は、使用したい任意のファイルのリストで train() メソッドを単に呼び出すことができます :
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
tokenizer.train(files, trainer)
これは full wikitext データセット上でトークナイザーを訓練するために数秒しかかからないはずです!総ての configuration と語彙を含む一つのファイルにトークナイザーをセーブするには、単に save() メソッドを使います :
tokenizer.save("data/tokenizer-wiki.json")
そしてそのファイルから from_file() クラスメソッドでトークナイザーを再ロードできます :
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")
トークナイザーを使用する
トークナイザーを訓練した今、で望む任意のテキスト上で enode() メソッドそれを使用できます :
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
これはテキスト上でトークナイザーの完全なパイプラインを適用し、エンコーディング・オブジェクトを返します。このパイプラインについて更に学習し、その一部をどのように適用 (or カスタマイズ) するかについては、このページ を調べてください。
そしてこのエンコーディング・オブジェクトは深層学習モデル (or その他) のために必要な総ての属性を持ちます。tokens 属性はテキストのセグメンテーションをトークンで含みます :
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]
同様に、ids 属性はそれらのトークンのトークナイザーの語彙内の各々のインデックスを含みます :
print(output.ids)
# [27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35]
Tokenizers ライブラリの重要な特徴はそれが完全なアライメント追跡を備えていることです、つまり与えられたトークンに対応する元のセンテンスの一部を常に得ることができます。それらはエンコーディング・オブジェクトの offsets 属性にストアされています。例えば、リストのインデックス 9 のトークンである、出現する “[UNK]” トークンを引き起こしたものを見つけることを望むとすれば、インデックスのオフセットを単に求めることができます :
print(output.offsets[9])
# (26, 27)
そしてそれらは元のセンテンスで絵文字に対応するインデックスです :
sentence = "Hello, y'all! How are you 😁 ?"
sentence[26:27]
# "😁"
後処理
トークナイザーが “[CLS]” や “[SEP]” のような特殊トークンを自動的に追加することを望むかもしれません。これを行なうために、post-processor を使用します。TemplateProcessing が最も一般的に使用されます、単一センテンスとセンテンスのペアの処理のために特殊トークンとそれらの ID と一緒に、単にテンプレートを指定しなければなりません。
トークナイザーを構築したとき、特殊トークンのリストの位置 1 と 2 に “[CLS]” と “[SEP]” を設定したので、これはそれらの ID であるはずです。再確認するため、token_to_id() メソッドを使用できます :
tokenizer.token_to_id("[SEP]")
# 2
ここに伝統的な BERT 入力を与える後処理をどのように設定できるかがあります :
from tokenizers.processors import TemplateProcessing
tokenizer.post_processor = TemplateProcessing(
single="[CLS] $A [SEP]",
pair="[CLS] $A [SEP] $B:1 [SEP]:1",
special_tokens=[
("[CLS]", tokenizer.token_to_id("[CLS]")),
("[SEP]", tokenizer.token_to_id("[SEP]")),
],
)
コードのこのスニペットを詳しく調べましょう。最初に単一センテンスのためにテンプレートを指定します : これはフォーム “[CLS] $A [SEP]” を持つべきです、ここで $A はセンテンスを表すします。
そして、センテンスペアのためのテンプレートを指定します、これはフォーム “[CLS] $A [SEP] $B [SEP]” を持つべきです、ここで $A は最初のセンテンスそして $B は 2 番目のものを表します。テンプレートで追加された :1 は入力の各パートのために望むタイプ ID を表します : それは総てについて 0 がデフォルトで (それが $A:0 をもたない理由です) そしてここでは 2 番目のセンテンスのトークンと最後の “[SEP]” トークンのためにそれを 1 に設定します。
最後に、使用した特殊トークンとそれらの ID をトークナイザーの語彙で指定します。
これが正しく動作したことを確認するため、前と同じセンテンスをエンコードしてみましょう :
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?", "[SEP]"]
センテンスのペア上の結果を確認するには、2 つのセンテンスを encode() に渡すだけです :
output = tokenizer.encode("Hello, y'all!", "How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "[SEP]", "How", "are", "you", "[UNK]", "?", "[SEP]"]
それから各トークンに属する type ID が正しいかを次で確認できます :
print(output.type_ids)
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
save() でトークナイザーをセーブする場合、post-processor も一緒にセーブされます。
バッチで複数センテンスをエンコードする
Tokenizers ライブラリのフルスピードを得るには、encode_batch() メソッドを使用してバッチによりテキストを処理することが最善です :
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
そして出力は前にみたもののような Encoding オブジェクトのリストです。メモリに収まる限りは、必要なだけの多くのテキストを処理できます。
センテンスペアのバッチを処理するには、2 つのリストを encode_batch() メソッドに渡します : センテンス A のリストとセンテンス B のリストです :
output = tokenizer.encode_batch(
[["Hello, y'all!", "How are you 😁 ?"], ["Hello to you too!", "I'm fine, thank you!"]]
)
複数センテンスをエンコードするとき、pad_token とその ID により (前のように token_to_id() によりパディングトークンのための id を再確認できます)、enable_padding() を使用して出力を存在する最長センテンスに自動的にパッドできます :
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")
総てのサンプルをその特定の数にパッドすることを望む場合、パディングの方向や指定された長さを設定できます (ここでは最長テキストのサイズへのパッドを設定しないままにします)。
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
print(output[1].tokens)
# ["[CLS]", "How", "are", "you", "[UNK]", "?", "[SEP]", "[PAD]"]
この場合、トークナイザーによりせいせいされたアテンションマスクはパディングを考慮に入れます :
print(output[1].attention_mask)
# [1, 1, 1, 1, 1, 1, 1, 0]
事前訓練されたトークナイザーを使用する
語彙ファイルを持つ限りは事前訓練されたトークナイザーもまた直接利用できます。例えば、ここに古典的な事前訓練された BERT トークナイザーをどのように得るかがあります :
from tokenizers import BertWordPieceTokenizer
tokenizer = BertWordPieceTokenizer("bert-base-uncased-vocab.txt", lowercase=True)
一方でファイル bert-base-uncased-vocab.txt は次でダウンロードしておきます :
wget https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-vocab.txt
以上