khalidFlex commited on
Commit
572ea6f
·
verified ·
1 Parent(s): 9d3e5d1

Upload validate_collator.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. validate_collator.py +103 -0
validate_collator.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # dependencies = [
3
+ # "transformers>=5.14.0",
4
+ # "datasets>=4.0",
5
+ # "trackio",
6
+ # "Pillow>=10.0",
7
+ # "torch>=2.5",
8
+ # "accelerate>=1.0",
9
+ # "num2words",
10
+ # ]
11
+ # ///
12
+ """CPU pre-flight for the Gemma 4 training collator.
13
+
14
+ Runs the EXACT collate path from train_sft.py on a few real dataset rows, using
15
+ the processor + model config (no 16GB weights), then decodes one example to
16
+ prove which tokens are supervised vs masked. Catches chat-template, token-id,
17
+ and shape bugs on cpu-basic (~$0.01) before we burn GPU minutes.
18
+
19
+ python gemma4/validate_collator.py --dataset khalidFlex/gui-agent-smoke
20
+ """
21
+
22
+ import argparse
23
+
24
+ import torch
25
+ from datasets import load_dataset
26
+ from transformers import AutoConfig, AutoProcessor
27
+
28
+
29
+ def main():
30
+ ap = argparse.ArgumentParser()
31
+ ap.add_argument("--model", default="google/gemma-4-E4B-it")
32
+ ap.add_argument("--dataset", default="khalidFlex/gui-agent-smoke")
33
+ ap.add_argument("--n", type=int, default=4)
34
+ args = ap.parse_args()
35
+
36
+ print(f"[val] processor + config for {args.model}")
37
+ processor = AutoProcessor.from_pretrained(args.model)
38
+ config = AutoConfig.from_pretrained(args.model)
39
+
40
+ image_token_ids = {
41
+ tid for tid in [
42
+ getattr(config, "image_token_id", None),
43
+ getattr(config, "boi_token_id", None),
44
+ getattr(config, "eoi_token_id", None),
45
+ ] if isinstance(tid, int)
46
+ }
47
+ print(f"[val] image/structural token ids: {sorted(image_token_ids)}")
48
+ processor.tokenizer.padding_side = "right"
49
+
50
+ ds = load_dataset(args.dataset, split=f"train[:{args.n}]")
51
+ print(f"[val] loaded {len(ds)} rows")
52
+
53
+ def to_messages(ex):
54
+ return [
55
+ {"role": "user", "content": [
56
+ {"type": "image"},
57
+ {"type": "text", "text": f"{ex['system']}\n\n{ex['user']}"},
58
+ ]},
59
+ {"role": "assistant", "content": [{"type": "text", "text": ex["assistant"]}]},
60
+ ]
61
+
62
+ full_texts, prompt_texts, images = [], [], []
63
+ for ex in ds:
64
+ msgs = to_messages(ex)
65
+ full_texts.append(processor.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False))
66
+ prompt_texts.append(processor.apply_chat_template(msgs[:-1], tokenize=False, add_generation_prompt=True))
67
+ images.append([ex["image"].convert("RGB")])
68
+
69
+ batch = processor(text=full_texts, images=images, return_tensors="pt", padding=True)
70
+ prompt_batch = processor(text=prompt_texts, images=images, return_tensors="pt", padding=True)
71
+ prompt_lens = prompt_batch["attention_mask"].sum(dim=1)
72
+
73
+ labels = batch["input_ids"].clone()
74
+ labels[batch["attention_mask"] == 0] = -100
75
+ for tid in image_token_ids:
76
+ labels[labels == tid] = -100
77
+ for i, plen in enumerate(prompt_lens):
78
+ labels[i, : int(plen)] = -100
79
+
80
+ print("\n[val] === batch shapes ===")
81
+ for k, v in batch.items():
82
+ if isinstance(v, torch.Tensor):
83
+ print(f" {k}: {tuple(v.shape)} {v.dtype}")
84
+
85
+ # Prove supervision: decode only the non-masked (label != -100) tokens of row 0.
86
+ row0 = labels[0]
87
+ supervised = batch["input_ids"][0][row0 != -100]
88
+ n_sup = int((row0 != -100).sum())
89
+ n_tot = int((batch["attention_mask"][0] == 1).sum())
90
+ print(f"\n[val] row0 supervised tokens: {n_sup}/{n_tot} real tokens")
91
+ print(f"[val] supervised text (should be ONLY the assistant answer):")
92
+ print(" ", repr(processor.tokenizer.decode(supervised)))
93
+
94
+ assert n_sup > 0, "no supervised tokens — masking is wrong!"
95
+ assert n_sup < n_tot, "everything supervised — prompt not masked!"
96
+ decoded = processor.tokenizer.decode(supervised)
97
+ assert "<code>" in decoded or "click" in decoded or "final_answer" in decoded, \
98
+ "supervised span doesn't look like an action — offset is off"
99
+ print("\n[val] ✓ collator OK — prompt+image masked, assistant supervised.")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()