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

DoRA Learned Extract (Knee Detection) (WIP)

DoRA Learned Extract (Knee Detection) — automatic rank, then gradient-refined

By silveroxides·Created 2 years ago·Updated 3 days ago· 17
DoRA Learned Extract (Knee Detection) (WIP)
  • layer_parameters
  • output_path
◄model_a▾►
◄model_b▾►
◄knee_methodsv_knee►
◄knee_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►

This is DoRAExtractKnee with a gradient-descent stage bolted on, and it carries the "(WIP)" tag in its own display name to prove it - the pack's author is telling you plainly that this is the newest, least-battle-tested corner of the extraction tools, not a settled feature. If you want the hands-off version, automatic rank selection with no learned refinement, use DoRAExtractKnee instead; this one is for when you want the same automatic per-layer rank decision but are willing to spend extra compute trying to squeeze out a better fit.

The two-stage idea is the same across all five "Learned" nodes in this pack: run knee detection on each layer's singular-value curve to find the natural elbow - the point where the curve stops dropping steeply and flattens out, meaning everything past it contributes little - use that as your rank and your SVD-based starting point, then run gradient descent to directly minimize the reconstruction error between the extracted low-rank adapter and the actual weight delta. Plain SVD is provably optimal for a low-rank matrix approximation in isolation, but that guarantee doesn't account for how DoRA's magnitude-plus-direction split behaves once loaded, or for interactions across layers - refining past the SVD answer is a bet that direct loss minimization can do better on the thing you actually care about.

Inputs that matter. model_a (finetune) and model_b (base) set up the extraction. knee_method (default "sv_knee") picks the curve-detection algorithm - same field as the non-learned DoRAExtractKnee. linear_max_rank and conv_max_rank (both default 128) still cap whatever rank the knee detector lands on, split for linear/attention versus convolutional layers.

The optimization stage brings the full Learned-family toolkit: optimize_iters (default 500, 0 disables refinement entirely and falls back to plain knee-based SVD), optimizer (default "prodigy" - adaptive, auto-tunes its own learning rate, which is the community's usual reason to reach for it), learning_rate (default 1, with the tooltip warning AdamW wants roughly 0.01 while Prodigy wants roughly 1.0 - don't carry one value across a switch between the two), lr_schedule (default "plateau") governed by lr_patience (default 2), lr_factor (default 0.9), and lr_cooldown (default 2), plus three early-stop conditions - early_stop_loss (1e-6), early_stop_stall (2000 iterations), early_stop_lr (9.01e-9) - so it isn't burning compute after convergence.

Shared extraction plumbing: chunk_large_layers for splitting fused QKV/MLP blocks common in transformer diffusion architectures, clamp_quantile (default 0.99) to bound outlier singular values, min_diff (default 0) to skip layers the fine-tune barely changed, mismatch_mode (default "skip"), skip_patterns/glob_skip_patterns for manual exclusions, 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) - an output node, so it executes as a terminal step regardless of whether anything reads that path.

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

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

then restart.

Troubleshooting. Because knee detection already makes the rank decision for you, the main thing worth testing here is whether the added optimize_iters refinement is actually earning its compute cost over the plain DoRAExtractKnee - run both against the same model pair and compare file size and, more importantly, actual behavior when loaded, rather than assuming more compute means a better result. If refinement runs to the full iteration budget without an early stop firing, that's usually a learning-rate mismatch with whichever optimizer you've picked, not a sign the model pair is unusually hard to fit. As with every extraction node in this pack, two full checkpoints resident at once is a real VRAM cost - keep lazy_load and force_clear_cache on unless you know you have headroom to spare.

CategoryModelUtils/DoRA

Inputs (29)

NameTypeDefaultDescription
model_aCOMBOFinetuned model (A - B = LoRA)
model_bCOMBOBase model (A - B = LoRA)
knee_methodCOMBOsv_kneeDetect the knee from raw singular values or their cumulative distribution.
knee_probe_offsetINT321–4096Extra singular values probed beyond Max Rank to avoid detecting a false knee at the partial-spectrum boundary.
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_max_rank; b=conv_max_rank; c=clamp_quantile; d=min_diff Full names are also accepted. Unassigned values use this node's settings; existing filters still apply.

Outputs (1)

NameTypeDescription
output_path*—