APX AI
Online
When adapting Large Language Models to specific domain tasks, full parameter training quickly hits physical hardware limits. Updating every weight in an 8-billion parameter model requires over 120 GB of VRAM just to store the FP16 weights, gradients, and AdamW optimizer states. This places standard fine-tuning out of reach for single consumer GPUs like the RTX 4090 24GB and requires expensive enterprise accelerators. Quantized Low-Rank Adaptation (QLoRA) systematically bypasses these limits by compressing the frozen base model into a 4-bit data format while training only a tiny fraction of attached adapter weights in 16-bit precision. This drastically shrinks the memory footprint, allowing an 8-billion parameter model to easily train on a single 24GB GPU, or a 70-billion parameter model to train on an 80GB A100.
QLoRA relies on a specific data type called 4-bit NormalFloat (NF4). Standard FP16 weights require 2.0 bytes per parameter. NF4 reduces this to 0.5 bytes per parameter. The training system freezes these 4-bit weights so they do not require gradients or optimizer states. To learn new behaviors, QLoRA injects small, trainable low-rank adapter matrices into the transformer layers. These adapters remain in BF16 or FP16 format, requiring 2.0 bytes per parameter. During the forward pass, the 4-bit base weights are dynamically dequantized back to 16-bit precision to perform matrix multiplication with the input activations, meaning the actual compute arithmetic still happens in higher precision.
To further compress memory, QLoRA uses Double Quantization, which quantizes the quantization constants themselves, saving about 0.37 bits per parameter. It also relies on Paged Optimizers, which can page memory spikes for optimizer states into system CPU RAM over PCIe, preventing Out-Of-Memory (OOM) crashes on the GPU during long context training sequences.
To size a hardware cluster for QLoRA, you must sum the memory required for the frozen base model, the trainable LoRA adapters, the optimizer states for those adapters, and the forward activations.
The base model memory in NF4 is calculated as:
The adapter memory, assuming FP16 precision, and their AdamW optimizer states (which require 8.0 bytes per trainable parameter for 32-bit momentum and variance), are calculated as:
Let us apply this to meta-llama/Meta-Llama-3.1-8B. The total parameter count is approximately 8 billion. If we target all linear modules with a LoRA rank () of 16, the trainable parameter count is roughly 20 million.
Base weights: Adapters: Optimizer:
The static memory footprint is only 4.2 GB. This leaves massive headroom on an RTX 3060 12GB or RTX 4090 24GB for the forward activations, which scale dynamically with batch size and sequence length.
For a much larger model like meta-llama/Meta-Llama-3.1-70B-Instruct, the static footprint scales proportionally.
Base weights:
Adapters (targeting all layers with , approx 150 million parameters):
Optimizer:
The static footprint for a 70B model under QLoRA is 36.5 GB. You cannot fit this on a single 24GB GPU, but it fits comfortably on a dual-GPU 48GB workstation setup or a single Mac Studio with 64GB of unified Apple Silicon memory. An enterprise 80GB H100 would handle this footprint effortlessly while supporting a massive 8k token context window.
QLoRA memory stack breakdown demonstrating how the NF4 base model dominates static memory while trainable states remain negligible.
Combining 4-bit NF4 base quantization with LoRA adapters enables fine-tuning on a single 24GB GPU:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model
model_id = "Qwen/Qwen2.5-7B"
# Define 4-bit quantization configuration
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
# Load the base model in 4-bit precision
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
device_map="auto"
)
# Enable gradient checkpointing to save activation memory
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)
# Define the LoRA adapter configuration
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# Attach adapters to the quantized base model
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()
While the static parameters are highly compressed, the forward pass activations still generate intermediate tensors in 16-bit precision. If you are fine-tuning on long documents, this activation memory will rapidly consume your remaining VRAM.
For example, fine-tuning mistralai/Mistral-7B-v0.1 on an 8k context window with a batch size of 1 can easily generate over 14 GB of activation memory. Added to the 4 GB base model footprint, this pushes you dangerously close to the 24GB limit of an RTX 3090, leaving little room for CUDA context overheads.
To fit long sequences into standard hardware, you must use gradient checkpointing. Instead of keeping all intermediate layer activations in memory during the forward pass, gradient checkpointing discards most of them and recomputes them on the fly during the backward pass. This trades compute time, resulting in roughly a 20 to 30 percent increase in training duration, for a massive reduction in VRAM. With gradient checkpointing enabled, activation memory scales at instead of , where is the number of transformer layers.
When designing your cluster, be aware of the performance tradeoffs inherent to quantized training methods. The continuous dequantization of the NF4 base weights during the forward pass introduces significant arithmetic overhead on the Tensor Cores. As a result, QLoRA training throughput, measured in tokens per second, is often slower than standard FP16 LoRA training on the exact same hardware.
Furthermore, multi-GPU scaling behaves differently with quantized weights. If you distribute a QLoRA job across two RTX 4090s or a 4-GPU A100 node using Fully Sharded Data Parallel (FSDP), you cannot easily shard the 4-bit base weights across the interconnect network. The bitsandbytes format is structurally tied to single-device memory layouts. To scale quantized training across multiple GPUs, engineers typically rely on Data Parallelism, which duplicates the 4-bit base model on every GPU and only shards the gradients and optimizer states for the small LoRA adapters. Because the adapters are so small, the communication overhead over PCIe or NVLink is minimal, making QLoRA highly efficient even over slower interconnect networks.
Wei-Ming Thor
• Founder & Engineer, ApX Machine Learning
Specializes in model architecture analysis and hardware capacity sizing for LLM infrastructure. Maintains the ApX VRAM Calculator.
© 2026 ApX Machine LearningContent Integrity & Transparency•