codedebugger / training /train_grpo.py
psdhanushkumar's picture
Upload folder using huggingface_hub
f361447 verified
Raw
History Blame Contribute Delete
7.16 kB
import os
import sys
import json
import re
import torch
from datetime import datetime
# Add root directory to sys.path so we can import project modules
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from data.bug_dataset import TRAINING_SCENARIOS
from orchestrator import Debugger
from env.codedebugger_env import CodeDebuggerEnv
from utils.prompts import get_simplified_prompt
# Unsloth & TRL imports
from unsloth import FastLanguageModel
from trl import GRPOConfig, GRPOTrainer
from datasets import Dataset
# 1. Setup Model via Unsloth (4-bit quantization for fast training)
max_seq_length = 2048
lora_rank = 16
print("Loading unsloth/Llama-3.2-1B-Instruct...")
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Llama-3.2-1B-Instruct",
max_seq_length=max_seq_length,
load_in_4bit=True,
fast_inference=False, # Set to False since vLLM is not installed
)
# Apply LoRA Adapter
model = FastLanguageModel.get_peft_model(
model,
r=lora_rank,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha=lora_rank,
use_gradient_checkpointing="unsloth",
random_state=3407,
)
# 2. Setup Curriculum Dataset
# Train easy problems first, then medium, then hard
difficulty_map = {"easy": 0, "medium": 1, "hard": 2}
sorted_scenarios = sorted(TRAINING_SCENARIOS, key=lambda x: difficulty_map.get(x["difficulty"], 3))
def format_prompt(problem):
sys_prompt = "Fix the python code. Return ONLY the fixed code in a markdown block."
user_prompt = get_simplified_prompt(problem["buggy_code"], problem.get("error_type", "Bug"))
return [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt}
]
dataset_dict = {
"prompt": [format_prompt(p) for p in sorted_scenarios],
"problem_id": [p["id"] for p in sorted_scenarios],
"difficulty": [p["difficulty"] for p in sorted_scenarios]
}
train_dataset = Dataset.from_dict(dataset_dict)
# 3. Setup Rollout Reward Function
env = CodeDebuggerEnv(max_iterations=1)
global_step = 0
os.makedirs("outputs", exist_ok=True)
log_file = open("outputs/training_log.jsonl", "w")
def get_completion_text(comp):
if isinstance(comp, list): return comp[0]["content"]
if isinstance(comp, dict): return comp["content"]
return str(comp)
def executor_reward(prompts, completions, problem_id, difficulty, **kwargs):
global global_step
rewards = []
for i, comp in enumerate(completions):
prob_id = problem_id[i][0] if isinstance(problem_id[i], list) else problem_id[i]
diff = difficulty[i][0] if isinstance(difficulty[i], list) else difficulty[i]
# Find corresponding problem
prob = next(p for p in TRAINING_SCENARIOS if p["id"] == prob_id)
# Extract code from LLM output
completion_text = get_completion_text(comp)
match = re.search(r"```(?:python)?\n?(.*?)\n?```", completion_text, re.DOTALL)
fixed_code = match.group(1).strip() if match else completion_text.strip()
# Execute code in env
env.reset(prob)
obs, reward_dict, done, info = env.step(fixed_code)
total_reward = reward_dict["total"]
rewards.append(total_reward)
# Log to jsonl
log_entry = {
"step": global_step,
"problem_id": prob_id,
"difficulty": diff,
"reward": total_reward,
"test_score": reward_dict.get("test_score", 0.0),
"total": total_reward
}
log_file.write(json.dumps(log_entry) + "\n")
log_file.flush()
global_step += 1
return rewards
# 4. Training configuration
training_args = GRPOConfig(
output_dir="outputs/grpo_training",
learning_rate=5e-6,
lr_scheduler_type="cosine",
max_steps=100,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
optim="adamw_8bit",
weight_decay=0.01,
warmup_ratio=0.1,
logging_steps=1,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
report_to="none", # Disabling wandb for simplicity
)
trainer = GRPOTrainer(
model=model,
processing_class=tokenizer,
reward_funcs=executor_reward,
args=training_args,
train_dataset=train_dataset,
)
print("\nStarting GRPO Training...")
trainer.train()
# 5. CRITICAL: Save ONLY the adapter, do not merge
print("\nSaving LoRA adapter...")
model.save_pretrained("outputs/trained_adapter")
tokenizer.save_pretrained("outputs/trained_adapter")
print("Adapter saved successfully to outputs/trained_adapter")
# 6. Evaluate all 30 problems using the trained model
class LocalLLMFixer:
def __init__(self, model, tokenizer):
self.model = model
self.tokenizer = tokenizer
FastLanguageModel.for_inference(self.model) # Enable native 2x faster inference
def fix_code(self, buggy_code, error_type, description, test_cases, test_results=None, previous_explanation=None, iteration=1):
prompt_text = get_simplified_prompt(buggy_code, error_type)
messages = [
{"role": "system", "content": "Fix the python code. Return ONLY the fixed code in a markdown block."},
{"role": "user", "content": prompt_text}
]
inputs = self.tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to("cuda")
outputs = self.model.generate(input_ids=inputs, max_new_tokens=1024, temperature=0.2)
response = self.tokenizer.decode(outputs[0][inputs.shape[-1]:], skip_special_tokens=True)
match = re.search(r"```(?:python)?\n?(.*?)\n?```", response, re.DOTALL)
fixed_code = match.group(1).strip() if match else response.strip()
return {"fixed_code": fixed_code, "method": "local_llm"}
print("\nEvaluating all 30 problems with trained model...")
debugger = Debugger(max_iterations=3)
debugger.fixer = LocalLLMFixer(model, tokenizer) # Hot-swap the fixer
trained_results = []
for i, prob in enumerate(TRAINING_SCENARIOS):
print(f"[{i+1}/30] Evaluating {prob['title']}...")
res = debugger.run(prob, verbose=False)
trained_results.append(res)
output = {
"format_version": 1,
"model": "unsloth/Llama-3.2-1B-Instruct-GRPO",
"timestamp": datetime.now().isoformat(),
"results": trained_results,
}
with open("outputs/trained_scores.json", "w") as f:
json.dump(output, f, indent=2)
# 7. Print summary comparison
try:
with open("outputs/baseline_scores.json", "r") as f:
b_data = json.load(f)
baseline = b_data.get("results", b_data) if isinstance(b_data, dict) else b_data
base_solved = sum(1 for p in baseline if p.get("solved"))
except Exception:
base_solved = "?"
train_solved = sum(1 for r in trained_results if r["solved"])
print("\n" + "="*50)
print(f"TRAINING AND EVALUATION COMPLETE!")
print(f"Baseline Solved: {base_solved}/30")
print(f"Trained Solved: {train_solved}/30")
print("="*50)
print("Dashboard ready: run `streamlit run app.py`")