Troubleshooting
The errors you are most likely to hit, what actually causes them, and the fix.
Start with gradian doctor. It reports installed versions against the supported window, visible
devices, determinism settings, and which backends are available along with the reason any are not. Its
output is also the right thing to paste into an issue.
no LoRA blocks found#
The adapter is not attached, or grads.module_filter matched nothing.
Gradian attributes over adapter parameters, so with no LoRA blocks there is nothing to differentiate.
Check that --adapter points at a PEFT adapter directory, and if you set a module filter, that its
regex actually matches your module names. gradian show-index <dir> prints the block list from a built
index.
Also worth knowing: LoRA on embeddings or convolutions is skipped by the hook path, so an adapter that targets only those produces no blocks.
Out of memory during indexing#
Lower grads.batch_size first, then grads.max_seq_length. Those two are the only settings that touch
the allocation that actually fails.
The peak is the fp32 upcast of the logits, sized batch_size x supervised_tokens x vocab x 4 bytes.
Gradian gathers the supervised positions before that upcast, so prompt tokens and padding cost
nothing here. On instruction data where the prompt is most of the sequence that is a large
reduction, and it also means two runs at the same max_seq_length can differ in peak memory when
one has longer answers than the other.
With a 128k-vocab Llama-3 tokenizer at batch 8 and 512 supervised tokens that single tensor is about 2.1 GB on top of the fp16 logits it came from.
grads.storage_dtype, grads.module_filter and grads.projection_dim shrink the stored index, not
this tensor, so they will not rescue an out-of-memory error at this line. Reach for them when the index
does not fit on disk.
Measured: a 16GB V100 runs out of memory at a gradient batch of 8 on a 1B model, and runs clean at 2.
The index does not fit on disk#
That is the other problem, and it has different levers.
grads:
module_filter: down_proj # about 4.3x smaller
storage_dtype: float16 # halves it, nearly free in accuracy
projection_dim: 2048 # independent of model size, costs ranking fidelityAttribution is over the parameters you index, so a subset is a legitimate and documented choice rather than a workaround. The projection is the one with a real accuracy cost, so tune it rather than assuming a width.
module_filter can also cut by depth. It is a regex over the full short module name, which looks
like model.layers.12.mlp.down_proj, so a pattern that pins the layer number indexes only the
layers you name. At depth that is a sharper lever than module type, because the count of layers is
larger than the count of module types.
attribution.no_capability_target in the report#
You ran without --eval-dataset, so Gradian fell back to attributing against the first few training
examples. It runs, but it is not a diagnosis. Give it an eval set describing the capability, with the
answers you wanted.
The report says the delta is not significant, and refuses to attribute#
Working as intended. The capability delta's bootstrap confidence interval includes zero, so there is no established regression to explain.
Usually the eval set is too small. Widen it. If you are certain the regression is real, check that
evaluation.metric suits the capability, since an exact_match metric on free-form answers will
measure formatting rather than correctness.
Do not reach for evaluation.require_significant: false. That removes the main guard against reading
noise as a result.
The results look backwards, and the poisoned data ranks as helpful#
Almost always the query gradient. For a substitution failure, where the model now says something else, the gradient of the expected answer alone is dominated by answer format, and it inverts the sign of the diagnosis.
Check two settings. evaluation.query_signal should be contrastive or auto, and
evaluation.metric should be a text metric. Because loglik produces no generated text to subtract,
pairing it with auto silently gives you the classic formulation and the backwards answer along with
it.
The culprit is a single sentence and nothing ranks it#
Whole-example influence dilutes a wrong sentence inside a long entry by roughly 760x. Cluster
aggregation recovers it when several examples share the problem. For a single entry, use span-level
drill-down through gradian.query.spans, and build the query from the one failing item rather than an
average, which is the difference between 92% and 33% localization.
Attributing a fine-tune that unsloth produced#
Fine-tune with unsloth in your own script, then point gradian attribute at the adapter it wrote.
That adapter is a standard PEFT directory, so this is the ordinary path and there is nothing to
configure for it.
gradian train drives hf and trl only. Reach for --backend hf when you want Gradian to run
the training itself, which is what counterfactual retraining and the injection test need.
unsloth is imported in this process, so a plain transformers model cannot be built here#
Exactly what it says: something in this process ran import unsloth, which patches transformers,
peft and torch globally. Building a plain model afterwards produces one that fails later, deep in
attention, on an attention mask sized for the prompt against scores sized for the whole sequence,
so Gradian refuses at the point where the cause is still visible.
Run attribution in a fresh process that never imports unsloth. That is the supported path and your adapter needs nothing special for it.
If you are holding an unsloth model in memory and want to attribute on that object rather than load
a new one, you can skip the plain load entirely: pass the model through
gradian.model.unsloth_compat.prepare_for_gradients, wrap it in a LoadedModel, and give that to
build_index. The shim covers fused cross-entropy, gradient checkpointing, use_cache and
inference mode, and unsloth_compat_report(model) prints the state a reviewer needs. It is verified
at contract level by pytest tests/integration/test_unsloth_contract.py -q and has not been run end
to end on a GPU.
GRADIAN_ALLOW_UNSLOTH_IN_PROCESS=1 bypasses the refusal, for experiments. It does not make the
underlying problem go away.
Versions drift out of the supported window#
gradian doctor warns rather than fails. The supported window is torch 2.4 to 2.11, transformers 4.51
to 5.5, peft 0.18 and above, and trl 0.24 and below. The exact pins live in constraints/.
Installing with the script keeps you inside the window automatically.
Everything passes but the report looks wrong#
Read the Provenance section of the markdown report. Every number is reproducible from the
manifest it prints, and a changed manifest field is the explanation. gradian show-index <dir>
prints the same
manifest for an index directory.
If two runs disagree, diff their manifests before anything else. The index id hashes the checkpoint, dataset, gradient spec, seed, damping and library versions, so a differing id means one of those changed.