Feature Map
See which interpretable features your prompt actually activates
- conditioning
- VISUALIZATION
- FEATURES
The debugger of the pack. Feature Map takes your conditioning - whatever your text encoder produced for a prompt - runs it through a trained SAE, and shows you which sparse features are actually active and how strongly. The prompt "a moody city street at night" isn't a black box anymore: it's a ranked list of feature indices with activations, and if you built a Feature Dictionary, those indices come back labeled with what they respond to. This is where you stop guessing what your prompt is "really" doing and start looking at it.
How it works
Conditioning in ComfyUI is a tensor of token embeddings; the SAE lives in the text encoder's residual-stream space. The node encodes each token's activation through the SAE to get a sparse feature vector, then ranks features by activation and renders a visualization plus a string list. Two things to keep in mind:
- The SAE decomposes a specific layer of the encoder. Your
layerandsae_expansionmust match the SAE you trained - mix and match and you get noise. - The conditioning tensor is post-encoder output, so there's a space question. The node handles it via
pool_mode:per_token(default, encodes each token individually, matches SAE training),mean(average tokens then encode - faster, less accurate), ormax. For inspection work,per_tokenis the honest one.
That FEATURES output is the whole point: it's a paste-ready list of indices you feed straight into Feature Gate (to suppress or amplify them) or Feature Probe (to visualize a single one). The workflow the pack intends: Map → discover → Gate.
The inputs that matter
- conditioning - from CLIP Text Encode / your text encoder.
- sae_path - your trained SAE
.pt(or leave empty and usetranscoder_repo). - top_k - how many top features to show. 30 default; raise to 100 if you want to see the long tail.
- sae_expansion - 8 for trained SAEs; auto-detected as 64 for pretrained transcoders.
- transcoder_repo - a HuggingFace repo of pretrained 64× transcoders (e.g.
mwhanna/qwen3-4b-transcoders). This is the "skip training your own SAE" path - it auto-downloads ~1.7 GB per layer on first use, so don't set it on a whim on a metered connection. - pool_mode -
per_tokenrecommended. - dict_path - the Feature Dictionary JSON, if you want labels next to the indices.
Outputs: VISUALIZATION (IMAGE) and FEATURES (STRING, the indices to paste elsewhere).
Installing
Pack-standard: ComfyUI Manager search "Concept Steer", or git clone https://github.com/Nynxz/ComfyUI-ConceptSteer into custom_nodes/ and restart. You need a trained SAE before any of this is useful, plus transformers/safetensors. No pretrained SAE ships with the pack.
Common gotchas
- Wrong SAE = confident garbage. Layer 22 SAE on layer 30 conditioning produces a pretty chart of meaningless indices. Keep layer/expansion consistent everywhere.
- Labeled output requires the dict_path wiring. Bare indices are the default; remember to pipe the Feature Dictionary JSON in.
meanpooling hides per-token structure. If two tokens in your prompt are doing very different things, mean-pooling blurs them together. Start withper_tokenbefore optimizing for speed.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| conditioning | CONDITIONING | — | |
| sae_path | STRING | Absolute path to SAE weights (.pt file). Train one with 'Train Lens (SAE)' using --sae-save, or from the lens_factory CLI. | |
| top_k | INT | 305–100 | Number of top features to display |
| sae_expansion | INT | 82–128 | SAE expansion factor (auto-detected for transcoders). 8x for trained SAEs, 64x for pretrained transcoders. |
| transcoder_repo | STRING | HuggingFace repo for pretrained transcoders (e.g. 'mwhanna/qwen3-4b-transcoders'). Auto-downloads ~1.7GB layer file on first use. Leave empty to use sae_path instead. | |
| pool_mode | COMBO | per_token | How to handle token activations before SAE encoding. 'per_token' = encode each token individually (matches SAE training), 'mean' = average tokens then encode (fast but less accurate), 'max' = max activation per feature across tokens |
| dict_path | STRING | Path to feature dictionary JSON (from Feature Dictionary node). When provided, features are labeled with what they respond to. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| VISUALIZATION | IMAGE | — |
| FEATURES | STRING | — |