Nodes/ComfyUI-GPTQ-Calibration/Calibration Data Collector
ComfyUI Node

Calibration Data Collector

It Doesn't Quantize Anything (and That's the Point)

By ThunderFun·Created 3 months ago·Updated 2 months ago· 0
Calibration Data Collector
  • model
  • conditioning
  • calibration_path
num_steps4
num_samples16
seed0
hessian_block_size128
hessian_formatdlr_nystrom
dlr_rank128
collect_amaxtrue
output_path/tmp/ComfyUI/output/calibration.pt
latent_height64
latent_width64
convrotfalse
rot_size256
rpbhfalse
rpbh_seed42
rpbh_block_size256
permuquantfalse
pisofalse
sigma_min0.00
sigma_max1.00
force_cpu_hookfalse
timestep_channel_amaxfalse
timestep_buckets4

The name is half a lie. This node doesn't quantize a single weight - it collects the calibration data that lets some other tool quantize your model well. That's not a bug; it's the whole design. Most people never touch this thing: they grab a GGUF or fp8 pack from civitai and move on, because someone already ran this exact pipeline for them. You reach for it when nobody has - a fine-tune or video model with no pack, or a custom bit-depth no prebuilt quant covers.

It's the first half of a two-stage pipeline. The second half is the author's companion tool, int_crush_converter, which takes the .pt file this node writes and actually does the GPTQ/OBQ/ConvRot weight compression.

How it actually works

Here's the mechanism, grounded in the source rather than the marketing. The node registers forward hooks on every layer, then runs a short sampling pass: num_samples independent denoising runs of num_steps steps each. Each time a layer's activations flow through, the hook accumulates H += xᵀx - the Gram matrix, i.e. the Hessian of the layer - plus a running amax (max |x|) if you ask for it. That Hessian is the whole trick: GPTQ uses second-order information to decide where to spend precision, instead of the naive per-row max that plain absmax quantization uses. More calibration data means the quantizer knows which channels actually matter, and compresses the ones that don't.

Nothing about the model is modified. It's read-only - the .pt it writes is the entire output.

The inputs that matter

You can leave most of these alone. The ones you actually touch:

  • model and conditioning - just wire your loaded checkpoint and a CLIPTextEncode like a normal generation. It samples like a sampler, so it needs a real prompt.
  • num_samples - independent samples to accumulate over. The tooltip says 16–128 recommended; more is smoother statistics, slower to run.
  • num_steps - denoising steps per sample. Default 4 is fine; you're not making art, just statistics.
  • hessian_format - leave it on dlr_nystrom. That's a Diagonal + Low-Rank Hessian built with a randomized Nyström sketch: same accuracy as the plain dlr streaming version at roughly 100× the speed, and it stores the exact diagonal separately. block gives you diagonal blocks sized by hessian_block_size; full is the paper-accurate O(n²) version that memory-maps to disk and can produce multi-GB files.
  • dlr_rank - only used in DLR modes; 128 is a good default, 64–256 the sane range.
  • collect_amax - on by default, but weight-only GPTQ ignores it. You only need it for activation quantization.
  • latent_height / latent_width - set 128 for a 1024px model, 64 for 512px. Get this wrong and your statistics won't match what the model actually sees at inference.

Everything else - convrot, rpbh, permuquant, piso, sigma_min/sigma_max - is advanced. The rotation flags (ConvRot Hadamard, RPBH) make Hessians more block-diagonal for better quantization; the catch is the converter must use the same rpbh_seed (default 42) to fold the rotation into the weights. sigma_min/sigma_max clip the noise range, which matters for MoE models like Wan 2.2 where different experts own different noise regimes - the README cites a boundary at sigma ≈ 0.875.

The output

One output: calibration_path (a STRING pointing at the saved .pt). It's an output node - you run the workflow, note the path (default ComfyUI/output/calibration.pt), and feed that file to the converter.

Installing it

ComfyUI Manager (search "ComfyUI-GPTQ-Calibration"), or:

cd ComfyUI/custom_nodes
git clone https://github.com/ThunderFun/ComfyUI-GPTQ-Calibration.git

Then restart ComfyUI. Dependencies are torch and numpy, which ship with ComfyUI already - no extra install. The one optional extra is Triton, which GPU-accelerates the Hadamard rotation for ConvRot; without it you get a slower CPU fallback:

pip install triton

There are no model downloads; it reads whatever you load into it.

Where people get burned

Be honest about what this pack is. The README leads with a warning: "This code has not been thoroughly tested. Verify outputs before relying on it." It's an AI-assisted version 0.3 with essentially zero community footprint - no real discussion of it anywhere as of this writing. Budget for rough edges, and sanity-check the quant against the fp16 original.

Known sharp edges from the docs and source: hessian_format='full' or hessian_block_size=0 is O(n²) and memory-mapped to a .gptq_hessian_tmp/ directory (auto-cleaned, but multi-GB territory on big models). If you hit GPU OOM during calibration, flip force_cpu_hook - the fast path only uses ~20–50 MB transient VRAM per layer, but on a tight card that's enough to hurt. And remember the rotation-seed rule: change rpbh_seed here and the converter must use the same one, or the whole quant is garbage.

Categorymodel/quantization

Inputs (24)

NameTypeDefaultDescription
modelMODELLoaded diffusion model (FP16/BF16/FP32).
conditioningCONDITIONINGPre-encoded conditioning from CLIPTextEncode or similar.
num_stepsINT41–50Denoising steps per sample.
num_samplesINT161–4096Independent samples to accumulate over. 16-128 recommended.
seedINT00–4294967295Seed for noise and timestep sampling.
hessian_block_sizeINT1280–10240 = full H (paper-accurate, auto memory-mapped to disk). 128 = diagonal blocks (default, saves RAM). Ignored when hessian_format='dlr'.
hessian_formatCOMBOdlr_nystromHessian storage format. 'block' = diagonal blocks (use hessian_block_size). 'full' = full Hessian (memory-mapped). 'dlr' = Diagonal + Low-Rank via FrequentDirections (streaming, exact diagonal, repeated SVD). 'dlr_nystrom' = DLR via randomized Nyström (streaming, exact diagonal, ~100× faster construction — recommended default).
dlr_rankINT1281–4096Rank for DLR Hessian (only used when hessian_format is 'dlr' or 'dlr_nystrom'). Same memory budget as block_size=rank. Recommended: 64-256.
collect_amaxBOOLEANtrueAlso collect max(abs(x)) per layer. Required for activation quantization; not used by weight-only GPTQ.
output_pathSTRING/tmp/ComfyUI/output/calibration.ptWhere to save the calibration .pt file.
latent_heightoptINT648–1024Latent spatial height. 128 for 1024px, 64 for 512px.
latent_widthoptINT648–1024Latent spatial width. 128 for 1024px, 64 for 512px.
convrotoptBOOLEANfalseEnable ConvRot Hadamard rotation. Collects Hessians in rotated space for better block-diagonal approximation.
rot_sizeoptINT25616–4096Hadamard group size (must be power of 2). 256 recommended for ConvRot.
rpbhoptBOOLEANfalseEnable RPBH (Randomized Permuted Block-Hadamard) rotation. Mutually exclusive with ConvRot. Spreads outliers via random permutation + block Hadamard + sign flip. Uses per-layer auto-detected block size (largest power-of-2 dividing in_features). Best paired with DLR Hessian format — block format is lossy because RPBH's permutation destroys block-diagonal structure. Not recommended with PermuQuant.
rpbh_seedoptINT420–2147483647Random seed for RPBH permutation and signs. The converter must use the same seed to regenerate matching rotation parameters for weight folding.
rpbh_block_sizeoptINT2560–65536RPBH Hadamard block size. 0 = auto-detect (largest power-of-2 dividing in_features, default). Set to 256 for uniform block size across all layers — smaller blocks give finer-grained outlier spreading. Must be a power of 2 if non-zero. The converter must use the same block size.
permuquantoptBOOLEANfalseEnable PermuQuant channel reordering. Runs a second calibration pass with channels sorted by second moment for better quantization.
pisooptBOOLEANfalseCollect Hessian diagonal for PiSO data-aware scale optimization. Adds a small overhead to store diag(X^T X) per layer, which the converter uses to compute optimal per-row scales instead of absmax.
sigma_minoptFLOAT0.000–1Lower bound of the sigma range to sample. Set to 0.875 with Wan 2.2 high-noise expert, or 0.0 for full range (default).
sigma_maxoptFLOAT1.000–1Upper bound of the sigma range to sample. Set to 0.875 with Wan 2.2 low-noise expert, or 1.0 for full range (default).
force_cpu_hookoptBOOLEANfalseForce hook-side processing to CPU. Enable only if you hit GPU OOM during calibration — the GPU-fast path uses ~20-50 MB of transient VRAM per layer.
timestep_channel_amaxoptBOOLEANfalseCollect per-timestep-bucket per-channel amax in addition to overall per-channel amax. Enables the converter to analyse time-varying channel imbalance for static-dynamic decomposition (ViDiT-Q Section 4.2). Automatically enables per-channel amax collection. Adds 'channel_amax_by_timestep' to the output.
timestep_bucketsoptINT41–64Number of timestep buckets for per-timestep channel amax. 4 = divide denoising into 4 equal ranges (ViDiT-Q paper default).

Outputs (1)

NameTypeDescription
calibration_pathSTRING