| import argparse |
| import logging |
| import os |
|
|
| import matplotlib.pyplot as plt |
| import torch |
|
|
| from src.model.config import ( |
| GPT124M_CONFIG, |
| DEFAULT_DATA_URL, |
| DEFAULT_DATASET_PATH, |
| DEFAULT_OUTPUT_DIR, |
| ) |
| from src.data.utils import download_text, load_text, load_wikitext |
| from src.data.dataset import create_gpt_dataloader |
| from src.model.gpt import GPTModel |
| from src.data.tokenizer import TikTokenizer |
| from src.engine.train import train |
| from huggingface_hub import hf_hub_download |
|
|
| logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") |
|
|
|
|
| def parse_args(): |
| parser = argparse.ArgumentParser(description="Train GPT-2 on GPU") |
| parser.add_argument("--sample-prompt", type=str, default=None) |
| parser.add_argument("--data-url", type=str, default=DEFAULT_DATA_URL) |
| parser.add_argument("--dataset-path", type=str, default=DEFAULT_DATASET_PATH) |
| parser.add_argument("--output-dir", type=str, default=DEFAULT_OUTPUT_DIR) |
| parser.add_argument("--repo-id", type=str, default="triton329/gpt2") |
| parser.add_argument("--filename", type=str, default="GPT-2/artifacts/model.pth") |
| return parser.parse_args() |
|
|
|
|
| if __name__ == "__main__": |
| args = parse_args() |
| cfg = GPT124M_CONFIG |
| sample_prompt = args.sample_prompt or "Gisburn had a evil smile, and" |
|
|
| logging.info("Loading data...") |
| download_text(url=args.data_url, file_path=args.dataset_path) |
| raw_text = load_text(file_path=args.dataset_path) |
| logging.info(f" {len(raw_text):,} chars loaded") |
|
|
| train_text, val_text = load_wikitext() |
| logging.info(f"train_text:{train_text[:10]}") |
|
|
| tokenizer = TikTokenizer("gpt2") |
|
|
| train_loader = create_gpt_dataloader( |
| train_text, |
| tokenizer=tokenizer, |
| max_len=cfg.context_window_size, |
| stride=cfg.stride, |
| batch_size=cfg.batch_size, |
| num_workers=cfg.num_workers, |
| pin_memory=True, |
| ) |
| val_loader = create_gpt_dataloader( |
| val_text, |
| tokenizer=tokenizer, |
| max_len=cfg.context_window_size, |
| stride=cfg.stride, |
| batch_size=cfg.batch_size, |
| num_workers=cfg.num_workers, |
| pin_memory=True, |
| ) |
| logging.info( |
| f" Train batches: {len(train_loader)} | Val batches: {len(val_loader)}" |
| ) |
|
|
| device = torch.device("cuda") |
| if not torch.cuda.is_available(): |
| raise RuntimeError("No CUDA device found. CPU training is not supported.") |
|
|
| model = GPTModel(cfg).to(device) |
| local_path = os.path.join(args.output_dir, "model.pth") |
| if os.path.exists(local_path): |
| weights_path = local_path |
| logging.info(f"Loading local weights from {weights_path}") |
| else: |
| logging.info(f"Downloading weights from {args.repo_id} ...") |
| weights_path = hf_hub_download(repo_id=args.repo_id, filename=args.filename) |
| state_dict = torch.load(weights_path, map_location=device) |
| model.load_state_dict(state_dict, strict=False) |
| |
| logging.info("Loaded pretrained weights") |
| optimizer = torch.optim.AdamW(model.parameters(), lr=cfg.lr) |
| logging.info( |
| f" Model: {sum(p.numel() for p in model.parameters()):,} params | device: {device}" |
| ) |
|
|
| logging.info(f"Starting training for {cfg.num_epochs} epochs...\n") |
| history = train( |
| model, |
| optimizer, |
| train_loader, |
| val_loader, |
| device, |
| cfg, |
| sample_prompt, |
| tokenizer, |
| ) |
|
|
| plt.plot(history[:, 0], label="train") |
| plt.plot(history[:, 1], label="val") |
| plt.legend() |
| plt.xlabel("epoch") |
| plt.ylabel("loss") |
| os.makedirs(args.output_dir, exist_ok=True) |
| plt.savefig(os.path.join(args.output_dir, "train_validation_curve.png")) |
| plt.close() |
|
|
| torch.save(model.state_dict(), os.path.join(args.output_dir, "model.pth")) |
| logging.info("Saved model") |
|
|