Nodes/Concept Steer/Train Lens (SAE)
ComfyUI Node

Train Lens (SAE)

The interpretable lens — get the feature list, not just the vector

By Nynxz·Created 6 months ago·Updated 6 months ago· 4
Train Lens (SAE)
    • lens_path
    concept_namemy_concept
    positive_texts
    negative_texts
    layer22
    sae_expansion8
    sae_epochs200
    sae_features30
    n_prompts500
    refine_contrastivetrue
    contrastive_steps500
    sae_save_path
    sae_load_path
    transcoder_repo
    encoder_path
    output_dir
    protect_existingtrue

    The contrastive trainer gives you a direction that separates concepts, but it's opaque - a black box vector you have to trust. This node is the "show me your work" version. It trains a Sparse Autoencoder on the text encoder's residual stream, decomposes the concept into specific sparse features, and saves a lens whose metadata records exactly which feature indices define "cinematic" and by how much. It's slower - roughly five minutes instead of thirty seconds - but it's the pack's bridge into real mechanistic interpretability, the same SAE approach from Bricken et al. (Anthropic, 2023) that's been the backbone of LLM interpretability research.

    How it works

    Full pipeline, straight from the pack's HOW_IT_WORKS.md:

    1. Hook into the text encoder's residual stream at layer 22 (~60% depth - the point where syntactic processing gives way to semantic representation), and collect token-level activations from ~500 diverse prompts (~15k vectors).
    2. Train an SAE on those activations: 8× expansion on Qwen's 2560d gives 20,480 sparse features, with unit-norm decoder columns and L1 sparsity.
    3. Run your positive and negative concept texts through the model + SAE, compute mean(pos_features) - mean(neg_features), and take the top-K features by absolute difference.
    4. Reconstruct a direction from those features via bias-free decoding (decode_sparse).
    5. Optionally blend with a contrastive direction (on by default) for better separation.

    The payoff is that the lens carries sae_features in its metadata - indices like [4821, 12033, 7944, ...] - plus sanity metrics like how well the SAE direction matches the raw centroid difference. That's the thing contrastive lenses just don't give you.

    The inputs that matter

    • positive_texts / negative_texts - same discipline as contrastive: one per line, similar scenes without the concept for the negatives, 10+ pairs recommended.
    • layer - 22 by default. Earlier layers = lower-level features, later = more abstract. 22 is the documented sweet spot for style/aesthetics.
    • sae_expansion - 8 (20,480 features). Higher is more fine-grained but slower and a bigger file.
    • sae_features - how many top features go into the direction. 30 default; more = richer but noisier.
    • refine_contrastive - on by default; blends in a contrastive direction so you get interpretability and robust separation.
    • sae_save_path / sae_load_path - this is the good stuff. Train the SAE once, save it, and reuse it across concepts to skip the ~5-minute training step for every subsequent lens.
    • transcoder_repo - a HuggingFace repo for pretrained 64× transcoders (e.g. mwhanna/qwen3-4b-transcoders, 163,840 features trained on ~1B tokens). This is the shortcut: skip training a small SAE entirely and use a much larger, better-quality one. It downloads on first use.

    Output is lens_path - the absolute path to the saved lens, which also lands in lenses/ for the dropdown. The exact same training is available from the CLI with python tools/lens_factory.py sae <preset> (add --sae-save ./my_sae.pt to cache the SAE).

    Installing

    Standard for this pack - ComfyUI Manager search "Concept Steer", or clone https://github.com/Nynxz/ComfyUI-ConceptSteer into custom_nodes/ and restart. Training needs transformers and safetensors (pip install transformers safetensors) and ~10 GB VRAM for Qwen. No pretrained lenses ship with the pack - everything is generated locally.

    Common gotchas

    • Five minutes feels like forever on the first run because the SAE activation collection + training is the slow part. Save with sae_save_path and reuse it; subsequent lenses are ~30s.
    • The feature indices aren't human-readable yet. The README is direct about this: without a feature labeling pipeline, [4821, 12033, ...] is just numbers. That's what the Feature Dictionary / Feature Map / Feature Gate / Feature Probe nodes in the pack are for - this lens is one stop on a longer interpretability workflow.
    • Refine-contrastive is on by default for a reason. Turning it off gives you a purer SAE direction but weaker separation. Turn it off only if you specifically want the raw SAE vector.
    CategoryConcept Steer

    Inputs (16)

    NameTypeDefaultDescription
    concept_nameSTRINGmy_conceptName for the concept (used in filename)
    positive_textsSTRINGTexts that EMBODY the concept (one per line). 10+ pairs recommended for strong results.
    negative_textsSTRINGNeutral/opposite texts (one per line, same count as positive). Should describe SIMILAR scenes WITHOUT the concept.
    layerINT221–36Which transformer layer to hook into. Default 22 (~60% depth) is the sweet spot for concepts. Earlier layers = lower-level features, later = more abstract.
    sae_expansionINT82–16SAE hidden dimension multiplier. 8x (default) = 20,480 features for 2560d Qwen. Higher = more features but slower training.
    sae_epochsINT20050–1000SAE training epochs (default 200, more = better features)
    sae_featuresINT305–100Number of top SAE features to keep in the concept direction. More features = richer concept but noisier.
    n_promptsINT500100–2000Number of diverse prompts for SAE activation collection
    refine_contrastiveBOOLEANtrueAlso run contrastive optimization on output embeddings and blend with SAE direction. Recommended for best results.
    contrastive_stepsINT500500–20000Contrastive optimization steps (only used if refine_contrastive is enabled)
    sae_save_pathSTRINGSave the trained SAE to this path for reuse across concepts. Saves ~5min per subsequent concept.
    sae_load_pathSTRINGLoad a pre-trained SAE/transcoder instead of training a new one. Supports both native .pt SAE and .safetensors transcoder formats.
    transcoder_repoSTRINGHuggingFace repo for pretrained transcoders (e.g. 'mwhanna/qwen3-4b-transcoders'). Downloads a 64x expansion transcoder with 163,840 features trained on ~1B tokens. Much better than training a small SAE. Leave empty to train your own SAE.
    encoder_pathSTRINGPath to Qwen 3.4B safetensors file. Leave empty to use QWEN_ENCODER_PATH env var.
    output_dirSTRINGOverride output directory for the lens file
    protect_existingBOOLEANtrueIf the output lens file already exists, save as _v2, _v3, … instead of overwriting. Disable only when intentionally replacing.

    Outputs (1)

    NameTypeDescription
    lens_pathSTRING