TE DoRA Extract (Fixed Rank)
Extract a text-encoder DoRA at a fixed rank, no training required
- output_path
You have a fine-tuned text encoder and the base it came from. You want the difference between them as a lightweight file you can load on top of any compatible base - without retraining anything. That's LoRA extraction, and this node does it with the DoRA variant: instead of a plain low-rank adapter, you get the low-rank factors plus a per-channel magnitude vector, which is what makes DoRA train and behave more like a full fine-tune. It's from the same extraction machinery as the pack's LoRA extractors, just pointed at text encoders and wired for DoRA output.
How it works
Model A is the fine-tuned text encoder, Model B is the base. The node computes A − B per layer, SVD-decomposes that delta into low-rank down/up factors, and - the DoRA part - separates the magnitude direction and stores it as a dora_scale tensor alongside the factors. The result is written to your LoRA directory and loads like any LoRA.
This is the Fixed Rank flavor: you pick linear_dim (default 64) for linear/attention layers and conv_dim (default 32) for conv layers, and every layer of that type gets exactly that rank. Predictable size, predictable shape - but it ignores what the singular value spectrum actually wants. A layer whose delta only needs rank 4 gets rank 64 anyway, which is wasted bytes but never a correctness problem.
Inputs that matter
- model_a / model_b - fine-tuned and base text encoders, both from
models/text_encoders. The tooltip spells it out:A - B = LoRA. - linear_dim / conv_dim - the fixed ranks. Start near the defaults; 64/32 is a sane text-encoder size.
- svd_niter (default 2) - power iterations for the SVD. A couple of extra iterations tighten the low-rank approximation for a modest compute cost; 0–10 range, leave at 2 unless you're chasing quality.
- output_filename (default
extracted_te_lora) - written to the LoRA directory, no extension. - save_dtype (default fp16) - dtype for the extracted factors.
- clamp_quantile (default 0.99) - clamps outlier singular values, which keeps one freak value from dominating the reconstruction.
- min_diff (default 0) - skip layers whose max difference from base is below this; a cheap way to ignore layers that barely changed.
- chunk_large_layers (default off) - split large fused layers (QKV, MLP) into chunks before SVD. Helps if a fused layer blows memory.
- skip_patterns / glob_skip_patterns - skip layers by regex (default) or glob.
- mismatch_mode -
skip/zeros/errorfor missing or incompatible text-encoder tensors. - lazy_load (default on), force_clear_cache (default off here), device - streaming, cache flushing, and CUDA-with-CPU-fallback.
Outputs
Just output_path - the node is a terminal/output node that reports where the DoRA file landed. It's an output-node, so nothing else is produced; the file is the deliverable.
When to pick Fixed
When you know the rank you want - you're matching a training recipe's dim, or you want every layer to have identical capacity, or you care most about a predictable final size. If you'd rather let the data decide rank per layer, the Frobenius (TextEncoderDoRAExtractFrobenius) and Knee (TextEncoderDoRAExtractKnee) variants from the same pack are the alternatives, and one of them is usually the better first try for a random pair of models.
Install
Part of Model Utility Toolkit (silveroxides/ComfyUI-ModelUtils). ComfyUI Manager → search "Model Utility Toolkit", or:
cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-ModelUtils
Restart ComfyUI. Real dependency: unifiedefficientloader (UEL). Keep ComfyUI current - the pack uses the newer extension API. The pack's extraction code acknowledges kohya-ss/sd-scripts and LyCORIS as its lineage, so if you've used those tools the workflow will feel familiar.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| model_a | COMBO | Finetuned Text Encoder model (A - B = LoRA) | |
| model_b | COMBO | Base Text Encoder model (A - B = LoRA) | |
| linear_dim | INT | 641–16384 | Rank for linear/attention layers |
| conv_dim | INT | 321–16384 | Rank for conv layers |
| svd_niter | INT | 20–10 | SVD power iterations |
| lazy_load | BOOLEAN | true | Low memory mode: load tensors from disk on demand |
| force_clear_cache | BOOLEAN | false | Clear CUDA cache after each layer; slower but useful under severe VRAM pressure. |
| 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 | Handle missing or incompatible text-encoder tensors by skipping them, substituting zeros where supported, or aborting. |
| output_filename | STRING | extracted_te_lora | Output filename without extension, written under the ComfyUI LoRA directory. |
| save_dtype | COMBO | fp16 | Data type used to save extracted text-encoder LoRA factors. |
| device | COMBO | cuda | Device used for per-layer extraction arithmetic; CUDA out-of-memory retries the affected layer on CPU where supported. |
| 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 | * | — |