HuggingFace Transformers 4.29 : Tutorials : 前処理 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/26/2023 (v4.29.1)
* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
HuggingFace Transformers 4.29 : Tutorials : 前処理
モデルをデータセットで訓練できる前にそれは想定されるモデル入力形式に前処理される必要があります。データがテキスト、画像あるいは音声であれ、それらはテンソルのバッチに変換されて集められる必要があります。🤗 Transformers はモデル用にデータを準備するのを支援するために前処理クラスのセットを提供しています。このチュートリアルでは、以下のためにそれらを学習します :
- テキストでは、トークナイザー を使用してテキストをトークンのシークエンスに変換し、トークンの数値表現を作成し、そしてそれらをテンソルに集めます。
- 発話と音声では、特徴抽出器 を使用して音声波形からシーケンシャルな特徴を抽出してそれらをテンソルに変換します。
- 画像入力は画像をテンソルに変換するために ImageProcessor を使用します。
- マルチモーダル入力では、トークナイザーと特徴抽出器や画像プロセッサを組み合わせるために Processor を使用します。
AutoProcessor は常に動作し、トークナイザー、画像プロセッサ、特徴抽出器やプロセッサのいずれを使用していても、使用しているモデルに対して正しいクラスを自動的に選択します、
Before you begin, install 🤗 Datasets so you can load some datasets to experiment with:
pip install datasets
自然言語処理
テキストデータを処理する主要ツールは トークナイザー です。トークナイザーはルールのセットに従ってテキストをトークンに分割することから始めます。トークンは数値に、それからテンソルに変換されます、これはモデル入力となります。モデルにより必要とされる任意の追加の入力もまたトークナイザーにより追加されます。
事前訓練モデルを使用する計画があれば、関連する事前訓練済みトークナイザーを使用することは重要です。これは、テキストが事前訓練済みコーパスと同じ方法で分割されて、事前訓練の間に同じ対応するトークン-to-インデックス (通常は語彙として参照) を使用することを保証します。
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, 2079, 2025, 19960, 10362, 1999, 1996, 3821, 1997, 16657, 1010, 2005, 2027, 2024, 11259, 1998, 4248, 2000, 4963, 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], 'attention_mask': [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]'
ご覧になれるように、トークナイザーはセンテンスに 2 つの特殊トークン – CLS と SEP (classifier と separator) – を追加しました。総てのモデルが特殊トークンを必要とはしませんが、必要な場合には、トークナイザーはそれらを自動的に追加します。
前処理したい幾つかのセンテンスがある場合には、それらをリストとしてトークナイザーに渡します :
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]]}
パッド
センテンスは常に同じ長さとは限りません、これは問題となる可能性があります、テンソル、モデル入力は均一な shape を持つ必要があるからです。パディングは、より短いセンテンスに特殊なパディング・トークンを追加することによりテンソルが矩形であることを保証するストラテジーです。
最長のシークエンスに一致するようにバッチの短いシークエンスをパッドするために 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]]}
最初と 3 番目のセンテンスは 0 でパディングされています、それらは短いからです。
Truncation
その対極として、シークエンスがモデルが処理するには長すぎる場合も時々あるかもしれません。この場合、シークエンスを短い長さに切り詰める (truncate) 必要があります。
シークエンスをモデルが受け取れる最大長に切り詰めるには 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]]}
Check out the Padding and truncation concept guide to learn more different padding and truncation arguments.
テンソルの構築
最後に、トークナイザーにモデルに供給される実際のテンソルを返すことを望みます。
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]])}
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="tf")
print(encoded_input)
{'input_ids': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[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]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[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]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(2, 9), dtype=int32, numpy= array([[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]], dtype=int32)>}
音声
音声タスクについては、モデル用にデータセットを準備するためには 特徴抽出器 が必要です。特徴抽出器は raw 音声データから特徴を抽出して、それらをテンソルに変換するように設計されています。
特徴抽出器を音声データセットで使用できる方法を見るため MInDS-14 データセットをロードします (データセットをロードする方法の詳細は 🤗 Datasets チュートリアル 参照) :
from datasets import load_dataset, Audio
dataset = load_dataset("PolyAI/minds14", name="en-US", split="train")
入力を見るために音声カラムの最初の要素にアクセスします。audio カラムを呼び出すと音声ファイルを自動的にロードして再サンプリングします :
dataset[0]["audio"]
{'array': array([ 0. , 0.00024414, -0.00024414, ..., -0.00024414, 0. , 0. ], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav', 'sampling_rate': 8000}
これは 3 つの項目を返します :
- array は 1D 配列としてロードされて – そして潜在的に再サンプリングされた – 発話信号です。
- path は音声ファイルの位置を指します。
- sampling_rate は発話信号の幾つのデータポイントが毎秒測定されたかを示します。
このチュートリアルについては、Wav2Vec2 モデルを使用します。モデルカードを見れば、Wav2Vec2 は 16kHz でサンプリングされた発話音声で事前訓練されたことがわかります。貴方の音声データのサンプリングレートがモデルを事前訓練するために使用されたデータセットのサンプリングレートに一致していることは重要です。もしデータのサンプリングレートが同じでないなら、データを再サンプリングする必要があります。
- サンプリングレートを 16kHz にアップサンプリングするために 🤗 Datasets の cast_column メソッドを使用します :
dataset = dataset.cast_column("audio", Audio(sampling_rate=16_000))
- 音声ファイルを再サンプリングするために audio カラムを再度呼び出します :
dataset[0]["audio"]
{'array': array([ 2.3443763e-05, 2.1729663e-04, 2.2145823e-04, ..., 3.8356509e-05, -7.3497440e-06, -2.1754686e-05], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/f14948e0e84be638dd7943ac36518a4cf3324e8b7aa331c5ab11541518e9368c/en-US~JOINT_ACCOUNT/602ba55abb1e6d0fbce92065.wav', 'sampling_rate': 16000}
次に、入力を正規化してパディングする特徴抽出器をロードします。テキストデータをパディングするとき、短いセンテンスに 0 が追加されます。同じアイデアが音声データにも適用されます。特徴抽出器は配列に 0 – 無音 (silence) として解釈されます – を追加します。
特徴抽出器を AutoFeatureExtractor.from_pretrained() でロードします :
from transformers import AutoFeatureExtractor
feature_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-base")
音声配列を特徴抽出器に渡します。発生するかもしれない無音エラーをより上手くデバッグするために、特徴抽出器に sampling_rate 引数を追加することも勧めます。
audio_input = [dataset[0]["audio"]["array"]]
feature_extractor(audio_input, sampling_rate=16000)
{'input_values': [array([ 3.8106556e-04, 2.7506407e-03, 2.8015103e-03, ..., 5.6335266e-04, 4.6588284e-06, -1.7142107e-04], dtype=float32)]}
ちょうどトークナイザーのように、バッチの可変なシークエンスを処理するためにパディングや切り捨て (truncation) を適用することができます。それら 2 つの音声サンプルのシークエンス長を見てみましょう :
dataset[0]["audio"]["array"].shape
dataset[1]["audio"]["array"].shape
(173398,) (106496,)
音声サンプルが同じ長さになるようにデータセットを前処理する関数を作成します。最大サンプル長を指定すると、特徴抽出器はそれに一致するようにシークエンスをパディングするか切り捨てます :
def preprocess_function(examples):
audio_arrays = [x["array"] for x in examples["audio"]]
inputs = feature_extractor(
audio_arrays,
sampling_rate=16000,
padding=True,
max_length=100000,
truncation=True,
)
return inputs
データセットの最初の幾つかのサンプルに preprocess_function 関数を適用します :
processed_dataset = preprocess_function(dataset[:5])
サンプルの長さは同じになり、指定された最大長に一致しました。今では処理されたデータセットをモデルに渡すことができます!
processed_dataset["input_values"][0].shape
processed_dataset["input_values"][1].shape
(100000,) (100000,)
コンピュータビジョン
コンピュータビジョン・タスクについては、モデル用にデータセットを準備するために 画像プロセッサ が必要です。画像前処理は、画像をモデルが想定する入力に変換する幾つかのステップから構成されます。これらのステップはリサイズ、正規化、カラーチャネル補正、そして画像のテンソルへの変換を含みますが、それらに限定されるわけではありません。
画像プロセッサをどのように使用できるか見るために food101 データセット (データセットをロードする方法の詳細は 🤗 Datasets チュートリアル 参照) をロードします :
Use 🤗 Datasets split parameter to only load a small sample from the training split since the dataset is quite large!
from datasets import load_dataset
dataset = load_dataset("food101", split="train[:100]")
次に、 Datasets の Image 特徴で画像を見てみましょう :
dataset[0]["image"]
AutoImageProcessor.from_pretrained() で画像プロセッサをロードします :
from transformers import AutoImageProcessor
image_processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224")
まず、幾つかのデータ増強を追加しましょう。好みのライブラリを使用できますが、このチュートリアルでは、torchvision の transforms モジュールを使用します。他のデータ増強ライブラリを使用することに興味があれば、Albumentations や Kornia ノートブック でその方法を学習してください。
- ここでは幾つかの transforms – RandomResizedCrop と ColorJitter – をまとめて連鎖するために Compose を使用します。リサイズについては、image_processor で画像サイズ要件を取得できることに注意してください。幾つかのモデルについては、正確な高さと幅が想定されていて、他のものは shortest_edge が定義されているだけです。
from torchvision.transforms import RandomResizedCrop, ColorJitter, Compose size = ( image_processor.size["shortest_edge"] if "shortest_edge" in image_processor.size else (image_processor.size["height"], image_processor.size["width"]) ) _transforms = Compose([RandomResizedCrop(size), ColorJitter(brightness=0.5, hue=0.5)])
- モデルはその入力として pixel_values を受け取ります。ImageProcessor は画像の正規化、そして適切なテンソルの生成を処理できます。画像のバッチに対して画像増強と画像前処理を組み合わせて pixel_values を生成する関数を作成します :
def transforms(examples): images = [_transforms(img.convert("RGB")) for img in examples["image"]] examples["pixel_values"] = image_processor(images, do_resize=False, return_tensors="pt")["pixel_values"] return examples
- それから transforms を on-the-fly に適用するために 🤗 データセットの set_transform を使用します :
dataset.set_transform(transforms)
- そして画像にアクセスするとき、画像プロセッサが pixel_values を追加したことに気付くでしょう。今は処理されたデータセットをモデルに渡すことができます!
dataset[0].keys()
ここに画像が transforms が適用された後どのように見えるかがあります。画像はランダムにクロップされてそのカラー特性は異なっています。
import numpy as np
import matplotlib.pyplot as plt
img = dataset[0]["pixel_values"]
plt.imshow(img.permute(1, 2, 0))
For tasks like object detection, semantic segmentation, instance segmentation, and panoptic segmentation, ImageProcessor offers post processing methods. These methods convert model’s raw outputs into meaningful predictions such as bounding boxes, or segmentation maps.
パッド
幾つかのケースでは、例えば、DETR を再調整するとき、モデルは訓練中にスケール増強を適用します。これはバッチ内で画像が異なるサイズになる可能性があります。DetrImageProcessor から DetrImageProcessor.pad_and_create_pixel_mask() を使用して画像をまとめてバッチ処理するカスタム collate_fn を定義することができます。
def collate_fn(batch):
pixel_values = [item["pixel_values"] for item in batch]
encoding = image_processor.pad_and_create_pixel_mask(pixel_values, return_tensors="pt")
labels = [item["labels"] for item in batch]
batch = {}
batch["pixel_values"] = encoding["pixel_values"]
batch["pixel_mask"] = encoding["pixel_mask"]
batch["labels"] = labels
return batch
マルチモーダル
マルチモーダル入力を含むタスクについては、モデル用のデータセットを準備する プロセッサ が必要です。プロセッサは、トークナイザーと特徴抽出器のような 2 つの処理オブジェクトを連結します。
自動発話認識 (ASR) 用のプロセッサがどのように使用できるかを見るために LJ Speech データセットをロードします :
from datasets import load_dataset
lj_speech = load_dataset("lj_speech", split="train")
ASR については、audio と text に主としてフォーカスしていますので他のカラムを削除できます :
lj_speech = lj_speech.map(remove_columns=["file", "id", "normalized_text"])
そして audio と text カラムを見てみましょう :
lj_speech[0]["audio"]
lj_speech[0]["text"]
{'array': array([-7.3242188e-04, -7.6293945e-04, -6.4086914e-04, ..., 7.3242188e-04, 2.1362305e-04, 6.1035156e-05], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/917ece08c95cf0c4115e45294e3cd0dee724a1165b7fc11798369308a465bd26/LJSpeech-1.1/wavs/LJ001-0001.wav', 'sampling_rate': 22050} 'Printing, in the only sense with which we are at present concerned, differs from most if not from all the arts and crafts represented in the Exhibition'
貴方の音声データセットのサンプリングレートをモデルを事前訓練するために使用されたデータセットのサンプリングレートに一致するように、常に 再サンプリングする 必要があることを忘れないでください!
lj_speech = lj_speech.cast_column("audio", Audio(sampling_rate=16_000))
AutoProcessor.from_pretrained() でプロセッサをロードします :
from transformers import AutoProcessor
processor = AutoProcessor.from_pretrained("facebook/wav2vec2-base-960h")
- 配列に含まれる音声データを input_values に処理して、テキストをラベルにトークン化する関数を作成します。これらはモデルへの入力です :
def prepare_dataset(example): audio = example["audio"] example.update(processor(audio=audio["array"], text=example["text"], sampling_rate=16000)) return example
- prepare_dataset 関数をサンプルに適用します :
prepare_dataset(lj_speech[0])
プロセッサは input_values とラベルを追加しました、そしてサンプリングレートもまた 16kHz に正しくダウンサンプリングされました。今では処理されたデータセットをモデルに渡すことができます!
以上