fela-acml2026 / scripts /gen_samples.py
itstheraj's picture
FELA: training code, checkpoints, and evaluation results
a24d5a6
Raw
History Blame
10.4 kB
from __future__ import annotations
import argparse
import io
import sys
from pathlib import Path
from typing import Any
import torch
sys.path.insert(0, str(Path(__file__).parent.parent))
from model_cpu_gpt2 import CPUGPT, gpt2_small_config, smoke_config
def _load_ckpt(path: str) -> dict:
if path.startswith("s3://"):
import boto3
bucket, key = path[5:].split("/", 1)
buf = io.BytesIO()
boto3.client("s3").download_fileobj(bucket, key, buf)
buf.seek(0)
return torch.load(buf, map_location="cpu", weights_only=False)
return torch.load(path, map_location="cpu", weights_only=False)
def build_our_model(config_name: str, ckpt_path: str | None) -> CPUGPT:
cfg_map = {
"gpt2-small": gpt2_small_config,
"smoke": smoke_config,
}
if config_name not in cfg_map:
raise ValueError(f"Unknown config '{config_name}'. Choices: {list(cfg_map)}")
cfg = cfg_map[config_name]()
model = CPUGPT(cfg)
if ckpt_path:
ckpt = _load_ckpt(ckpt_path)
state = ckpt.get("model", ckpt)
state = {k.replace("_orig_mod.", ""): v for k, v in state.items()}
model.load_state_dict(state, strict=True)
print(f"Loaded checkpoint: {ckpt_path}", flush=True)
else:
print("No checkpoint — using random weights.", flush=True)
model.eval()
return model
def build_hf_gpt2():
try:
from transformers import GPT2LMHeadModel, GPT2Tokenizer
except ImportError:
print(
"\nERROR: transformers is not installed (needed for GPT-2 baseline).\n"
"Install with: pip install transformers\n",
file=sys.stderr,
)
return None, None
print("Loading HuggingFace gpt2...", flush=True)
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained("gpt2")
model.eval()
return model, tokenizer
def generate_our_model(
model: CPUGPT,
prompt_text: str,
n_samples: int,
temperature: float,
top_p: float,
max_new_tokens: int,
) -> list[str]:
import tiktoken
enc = tiktoken.get_encoding("gpt2")
prompt_ids = enc.encode_ordinary(prompt_text)
max_T = model.cfg.seq_len
if len(prompt_ids) > max_T:
prompt_ids = prompt_ids[-max_T:]
prompt_tensor = torch.tensor(prompt_ids, dtype=torch.long).unsqueeze(0)
top_k = max(1, int(50 * top_p))
completions = []
for _ in range(n_samples):
with torch.no_grad():
out = model.generate(
prompt_tensor,
max_new_tokens=max_new_tokens,
temperature=temperature,
top_k=top_k,
)
gen_ids = out[0].tolist()
completions.append(enc.decode(gen_ids))
return completions
def generate_hf_gpt2(
hf_model,
tokenizer,
prompt_text: str,
n_samples: int,
temperature: float,
top_p: float,
max_new_tokens: int,
) -> list[str]:
inputs = tokenizer(prompt_text, return_tensors="pt")
input_ids = inputs["input_ids"]
completions = []
for _ in range(n_samples):
with torch.no_grad():
if temperature == 0.0:
out = hf_model.generate(
input_ids,
max_new_tokens=max_new_tokens,
do_sample=False,
pad_token_id=tokenizer.eos_token_id,
)
else:
out = hf_model.generate(
input_ids,
max_new_tokens=max_new_tokens,
do_sample=True,
temperature=temperature,
top_p=top_p,
pad_token_id=tokenizer.eos_token_id,
)
gen_ids = out[0, input_ids.shape[1] :].tolist()
gen_str = tokenizer.decode(gen_ids, skip_special_tokens=True)
completions.append(gen_str)
return completions
def load_prompts(yaml_path: str) -> list[dict[str, str]]:
try:
import yaml
except ImportError:
return _parse_prompts_fallback(yaml_path)
with open(yaml_path) as f:
data = yaml.safe_load(f)
return data["prompts"]
def _parse_prompts_fallback(yaml_path: str) -> list[dict[str, str]]:
prompts = []
current: dict[str, str] = {}
with open(yaml_path) as f:
for line in f:
line = line.rstrip()
if line.startswith(" - category:"):
if current:
prompts.append(current)
current = {"category": line.split(":", 1)[1].strip()}
elif line.startswith(" text:"):
val = line.split(":", 1)[1].strip().strip('"')
current["text"] = val
if current:
prompts.append(current)
return prompts
def _escape_md(text: str) -> str:
return text.replace("|", "\\|").replace("\n", " ").strip()
def build_markdown_table(
prompts: list[dict[str, str]],
our_completions: list[list[str]],
hf_completions: list[list[str]] | None,
n_samples: int,
) -> str:
lines = []
if hf_completions:
lines.append("| # | Prompt | Our Model | GPT-2 Small |")
lines.append("|---|--------|-----------|-------------|")
else:
lines.append("| # | Prompt | Our Model |")
lines.append("|---|--------|-----------|")
for pi, prompt in enumerate(prompts):
prompt_text = _escape_md(prompt["text"])
for si in range(n_samples):
our_text = (
_escape_md(our_completions[pi][si])
if si < len(our_completions[pi])
else ""
)
row_label = f"{pi + 1}.{si + 1}"
if hf_completions:
hf_text = (
_escape_md(hf_completions[pi][si])
if si < len(hf_completions[pi])
else ""
)
lines.append(
f"| {row_label} | {prompt_text} | {our_text} | {hf_text} |"
)
else:
lines.append(f"| {row_label} | {prompt_text} | {our_text} |")
if pi < len(prompts) - 1:
if hf_completions:
lines.append("| | | | |")
else:
lines.append("| | | |")
return "\n".join(lines)
def run_gen_samples(
config_name: str,
prompts: list[dict[str, str]],
n_samples: int,
temperature: float,
top_p: float,
max_new_tokens: int,
ckpt_path: str | None = None,
include_hf_baseline: bool = True,
) -> str:
our_model = build_our_model(config_name, ckpt_path)
hf_model, hf_tokenizer = None, None
if include_hf_baseline:
hf_model, hf_tokenizer = build_hf_gpt2()
our_all: list[list[str]] = []
hf_all: list[list[str]] = []
for prompt in prompts:
print(f" [{prompt['category']}] {prompt['text'][:60]}...", flush=True)
our_completions = generate_our_model(
our_model, prompt["text"], n_samples, temperature, top_p, max_new_tokens
)
our_all.append(our_completions)
if hf_model is not None:
hf_completions = generate_hf_gpt2(
hf_model,
hf_tokenizer,
prompt["text"],
n_samples,
temperature,
top_p,
max_new_tokens,
)
hf_all.append(hf_completions)
table = build_markdown_table(
prompts, our_all, hf_all if hf_model else None, n_samples
)
return table
def main():
p = argparse.ArgumentParser(
description="Generate qualitative samples (our model vs GPT-2)"
)
p.add_argument(
"--ckpt", default=None, help="Checkpoint path (omit for random weights)"
)
p.add_argument(
"--config", default="gpt2-small", help="Model config: gpt2-small | smoke"
)
p.add_argument(
"--prompts",
default="scripts/eval_prompts.yaml",
help="Path to eval_prompts.yaml",
)
p.add_argument("--n-samples", type=int, default=3, help="Completions per prompt")
p.add_argument("--temperature", type=float, default=0.8)
p.add_argument("--top-p", type=float, default=0.9)
p.add_argument("--max-new-tokens", type=int, default=100)
p.add_argument(
"--output",
required=True,
help="Output markdown file (e.g. results/samples_v5.md)",
)
p.add_argument(
"--no-baseline", action="store_true", help="Skip HuggingFace GPT-2 baseline"
)
args = p.parse_args()
print(f"\n=== gen_samples.py ===", flush=True)
print(f"Config: {args.config}", flush=True)
print(f"Ckpt: {args.ckpt or '(random weights)'}", flush=True)
print(f"Prompts: {args.prompts}", flush=True)
print(f"N-samples: {args.n_samples}", flush=True)
print(f"Temperature: {args.temperature}", flush=True)
print(f"Top-p: {args.top_p}", flush=True)
print(f"Max tokens: {args.max_new_tokens}", flush=True)
print(flush=True)
prompts_path = Path(args.prompts)
if not prompts_path.exists():
print(f"ERROR: prompts file not found: {prompts_path}", file=sys.stderr)
sys.exit(1)
prompts = load_prompts(str(prompts_path))
print(f"Loaded {len(prompts)} prompts from {prompts_path}", flush=True)
table = run_gen_samples(
config_name=args.config,
prompts=prompts,
n_samples=args.n_samples,
temperature=args.temperature,
top_p=args.top_p,
max_new_tokens=args.max_new_tokens,
ckpt_path=args.ckpt,
include_hf_baseline=not args.no_baseline,
)
print("\n" + "=" * 60)
print("Sample outputs")
print("=" * 60)
print(table)
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "w") as f:
f.write(f"# Qualitative Generation Samples\n\n")
f.write(f"Config: `{args.config}` \n")
f.write(f"Checkpoint: `{args.ckpt or 'random_weights'}` \n")
f.write(
f"Temperature: {args.temperature}, Top-p: {args.top_p}, "
f"Max new tokens: {args.max_new_tokens}\n\n"
)
f.write(table)
f.write("\n")
print(f"\nSaved to: {out_path}", flush=True)
if __name__ == "__main__":
main()