SAELens

repository·main·Indexed 23 days ago

https://github.com/decoderesearch/saelens

A research library for training and analyzing Sparse Autoencoders (SAEs) to aid in mechanistic interpretability and AI safety. It features deep integration with TransformerLens via the HookedSAETransformer class and supports framework-agnostic inference with Hugging Face Transformers, NNsight, or PyTorch-based models. The library provides tools for loading pre-trained SAEs, custom architecture definition through TrainingSAE and SAE base classes, and automated deployment via Ansible playbooks for AWS.

Tokens
64.3K
Snippets
123
Records
219
Agent score
81%

What's inside sae-lens

  1. Overview of SAELens

    main

    SAELens is a library designed for researchers to train sparse autoencoders (SAEs) and perform mechanistic interpretability analysis. It supports training new SAEs and analyzing existing ones to generate insights for AI safety and alignment.

    Key capabilities include:

    • Training sparse autoencoders.
    • Analyzing SAEs to research mechanistic interpretability.
    • Deep integration with TransformerLens via the HookedSAETransformer class.
    • Framework-agnostic inference: While optimized for TransformerLens, SAEs can be used with Hugging Face Transformers, NNsight, or any PyTorch-based model by extracting activations and using the SAE's encode() and decode() methods.
  2. Summary of breaking changes in SAELens v6

    main

    The transition to v6 introduced several breaking changes to simplify the API and decouple LLM-specific code from SAE training:

    • Renamed Runner: SAETrainingRunner is now LanguageModelSAETrainingRunner (though the old name is temporarily supported).
    • Config Renaming:
      • JumpReLU L0 coefficient is now l0_coefficient.
      • TopK k is now an explicit parameter in the config.
    • Default Changes:
      • JumpReLU bandwidth default increased from 0.001 to 0.05.
      • JumpReLU starting threshold increased from 0.001 to 0.01.
    • Removed Options: Several legacy options were removed, including expansion_factor, hook_layer, ghost grads, b_dec init options, decoder init options, MSE loss normalization, decoder normalization, finetuning_tokens/finetuning_method, noise_scale, and activation_fn/activation_fn_kwargs.
    • Metadata Move: SAE.cfg now only contains essential keys. Non-essential keys like prepend_bos have been moved to SAE.cfg.metadata.
  3. What is SynthSAEBench?

    main

    SynthSAEBench is a benchmark for developing and comparing Sparse Autoencoder (SAE) architectures using synthetic data with known ground-truth features. Unlike LLM-based benchmarks that rely on noisy proxies (like reconstruction loss or downstream probes), SynthSAEBench allows for direct measurement of whether an SAE architecture successfully recovers specific, known features.

    It includes SynthSAEBench-16k, a standardized synthetic model with 16,384 ground-truth features, providing a controlled environment to test if architectures can recover sparse linear features.

  4. What is an ActivationGenerator in SAELens synthetic data?

    main

    An ActivationGenerator is used to sample sparse feature activations with controlled firing probabilities. This allows you to simulate different data distributions (like Zipfian or correlated features) to test how an SAE performs under various conditions.

    from sae_lens.synthetic import ActivationGenerator
    import torch
    
    firing_probs = torch.ones(16) * 0.25  # Each feature fires 25% of the time
    
    activation_gen = ActivationGenerator(
        num_features=16,
        firing_probabilities=firing_probs,
    )
    
    # Sample a batch of sparse feature activations
    feature_activations = activation_gen.sample(batch_size=1024)
  5. What is a FeatureDictionary in SAELens synthetic data?

    main

    A FeatureDictionary maps sparse feature activations to dense hidden activations. It acts as the ground truth for synthetic training by storing a matrix of feature vectors. It computes the relationship as hidden = features @ feature_vectors + bias.

    from sae_lens.synthetic import FeatureDictionary, orthogonal_initializer
    
    # Create dictionary with 16 features in 32-dimensional space
    feature_dict = FeatureDictionary(
        num_features=16,
        hidden_dim=32,
        initializer=orthogonal_initializer(),  # Makes features orthogonal
    )
  6. Use error terms with SAEs

    main

    To preserve the original model behavior while accessing SAE features, you can enable the error term. When sae.use_error_term = True, the SAE output is calculated as SAE(x) + error_term = x (where x is the original activation). This allows you to intervene on the error term (e.g., via hook_sae_error) without changing the model's output.

    sae.use_error_term = True
    model.add_sae(sae)
    
    # Output is now: SAE(x) + error_term = x (original activation)
    logits = model(tokens)
    
    # Intervene on the error term
    logits = model.run_with_hooks(
        tokens,
        fwd_hooks=[
            ("blocks.12.hook_resid_post.hook_sae_error", lambda act, hook: torch.zeros_like(act))
        ]
    )
  7. Shuffle sequential activations with `mixing_buffer`

    main

    When collecting activations sequentially (e.g., processing documents one at a time), consecutive activations are highly correlated, which can negatively impact training. The mixing_buffer utility shuffles activations to ensure each batch contains data from many different contexts.

    How it works:

    1. Accumulates activations until reaching buffer_size.
    2. Randomly shuffles the buffer.
    3. Yields half as batches while keeping the other half.
    4. Refills with new activations and repeats.

    Use mixing_buffer by passing a buffer_size, a batch_size, and an activations_loader (an iterator yielding tensors).

    from sae_lens.training.mixing_buffer import mixing_buffer
    from collections.abc import Iterator
    import torch
    
    def my_sequential_activations() -> Iterator[torch.Tensor]:
        """Yields activations in document order (correlated)."""
        while True:
            # Process a document and yield its activations
            yield torch.randn(1024, 768)  # (tokens_in_doc, d_in)
    
    # Wrap with mixing_buffer
    shuffled_provider = mixing_buffer(
        buffer_size=100_000,      # Total activations to buffer
        batch_size=4096,          # Output batch size
        activations_loader=my_sequential_activations(),
    )
    
    # Now batches are shuffled!
    for batch in shuffled_provider:
        print(batch.shape)  # (4096, 768)
        break
  8. Use SAEs with any PyTorch model or framework

    main

    SAELens SAEs are standard PyTorch modules. While they offer deep integration with TransformerLens via HookedSAETransformer, they are compatible with any framework that provides activation tensors of the correct shape.

    • Hugging Face Transformers: Extract activations using PyTorch hooks and pass them to sae.encode() or sae.decode().
    • nnsight: Use the nnsight tracing API for intervention patterns.
    • Generic PyTorch: Pass any tensor with the correct input dimension to the core methods.
    import torch
    from sae_lens import SAE
    
    sae = SAE.from_pretrained(
        release="gemma-scope-2b-pt-res-canonical",
        sae_id="layer_12/width_16k/canonical",
        device="cuda"
    )
    
    # SAEs work with any activation tensor of the right shape
    # Gemma 2 2B has d_model=2304
    activations = torch.randn(1, 128, 2304, device="cuda")  # From any source
    features = sae.encode(activations)
    reconstructed = sae.decode(features)
  9. Understand the SAE Architecture System

    main

    SAELens uses a modular architecture system that separates the training process from the inference process. To create a custom architecture, you must define three components:

    1. Configuration classes: Define hyperparameters and settings (inheriting from SAEConfig or TrainingSAEConfig).
    2. SAE classes: Implement the neural network logic (inheriting from SAE or TrainingSAE).
    3. Registration: Making the architecture available to the training system via register_sae_training_class.

    Training vs Inference SAEs

    • TrainingSAE: Used during the training phase to implement custom training logic (e.g., specialized loss functions).
    • SAE (Inference SAE): The final model saved after training, designed for deployment and efficient inference.

    Note that some training architectures might save as a different inference type. For example, BatchTopKTrainingSAE is saved as a JumpReLU SAE for inference.

  10. Import SAEs from other libraries

    main
    If you need to use an SAE created with a different library, you can implement a custom loader. You can write a PretrainedSaeHuggingfaceLoader for use with SAE.from_pretrained() or a PretrainedSaeDiskLoader for use with SAE.load_from_disk(). Refer to sae_lens/loading/pretrained_sae_loaders.py for implementation details.
  11. Implement a DataProvider for custom activations

    main

    A DataProvider is any iterator that yields activation tensors. For sequential data sources (like activations extracted from a model pass), it is highly recommended to wrap your iterator in mixing_buffer to improve training stability by shuffling activations.

    Requirements:

    • The iterator must yield tensors of shape (batch, d_in).
    • The train_batch_size_samples in SAETrainerConfig should match the batch size yielded by your data provider.
    from sae_lens.training.mixing_buffer import mixing_buffer
    
    # Wrap your activation iterator with mixing_buffer
    data_provider = mixing_buffer(
        buffer_size=50_000,
        batch_size=4096,
        activations_loader=your_activation_iterator,
    )
  12. Train multiple SAEs in parallel (Experimental)

    main

    The MultiSAETrainingRunner allows you to train a sweep of SAEs (e.g., different l1_coefficient values or different layers) simultaneously. The runner performs a single LLM forward pass per batch and multiplexes the resulting activations to all configured SAEs, which is more efficient than training them sequentially.

    Key Constraints (V1):

    • Does not support CLI/argparse, cached activations, or from_pretrained_path per entry.
    • compile_sae=True is not supported, but compile_llm=True is.
    • SAEs sharing a hook must agree on d_in and hook_head_index.

    Configuration:

    • saes: A dictionary mapping names to SAE configurations (e.g., StandardTrainingSAEConfig or TopKTrainingSAEConfig).
    • hook_names: A dictionary mapping SAE names to their specific hook points, or a single string if all SAEs share the same hook.

    Checkpointing: Checkpoints include per-SAE subdirectories. To resume, use resume_from_checkpoint=<checkpoint_dir> and ensure the keys in cfg.saes match the subdirectory names.

    from sae_lens import (
        MultiSAETrainingRunner,
        MultiSAETrainingRunnerConfig,
        StandardTrainingSAEConfig,
        TopKTrainingSAEConfig,
        LoggingConfig,
    )
    
    cfg = MultiSAETrainingRunnerConfig(
        saes={
            "h5_l1_low":  StandardTrainingSAEConfig(d_in=768, d_sae=16 * 1024, l1_coefficient=2.0),
            "h5_l1_high": StandardTrainingSAEConfig(d_in=768, d_sae=16 * 1024, l1_coefficient=5.0),
            "h10_topk":   TopKTrainingSAEConfig(d_in=768, d_sae=16 * 1024, k=64),
        },
        hook_names={
            "h5_l1_low":  "blocks.5.hook_resid_pre",
            "h5_l1_high": "blocks.5.hook_resid_pre",
            "h10_topk":   "blocks.10.hook_resid_pre",
        },
        # OR a single string when every SAE shares one hook:
        # hook_names="blocks.5.hook_resid_pre",
    
        model_name="gpt2",
        dataset_path="apollo-research/Skylion007-openwebtext-tokenizer-gpt2",
        training_tokens=int(1e8),
        train_batch_size_tokens=4096,
        output_path="output/sweep_run_1",
        logger=LoggingConfig(log_to_wandb=True, wandb_project="multi_sae_sweep"),
    )
    
    trained_saes = MultiSAETrainingRunner(cfg).run()  # dict[name, TrainingSAE]