LoRA Extract (Knee Detection)
LoRA Extract (Knee Detection) — auto-rank LoRA extraction from two checkpoints
- output_path
Of the five LoRA extraction nodes in silveroxides' ComfyUI-ModelUtils, this is the one for people who don't want to pick a rank, a ratio, or a percentage at all. LoRA Extract (Knee Detection) looks at the singular-value decay curve for each layer and finds the "knee" - the point where adding more singular values stops paying for itself - and sets the rank there automatically. You give it a ceiling and let the math decide the rest.
The mechanism, and why a knee. Extraction works by diffing your fine-tune (model_a) against its base (model_b, per the node's own tooltip labeling: "A - B = LoRA") and running SVD on the difference for each layer. Plot the singular values of that diff in descending order and you typically get a curve that drops fast at first, then flattens - the "knee" is where it bends from steep to flat, and everything past that point is mostly noise rather than signal. Instead of a fixed rank (uniform everywhere) or a percentage target (Frobenius/Quantile), Knee Detection reads that curve per layer and picks the cutoff itself, which means simple layers end up with a small rank and complex ones get more, without you tuning either.
Inputs that matter. knee_method (default sv_knee) picks which detection algorithm runs the curve analysis - the dropdown will show whatever variants the node ships with; if you're not sure, the default is the reasonable starting point. linear_max_rank/conv_max_rank (both 128) cap how far the automatic selection is allowed to go, since a layer with a very gradual, almost-flat decay could otherwise get assigned a huge rank chasing a knee that barely exists. Beyond that, this shares the same toolkit as every extraction node here: svd_niter, clamp_quantile (0.99, clips outlier singular values), min_diff (skips layers that barely changed), skip_patterns/glob_skip_patterns, save_dtype (fp16), and device (cuda). One output, output_path, points to the saved LoRA file.
Installing it. ComfyUI Manager, search "Model Utility Toolkit" - or clone directly: cd ComfyUI/custom_nodes && git clone https://github.com/silveroxides/ComfyUI-ModelUtils, restart ComfyUI. No extra dependencies in the README; you supply the two checkpoints.
When to actually reach for this one. If you've already tried Fixed Rank and found some layers over-provisioned (wasting file size on a layer that barely changed) while others feel under-served, Knee Detection is the fix - it's the node built specifically to stop you from having to guess rank layer by layer. It's also the right first attempt if you have no strong intuition about how much any given layer actually changed between your two checkpoints, which, honestly, is most of the time.
Costs and failure modes, same family as its siblings. You're loading two full checkpoints and running SVD across every layer, which is slow and memory-hungry regardless of which extraction node you pick - lazy_load and force_clear_cache (both on by default) exist to keep that from blowing out VRAM, and chunk_large_layers helps on modern DiT architectures with big fused QKV/MLP blocks if extraction is choking. And the constraint that applies to all five: model_a and model_b need to be the same architecture. mismatch_mode defaults to skip, meaning a genuine architecture mismatch between the two checkpoints won't throw an error - it'll just quietly drop the layers that don't line up, and knee detection has nothing to say about layers it never saw. If your extracted LoRA seems oddly thin, check that both source checkpoints actually share a lineage before blaming the knee-detection logic.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| model_a | COMBO | Finetuned model (A - B = LoRA) | |
| model_b | COMBO | Base model (A - B = LoRA) | |
| knee_method | COMBO | sv_knee | Knee detection method |
| linear_max_rank | INT | 1281–16384 | Maximum rank for linear layers |
| conv_max_rank | INT | 1281–16384 | Maximum rank for conv layers |
| lazy_load | BOOLEAN | true | Low memory mode: load tensors from disk on demand |
| force_clear_cache | BOOLEAN | true | Clear CUDA cache after each layer |
| chunk_large_layers | BOOLEAN | false | Split large fused layers (QKV, MLP) into chunks |
| clamp_quantile | FLOAT | 0.990.5–1 | Clamp outlier singular values |
| min_diff | FLOAT | 0.0000–1 | Skip layers with max difference below this |
| mismatch_mode | COMBO | skip | 3 options: skip, zeros, error |
| output_filename | STRING | extracted_lora | — |
| save_dtype | COMBO | fp16 | 3 options: fp16, bf16, fp32 |
| device | COMBO | cuda | 2 options: cuda, cpu |
| skip_patterns | STRING | Patterns for layers to skip (regex or glob depending on glob_skip_patterns) | |
| glob_skip_patterns | BOOLEAN | false | When True, skip_patterns use glob syntax (* = any sequence, ? = any char, dots are literal). When False (default), patterns are Python regex matched as substrings. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| output_path | STRING | — |