Nodes/Model Utility Toolkit/DoRA Extract (Frobenius)
ComfyUI Node

DoRA Extract (Frobenius)

Adaptive-rank extraction that targets quality, not a fixed size

By silveroxides·Created 2 years ago·Updated 3 days ago· 17
DoRA Extract (Frobenius)
  • layer_parameters
  • output_path
◄model_a▾►
◄model_b▾►
◄linear_target0.90►
◄conv_target0.90►
◄probe_offset32►
◄linear_max_rank128►
◄conv_max_rank128►
◄lazy_loadtrue►
◄force_clear_cachefalse►
◄chunk_large_layersfalse►
◄clamp_quantile0.99►
◄min_diff0.000►
◄mismatch_modeskip►
◄output_filenameextracted_lora►
◄save_dtypefp16►
◄devicecuda►
◄skip_patterns►
◄glob_skip_patternsfalse►
◄include_modefalse►

DoRAExtractFixed's sibling, and the more interesting of the two if you're not sure what rank you actually need. Both nodes do the same fundamental thing - take a base checkpoint and a fine-tune of it, compute the difference, and compress that difference into a low-rank DoRA adapter - but where DoRAExtractFixed makes you pick the rank up front, this one flips the question around: you tell it how much of the original difference's strength you want preserved, and it figures out the rank needed to get there, per layer, up to a ceiling you set. The node's own description names the target directly: "Preserve target fraction of Frobenius norm." The Frobenius norm is a standard way to measure a matrix's overall magnitude, so "preserve 0.9 of it" means "keep the compressed version at roughly 90% of the original difference's strength," however many singular values that takes.

How it works

model_a and model_b set the direction, same as the fixed-rank version - model_a is the finetuned model, model_b is the base ("A - B = LoRA," per the tooltip). Get this backwards and the math runs fine but produces the inverse of the adapter you meant.

linear_target and conv_target (both default 0.9) are the actual targets - the fraction of Frobenius norm to preserve for linear/attention layers and convolutional layers respectively, set separately because the two layer types compress differently. linear_max_rank and conv_max_rank (both default 128) are the ceiling: even if hitting your target fraction would need a higher rank than that, the extraction won't exceed it, trading a bit of fidelity for a size cap you control. That's the practical read on this node versus the fixed-rank one - instead of guessing a single rank for the whole model and hoping it's enough everywhere, you set a quality floor and a size ceiling, and the extraction spends more rank on layers that actually changed a lot and less on ones that barely moved.

The rest of the field set matches DoRAExtractFixed closely: lazy_load/force_clear_cache (both default on) for memory management, chunk_large_layers (off by default) for splitting fused QKV/MLP blocks on tight VRAM, clamp_quantile (default 0.99) to clip outlier singular values, min_diff (default 0) to skip layers that barely changed, mismatch_mode (default skip) for tensors present in only one model, skip_patterns (regex, or glob with glob_skip_patterns) to exclude layers outright, save_dtype (default fp16) and device (default cuda). Output is output_path, with output_filename defaulting to extracted_lora.

Installing it

ComfyUI Manager: search Model Utility Toolkit, install, restart. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-ModelUtils

Restart ComfyUI. No downloads needed beyond the two source models.

Where people get burned

Same directional trap as the fixed-rank version: swap model_a and model_b and the extraction runs cleanly but produces the inverse adapter, which reads as "this DoRA does nothing" or "this DoRA does the opposite of what I trained" rather than an obvious error.

The setting most likely to surprise you is linear_max_rank/conv_max_rank capping the output below your target fraction on a layer that changed a lot - if a particular layer needed rank 200 to hit 0.9 Frobenius preservation and your ceiling is 128, you get 128 and a smaller fraction preserved on that layer specifically, silently. If the extracted adapter feels weaker than expected on a specific effect, that's worth checking before assuming the extraction failed outright.

And the same honest caveat about DoRA as a format applies here regardless of which rank-selection method produced it: the community's real-world verdict, after a genuine wave of "DoRA beats LoRA in every way" enthusiasm, settled on DoRA adapters not stacking well together and likeness results being dataset-dependent rather than a reliable upgrade. Extract it, use it on its own, and don't assume it'll combine cleanly with other adapters the way plain LoRAs usually do.

CategoryModelUtils/DoRA

Inputs (20)

NameTypeDefaultDescription
model_aCOMBOFinetuned model (A - B = LoRA)
model_bCOMBOBase model (A - B = LoRA)
linear_targetFLOAT0.900–1Target Frobenius norm fraction for linear
conv_targetFLOAT0.900–1Target Frobenius norm fraction for conv
probe_offsetINT321–4096Extra singular values sampled beyond Max Rank for a reliable bounded rank decision.
linear_max_rankINT1281–16384Maximum rank for linear layers
conv_max_rankINT1281–16384Maximum rank for conv layers
lazy_loadBOOLEANtrueLow memory mode: load tensors from disk on demand
force_clear_cacheBOOLEANfalseClear CUDA cache after each layer; slower but useful under severe VRAM pressure.
chunk_large_layersBOOLEANfalseSplit large fused layers (QKV, MLP) into chunks
clamp_quantileFLOAT0.990.5–1Clamp outlier singular values
min_diffFLOAT0.0000–1Skip layers with max difference below this
mismatch_modeCOMBOskipHandle missing or incompatible model tensors by skipping them, substituting zeros where supported, or aborting.
output_filenameSTRINGextracted_loraOutput filename without extension, written under ComfyUI's LoRA directory.
save_dtypeCOMBOfp16Data type used to save the extracted DoRA tensors.
deviceCOMBOcudaDevice used for extraction arithmetic; CUDA out-of-memory processing falls back per affected layer where supported.
skip_patternsSTRINGPatterns for layers to skip (regex or glob depending on glob_skip_patterns)
glob_skip_patternsBOOLEANfalseWhen True, skip_patterns use glob syntax (* = any sequence, ? = any char, dots are literal). When False (default), patterns are Python regex matched as substrings.
include_modeBOOLEANfalseUse Skip Patterns as a whitelist instead. Only matching layers are extracted; an empty whitelist extracts nothing.
layer_parametersoptMODELUTILS_LAYER_PARAMETERSOptional Layer Parameter Configuration. a=linear_target; b=conv_target; c=clamp_quantile; d=min_diff; e=linear_max_rank; f=conv_max_rank Full names are also accepted. Unassigned values use this node's settings; existing filters still apply.

Outputs (1)

NameTypeDescription
output_path*—