186 lines
5.3 KiB
Python
186 lines
5.3 KiB
Python
import torch
|
|
torch.backends.cuda.matmul.allow_tf32 = True
|
|
torch.backends.cudnn.allow_tf32 = True
|
|
torch.set_float32_matmul_precision('high')
|
|
#!/usr/bin/env python3
|
|
import argparse
|
|
import os
|
|
import torch
|
|
|
|
from datasets import load_dataset
|
|
from transformers import (
|
|
GPT2LMHeadModel,
|
|
GPT2Tokenizer,
|
|
Trainer,
|
|
TrainingArguments,
|
|
)
|
|
|
|
PROMPT_TEMPLATE = """Below is an instruction that describes a task. Write a response that appropriately completes the request.
|
|
|
|
### Instruction:
|
|
{instruction}
|
|
|
|
### Response:
|
|
"""
|
|
|
|
|
|
def build_instruction(example):
|
|
instruction = example["instruction"].strip()
|
|
inp = example.get("input", "")
|
|
|
|
if inp and inp.strip():
|
|
instruction = instruction + "\n\nInput:\n" + inp.strip()
|
|
|
|
return instruction
|
|
|
|
|
|
def tokenize_example(example, tokenizer, max_length):
|
|
instruction = build_instruction(example)
|
|
response = example["output"].strip()
|
|
|
|
prompt = PROMPT_TEMPLATE.format(instruction=instruction)
|
|
full_text = prompt + response + tokenizer.eos_token
|
|
|
|
prompt_ids = tokenizer(
|
|
prompt,
|
|
add_special_tokens=False,
|
|
truncation=True,
|
|
max_length=max_length,
|
|
)["input_ids"]
|
|
|
|
full = tokenizer(
|
|
full_text,
|
|
add_special_tokens=False,
|
|
truncation=True,
|
|
max_length=max_length,
|
|
)
|
|
|
|
input_ids = full["input_ids"]
|
|
attention_mask = full["attention_mask"]
|
|
|
|
labels = input_ids.copy()
|
|
prompt_len = min(len(prompt_ids), len(labels))
|
|
labels[:prompt_len] = [-100] * prompt_len
|
|
|
|
return {
|
|
"input_ids": input_ids,
|
|
"attention_mask": attention_mask,
|
|
"labels": labels,
|
|
}
|
|
|
|
|
|
class CausalCollator:
|
|
def __init__(self, tokenizer, pad_to_multiple_of=8):
|
|
self.tokenizer = tokenizer
|
|
self.pad_to_multiple_of = pad_to_multiple_of
|
|
|
|
def __call__(self, features):
|
|
max_len = max(len(x["input_ids"]) for x in features)
|
|
|
|
if self.pad_to_multiple_of:
|
|
rem = max_len % self.pad_to_multiple_of
|
|
if rem:
|
|
max_len += self.pad_to_multiple_of - rem
|
|
|
|
input_ids = []
|
|
attention_mask = []
|
|
labels = []
|
|
|
|
for x in features:
|
|
pad_len = max_len - len(x["input_ids"])
|
|
|
|
input_ids.append(x["input_ids"] + [self.tokenizer.pad_token_id] * pad_len)
|
|
attention_mask.append(x["attention_mask"] + [0] * pad_len)
|
|
labels.append(x["labels"] + [-100] * pad_len)
|
|
|
|
return {
|
|
"input_ids": torch.tensor(input_ids, dtype=torch.long),
|
|
"attention_mask": torch.tensor(attention_mask, dtype=torch.long),
|
|
"labels": torch.tensor(labels, dtype=torch.long),
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--model", default="gpt2-xl")
|
|
parser.add_argument("--dataset", default="yahma/alpaca-cleaned")
|
|
parser.add_argument("--out", default="gpt2-xl-alpaca-full")
|
|
parser.add_argument("--max-length", type=int, default=1024)
|
|
parser.add_argument("--epochs", type=float, default=1.0)
|
|
parser.add_argument("--lr", type=float, default=1e-5)
|
|
parser.add_argument("--batch-size", type=int, default=1)
|
|
parser.add_argument("--grad-accum", type=int, default=16)
|
|
parser.add_argument("--limit", type=int, default=0)
|
|
parser.add_argument("--save-steps", type=int, default=500)
|
|
parser.add_argument("--logging-steps", type=int, default=10)
|
|
args = parser.parse_args()
|
|
|
|
os.makedirs(args.out, exist_ok=True)
|
|
|
|
tokenizer = GPT2Tokenizer.from_pretrained(args.model)
|
|
tokenizer.pad_token = tokenizer.eos_token
|
|
|
|
model = GPT2LMHeadModel.from_pretrained(
|
|
args.model if torch.cuda.is_available() else torch.float32,
|
|
)
|
|
|
|
model.config.pad_token_id = tokenizer.eos_token_id
|
|
model.config.use_cache = False
|
|
# # model.gradient_checkpointing_enable()
|
|
|
|
ds = load_dataset(args.dataset, split="train")
|
|
ds = ds.shuffle(seed=42)
|
|
|
|
if args.limit > 0:
|
|
ds = ds.select(range(min(args.limit, len(ds))))
|
|
|
|
tokenized = ds.map(
|
|
lambda ex: tokenize_example(ex, tokenizer, args.max_length),
|
|
remove_columns=ds.column_names,
|
|
desc="Tokenizing",
|
|
)
|
|
|
|
tokenized = tokenized.filter(
|
|
lambda ex: any(label != -100 for label in ex["labels"]),
|
|
desc="Filtering empty-label examples",
|
|
)
|
|
|
|
training_args = TrainingArguments(
|
|
output_dir=args.out,
|
|
num_train_epochs=args.epochs,
|
|
per_device_train_batch_size=args.batch_size,
|
|
gradient_accumulation_steps=args.grad_accum,
|
|
learning_rate=args.lr,
|
|
warmup_ratio=0.03,
|
|
lr_scheduler_type="cosine",
|
|
dataloader_num_workers=8,
|
|
dataloader_pin_memory=True,
|
|
logging_steps=args.logging_steps,
|
|
save_steps=args.save_steps,
|
|
save_total_limit=3,
|
|
fp16=torch.cuda.is_available(),
|
|
optim="adamw_torch",
|
|
weight_decay=0.01,
|
|
max_grad_norm=1.0,
|
|
report_to="none",
|
|
remove_unused_columns=False,
|
|
)
|
|
|
|
trainer = Trainer(
|
|
model=model,
|
|
args=training_args,
|
|
train_dataset=tokenized,
|
|
data_collator=CausalCollator(tokenizer),
|
|
)
|
|
|
|
trainer.train(resume_from_checkpoint="/gpt2-xl-alpaca-full/checkpoint-500")
|
|
|
|
trainer.save_model(args.out)
|
|
tokenizer.save_pretrained(args.out)
|
|
|
|
print("saved full model to", args.out)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|