This project guides you through training a LoRA fine-tuned model from start to finish: dataset creation, QLoRA training on a cloud GPU, wandb loss monitoring, and checkpoint evaluation.
A fine-tuned Llama 3.2 3B model specialized for structured data extraction: given a product description, output a clean JSON with name, price, and features. You will train on 500 synthetic examples and evaluate against a 50-example test set.
# Recommended GPU options (in cost order, cheapest first): # - Google Colab Pro (T4/A100, $10/mo) — easiest for first run # - Lambda Labs (H100, $2.49/hr) — good performance/cost # - RunPod (A100 80GB, $1.89/hr) — cheapest A100 # - Modal (A100, ~$2/hr, serverless) — no setup # Install on your cloud GPU instance: pip install transformers peft datasets trl bitsandbytes accelerate pip install wandb # Loss tracking pip install huggingface_hub # Push model to Hub huggingface-cli login # Authenticate with your HF token wandb login # Authenticate with wandb
Generate 500 (product_description, json_output) pairs using claude-haiku-4-5-20251001
Use the synthetic data generation pattern from Lesson 4. Save to data/train.json
Generate 50 held-out test examples
Keep test set completely separate — save to data/test.json. Never use for training.
Run deduplication and quality filters
MinHash dedup + length filter. Log how many examples were removed.
Sample 10 training examples and verify them manually
Spot-check: are the outputs valid JSON? Are they accurate?
import wandb
wandb.init(project="product-extraction-ft", name="llama-3b-qlora-v1")
# Use the QLoRA script from Lesson 3 with these settings:
lora_config = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj","v_proj"])
training_args = TrainingArguments(
output_dir="./checkpoints",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # Effective batch = 16
learning_rate=2e-4,
warmup_ratio=0.05,
save_strategy="steps",
save_steps=50,
evaluation_strategy="steps",
eval_steps=50,
load_best_model_at_end=True,
metric_for_best_model="eval_loss",
report_to="wandb", # ← Log to wandb automatically
)
# Expected training time: ~15–25 min on A100, ~45 min on T4Run training and watch wandb loss curves
Train loss should decrease steadily. Eval loss should track train loss. Divergence = overfitting.
Identify the best checkpoint (lowest eval_loss)
load_best_model_at_end=True handles this automatically
Merge LoRA adapters into base model
merged = peft_model.merge_and_unload() — required for efficient inference
import json
from transformers import pipeline
# Load your fine-tuned model
pipe = pipeline("text-generation", model="./merged-model", device_map="auto")
# Load test set
test_cases = json.loads(open("data/test.json").read())
correct, total = 0, 0
json_parse_errors = 0
for case in test_cases:
prompt = f"### Instruction:\n{case['instruction']}\n\n### Input:\n{case['input']}\n\n### Response:\n"
output = pipe(prompt, max_new_tokens=200, do_sample=False)[0]["generated_text"]
response = output[len(prompt):] # Strip prompt from output
try:
predicted = json.loads(response.strip())
expected = json.loads(case["output"])
# Check if required fields match
if predicted.get("name") == expected.get("name") and predicted.get("price") == expected.get("price"):
correct += 1
total += 1
except json.JSONDecodeError:
json_parse_errors += 1
total += 1
print(f"Field accuracy: {correct}/{total} = {correct/total:.1%}")
print(f"JSON parse errors: {json_parse_errors}/{total} = {json_parse_errors/total:.1%}")
# Target: >90% field accuracy, <5% parse errorsPush merged model to your HF Hub repo
merged.push_to_hub("your-username/llama-3b-product-extractor")
Write a model card (README.md) on the Hub
Document: base model, training data size, eval metrics, intended use, limitations
Test the model via HF Inference API
Hit the endpoint with 5 real product descriptions not in your training set
In the next lesson, we evaluate what the fine-tuned model can and cannot do — and learn to read training curves to diagnose overfitting and underfitting.