td-builder commited on
Commit
c620ae1
·
verified ·
1 Parent(s): a268cc1

Upload 149 files

Browse files
hugging/td_fuse/grpo_reason.py CHANGED
@@ -45,7 +45,7 @@ from dataclasses import dataclass
45
  @dataclass
46
  class GRPOConfig:
47
  """Configuration for overnight GRPO reasoning training."""
48
- model_path: str = "td_fuse_outputs/healed_final"
49
  output_dir: str = "td_fuse_outputs/grpo_cycle2"
50
  progress_file: str = "td_fuse_outputs/grpo_cycle2/progress.json"
51
 
@@ -208,10 +208,18 @@ def generate_all_problems(cfg: GRPOConfig) -> List[Dict]:
208
  b_val = random.randint(10, 100)
209
  diff = abs(a_val - b_val)
210
  who = "Alice" if a_val > b_val else "Bob"
 
 
211
  problems.append({
212
- "question": f"Alice has {a_val} marbles and Bob has {b_val} marbles. Who has more and by how many?",
 
 
 
 
 
 
213
  "answer": str(diff), "type": "math_reasoning",
214
- "think_hint": f"{who} has more. Difference: {max(a_val,b_val)} - {min(a_val,b_val)} = {diff}."
215
  })
216
 
217
  random.shuffle(problems)
@@ -364,13 +372,21 @@ def verify_answer(problem: Dict, response: str) -> float:
364
  except ValueError:
365
  pass
366
 
367
- # Second: check last 2 numbers only (avoids problem restatement numbers)
 
 
368
  numbers = re.findall(r'-?\d+\.?\d*', answer_text)
369
  if numbers:
370
- check_nums = numbers[-2:] if len(numbers) > 2 else numbers
371
- for num in check_nums:
 
 
 
 
 
 
372
  try:
373
- if abs(float(num) - float(expected)) < 0.01:
374
  return 1.0
375
  except ValueError:
376
  pass
 
45
  @dataclass
46
  class GRPOConfig:
47
  """Configuration for overnight GRPO reasoning training."""
48
+ model_path: str = "td_fuse_outputs/reasoning_healed"
49
  output_dir: str = "td_fuse_outputs/grpo_cycle2"
50
  progress_file: str = "td_fuse_outputs/grpo_cycle2/progress.json"
51
 
 
208
  b_val = random.randint(10, 100)
209
  diff = abs(a_val - b_val)
210
  who = "Alice" if a_val > b_val else "Bob"
211
+ # Split into two separate problems to avoid compound answer verification issues
212
+ # Problem 1: Who has more? (name answer)
213
  problems.append({
214
+ "question": f"Alice has {a_val} marbles and Bob has {b_val} marbles. Who has more?",
215
+ "answer": who.lower(), "type": "math_reasoning",
216
+ "think_hint": f"Alice has {a_val}, Bob has {b_val}. {who} has more."
217
+ })
218
+ # Problem 2: By how many? (numeric answer)
219
+ problems.append({
220
+ "question": f"Alice has {a_val} marbles and Bob has {b_val} marbles. What is the difference?",
221
  "answer": str(diff), "type": "math_reasoning",
222
+ "think_hint": f"Difference: {max(a_val,b_val)} - {min(a_val,b_val)} = {diff}."
223
  })
224
 
225
  random.shuffle(problems)
 
372
  except ValueError:
373
  pass
374
 
375
+ # Second: check the LAST number only (the conclusion)
376
+ # If model says "25 - 5 = 20, answer is 20", last number = 20
377
+ # Checking last 2 caused false positives when expected appeared in calculation
378
  numbers = re.findall(r'-?\d+\.?\d*', answer_text)
379
  if numbers:
380
+ try:
381
+ if abs(float(numbers[-1]) - float(expected)) < 0.01:
382
+ return 1.0
383
+ except ValueError:
384
+ pass
385
+ # Fallback: check for "answer is X" or "= X" patterns
386
+ answer_pattern = re.search(r'(?:answer is|equals|=)\s*(-?\d+\.?\d*)', answer_text)
387
+ if answer_pattern:
388
  try:
389
+ if abs(float(answer_pattern.group(1)) - float(expected)) < 0.01:
390
  return 1.0
391
  except ValueError:
392
  pass
hugging/td_fuse/test_before_grpo.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Quick test: verify functions + healed model reasoning check.
3
+ Run this BEFORE GRPO to make sure everything works.
4
+
5
+ Part 1: Unit tests on verify_answer (catches false positives)
6
+ Part 2: Ask the healed model 10 reasoning questions
7
+ """
8
+
9
+ import sys
10
+ import os
11
+
12
+ # ============================================================
13
+ # PART 1: UNIT TESTS ON VERIFY FUNCTIONS
14
+ # ============================================================
15
+
16
+ print("=" * 60)
17
+ print("PART 1: VERIFY FUNCTION UNIT TESTS")
18
+ print("=" * 60)
19
+
20
+ from td_fuse.grpo_reason import verify_answer
21
+
22
+ tests_passed = 0
23
+ tests_failed = 0
24
+
25
+ def check(desc, problem, response, expected_score):
26
+ global tests_passed, tests_failed
27
+ score = verify_answer(problem, response)
28
+ status = "PASS" if score == expected_score else "FAIL"
29
+ if status == "FAIL":
30
+ tests_failed += 1
31
+ print(f" {status}: {desc}")
32
+ print(f" Expected {expected_score}, got {score}")
33
+ print(f" Response: {response[:100]}")
34
+ else:
35
+ tests_passed += 1
36
+
37
+ # -- "same" trick questions --
38
+ check("same: correct answer",
39
+ {"answer": "same"}, "They both weigh the same — a pound is a pound.", 1.0)
40
+ check("same: wrong answer says heavier",
41
+ {"answer": "same"}, "Bricks is heavier because bricks are denser.", 0.0)
42
+ check("same: mentions 'same' but also says heavier (contradiction)",
43
+ {"answer": "same"}, "They are the same but steel is heavier", 0.0)
44
+
45
+ # -- yes/no questions --
46
+ check("yes: correct",
47
+ {"answer": "yes"}, "Yes, whales do breathe air since they are mammals.", 1.0)
48
+ check("yes: wrong (says no)",
49
+ {"answer": "yes"}, "No, whales cannot breathe air.", 0.0)
50
+ check("no: correct (not definitely)",
51
+ {"answer": "no"}, "Not necessarily — only some dogs are large.", 1.0)
52
+ check("no: wrong (starts with yes)",
53
+ {"answer": "no"}, "Yes, Rex is definitely large.", 0.0)
54
+
55
+ # -- "once" trick --
56
+ check("once: correct",
57
+ {"answer": "once"}, "You can only subtract 5 from 25 once. After that it's 20.", 1.0)
58
+ check("once: wrong (says 5 times)",
59
+ {"answer": "once"}, "You can subtract 5 from 25 five times: 25, 20, 15, 10, 5.", 0.0)
60
+
61
+ # -- "all" months trick --
62
+ check("all: correct (all 12)",
63
+ {"answer": "all"}, "All 12 months have at least 28 days.", 1.0)
64
+ check("all: wrong (says 1)",
65
+ {"answer": "all"}, "Only February has exactly 28 days.", 0.0)
66
+
67
+ # -- "neither" rooster trick --
68
+ check("neither: correct",
69
+ {"answer": "neither"}, "Neither side — roosters don't lay eggs!", 1.0)
70
+ check("neither: wrong (says left)",
71
+ {"answer": "neither"}, "The egg rolls down the left side.", 0.0)
72
+
73
+ # -- ordinal (2nd place) --
74
+ check("2nd: correct with CoT",
75
+ {"answer": "2nd"},
76
+ "<think>If you pass 2nd place, you take their spot.</think>\nYou are now in second place.", 1.0)
77
+ check("2nd: wrong (says 1st)",
78
+ {"answer": "2nd"},
79
+ "<think>You passed 2nd, so you must be first!</think>\nYou are in first place.", 0.0)
80
+
81
+ # -- day names (temporal) --
82
+ check("day: correct (wednesday)",
83
+ {"answer": "wednesday"},
84
+ "<think>Yesterday was Monday, so today is Tuesday. Tomorrow is Wednesday.</think>\nThe answer is Wednesday.", 1.0)
85
+ check("day: wrong (says thursday)",
86
+ {"answer": "wednesday"},
87
+ "<think>Yesterday was Monday, today is Tuesday, tomorrow is Thursday.</think>\nThursday.", 0.0)
88
+
89
+ # -- numeric: last number only --
90
+ check("numeric: correct (last number matches)",
91
+ {"answer": "150"}, "60 × 2.5 = 150. The train travels 150 miles.", 1.0)
92
+ check("numeric: false positive check (expected in calculation but wrong answer)",
93
+ {"answer": "20"}, "I start with 25 apples and eat 3, so I have 22 left.", 0.0)
94
+ check("numeric: #### format correct",
95
+ {"answer": "105"}, "Each shirt is $15. 7 × 15 = 105.\n#### 105", 1.0)
96
+ check("numeric: #### format wrong",
97
+ {"answer": "105"}, "Each shirt is $15. 7 × 15 = 100.\n#### 100", 0.0)
98
+
99
+ # -- name answers (sam, carol, etc) --
100
+ check("name: correct (sam won)",
101
+ {"answer": "sam"}, "<think>Sam finished first, then Tom, then Jerry.</think>\nSam won the race.", 1.0)
102
+ check("name: wrong (says tom)",
103
+ {"answer": "sam"}, "<think>Tom finished first.</think>\nTom won the race.", 0.0)
104
+
105
+ # -- single letter answers --
106
+ check("letter: correct (c is shortest)",
107
+ {"answer": "c"}, "<think>A > B > C.</think>\nC is the shortest.", 1.0)
108
+
109
+ print(f"\nResults: {tests_passed} passed, {tests_failed} failed out of {tests_passed + tests_failed}")
110
+ if tests_failed > 0:
111
+ print("FIX THE VERIFY BUGS BEFORE RUNNING GRPO!")
112
+ sys.exit(1)
113
+ else:
114
+ print("All verify tests passed!")
115
+
116
+ # ============================================================
117
+ # PART 2: ASK THE HEALED MODEL (if model exists)
118
+ # ============================================================
119
+
120
+ MODEL_PATH = "td_fuse_outputs/reasoning_healed"
121
+ if not os.path.exists(MODEL_PATH):
122
+ print(f"\nModel not found at {MODEL_PATH} — skipping live test")
123
+ sys.exit(0)
124
+
125
+ print(f"\n{'=' * 60}")
126
+ print("PART 2: HEALED MODEL REASONING TEST")
127
+ print(f"{'=' * 60}")
128
+
129
+ import torch
130
+ from transformers import AutoModelForImageTextToText, AutoTokenizer
131
+
132
+ print("Loading model...")
133
+ model = AutoModelForImageTextToText.from_pretrained(
134
+ MODEL_PATH, dtype=torch.bfloat16,
135
+ device_map="auto", trust_remote_code=True
136
+ )
137
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
138
+ eos_id = tokenizer.convert_tokens_to_ids("<|im_end|>")
139
+ print("Loaded!\n")
140
+
141
+ questions = [
142
+ ("Which is heavier: a pound of feathers or a pound of bricks?", "same"),
143
+ ("If yesterday was Monday, what day is tomorrow?", "wednesday"),
144
+ ("A farmer has 17 sheep. All but 9 die. How many are left?", "9"),
145
+ ("What is 15% of 200?", "30"),
146
+ ("All mammals breathe air. Whales are mammals. Do whales breathe air?", "yes"),
147
+ ("In a race, Tom finished before Jerry but after Sam. Who won?", "sam"),
148
+ ("How many months have 28 days?", "all"),
149
+ ("If A > B and B > C, who is smallest?", "c"),
150
+ ("A car goes 80 mph for 3 hours. How far?", "240"),
151
+ ("What comes next: 3, 7, 11, 15, 19?", "23"),
152
+ ]
153
+
154
+ correct = 0
155
+ for q, expected in questions:
156
+ prompt = (
157
+ f"<|im_start|>user\n{q}\n"
158
+ f"Think step by step before giving your final answer.<|im_end|>\n"
159
+ f"<|im_start|>assistant\n<think>\n"
160
+ )
161
+ ids = tokenizer(prompt, return_tensors="pt").to(model.device)
162
+ out = model.generate(**ids, max_new_tokens=500, do_sample=False, eos_token_id=eos_id)
163
+ response = tokenizer.decode(out[0][ids.input_ids.shape[1]:], skip_special_tokens=True)
164
+
165
+ score = verify_answer({"answer": expected}, response)
166
+ answer_part = response.split("</think>")[-1].strip() if "</think>" in response else response[:100]
167
+ status = "OK" if score > 0 else "FAIL"
168
+ if score > 0:
169
+ correct += 1
170
+ print(f" {status}: {q[:60]}")
171
+ print(f" -> {answer_part[:120]}")
172
+
173
+ print(f"\nScore: {correct}/10")
174
+ print("Model is ready for GRPO!" if correct >= 5 else "Model might need more healing — low score")