This document is relevant for: Trn2, Trn3
Accuracy debugger tools#
Task overview#
This topic discusses how to run the accuracy debugger pipeline using vLLM Neuron. The accuracy debugger automates the process of identifying where model outputs diverge from a HuggingFace reference by running task evaluation, prompt-level logit validation, KV cache comparison, and tensor comparison.
Prerequisites#
Working vLLM Neuron environment: See the setup guide.
Model checkpoint: Access to the model weights on local disk or HuggingFace Hub.
Sufficient memory: The debugger loads HF models (FP32 + BF16) on CPU alongside the Neuron model. Ensure enough system RAM for your model size.
Python dependencies:
torch,transformers,vllm,plotly(for visualization).
Instructions#
1. Pipeline overview#
The debugger runs in stages, each usable on its own:
Task analysis — run a dataset eval against a running vLLM server, then score it against thresholds and extract the prompts the model got wrong (Section 2).
Prompt analysis — for those deviated prompts, run logit validation, KV cache comparison, and tensor compare (Section 3).
Report — combine the artifacts into an HTML report (Section 4).
The debugger only analyzes results — you run the eval and manage the vLLM server, then pass the results in. This keeps it decoupled from any particular eval runner or server harness.
Runnable examples. End-to-end scripts that wire the whole pipeline together (launch a server, run each stage, generate the report) live under
examples/vllm_neuron/accuracy/— seerun_accuracy_debugger_llama.pyandrun_accuracy_debugger_gpt_oss.py. The sections below show how to drive the same stages directly.
2. Run task analysis programmatically#
run_task_analysis() does not run the eval itself — it analyzes
pre-computed eval results. Run your eval harness (for example, the
lm_eval runners shipped in vllm_neuron.accuracy.lm_eval) against a
running server first, then pass the results in via input_task_results:
from vllm_neuron.accuracy.lm_eval import run_accuracy_gsm8k_cot
from vllm_neuron.accuracy.accuracy_debugger import run_task_analysis
from vllm_neuron.accuracy.accuracy_debugger.task_plugins.lm_eval_analyzer import LmEvalAnalyzer
# Run the eval yourself against a server you started
# (for example, `vllm serve /path/to/model --tensor-parallel-size 8`):
_scores, results_dir = run_accuracy_gsm8k_cot(
base_url="http://localhost:8000",
model="/path/to/model",
results_dir="./eval_out",
limit=200, # cap for a quick run; omit for the full dataset
)
result = run_task_analysis(
LmEvalAnalyzer(),
input_task_results=results_dir,
thresholds={"exact_match,flexible-extract": 0.435},
output_dir="./accuracy_report",
)
print(f"Passed: {result.passed}")
print(f"Scores: {result.scores}")
print(f"Deviated prompts: {len(result.deviated_prompts)}")
Parameters:
analyzer– An analyzer instance with ananalyze_all_resultsmethod (for example,LmEvalAnalyzer())input_task_results– Pre-computed eval results: either a path to an existing results directory (for example, an lm_eval--output_path) or a results dict. The analyzer interprets it (the API stays eval-runner agnostic)thresholds– Dict mapping metric names to minimum acceptable scores (all lower-bound>=checks)output_dir– Directory where reports and artifacts are saved
Keeping the eval outside the debugger means it stays decoupled from any
particular eval runner — it only analyzes the results and judges
pass/fail against thresholds.
3. Run prompt analysis with logit validation and KV cache#
Use run_prompt_analysis() to perform per-prompt logit validation
and KV cache comparison:
from vllm_neuron.accuracy.accuracy_debugger import run_prompt_analysis
from vllm_neuron.accuracy.accuracy_debugger.prompt_plugins import (
LogitValPlugin, KvCachePlugin,
)
cfg = {
"server": {
"model": "/path/to/model/checkpoint",
"tp_degree": 8,
"max_model_len": 256,
}
}
result = run_prompt_analysis(
server_config=cfg,
prompts=task_result.deviated_prompts[:3],
plugin_steps=[LogitValPlugin(), KvCachePlugin()],
output_dir="./accuracy_report",
)
for plugin_name, plugin_result in result.plugin_results.items():
passed = all(v.get("passed") for v in plugin_result.values() if isinstance(v, dict))
print(f"{plugin_name}: {'PASS' if passed else 'FAIL'}")
Note
Set max_model_len in server_config to control compiled model
length. Smaller values result in faster compilation but limit the
maximum sequence length that can be analyzed.
4. Configure tensor capture#
To capture intermediate tensors from the Neuron model, configure
tensor_capture in additional_config:
from vllm import LLM
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
additional_config={
"neuron_config": {
"tensor_capture": {
"modules": [
"model.layers.0-31",
"lm_head",
],
"capture_dir": "/tmp/captures",
}
}
},
)
Module pattern syntax:
Range patterns:
model.layers.0-31expands to match layers 0 through 31Regex patterns:
model\.layers\.\d+\.self_attnmatches all self-attention modulesExact names:
lm_headmatches the language model head
Output location: Captures are saved to the specified
capture_dir organized by data-parallel rank, phase (prefill or
decode), and module name:
/tmp/captures/
+-- dp0/
+-- prefill_s128_0/
| +-- model.layers.0/
| | +-- rank0.pt
| +-- prefill_s128_0_meta.json
+-- decode_b1_0/
+-- model.layers.0/
| +-- rank0.pt
+-- decode_b1_0_meta.json
5. Run tensor comparison#
Use the TensorComparePlugin for three-way intermediate tensor
comparison. This must run in a separate run_prompt_analysis()
call from LogitVal/KvCache plugins because it manages its own Neuron
LLM:
from vllm_neuron.accuracy.accuracy_debugger import run_prompt_analysis
from vllm_neuron.accuracy.accuracy_debugger.prompt_plugins import (
TensorComparePlugin,
)
cfg = {
"server": {
"model": "/path/to/model/checkpoint",
"tp_degree": 8,
"max_model_len": 256,
}
}
tc_result = run_prompt_analysis(
server_config=cfg,
prompts=task_result.deviated_prompts[:3],
plugin_steps=[TensorComparePlugin(
modules=["model.embed_tokens", "model.layers.0-31", "lm_head"],
tp_size=8,
)],
output_dir="./accuracy_report",
output_length=3,
)
tc_plugin_res = tc_result.plugin_results["tensor_compare"]
passed = all(v.get("passed") for v in tc_plugin_res.values() if isinstance(v, dict))
print(f"Tensor compare: {'PASS' if passed else 'FAIL'}")
Configurable threshold: By default, the plugin fails if any module has L2 ratio >= 3.0. Adjust with:
TensorComparePlugin(
modules=["model.embed_tokens", "model.layers.0-31", "lm_head"],
tp_size=8,
max_l2_ratio=5.0,
)
Standalone tensor comparison (without the debugger pipeline):
from vllm_neuron.accuracy.tensor_io import read as tensor_io_read
from vllm_neuron.accuracy import (
align_decode_captures,
compare_captures_three_way,
print_three_way_report,
)
from vllm_neuron.accuracy.tensor_alignment_utils import (
align_and_truncate_hidden,
hf_reference_reconstruction,
)
# Read captures from disk
fp32 = tensor_io_read("/tmp/captures/hf_fp32")
bf16 = tensor_io_read("/tmp/captures/hf_bf16")
neuron = tensor_io_read("/tmp/captures/neuron")
# Align decode steps by position
fp32 = align_decode_captures(fp32, neuron)
bf16 = align_decode_captures(bf16, neuron)
# Three-way comparison
results = compare_captures_three_way(
fp32, bf16, neuron,
reference_reconstruction_fn=hf_reference_reconstruction,
target_reconstruction_fn=my_reconstruct,
alignment_fn=align_and_truncate_hidden,
)
print_three_way_report(results)
6. Generate the HTML report#
After running task and/or prompt analysis, generate the combined interactive report:
from vllm_neuron.accuracy.accuracy_debugger import generate_report
report_path = generate_report("./accuracy_report")
print(f"Report: {report_path}")
# Output: ./accuracy_report/combined_report.html
Open the report in your browser:
open ./accuracy_report/combined_report.html
7. Interpret the HTML report#
The report contains three tabs:
Overview Tab:
Status banner: green (all passed), yellow (minor deviations), red (failures)
Summary cards for each analyzed prompt with pass/fail status
Triage flowchart for navigating failures
Task Analysis Tab:
Metrics Summary table: score vs threshold with pass/fail indicators
Deviating samples (expanded): question, expected answer, actual response
Matching samples (collapsed): correctly answered samples
Reproduce section: copy-paste commands to re-run the analysis
Prompt Analysis Tab:
Sidebar listing analyzed prompts with pass/fail dots
Logit Validation: per-token L-inf/L2 error, BC, divergent token markers
KV Cache: per-layer heatmaps with L-inf error and BC per attention head
How to read logit validation results:
Indicator |
Meaning |
Action |
|---|---|---|
Three-way ratio approximately 1.0x |
Dtype-inherent BF16 noise, not a bug |
No action needed |
Three-way ratio >> 1.0x |
vllm-neuron-specific excess error |
Investigate with tensor compare |
BC >= 0.99 |
Error distributions nearly identical |
Model is accurate |
Token 0 fails |
Prefill bug |
Check tensor compare for first divergent module |
Tokens 1+ fail, token 0 passes |
Decode or KV cache bug |
Check KV cache heatmaps |
Pass/fail thresholds (three-way):
Check |
Threshold |
|---|---|
sigma-ratio |
<= 1.0 |
Aggregate BC |
>= 0.99 |
L-inf ratio (max across tokens) |
< 1.5x |
L2 ratio (max across tokens) |
< 1.5x |
Pass/fail thresholds (two-way static fallback):
Top-k |
Threshold |
|---|---|
K5 |
< 0.011 |
K50 |
< 0.02 |
K1000 |
< 0.03 |
All |
< 0.05 |
The overall verdict passes if any of: sigma-ratio <= 1.0, aggregate BC >= 0.99, or all two-way thresholds are met.
8. Use golden caching to speed up repeated runs#
The golden caching system avoids recomputing expensive HF reference logits across test runs:
from vllm_neuron.accuracy.goldens import get_cached_reference_goldens
goldens = get_cached_reference_goldens(
model_id="meta-llama/Llama-3.2-1B-Instruct",
model_checkpoint=checkpoint_path,
model_config=config,
tokenizer=tokenizer,
prompts=["The capital of France is", ...],
output_length=16,
)
# goldens["fp32_logits"] - FP32 baseline
# goldens["dtype_logits"] - BF16 expected (teacher-forced)
# goldens["input_ids"] - Tokenized prompts
Cache location defaults to ~/.cache/vllm/neuron/goldens. Override
with the $VLLM_NEURON_GOLDEN_CACHE_DIR environment variable.
Confirm your work#
The accuracy debugger confirms your model is accurate when:
The HTML report shows green status banner (all checks passed)
Task analysis scores meet or exceed defined thresholds
Logit validation shows three-way ratios approximately 1.0x and BC >= 0.99 for all prompts
Tensor compare shows L2 ratios < 3.0 for all modules
To verify programmatically:
# Task analysis
assert result.passed, f"Task failed: {result.scores}"
# Prompt analysis
for name, prompt_results in result.plugin_results.items():
assert all(v.get("passed") for v in prompt_results.values() if isinstance(v, dict)), \
f"Plugin {name} failed"
Common issues#
HF reference model runs out of memory#
Possible solution: The HuggingFace reference runs on CPU. For large models (70B+), ensure sufficient system RAM (at least 3x model size for FP32 + BF16 + overhead). Alternatively, reduce
max_model_lento limit sequence length or analyze fewer prompts.
Tensor compare plugin fails with “cannot occupy Neuron cores”#
Possible solution:
TensorComparePluginmust run in a separaterun_prompt_analysis()call fromLogitValPluginandKvCachePlugin. The shared LLM and the tensor compare LLM cannot coexist on Neuron cores simultaneously.
Report shows failures but three-way ratio is close to 1.0x#
Possible solution: Ratios between 1.0x and 2.0x are borderline. Check if
max_model_lenmatches the calibration target used by other logit tests. Different compilation targets can cause minor numerical differences that are not bugs.
Logit validation fails at token 0 only#
Possible solution: This indicates a prefill bug. Run tensor compare to identify which module first introduces excess error during prefill. Check embedding weights, input processing, and the first attention layer.
Tensor capture causes torch.compile recompile#
Possible solution: Avoid wildcard module patterns like
[".*"]. Use explicit module patterns instead:
# Works correctly:
modules = [
"model.layers.0-15.input_layernorm",
"model.layers.0-15.self_attn",
"lm_head",
]
# Causes recompile guard (avoid):
modules = [".*"]
Golden cache returns stale results after model update#
Possible solution: The cache key includes
model_idandpromptsbut not the checkpoint hash. Delete the cache directory to force recomputation:
rm -rf ~/.cache/vllm/neuron/goldens
Or set a different cache directory:
export VLLM_NEURON_GOLDEN_CACHE_DIR=/tmp/fresh_goldens