객관식

객관식 (Multiple choice)

객관식 작업은 질의응답과 유사하지만, 문맥과 함께 여러 후보 답변이 제공되고 모델이 올바른 답변을 선택하도록 훈련된다는 점이 다릅니다.

출처: 문서

본문

이 가이드에서 다룰 내용은 다음과 같습니다.

  1. SWAG 데이터셋의 regular 구성에서 BERT를 파인튜닝해 여러 옵션과 문맥이 주어졌을 때 최상의 답변을 선택합니다.
  2. 파인튜닝된 모델을 추론(inference)에 사용합니다.

시작하기 전에 필요한 라이브러리를 모두 설치했는지 확인해 주세요.

pip install transformers datasets evaluate

Hugging Face 계정에 로그인해 모델을 커뮤니티에 업로드하고 공유하는 것을 권장합니다. 프롬프트가 나타나면 토큰을 입력해 로그인하세요.

>>> from huggingface_hub import notebook_login

>>> notebook_login()

SWAG 데이터셋 로드하기

🤗 Datasets 라이브러리에서 SWAG 데이터셋의 regular 구성을 로드하는 것부터 시작하겠습니다.

>>> from datasets import load_dataset

>>> swag = load_dataset("swag", "regular")

그런 다음 예시를 하나 살펴봅니다.

>>> swag["train"][0]
{'ending0': 'passes by walking down the street playing their instruments.',
 'ending1': 'has heard approaching them.',
 'ending2': "arrives and they're outside dancing and asleep.",
 'ending3': 'turns the lead singer watches the performance.',
 'fold-ind': '3416',
 'gold-source': 'gold',
 'label': 0,
 'sent1': 'Members of the procession walk down the street holding small horn brass instruments.',
 'sent2': 'A drum line',
 'startphrase': 'Members of the procession walk down the street holding small horn brass instruments. A drum line',
 'video-id': 'anetv_jkn6uvmqwh4'}

여기 필드가 많은 것처럼 보이지만 실제로는 꽤 간단합니다.

  • sent1과 sent2: 이 필드들은 문장이 어떻게 시작되는지 보여줍니다. 둘을 합치면 startphrase 필드가 됩니다.
  • ending: 문장이 어떻게 끝날 수 있는지 가능한 결말을 제안하지만, 그중 하나만 정확합니다.
  • label: 올바른 문장 결말을 식별합니다.

전처리 (Preprocess)

다음 단계는 문장 시작 부분과 네 가지 가능한 결말을 처리할 BERT 토크나이저를 로드하는 것입니다.

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-uncased")

만들 전처리 함수는 다음을 수행해야 합니다.

  1. sent1 필드의 복사본 네 개를 만들고 각각을 sent2와 결합해 문장이 어떻게 시작되는지 재구성합니다.
  2. sent2를 네 가지 가능한 문장 결말 각각과 결합합니다.
  3. 이 두 리스트를 평탄화해 토크나이즈하고, 그 후 다시 언플래튼해 각 예시에 해당하는 input_ids, attention_mask, labels 필드를 갖게 합니다.
>>> ending_names = ["ending0", "ending1", "ending2", "ending3"]

>>> def preprocess_function(examples):
...     first_sentences = [[context] * 4 for context in examples["sent1"]]
...     question_headers = examples["sent2"]
...     second_sentences = [
...         [f"{header} {examples[end][i]}" for end in ending_names] for i, header in enumerate(question_headers)
...     ]

...     first_sentences = sum(first_sentences, [])
...     second_sentences = sum(second_sentences, [])

...     tokenized_examples = tokenizer(first_sentences, second_sentences, truncation=True)
...     return {k: [v[i : i + 4] for i in range(0, len(v), 4)] for k, v in tokenized_examples.items()}

이 전처리 함수를 전체 데이터셋에 적용하려면 🤗 Datasets map 메서드를 사용하세요. batched=True로 설정하면 데이터셋의 여러 요소를 한 번에 처리해 map 함수를 더 빠르게 할 수 있습니다.

>>> tokenized_swag = swag.map(preprocess_function, batched=True)

예시 배치를 만들 때는 전체 데이터셋을 최대 길이로 패딩하는 것보다 콜레이션 중에 문장을 배치에서 가장 긴 길이로 동적으로 패딩하는 것이 더 효율적입니다. DataCollatorForMultipleChoice는 모든 모델 입력을 평탄화하고 패딩을 적용한 다음 결과를 다시 언플래튼합니다.

>>> from transformers import DataCollatorForMultipleChoice
>>> collator = DataCollatorForMultipleChoice(tokenizer=tokenizer)

평가 (Evaluate)

훈련 중에 메트릭을 포함하는 것은 모델 성능을 평가하는 데 자주 도움이 됩니다. 🤗 Evaluate 라이브러리로 평가 방법을 빠르게 로드할 수 있습니다. 이 작업에서는 accuracy 메트릭을 로드합니다(메트릭 로드·계산 방법에 대해 더 알아보려면 🤗 Evaluate quick tour를 참고하세요).

>>> import evaluate

>>> accuracy = evaluate.load("accuracy")

그런 다음 compute에 예측값과 라벨을 전달해 정확도를 계산하는 함수를 만듭니다.

>>> import numpy as np

>>> def compute_metrics(eval_pred):
...     predictions, labels = eval_pred
...     predictions = np.argmax(predictions, axis=1)
...     return accuracy.compute(predictions=predictions, references=labels)

compute_metrics 함수가 준비되었습니다. 훈련을 설정할 때 다시 사용하게 됩니다.

훈련 (Train)

Trainer로 모델을 파인튜닝하는 방법이 익숙하지 않다면 여기의 기본 튜토리얼을 확인해 보세요!

이제 모델 훈련을 시작할 준비가 되었습니다! AutoModelForMultipleChoice으로 BERT를 로드합니다.

>>> from transformers import AutoModelForMultipleChoice, TrainingArguments, Trainer

>>> model = AutoModelForMultipleChoice.from_pretrained("google-bert/bert-base-uncased")

이 시점에서 남은 단계는 세 가지뿐입니다.

  1. TrainingArguments에서 훈련 하이퍼파라미터를 정의합니다. 유일한 필수 매개변수는 모델을 저장할 위치를 지정하는 output_dir입니다. push_to_hub=True로 설정하면 이 모델을 Hub에 푸시합니다(모델을 업로드하려면 Hugging Face에 로그인해야 합니다). 각 에폭이 끝날 때마다 Trainer는 정확도를 평가하고 훈련 체크포인트를 저장합니다.
  2. Trainer에 모델, 데이터셋, 토크나이저, 데이터 콜레이터, compute_metrics 함수와 함께 훈련 인자를 전달합니다.
  3. train()을 호출해 모델을 파인튜닝합니다.
>>> training_args = TrainingArguments(
...     output_dir="my_awesome_swag_model",
...     eval_strategy="epoch",
...     save_strategy="epoch",
...     load_best_model_at_end=True,
...     learning_rate=5e-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_swag["train"],
...     eval_dataset=tokenized_swag["validation"],
...     processing_class=tokenizer,
...     data_collator=collator,
...     compute_metrics=compute_metrics,
... )

>>> trainer.train()

훈련이 끝나면 push_to_hub() 메서드로 모델을 Hub에 공유해 모두가 사용할 수 있게 하세요.

>>> trainer.push_to_hub()

객관식 작업을 위해 모델을 파인튜닝하는 더 심층적인 예시는 해당 PyTorch notebook을 참고하세요.

추론 (Inference)

좋습니다. 이제 모델을 파인튜닝했으니 추론에 사용할 수 있습니다!

일부 텍스트와 두 개의 후보 답변을 생각해 보세요.

>>> prompt = "France has a bread law, Le Décret Pain, with strict rules on what is allowed in a traditional baguette."
>>> candidate1 = "The law does not apply to croissants and brioche."
>>> candidate2 = "The law applies to baguettes."

각 프롬프트와 후보 답변 쌍을 토크나이즈하고 PyTorch 텐서를 반환합니다. 또한 일부 labels를 만들어야 합니다.

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_swag_model")
>>> inputs = tokenizer([[prompt, candidate1], [prompt, candidate2]], return_tensors="pt", padding=True)
>>> labels = torch.tensor(0).unsqueeze(0)

입력과 라벨을 모델로 전달하고 logits을 얻습니다.

>>> from transformers import AutoModelForMultipleChoice

>>> model = AutoModelForMultipleChoice.from_pretrained("username/my_awesome_swag_model")
>>> outputs = model(**{k: v.unsqueeze(0) for k, v in inputs.items()}, labels=labels)
>>> logits = outputs.logits

확률이 가장 높은 클래스를 구합니다.

>>> predicted_class = logits.argmax().item()
>>> predicted_class
0

더 알아보기 (Learn more)