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

Sponsored

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:

  1. Organize your project — Have your adapter checkpoint and training logs handy.
  2. Load the adapter weights — Use Hugging Face’s PEFT library or directly with torch.load and safetensors.torch.load_file.
  3. Compute summary statistics — For each weight tensor, get min, max, mean, std, and NaN/Inf counts.
  4. Visualize distributions — Plot histograms (via matplotlib or seaborn) and, if the model is large, subsample.
  5. Interpret the results — Compare against known healthy ranges or your own training baseline.
  6. 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_A layers — You may have loaded the base model instead of the adapter. Ensure adapter_config.json is 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 torch but saved with safetensors, load accordingly. Use safetensors.torch.load_file for .safetensors files.
  • 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.load on .safetensors files, which causes errors — use safetensors.torch instead.

Variations

  1. Use get_peft_model_state_dict from PEFT to access the adapter tensors in a standardized way.
  2. Leverage torchinfo or summary for a quick model architecture peek, though it shows shapes not values.
  3. Integrate weight-stat logging into your training loop’s TrainerCallback to 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).

Sponsored

Sponsored