HuggingFace Transformers 3.4 : データを前処理する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 11/20/2020 (3.4.0)
* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
HuggingFace Transformers : データを前処理する
このチュートリアルでは、 Transformers を使用して貴方のデータをどのように前処理するかを探求します。このための主要ツールは私達が tokenizer と呼ぶものです。貴方が使用したいモデルに関係する tokenizer クラスを使用するか、あるいは直接 AutoTokenizer クラスでそれを構築できます。
クイックツアーで見たように、tokenizer は与えられたテキストを最初にトークンと呼ばれる単語 (or 単語の部分、句読点記号, etc.) に分割します。それからそれはそれらから tensor を構築できるようにトークンを数字に変換してそしてそれらをモデルに供給します。それはまたモデルが正しく動作するために想定するかもしれない任意の追加入力も追加します。
Note: 事前訓練モデルを使用する計画を立てているならば、関連する事前訓練 tokenizer を使用することは重要です : それは貴方がそれに与えるテキストを事前訓練コーパスのためのと同じ方法でトークンを分割してそしてそれはインデックスするために事前訓練の間と同じ correspondence トークン (それは通常は vocab と呼称します) を使用します。
与えられたモデルを事前訓練か再調整する間に使用された vocab を自動的にダウンロードするためには、from_pretrained() メソッドを利用できます :
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
Base 使用方法
PreTrainedTokenizer は多くのメソッドを持ちますが、前処理のために覚えておく必要がある唯一のものはその __call__ です : センテンスを tokenizer オブジェクトに供給する必要があるだけです。
encoded_input = tokenizer("Hello, I'm a single sentence!") print(encoded_input)
{'input_ids': [101, 138, 18696, 155, 1942, 3190, 1144, 1572, 13745, 1104, 159, 9664, 2107, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
これは int のリストへの辞書文字列を返します。input_ids はセンテンスの各トークンに対応するインデックスです。下で attention_mask が何のために使用されるか、そして次のセクション で token_type_ids の目的を見ます。
tokenizer はトークン id のリストを正しいセンテンスでデコードできます :
tokenizer.decode(encoded_input["input_ids"])
"[CLS] Hello, I'm a single sentence! [SEP]"
見れるように、tokenizer はモデルが想定する幾つかの特殊トークンを自動的に追加しました。総てのモデルが特殊トークンを必要とはしません ; 例えば、tokenizer を作成するために bert-base-cased の代わりに `gtp2-medium` を使用した場合、ここで元の一つと同じセンテンスを見るでしょう。add_special_tokens=False を渡すことによりこの挙動を無効にできます (これは貴方自身で特殊トークンを追加した場合にのみ勧められます)。
処理することを望む幾つかのセンテンスを持つ場合、リストとしてそれらを tokenizer に送ることにより効率的にこれを行なうことができます :
batch_sentences = ["Hello I'm a single sentence", "And another sentence", "And the very very last one"] encoded_inputs = tokenizer(batch_sentences) print(encoded_inputs)
{'input_ids': [[101, 8667, 146, 112, 182, 170, 1423, 5650, 102], [101, 1262, 1330, 5650, 102], [101, 1262, 1103, 1304, 1304, 1314, 1141, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]]}
再度辞書を入手して、今回は int のリストのリストである値を持ちます。
一度に幾つかのセンテンスを tokenizer に送る目的がモデルに供給するバッチを構築することであるならば、貴方は多分以下を望むでしょう :
- バッチで最大長に各センテンスをパッドする。
- モデルが受け取れる最大長に各センテンスを truncate する (妥当な場合)。
- tensor を返す。
センテンスのリストを tokenzier に供給するとき以下のオプションを使用してこれの総てを行なうことができます :
batch = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="tf") print(batch)
{'input_ids': tf.Tensor([[ 101, 8667, 146, 112, 182, 170, 1423, 5650, 102], [ 101, 1262, 1330, 5650, 102, 0, 0, 0, 0], [ 101, 1262, 1103, 1304, 1304, 1314, 1141, 102, 0]]), 'token_type_ids': tf.Tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tf.Tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 0]])}
batch = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="pt") print(batch)
{'input_ids': tensor([[ 101, 8667, 146, 112, 182, 170, 1423, 5650, 102], [ 101, 1262, 1330, 5650, 102, 0, 0, 0, 0], [ 101, 1262, 1103, 1304, 1304, 1314, 1141, 102, 0]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 0]])}
それは文字列キーと tensor 値を持つ辞書を返します。今では attention_mask が何かを総て見ることができます : それはモデルがどのトークンに注意を払うべきか、そしてどれに注意を払うべきでないかを指摘します (何故ならばこの場合それらはパディングを表すからです)。
モデルがそれに関連する最大長を持たない場合には、上のコマンドは警告を投げることに注意してください。それを安全に無視することができます。その種類の警告を投げることを tokenzier にやめさせるために verbose=False を渡すこともできます。
センテンスのペアを前処理する
時に貴方のモデルにセンテンスのペアを供給する必要があります。例えば、ペアの 2 つのセンテンスが類似しているか分類することを望んだり、コンテキストと質問を取る質問応答モデルについてです。BERT モデルに対しては、入力はこのように表されます : [CLS] Sequence A [SEP] Sequence B [SEP]
2 つの引数として 2 つのセンテンスを供給することによりモデルにより想定される形式でセンテンスのペアをエンコードできます (リストではありません、何故ならば 前に見たように、2 つのセンテンスのリストは 2 つの単一のセンテンスのバッチとして解釈されるからです)。これは再度 int のリストへの辞書文字列を返します :
encoded_input = tokenizer("How old are you?", "I'm 6 years old") print(encoded_input)
{'input_ids': [101, 1731, 1385, 1132, 1128, 136, 102, 146, 112, 182, 127, 1201, 1385, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
これは token_type_ids が何のためであるかを示します : それらはモデルに入力のどの部分が最初のセンテンスに対応してそしてどの部分が 2 番目のセンテンスに対応するかを示します。token_type_ids は総てのモデルにより必要とされたり処理されたりするわけではないことに注意してください。デフォルトでは、tokenizer は関連モデルが想定する入力を返すだけです。return_input_ids か return_token_type_ids を使用してそれらの特別な引数のいずれかの return (or non-return) を強制できます。
私達が得たトークン id をデコードすれば、特殊トークンが正しく追加されたことを見ます。
tokenizer.decode(encoded_input["input_ids"])
"[CLS] How old are you? [SEP] I'm 6 years old [SEP]"
処理することを望むシークエンスのペアのリストを持つ場合、それらを tokenizer に 2 つのリストとして供給するべきです : 最初のセンテンスのリストと 2 番目のセンテンスのリストです :
batch_sentences = ["Hello I'm a single sentence", "And another sentence", "And the very very last one"] batch_of_second_sentences = ["I'm a sentence that goes with the first sentence", "And I should be encoded with the second sentence", "And I go with the very last one"] encoded_inputs = tokenizer(batch_sentences, batch_of_second_sentences) print(encoded_inputs)
{'input_ids': [[101, 8667, 146, 112, 182, 170, 1423, 5650, 102, 146, 112, 182, 170, 5650, 1115, 2947, 1114, 1103, 1148, 5650, 102], [101, 1262, 1330, 5650, 102, 1262, 146, 1431, 1129, 12544, 1114, 1103, 1248, 5650, 102], [101, 1262, 1103, 1304, 1304, 1314, 1141, 102, 1262, 146, 1301, 1114, 1103, 1304, 1314, 1141, 102]], 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
見れるように、それは各値が int のリストのリストであるような辞書を返します。
何がモデルに供給されるかを二重チェックするために、input_ids の各リストを一つずつデコードできます :
for ids in encoded_inputs["input_ids"]: print(tokenizer.decode(ids))
[CLS] Hello I'm a single sentence [SEP] I'm a sentence that goes with the first sentence [SEP] [CLS] And another sentence [SEP] And I should be encoded with the second sentence [SEP] [CLS] And the very very last one [SEP] And I go with the very last one [SEP]
再度、入力をバッチの最大センテンス長に自動的にパッドし、モデルが受容できる最大長に truncate してそして以下とともに tensor を直接返すことができます :
batch = tokenizer(batch_sentences, batch_of_second_sentences, padding=True, truncation=True, return_tensors="tf")
batch = tokenizer(batch_sentences, batch_of_second_sentences, padding=True, truncation=True, return_tensors="pt")
パディングと truncation について貴方が常に知りたいことの総て
殆どの場合について動作するコマンドを見ました (バッチを最大センテンス長にパッドし、モデルが受容できる最大長に truncate します)。けれども、API はより多くのストラテジーをサポートします、貴方がそれらを必要とする場合に。このために知る必要がある 3 つの引数は padding, truncation と max_length です。
- padding はパディングを制御します。それは以下であるべき boolean か文字列であり得ます :
- True or ‘longest’、バッチの最長のシークエンスにパッドします (単一シークエンスだけを提供する場合にはパディングをしません)。
- ‘max_length’、max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される長さにパッドします。単一シークエンスだけを提供する場合、パディングは依然としてそれに適用されます。
- False or ‘do_not_pad’、シークエンスをパッドしません。前に見たように、これはデフォルトの動作です。
- truncation は truncation を制御します。それは以下であるべき boolean か文字列であり得ます :
- True or ‘only_first’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に truncate します。シークエンスのペア (or シークエンスのペアのバッチ) が提供される場合、ペアの最初のセンテンスだけを truncate します。
- ‘only_second’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に truncate します。これはシークエンスのペア (or シークエンスのペアのバッチ) が提供される場合、ペアの 2 番目のセンテンスだけを truncate します。
- ‘longest_first’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に truncate します。これはトークン毎に truncate し、適切な長さに達するまでペアの最長のシークエンスからトークンを除去します。
- False or ‘do_not_truncate’ はシークエンスを truncate しません。前に見たように、これはデフォルトの動作です。
- max_length はパディング/truncation の長さを制御します。それは整数か None であり得て、その場合それはモデルが受容できる最大長をデフォルトとします。モデルは特定の最大入力長を持たない場合、max_length への truncation/パディングは無効にされます。
ここにパディングと tuncation をセットアップするための推奨方法を要約するテーブルがあります。以下のサンプルのいずれかで入力シークエンスのペアを使用する場合、truncation=True を [‘only_first’, ‘only_second’, ‘longest_first’] で選択された STRATEGY で置き換えることができます、i.e. truncation=’only_second’ or truncation= ‘longest_first’ は前に詳述されたようにペアの両者のシークエンスがどのように truncate されるかを制御するかです。
(訳注: テーブルは 原文 参照)
Pre-tokenized 入力
tokenizer はまた pre-tokenized 入力も受け取ります。これは固有表現認識 (NER) や品詞タギング (POS タギング) でラベルを計算して予測を抽出することを望むときに特に有用です。
Warning: pre-tokenized は、貴方の入力が既にトークン化されている (その場合にはそれらを tokenizer に渡す必要はありません) ことを意味しませんが、単に単語に分割されています (それはしばしば BPE のような 部分単語トークン化アルゴリズムの最初のステップです)。
pre-tokenized 入力を使用することを望む場合、入力を tokenzier に渡すとき単に is_split_into_words=True を設定します。例えば、以下を持ちます :
encoded_input = tokenizer(["Hello", "I'm", "a", "single", "sentence"], is_split_into_words=True) print(encoded_input)
{'input_ids': [101, 8667, 146, 112, 182, 170, 1423, 5650, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
tokenizer は add_special_tokens=False を渡さない限り特殊トークンの id を (妥当な場合) 依然として追加します。
これはセンテンスのバッチやセンテンスのペアのバッチのために前のように正確に動作します。センテンスのバッチをこのようにエンコードできます :
batch_sentences = [["Hello", "I'm", "a", "single", "sentence"], ["And", "another", "sentence"], ["And", "the", "very", "very", "last", "one"]] encoded_inputs = tokenizer(batch_sentences, is_split_into_words=True)
あるいこのようにペア・センテンスのバッチ :
batch_of_second_sentences = [["I'm", "a", "sentence", "that", "goes", "with", "the", "first", "sentence"], ["And", "I", "should", "be", "encoded", "with", "the", "second", "sentence"], ["And", "I", "go", "with", "the", "very", "last", "one"]] encoded_inputs = tokenizer(batch_sentences, batch_of_second_sentences, is_split_into_words=True)
そして前のようにパディング、truncation を追加できてそして直接 tensor を返すことができます :
batch = tokenizer(batch_sentences, batch_of_second_sentences, is_split_into_words=True, padding=True, truncation=True, return_tensors="tf")
batch = tokenizer(batch_sentences, batch_of_second_sentences, is_split_into_words=True, padding=True, truncation=True, return_tensors="pt")
以上