LoRA Extract (Quantile)
LoRA Extract (Quantile) — extract a LoRA by cumulative singular-value mass
- output_path
One of five LoRA extraction flavors in silveroxides' ComfyUI-ModelUtils, and the one to reach for when you'd rather think in "keep 90% of what matters" than in raw rank numbers. LoRA Extract (Quantile) diffs two checkpoints, runs SVD on each layer's difference, and keeps adding singular values until they account for a target cumulative percentage of the total - a size/fidelity dial expressed as a quantile instead of an absolute number.
The mechanism. Standard setup for this whole pack: model_a is your fine-tune, model_b is the base it came from ("A - B = LoRA," per the node's own tooltips). For each layer, the weight difference gets SVD-decomposed into singular values ranked by size, and Quantile extraction keeps adding the largest ones until they reach your target share of the cumulative total - linear_quantile/conv_quantile, both defaulting to 0.9. That's a per-layer adaptive rank, same spirit as Frobenius extraction in this pack, just budgeted against the running total of singular values rather than the matrix's overall norm. For most layers the two will land in a similar place; Quantile is the more intuitive framing if "percentage of the important stuff kept" is how you naturally think about compression.
Inputs that matter. linear_quantile/conv_quantile (0.9 default) are the actual controls - drop them for a smaller file, push toward 1.0 if the extraction feels like it's losing fidelity. linear_max_rank/conv_max_rank (both 128) cap how large a rank the quantile target is allowed to demand, protecting you from a layer with a very gradual decay eating an enormous rank chasing 90%. The rest is shared across every extraction node in this pack: svd_niter for SVD accuracy, clamp_quantile (0.99) to clip outlier singular values, min_diff to skip layers that barely changed, skip_patterns/glob_skip_patterns to exclude layers by name, save_dtype (fp16), device (cuda). Output is output_path, the saved LoRA's location.
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 flagged in the README; you need the two source checkpoints already on disk.
Why extraction is worth doing in the first place. If you have a base model and a full fine-tune, the difference between them can be compressed into a LoRA that's a fraction of the disk space and portable to other checkpoints in the same family - this is a well-established practice in the community, not something specific to this pack, and it's exactly what kohya-ss's training scripts (this pack's acknowledged inspiration for the LoRA tooling) have supported for years. Quantile is a reasonable default if you don't already have a strong opinion about rank and just want a sensible fidelity/size tradeoff without babysitting individual layers.
Costs and where it goes wrong. SVD across every layer of two full checkpoints is slow and memory-hungry no matter which extraction node you use - lazy_load and force_clear_cache, both on by default, exist specifically to keep this from OOMing on modest GPUs, and chunk_large_layers (off by default) helps if you're extracting from an architecture with big fused transformer blocks. The recurring gotcha across this whole family: model_a and model_b have to be genuinely related checkpoints. mismatch_mode defaults to skip, which means feeding it two unrelated models won't error - it'll silently drop every layer that doesn't line up and hand you back a LoRA that looks fine and is missing most of what you wanted. If the result seems thin, that's the first thing to check, not the quantile setting.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| model_a | COMBO | Finetuned model (A - B = LoRA) | |
| model_b | COMBO | Base model (A - B = LoRA) | |
| linear_quantile | FLOAT | 0.900–1 | Target cumulative % for linear layers |
| conv_quantile | FLOAT | 0.900–1 | Target cumulative % for conv layers |
| 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 | — |