機械学習
記事内に商品プロモーションを含む場合があります

Hugging FaceのTokenizerの動作まとめ

tadanori

Hugging FaceでBERT等を使う場合のトークナイザーの動作について疑問があったので、実際に動かして色々確認してみた。

また、Hugging Face Transformerを動かしてみたい場合は以下の記事を参考にしてください。

Hugging Face Transformer(BERT)でクラス分類
Hugging Face Transformer(BERT)でクラス分類
Hugging Face (BERT)で回帰(Regression)
Hugging Face (BERT)で回帰(Regression)

Tokenizerの動きがよくわからない

BERTなどを使う際、トークナイザーでトークン化してから入力することになります。

基本、モデルとトークナイザーは同じペアのものを取得する形になりますが、Tokenizerの動きがよくわからないのでパラメータを振って動作を確認してみました

よく使うパラメータは、padding, truncation, max_lengthの3つだと思いますが、これの挙動がよくわかっていなかったです。というか勘違いしている部分もありました。

padding,truncationはなぜするのか

機械学習を行う場合は、入力を固定長にしたほうが都合が良いことが多いです。また、入力サイズが決まっている場合には、それより長い部分はカットする必要があります。なので、padding, truncationを行う必要があります。

Hugging Faceに登録されているtransformerの場合、この辺りをよしなにやってくれているやつもある気がします。ここは、また調査したいと思います。

動作確認の結果

トークナイザーを取得

今回は、基本となるbert-base-uncasedのモデルで実験を行いました。以下がtokenizerを取得するコードです。

from transformers import AutoTokenizer

model_ckpt = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)

事前にpipでhugging faceをインストールしておいてください。

pip install transformers

実験に使ったテキスト

トークナイザーはリスト形式で複数のテキストを渡すと、それぞれをトークン化してくれます。今回は、ワード数の異なる3つのテキストを用意しました(とりあえず、天気関係で統一していたりします)。

texts = ["The sun shines brightly today.",
         "The sun shines brightly on a clear summer morning.",
         "Today is fine."]

トークン化した結果を表示する関数

結果表示にはこちらを使いました

def print_result(token) :
  print("[input_ids]:")
  for e in result['input_ids'] :
    print(e)
  print("[token_type_ids]:")
  for e in result['token_type_ids'] :
    print(e)
  print("[attention_mask]:")
  for e in result['attention_mask'] :
    print(e)

引数を設定しない場合

まず、何も引数を指定しない場合の動きを確認します。

result = tokenizer(texts)
print_result(result)

出力の意味はおおよそ、以下の通りです

  • input_idsは、テキストをトークン化したものになります
  • token_type_idsは、特殊なタイプがなければ0です
  • attention_maskは、パティングされた部分が0になります。

それぞれ、トークン化されていますが、長さはまちまちです。それぞれ、異なるサイズでトークン化されていることがわかります。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 2651, 1012, 102]
[101, 1996, 3103, 12342, 2015, 14224, 2006, 1037, 3154, 2621, 2851, 1012, 102]
[101, 2651, 2003, 2986, 1012, 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]
[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]
[1, 1, 1, 1, 1, 1]
トークンを逆変換する方法

トークンを逆変換するには、tokenizer.encode関数を利用します。

以下は、texts[0]をトークン化した、input_idsを逆変換する例です。

result = tokenizer(texts[0])
for e in result['input_ids']:
  r = tokenizer.decode(e, skip_special_tokens=True)
  print(e, r)

注意:トークン化された結果は、完全に元のテキストには戻りません

padding=Trueを設定

padding=Trueを設定した場合の挙動です。

result = tokenizer(texts, padding=True)
print_result(result)

padding=Trueにすると、入力したテキストのトークン数が一番多いものに合わせて0が挿入されていることがわかります。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 2651, 1012, 102, 0, 0, 0, 0]
[101, 1996, 3103, 12342, 2015, 14224, 2006, 1037, 3154, 2621, 2851, 1012, 102]
[101, 2651, 2003, 2986, 1012, 102, 0, 0, 0, 0, 0, 0, 0]
[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, 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, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]

padding=True, max_length=20を設定

padding=True, max_length=20を設定した場合の動きを確認してみました

result = tokenizer(texts, padding=True, max_length=20)
print_result(result)

予想と異なり、padding=Trueとした場合と同じです。予想ではmax_lengthで指定した20トークンになるようにパディングされると思っていましたが実際の動作は異なります要注意です。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 2651, 1012, 102, 0, 0, 0, 0]
[101, 1996, 3103, 12342, 2015, 14224, 2006, 1037, 3154, 2621, 2851, 1012, 102]
[101, 2651, 2003, 2986, 1012, 102, 0, 0, 0, 0, 0, 0, 0]
[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, 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, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]

padding="max_length", max_length=20を設定

調べてみるとpaddingにはmax_lengthを指定できるようです

result = tokenizer(texts, padding="max_length", max_length=20)
print_result(result)

この設定を行うと、max_length以下の部分にパディングが行われるようになりました。max_lengthに合わせてパディングを行いたい場合には、この設定を行う必要があるようです。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 2651, 1012, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[101, 1996, 3103, 12342, 2015, 14224, 2006, 1037, 3154, 2621, 2851, 1012, 102, 0, 0, 0, 0, 0, 0, 0]
[101, 2651, 2003, 2986, 1012, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[attention_mask]:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

padding=True, truncation=True, max_length=7を設定

truncationの動きを確認するために、上記の設定を行ってみました。max_lengthを7に設定したのは、一番短いテキストが6トークンなので、少ない場合の挙動がどうなるかを確認するためです。

result = tokenizer(texts, padding=True, truncation=True, max_length=7)
print_result(result)

こちらの設定で、7文字以上の部分はカット、7文字以下の部分はパディングという所望通りの動作をしました。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 102]
[101, 1996, 3103, 12342, 2015, 14224, 102]
[101, 2651, 2003, 2986, 1012, 102, 0]
[token_type_ids]:
[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, 0]

padding=True, truncation=False, max_length=5を設定

truncation=Falseを設定するとどうなるでしょうか。

result = tokenizer(texts, padding=True, truncation=False, max_length=5)
print_result(result)

想定通り打ち切りがなくなりました。padding=Trueにしているので、一番長いトークン数に合わせてパディングは行われています。

[input_ids]:
[101, 1996, 3103, 12342, 2015, 14224, 2651, 1012, 102, 0, 0, 0, 0]
[101, 1996, 3103, 12342, 2015, 14224, 2006, 1037, 3154, 2621, 2851, 1012, 102]
[101, 2651, 2003, 2986, 1012, 102, 0, 0, 0, 0, 0, 0, 0]
[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, 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, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]

まとめ

疑問を感じたのでトークナイザーの動きを確認してみました。

参考になれば幸いです

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました