PeterMaZep

Untitled

Mar 8th, 2026
39
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.81 KB | Source Code | 0 0
  1. import os
  2. from unsloth import FastLanguageModel, PatchFastRL, is_bfloat16_supported
  3. PatchFastRL("GRPO", FastLanguageModel)
  4.  
  5. import torch
  6. import re
  7. from datasets import load_dataset, Dataset, concatenate_datasets
  8. from transformers import AutoConfig, AutoTokenizer
  9. from trl import GRPOConfig, GRPOTrainer
  10.  
  11. # -------------------------------
  12. # Model setup
  13. # -------------------------------
  14. max_seq_length = 256
  15. lora_rank = 64
  16.  
  17. model, tokenizer = FastLanguageModel.from_pretrained(
  18.     model_name="unsloth/Phi-4-mini-reasoning-unsloth-bnb-4bit",
  19.     cache_dir="/mnt/d/AI/models/phi-4-mini-reasoning-unsloth-bnb-4bit",
  20.     max_seq_length=max_seq_length,
  21.     max_lora_rank=lora_rank,
  22.     gpu_memory_utilization=0.85,
  23.     device_map="auto",
  24.     fast_inference=False,
  25. )
  26.  
  27. print(type(tokenizer))
  28. print(model.config.model_type)
  29.  
  30. model = FastLanguageModel.get_peft_model(
  31.     model,
  32.     r=lora_rank,
  33.     target_modules=[
  34.         "q_proj", "k_proj", "v_proj", "o_proj",
  35.         "gate_proj", "up_proj", "down_proj",
  36.     ],
  37.     lora_alpha=lora_rank,
  38.     use_gradient_checkpointing="unsloth",
  39.     random_state=3407,
  40. )
  41.  
  42. # -------------------------------
  43. # Prompt format
  44. # -------------------------------
  45. SYSTEM_PROMPT = """You are Villager. Respond in the following format:
  46. <think>
  47. ...
  48. </think>
  49. ..."""
  50.  
  51. XML_COT_FORMAT = """<think>
  52. {reasoning}
  53. </think>
  54. {answer}"""
  55.  
  56. # -------------------------------
  57. # Extraction helpers
  58. # -------------------------------
  59. def extract_xml_answer(text: str) -> str:
  60.     if "</think>" in text:
  61.         return text.split("</think>")[-1].strip()
  62.     return text.strip()
  63.  
  64. def extract_think(text: str) -> str:
  65.     if "<think>" in text and "</think>" in text:
  66.         return text.split("<think>")[-1].split("</think>")[0].strip()
  67.     return ""
  68.  
  69. def extract_hash_answer(text: str) -> str | None:
  70.     if "####" not in text:
  71.         return None
  72.     return text.split("####")[1].strip()
  73.  
  74. # -------------------------------
  75. # Dataset loaders
  76. # -------------------------------
  77. def get_gsm8k_questions(split="train") -> Dataset:
  78.     data = load_dataset("openai/gsm8k", "main")[split]
  79.     data = data.map(lambda x: {
  80.         "prompt": [
  81.             {"role": "system", "content": SYSTEM_PROMPT},
  82.             {"role": "user", "content": x["question"]}
  83.         ],
  84.         "answer": extract_hash_answer(x["answer"])
  85.     })
  86.     return data
  87.  
  88. def get_mcwiki(split="train") -> Dataset:
  89.     data = load_dataset("lparkourer10/minecraft-wiki")[split]
  90.     data = data.map(lambda x: {
  91.         "prompt": [
  92.             {"role": "system", "content": SYSTEM_PROMPT},
  93.             {"role": "user", "content": x["question"]}
  94.         ],
  95.         "answer": x["answer"]
  96.     })
  97.     return data
  98.  
  99. gsm8k = get_gsm8k_questions()
  100. mcwiki = get_mcwiki()
  101. dataset = concatenate_datasets([gsm8k, mcwiki])
  102.  
  103. # -------------------------------
  104. # Reward functions
  105. # -------------------------------
  106. def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]:
  107.     responses = [completion[0]['content'] for completion in completions]
  108.     q = prompts[0][-1]['content']
  109.     extracted_responses = [extract_xml_answer(r) for r in responses]
  110.     print('-'*20, f"Question:\n{q}", f"\nAnswer:\n{answer[0]}",
  111.           f"\nResponse:\n{responses[0]}", f"\nExtracted:\n{extracted_responses[0]}")
  112.     return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)]
  113.  
  114. def int_reward_func(completions, **kwargs) -> list[float]:
  115.     responses = [completion[0]['content'] for completion in completions]
  116.     extracted_responses = [extract_xml_answer(r) for r in responses]
  117.     return [0.5 if r.isdigit() else 0.0 for r in extracted_responses]
  118.  
  119. def strict_format_reward_func(completions, **kwargs) -> list[float]:
  120.     """Check for strict <think>...</think>{answer} format."""
  121.     pattern = r"^<think>\n.*?\n</think>\n.*$"
  122.     responses = [completion[0]["content"] for completion in completions]
  123.     matches = [re.match(pattern, r, re.DOTALL) for r in responses]
  124.     return [0.5 if match else 0.0 for match in matches]
  125.  
  126. def soft_format_reward_func(completions, **kwargs) -> list[float]:
  127.     """Check for loose <think>...</think>{answer} format."""
  128.     pattern = r"<think>.*?</think>.*"
  129.     responses = [completion[0]["content"] for completion in completions]
  130.     matches = [re.match(pattern, r, re.DOTALL) for r in responses]
  131.     return [0.5 if match else 0.0 for match in matches]
  132.  
  133. def count_xml(text) -> float:
  134.     count = 0.0
  135.     if text.count("<think>\n") == 1:
  136.         count += 0.25
  137.     if text.count("\n</think>\n") == 1:
  138.         count += 0.25
  139.     return count
  140.  
  141. def xmlcount_reward_func(completions, **kwargs) -> list[float]:
  142.     contents = [completion[0]["content"] for completion in completions]
  143.     return [count_xml(c) for c in contents]
  144.  
  145. # -------------------------------
  146. # Training setup
  147. # -------------------------------
  148. training_args = GRPOConfig(
  149.     use_vllm=True,
  150.     learning_rate=2e-5,
  151.     adam_beta1=0.9,
  152.     adam_beta2=0.95,
  153.     weight_decay=0.01,
  154.     warmup_ratio=0.05,
  155.     lr_scheduler_type="cosine",
  156.     optim="adamw_8bit",
  157.     logging_steps=5,
  158.     bf16=is_bfloat16_supported(),
  159.     fp16=not is_bfloat16_supported(),
  160.     per_device_train_batch_size=1,
  161.     gradient_accumulation_steps=4,
  162.     num_generations=2,
  163.     max_prompt_length=256,
  164.     max_completion_length=256,
  165.     max_steps=500,
  166.     save_steps=100,
  167.     max_grad_norm=1.0,
  168.     report_to="wandb",
  169.     output_dir="outputs_phi4",
  170.     run_name="Villager"
  171. )
  172.  
  173. trainer = GRPOTrainer(
  174.     model=model,
  175.     processing_class=tokenizer,
  176.     reward_funcs=[
  177.         xmlcount_reward_func,
  178.         soft_format_reward_func,
  179.         strict_format_reward_func,
  180.         int_reward_func,
  181.         correctness_reward_func,
  182.     ],
  183.     args=training_args,
  184.     train_dataset=dataset,
  185. )
  186.  
  187. trainer.train()
  188. model.save_lora("grpo_saved_lora")
Tags: unsloth
Advertisement
Add Comment
Please, Sign In to add comment