Skip to content

Latest commit

 

History

History
423 lines (286 loc) · 12.6 KB

File metadata and controls

423 lines (286 loc) · 12.6 KB

Complete Guide to Fine-Tuning Qwen for First Principles Reasoning on Mac Using GRPO

1. Mac Environment Setup

Hardware Requirements

  • Apple Silicon (M1/M2/M3) with 16GB+ RAM (24GB+ recommended for 7B models)
  • macOS Sonoma 14.0+ with Xcode 15.4+

Install Core Dependencies

# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install Python and system tools
brew install python@3.10 git cmake

# Create virtual environment
python3.10 -m venv ~/grpo-env
source ~/grpo-env/bin/activate

# Install PyTorch with MPS support
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu

# Install vLLM from source (experimental Mac support)
git clone https://github.com/vllm-project/vllm.git
cd vllm && pip install -r requirements-cpu.txt && pip install -e .

2. Model Preparation

Download Qwen2.5 from Hugging Face

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "Qwen/Qwen2.5-7B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto",
    trust_remote_code=True
)

3. GRPO Training Configuration

Custom Training Script (grpo_train.py)

import os
import torch
import wandb
from datasets import load_dataset
from transformers import (
    AutoTokenizer,
    AutoModelForCausalLM,
    TrainingArguments,
    set_seed
)
from trl import GRPOConfig, GRPOTrainer
from peft import LoraConfig, get_peft_model

# Initialize W&B
wandb.init(project="qwen-first-principles", name="grpo-mac-run")

class FirstPrinciplesTrainer:
    def __init__(self):
        self.config = {
            "model_name": "Qwen/Qwen2.5-7B",
            "lora_config": {
                "r": 32,
                "lora_alpha": 64,
                "target_modules": ["q_proj", "v_proj"],
                "lora_dropout": 0.1,
                "bias": "none",
                "task_type": "CAUSAL_LM"
            },
            "grpo_config": {
                "num_generations": 4,
                "temperature": 0.7,
                "max_prompt_length": 512,
                "max_completion_length": 1024,
                "kl_penalty": 0.1
            },
            "training_args": {
                "output_dir": "./grpo-output",
                "num_train_epochs": 1,
                "per_device_train_batch_size": 2,
                "gradient_accumulation_steps": 8,
                "learning_rate": 1e-6,
                "fp16": True,
                "logging_steps": 10,
                "optim": "adamw_torch",
                "report_to": "wandb"
            }
        }

    def load_model(self):
        self.tokenizer = AutoTokenizer.from_pretrained(self.config["model_name"])
        self.model = AutoModelForCausalLM.from_pretrained(
            self.config["model_name"],
            torch_dtype=torch.float16,
            device_map="auto",
            trust_remote_code=True
        )
        
        # Apply LoRA for memory efficiency
        peft_config = LoraConfig(**self.config["lora_config"])
        self.model = get_peft_model(self.model, peft_config)
        self.model.print_trainable_parameters()

    def create_reward_function(self, response):
        """Verifiable first principles reward function"""
        score = 0.0
        
        # Structure validation
        if re.search(r'<reasoning>.*?</reasoning>\s*<answer>.*?</answer>', response):
            score += 0.3
            
        # First principles indicators
        fp_keywords = ['fundamental', 'core concept', 'building block', 'basic principle']
        score += 0.1 * sum(1 for kw in fp_keywords if kw in response.lower())
        
        # Feynman technique check
        feynman_indicators = ['analogy', 'simple terms', 'imagine that', 'for example']
        score += 0.1 * sum(1 for fi in feynman_indicators if fi in response.lower())
        
        # Answer correctness (simple placeholder)
        if "42" in response:  # Replace with actual validation
            score += 0.5
            
        return min(score, 1.0)

    def prepare_dataset(self):
        """Create first principles prompts dataset"""
        return load_dataset("json", data_files={"train": "first_principles_prompts.json"})

    def train(self):
        grpo_config = GRPOConfig(
            **self.config["grpo_config"],
            use_vllm=True,
            vllm_mode="colocate",
            vllm_gpu_memory_utilization=0.7
        )

        training_args = TrainingArguments(
            **self.config["training_args"],
            remove_unused_columns=False,
            dataloader_num_workers=0  # Required for Mac compatibility
        )

        trainer = GRPOTrainer(
            model=self.model,
            args=training_args,
            train_dataset=self.prepare_dataset()["train"],
            tokenizer=self.tokenizer,
            reward_func=self.create_reward_function
        )

        trainer.train()

if __name__ == "__main__":
    trainer = FirstPrinciplesTrainer()
    trainer.load_model()
    trainer.train()

4. Dataset Preparation

Sample Prompt Structure (first_principles_prompts.json)

[
    {
        "prompt": "Using first principles, explain why objects fall at the same rate in a vacuum. Format your answer with <reasoning> and <answer> blocks.",
        "category": "physics"
    },
    {
        "prompt": "Break down the concept of supply and demand from fundamental economic principles. Use the Feynman technique in your explanation.",
        "category": "economics"
    }
]

5. Running the Training

# Activate environment
source ~/grpo-env/bin/activate

# Login to W&B
wandb login

# Start training with MPS acceleration
MPS_DEVICE=1 CUDA_VISIBLE_DEVICES=0 python grpo_train.py

6. Key Mac-Specific Optimizations

Memory Management

# Add to training arguments
training_args = TrainingArguments(
    ...
    gradient_checkpointing=True,
    optim="adafactor",
    torch_compile=True  # Use Metal Shader Graph
)

vLLM Configuration

GRPOConfig(
    ...
    vllm_max_model_len=2048,
    vllm_enable_chunked_prefill=True,
    vllm_gpu_memory_utilization=0.8
)

7. Monitoring with Weights & Biases

![GRPO Training Dashboard](

GRPO Training Workflow for First Principles Reasoning on Mac

GRPO Training Workflow for First Principles Reasoning on Mac

)

8. Model Evaluation

def evaluate_response(prompt, response):
    wandb.log({
        "reasoning_score": float('<reasoning>' in response),
        "answer_score": float('<answer>' in response),
        "fp_keywords": sum(1 for kw in fp_keywords if kw in response),
        "feynman_score": sum(1 for fi in feynman_indicators if fi in response)
    })

9. Post-Training Usage

from peft import PeftModel

# Load fine-tuned model
model = PeftModel.from_pretrained(
    AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B"),
    "./grpo-output"
)

# Merge LoRA weights
model = model.merge_and_unload()

Troubleshooting Common Mac Issues

  1. MPS Memory Errors:
    • Reduce batch size: per_device_train_batch_size=1
    • Enable gradient checkpointing
    • Use torch.mps.empty_cache() periodically
  2. vLLM Compatibility:
# Reinstall with specific flags
CMAKE_ARGS="-DLLAMA_METAL=on" pip install -e .
  1. Performance Optimization:
# Enable Metal kernels
torch.set_float32_matmul_precision('high')

This guide provides an end-to-end solution for implementing verifiable first principles reasoning with Qwen on Apple Silicon, leveraging GRPO's efficiency and Mac's native Metal acceleration.