with one click
ml-lora-peft-techniques
Fine-tuning large models with limited GPU memory
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Fine-tuning large models with limited GPU memory
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | ml-lora-peft-techniques |
| description | Fine-tuning large models with limited GPU memory |
Scope: LoRA, QLoRA, adapter tuning, rank selection, merging, multi-adapter inference Lines: ~340 Last Updated: 2025-10-18
Activate this skill when:
What is PEFT:
PEFT Methods:
How LoRA Works:
Key Hyperparameters:
Memory Savings:
QLoRA Components:
Benefits:
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM
# Load base model
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8b",
device_map="auto",
torch_dtype="auto"
)
# Configure LoRA
lora_config = LoraConfig(
r=16, # Rank
lora_alpha=16, # Alpha (scaling)
target_modules=[
"q_proj", # Query projection
"k_proj", # Key projection
"v_proj", # Value projection
"o_proj", # Output projection
],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# Apply LoRA
model = get_peft_model(model, lora_config)
# Print trainable parameters
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 8,030,261,248 || trainable%: 0.05%
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model
import torch
# 4-bit quantization config
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4
bnb_4bit_compute_dtype=torch.bfloat16, # Compute in bf16
bnb_4bit_use_double_quant=True, # Double quantization
)
# Load quantized model
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8b",
quantization_config=bnb_config,
device_map="auto",
)
# Prepare for k-bit training
model = prepare_model_for_kbit_training(model)
# Apply LoRA
lora_config = LoraConfig(
r=64, # Higher rank for 4-bit to compensate
lora_alpha=128,
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj" # MLP layers
],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# Attention-only (memory efficient)
target_modules = ["q_proj", "v_proj"]
# All attention (good balance)
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"]
# Attention + MLP (best quality)
target_modules = [
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
]
# Auto-detect all linear layers
import re
def find_all_linear_names(model):
"""Find all linear layer names in model."""
cls = torch.nn.Linear
lora_module_names = set()
for name, module in model.named_modules():
if isinstance(module, cls):
names = name.split('.')
lora_module_names.add(names[-1])
# Remove output layer
if "lm_head" in lora_module_names:
lora_module_names.remove("lm_head")
return list(lora_module_names)
target_modules = find_all_linear_names(model)
# Small models (<3B) or simple tasks
lora_config = LoraConfig(r=8, lora_alpha=16)
# Medium models (7-13B) general tasks
lora_config = LoraConfig(r=16, lora_alpha=32)
# Large models (30-70B) or complex tasks
lora_config = LoraConfig(r=64, lora_alpha=128)
# Quality vs efficiency tradeoff
ranks = [8, 16, 32, 64]
for r in ranks:
print(f"Rank {r}:")
print(f" Trainable params: ~{r * 2 * 4096 * 32 / 1e6:.1f}M")
print(f" Storage: ~{r * 2 * 4096 * 32 * 2 / 1e6:.1f}MB")
from peft import PeftModel
# Save LoRA adapter only (~100MB for r=16)
model.save_pretrained("./lora-adapters")
# Load LoRA adapter
from transformers import AutoModelForCausalLM
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8b",
device_map="auto"
)
model = PeftModel.from_pretrained(
base_model,
"./lora-adapters"
)
# Merge adapter into base model (for deployment)
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./merged-model")
# Push to Hub (adapter only)
model.push_to_hub("username/lora-adapter")
# Push merged model
merged_model.push_to_hub("username/merged-model")
from peft import PeftModel
# Load base model once
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8b",
device_map="auto"
)
# Load first adapter
model = PeftModel.from_pretrained(
base_model,
"adapters/task1",
adapter_name="task1"
)
# Add second adapter
model.load_adapter("adapters/task2", adapter_name="task2")
# Add third adapter
model.load_adapter("adapters/task3", adapter_name="task3")
# Switch between adapters
model.set_adapter("task1")
output1 = model.generate(**inputs)
model.set_adapter("task2")
output2 = model.generate(**inputs)
# Disable all adapters (use base model)
model.disable_adapters()
output_base = model.generate(**inputs)
from peft import PeftModel
# Load base and first adapter
model = PeftModel.from_pretrained(
base_model,
"adapters/task1",
adapter_name="task1"
)
# Load additional adapters
model.load_adapter("adapters/task2", adapter_name="task2")
model.load_adapter("adapters/task3", adapter_name="task3")
# Merge adapters with weights
model.add_weighted_adapter(
adapters=["task1", "task2", "task3"],
weights=[0.5, 0.3, 0.2],
adapter_name="merged",
combination_type="linear" # or "cat" for concatenation
)
# Save merged adapter
model.save_pretrained("./merged-adapter")
from transformers import Trainer, TrainingArguments
from peft import get_peft_model, LoraConfig
# Configure LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# Apply to model
model = get_peft_model(model, lora_config)
# Training arguments
training_args = TrainingArguments(
output_dir="./lora-output",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4, # Higher LR for LoRA
num_train_epochs=3,
logging_steps=10,
save_strategy="epoch",
fp16=True,
)
# Train
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
)
trainer.train()
# Save LoRA adapter
model.save_pretrained("./lora-final")
import modal
app = modal.App("lora-finetune")
image = (
modal.Image.debian_slim()
.pip_install(
"torch",
"transformers",
"peft",
"bitsandbytes",
"accelerate"
)
)
@app.function(
gpu="l40s",
image=image,
timeout=3600
)
def train_lora(base_model: str, dataset_path: str, output_path: str):
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model
from datasets import load_dataset
import torch
# Load model
model = AutoModelForCausalLM.from_pretrained(
base_model,
device_map="auto",
torch_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(base_model)
# Configure LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# Load dataset
dataset = load_dataset("json", data_files=dataset_path)
# Train
training_args = TrainingArguments(
output_dir=output_path,
per_device_train_batch_size=4,
num_train_epochs=3,
learning_rate=2e-4,
logging_steps=10,
save_strategy="epoch",
bf16=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset["train"],
)
trainer.train()
# Save adapter
model.save_pretrained(output_path)
tokenizer.save_pretrained(output_path)
return {"status": "complete", "path": output_path}
Rank (r) | Trainable Params | Storage | Use Case
---------|------------------|----------|---------------------------
4-8 | ~1-2M | ~10MB | Simple tasks, proof-of-concept
16 | ~4M | ~50MB | General fine-tuning
32 | ~8M | ~100MB | Complex tasks
64 | ~16M | ~200MB | Very complex, QLoRA compensation
128+ | ~32M+ | ~400MB+ | Experimental, diminishing returns
Configuration | Modules | Quality | Memory
------------------------|----------------------------------|---------|--------
Minimal | q_proj, v_proj | Good | Low
Recommended | q_proj, k_proj, v_proj, o_proj | Better | Medium
Full Attention + MLP | All attention + gate/up/down | Best | High
# Common patterns
lora_alpha = r # 1:1 ratio (standard)
lora_alpha = r * 2 # 2:1 ratio (stronger updates)
lora_alpha = 32 # Fixed at 32 (some practitioners prefer)
# Alpha / r ratio controls update magnitude
# Higher ratio = stronger LoRA influence
Method | Memory | Quality | Speed | Use Case
------------|---------|---------|-------|----------------------------
LoRA | Low | High | Fast | General fine-tuning
QLoRA | Very Low| High | Fast | Large models, limited VRAM
Prefix | Low | Medium | Fast | Prompt engineering
Adapter | Medium | High | Medium| Task-specific modules
Full FT | High | Highest | Slow | Maximum quality, unlimited compute
❌ Rank too low: Poor adaptation to task ✅ Start with r=16, increase if quality insufficient
❌ Rank too high: Overfitting, wasted compute ✅ Rarely need r>64 even for complex tasks
❌ Wrong alpha/r ratio: Unstable training
# ❌ Bad: Alpha much larger than rank
lora_config = LoraConfig(r=8, lora_alpha=128)
# ✅ Good: Alpha = r or 2*r
lora_config = LoraConfig(r=16, lora_alpha=32)
❌ Training all layers: Negates memory benefits ✅ Target only attention or attention+MLP
❌ Not using gradient checkpointing: OOM on large models
# ✅ Enable gradient checkpointing
model.gradient_checkpointing_enable()
❌ Merging without testing adapters separately: Can't debug ✅ Test individual adapters before merging
❌ Using LoRA for small models with plenty VRAM: Unnecessary ✅ Use full fine-tuning for models <3B if VRAM allows
❌ Not saving adapter separately: Lose flexibility ✅ Save adapter before merging for reusability
❌ Ignoring base model precision: Quality/memory mismatch
# ❌ Bad: 4-bit model but fp32 LoRA
# ✅ Good: Match compute dtype
bnb_config = BitsAndBytesConfig(
bnb_4bit_compute_dtype=torch.bfloat16
)
unsloth-finetuning.md - Fast LoRA training with Unslothmodal-gpu-workloads.md - GPU selection for PEFT trainingllm-dataset-preparation.md - Preparing data for fine-tuninghuggingface-autotrain.md - Automated LoRA trainingmodel-merging.md - Advanced adapter merging techniquesquantization-techniques.md - Model quantization deep diveLast Updated: 2025-10-18 Format Version: 1.0 (Atomic)
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
Comprehensive guide to GNU Debugger (GDB) for debugging C/C++/Rust programs. Covers breakpoints, stack traces, variable inspection, TUI mode, .gdbinit customization, Python scripting, remote debugging, and core file analysis.
Paxos consensus algorithm including Basic Paxos, Multi-Paxos, roles, phases, and practical implementations
Gossip protocols for disseminating information, failure detection, and eventual consistency in large-scale distributed systems