Transformers documentation
質問応答
質問応答
Question answeringタスクでは、質問が与えられた際に回答を返します。Alexa、Siri、Googleなどの仮想アシスタントに天気について尋ねたことがあるなら、すでにquestion answeringモデルを使用した経験があることになります。question answeringタスクには、一般的な2つのタイプがあります。
- 抽出型: 与えられたコンテキストから回答を抽出します。
- Abstractive: 文脈から質問に正しく答える回答を生成します。
このガイドでは、次の方法について説明します:
- SQuADデータセットで抽出型質問応答のためにDistilBERTをファインチューニングします。
- ファインチューニングしたモデルを推論に使用します。
このタスクと互換性のあるすべてのアーキテクチャとチェックポイントを確認するには、タスクページを参照することをお勧めします。
開始前に、必要なライブラリがすべてインストールされていることを確認してください。
pip install transformers datasets evaluate
Hugging Face アカウントにログインして、モデルをアップロードし、コミュニティと共有できるようにすることを推奨します。プロンプトが表示されたら、ログイン用のトークンを入力してください:
>>> from huggingface_hub import notebook_login
>>> notebook_login()SQuAD データセットを読み込む
🤗 Datasets ライブラリから SQuAD データセットのより小さなサブセットを読み込むことから始めます。これにより、フルデータセットでの学習により多くの時間を費やす前に、実験を行い、すべてが正常に動作することを確認できます。
>>> from datasets import load_dataset
>>> squad = load_dataset("rajpurkar/squad", split="train[:5000]")train_test_split メソッドを使用して、データセットの train スプリットを学習セットとテストセットに分割します:
>>> squad = squad.train_test_split(test_size=0.2)次に、例を見てみましょう。
>>> 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: 回答トークンの開始位置と回答テキスト。context: モデルが回答を抽出する必要がある背景情報。question: モデルが回答すべき質問。
前処理
次のステップは、question と context フィールドを処理するために DistilBERT のトークナイザーを読み込むことです。
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")質問応答タスク特有の、注意すべき前処理ステップがいくつかあります。
- データセット内の一部の例では、モデルの最大入力長を超える非常に長い
contextが含まれている場合があります。より長いシーケンスを処理するには、truncation="only_second"を設定して、contextのみを切り捨てます。 - 次に、
return_offset_mapping=Trueを設定して、回答の開始位置と終了位置を元のcontextにマッピングします。 - マッピングが手に入ったら、次に回答の開始トークンと終了トークンを特定できます。
sequence_idsメソッドを使用して、オフセットのどの部分がquestionに対応し、どの部分がcontextに対応しているかを確認します。
answerの開始トークンと終了トークンをcontextに切り詰め、マッピングする関数を作成するには、次のようにします。
>>> 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 関数を使用します。map 関数では、batched=True を設定することで、データセットの複数の要素を一度に処理し、処理を高速化できます。不要な列は削除してください:
>>> tokenized_squad = squad.map(preprocess_function, batched=True, remove_columns=squad["train"].column_names)次に、DefaultDataCollatorを使用してバッチの例を作成します。🤗 Transformersの他のデータコラレーターとは異なり、DefaultDataCollatorはパディングなどの追加の前処理を適用しません。
>>> from transformers import DefaultDataCollator
>>> data_collator = DefaultDataCollator()学習
Trainer を用いたモデルのファインチューニングに慣れていない場合は、こちらの基本的なチュートリアルをご覧ください!
いよいよモデルの学習を開始できます!AutoModelForQuestionAnsweringでDistilBERTを読み込みましょう:
>>> from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer
>>> model = AutoModelForQuestionAnswering.from_pretrained("distilbert/distilbert-base-uncased")この時点で、残りのステップは3つだけです:
- TrainingArguments で学習ハイパーパラメータを定義します。必須のパラメータは
output_dirのみで、モデルの保存先を指定します。push_to_hub=Trueを設定することで、このモデルを Hub にプッシュできます(モデルをアップロードするには、Hugging Face にサインインしている必要があります)。 - モデル、データセット、トークナイザー、データコラクターとともに、学習引数をTrainerに渡します。
- train()を呼び出して、モデルをファインチューニングします。
>>> training_args = TrainingArguments(
... output_dir="my_awesome_qa_model",
... eval_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,
... push_to_hub=True,
... )
>>> trainer = Trainer(
... model=model,
... args=training_args,
... train_dataset=tokenized_squad["train"],
... eval_dataset=tokenized_squad["test"],
... processing_class=tokenizer,
... data_collator=data_collator,
... )
>>> trainer.train()トレーニングが完了したら、push_to_hub() メソッドでモデルを Hub に共有し、誰でもそのモデルを利用できるようにしてください。
>>> trainer.push_to_hub()質問応答モデルのファインチューニング方法に関するより詳細な例については、対応するPyTorchノートブックを参照してください。
評価
質問応答の評価には、大量の後処理が必要です。時間をかけすぎないよう、このガイドでは評価ステップを省略します。Trainerはトレーニング中に評価損失を計算するため、モデルの性能について完全に把握できないわけではありません。
時間に余裕があり、質問応答モデルの評価方法について知りたい場合は、🤗 Hugging Face Course の Question answering 章をご覧ください!
推論
素晴らしいですね。モデルのファインチューニングが完了したので、推論に使用できます!
モデルに予測させたい質問と、その文脈を考えましょう:
>>> question = "How many programming languages does BLOOM support?"
>>> context = "BLOOM has 176 billion parameters and can generate text in 46 languages natural languages and 13 programming languages."テキストをトークン化し、PyTorch テンソルを返します:
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("my_awesome_qa_model")
>>> inputs = tokenizer(question, context, return_tensors="pt")入力をモデルに渡し、logitsを返します:
>>> import torch
>>> from transformers import AutoModelForQuestionAnswering
>>> model = AutoModelForQuestionAnswering.from_pretrained("my_awesome_qa_model")
>>> with torch.no_grad():
... outputs = model(**inputs)モデルの出力から、開始位置と終了位置の最高確率を取得します:
>>> answer_start_index = outputs.start_logits.argmax()
>>> answer_end_index = outputs.end_logits.argmax()予測されたトークンをデコードして、回答を取得します:
>>> predict_answer_tokens = inputs.input_ids[0, answer_start_index : answer_end_index + 1]
>>> tokenizer.decode(predict_answer_tokens)
'176 billion parameters and can generate text in 46 languages natural languages and 13'