← TinyGPT · docs · devlog · roadmap · speedup
source: docs/prds/qlora-large-model-finetune.md · view on GitHub ↗

QLoRA on Mac — fine-tuning larger open models on 48 GB

Status: PRD (2026-06-10). Phase-3 centerpiece of the roadmap (specialist close-out → VLM/Pace → this). Implements the 2026-06-10 strategic shift: focus moves from training small models to fine-tuning/distilling larger open models. See tinygpt-product-thesis.md for the positioning this serves.

Problem

tinygpt’s trainer applies LoRA against an fp16-materialized base. Fine at 0.6B; impossible at the sizes that now matter:

Basefp16 weights4-bit weightsLoRA-trainable on 48 GB today?
Qwen3-0.6B1.4 GB0.4 GByes (current path)
Qwen3-4B~8 GB~2.3 GBmarginal fp16, easy at 4-bit
Qwen3-8B~16 GB~4.5 GBno fp16 (grads+activations blow past 48 GB), yes at 4-bit
Qwen3-14B~28 GB~8 GBimpossible fp16, feasible at 4-bit
Qwen3-30B-A3B~60 GB~18 GBteacher-only, never a student

QLoRA = frozen quantized base + fp16/fp32 LoRA A/B (and DoRA m) as the only trainable params. Optimizer state covers only the adapter (tens of MB at r=32), activations stay fp16. This is the single unlock that makes 8–14B adaptation fit the M5 Pro.

What exists already (leverage inventory)

Design

Q-LoRA layer

New QLoraLinear (and QDoraLinear) in Lora.swift:

Injection order (the invariant that bit us in serve)

quantize FIRST, inject SECOND. LoraInjectionHF.makeAdapterLinear gains a QuantizedLinear branch; the existing mutual-exclusion warning in serve/sample flips to the supported path once this lands.

Loading

Two entry points, same model afterwards:

  1. fp16 HF dir + --quantize-base 4 → quantize in-memory, then inject (works today-ish; peak RAM = fp16 size during load).
  2. Pre-quantized MLX safetensors (the #305 native packed load) → no fp16 materialization at all; peak RAM = packed size. Required for 14B.

Milestones (each gated, no silent regressions)

Decision rules (carried from the specialist era)

  1. Zero-shot base measured on the same gate before any training — the rule that would have saved v1–v10.
  2. Formula = (speed × accuracy) / cost decides size: a 4B at int4 serves ~50–80 tok/s vs the 0.6B’s 212 — accuracy must pay for that gap (accuracy > speed per the Pace decision framework, but measure, don’t assume).
  3. Every step has an automated gate (numerics or eval) or it doesn’t ship.
  4. Distillation signal: teacher = qwen3-30b-a3b (no thinking) or qwen3-14b (thinking) via LM Studio; data-level distillation first (v11 amplifier pattern), logit distillation only if data-level stalls.

Risks

Done when