Тонкая настройка GPT-2
Я пытаюсь точно настроить GPT-2 для задачи: если я даю пять последовательных чисел, каковы следующие последовательные числа. Например, если
input_text = "one | two | three | four | five"
,
output_text = "six | seven... | ten"
.
Важные части модели, которую я использовал через API huggingface, следующие:
class Model(pl.LightningModule):
def __init__(self,
tokenizer,
lr: float) -> None:
super().__init__()
self.lr = lr
self.tokenizer = Tokenizer(tokenizer)
self.model = GPT2LMHeadModel.from_pretrained('gpt2')
def common_step(self, batch: Tuple[List[str], List[str]]) -> torch.FloatTensor:
questions, answers = batch
combined = [input + " <EOS> " + output for input, output in zip(questions, answers)]
tokens = {k: v.to(self.device) for k, v in self.tokenizer(combined).items()}
labels = tokens["input_ids"].clone()
labels[tokens["attention_mask"]==0] = -100
outputs = self.model(
input_ids=tokens["input_ids"],
attention_mask=tokens["attention_mask"],
labels=labels,
return_dict=True
)
return outputs["loss"]
def training_step(self, batch: Tuple[List[str], List[str]], *args) -> torch.FloatTensor:
loss = self.common_step(batch)
return loss
def generate_examples(self, batch):
questions, answers = batch
combined = [question + " <EOS> " for question in questions]
tokens = {k: v.to(self.device) for k, v in self.tokenizer(combined).items()}
generated = self.model.generate(
input_ids=tokens["input_ids"],
attention_mask=tokens["attention_mask"],
)
print(questions[0])
print("="*30)
print(self.tokenizer.decode(generated[0]))
В результате я пытаюсь выдать цифры, но, к сожалению, выглядит так. Вывод начинается с того места, где отображается тег, иначе это просто копирование. Обратите внимание, что в токенизаторе GPT-2 нет:
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
<|endoftext|>five thousand, five hundred and ninety-one| five thousand, five hundred and ninety-two| five thousand, five hundred and ninety-three| five thousand, five hundred and ninety-four| five thousand, five hundred and ninety-five <EOS> <|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|> fifteen thousand, four hundred and thirty-six| ten thousand, six hundred and sixty-seven| fifteen thousand and sixty‑eight| 15 thousand and eighty-nine| fifteen hundred and seventy<|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|><|endoftext|>
Итак, вопрос в том, почему он генерирует возможного кандидата после кучи токенов <|endoftext|>. В обучающем наборе объедините ввод и вывод с помощью слова "" (это не фактический токен), и результат будет сразу без каких-либо дополнений.
Это связано с используемым мной токенизатором, который я определил ниже?
# make sure GPT2 appends EOS in begin and end
def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None):
outputs = [self.bos_token_id] + token_ids_0 + [self.eos_token_id]
return outputs
GPT2Tokenizer.build_inputs_with_special_tokens = build_inputs_with_special_tokens
gpt2_tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
# set pad_token_id to unk_token_id -> be careful here as unk_token_id == eos_token_id == bos_token_id
gpt2_tokenizer.pad_token = gpt2_tokenizer.unk_token
Рабочий пример на colab можно найти здесь .