ホーム » HuggingFace Transformers » HuggingFace Transformers 4.17 : ガイド : 下流タスク用の再調整 – 質問応答

HuggingFace Transformers 4.17 : ガイド : 下流タスク用の再調整 – 質問応答

HuggingFace Transformers 4.17 : ガイド : 下流タスク用の再調整 – 質問応答 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/28/2022 (v4.17.0)

* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

HuggingFace Transformers : ガイド : 下流タスク用の再調整 – 質問応答

質問応答タスクは質問が与えられたときに答えを返します。質問応答の 2 つの一般的な形式があります :

  • Extractive : 与えられたコンテキストから答えを抽出する。

  • Abstractive : コンテキストから正しく質問に答える答えを生成する。

このガイドは extractive 質問応答に対して SQuAD データセット上で DistilBERT を再調整する方法を示します。

Note : 質問応答と関連するモデル、データセットとメトリクスの他の形式についての詳細は質問応答 タスクのページ を見てください。

 

SQuAD データセットのロード

Datasets ライブラリから SQuAD データセットをロードします :

from datasets import load_dataset

squad = load_dataset("squad")

そしてサンプルを見てみましょう :

squad["train"][0]
{'answers': {'answer_start': [515], 'text': ['Saint Bernadette Soubirous']},
 'context': 'Architecturally, the school has a Catholic character. Atop the Main Building\'s gold dome is a golden statue of the Virgin Mary. Immediately in front of the Main Building and facing it, is a copper statue of Christ with arms upraised with the legend "Venite Ad Me Omnes". Next to the Main Building is the Basilica of the Sacred Heart. Immediately behind the basilica is the Grotto, a Marian place of prayer and reflection. It is a replica of the grotto at Lourdes, France where the Virgin Mary reputedly appeared to Saint Bernadette Soubirous in 1858. At the end of the main drive (and in a direct line that connects through 3 statues and the Gold Dome), is a simple, modern stone statue of Mary.',
 'id': '5733be284776f41900661182',
 'question': 'To whom did the Virgin Mary allegedly appear in 1858 in Lourdes France?',
 'title': 'University_of_Notre_Dame'
}

answers フィールドは答えの開始位置と答えのテキストを含む辞書です。

 

前処理

質問とコンテキストフィールドを処理するために DistilBERT トークナイザーをロードします :

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")

知っておくべき、質問応答に特有の幾つかの前処理ステップがあります :

  1. データセットの幾つかのサンプルは、モデルの最大入力長を超える、非常に長いコンテキストを持っている可能性があります。truncation=”only_second” を設定することでコンテキストだけを切り詰めてください。

  2. 次に、return_offset_mapping=True を設定して答えの開始と終了位置を元のコンテキストにマップします。

  3. 手動でマッピングすることで、答えの開始と終了トークンを見つけることができます。sequence_ids メソッドを使用して、オフセットのどの部分が質問に対応するのか、そしてどの部分がコンテキストに対応するのか見つけられます。

ここに、切り詰めて、答えの開始とトークンをコンテキストにマップする関数を作成できる方法があります :

def preprocess_function(examples):
    questions = [q.strip() for q in examples["question"]]
    inputs = tokenizer(
        questions,
        examples["context"],
        max_length=384,
        truncation="only_second",
        return_offsets_mapping=True,
        padding="max_length",
    )

    offset_mapping = inputs.pop("offset_mapping")
    answers = examples["answers"]
    start_positions = []
    end_positions = []

    for i, offset in enumerate(offset_mapping):
        answer = answers[i]
        start_char = answer["answer_start"][0]
        end_char = answer["answer_start"][0] + len(answer["text"][0])
        sequence_ids = inputs.sequence_ids(i)

        # Find the start and end of the context
        idx = 0
        while sequence_ids[idx] != 1:
            idx += 1
        context_start = idx
        while sequence_ids[idx] == 1:
            idx += 1
        context_end = idx - 1

        # If the answer is not fully inside the context, label it (0, 0)
        if offset[context_start][0] > end_char or offset[context_end][1] < start_char:
            start_positions.append(0)
            end_positions.append(0)
        else:
            # Otherwise it's the start and end token positions
            idx = context_start
            while idx <= context_end and offset[idx][0] <= start_char:
                idx += 1
            start_positions.append(idx - 1)

            idx = context_end
            while idx >= context_start and offset[idx][1] >= end_char:
                idx -= 1
            end_positions.append(idx + 1)

    inputs["start_positions"] = start_positions
    inputs["end_positions"] = end_positions
    return inputs

データセット全体に対して前処理関数を適用するためには Datasets map 関数を使用します。データセットの複数の要素を一度に処理する batched=True を設定することで map 関数を高速化できます。必要でないカラムは削除します :

tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)

サンプルのバッチを作成するために DefaultDataCollator を使用します。 Transformers の他のデータ collator とは違い、DefaultDataCollator はパディングのような追加の前処理を適用しません。

PyTorch
from transformers import DefaultDataCollator

data_collator = DefaultDataCollator()
TensorFlow
from transformers import DefaultDataCollator

data_collator = DefaultDataCollator(return_tensors="tf")

 

Trainer で再調整

AutoModelForQuestionAnswering で DistilBERT をロードします :

from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer

model = AutoModelForQuestionAnswering.from_pretrained("distilbert-base-uncased")

この時点で、3 つのステップだけが残っています :

  1. TrainingArguments で訓練ハイパーパラメータを定義します。

  2. 訓練引数をモデル、データセット、トークナイザー、そしてデータ collator と共に Trainer に渡します。

  3. モデルを再調整するために train() を呼び出します。
training_args = TrainingArguments(
    output_dir="./results",
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=3,
    weight_decay=0.01,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_squad["train"],
    eval_dataset=tokenized_squad["validation"],
    tokenizer=tokenizer,
    data_collator=data_collator,
)

trainer.train()

 

TensorFlow による再調整

TensorFlow でモデルを再調整することは、幾つかの違いはありますが、同様に簡単です。

データセットを to_tf_dataset で tf.data.Dataset 形式に変換します。columns で入力と答えの開始と終了位置を、データセット順序をシャッフルするか否か、バッチサイズ、そしてデータ collator を指定します :

tf_train_set = tokenized_squad["train"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "start_positions", "end_positions"],
    dummy_labels=True,
    shuffle=True,
    batch_size=16,
    collate_fn=data_collator,
)

tf_validation_set = tokenized_squad["validation"].to_tf_dataset(
    columns=["attention_mask", "input_ids", "start_positions", "end_positions"],
    dummy_labels=True,
    shuffle=False,
    batch_size=16,
    collate_fn=data_collator,
)

optimizer 関数, 学習率スケジュール, そして幾つかの訓練ハイパーパラメータをセットアップします :

from transformers import create_optimizer

batch_size = 16
num_epochs = 2
total_train_steps = (len(tokenized_squad["train"]) // batch_size) * num_epochs
optimizer, schedule = create_optimizer(
    init_lr=2e-5,
    num_warmup_steps=0,
    num_train_steps=total_train_steps,
)

TFAutoModelForQuestionAnswering で DistilBERT をロードします :

from transformers import TFAutoModelForQuestionAnswering

model = TFAutoModelForQuestionAnswering("distilbert-base-uncased")

compile で訓練するためにモデルを configure します :

import tensorflow as tf

model.compile(optimizer=optimizer)

モデルを再調整するために fit を呼び出します :

model.fit(x=tf_train_set, validation_data=tf_validation_set, epochs=3)

Note : 質問応答のためにモデルを再調整する方法の詳細なサンプルについては、対応する PyTorch ノートブックTensorFlow ノートブック を見てください。

 

以上



ClassCat® Chatbot

人工知能開発支援
クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com

カテゴリー