Nodes/Model Utility Toolkit/LoRA Extract (Frobenius)
ComfyUI Node

LoRA Extract (Frobenius)

LoRA Extract (Frobenius) — extract a LoRA sized by norm retention, not rank

By silveroxides·Created about a year ago·Updated 4 days ago· 14
LoRA Extract (Frobenius)
    • output_path
    model_a
    model_b
    linear_target0.90
    conv_target0.90
    linear_max_rank128
    conv_max_rank128
    lazy_loadtrue
    force_clear_cachetrue
    chunk_large_layersfalse
    clamp_quantile0.99
    min_diff0.000
    mismatch_modeskip
    output_filenameextracted_lora
    save_dtypefp16
    devicecuda
    skip_patterns
    glob_skip_patternsfalse

    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.

    CategoryModelUtils/LoRA

    Inputs (17)

    NameTypeDefaultDescription
    model_aCOMBOFinetuned model (A - B = LoRA)
    model_bCOMBOBase model (A - B = LoRA)
    linear_targetFLOAT0.900–1Target Frobenius norm fraction for linear
    conv_targetFLOAT0.900–1Target Frobenius norm fraction for conv
    linear_max_rankINT1281–16384Maximum rank for linear layers
    conv_max_rankINT1281–16384Maximum rank for conv layers
    lazy_loadBOOLEANtrueLow memory mode: load tensors from disk on demand
    force_clear_cacheBOOLEANtrueClear CUDA cache after each layer
    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_modeCOMBOskip3 options: skip, zeros, error
    output_filenameSTRINGextracted_lora
    save_dtypeCOMBOfp163 options: fp16, bf16, fp32
    deviceCOMBOcuda2 options: cuda, cpu
    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.

    Outputs (1)

    NameTypeDescription
    output_pathSTRING