LoRA Extract (Frobenius)
LoRA Extract (Frobenius) — extract a LoRA sized by norm retention, not rank
- output_path
Extracting a LoRA from two checkpoints means diffing them and SVD-compressing the difference - the question every extraction method has to answer is how much of that difference to keep. LoRA Extract (Frobenius), one of five extraction variants in silveroxides' ComfyUI-ModelUtils, answers it by norm: instead of picking a rank number out of the air, you tell it what fraction of each layer's Frobenius norm to preserve, and it works out the rank per layer that gets you there.
The mechanism. model_a is the fine-tune, model_b is the base - same "A - B = LoRA" setup every extraction node in this pack uses. Per layer, it computes the diff, runs SVD, and instead of stopping at a fixed count of singular values, it keeps adding them until the retained singular values account for your target fraction of the original matrix's Frobenius norm (a standard measure of a matrix's overall "size"). A layer that changed a lot needs more singular values to hit a given norm-retention target than a layer that barely moved - so rank comes out adaptive per layer, without you having to eyeball which layers matter.
Inputs that matter. linear_target and conv_target (both default 0.9, meaning "keep 90% of the norm") are the actual dials - lower them for smaller files at some cost to fidelity, raise them if the extracted LoRA looks weak. linear_max_rank/conv_max_rank (both 128) put a ceiling on how far the adaptive selection is allowed to go, so a layer that changed drastically doesn't blow your file size out trying to hit 90%. Beyond that, it's the same shared toolkit as every extraction node here: svd_niter, clamp_quantile (clips outlier singular values), min_diff (skips near-identical layers entirely), skip_patterns/glob_skip_patterns for excluding layers by name, save_dtype (fp16 default), and device (cuda default). Output is a single output_path string pointing at the saved .safetensors LoRA.
Installing it. Search "Model Utility Toolkit" in ComfyUI Manager, or cd ComfyUI/custom_nodes && git clone https://github.com/silveroxides/ComfyUI-ModelUtils and restart. No extra dependencies in the README - you just need the two source checkpoints already downloaded.
Frobenius vs the other four. If you've read about Quantile extraction (the sibling node that also uses a percentage target), the pitch sounds similar, and it is - both give you a fidelity dial in percentage terms instead of an absolute rank. The practical difference is what's being budgeted: Frobenius targets a fraction of the overall norm of the diff matrix, Quantile targets a fraction of the cumulative singular-value mass. In practice they'll often land close together for a given target, so if you're not sure which to reach for, Frobenius is a reasonable default when you're thinking in terms of "how much of this layer's actual change am I keeping" rather than "how many singular values."
What this costs, and where it bites. Same as every extraction node in this pack: you're holding two full checkpoints in play and running SVD across every layer, which is slow and memory-hungry. lazy_load and force_clear_cache (both on by default) exist to keep that from OOMing your GPU - leave them on unless you have VRAM to spare. chunk_large_layers (off by default) helps if you're extracting from a modern DiT architecture with big fused blocks. And the same architecture constraint applies as always: model_a and model_b need to actually share a lineage, because mismatch_mode defaulting to skip will quietly drop mismatched layers rather than failing loudly, which can leave you with a LoRA that looks complete but isn't. If you'd rather not think about norm targets at all and just want the node to find the natural cutoff itself, LoRA Extract (Knee Detection) - also in this pack - does that automatically.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| model_a | COMBO | Finetuned model (A - B = LoRA) | |
| model_b | COMBO | Base model (A - B = LoRA) | |
| linear_target | FLOAT | 0.900–1 | Target Frobenius norm fraction for linear |
| conv_target | FLOAT | 0.900–1 | Target Frobenius norm fraction for conv |
| 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 | — |