3.04 โ€“ Fine-Tuning & Custom Models | AI Course

๐ŸŽ“ 3.04 โ€“ Fine-Tuning & Custom Models

๐Ÿ“‘ On this page

๐Ÿ“Œ When to Fine-Tune

๐Ÿ“Œ RAG vs Fine-Tuning vs Prompt Engineering
AspectPrompt EngineeringRAGFine-Tuning
New knowledge โŒ Limited โœ… Best โœ… Good (if in training data)
Style/tone control โš ๏ธ Limited โŒ No โœ… Best
Cost to operate Low Medium (API + storage) Low (same base model cost)
One-time cost $0 $ (embedding) $$$ (training)
Iteration speed Fast (edit prompt) Medium (update docs) Slow (retrain)
๐Ÿ’ก When to Fine-Tune:

๐Ÿ“Š Data Preparation

๐Ÿ“Œ Format for OpenAI Fine-Tuning:
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "4"}]}
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "Paris"}]}
๐Ÿ“Œ Data Best Practices:
Creating Training Data from Examples:
import json

def create_training_data(inputs, outputs):
    training_data = []
    for user_input, assistant_output in zip(inputs, outputs):
        entry = {
            "messages": [
                {"role": "system", "content": "You are a customer service agent for Acme Corp. Be polite and helpful."},
                {"role": "user", "content": user_input},
                {"role": "assistant", "content": assistant_output}
            ]
        }
        training_data.append(entry)
    
    # Save to JSONL
    with open("training_data.jsonl", "w") as f:
        for entry in training_data:
            f.write(json.dumps(entry) + "\n")

โš™๏ธ Fine-Tuning Methods (LoRA, QLoRA)

๐Ÿ“Œ LoRA (Low-Rank Adaptation)

Instead of updating all model weights, LoRA adds small trainable matrices to selected layers. Much more efficient than full fine-tuning.

๐Ÿ“Œ QLoRA (Quantized LoRA)

Quantizes the base model to 4-bit, then applies LoRA. Allows fine-tuning on a single consumer GPU.

MethodMemory RequiredTraining SpeedQualityBest for
Full Fine-Tuning >80GB GPU (2-4xA100) Slowest Best Production models with resources
LoRA ~20-40GB (1xA100) Moderate ~90% of full Most practical for fine-tuning
QLoRA ~8-16GB (1xRTX 4090/3090) Slower due to quantization ~85-90% Consumer GPUs, personal projects
QLoRA Fine-Tuning with Hugging Face (Simplified):
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
import torch

# 4-bit quantization config
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

# Load base model with quantization
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-7b-hf",
    quantization_config=bnb_config,
    device_map="auto"
)

# LoRA config
lora_config = LoraConfig(
    r=8,  # rank
    lora_alpha=32,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(model, lora_config)

๐Ÿค– OpenAI Fine-Tuning

๐Ÿ“Œ OpenAI Fine-Tuning Steps:
# 1. Prepare and validate data
!openai tools fine_tunes.prepare_data -f training_data.jsonl

# 2. Upload file
openai.File.create(
    file=open("training_data.jsonl"),
    purpose="fine-tune"
)

# 3. Create fine-tuning job
openai.FineTuningJob.create(
    training_file="file-abc123",
    model="gpt-3.5-turbo"
)

# 4. Monitor progress
openai.FineTuningJob.retrieve("ftjob-abc123")

# 5. Use fine-tuned model
response = openai.ChatCompletion.create(
    model="ft:gpt-3.5-turbo:my-org:custom-id",
    messages=[{"role": "user", "content": "Hello!"}]
)
๐Ÿ’ก OpenAI Fine-Tuning Tips:

๐Ÿ’ป Local Fine-Tuning

๐Ÿ“Œ Popular Open-Source Models for Fine-Tuning:
Fine-Tuning with Unsloth (Fastest Option):
from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/llama-3-8b-bnb-4bit",
    max_seq_length=2048,
    dtype=None,
    load_in_4bit=True,
)

model = FastLanguageModel.get_peft_model(
    model,
    r=16,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
    lora_alpha=16,
    lora_dropout=0,
    bias="none",
    use_gradient_checkpointing="unsloth",
    random_state=3407,
)

๐Ÿ“ˆ Evaluating Fine-Tuned Models

๐Ÿ“Œ Evaluation Metrics:
Evaluation Script for Fine-Tuned Models:
import json
from openai import OpenAI

def evaluate_model(base_model, fine_tuned_model, test_cases):
    """Compare base model vs fine-tuned model on test cases."""
    client = OpenAI()
    results = {"base": [], "fine_tuned": []}
    
    for test in test_cases:
        # Test base model
        base_response = client.chat.completions.create(
            model=base_model,
            messages=[{"role": "user", "content": test["input"]}]
        )
        base_answer = base_response.choices[0].message.content
        
        # Test fine-tuned model
        ft_response = client.chat.completions.create(
            model=fine_tuned_model,
            messages=[{"role": "user", "content": test["input"]}]
        )
        ft_answer = ft_response.choices[0].message.content
        
        # Compare (use LLM as judge or human review)
        comparison = llm_judge(test["expected"], base_answer, ft_answer)
        
        results["base"].append(comparison["base_score"])
        results["fine_tuned"].append(comparison["ft_score"])
    
    return {
        "base_avg": sum(results["base"]) / len(results["base"]),
        "ft_avg": sum(results["fine_tuned"]) / len(results["fine_tuned"])
    }
โš ๏ธ Common Fine-Tuning Pitfalls:
๐Ÿ’ก Cost Comparison Example:

If you have 10,000 queries/day with 1,000 input tokens and 200 output tokens:

โœ๏ธ Exercises

Exercise 3.04.1 โ€“ Data preparation

You have 100 customer support conversations. How would you convert them into fine-tuning data? What would you include in the system message?

Sample system message:
"You are a customer support agent for Acme Corp. Be polite, empathetic, and solution-oriented. If you don't know the answer, say you'll escalate. Never make promises you can't keep."

Conversion process: Extract customer message as "user" and agent response as "assistant". Clean PII (names, emails, addresses) before training.
Exercise 3.04.2 โ€“ Method selection

You have a single RTX 3090 (24GB VRAM) and want to fine-tune Llama 3 8B. Which method do you use? Why not full fine-tuning?

Answer: QLoRA (4-bit quantization + LoRA). Full fine-tuning requires ~80GB VRAM. LoRA alone needs ~40GB. QLoRA reduces to ~12-16GB, fitting in a single 3090. Quality is ~85-90% of full fine-tuning โ€“ good enough for most use cases.
Exercise 3.04.3 โ€“ ROI calculation

You spend $200/month on GPT-4 API calls. Fine-tuning GPT-3.5 costs $100 and reduces token usage by 30% (shorter prompts). Monthly API cost after fine-tuning? Payback period?

Monthly savings: GPT-3.5 is 20x cheaper than GPT-4, plus 30% token reduction โ†’ approx $200 to $10/month. Savings = $190/month.
Payback period: $100 training cost รท $190 monthly savings = 16 days!
๐Ÿ“Œ Key Takeaways
๐Ÿ“˜ Next Module: 3.05 โ€“ AI Operations (AIOps) โ†’