CWB Embedding Self-Coalesce
Shrink an embedding by deleting its redundant rows
- cwb_config
- output_filename
- documentation
- cwb_report
Embedding files are already tiny - 10 to 80 KB of learned token vectors. So why would anyone want to shrink one? Because size was never the point. The CWB Embedding Self-Coalesce is a cleaner: it takes one embedding, finds the rows that are saying the same thing twice, and merges them into one. The file you get back is shorter, less redundant, and - if the technique holds up - a bit more focused than what you fed in.
What it's for
Textual inversions accumulate cruft. Merge a few embeddings (see its sibling node, the Multi-Merge), train an embedding for too many steps, or inherit one that's been edited by hand, and you end up with token rows that are near-duplicates: same concept, slightly different vector, both doing most of the same work. Every one of those redundant rows still costs attention and can muddy the concept. Coalescing folds each locally-similar cluster of rows down to a single representative, so the embedding keeps its meaning with fewer vectors. Think of it as running a tiny, purpose-built dedupe over the file.
Honest framing: this is an experimental node from a power-user toolkit, and its niche is small. For a normal embedding you'll never need it. You reach for it when you've just merged or trained something that feels bloated or mushy and you want to compact it before it goes into a shared workflow.
How it works
The node treats the embedding as a 2D tensor where each row is one token vector, then runs this loop:
- Normalize the rows and compute the pairwise cosine similarity between every row and every other row.
- Zero out any pair whose rows are too far apart in position (that's
coalesce_position_window- it keeps you from merging the start of the embedding with its end). - Keep only pairs that are mutual nearest neighbors - row A's closest match is B, and B's closest match is A - and whose similarity clears
coalesce_similarity_threshold. - Merge the best eligible pair into one vector using the CWB settings, insert it where the pair was, and repeat.
The loop stops when it hits target_vector_count or when no eligible pair remains. Set the target to 0 and it coalesces every pair it can, which is the "shrink as much as possible" mode.
The inputs that actually matter
- embedding - the file to coalesce. Everything else is fine-tuning.
- target_vector_count - 0 = coalesce everything eligible; set a number to stop at that many rows.
- coalesce_similarity_threshold (0.95 default) - how similar two rows must be before they're merged. This is your aggression dial. 0.9 shrinks harder but risks merging rows that were subtly different on purpose.
- coalesce_position_window (0.1 default) - how close in row position the pair must be. Leave it small unless you want a more aggressive global pass.
- vision_boundary_embeddings - off by default, and correct off for plain textual inversions. Only turn it on for complete encoded visual blocks, where the first and last rows are structural boundaries that must be preserved (the node validates them against
boundary_similarity_threshold). If you're not sure whether your embedding is one of these, it isn't. - legacy_boundary_search - requires vision mode and a
boundary_reference_embedding, and is for trimming accidental template rows from old files. Skip it unless you know you need it; it errors rather than guessing. - output_filename - name without extension, saved into ComfyUI's embeddings folder.
Outputs are output_filename, documentation, and cwb_report (the text log of which row pairs got merged).
Installing and using it
This node lives in the Model Utility Toolkit pack by silveroxides:
cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-ModelUtils
# restart ComfyUI
ComfyUI Manager users can search "Model Utility Toolkit" instead. The pack's dependencies (requests, Pillow, mutagen, unifiedefficientloader, av) get pulled by its requirements file; the coalesce nodes mainly lean on the unified loader for lazy_load streaming so big embedding tensors don't sit in RAM. Nothing extra to download.
Gotchas
- Same-encoder rule applies. Coalescing works within one embedding, so it can't fix an embedding that was already cross-encoder garbage - and merging the wrong families is how embeddings get ruined in the first place.
- It writes a new file, it doesn't modify the original. That's a feature: your source embedding stays untouched, so you can A/B the coalesced version against it.
- Watch the thresholds. Crank
coalesce_similarity_thresholdtoo low and you'll weld distinct concepts together; the default 0.95 is deliberately conservative.
Start with defaults, read the cwb_report output to see how many pairs it actually merged, and only then decide whether to push the similarity threshold down.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| embedding | COMBO | Embedding to coalesce. | |
| execution_mode | COMBO | MERGE | MERGE writes a new embedding. DOCUMENTATION ONLY opens no files. |
| cwb_preset | COMBO | balanced_sim_mean | Embedding CWB preset used for multi-input alignment and coalesced row pairs. |
| target_vector_count | INT | 00–100000 | Target body-row count. Zero coalesces every eligible pair; otherwise stops at this count or when no eligible pair remains. |
| coalesce_similarity_threshold | FLOAT | 0.950–1 | Minimum cosine similarity for a mutual-nearest body-row pair to coalesce. |
| coalesce_position_window | FLOAT | 0.100–1 | Maximum normalized row-position distance for an eligible pair. |
| vision_boundary_embeddings | BOOLEAN | false | Enable for complete encoded visual blocks. Start/end rows are validated and preserved; False treats the full tensor as open textual inversion. |
| legacy_boundary_search | BOOLEAN | false | Search and trim legacy template rows using Boundary Reference Embedding. Requires vision boundary mode and a selected reference. |
| boundary_reference_embedding | COMBO | None | Known-clean visual block used by legacy boundary search. Its first and last encoded rows define vision start/end. |
| boundary_similarity_threshold | FLOAT | 0.95-1–1 | Minimum encoded cosine similarity required independently for visual start and end validation. |
| output_filename | STRING | cwb_coalesced_embedding | Filename without extension under ComfyUI's embeddings category. |
| save_dtype | COMBO | fp32 | Requested dtype for generated floating embedding tensors. |
| process_device | COMBO | cuda | Per-tensor FP32 CWB arithmetic device; CUDA OOM retries that tensor on CPU. |
| lazy_load | BOOLEAN | true | Use bounded UEL streaming and release each tensor after its work unit. |
| force_clear_cache | BOOLEAN | true | Clear Python and CUDA caches before each tensor at a possible speed cost. |
| override_dtype | BOOLEAN | false | Force generated floating tensors to Save Dtype. |
| cwb_configopt | CWB_CONFIG | Optional complete CWB configuration override. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| output_filename | * | — |
| documentation | STRING | — |
| cwb_report | STRING | — |