BXUP Track progress & test yourself — free
Data Science & ML/AI · Playbook · Advanced · ~5 min read
DATA SCIENCE & ML/AI · PLAYBOOK · ADVANCED

LoRA and QLoRA Fine-Tuning on a Free 16GB GPU

LoRA and QLoRA are named verbatim in Indian DS/ML job postings for a reason: they are what makes fine-tuning feasible without a GPU cluster.

Tier: Advanced · Workflow area: Fine-tuning & Model Adaptation

This playbook runs a complete parameter-efficient fine-tune on a free Kaggle T4/P100 (16GB) — LoRA in fp16 for 1–3B models, QLoRA 4-bit for 7–8B — with configs that actually fit, a before/after eval, and the fidelity trade-off measured rather than assumed.

When to use

  • pb-27 concluded fine-tune, and pb-28 gave you a curated, decontaminated SFT dataset.
  • You need domain format/style/procedure behavior from a small open model you can self-host cheaply.
  • You want hands-on LoRA/QLoRA evidence for interviews where "have you actually fine-tuned a model?" is the filter question.

Skip if a system prompt plus few-shot already hits your eval target — an adapter you did not need is pure maintenance debt.

Prerequisites

  • Kaggle account (verified, ~30 GPU-h/week on 16GB T4/P100) or Colab free; Kaggle is the predictable choice.
  • Your pb-28 dataset (~1k pairs, TRL messages format) uploaded as a private Kaggle dataset.
  • transformers, peft, trl, bitsandbytes, accelerate (preinstalled or pip-installable in the notebook).
  • Your eval set and grading script, kept strictly out of training data (pb-28 step 5).

The workflow

  1. Pick the model to fit the memory math. fp16 weights cost ~2 bytes/param: a 3B base ≈ 6GB, leaving room for LoRA fp16 training on 16GB. A 7–8B base ≈ 14–16GB in fp16 — weights alone exhaust the card, so 7–8B requires QLoRA (4-bit base ≈ 4–5GB). Start with 3B LoRA for the mechanics, then repeat at 7–8B QLoRA.

  2. Baseline before training. Run the untuned base model on your full eval set and store per-example scores. No baseline, no claim — "after" numbers without "before" numbers are decoration.

  3. Configure QLoRA quantization (7–8B path).

    from transformers import BitsAndBytesConfig
    import torch
    
    bnb = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_use_double_quant=True,
        bnb_4bit_compute_dtype=torch.float16,  # T4/P100 have no bf16 — fp16, not bfloat16
    )
    
  4. Configure the LoRA adapter.

    from peft import LoraConfig
    
    lora = LoraConfig(
        r=16, lora_alpha=32, lora_dropout=0.05,
        target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                        "gate_proj", "up_proj", "down_proj"],
        task_type="CAUSAL_LM",
    )
    

    r=16/alpha=32 is the sane default; move r to 8 if memory is tight or 32 if the task is far from the base distribution. Targeting all linear projections (not just q/v) is the 2026 default and measurably helps.

  5. Train with 16GB-safe settings. SFTTrainer with: per_device_train_batch_size=2, gradient_accumulation_steps=8 (effective batch 16), max_seq_length=1024 (2048 only on 3B), learning_rate=2e-4, cosine schedule with warmup_ratio=0.03, num_train_epochs=2, fp16=True, gradient_checkpointing=True, optim="paged_adamw_8bit". On Kaggle, save adapter checkpoints every epoch to /kaggle/working — sessions die at 9 hours.

  6. Watch the loss like a statistician. Training loss should fall smoothly and eval loss should track it; eval loss climbing while train loss falls = overfitting your 1k pairs — stop at the earlier checkpoint. Loss spiking to NaN on fp16 usually means learning rate too high; halve it.

  7. Run the before/after eval. Same eval set, same grader, base vs tuned. Report per-slice deltas, not just the average. For the QLoRA path, also compare the 4-bit base (untuned) against the fp16 base on 20 cases to isolate the quantization cost from the tuning gain — this is the fidelity trade-off number most people never measure.

  8. Package the artifact. Save only the adapter (model.save_pretrained — tens of MB, not GB), with a model card: base model + revision, dataset version, full config, before/after table, and known regressions from pb-33.

Pro Tip

Unsloth wraps this exact stack with ~2x speed and lower VRAM on the same free GPUs — same LoRA concepts, one-line model loading; use it when Kaggle hours are the binding constraint.

Tool variants

  • HF PEFT + TRL + bitsandbytes: the canonical stack above; maximum transferability to job descriptions.
  • Unsloth: fastest path on free GPUs; ideal for iteration loops once the canonical stack is understood.
  • Axolotl or provider fine-tuning APIs: YAML-driven or fully managed alternatives; managed APIs remove GPU work but lock the adapter to the provider.

Verification

  • Peak VRAM (torch.cuda.max_memory_allocated()) stayed under 15GB — headroom matters for reproducibility on busier sessions.
  • Eval-loss curve shows no divergence from train loss; the shipped checkpoint is the best-eval one, not the last one.
  • Before/after table on the untouched eval set shows the target-metric lift, per slice.
  • Quantization-fidelity spot check (fp16 vs nf4 base) is recorded for the QLoRA path.
  • The adapter reloads cleanly in a fresh session on top of the exact base revision and reproduces the eval numbers.

Pitfalls

  • bf16=True on a T4/P100. These cards do not support bfloat16 — training silently falls back or crashes; use fp16=True with the paged optimizer to absorb instability.
  • Chat-template mismatch between training and inference. Formatting drift between how pairs were templated (pb-28) and how you prompt the tuned model produces mysterious quality loss; use the identical template both ways.
  • Judging the fine-tune on training-style examples. Held-out means held out — pb-28's decontamination exists precisely so this number means something.
  • Three-plus epochs on a 1k-pair set. Small sets overfit fast; 1–2 epochs with early stopping is the norm.
  • Losing the run to session death. Kaggle 9-hour limits are real; checkpoint every epoch and design runs to finish in ≤ 4 hours.

Restricted-environment variant

For client data or offline mandates:

  • Kaggle/Colab are public clouds — if the dataset cannot leave the client boundary, run the identical stack on an approved GPU (a T4/A10 spot instance in ap-south-1 runs ~$0.10–0.40/hr) or on-prem hardware.
  • Pre-download base weights inside the boundary (HF offline mode, HF_HUB_OFFLINE=1) so training needs no egress.
  • The adapter file itself encodes training data patterns — store and share it under the same classification as the dataset.
Warning

Uploading a client-derived dataset to a free notebook platform is a data transfer to a third party — get that decision made by the data owner, not by the person holding the GPU quota.

Success criteria

  • A complete LoRA (3B) and QLoRA (7–8B) run finished within free-tier GPU hours, with configs committed.
  • Measured lift on the held-out eval set over the true base-model baseline, reported per slice.
  • The quantization trade-off is a number in your report, not a vibe.
  • Anyone on the team can reproduce the run from the model card, dataset version, and seed.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in