🦔

transformersのtutorialを読んでみた - part2

2022/09/15に公開約5,700字

はじめに

最近transformersというライブラリを勉強していて、今はtutorialsを読んでいます!せっかくなので、tutorialsの内容を訳して記事にしようと思っています!今回は第二弾ということで、以下の記事を参考に書きました~ ぜひ最後まで読んでください!(google colabで実装しながら読んで頂けると良いと思います!)
なお、以下の記事では言語、音声、画像の処理について説明していますが、本記事では言語の処理の説明のみとなります。(今後、音声、画像についても記事を書きたいと思っています。)

https://huggingface.co/docs/transformers/preprocessing

Preprocess

モデルでデータを使用する前に、データをモデルに受け入れられる形式に処理する必要があります。モデルは生のテキスト、画像、音声を理解できません。これらの入力を数値に変換し、テンソルにする必要があります。

NLP

テキストデータを処理するための主なツールはトークナイザーです。 トークナイザーは、一連のルールに従ってテキストをトークンに分割することから始めます。トークンは数値に変換され、モデルへの入力としてテンソルを構築するために使用されます。モデルに必要な追加の入力も、トークナイザーによって追加されます。

事前学習済みモデルの使用をしたい場合は、そのモデルと関連する事前学習済みトークナイザーを使用することが重要です。これにより、テキストが事前学習の時と同じ方法で分割され、事前学習に対応するトークンとインデックスが使用されます。

AutoTokenizerクラスを使用して事前学習済みのトークナイザーをロードします。

Tokenize

学習済みのトークナイザーをAutoTokenizer.from_pretrained()でロードします。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

次に、文章をトークナイザーに入力します。

encoded_input = tokenizer("Do not meddle in the affairs of wizards, for they are subtle and quick to anger.")
print(encoded_input)

・出力
{'input_ids': [101, 2091, 1136, 1143, 13002, 1107, 1103, 5707, 1104, 16678, 1116, 117, 1111, 1152, 1132, 11515, 1105, 3613, 1106, 4470, 119, 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]}

出力から分かるように、トークナイザーは次の3つの重要な項目を含む辞書を返します。
・input_ids:文の各トークンに対応するインデックス
・Attention_mask:トークンに注意を払う必要があるかどうかを表す数字
token_type_ids:複数の配列がある場合にトークンが属する配列を識別する数字

また、input_idsをデコードして元の入力を返すことができます

tokenizer.decode(encoded_input["input_ids"])

・出力
[CLS] Do not meddle in the affairs of wizards, for they are subtle and quick to anger. [SEP]

出力のように、トークナイザーはCLSとSEP(分類子と区切り記号)という2つの特別なトークンを文に追加しました。すべてのモデルに特別なトークンが必要なわけではありませんが、必要な場合はトークナイザーが自動的に追加します。

複数の文を処理したい場合は、文をリストとしてトークナイザーに渡します。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_inputs = tokenizer(batch_sentences)
print(encoded_inputs)

・出力
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 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, 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, 1, 1]]}

Pad

複数の文を処理するとき、それらは常に同じ長さではありません。しかし、モデルへの入力であるテンソルは均一な形状である必要があります。そこで、padding パラメーターをTrueに設定して、バッチ内の短い配列をパディングして、最長の配列に一致させます。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True)
print(encoded_input)

・出力
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 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]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 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, 0]]}

出力から分かるように、1つ目と3つ目の文章については、2つ目の文章に比べて短かったため、長さを揃えるために0でpaddingされていることが分かります。

Truncation

配列が長すぎてモデルが処理できない場合があります。この場合は先ほどとは反対に配列を短く切り詰める必要があります。

truncationパラメーターをTrueに設定して、モデルが受け入れる最大の長さに配列を切り捨てます。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True)
print(encoded_input)

・出力
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], [101, 1327, 1164, 5450, 23434, 136, 102, 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]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 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, 0]]}

Build tensors

最後に、モデルに入力できるようにするために、トークナイザーがテンソルを返すようにします。

return_tensorsパラメーターを、PyTorchの場合はptに、TensorFlowの場合はtfに設定します。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True, return_tensors="pt")
print(encoded_input)

・出力
{'input_ids': tensor([[ 101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0,
0, 0, 0, 0, 0],
[ 101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462,
117, 21902, 1643, 119, 102],
[ 101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0,
0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 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, 0]])}

おわりに

最後まで読んで頂き、ありがとうございました!言語データの前処理は難しいイメージがありましたが、transformersを使うと思ったより簡単にできそうですね!

GitHubで編集を提案

Discussion

ログインするとコメントできます