Open Concept Steering is an open-source library for discovering and manipulating interpretable features in large language models using Sparse Autoencoders (SAEs). Inspired by Anthropic's work on Scaling Monosemanticity and Golden Gate Claude, this project aims to make concept steering accessible to the broader research community.
Right now, this repository implements Sparse Autoencoder (SAE) feature steering for OLMo 2 7B, allowing for controlled concept amplification in model outputs. The project demonstrates three steering vectors (Batman/Bruce Wayne, Japan, and Baseball) and provides tools to discover and experiment with more features.
Live Demo: If you just want to chat with Batman OLMo, check out the HuggingFace Space.
Blog Post: For a more full discussion of motivations and musings, see the blog post.
- Pre-trained Models & Datasets
- System Requirements
- Installation
- Quick Start
- Usage
- Project Structure
- Configuration Files
- License
- Acknowledgments
Pre-trained SAE: The weights of the 65k feature SAE can be found on Hugging Face.
Dataset: ~600 million residual streams extracted from OLMo-2-7B on FineWeb, available on Hugging Face.
- GPU: NVIDIA GPU with 16GB+ VRAM (e.g., RTX 4090, A100)
- RAM: 32GB+ system RAM
- Storage: 10GB+ free space
- CUDA: 11.8 or higher
- GPU: NVIDIA GPU with 24GB+ VRAM (e.g., RTX 4090, A100)
- RAM: 64GB+ system RAM
- Storage: 100GB+ free space (for datasets)
- CUDA: 12.0 or higher
- Python 3.10+
- PyTorch 2.0+
- CUDA-compatible GPU drivers
- Clone the repository:
git clone https://github.com/hbfreed/open-concept-steering.git
cd open-concept-steering- Create a virtual environment (recommended):
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Set up environment variables (optional):
cp .env.example .env
# Edit .env with your API keys (if using synthetic data generation)import torch
import json
from model import SAE
from transformers import AutoModelForCausalLM, AutoTokenizer
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
device = "cuda" if torch.cuda.is_available() else "cpu"
# Load OLMo-2
model = AutoModelForCausalLM.from_pretrained(
"allenai/OLMo-2-1124-7B-Instruct",
torch_dtype=torch.bfloat16
).to(device)
tokenizer = AutoTokenizer.from_pretrained("allenai/OLMo-2-1124-7B-Instruct")
# Load pre-trained SAE from HuggingFace Hub
sae_path = hf_hub_download(
repo_id="open-concept-steering/olmo2-7b-sae-65k-v1",
filename="sae_weights.safetensors"
)
config_path = hf_hub_download(
repo_id="open-concept-steering/olmo2-7b-sae-65k-v1",
filename="sae_config.json"
)
state_dict = load_file(sae_path)
with open(config_path) as f:
config = json.load(f)
sae = SAE(config['input_size'], config['hidden_size']).to(device).to(torch.bfloat16)
sae.load_state_dict(state_dict)
# Now you can use the SAE for analysis or steering!python train.py \
--data_dir /path/to/your/data \
--out_dir out/sae_test \
--hidden_size 8192 \
--batch_size 4096 \
--num_epochs 1 \
--lambda_final 5.0First, collect residual stream activations from your model:
python collect_dataset.pyNote: You'll need to edit the script to set your desired:
model_name: Which model to extract fromtarget_vectors: How many residual stream vectors to collectout_dir: Where to save the data
This will save data as .parquet files.
You can use a pre-defined config:
python train.py --config_path config/train_sae_8k.pyOr specify parameters directly:
python train.py \
--data_dir /path/to/parquet/files \
--out_dir out/my_sae \
--input_size 4096 \
--hidden_size 65536 \
--batch_size 4096 \
--learning_rate 5e-5 \
--num_epochs 1 \
--lambda_final 20 \
--wandb_project my-sae-trainingKey hyperparameters:
hidden_size: Number of SAE features (8k, 16k, 65k, etc.)lambda_final: Sparsity penalty (higher = sparser features)batch_size: Larger is better for stability (if VRAM allows)
If using Weights & Biases:
wandb login
# Then training logs will appear in your W&B dashboardFrom HuggingFace Hub (recommended):
import torch
import json
from model import SAE
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
device = "cuda"
# Download from HuggingFace Hub
sae_path = hf_hub_download(
repo_id="open-concept-steering/olmo2-7b-sae-65k-v1",
filename="sae_weights.safetensors"
)
config_path = hf_hub_download(
repo_id="open-concept-steering/olmo2-7b-sae-65k-v1",
filename="sae_config.json"
)
# Load weights and config
state_dict = load_file(sae_path)
with open(config_path) as f:
config = json.load(f)
# Initialize and load
sae = SAE(config['input_size'], config['hidden_size']).to(device).to(torch.bfloat16)
sae.load_state_dict(state_dict)
sae.eval()
print(f"Loaded SAE with {config['hidden_size']} features")From local checkpoint:
import torch
from model import SAE
device = "cuda"
sae_path = "out/sae_65k/sae_final.pt"
# Load checkpoint
state_dict, config = torch.load(sae_path, map_location=device).values()
# Fix compiled model keys (if present)
fixed_state_dict = {k.replace('_orig_mod.', ''): v for k, v in state_dict.items()}
# Initialize and load
sae = SAE(config['input_size'], config['hidden_size']).to(device).to(torch.bfloat16)
sae.load_state_dict(fixed_state_dict)
sae.eval()
print(f"Loaded SAE with {config['hidden_size']} features")See find_features.ipynb for a full example. Here's a simplified version:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from model import SAE
device = "cuda"
# Load model
model = AutoModelForCausalLM.from_pretrained(
"allenai/OLMo-2-1124-7B-Instruct",
torch_dtype=torch.bfloat16
).to(device)
tokenizer = AutoTokenizer.from_pretrained("allenai/OLMo-2-1124-7B-Instruct")
# Load SAE (as shown above)
sae = ...
# Identify interesting features (e.g., feature 758 = Batman/superhero)
# You can discover features using the notebooks or pre-computed labels
# Apply steering (simplified - see notebooks for full implementation)
def generate_with_steering(prompt, feature_idx, strength):
# Hook into model to apply SAE steering at specific layer
# See find_features.ipynb for complete implementation
pass
# Example: Amplify "Batman" feature
response = generate_with_steering(
"What should I do with $5?",
feature_idx=758,
strength=10.0
)
print(response) # Should have Batman-themed response!open-concept-steering/
├── config/ # Training configuration files
│ ├── train_sae_8k.py
│ ├── train_sae_65k.py
│ └── ...
├── data/ # Dataset storage (not tracked)
├── out/ # Training outputs (not tracked)
├── results_65k.../ # Analysis results for 65k SAE
├── collect_dataset.py # Collect residual streams
├── train.py # Train an SAE
├── model.py # SAE model definition
├── sae_tracker.py # Training metrics tracker
├── synthetic_data_generation.py # Generate synthetic test data
├── dev.ipynb # Development notebook
├── find_features.ipynb # Feature discovery and steering
├── run_training.sh # Multi-GPU training script
├── requirements.txt # Python dependencies
└── README.md
The config/ directory contains pre-defined training configurations:
| Config File | Features | Lambda | Use Case |
|---|---|---|---|
train_sae_8k.py |
8,192 | 0 | Quick testing, experimentation |
train_sae_16k.py |
16,384 | varies | Small-scale features |
train_sae_32k.py |
32,768 | varies | Medium-scale features |
train_sae_65k.py |
65,536 | 20 | Production-quality features |
train_sae_131k.py |
131,072 | varies | Large-scale features |
train_sae_262k.py |
262,144 | varies | Very large features |
train_sae_524k.py |
524,288 | varies | Massive scale |
train_sae_1m.py |
1,048,576 | varies | Experimental scale |
General guidelines:
- More features = better resolution but more compute
- Start with 8k-16k for testing
- Use 65k+ for research/production
- Lambda controls sparsity (0 = dense, 20+ = very sparse)
Note: All configs have hardcoded paths (/media/henry/MoreFiles/olmo_dataset). Update data_dir and out_dir for your setup.
Use the provided script:
./run_training.shEdit the script to specify which configs to run.
If you run out of VRAM:
- Reduce
batch_size - Use fewer
num_workersin DataLoader - Enable gradient checkpointing (requires code modification)
- Train an SAE
- Run
find_features.ipynbto analyze top activating tokens - Use the OpenAI API integration to auto-label features
- Experiment with steering different features!
This project is licensed under the MIT License.
This project is based directly upon the work described in "Scaling Monosemanticity: Extracting Interpretable Features from Claude 3 Sonnet" by Anthropic, as well as the preceding papers:
Special thanks to:
- The Allen Institute for AI for OLMo 2
- Anthropic for pioneering SAE interpretability research
- The open-source ML community
If you use this work in your research, please cite:
@misc{open-concept-steering,
author = {Henry Freed},
title = {Open Concept Steering: SAE-based Feature Steering for OLMo 2},
year = {2025},
publisher = {GitHub},
url = {https://github.com/hbfreed/open-concept-steering}
}Contributions are welcome! Please feel free to submit a Pull Request.
For questions or feedback, please open an issue on GitHub.