victor HF Staff commited on
Commit
7ac42fb
·
verified ·
1 Parent(s): 3a6b9fc

Put eval.py

Browse files
Files changed (1) hide show
  1. eval.py +119 -0
eval.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Evaluate the FunctionGemma LoRA adapter vs the base model on the held-out split.
3
+
4
+ Reports masked eval loss and next-token accuracy over model (assistant) turns
5
+ only, on the same 90/10 split used for training.
6
+ """
7
+ import argparse
8
+ import re
9
+ import torch
10
+ from datasets import load_dataset
11
+ from peft import PeftModel
12
+ from transformers import AutoModelForCausalLM, AutoTokenizer
13
+
14
+ BASE = "unsloth/functiongemma-270m-it"
15
+ ADAPTER = "victor/functiongemma-270m-agent-sft-lora"
16
+
17
+
18
+ def model_spans(text):
19
+ spans = []
20
+ for m in re.finditer(r"<start_of_turn>model\n", text):
21
+ start = m.end()
22
+ em = re.search(r"\n<end_of_turn>", text[start:])
23
+ if em:
24
+ spans.append((start, start + em.end()))
25
+ return spans
26
+
27
+
28
+ def tokenize_row(row, tokenizer, max_length):
29
+ text = row["text"]
30
+ enc = tokenizer(text, return_offsets_mapping=True, truncation=True, max_length=max_length)
31
+ spans = model_spans(text)
32
+ labels = []
33
+ for (s, e), tid in zip(enc["offset_mapping"], enc["input_ids"]):
34
+ keep = any(a <= e and s <= b for (a, b) in spans)
35
+ labels.append(tid if keep else -100)
36
+ return {"input_ids": enc["input_ids"], "attention_mask": enc["attention_mask"], "labels": labels}
37
+
38
+
39
+ def collate(rows, pad_id):
40
+ ids = [r["input_ids"] for r in rows]
41
+ att = [r["attention_mask"] for r in rows]
42
+ lab = [r["labels"] for r in rows]
43
+ ml = max(len(x) for x in ids)
44
+ ids_t = torch.full((len(rows), ml), pad_id, dtype=torch.long)
45
+ att_t = torch.zeros((len(rows), ml), dtype=torch.long)
46
+ lab_t = torch.full((len(rows), ml), -100, dtype=torch.long)
47
+ for i, (a, m, l) in enumerate(zip(ids, att, lab)):
48
+ ids_t[i, : len(a)] = torch.tensor(a)
49
+ att_t[i, : len(m)] = torch.tensor(m)
50
+ lab_t[i, : len(l)] = torch.tensor(l)
51
+ return ids_t, att_t, lab_t
52
+
53
+
54
+ @torch.no_grad()
55
+ def evaluate(model, ds, tokenizer, batch, device):
56
+ model.eval()
57
+ nll_sum, tok_sum, corr_sum = 0.0, 0, 0
58
+ for i in range(0, len(ds), batch):
59
+ rows = [ds[j] for j in range(i, min(i + batch, len(ds)))]
60
+ ids, att, labs = collate(rows, tokenizer.pad_token_id)
61
+ ids, att, labs = ids.to(device), att.to(device), labs.to(device)
62
+ out = model(input_ids=ids, attention_mask=att, labels=labs)
63
+ # accuracy over masked (assistant) tokens with causal shift
64
+ logits = out.logits[:, :-1] # (B, T-1, V)
65
+ targets = labs[:, 1:]
66
+ mask = targets != -100
67
+ preds = logits.argmax(-1)
68
+ corr = ((preds == targets) & mask)
69
+ n = mask.sum().item()
70
+ tok_sum += n
71
+ corr_sum += corr.sum().item()
72
+ if n > 0:
73
+ nll_sum += out.loss.item() * n
74
+ loss = nll_sum / max(tok_sum, 1)
75
+ acc = corr_sum / max(tok_sum, 1)
76
+ return loss, acc
77
+
78
+
79
+ def main():
80
+ ap = argparse.ArgumentParser()
81
+ ap.add_argument("--max_length", type=int, default=8192)
82
+ ap.add_argument("--batch", type=int, default=8)
83
+ args = ap.parse_args()
84
+
85
+ device = "cuda" if torch.cuda.is_available() else "cpu"
86
+ print("device:", device)
87
+
88
+ tokenizer = AutoTokenizer.from_pretrained(BASE, trust_remote_code=True)
89
+ if tokenizer.pad_token is None:
90
+ tokenizer.pad_token = tokenizer.eos_token
91
+
92
+ ds = load_dataset("victor/functiongemma-agent-sft", split="train")
93
+ split = ds.train_test_split(test_size=0.1, seed=42)
94
+ eval_ds = split["test"]
95
+ eval_ds = eval_ds.map(
96
+ lambda r: tokenize_row(r, tokenizer, args.max_length), remove_columns=["text"]
97
+ )
98
+ print("held-out rows:", len(eval_ds))
99
+
100
+ dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
101
+
102
+ print("\n=== Base model ===")
103
+ base = AutoModelForCausalLM.from_pretrained(BASE, trust_remote_code=True, torch_dtype=dtype).to(device)
104
+ base_loss, base_acc = evaluate(base, eval_ds, tokenizer, args.batch, device)
105
+ print(f"base eval_loss={base_loss:.4f} eval_token_acc={base_acc:.4f}")
106
+
107
+ print("\n=== Finetuned (base + LoRA adapter) ===")
108
+ ft = AutoModelForCausalLM.from_pretrained(BASE, trust_remote_code=True, torch_dtype=dtype).to(device)
109
+ ft = PeftModel.from_pretrained(ft, ADAPTER).to(device)
110
+ ft_loss, ft_acc = evaluate(ft, eval_ds, tokenizer, args.batch, device)
111
+ print(f"ft eval_loss={ft_loss:.4f} eval_token_acc={ft_acc:.4f}")
112
+
113
+ print("\n=== Summary ===")
114
+ print(f"eval_loss : base {base_loss:.4f} -> ft {ft_loss:.4f}")
115
+ print(f"eval_tok_acc : base {base_acc:.4f} -> ft {ft_acc:.4f}")
116
+
117
+
118
+ if __name__ == "__main__":
119
+ main()