Inspect Adapter Weights
Learn to inspect adapter weights for quality issues in LLM finetuning with LoRA/QLoRA. Step-by-step walkthrough, troubleshooting, and next steps.
Focus: inspect adapter weights for quality issues
You’ve trained your LoRA or QLoRA adapter, validation loss looks decent, and the model generates plausible outputs. But are your adapter weights actually healthy? Fine-tuned adapters can silently degrade — weights may beNaN, explode, collapse to near-zero, or overfit to a narrow distribution — and surface-level metrics won’t catch it until your model starts producing nonsense in production. This lesson shows you how to inspect adapter weights for quality issues using practical Python scripts, so you can catch problems early and ship a reliable model.
The problem this lesson solves
Training logs give you loss curves, but they rarely tell you the full story. A loss curve can look perfect while your adapter’s weight distribution collapses or diverges. Consider what happens when you’re fine-tuning a 7B parameter model with LoRA: the base model stays frozen, and only the low-rank A and B matrices update. If those matrices develop issues — like NaN values, extreme outliers, or uniform degradation — your outputs will be subtly wrong, often only detectable when you sample generations.
Quality issues in adapter weights manifest as:
- NaN or Inf values — Training may ‘complete’ if the loss is masked, but inference will crash or produce garbage.
- Exploding weights — A few weights dominate the adapter’s behavior, leading to overconfident or erratic outputs.
- Collapsed weights — All weights shrink toward zero, effectively making the adapter output a no-op, so the model ignores your fine-tuning.
- Distribution skew — Weights cluster in a narrow range, indicating the adapter hasn’t learned meaningful features.
Without inspection, these issues go straight to production. Inspection is your early warning system.
Core concept / mental model
Think of adapter weights as the sculptor’s chisel on the frozen base model. The base model is the raw block of marble; the adapter’s low-rank matrices are the fine carving tools. If the chisel is damaged (NaN or explosive), the sculpture becomes malformed. If it’s too dull (collapsed weights), you’re just polishing a stone that looks original but never improves.
The key idea is statistical health monitoring. Instead of looking at individual weights (which is overwhelming), you look at distributions and summary statistics. It’s like checking vital signs: temperature, blood pressure, heart rate. For adapters, the vitals are:
- Mean and standard deviation — Detect shifts from expected ranges.
- Percentiles and extremes — Spot outliers that could cause instability.
- NaN/Inf count — The easiest red flag.
- Histogram shape — Tell you if the distribution is healthy or skewed.
In practice, a healthy adapter trained with LoRA often has weights in a symmetric distribution around zero, with standard deviation around 0.01–0.1 (depending on rank and alpha). If you see a standard deviation over 1.0 or under 1e-5, something is wrong.
How it works step by step
Start by loading your trained adapter (usually a directory with adapter_model.bin or adapter_model.safetensors). Here’s the mental sequence:
- Organize your project — Have your adapter checkpoint and training logs handy.
- Load the adapter weights — Use Hugging Face’s PEFT library or directly with
torch.loadandsafetensors.torch.load_file. - Compute summary statistics — For each weight tensor, get
min,max,mean,std, and NaN/Inf counts. - Visualize distributions — Plot histograms (via
matplotliborseaborn) and, if the model is large, subsample. - Interpret the results — Compare against known healthy ranges or your own training baseline.
- Decide action — If anomalies appear, consider retraining with adjusted hyperparameters or resetting the adapter.
The anatomy of an adapter checkpoint
PEFT adapters store only the low-rank matrices plus metadata. In a LoRA adapter, you’ll see layers named like base_model.model.model.layers.0.self_attn.q_proj.lora_A.weight. The lora_A and lora_B tensors are your inspection targets.
Reading the signs like a doctor
| Symptom | Possible cause | Action |
|---|---|---|
| NaN/Inf | Learning rate too high, data issues, precision mismatch | Lower LR, reset adapter, debug data |
| Large std (e.g., >1) | Exploding gradients | Gradient clipping, lower LR |
| Tiny std (e.g., <1e-6) | Dead adapter — no learning | Increase LR, check from-scratch vs pretrained |
| Skewed mean (e.g., 1.0) | Bias toward one class | Rebalance data, adjust lambda |
Hands-on walkthrough
Let’s write Python code to inspect a real adapter. We’ll use a sample adapter path; replace with your own.
import torch
from safetensors.torch import load_file
# Load adapter weights (adjust path)
path = "./my_adapter/adapter_model.safetensors"
weights = load_file(path)
# Print available layers
print("Layers found:", len(weights))
print(list(weights.keys())[:5])
Expected output (list may vary):
Layers found: 24
['base_model.model.model.layers.0.self_attn.q_proj.lora_A.weight', ...]
Now compute summary statistics per tensor:
import torch
def inspect_weights(weights):
"""Print per-tensor stats."""
for name, tensor in weights.items():
t = tensor.float()
n_nan = torch.isnan(t).sum().item()
n_inf = torch.isinf(t).sum().item()
print(f"{name}: mean={t.mean():.4f}, std={t.std():.4f}, min={t.min():.4f}, max={t.max():.4f}, nan={n_nan}, inf={n_inf}")
inspect_weights(weights)
Watch for lines with nan or inf > 0. That’s your first red flag.
Visualizing the distribution
A histogram is worth a thousand numbers:
import matplotlib.pyplot as plt
import torch
# Flatten all weights into one vector
all_weights = torch.cat([t.flatten() for t in weights.values()]).float()
plt.hist(all_weights.numpy(), bins=100, alpha=0.7)
plt.title("All adapter weights distribution")
plt.xlabel("Weight value")
plt.ylabel("Frequency")
plt.grid(True)
plt.show()
A healthy LoRA adapter often shows a bell-shaped curve around zero. If you see spikes at very large values, that’s a warning.
Automating the check
Build a reusable function that returns a health score:
def adapter_health_check(weights, std_range=(0.001, 1.0)):
"""Return a list of anomalies."""
issues = []
for name, tensor in weights.items():
t = tensor.float()
if torch.isnan(t).any() or torch.isinf(t).any():
issues.append(f"{name}: contains NaN/Inf")
if t.std() > std_range[1] or t.std() < std_range[0]:
issues.append(f"{name}: std={t.std():.4f} outside healthy range")
return issues
issues = adapter_health_check(weights)
if issues:
print("Issues found:")
for i in issues:
print(" -", i)
else:
print("All good — adapter weights look healthy.")
Compare options / when to choose what
There are several ways to inspect adapter weights, each with its trade-offs:
| Method | Pros | Cons | Best when |
|---|---|---|---|
| Manual summary stats (as above) | Fast, no extra deps | Needs eyeballing | Quick sanity check |
| Visualization (histograms) | Intuitive, catches skew | Subjective | Exploratory analysis |
| Automated threshold checks | Reproducible, CI-friendly | Need to set thresholds | Production pipelines |
Using peft built-in tools (e.g., get_peft_model_state_dict) |
Seamless with transformers | Less control | Already in HF ecosystem |
| Correlation with eval loss | Direct impact measurement | High cost | Post-training validation |
Our recommendation: Use manual summary stats for iterative debugging, and automate threshold checks in your training pipeline to catch regressions early.
Troubleshooting & edge cases
- My adapter has no
lora_Alayers — You may have loaded the base model instead of the adapter. Ensureadapter_config.jsonis present and you’re reading the right file. - All weights are zero — This happens if you loaded a model that was never trained (e.g., wrong checkpoint path). Verify the training run actually updated the adapter.
- Weights show NaN but training loss was fine — Sometimes the loss is computed after masking so NaN weights never cause a crash. Check the raw tensors during training callback.
- PyTorch vs safetensors mismatch — If you trained with
torchbut saved withsafetensors, load accordingly. Usesafetensors.torch.load_filefor.safetensorsfiles. - Large model memory issues — When loading a 70B adapter, load only the tensors you need:
load_file(path, device="cpu")then compute stats per tensor and free memory.
For serious cases, write a training-time hook to log weight stats every N steps and catch problems before they corrupt the checkpoint.
What you learned & what's next
You now know how to inspect adapter weights for quality issues by computing summary statistics, visualizing distributions, and building automated health checks. You can identify NaN/Inf, exploding or collapsed weights, and distribution skew — the most common quality traps in LoRA/QLoRA fine-tuning.
In the next lesson, you’ll learn how to quantize your adapter for efficient inference — using the healthy adapter you’ve verified to fit into production memory constraints. Keep your adapter health checks handy; they’ll save you from debugging downstream chaos.
Pro tip: Run this inspection script right after every training run, before you even look at generation samples. It takes 10 seconds and saves you hours of pain.
Practice recap
Now, run the adapter_health_check script on your most recent fine-tuned adapter. Note any flagged layers and try adjusting the learning rate or gradient clipping. Then retrain a small model and compare the weight stats — see how your changes affect the distribution.
Common mistakes
- Only relying on loss curves — a low loss can mask NaN or exploding weights symptoms.
- Inspecting the base model weights instead of the adapter (make sure you load the PEFT checkpoint).
- Checking only one tensor and concluding the whole adapter is healthy — always aggregate over all layers.
- Using
torch.loadon.safetensorsfiles, which causes errors — usesafetensors.torchinstead.
Variations
- Use
get_peft_model_state_dictfrom PEFT to access the adapter tensors in a standardized way. - Leverage
torchinfoorsummaryfor a quick model architecture peek, though it shows shapes not values. - Integrate weight-stat logging into your training loop’s
TrainerCallbackto catch anomalies in real time.
Real-world use cases
- A chatbot startup checks adapter weight distributions after each fine-tuning run to ensure the model doesn't suddenly produce gibberish in production.
- An ML engineer debugging a fine-tuned model's hallucinations uses weight statistics to confirm the adapter didn't explode or collapse during training.
- A team automating the fine-tuning pipeline runs a weight-health check in CI to reject any checkpoint that fails NaN or std thresholds before deployment.
Key takeaways
- Adapter weights can harbor quality issues (NaN, exploding, collapsed) invisible in loss curves.
- Inspect summary statistics (mean, std, min, max) and NaN/Inf counts for every layer.
- Visualize weight distributions with histograms to spot skewness or anomalies.
- Automate threshold checks to make inspection reproducible and part of your pipeline.
- Always load the adapter (not the base model) and use the correct file format (safetensors vs torch).