Siddh12334 commited on
Commit
5f54992
·
1 Parent(s): ef79a5f

feat: baseline eval and GRPO training script

Browse files
eval/baseline_eval.py CHANGED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import random
3
+ from pathlib import Path
4
+
5
+ from environment.env import ContextCorruptionEnv
6
+ from environment.actions import ContextCorruptionAction, ActionType
7
+
8
+ NUM_EPISODES = 100
9
+ RESULTS_PATH = Path(__file__).parent / "baseline_results.json"
10
+
11
+
12
+ def run_baseline():
13
+ env = ContextCorruptionEnv()
14
+ rewards = []
15
+
16
+ for ep in range(NUM_EPISODES):
17
+ obs = env.reset()
18
+ done = False
19
+
20
+ while not done:
21
+ # Randomly flag 0-4 docs
22
+ if random.random() < 0.4 and obs.budget_remaining > 1:
23
+ doc_id = random.randint(0, len(obs.documents) - 1)
24
+ action = ContextCorruptionAction(
25
+ action_type=ActionType.flag_suspicious,
26
+ doc_id=doc_id,
27
+ )
28
+ else:
29
+ action = ContextCorruptionAction(
30
+ action_type=ActionType.submit_answer,
31
+ answer="unknown",
32
+ confidence=0.5,
33
+ )
34
+
35
+ obs = env.step(action)
36
+ done = obs.episode_done
37
+
38
+ rewards.append(obs.reward)
39
+ if (ep + 1) % 10 == 0:
40
+ print(f"Episode {ep + 1}/{NUM_EPISODES} | reward: {obs.reward:.4f} | avg so far: {sum(rewards)/len(rewards):.4f}")
41
+
42
+ avg = round(sum(rewards) / len(rewards), 4)
43
+ minimum = round(min(rewards), 4)
44
+ maximum = round(max(rewards), 4)
45
+
46
+ results = {
47
+ "num_episodes": NUM_EPISODES,
48
+ "avg_reward": avg,
49
+ "min_reward": minimum,
50
+ "max_reward": maximum,
51
+ "all_rewards": rewards,
52
+ }
53
+
54
+ RESULTS_PATH.write_text(json.dumps(results, indent=2))
55
+ print(f"\nBaseline results — avg: {avg} | min: {minimum} | max: {maximum}")
56
+ print(f"Saved to {RESULTS_PATH}")
57
+ return results
58
+
59
+
60
+ if __name__ == "__main__":
61
+ run_baseline()
eval/baseline_results.json ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "num_episodes": 100,
3
+ "avg_reward": 0.1314,
4
+ "min_reward": -0.0792,
5
+ "max_reward": 0.3292,
6
+ "all_rewards": [
7
+ 0.1458,
8
+ 0.1458,
9
+ 0.1458,
10
+ 0.1458,
11
+ 0.1458,
12
+ 0.0417,
13
+ 0.0417,
14
+ 0.1875,
15
+ 0.1458,
16
+ 0.2917,
17
+ 0.1458,
18
+ 0.1458,
19
+ 0.0417,
20
+ 0.1458,
21
+ 0.1458,
22
+ 0.1458,
23
+ 0.1458,
24
+ 0.0417,
25
+ 0.0417,
26
+ -0.0792,
27
+ 0.1458,
28
+ 0.1458,
29
+ 0.0417,
30
+ 0.1458,
31
+ 0.1458,
32
+ 0.1458,
33
+ 0.1458,
34
+ 0.2917,
35
+ 0.1458,
36
+ 0.0417,
37
+ 0.1458,
38
+ 0.2917,
39
+ 0.1458,
40
+ 0.1458,
41
+ 0.0417,
42
+ 0.1458,
43
+ 0.1458,
44
+ 0.3292,
45
+ 0.0417,
46
+ 0.0417,
47
+ 0.0833,
48
+ 0.1458,
49
+ 0.1458,
50
+ 0.1458,
51
+ 0.1458,
52
+ 0.0417,
53
+ 0.1458,
54
+ 0.1125,
55
+ 0.0417,
56
+ 0.2417,
57
+ 0.1458,
58
+ 0.1458,
59
+ 0.1458,
60
+ 0.1458,
61
+ 0.2417,
62
+ 0.1458,
63
+ 0.0417,
64
+ 0.1458,
65
+ 0.1458,
66
+ 0.1458,
67
+ 0.1458,
68
+ 0.1458,
69
+ 0.1458,
70
+ 0.1458,
71
+ -0.0667,
72
+ 0.1458,
73
+ 0.1458,
74
+ 0.1125,
75
+ 0.0417,
76
+ 0.1125,
77
+ 0.0417,
78
+ 0.1458,
79
+ 0.1458,
80
+ 0.1458,
81
+ 0.1458,
82
+ 0.2167,
83
+ 0.1458,
84
+ 0.1125,
85
+ 0.1458,
86
+ 0.1458,
87
+ 0.1458,
88
+ 0.0417,
89
+ 0.1292,
90
+ 0.1458,
91
+ 0.1458,
92
+ 0.1458,
93
+ 0.1458,
94
+ 0.1458,
95
+ 0.1458,
96
+ 0.2333,
97
+ 0.2167,
98
+ 0.1458,
99
+ 0.0417,
100
+ 0.0417,
101
+ 0.1458,
102
+ 0.1458,
103
+ 0.1458,
104
+ 0.1458,
105
+ 0.1458,
106
+ 0.1458
107
+ ]
108
+ }
training/train_grpo.py CHANGED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import wandb
3
+
4
+ # Fill in after HF Space is live
5
+ ENV_URL = os.getenv("ENV_URL", "http://localhost:8000")
6
+
7
+ SYSTEM_PROMPT = """You are an epistemic agent. You will be given a question and a set of documents.
8
+ Your goal is to identify the correct answer and flag any documents that contain corrupted or false information.
9
+
10
+ You have three tools:
11
+ - read_doc(doc_id): Spend 1 budget unit to focus on a specific document (content already visible).
12
+ - flag_suspicious(doc_id): Mark a document as potentially corrupted.
13
+ - submit_answer(answer, confidence): End the episode with your final answer and a confidence score (0.0–1.0).
14
+
15
+ Strategy:
16
+ - Cross-reference claims across documents. Corrupted documents will contradict the majority or your parametric knowledge.
17
+ - When documents conflict, trust your parametric knowledge and flag the outlier.
18
+ - Use your budget wisely — you have 12 actions total. Flag only when confident, as false positives are penalised.
19
+ - Submit as soon as you are confident; unused budget gives a small efficiency bonus."""
20
+
21
+
22
+ def build_prompt(obs_dict: dict) -> str:
23
+ docs_text = "\n\n".join(
24
+ f"[Doc {d['id']}] {d['title']}\n{d['content']}"
25
+ for d in obs_dict["documents"]
26
+ )
27
+ flagged = obs_dict.get("flagged_ids", [])
28
+ return (
29
+ f"Question: {obs_dict['question']}\n\n"
30
+ f"Documents:\n{docs_text}\n\n"
31
+ f"Flagged so far: {flagged}\n"
32
+ f"Budget remaining: {obs_dict['budget_remaining']}\n"
33
+ f"Turn: {obs_dict['turn']}\n\n"
34
+ "What is your next action? Respond with a JSON action object:\n"
35
+ '{"action_type": "submit_answer", "answer": "...", "confidence": 0.9}\n'
36
+ "or\n"
37
+ '{"action_type": "flag_suspicious", "doc_id": 2}'
38
+ )
39
+
40
+
41
+ def main():
42
+ from unsloth import FastLanguageModel
43
+ from trl import GRPOTrainer, GRPOConfig
44
+
45
+ wandb.init(project="context-corruption-env", name="qwen-1.5b-grpo-run1")
46
+
47
+ # Load model with Unsloth
48
+ model, tokenizer = FastLanguageModel.from_pretrained(
49
+ model_name="unsloth/Qwen2-1.5B-Instruct",
50
+ max_seq_length=2048,
51
+ load_in_4bit=True,
52
+ )
53
+ model = FastLanguageModel.get_peft_model(
54
+ model,
55
+ r=16,
56
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
57
+ lora_dropout=0.0,
58
+ use_gradient_checkpointing="unsloth",
59
+ )
60
+
61
+ # Environment factory for GRPO — one env instance per parallel generation
62
+ from environment.env import ContextCorruptionEnv
63
+
64
+ def env_factory():
65
+ return ContextCorruptionEnv()
66
+
67
+ config = GRPOConfig(
68
+ num_train_epochs=3,
69
+ per_device_train_batch_size=4,
70
+ gradient_accumulation_steps=4,
71
+ learning_rate=5e-5,
72
+ max_completion_length=512,
73
+ num_generations=8,
74
+ report_to="wandb",
75
+ logging_steps=10,
76
+ save_steps=50,
77
+ output_dir="checkpoints/grpo-qwen-1.5b",
78
+ )
79
+
80
+ trainer = GRPOTrainer(
81
+ model=model,
82
+ args=config,
83
+ processing_class=tokenizer,
84
+ environment_factory=env_factory,
85
+ )
86
+
87
+ trainer.train()
88
+ wandb.finish()
89
+
90
+ # Save model
91
+ model.save_pretrained("checkpoints/grpo-qwen-1.5b-final")
92
+ tokenizer.save_pretrained("checkpoints/grpo-qwen-1.5b-final")
93
+ print("Training complete. Model saved to checkpoints/grpo-qwen-1.5b-final")
94
+
95
+
96
+ if __name__ == "__main__":
97
+ main()