LTX Attn — Head Candidates
Stop eyeballing heatmaps, get a shortlist
- report
- candidates_csv
- control_csv
Compare Runs hands you a diff heatmap and a ranked table per metric. Then you stare at several of those tables and try to figure out which heads keep showing up. Head Candidates automates that last, tedious step: it combines several metrics' zscore diffs into one composite score per head - mean(|zscore(A − B)|) across the metrics you list - and outputs a ranked shortlist of candidate heads plus a control group, ready to paste into Head Freeze.
This is the node that closes the loop between "profiling two runs" and "intervening." Its whole point is that the output CSV is copy-paste compatible with the intervention nodes' targets widgets, so the workflow becomes: capture A, capture B, run Head Candidates, paste candidates_csv into Head Freeze, regenerate, see if it does anything.
How it works
It reuses Compare Runs' store-loading and zscore logic, but instead of rendering a heatmap it computes, for each (block, head), the mean absolute zscore across the metric list you provide - the composite. A head that's a consistent outlier on temporal, frame_dist_mean_norm, and frame_dist_std_norm scores high because it moved across every metric; a head carried by only one metric scores lower. The report shows each individual metric's zscore per head, not just the composite, so you can tell "consistently implicated" from "one metric fluke."
Two groups come out: the candidate shortlist (top top_k by composite score) and a control group - either the heads least implicated (control_mode=lowest_score) or a random sample (random + seed). The control group excludes anything already in the candidates, and it exists because an intervention experiment without a control is just a video you made.
The inputs that matter
store_handle_a/store_handle_b- the two runs.metrics- comma-separated, e.g.temporal,frame_dist_mean_norm,frame_dist_std_norm. Use_normvariants when the runs don't share frame count/resolution.attn_type-saorca.top_k- candidate shortlist size.control_mode/control_k/seed- control group behavior;control_k=0skips it.
Outputs are report (STRING, the readable breakdown), candidates_csv (STRING, one block,head per line for pasting), and control_csv (STRING, same format for the control group).
Install
cd ComfyUI/custom_nodes
git clone https://github.com/g-raw/ComfyUI-LTX-Attention-Toolkit.git
Restart ComfyUI. No extra dependencies, no model files. The pack is work-in-progress, so treat saved stores as breakable between versions.
Common issues
Same input hygiene as Compare Runs: dump-and-reload a .pt via Store Load first if your runs aren't live in the registry, and use _norm distance metrics for cross-resolution comparisons. Since this only needs metrics (computed in every capture mode), no full-map capture is required - that's the nice part. If the composite scores all come out near zero, you're comparing two runs that are essentially identical on the metrics you picked; either the difference is real but elsewhere (try other metrics), or your two generations really didn't diverge.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| store_handle_a | STRING | — | |
| store_handle_b | STRING | — | |
| attn_type | COMBO | sa | 2 options: sa, ca |
| metrics | STRING | temporal,frame_dist_mean_norm,frame_dist_std_norm | Comma-separated metric names. Composite score = mean(|zscore|) across all of them, per (block, head). |
| step_idx | INT | -1-1–255 | — |
| top_k | INT | 121–1536 | How many (block, head) candidates to shortlist, ranked by the composite score. |
| control_mode | COMBO | lowest_score | How to pick the control group: the heads least implicated by these metrics, or a random sample (excluding the candidates either way). |
| control_k | INT | 120–1536 | Control group size. 0 to skip it. |
| seed | INT | 00–2147483647 | Only used when control_mode=random. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| report | STRING | — |
| candidates_csv | STRING | — |
| control_csv | STRING | — |