Nodes/Model Utility Toolkit/LoRA Extract (Fixed Rank)
ComfyUI Node

LoRA Extract (Fixed Rank)

LoRA Extract (Fixed Rank) — pull a LoRA out of two checkpoints

By silveroxides·Created about a year ago·Updated 4 days ago· 14
LoRA Extract (Fixed Rank)
    • output_path
    model_a
    model_b
    linear_dim64
    conv_dim32
    svd_niter2
    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

    If you've got a base checkpoint and a fine-tune of it, you can subtract one from the other and compress the difference into a LoRA - a fraction of the disk space, and something you can apply to other checkpoints in the same family. That's LoRA extraction, and it's a real, well-established technique (kohya-ss's sd-scripts has shipped an implementation of it for years, and it's the acknowledged lineage this whole pack's LoRA tooling draws from). LoRA Extract (Fixed Rank) is silveroxides' ComfyUI-native version of it, and it's the simplest of the five extraction nodes in ComfyUI-ModelUtils - you tell it exactly what rank you want, it gives you exactly that.

    The mechanism. model_a is your fine-tuned model, model_b is the base it started from (the node's own tooltips spell this out: "A - B = LoRA"). For every matching layer, the node computes the weight difference, then runs SVD (singular value decomposition) on that difference matrix and keeps the top singular values and vectors as the LoRA's low-rank up/down matrices. Fixed Rank is the version where you pick the rank yourself, up front, applied uniformly - no adaptive logic deciding some layers need more capacity than others. It's the most predictable of the five in output size, and the least adaptive.

    Inputs that matter. linear_dim (default 64) and conv_dim (default 32) set the rank for attention/linear layers and conv layers separately - this is the one thing you're actually choosing here. svd_niter (default 2) trades accuracy for speed in the SVD approximation; raise it if extraction quality looks off, at the cost of runtime. clamp_quantile (0.99) clips outlier singular values so one runaway layer doesn't dominate. min_diff skips layers where A and B barely differ, which trims dead weight from the output. skip_patterns (regex, or glob if glob_skip_patterns is on) lets you exclude layers by name entirely - handy for skipping a text encoder you don't want touched. The single output, output_path, is where the extracted .safetensors LoRA lands.

    Installing it. ComfyUI Manager, search "Model Utility Toolkit" - or cd ComfyUI/custom_nodes && git clone https://github.com/silveroxides/ComfyUI-ModelUtils and restart. No extra dependencies flagged in the README; you'll need the two checkpoints already sitting in your models folder.

    What this actually costs. You're loading two full checkpoints - 2-12GB apiece depending on architecture - and running SVD across every layer that changed. This is not a quick node. device defaults to cuda because SVD on CPU is painfully slow at this scale; lazy_load (on by default) streams tensors from disk instead of holding both full checkpoints in memory at once, and force_clear_cache (also on by default) clears CUDA memory after each layer. Both exist because this is exactly the kind of operation that OOMs on modest cards if you leave them off. If you're on a transformer-heavy architecture with big fused QKV or MLP blocks, chunk_large_layers (off by default) splits those into pieces the SVD step can chew through instead of choking.

    Where it bites. model_a and model_b have to be the same architecture - a LoRA extracted between two unrelated models is nonsense, because LoRAs are architecture-bound in general and this is the same constraint showing up at diff time. mismatch_mode defaults to skip, which quietly drops any layer that doesn't line up between A and B rather than erroring - convenient if you expect a few genuinely-added layers, dangerous if you fed it two checkpoints that don't actually share a lineage, because you'll get a LoRA back that looks plausible and is actually missing most of what you wanted. If the rank you picked seems arbitrary and you'd rather the node figure it out per layer, that's what the other four extraction nodes in this pack (Ratio, Quantile, Frobenius, Knee) are for - Fixed Rank is the one to reach for when you specifically want predictable, uniform output size.

    CategoryModelUtils/LoRA

    Inputs (16)

    NameTypeDefaultDescription
    model_aCOMBOFinetuned model (A - B = LoRA)
    model_bCOMBOBase model (A - B = LoRA)
    linear_dimINT641–16384Rank for linear/attention layers
    conv_dimINT321–16384Rank for conv layers
    svd_niterINT20–10SVD power iterations (higher = more accurate but slower)
    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