HuggingFace Transformers 4.5 : 利用方法 : データの前処理 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/11/2021 (4.5.1)
* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- Using Transformers : Preprocessing data
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
スケジュールは弊社 公式 Web サイト でご確認頂けます。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。
人工知能研究開発支援 | 人工知能研修サービス | テレワーク & オンライン授業を支援 |
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。) |
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ ; Facebook |
HuggingFace Transformers : 利用方法 : データの前処理
- 訳注 : 本ドキュメントの TensorFlow バージョンは こちら をご覧ください。
このチュートリアルでは、 Transformers を使用してデータをどのように前処理するかを探求します。このための主要ツールは tokenizer と呼ぶものです。貴方が使用したいモデルに関連する tokenizer クラスを使用するか、あるいは AutoTokenizer クラスで直接それを構築できます。
クイックツアーで見たように、tokenizer は与えられたテキストを最初にトークンと呼ばれる単語 (or 単語の部分、句読点記号, etc.) に分割します。それからそれはそれらから tensor を構築できるようにトークンを数字に変換してそしてそれらをモデルに供給します。それはまたモデルが正しく動作するために想定するかもしれない任意の追加入力も追加します。
Note: 事前訓練モデルを使用する計画を立てているならば、関連する事前訓練 tokenizer を使用することは重要です : それは貴方がそれに与えるテキストを事前訓練コーパスのためのと同じ方法でトークンを分割し、そしてそれはインデックスするために事前訓練の間と同じ 対応トークン (それは通常は語彙と呼称します) を使用します。
与えられたモデルを事前訓練するか再調整する間に使用された語彙を自動的にダウンロードするために、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 に送る目的がモデルに供給するバッチを構築することであるならば、貴方は多分以下を望むでしょう :
- バッチで最大長に各センテンスをパッドする。
- モデルが受け取れる最大長に各センテンスを切り捨てる (妥当な場合)。
- tensor を返す。
センテンスのリストを tokenzier に供給するとき以下のオプションを使用してこれの総てを行なうことができます :
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]
再度、入力をバッチの最大センテンス長に自動的にパッドし、モデルが受け取れる最大長に切り捨ててそして以下で tensor を直接返すことができます :
batch = tokenizer(batch_sentences, batch_of_second_sentences, padding=True, truncation=True, return_tensors="pt")
パディングと切り捨てについて貴方が常に知りたいことの総て
殆どの場合について動作するコマンドを見てきました (バッチを最大センテンス長にパッドし、モデルが受け取れる最大長に切り捨てます)。けれども、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 は切り捨てを制御します。それは以下であるべき boolean か文字列であり得ます :
- True or ‘only_first’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に切り捨てます。シークエンスのペア (or シークエンスのペアのバッチ) が提供される場合、ペアの最初のセンテンスだけを切り捨てます。
- ‘only_second’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に切り捨てます。これはシークエンスのペア (or シークエンスのペアのバッチ) が提供される場合、ペアの 2 番目のセンテンスだけを切り捨てます。
- ‘longest_first’ は max_length 引数か (max_length が提供されない場合 (max_length=None)) モデルにより受容される最大長により指定される最大長に切り捨てます。これはトークン毎に切り捨て、適切な長さに達するまでペアの最長のシークエンスからトークンを除去します。
- False or ‘do_not_truncate’ はシークエンスを切り捨てません。前に見たように、これはデフォルトの動作です。
- max_length はパディング/切り捨ての長さを制御します。それは整数か None であり得て、その場合それはモデルが受容できる最大長をデフォルトとします。モデルは特定の最大入力長を持たない場合、max_length への切り捨て/パディングは無効にされます。
ここにパディングと切り捨てをセットアップするための推奨方法を要約するテーブルがあります。以下のサンプルのいずれかで入力シークエンスのペアを使用する場合、truncation=True を [‘only_first’, ‘only_second’, ‘longest_first’] で選択された STRATEGY で置き換えることができます、i.e. truncation=’only_second’ or truncation= ‘longest_first’ は前に詳述されたようにペアの両者のシークエンスがどのように切り捨てられるかを制御します。
(訳注: テーブルについては 原文 参照)
事前にトークン化された入力
tokenizer はまた事前にトークン化された入力も受け取ります。これは固有表現認識 (NER) や品詞タギング (POS タギング) でラベルを計算して予測を抽出することを望むときに特に有用です。
Warning: 事前トークン化は、貴方の入力が既にトークン化されている (その場合にはそれらを tokenizer に渡す必要はありません) ことを意味しませんが、単に単語に分割されています (それはしばしば BPE のような 部分単語トークン化アルゴリズムの最初のステップです)。
事前にトークン化された入力を使用することを望む場合、入力を 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)
そして前のようにパディング、切り捨てを追加できてそして直接 tensor を返すことができます :
batch = tokenizer(batch_sentences,
batch_of_second_sentences,
is_split_into_words=True,
padding=True,
truncation=True,
return_tensors="pt")
以上