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

DoRA Learned Extract (Frobenius) (WIP)

DoRA Learned Extract (Frobenius) — target energy retention, then refine it

By silveroxides·Created 2 years ago·Updated 3 days ago· 17
DoRA Learned Extract (Frobenius) (WIP)
  • layer_parameters
  • output_path
◄model_a▾►
◄model_b▾►
◄linear_target0.90►
◄conv_target0.90►
◄probe_offset32►
◄linear_max_rank128►
◄conv_max_rank128►
◄optimize_iters500►
◄learning_rate1.000►
◄optimizerprodigy►
◄lr_scheduleplateau►
◄lr_patience2►
◄lr_factor0.90►
◄lr_cooldown2►
◄early_stop_loss0►
◄early_stop_stall2000►
◄early_stop_lr0►
◄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►

Marked "(WIP)" in its own display name, so treat everything below as a documented capability rather than a settled, battle-tested one - this is one of five "Learned Extract" nodes in the pack that add a gradient-descent refinement step on top of the usual SVD-based DoRA extraction, and it's the one that targets Frobenius norm specifically rather than a rank you set by hand.

If you've used DoRAExtractQuantile, this will look familiar - both let you set a target percentage per layer instead of a fixed rank. But they're measuring slightly different things, and it's a real distinction, not just naming. Quantile targets cumulative singular-value magnitude: keep adding singular values, largest first, until their sum hits your target fraction of the total sum. Frobenius targets cumulative squared magnitude - the Frobenius norm is, by definition, the square root of the sum of squared singular values, and it's the standard measure of how much "energy" a low-rank approximation retains relative to the original matrix (this is the same quantity the Eckart-Young theorem is about: SVD truncation is the provably optimal way to minimize the Frobenius-norm error of a low-rank approximation). In practice the two targeting methods often land on similar ranks for a given layer, but not identical ones - squaring disproportionately weights the largest singular values, so a Frobenius target tends to be a little more willing to cut a long tail of small values than a quantile target set to the same nominal percentage.

What "Learned" adds on top. Rather than stopping once SVD hits the target, this node initializes from that SVD result and then runs gradient descent to directly minimize reconstruction error against the real weight delta - the same refinement idea behind all five nodes in this sub-family, on the theory that closed-form SVD optimality per layer isn't necessarily optimal for how the extracted adapter behaves once loaded.

Inputs that matter. model_a (finetune) and model_b (base) set up the diff being extracted. linear_target and conv_target (both default 0.9) are the Frobenius-retention targets, split between linear/attention and convolutional layers. linear_max_rank and conv_max_rank (both default 128) cap how much rank a layer can claim regardless of what hitting the target would otherwise require.

The optimization controls match the rest of the Learned family: optimize_iters (default 500, 0 skips refinement entirely), optimizer (default "prodigy", with the tooltip noting AdamW wants a learning rate around 0.01 versus Prodigy's roughly 1.0), learning_rate (default 1), lr_schedule (default "plateau") with lr_patience (default 2), lr_factor (default 0.9), and lr_cooldown (default 2), plus three early-stopping conditions - early_stop_loss (default 1e-6), early_stop_stall (default 2000), early_stop_lr (default 9.01e-9) - so it doesn't burn the full iteration budget once it's converged.

Shared plumbing rounds it out: chunk_large_layers for fused QKV/MLP layers, clamp_quantile (default 0.99), min_diff (default 0) to skip barely-touched layers, mismatch_mode (default "skip"), skip_patterns/glob_skip_patterns, output_filename (default "extracted_lora"), save_dtype (default fp16), device (default cuda), lazy_load (on by default), force_clear_cache (on by default).

Output. output_path (STRING). Output node - runs as a terminal step whether or not that path is wired anywhere else.

Installing it. ComfyUI Manager, search "Model Utility Toolkit," or:

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

then restart.

Troubleshooting. If you're unsure whether to reach for this over the plain DoRAExtractQuantile, the honest answer given the WIP label is: try the non-learned quantile node first, since it's the faster, more predictable path, and only reach for this one if you specifically want the gradient-descent refinement and are prepared to verify the result rather than trust it blind. Watch optimize_iters the same way you would on any Learned node here - if it consistently runs to the full budget without an early stop triggering, your learning rate is probably mismatched to whichever optimizer you've selected, and that's worth fixing before you draw any conclusion about whether the refinement itself is helping.

CategoryModelUtils/DoRA

Inputs (30)

NameTypeDefaultDescription
model_aCOMBOFinetuned model (A - B = LoRA)
model_bCOMBOBase model (A - B = LoRA)
linear_targetFLOAT0.900–1Target Frobenius-norm fraction retained for linear layers.
conv_targetFLOAT0.900–1Target Frobenius-norm fraction retained for convolution layers.
probe_offsetINT321–4096Extra singular values sampled beyond Max Rank for a reliable bounded rank decision.
linear_max_rankINT1281–16384Maximum extracted rank for linear layers.
conv_max_rankINT1281–16384Maximum extracted rank for convolution layers.
optimize_itersINT5000–10000Number of gradient descent iterations (0 = skip optimization)
learning_rateFLOAT1.0000.0001–100Base learning rate. AdamW prefers ~0.01, Prodigy prefers ~1.0
optimizerCOMBOprodigyOptimization algorithm
lr_scheduleCOMBOplateauLearning rate scheduling strategy
lr_patienceINT20–1000Steps to wait before decaying LR (Plateau)
lr_factorFLOAT0.900.01–1Factor to decay LR by (Plateau)
lr_cooldownINT20–1000Steps to wait after decay before checking again (Plateau)
early_stop_lossFLOAT00–1Stop early if MSE loss drops below this value
early_stop_stallINT20000–10000Stop early if loss doesn't improve for this many iterations
early_stop_lrFLOAT00–1Stop early if learning rate drops below this value
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*—