PQHSGS/SteerBench

Python

0

0 commits

updated Aug 18, 2026

See the code

README

SAESteeringBench

A unified framework for steering vector extraction, application, and evaluation across 30+ methods.

Python 3.10+ License: MIT


Table of Contents


Installation

Prerequisites

  • Python: 3.10 or higher
  • CUDA: 11.8+ (for GPU acceleration)
  • GPU: NVIDIA GPU with 16GB+ VRAM recommended (for Gemma-2-2B)

Step 1: Clone the Repository

git clone https://github.com/PQHSGS/SteerBench.git
cd SteerBench

Step 2: Create Environment

Using Conda (Recommended):

conda create -n sae-steering python=3.11
conda activate sae-steering

Using venv:

python -m venv .venv
source .venv/bin/activate  # Linux/Mac
# or: .venv\Scripts\activate  # Windows

Step 3: Install Dependencies

Using pip with requirements.txt (Recommended):

pip install -r requirements.txt

Or install manually:

# Core dependencies
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# ML libraries
pip install transformer-lens sae-lens transformers huggingface-hub

# Utilities
pip install numpy pandas scikit-learn tqdm pyyaml

# Development (optional)
pip install pytest pytest-cov black isort mypy

Step 4: Authenticate with HuggingFace

# Set your token (required for gated models like Gemma)
export HF_TOKEN="your_huggingface_token"

# Or login interactively
huggingface-cli login

Step 5: Verify Installation

python -c "from Steering import SteeringPipeline; print('✓ Installation successful')"

Quick Start

Option 1: Config-Based CLI

# Run steering generation/extraction with a config file
python -m Steering.cli --task run --config Configs/Eval/caa_example.json

# Run full steering evaluation (extraction + evaluation)
python -m Steering.cli --task eval --config Configs/Eval/caa_example.json

# Quiet mode (minimal output)
python -m Steering.cli --task eval --config Configs/Eval/caa_example.json --quiet

Option 2: Python API

from Steering import SteeringPipeline, PipelineConfig

# From config file
config = PipelineConfig.load("Configs/Eval/caa_example.json")
pipeline = SteeringPipeline.from_config(config)
result = pipeline.evaluate_from_config(config)

# Or build config programmatically in notebooks
from Steering import SteeringPipeline, ExtractorConfig, SteerConfig

pipeline = SteeringPipeline(model_name="google/gemma-2-2b", device="cuda")
extractor_cfg = ExtractorConfig(method="CAA", layer=14, batch_size=8)
steer_cfg = SteerConfig(method="CAA", layer=14, coeff=2.0)

result = pipeline.evaluate(
    extractor_config=extractor_cfg,
    steer_config=steer_cfg,
    train_dataset="sycophancy",
    test_dataset="csqa",
)

Supported Methods

MethodTypeDescription
CAADenseContrastive Activation Addition — mean-difference vector added to residual stream
COLDDenseIn-context One-step Learning Dynamics — finite-difference or kernel variant
CAST / CAST_HFDenseConditional Activation Steering — steers only when condition similarity threshold is met
MANIFOLDDenseManifold-based steering for reducing overthinking behaviors
SPHERICALDensevMF-gated spherical steering with kappa/alpha/beta hyperparameters
SAE-FREEDenseEigendecomposition of activation differences (uses SAE feature space, steers dense)
ACTTransportActivation Transport / Mean-AcT — affine transport of residual activation distributions
ANGULARTransport2D rotation-based steering with automatic layer selection via cosine similarity
CURVEBALLTransportPolynomial Kernel-PCA nonlinear steering with pre-image reconstruction
FLOW / FLOWGLPTransportFlow Matching transport from source to target residual states
PIDTransportLayerwise PID controller over contrastive activation error signals
ODETransportKernel classifier ODE steering (normed-poly / RBF)
BIPOTransportBilateral Preference Optimization steering
CHARSTransportConcept Heterogeneity-Aware Representation Steering
LinNEASTransportLinearized Non-linear End-to-end Activation Steering
REFT / LOREFT / REPSParametricLow-rank residual fine-tuning / representation engineering
FLASParametricFlow-based Activation Steering — concept-conditioned velocity field with Euler integration
WEIGHTSTEERWeightContrastive LoRA weight steering
CORRSTEERSAEGeneration-time correlation between SAE features and behavior
SASSAEContrastive SAE activation differencing
SPARESAEMutual information feature selection with SAE reconstruction
SRESAESparse Representation Engineering for targeted feature manipulation
SRPSSAESemantic token masking + SAE with beta parameter for activation balancing
SSVSAETwo-stage ANOVA + linear classifier refinement + optimization
SAE-RSVSAEMulti-component (α1, α2, α3) residual steering vector
SAE-TSSAELinearAdapter trained on feature effects
SAEIOSAEInput/Output score filtering: logit lens + amplification test per feature
SAE-COTSAEGT SAE latent feature overwrite steering
FEATSAEDirect feature list manipulation in SAE latent space
FGAASAEFeature-Guided Activation Additions with density threshold

Dense methods modify residual stream activations directly. Transport methods apply learned or analytic maps (OT, flow, rotation, PCA) to move activations. Parametric methods train a lightweight module (LoRA, flow network) during extraction. SAE methods require a Sparse Autoencoder (currently only available for Gemma models).

Storage constraint for FLAS and REPS: These methods produce very large checkpoints (several GB). All runs must overwrite a single unified path per method to avoid filling disk:

  • FLAS: Vector/FLAS/gemma_gms8k
  • REPS: Vector/REPS/gemma_reps_gms8k

Type Contracts

All extractors and steer models use strict dict-based types for multi-layer support:

ParameterTypeDescription
layerList[int]Target layer(s)
steering_vectorDict[int, Tensor]Steering vector keyed by layer
coeffDict[int, float]Steering coefficient keyed by layer
saeDict[int, SAE]Sparse autoencoder keyed by layer

Method-Specific Notes

  • CAST: Requires separate conditional_dataset and conditional_layer for condition detection
  • ANGULAR: Uses auto_select_layer=true to auto-select optimal layer via cosine similarity
  • ACT: Uses Code/AcT as the ground-truth surface. Code/PID/Mean-AcT overlaps with ACT and adds PID-specific behavior, so PID-specific Mean-AcT changes are tracked under PID.
  • CURVEBALL and FLOW: Implement extraction and steering inference only, without demos or paper experiment harnesses.
  • PID: LLM-only implementation. Diffusion paths in Code/PID/Mean-AcT are reference context and are not part of Steering inference.
  • SRPS: Uses beta parameter in extractor (not steer) to balance activation vs active dimensions
  • FLAS: Uses HF decoder-layer module hooks (not TransformerLens). SteerConfig.coeff maps to flow time T. Requires flas_concept_text at steering time.

Pareto Frontier Analysis

A central contribution of SAESteeringBench is evaluating the fundamental Pareto trade-off in representation steering: Steering Efficacy (Task Accuracy / Target Alignment) versus Language Modeling Coherence (Perplexity & Non-Degradation).

Aggressive steering often increases target concept expression at the expense of pushing residual representations off the model's manifold, leading to catastrophic repetition or incoherence. The Pareto frontier identifies methods that achieve maximum steering with minimal distribution distortion.

Cross-Method Pareto Frontiers

Below are benchmark Pareto frontiers comparing Dense, Transport, and SAE Latent steering techniques across core safety and behavior tasks:

Overall Multi-Method BenchmarkSafety & Refusal Steering Frontier
Pareto Benchmark FrontierRefusal Open Pareto Comparison
Perplexity Cluster Analysis (Top Performers)Transport Ablation (AcT vs. CHaRS)
Refusal Open Cluster ModeAcT and CHaRS Ablation

Key Benchmark Takeaways

  1. Dense vs. Transport Stability: Standard linear addition (CAA, COLD) exhibits steep trade-off curves where higher coefficients rapidly elevate perplexity. In contrast, optimal transport and manifold-aware methods (ACT, CHARS, COBRA) maintain tighter perplexity bounds.
  2. SAE Feature Selection: SAE-based approaches (SRPS, SPARE, SAS) provide fine-grained steering along interpretable directions, with feature balancing preventing severe degradation.
  3. Reproducing Pareto Plots: You can generate and customize all Pareto frontier curves using pareto.py:
    # Compare methods across safety datasets
    python pareto.py --mode methods --datasets Refusal_open,SorryBench --methods-topk 15 --output-dir Pareto
    
    # Cluster analysis by perplexity intervals
    python pareto.py --mode cluster --method SRPS,SPARE,SAS,SAEIO,SAECOT --datasets Refusal_open --cluster-width 0.5 --output-dir Pareto
    
    For full CLI options, see Pareto_CLI_Guide.md.

Configuration

Config File Structure

{
  "name": "my_experiment",
  "model": {
    "name": "google/gemma-2-2b",
    "device": "cuda",
    "dtype": "bfloat16"
  },
  "extractor": {
    "method": "CAA",
    "layer": 14,
    "batch_size": 4
  },
  "steer": {
    "method": "CAA",
    "layer": 14,
    "coeff": 1.00
  },
  "train_dataset": "sycophancy",
  "test_dataset": "csqa",
  "n_train": 100,
  "n_test": 200
}

Available Datasets

Training (for vector extraction):

  • sycophancy, sycophancy_philosophy, sycophancy_political
  • refusal, hallucination
  • ai_risk_coordinate, ai_risk_corrigible, ai_risk_myopic, ai_risk_survival

Testing (for evaluation):

  • csqa - CommonsenseQA (multiple choice)
  • gms8k - Grade School Math (math)
  • refusal - SorryBench (refusal detection)

Example Configs

Configs/Eval/
├── angular_refusal.json    # Angular steering for refusal
├── caa_example.json        # Basic CAA setup
├── cast_refusal.json       # Conditional steering
├── minimal_example.json    # Minimal required fields
├── sas_hallucination.json  # SAS for hallucination
├── spare_sycophancy.json   # SAE-based SPARE
├── sre_sycophancy.json     # SRE for sycophancy
├── srps_sycophancy.json    # SRPS for sycophancy
├── ssv_ai_risk.json        # SSV for AI safety
├── LinearAcT/              # Activation Transport variant
├── PID/                    # PID steering
├── FLOW/                   # FlowSteer configs
├── CURVE/                  # Curveball configs
└── TruthFlow/              # TruthFlow configs

Python API

Basic Usage

from Steering import SteeringPipeline, PipelineConfig

# From config file
config = PipelineConfig.load("Configs/caa_example.json")
pipeline = SteeringPipeline.from_config(config)
results = pipeline.run()

Using Factory Functions

from Steering import SteeringPipeline

# List all available methods
print(SteeringPipeline.list_methods())  # ['CAA', 'CAST', 'ANGULAR', ...]

# Create extractor
extractor = SteeringPipeline.create_extractor("CAA", model, layer=14)
vector = extractor.extract(target_prompts, contrast_prompts)

# Create steer model
steer_model = SteeringPipeline.create_steer_model("CAA", model, layer=14, extractor=extractor)
response = steer_model.generate("Your prompt here", coeff=2.0)

Direct Class Usage

from Steering.extractors import CAAExtractor
from Steering.steer_models import DenseSteerModel

# Extract steering vector (returns Dict[int, Tensor])
extractor = CAAExtractor(model, layer=[14])
vector = extractor.extract(target_data, contrast_data)

# Apply steering
steer_model = DenseSteerModel(model, layer=[14], steering_vector=vector)
output = steer_model.generate("Hello!", coeff={14: 2.0})

Project Structure

SAESteeringBench/
├── Steering/               # Core steering framework (the main library)
│   ├── __init__.py         # Public API exports & factory functions
│   ├── pipeline.py         # SteeringPipeline — orchestrates extract → steer → evaluate
│   ├── base.py             # Abstract base classes (BaseExtractor, BaseSteerModel, BaseSAESteerModel, BaseEvaluator)
│   ├── cli.py              # CLI entrypoint (python -m Steering.cli)
│   ├── utils.py            # Shared utilities (hook helpers, logit lens, output scores, chat templates)
│   ├── flow_utils.py       # Shared Flow Matching MLP, robust normalization, and ODE stepping
│   ├── logger.py           # Colored logging system
│   ├── exceptions.py       # Custom exception hierarchy
│   ├── IMPLEMENTATION_NOTES.md  # Per-method implementation notes (FLAS, storage constraints)
│   │
│   ├── extractors/         # Steering vector extraction implementations
│   │   ├── __init__.py     # EXTRACTOR_MAP registry
│   │   ├── dense.py        # Dense extractors (CAA, COLD, CAST, Manifold, Spherical)
│   │   ├── nonlinear.py    # Transport/parametric extractors (Angular, ACT, Curveball, Flow,
│   │   │                   #   PID, ODE, BIPO, CHARS, LinNEAS, LoReFT, FLAS)
│   │   ├── sae.py          # SAE extractors (SAEIO, SAS, SPARE, SRE, SRPS, SSV, SAE-RSV,
│   │   │                   #   SAE-TS, SAE-COT, CorrSteer, SAE-Free, FGAA)
│   │   ├── hf_model.py     # HFCASTExtractor (HuggingFace-based CAST)
│   │   └── weight.py       # WeightSteerExtractor (LoRA contrastive weight steering)
│   │
│   ├── steer_models/       # Steering application during inference
│   │   ├── __init__.py     # STEER_MAP registry
│   │   ├── dense.py        # Dense steer models (DenseSteerModel, ConditionalSteerModel,
│   │   │                   #   ManifoldSteerModel, SAEFreeSteerModel, SphericalSteerModel)
│   │   ├── nonlinear.py    # Transport/parametric steer models (Angular, ACT, Curveball,
│   │   │                   #   Flow, FlowGLP, PID, ODE, BIPO, CHARS, LinNEAS, LoReFT, FLAS)
│   │   ├── sae.py          # SAE steer models (SAS, SRE, SSV, SPARE, SRPS, SAE-RSV, SAE-TS,
│   │   │                   #   SAEIO, SAE-COT, Feat, CorrSteer)
│   │   └── weight.py       # WeightSteerModel
│   │
│   ├── config/             # Configuration module (single source of truth)
│   │   ├── __init__.py     # Exports all config components
│   │   ├── models.py       # ModelConfig, MODEL_SAE_REGISTRY (Gemma-2, Llama-2/3)
│   │   ├── methods.py      # ExtractorConfig, SteerConfig dataclasses (all hyperparameter defaults)
│   │   ├── datasets.py     # TrainDatasetConfig, TestDatasetConfig, path roots
│   │   ├── pipeline.py     # PipelineConfig (top-level experiment config)
│   │   ├── post_process.py # PostProcessConfig (GLP denoising + classifier guidance)
│   │   └── results.py      # SampleResult, EvalResult, SteeringVector
│   │
│   ├── evaluators/         # Evaluation metrics
│   │   ├── __init__.py     # EVALUATOR_MAP registry (13 evaluator types)
│   │   ├── scoring_metrics.py  # MultipleChoiceMatcher, MathMatcher, BehaviorMatcher,
│   │   │                       #   RefusalMatcher, SemanticMatcher, LogitMatcher, CastRefusalMatcher
│   │   └── model_capabilities.py  # PerplexityMatcher, CoherenceMatcher
│   │
│   ├── data/               # Data loading & formatting
│   │   ├── __init__.py     # Exports DataLoader, EvalDataLoader
│   │   ├── loader.py       # DataLoader (train), EvalDataLoader (test)
│   │   ├── readers.py      # File readers (JSON, JSONL, CSV)
│   │   ├── formatters.py   # Schema formatters (binary_choice, CAST_condition, etc.)
│   │   └── data_registry.py  # TRAIN_DATASET_REGISTRY, TEST_DATASET_REGISTRY,
│   │                         #   COMPOSITE_DATASET_REGISTRY
│   │
│   ├── post_process/       # GLP post-processing pipeline
│   │   ├── __init__.py
│   │   ├── cli.py          # CLI: train-classifier, run post-processing
│   │   ├── hook.py         # GLPPostProcessor — attaches GLP denoising hooks
│   │   ├── glp.py          # GLP manifold denoiser model
│   │   ├── classifier.py   # ConceptClassifier for guidance
│   │   ├── flow_matching.py
│   │   ├── subspace.py     # Subspace GLP variant
│   │   ├── train_classifier.py
│   │   └── train_stream.py
│   │
│   ├── finetune/           # LoRA fine-tuning support
│   │   ├── __init__.py
│   │   ├── cli.py
│   │   ├── trainer.py
│   │   ├── config.py
│   │   ├── lora.py
│   │   └── registry.py
│   │
│   └── tests/              # Unit test suite
│       ├── conftest.py     # Pytest fixtures
│       ├── test_config.py, test_data_loader.py, test_factory.py, ...
│       └── unit/           # Granular unit tests
│
├── Verification/           # Ground-truth validation (Level 1 extraction, Level 2 steering)
│   ├── shared_utils.py     # Shared utilities (load_sycophancy_data, load_model_and_sae, get_sae_activations)
│   ├── task.md             # Verification status tracker
│   ├── Level1/             # Extraction match (vector cosine similarity)
│   │   ├── Angular/, CAA/, CAST/, CorrSteer/, SAEIO/, SAESSV/, SAETS/, SPARE/
│   └── Level2/             # Inference match (steered logit comparison)
│       ├── Angular/, CAA/, CAST/, CorrSteer/, SAEIO/, SAESSV/, SAETS/, SPARE/
│
├── Configs/                # Experiment configuration files (JSON)
│   ├── Eval/               # Evaluation configs (end-to-end experiments)
│   └── Inference/          # Inference-only configs (generation)
│
├── TrainDataset/           # Training datasets (for vector extraction)
│   ├── behaviour/          # sycophancy, refusal, hallucination, advanced-ai-risk, politic, ...
│   ├── instruction_following/
│   ├── QA/                 # CSQA, SimpleQA, NQSwap
│   └── reasoning/          # GMS8K, SVAMP
│
├── TestDataset/            # Test datasets (for evaluation)
│   ├── behaviour/          # sycophancy, coordinate, corrigible, survival, myopic
│   ├── hallucination/      # TruthfulQA, HaluEval
│   ├── mmlu/
│   ├── QA/
│   ├── reasoning/          # GMS8K, SVAMP test splits
│   └── refusal/            # AdvBench, XSTest, HarmBench, DoNotAnswer, SorryBench
│
├── Code/                   # Reference implementations (ground truth)
│   ├── angular-steering/, CAA/, CAST/, AcT/, PID/, CorrSteer/
│   ├── SAE-free/, SAE-SSV/, SAE-TS/, SPARE/
│   └── saes-are-good-for-steering/
│
├── Vector/                 # Pre-extracted steering vectors (folder-per-vector)
│   ├── Angular/, CAA/, CAST/, SPARE/, SSV/
│   ├── FLAS/gemma_gms8k/        # ← FLAS unified path (overwrite only)
│   └── REPS/gemma_reps_gms8k/   # ← REPS unified path (overwrite only)
│
├── Verification_Results/   # Saved verification artifacts
│   ├── *.pt                # Saved vectors/adapters for reproducibility
│   └── Angular Log/
│
├── Results/                # Experiment evaluation results
│   ├── full_side_by_side_comparison.csv   # All methods × tasks × sole vs OT
│   ├── matched_pairs_clean.csv            # Paired sole vs OT with improvement delta
│   ├── experiments_summary_*.csv          # Per-method summaries
│   └── <method>/                          # Per-method result directories
│
├── Experiment/             # Legacy experiment configs and outputs
│   ├── CorrSteer/, SAE-SSV/
│
├── Experiments/            # Experiment scripts + results for tracking & replication
│
├── Notebooks/              # Interactive Jupyter notebooks (per method)
│   ├── Angular.ipynb, CAA.ipynb, CAST.ipynb, CorrSteer.ipynb,
│   │   Manifold.ipynb, SAE_RSV.ipynb, SAE_SSV.ipynb, SAE_TS.ipynb,
│   │   SAS.ipynb, SPARE.ipynb, SRE.ipynb, SRPS.ipynb,
│   │   EvaluationDemo.ipynb, playground.ipynb
│
├── Paper/                  # Paper-related files
├── README.md               # This file
└── requirements.txt        # Python dependencies

Steering/ Framework

The Steering/ directory is the core library. Everything is designed around a 3-stage pipeline: Extract → Steer → Evaluate.

base.py — Abstract Base Classes

ClassPurposeKey Methods
BaseExtractorAbstract extractor for all methodsextract(target_data, contrast_data) → Dict[int, Tensor], _get_activations()
BaseSteerModelAbstract steer model for dense methodssetup_hooks(coeff), generate(prompt, coeff), get_token_probs(prompt, tokens), hook_fn()
BaseSAESteerModelExtended steer model for SAE methodsSame as BaseSteerModel + sae dict parameter
BaseEvaluatorAbstract evaluatorbatch(steer_model, samples, coeff), check(response, ground_truth)

Type contracts — all multi-layer parameters use strict dict-based types:

ParameterTypeExample
layerList[int][14] or [14, 20]
steering_vectorDict[int, Tensor]{14: tensor([...])}
coeffDict[int, float]{14: 2.0}
saeDict[int, SAE]{14: sae_object}

pipeline.py — SteeringPipeline

SteeringPipeline is the only runtime orchestrator used by the CLI:

  1. PipelineConfig.load(...).resolve() normalizes layer, hook_point, and coeff into list/dict forms.
  2. _get_model_type_for_method() chooses TransformerLens, HuggingFace, or SAE-enabled model loading.
  3. _setup_vector_from_extraction() loads train_dataset, builds target/contrast prompt lists, and calls the extractor from EXTRACTOR_MAP.
  4. extractor.get_steer_params() converts SteeringVector metadata into constructor args for the steer model from STEER_MAP.
  5. run() and evaluate() call only BaseSteerModel.generate() or get_token_probs(), so hooks remain centralized in steer-model classes.
class SteeringPipeline:
    list_methods()           # List available extractor methods
    list_evaluators()        # List available evaluators
    list_train_datasets()    # List available training datasets
    list_test_datasets()     # List available test datasets

    __init__(config)         # Initialize from PipelineConfig
    from_config(path_or_cfg) # Factory method
    setup()                  # Load model, extract/load vector, init steer model
    evaluate()               # Full pipeline: extract → steer → evaluate → EvalResult
    run()                    # Generate steered responses (no evaluation)

extractors/ — Steering Vector Extraction

Dense extractors (dense.py):

ClassMethod KeyAlgorithm
CAAExtractorCAAMean difference of target vs contrast activations
COLDExtractorCOLDFinite-difference or kernel in-context learning dynamics
CASTExtractorCASTContrastive steering with PCA option + conditional vector
ManifoldExtractorMANIFOLDPCA/manifold decomposition for overthinking reduction
SphericalExtractorSPHERICALvMF-gated spherical direction extraction

Transport / nonlinear extractors (nonlinear.py):

ClassMethod KeyAlgorithm
AngularExtractorANGULAR2D rotation with automatic layer selection via cosine similarity
ActivationTransportExtractorACTPer-layer source/target activation statistics for Mean/Linear ACT
PIDExtractorPIDLayerwise PID controller over normalized contrastive activation errors
CurveballExtractorCURVEBALLPolynomial Kernel-PCA subspace and steering direction
FlowExtractorFLOWFlow Matching model from source to target residual states
ODEExtractorODEKernel classifier (normed-poly / RBF) for ODE steering
BIPOExtractorBIPOBilateral Preference Optimization
CHARSExtractorCHARSConcept Heterogeneity-Aware Representation Steering
LinNEASExtractorLINNEASLinearized non-linear end-to-end activation steering
LoReFTExtractorREFT / LOREFT / REPSLow-rank residual fine-tuning
FLASExtractorFLASTrains or loads concept-conditioned velocity field

SAE extractors (sae.py):

ClassMethod KeyAlgorithm
SAEIOExtractorSAEIOInput/output score filtering: logit lens + amplification test
SASExtractorSASContrastive SAE activation differencing
SPAREExtractorSPAREMutual information feature selection → SAE decode → dense vector
SREExtractorSRESparse feature selection with mean-difference scoring
SRPSExtractorSRPSSemantic token masking + beta-weighted activation balancing
SSVExtractorSSVANOVA feature selection → linear classifier refinement → optimization
SAERSVExtractorSAE-RSVMulti-component (α1, α2, α3) residual steering vector
SAETSSExtractorSAE-TSLinearAdapter trained on feature effects
SAECoTExtractorSAE-COTGT SAE latent feature overwrite
CorrSteerExtractorCORRSTEERGeneration-time correlation between features and behavior
SAEFreeExtractorSAE-FREEEigendecomposition of activation differences
FGAAExtractorFGAAFeature-Guided Activation Additions with density threshold

steer_models/ — Steering During Inference

Dense steer models (dense.py):

ClassMethod KeyHook Logic
DenseSteerModelCAA, COLD, FGAAresid += coeff * vector
SAEFreeSteerModelSAE-FREEDense addition with optional renormalization
ConditionalSteerModelCAST, CAST_HFSteer only when condition similarity exceeds threshold
ManifoldSteerModelMANIFOLDManifold-based activation manipulation
SphericalSteerModelSPHERICALvMF-gated spherical steering

Transport / nonlinear steer models (nonlinear.py):

ClassMethod KeyHook Logic
AngularSteerModelANGULAR2D rotation in the plane defined by target direction
ActivationTransportSteerModelACTApply affine transport map and interpolate with coeff
PIDSteerModelPIDAdd PID-computed dense vector with optional norm preservation
CurveballSteerModelCURVEBALLProject to KPCA space, steer, inverse transform
FlowSteerModelFLOWSolve learned flow from current residual to target distribution
FlowGLPFLOWGLPSubspace GLP flow steering
ODESteerModelODEKernel classifier ODE integration
BIPOSteerModelBIPOBilateral preference optimization hook
CHARSSteerModelCHARSConcept heterogeneity-aware addition
LinNEASSteerModelLINNEASLinearized non-linear steering
LoReFTSteerModelREFT / LOREFT / REPSLow-rank residual intervention
FLASSteerModelFLASConcept-conditioned Euler flow via HF module hooks

SAE steer models (sae.py):

ClassMethod KeyHook Logic
SASSteerModelSASSAE encode → modify latent → SAE decode
SRESteerModelSRESAE encode → add sparse features → SAE decode
SSVSteerModelSSVSAE encode → add SSV to latent space → SAE decode
SPARESteerModelSPARESAE encode → clamp selected features → SAE decode
SRPSSteerModelSRPSSAE encode → scale features by coefficient → SAE decode
SAERSVSteerModelSAE-RSVDirect residual stream addition
SAETSSteerModelSAE-TSSAE encode → apply LinearAdapter → SAE decode
SAEIOSteerModelSAEIOSAE encode → amplify selected features → SAE decode
SAECoTSteerModelSAE-COTSAE encode → overwrite GT latent features → SAE decode
FeatSteerModelFEATDirect feature list manipulation
CorrSteerModelCORRSTEERDirect residual stream addition (correlation-weighted)

evaluators/ — Evaluation Metrics

13 evaluator types registered in EVALUATOR_MAP:

Evaluator KeyClassUse Case
multiple_choiceMultipleChoiceMatcherA/B/C/D answer matching
mathMathMatcherNumerical answer extraction & comparison
semanticSemanticMatcherEmbedding-based semantic similarity
refusalRefusalMatcherDetects if model refuses (keyword + LLM judge)
logitLogitMatcherSingle-token logit-based evaluation
perplexityPerplexityMatcherResponse perplexity measurement
coherenceCoherenceMatcherText coherence scoring
sycophancyBehaviorMatcher(mode='sycophancy')Sycophancy detection
corrigibleBehaviorMatcher(mode='corrigible')Corrigibility detection
coordinateBehaviorMatcher(mode='coordinate')Coordination behavior detection
politicsBehaviorMatcher(mode='politics')Political leaning detection
cast_refusalCastRefusalMatcherCAST-specific refusal detection

config/ — Configuration System

  • models.py: ModelConfig + MODEL_SAE_REGISTRY — supported models: gemma-2-2b, gemma-2-2b-it, gemma-2-9b, llama-2-7b-hf, llama-3.1-8b
  • methods.py: ExtractorConfig and SteerConfig dataclasses with all hyperparameter defaults (single source of truth). Covers all 30+ methods.
  • datasets.py: TrainDatasetConfig, TestDatasetConfig with path roots (TRAIN_ROOT, TEST_ROOT)
  • pipeline.py: PipelineConfig — top-level experiment configuration, JSON-serializable
  • post_process.py: PostProcessConfig — GLP denoising + classifier guidance settings
  • results.py: SampleResult, EvalResult, SteeringVector — result containers

post_process/ — GLP Post-Processing Pipeline

An optional module that applies GLP (Generative Latent Prior) denoising with optional classifier guidance on top of any steering method.

Core idea:

  1. Train a classifier p_phi(y | z_t, t) from contrastive activations:

    • Build concept-present (h+) and concept-absent (h-) clean activations
    • Sample t ~ U(0, 1) and noise eps; create noisy sample z_t = (1 - t) * h + t * eps
    • Train binary classifier with timestep conditioning
  2. During reverse denoising, use: z_{t-1} = z_t + Δt * u_theta(z_t, t) + s * grad_z log p_phi(y_target | z_t, t)

Train classifier:

python -m Steering.post_process.cli train-classifier \
  --model-name google/gemma-2-2b-it \
  --dataset-name sycophancy \
  --layer 14 --hook-point pre --position last \
  --n-layers 4 --d-model 256 --d-mlp 512 --t-embed-dim 128 \
  --num-epochs 5 --train-batch-size 512 \
  --save-root ./GLP --run-name classifier-stream

Use in pipeline config:

"post_process": {
  "enabled": true,
  "source": "PQPQPQHUST/glp-gemma",
  "checkpoint": "final",
  "layer": 14,
  "hook_point": "pre",
  "position": "last",
  "noise_rate": 0.5,
  "num_timesteps": 20,
  "use_classifier": true,
  "scale": 1.0,
  "negative": false,
  "classifier_checkpoint": "final"
}

data/ — Data Loading

  • loader.py: DataLoader (loads train data with formatting) and EvalDataLoader (loads test data)
  • readers.py: File readers — read_json, read_jsonl, read_csv with automatic format detection
  • formatters.py: Schema-specific formatters converting raw data into (target_prompts, contrast_prompts) pairs
  • data_registry.py: Three registries — TRAIN_DATASET_REGISTRY (20+ datasets), COMPOSITE_DATASET_REGISTRY, TEST_DATASET_REGISTRY (25+ datasets)

utils.py — Shared Utilities

FunctionPurpose
get_hook_name(layer, position)Returns TransformerLens hook name (e.g., blocks.14.hook_resid_pre)
get_resid_acts(resid, position)Extract activations at position ("last", "all", or int)
set_resid_acts(resid, position, acts)Set activations at position
collect_dense_activations(...)Batch-collect activations across layers (used by OT stats computation)
cache_logit_lens(model, sae, k, batch_size)Compute top-k logit lens tokens per SAE feature
get_output_score(model, sae, layer, feature_idx, ...)Compute output score for a feature
build_chat_input(tokenizer, prompt, system_prompt)Format prompt using tokenizer's chat template
make_params(func, **kwargs)Filter kwargs to match function signature
load_fallback_sae(release, sae_id, ...)Download & patch SAE for sae_lens compatibility

cli.py — Command Line Interface

python -m Steering.cli --task eval    --config Configs/Eval/caa_example.json
python -m Steering.cli --task run     --config Configs/Inference/cast_refusal.json
python -m Steering.cli --task extract --config Configs/Eval/spare_sycophancy.json

Datasets

TrainDataset/ — For Vector Extraction

Training data is organized by behavioral category. Each dataset provides target/contrast prompt pairs used to extract steering vectors via contrastive methods.

CategoryDatasetsSchema
Sycophancysycophancy, sycophancy_nlp_survey, sycophancy_philosophy, sycophancy_politicalbinary_choice
AI Safetyai_risk_coordinate, ai_risk_corrigible, ai_risk_myopic, ai_risk_survivalbinary_choice
Refusalrefusal (CAST), refusal_sorrybench, refusal_caa, refusal_advbench, refusal_cast_alpacaVarious
Hallucinationhallucinationbinary_choice
Politicspolitics_twinviewstwin_views
Roleplayroleplay_arithmetic, roleplay_common_sensequestion_only
Reasoninggms8k_train, svamp_trainmath_question
QAcsqa_train, simple_qa, nqswap_trainVarious

Composite datasets (multi-file combinations):

NameComponentsUse Case
refusal_cast_responsesCAST responses + alpaca questionsCAST conditional steering
srps_roleplay_gms8kRoleplay prompts + GMS8K questionsSRPS roleplay arithmetic
srps_roleplay_csqaRoleplay prompts + CSQA questionsSRPS roleplay commonsense
refusal_angularAdvBench harmful + alpaca harmlessAngular refusal steering

TestDataset/ — For Evaluation

Test data is used to evaluate the effect of steering on model capabilities.

CategoryDatasetsEvaluator
QAcsqa (CommonsenseQA)logit
Mathgms8k, svampmath
Knowledgemmlulogit
Refusalrefusal_ab, refusal_open, xstest, harmbench, advbench, sorrybench, donotanswerlogit / refusal / cast_refusal
Hallucinationtruthfulqa, hallucination_ab, haluevallogit / semantic
Sycophancysycophancy_ab, sycophancy_openlogit / sycophancy
AI Safetycoordinate_ab/open, corrigible_ab/open, survival_ab, myopic_ablogit / behavior
Politicstwinviews_testpolitics
NQSwapnqswap_testsemantic

Data Format Schemas

SchemaFormatUsed By
binary_choice{question, answer_matching_behavior, answer_not_matching_behavior}Most behavioral datasets
CAST_condition{harmful: [...], harmless: [...]}CAST refusal
math_question{question, answer}Math/reasoning
question_only{question}Open-ended generation
csqa{question, choices, answer}CommonsenseQA
mmlu{question, A, B, C, D, answer}MMLU
twin_viewsTwo-column CSV (liberal/conservative)Political viewpoints
SorryBench{category, question}SorryBench refusal
AdvBenchCSV with goal columnAdversarial behaviors

Verification/

The Verification/ directory contains ground-truth validation scripts that verify the Steering/ implementations match their reference implementations in Code/. Validation is organized into 3 levels of increasing strictness.

Validation Levels

LevelWhat It TestsPass Criteria
Level 1 (Extraction Match)Extracted steering vectors match GT code outputCosine similarity ≥ 0.99 or exact feature overlap
Level 2 (Inference Match)Steered model logits match GT hook outputLogit cosine ≥ 0.95, KL divergence < 1e-6, or top-5 token overlap
Level 3 (Accuracy Match)End-to-end accuracy matches paper-reported resultsAccuracy within reported tolerance

shared_utils.py

Shared test infrastructure used by all validation scripts:

load_sycophancy_data(n_per_class=100)     # → (target_texts, contrast_texts)
load_model_and_sae(layer=20, device="cuda")  # → (model, sae, layer)
get_sae_activations(model, sae, texts, layer)  # → List[Tensor]  per-text SAE latents

Methods Covered

MethodL1 ScriptL2 ScriptL3 Script
AngularLevel1/Angular/validate_angular_l1.pyLevel2/Angular/validate_angular_l2.pyLevel3/Angular/verify_angular_l3.py
CASTLevel1/CAST/compare.pyLevel2/CAST/compare.py—
CorrSteerLevel1/CorrSteer/validate_corrsteer_l1.pyLevel2/CorrSteer/validate_corrsteer_l2.py—
SAEIOLevel1/SAEIO/validate_saeio_l1.pyLevel2/SAEIO/validate_saeio_l2.py—
SAE-SSVLevel1/SAESSV/validate_ssv_l1.pyLevel2/SAESSV/validate_ssv_l2.py—
SAE-TSLevel1/SAETS/validate_saets_l1.pyLevel2/SAETS/validate_saets_l2.py—
SPARELevel1/SPARE/validate_spare_l1.pyLevel2/SPARE/validate_spare_l2.py—

Verification Status

MethodL1L2L3
Angular✅ cos=0.9999⚠️ Semantic match (TL vs HF minor diffs)⏸ Deferred
SAE-TS✅ cos=1.0000✅ KL=8.1e-9⏸
CorrSteer✅ exact=0.00✅ cos=0.99999⏸
SPARE✅ Jaccard=1.0, cos=1.0✅ prob cos=0.9992⏸
SAEIO✅ All 3 tests passed⚠️ Under investigation⏸
SAE-SSV✅ ANOVA + classifier match✅ cos>0.99⏸

Running Verification Scripts

conda activate sae_circuit
unset CUDA_VISIBLE_DEVICES

# Level 1 (extraction match)
python Verification/Level1/SPARE/validate_spare_l1.py
python Verification/Level1/CorrSteer/validate_corrsteer_l1.py

# Level 2 (inference match)
python Verification/Level2/SAETS/validate_saets_l2.py
python Verification/Level2/SPARE/validate_spare_l2.py

Verification_Results/

Contains saved artifacts from verification runs:

FileDescription
effects_2b.ptPre-computed SAE feature effects for Gemma-2-2B
saets_adapter_real.ptTrained SAE-TS LinearAdapter (real data)
saets_vector_real.ptSAE-TS steering vector (real data)
spare_vector.ptSPARE steering vector
ssv_vector.ptSSV steering vector
corr_vector.ptCorrSteer steering vector

Configs/

Pre-built JSON experiment configurations.

Eval/ — Evaluation Configs

Run a complete extract → steer → evaluate pipeline:

ConfigMethodTrain DataTest Data
minimal_example.jsonCAAsycophancycsqa
corrsteer_sycophancy.jsonCORRSTEERsycophancycsqa
saets_sycophancy.jsonSAE-TSsycophancycsqa
sas_hallucination.jsonSAShallucinationtruthfulqa
spare_sycophancy.jsonSPAREsycophancycsqa
spare_nqswap.jsonSPAREnqswap_trainnqswap_test
sre_sycophancy.jsonSREsycophancycsqa
ssv_politics.jsonSSVpolitics_twinviewstwinviews_test
ssv_ai_risk.jsonSSVai_risk_coordinatecoordinate_ab

Inference/ — Generation Configs

Run steered text generation without evaluation:

ConfigMethodUse Case
caa_example.jsonCAABasic CAA generation
cast_refusal.jsonCASTConditional refusal steering
srps_roleplay_gms8k.jsonSRPSRoleplay + math generation

Config Structure

{
  "name": "experiment_name",
  "model": {
    "name": "google/gemma-2-2b",
    "device": "cuda",
    "dtype": "bfloat16",
    "sae_release": "gemma-scope-2b-pt-res-canonical",
    "sae_id": "layer_{}/width_16k/canonical"
  },
  "extractor": {
    "method": "CAA",
    "layer": 14,
    "batch_size": 4,
    "hook_point": "pre",
    "train_dataset": "sycophancy",
    "n_train": 100
  },
  "steer": {
    "method": "CAA",
    "layer": 14,
    "coeff": 1.00,
    "hook_point": "pre"
  },
  "test_dataset": "csqa",
  "n_test": 200,
  "output": "./Results/caa"
}

Code/ (Reference Implementations)

Original paper implementations used as ground truth for verification:

DirectoryPaper/MethodKey Files
angular-steering/Angular steeringpytorch_pure/extract_directions.py, direction_forcing.py
CAA/Contrastive Activation AdditionRimsky et al. reference code
CAST/Conditional Activation Steeringactivation_steering/ library
CorrSteer/Correlation-based steeringtrain.py, corrsteer/steer.py
SAE-free/SAE-Free steeringEigendecomposition approach
SAE-SSV/Supervised Steering Vectorsaessv-demo.py (ANOVA + classifier)
SAE-TS/Task-Specific SAE steeringsrc/sae_ts/ft_effects/train.py
SPARE/SPARE feature selectionutils.py, mutual information
saes-are-good-for-steering/SAEIO (Input/Output scores)src/output_score.py, src/utils.py, src/sae_utils.py

Other Directories

Vector/

Pre-extracted steering vectors are stored as folder-per-vector, organized by method. Each vector folder contains:

  • vector.pt: tensor or layer→tensor dict payload
  • metadata.json: extraction metadata sidecar (JSON-serializable)

Legacy single-file .pt paths are still accepted and resolved automatically to the folder layout.

FLAS and REPS use fixed unified paths (Vector/FLAS/gemma_gms8k, Vector/REPS/gemma_reps_gms8k) that must be overwritten on each run — never create sequential filenames.

Experiments/

Experiment scripts, results analysis, and replication code. Every experiment (prefix-split, PID, etc.) lives here as self-contained scripts so results can be verified, re-run, or debugged without hunting across directories. Organized by experiment label.

Experiments/
├── ExpA/          # Per-position SNR (extraction bottleneck test)
├── ExpB/          # Perturbation norm survival
├── ExpC/          # Direction rotation tracking
├── ExpD/          # Logit lens KL / cancellation circuit
├── ExpE/          # Contrastive alignment
├── Exp6/          # Coupling mass (CHARS K-sweep + matrix deep dive)
├── TailFailure/   # Tail distribution → steering failure correlation
├── Prefix/        # Evil prefix splitting analysis
├── Utility/       # Pareto plots, tables, metrics, activations
└── *.pt           # Support data files (e.g. flas_evil_effective_vector.pt)

Notebooks/

Interactive Jupyter notebooks for each method — useful for exploration and debugging:

NotebookPurpose
Angular.ipynbAngular steering exploration
CAA.ipynbCAA vector extraction & evaluation
CAST.ipynbCAST conditional steering
CorrSteer.ipynbCorrSteer correlation analysis
SAE_RSV.ipynbSAE-RSV residual vector
SAE_SSV.ipynbSSV ANOVA + classifier pipeline
SAE_TS.ipynbSAE-TS adapter training
SAS.ipynbSAS contrastive SAE
SPARE.ipynbSPARE mutual information
SRE.ipynbSRE sparse engineering
SRPS.ipynbSRPS semantic projection
Manifold.ipynbManifold-based steering
EvaluationDemo.ipynbEvaluation pipeline demo
playground.ipynbExperimental playground
Steering/post_process/sae.ipynbSAE post-processing analysis
Steering/post_process/subspace_demo.ipynbSubspace GLP demo
Steering/post_process/gemma_demo.ipynbGLP on Gemma demo

Modification Quick Reference

TaskFile(s) to EditWhat to Do
Add steering methodextractors/dense.py or extractors/sae.py + steer_models/Add class + add to map in appropriate submodule
Add training datasetconfig/datasets.pyAdd entry to TRAIN_DATASET_REGISTRY
Add test datasetconfig/datasets.pyAdd entry to TEST_DATASET_REGISTRY
Add data formatterdata/formatters.pyAdd function + add to FORMATTERS dict
Add evaluatorSteering/evaluators/Add class + add to EVALUATOR_MAP
Change param defaultconfig/methods.pyEdit field default in ExtractorConfig or SteerConfig dataclass
Add NEW paramconfig/methods.py + extractors/ or steer_models/Add field to dataclass + accept in class
Add model-SAE mappingconfig/models.pyAdd entry to MODEL_SAE_REGISTRY

Design Principles:

  • "Registration lives with implementation" — each registry dict is at the bottom of the file containing the classes
  • "Single source of truth" — all defaults are in ExtractorConfig/SteerConfig dataclass fields

Adding New Methods

Adding a new steering method requires editing 3 files.

Step 1: Add Extractor Class

Add your extractor class to Steering/extractors/dense.py (for dense methods) or Steering/extractors/sae.py (for SAE methods), then register it in EXTRACTOR_MAP in Steering/extractors/init.py:

class MyExtractor(BaseExtractor):
    """My custom steering vector extractor."""
    
    def __init__(self, model, layer: List[int], my_param: float = 1.0, **kwargs):
        super().__init__(model, layer, **kwargs)
        self.my_param = my_param
    
    def extract(self, target_data, contrast_data=None, **kwargs) -> Dict[int, torch.Tensor]:
        # Implementation — must return Dict[int, Tensor] keyed by layer
        return self.vector

# In Steering/extractors/__init__.py, add to EXTRACTOR_MAP:
EXTRACTOR_MAP = {
    "CAA": CAAExtractor,
    ...
    "MYMETHOD": MyExtractor,  # ← Add this line
}

Step 2: Add Steer Model Class

Add your steer model class to the appropriate file in Steering/steer_models/, then register it in the STEER_MAP in __init__.py:

# For dense methods: add to steer_models/dense.py
# For SAE methods: add to steer_models/sae.py

# Add your class anywhere in the file
class MySteerModel(BaseSteerModel):
    """Apply my steering method."""
    
    def _apply_steering_hook(self, resid, coeff, hook):
        return resid + coeff * self.steering_vector

# Then add to STEER_MAP in steer_models/__init__.py:
STEER_MAP = {
    "CAA": DenseSteerModel,
    ...
    "MYMETHOD": MySteerModel,  # ← Add this line
}

Step 3 (Optional): Add Method to SAE_METHODS

If your method requires SAE, add to Steering/config/methods.py:

# Add to SAE_METHODS if your method requires SAE
SAE_METHODS: Set[str] = {"SAS", "SPARE", ..., "MYMETHOD"}  # Only if uses SAE

Default hyperparameters are defined directly in the ExtractorConfig and SteerConfig dataclass fields.

That's It!

Your method is now available everywhere:

from Steering import SteeringPipeline

print(SteeringPipeline.list_methods())  # [..., 'MYMETHOD']

extractor = SteeringPipeline.create_extractor("MYMETHOD", model, layer=14, my_param=0.5)
steer_model = SteeringPipeline.create_steer_model("MYMETHOD", model, layer=14, extractor=extractor)

Adding New Datasets

Adding datasets requires editing 1 file: Steering/config/datasets.py

For Training Datasets

# Add to TRAIN_DATASET_REGISTRY
TRAIN_DATASET_REGISTRY["my_dataset"] = TrainDatasetConfig(
    file="my_category/train.json",        # Relative path from TRAIN_ROOT
    schema="binary_choice",               # Data format schema
    target_key="correct_prompt",          # Key for target prompts
    contrast_key="false_prompt",          # Key for contrast prompts
)

For Test Datasets

# Add to TEST_DATASET_REGISTRY
TEST_DATASET_REGISTRY["my_test"] = TestDatasetConfig(
    file="my_category/test.json",         # Relative path from TEST_ROOT
    schema="multiple_choice",             # Data format schema
    evaluator="multiple_choice",          # Evaluator type
    prompt_template="Q: {question}\nA:",  # Prompt template
    ground_truth_key="answer",            # Key for ground truth
)

That's It!

Your dataset is now available:

from Steering.data import DataLoader, EvalDataLoader

# Training data
loader = DataLoader()
data = loader.load("my_dataset")
cfg = loader.get_config("my_dataset")
targets = [d[cfg.target_key] for d in data]
contrasts = [d[cfg.contrast_key] for d in data]

# Test data
eval_loader = EvalDataLoader()
test_data = eval_loader.load("my_test")

Available Formatters (Schemas)

SchemaDescriptionUse For
binary_choiceTarget/contrast pairsContrastive training
CAST_conditionConditional steeringCAST method
multiple_choiceA/B/C/D choicesQA evaluation
math_questionQ&A pairsReasoning tasks
question_onlyJust the questionOpen-ended generation
SorryBenchSorryBench formatRefusal training
AdvBenchAdvBench formatHarmful behavior

Adding a New Formatter

If your data has a new schema, add to Steering/data/formatters.py:

# Add formatter function
def format_my_schema(data: List[Dict]) -> Tuple[List, List]:
    """Format my custom data schema."""
    positive, negative = [], []
    for item in data:
        positive.append(item["my_positive_field"])
        negative.append(item["my_negative_field"])
    return positive, negative

# Add to FORMATTERS dict at bottom:
FORMATTERS = {
    ...
    "my_schema": format_my_schema,  # ← Add this line
}

Adding New Evaluators

Adding evaluators requires editing 1 file: Evaluation/scoring_metrics.py

Step 1: Create Evaluator Class and Register

Add your evaluator class, then add to EVALUATOR_MAP at the bottom of the same file:

# Add your class anywhere in the file
class MyEvaluator:
    """My custom evaluator."""
    
    def __init__(self, device: str = "cuda:0", **kwargs):
        self.device = device
    
    def check(self, response: str, expected: str = None, **kwargs) -> Tuple[bool, float]:
        """Return (is_correct, confidence)."""
        # Implementation
        return is_correct, confidence

# At bottom of file, add to EVALUATOR_MAP:
EVALUATOR_MAP = {
    "multiple_choice": MultipleChoiceMatcher,
    "math": MathMatcher,
    "refusal": BehaviorMatcher,
    "semantic": SemanticMatcher,
    "my_evaluator": MyEvaluator,  # ← Add this line
}

That's It!

Your evaluator is now available:

from Steering import EvalPipeline

print(EvalPipeline.list_evaluators())  # [..., 'my_evaluator']

# Internal usage within pipeline

Adjusting Hyperparameters

Changing Default Values

All defaults are in the ExtractorConfig and SteerConfig dataclass fields in Steering/config/methods.py:

@dataclass
class ExtractorConfig:
    method: str
    layer: int
    batch_size: int = 8       # Change default here
    top_k: int = 15           # Change default here
    ...

@dataclass  
class SteerConfig:
    method: str
    layer: int
    coeff: float = 1.0        # Change default here
    ...

Adding a NEW Parameter

Adding a new parameter requires 2 files:

StepFileAction
1config/methods.pyAdd field to ExtractorConfig or SteerConfig dataclass
2extractors.py or steer_models/Accept & use the parameter in the class

Example - Adding a temperature parameter:

# Step 1: config/methods.py - Add to dataclass
@dataclass
class ExtractorConfig:
    ...
    temperature: float = 1.0  # ← NEW FIELD with default

# Step 2: extractors.py or steer_models/dense.py - Accept and use it
class MyExtractor(BaseExtractor):
    def __init__(self, model, layer, temperature=1.0, **kwargs):
        super().__init__(model, layer, **kwargs)
        self.temperature = temperature

Testing

Run All Tests

# Run full test suite
python -m pytest tests/ -v

# With coverage
python -m pytest tests/ --cov=Steering --cov-report=html

Run Specific Tests

# Test config module
python -m pytest tests/test_config.py -v

# Test data loading
python -m pytest tests/test_data_loader.py -v

Validate a Config

python -m Steering.validate_config Configs/caa_example.json

Troubleshooting

Common Issues

1. CUDA Out of Memory

# Reduce batch size in config
"extractor": {"batch_size": 4}

# Or use float16 instead of bfloat16
"model": {"dtype": "float16"}

2. HuggingFace Authentication Error

# Make sure token is set
export HF_TOKEN="hf_..."

# Or login
huggingface-cli login

3. SAE Not Available for Model

SAE methods (SAS, SPARE, etc.) currently only support Gemma models.
Use dense methods (CAA, CAST, ANGULAR) for other models.

4. Dataset Not Found

python -c "from Steering.config import TRAIN_DATASET_REGISTRY; print(list(TRAIN_DATASET_REGISTRY.keys()))"

5. FLAS / REPS Disk Full

These methods produce multi-GB checkpoints. Always use the unified overwrite paths and never create sequential filenames:

  • FLAS: Vector/FLAS/gemma_gms8k
  • REPS: Vector/REPS/gemma_reps_gms8k

Contributing

We welcome contributions! Here's how:

Code Style

# Format code
black Steering/ Evaluation/
isort Steering/ Evaluation/

# Type check
mypy Steering/

Pull Request Process

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests: python -m pytest tests/
  6. Submit a pull request

What We Need

  • More steering methods
  • More evaluation datasets
  • Support for more models (Llama, Mistral)
  • Better documentation
  • Performance optimizations

License

MIT License


Citation

If you use this benchmark in your research, please cite:

@software{sae_steering_bench,
  title = {SAESteeringBench: A Unified Framework for Steering Vector Methods},
  year = {2025},
  url = {https://github.com/PQHSGS/SAESteeringBench}
}

PQHSGS/SteerBench

Python

0

0 commits

updated Aug 18, 2026

See the code

README

SAESteeringBench

A unified framework for steering vector extraction, application, and evaluation across 30+ methods.

Python 3.10+ License: MIT


Table of Contents


Installation

Prerequisites

  • Python: 3.10 or higher
  • CUDA: 11.8+ (for GPU acceleration)
  • GPU: NVIDIA GPU with 16GB+ VRAM recommended (for Gemma-2-2B)

Step 1: Clone the Repository

git clone https://github.com/PQHSGS/SteerBench.git
cd SteerBench

Step 2: Create Environment

Using Conda (Recommended):

conda create -n sae-steering python=3.11
conda activate sae-steering

Using venv:

python -m venv .venv
source .venv/bin/activate  # Linux/Mac
# or: .venv\Scripts\activate  # Windows

Step 3: Install Dependencies

Using pip with requirements.txt (Recommended):

pip install -r requirements.txt

Or install manually:

# Core dependencies
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# ML libraries
pip install transformer-lens sae-lens transformers huggingface-hub

# Utilities
pip install numpy pandas scikit-learn tqdm pyyaml

# Development (optional)
pip install pytest pytest-cov black isort mypy

Step 4: Authenticate with HuggingFace

# Set your token (required for gated models like Gemma)
export HF_TOKEN="your_huggingface_token"

# Or login interactively
huggingface-cli login

Step 5: Verify Installation

python -c "from Steering import SteeringPipeline; print('✓ Installation successful')"

Quick Start

Option 1: Config-Based CLI

# Run steering generation/extraction with a config file
python -m Steering.cli --task run --config Configs/Eval/caa_example.json

# Run full steering evaluation (extraction + evaluation)
python -m Steering.cli --task eval --config Configs/Eval/caa_example.json

# Quiet mode (minimal output)
python -m Steering.cli --task eval --config Configs/Eval/caa_example.json --quiet

Option 2: Python API

from Steering import SteeringPipeline, PipelineConfig

# From config file
config = PipelineConfig.load("Configs/Eval/caa_example.json")
pipeline = SteeringPipeline.from_config(config)
result = pipeline.evaluate_from_config(config)

# Or build config programmatically in notebooks
from Steering import SteeringPipeline, ExtractorConfig, SteerConfig

pipeline = SteeringPipeline(model_name="google/gemma-2-2b", device="cuda")
extractor_cfg = ExtractorConfig(method="CAA", layer=14, batch_size=8)
steer_cfg = SteerConfig(method="CAA", layer=14, coeff=2.0)

result = pipeline.evaluate(
    extractor_config=extractor_cfg,
    steer_config=steer_cfg,
    train_dataset="sycophancy",
    test_dataset="csqa",
)

Supported Methods

MethodTypeDescription
CAADenseContrastive Activation Addition — mean-difference vector added to residual stream
COLDDenseIn-context One-step Learning Dynamics — finite-difference or kernel variant
CAST / CAST_HFDenseConditional Activation Steering — steers only when condition similarity threshold is met
MANIFOLDDenseManifold-based steering for reducing overthinking behaviors
SPHERICALDensevMF-gated spherical steering with kappa/alpha/beta hyperparameters
SAE-FREEDenseEigendecomposition of activation differences (uses SAE feature space, steers dense)
ACTTransportActivation Transport / Mean-AcT — affine transport of residual activation distributions
ANGULARTransport2D rotation-based steering with automatic layer selection via cosine similarity
CURVEBALLTransportPolynomial Kernel-PCA nonlinear steering with pre-image reconstruction
FLOW / FLOWGLPTransportFlow Matching transport from source to target residual states
PIDTransportLayerwise PID controller over contrastive activation error signals
ODETransportKernel classifier ODE steering (normed-poly / RBF)
BIPOTransportBilateral Preference Optimization steering
CHARSTransportConcept Heterogeneity-Aware Representation Steering
LinNEASTransportLinearized Non-linear End-to-end Activation Steering
REFT / LOREFT / REPSParametricLow-rank residual fine-tuning / representation engineering
FLASParametricFlow-based Activation Steering — concept-conditioned velocity field with Euler integration
WEIGHTSTEERWeightContrastive LoRA weight steering
CORRSTEERSAEGeneration-time correlation between SAE features and behavior
SASSAEContrastive SAE activation differencing
SPARESAEMutual information feature selection with SAE reconstruction
SRESAESparse Representation Engineering for targeted feature manipulation
SRPSSAESemantic token masking + SAE with beta parameter for activation balancing
SSVSAETwo-stage ANOVA + linear classifier refinement + optimization
SAE-RSVSAEMulti-component (α1, α2, α3) residual steering vector
SAE-TSSAELinearAdapter trained on feature effects
SAEIOSAEInput/Output score filtering: logit lens + amplification test per feature
SAE-COTSAEGT SAE latent feature overwrite steering
FEATSAEDirect feature list manipulation in SAE latent space
FGAASAEFeature-Guided Activation Additions with density threshold

Dense methods modify residual stream activations directly. Transport methods apply learned or analytic maps (OT, flow, rotation, PCA) to move activations. Parametric methods train a lightweight module (LoRA, flow network) during extraction. SAE methods require a Sparse Autoencoder (currently only available for Gemma models).

Storage constraint for FLAS and REPS: These methods produce very large checkpoints (several GB). All runs must overwrite a single unified path per method to avoid filling disk:

  • FLAS: Vector/FLAS/gemma_gms8k
  • REPS: Vector/REPS/gemma_reps_gms8k

Type Contracts

All extractors and steer models use strict dict-based types for multi-layer support:

ParameterTypeDescription
layerList[int]Target layer(s)
steering_vectorDict[int, Tensor]Steering vector keyed by layer
coeffDict[int, float]Steering coefficient keyed by layer
saeDict[int, SAE]Sparse autoencoder keyed by layer

Method-Specific Notes

  • CAST: Requires separate conditional_dataset and conditional_layer for condition detection
  • ANGULAR: Uses auto_select_layer=true to auto-select optimal layer via cosine similarity
  • ACT: Uses Code/AcT as the ground-truth surface. Code/PID/Mean-AcT overlaps with ACT and adds PID-specific behavior, so PID-specific Mean-AcT changes are tracked under PID.
  • CURVEBALL and FLOW: Implement extraction and steering inference only, without demos or paper experiment harnesses.
  • PID: LLM-only implementation. Diffusion paths in Code/PID/Mean-AcT are reference context and are not part of Steering inference.
  • SRPS: Uses beta parameter in extractor (not steer) to balance activation vs active dimensions
  • FLAS: Uses HF decoder-layer module hooks (not TransformerLens). SteerConfig.coeff maps to flow time T. Requires flas_concept_text at steering time.

Pareto Frontier Analysis

A central contribution of SAESteeringBench is evaluating the fundamental Pareto trade-off in representation steering: Steering Efficacy (Task Accuracy / Target Alignment) versus Language Modeling Coherence (Perplexity & Non-Degradation).

Aggressive steering often increases target concept expression at the expense of pushing residual representations off the model's manifold, leading to catastrophic repetition or incoherence. The Pareto frontier identifies methods that achieve maximum steering with minimal distribution distortion.

Cross-Method Pareto Frontiers

Below are benchmark Pareto frontiers comparing Dense, Transport, and SAE Latent steering techniques across core safety and behavior tasks:

Overall Multi-Method BenchmarkSafety & Refusal Steering Frontier
Pareto Benchmark FrontierRefusal Open Pareto Comparison
Perplexity Cluster Analysis (Top Performers)Transport Ablation (AcT vs. CHaRS)
Refusal Open Cluster ModeAcT and CHaRS Ablation

Key Benchmark Takeaways

  1. Dense vs. Transport Stability: Standard linear addition (CAA, COLD) exhibits steep trade-off curves where higher coefficients rapidly elevate perplexity. In contrast, optimal transport and manifold-aware methods (ACT, CHARS, COBRA) maintain tighter perplexity bounds.
  2. SAE Feature Selection: SAE-based approaches (SRPS, SPARE, SAS) provide fine-grained steering along interpretable directions, with feature balancing preventing severe degradation.
  3. Reproducing Pareto Plots: You can generate and customize all Pareto frontier curves using pareto.py:
    # Compare methods across safety datasets
    python pareto.py --mode methods --datasets Refusal_open,SorryBench --methods-topk 15 --output-dir Pareto
    
    # Cluster analysis by perplexity intervals
    python pareto.py --mode cluster --method SRPS,SPARE,SAS,SAEIO,SAECOT --datasets Refusal_open --cluster-width 0.5 --output-dir Pareto
    
    For full CLI options, see Pareto_CLI_Guide.md.

Configuration

Config File Structure

{
  "name": "my_experiment",
  "model": {
    "name": "google/gemma-2-2b",
    "device": "cuda",
    "dtype": "bfloat16"
  },
  "extractor": {
    "method": "CAA",
    "layer": 14,
    "batch_size": 4
  },
  "steer": {
    "method": "CAA",
    "layer": 14,
    "coeff": 1.00
  },
  "train_dataset": "sycophancy",
  "test_dataset": "csqa",
  "n_train": 100,
  "n_test": 200
}

Available Datasets

Training (for vector extraction):

  • sycophancy, sycophancy_philosophy, sycophancy_political
  • refusal, hallucination
  • ai_risk_coordinate, ai_risk_corrigible, ai_risk_myopic, ai_risk_survival

Testing (for evaluation):

  • csqa - CommonsenseQA (multiple choice)
  • gms8k - Grade School Math (math)
  • refusal - SorryBench (refusal detection)

Example Configs

Configs/Eval/
├── angular_refusal.json    # Angular steering for refusal
├── caa_example.json        # Basic CAA setup
├── cast_refusal.json       # Conditional steering
├── minimal_example.json    # Minimal required fields
├── sas_hallucination.json  # SAS for hallucination
├── spare_sycophancy.json   # SAE-based SPARE
├── sre_sycophancy.json     # SRE for sycophancy
├── srps_sycophancy.json    # SRPS for sycophancy
├── ssv_ai_risk.json        # SSV for AI safety
├── LinearAcT/              # Activation Transport variant
├── PID/                    # PID steering
├── FLOW/                   # FlowSteer configs
├── CURVE/                  # Curveball configs
└── TruthFlow/              # TruthFlow configs

Python API

Basic Usage

from Steering import SteeringPipeline, PipelineConfig

# From config file
config = PipelineConfig.load("Configs/caa_example.json")
pipeline = SteeringPipeline.from_config(config)
results = pipeline.run()

Using Factory Functions

from Steering import SteeringPipeline

# List all available methods
print(SteeringPipeline.list_methods())  # ['CAA', 'CAST', 'ANGULAR', ...]

# Create extractor
extractor = SteeringPipeline.create_extractor("CAA", model, layer=14)
vector = extractor.extract(target_prompts, contrast_prompts)

# Create steer model
steer_model = SteeringPipeline.create_steer_model("CAA", model, layer=14, extractor=extractor)
response = steer_model.generate("Your prompt here", coeff=2.0)

Direct Class Usage

from Steering.extractors import CAAExtractor
from Steering.steer_models import DenseSteerModel

# Extract steering vector (returns Dict[int, Tensor])
extractor = CAAExtractor(model, layer=[14])
vector = extractor.extract(target_data, contrast_data)

# Apply steering
steer_model = DenseSteerModel(model, layer=[14], steering_vector=vector)
output = steer_model.generate("Hello!", coeff={14: 2.0})

Project Structure

SAESteeringBench/
├── Steering/               # Core steering framework (the main library)
│   ├── __init__.py         # Public API exports & factory functions
│   ├── pipeline.py         # SteeringPipeline — orchestrates extract → steer → evaluate
│   ├── base.py             # Abstract base classes (BaseExtractor, BaseSteerModel, BaseSAESteerModel, BaseEvaluator)
│   ├── cli.py              # CLI entrypoint (python -m Steering.cli)
│   ├── utils.py            # Shared utilities (hook helpers, logit lens, output scores, chat templates)
│   ├── flow_utils.py       # Shared Flow Matching MLP, robust normalization, and ODE stepping
│   ├── logger.py           # Colored logging system
│   ├── exceptions.py       # Custom exception hierarchy
│   ├── IMPLEMENTATION_NOTES.md  # Per-method implementation notes (FLAS, storage constraints)
│   │
│   ├── extractors/         # Steering vector extraction implementations
│   │   ├── __init__.py     # EXTRACTOR_MAP registry
│   │   ├── dense.py        # Dense extractors (CAA, COLD, CAST, Manifold, Spherical)
│   │   ├── nonlinear.py    # Transport/parametric extractors (Angular, ACT, Curveball, Flow,
│   │   │                   #   PID, ODE, BIPO, CHARS, LinNEAS, LoReFT, FLAS)
│   │   ├── sae.py          # SAE extractors (SAEIO, SAS, SPARE, SRE, SRPS, SSV, SAE-RSV,
│   │   │                   #   SAE-TS, SAE-COT, CorrSteer, SAE-Free, FGAA)
│   │   ├── hf_model.py     # HFCASTExtractor (HuggingFace-based CAST)
│   │   └── weight.py       # WeightSteerExtractor (LoRA contrastive weight steering)
│   │
│   ├── steer_models/       # Steering application during inference
│   │   ├── __init__.py     # STEER_MAP registry
│   │   ├── dense.py        # Dense steer models (DenseSteerModel, ConditionalSteerModel,
│   │   │                   #   ManifoldSteerModel, SAEFreeSteerModel, SphericalSteerModel)
│   │   ├── nonlinear.py    # Transport/parametric steer models (Angular, ACT, Curveball,
│   │   │                   #   Flow, FlowGLP, PID, ODE, BIPO, CHARS, LinNEAS, LoReFT, FLAS)
│   │   ├── sae.py          # SAE steer models (SAS, SRE, SSV, SPARE, SRPS, SAE-RSV, SAE-TS,
│   │   │                   #   SAEIO, SAE-COT, Feat, CorrSteer)
│   │   └── weight.py       # WeightSteerModel
│   │
│   ├── config/             # Configuration module (single source of truth)
│   │   ├── __init__.py     # Exports all config components
│   │   ├── models.py       # ModelConfig, MODEL_SAE_REGISTRY (Gemma-2, Llama-2/3)
│   │   ├── methods.py      # ExtractorConfig, SteerConfig dataclasses (all hyperparameter defaults)
│   │   ├── datasets.py     # TrainDatasetConfig, TestDatasetConfig, path roots
│   │   ├── pipeline.py     # PipelineConfig (top-level experiment config)
│   │   ├── post_process.py # PostProcessConfig (GLP denoising + classifier guidance)
│   │   └── results.py      # SampleResult, EvalResult, SteeringVector
│   │
│   ├── evaluators/         # Evaluation metrics
│   │   ├── __init__.py     # EVALUATOR_MAP registry (13 evaluator types)
│   │   ├── scoring_metrics.py  # MultipleChoiceMatcher, MathMatcher, BehaviorMatcher,
│   │   │                       #   RefusalMatcher, SemanticMatcher, LogitMatcher, CastRefusalMatcher
│   │   └── model_capabilities.py  # PerplexityMatcher, CoherenceMatcher
│   │
│   ├── data/               # Data loading & formatting
│   │   ├── __init__.py     # Exports DataLoader, EvalDataLoader
│   │   ├── loader.py       # DataLoader (train), EvalDataLoader (test)
│   │   ├── readers.py      # File readers (JSON, JSONL, CSV)
│   │   ├── formatters.py   # Schema formatters (binary_choice, CAST_condition, etc.)
│   │   └── data_registry.py  # TRAIN_DATASET_REGISTRY, TEST_DATASET_REGISTRY,
│   │                         #   COMPOSITE_DATASET_REGISTRY
│   │
│   ├── post_process/       # GLP post-processing pipeline
│   │   ├── __init__.py
│   │   ├── cli.py          # CLI: train-classifier, run post-processing
│   │   ├── hook.py         # GLPPostProcessor — attaches GLP denoising hooks
│   │   ├── glp.py          # GLP manifold denoiser model
│   │   ├── classifier.py   # ConceptClassifier for guidance
│   │   ├── flow_matching.py
│   │   ├── subspace.py     # Subspace GLP variant
│   │   ├── train_classifier.py
│   │   └── train_stream.py
│   │
│   ├── finetune/           # LoRA fine-tuning support
│   │   ├── __init__.py
│   │   ├── cli.py
│   │   ├── trainer.py
│   │   ├── config.py
│   │   ├── lora.py
│   │   └── registry.py
│   │
│   └── tests/              # Unit test suite
│       ├── conftest.py     # Pytest fixtures
│       ├── test_config.py, test_data_loader.py, test_factory.py, ...
│       └── unit/           # Granular unit tests
│
├── Verification/           # Ground-truth validation (Level 1 extraction, Level 2 steering)
│   ├── shared_utils.py     # Shared utilities (load_sycophancy_data, load_model_and_sae, get_sae_activations)
│   ├── task.md             # Verification status tracker
│   ├── Level1/             # Extraction match (vector cosine similarity)
│   │   ├── Angular/, CAA/, CAST/, CorrSteer/, SAEIO/, SAESSV/, SAETS/, SPARE/
│   └── Level2/             # Inference match (steered logit comparison)
│       ├── Angular/, CAA/, CAST/, CorrSteer/, SAEIO/, SAESSV/, SAETS/, SPARE/
│
├── Configs/                # Experiment configuration files (JSON)
│   ├── Eval/               # Evaluation configs (end-to-end experiments)
│   └── Inference/          # Inference-only configs (generation)
│
├── TrainDataset/           # Training datasets (for vector extraction)
│   ├── behaviour/          # sycophancy, refusal, hallucination, advanced-ai-risk, politic, ...
│   ├── instruction_following/
│   ├── QA/                 # CSQA, SimpleQA, NQSwap
│   └── reasoning/          # GMS8K, SVAMP
│
├── TestDataset/            # Test datasets (for evaluation)
│   ├── behaviour/          # sycophancy, coordinate, corrigible, survival, myopic
│   ├── hallucination/      # TruthfulQA, HaluEval
│   ├── mmlu/
│   ├── QA/
│   ├── reasoning/          # GMS8K, SVAMP test splits
│   └── refusal/            # AdvBench, XSTest, HarmBench, DoNotAnswer, SorryBench
│
├── Code/                   # Reference implementations (ground truth)
│   ├── angular-steering/, CAA/, CAST/, AcT/, PID/, CorrSteer/
│   ├── SAE-free/, SAE-SSV/, SAE-TS/, SPARE/
│   └── saes-are-good-for-steering/
│
├── Vector/                 # Pre-extracted steering vectors (folder-per-vector)
│   ├── Angular/, CAA/, CAST/, SPARE/, SSV/
│   ├── FLAS/gemma_gms8k/        # ← FLAS unified path (overwrite only)
│   └── REPS/gemma_reps_gms8k/   # ← REPS unified path (overwrite only)
│
├── Verification_Results/   # Saved verification artifacts
│   ├── *.pt                # Saved vectors/adapters for reproducibility
│   └── Angular Log/
│
├── Results/                # Experiment evaluation results
│   ├── full_side_by_side_comparison.csv   # All methods × tasks × sole vs OT
│   ├── matched_pairs_clean.csv            # Paired sole vs OT with improvement delta
│   ├── experiments_summary_*.csv          # Per-method summaries
│   └── <method>/                          # Per-method result directories
│
├── Experiment/             # Legacy experiment configs and outputs
│   ├── CorrSteer/, SAE-SSV/
│
├── Experiments/            # Experiment scripts + results for tracking & replication
│
├── Notebooks/              # Interactive Jupyter notebooks (per method)
│   ├── Angular.ipynb, CAA.ipynb, CAST.ipynb, CorrSteer.ipynb,
│   │   Manifold.ipynb, SAE_RSV.ipynb, SAE_SSV.ipynb, SAE_TS.ipynb,
│   │   SAS.ipynb, SPARE.ipynb, SRE.ipynb, SRPS.ipynb,
│   │   EvaluationDemo.ipynb, playground.ipynb
│
├── Paper/                  # Paper-related files
├── README.md               # This file
└── requirements.txt        # Python dependencies

Steering/ Framework

The Steering/ directory is the core library. Everything is designed around a 3-stage pipeline: Extract → Steer → Evaluate.

base.py — Abstract Base Classes

ClassPurposeKey Methods
BaseExtractorAbstract extractor for all methodsextract(target_data, contrast_data) → Dict[int, Tensor], _get_activations()
BaseSteerModelAbstract steer model for dense methodssetup_hooks(coeff), generate(prompt, coeff), get_token_probs(prompt, tokens), hook_fn()
BaseSAESteerModelExtended steer model for SAE methodsSame as BaseSteerModel + sae dict parameter
BaseEvaluatorAbstract evaluatorbatch(steer_model, samples, coeff), check(response, ground_truth)

Type contracts — all multi-layer parameters use strict dict-based types:

ParameterTypeExample
layerList[int][14] or [14, 20]
steering_vectorDict[int, Tensor]{14: tensor([...])}
coeffDict[int, float]{14: 2.0}
saeDict[int, SAE]{14: sae_object}

pipeline.py — SteeringPipeline

SteeringPipeline is the only runtime orchestrator used by the CLI:

  1. PipelineConfig.load(...).resolve() normalizes layer, hook_point, and coeff into list/dict forms.
  2. _get_model_type_for_method() chooses TransformerLens, HuggingFace, or SAE-enabled model loading.
  3. _setup_vector_from_extraction() loads train_dataset, builds target/contrast prompt lists, and calls the extractor from EXTRACTOR_MAP.
  4. extractor.get_steer_params() converts SteeringVector metadata into constructor args for the steer model from STEER_MAP.
  5. run() and evaluate() call only BaseSteerModel.generate() or get_token_probs(), so hooks remain centralized in steer-model classes.
class SteeringPipeline:
    list_methods()           # List available extractor methods
    list_evaluators()        # List available evaluators
    list_train_datasets()    # List available training datasets
    list_test_datasets()     # List available test datasets

    __init__(config)         # Initialize from PipelineConfig
    from_config(path_or_cfg) # Factory method
    setup()                  # Load model, extract/load vector, init steer model
    evaluate()               # Full pipeline: extract → steer → evaluate → EvalResult
    run()                    # Generate steered responses (no evaluation)

extractors/ — Steering Vector Extraction

Dense extractors (dense.py):

ClassMethod KeyAlgorithm
CAAExtractorCAAMean difference of target vs contrast activations
COLDExtractorCOLDFinite-difference or kernel in-context learning dynamics
CASTExtractorCASTContrastive steering with PCA option + conditional vector
ManifoldExtractorMANIFOLDPCA/manifold decomposition for overthinking reduction
SphericalExtractorSPHERICALvMF-gated spherical direction extraction

Transport / nonlinear extractors (nonlinear.py):

ClassMethod KeyAlgorithm
AngularExtractorANGULAR2D rotation with automatic layer selection via cosine similarity
ActivationTransportExtractorACTPer-layer source/target activation statistics for Mean/Linear ACT
PIDExtractorPIDLayerwise PID controller over normalized contrastive activation errors
CurveballExtractorCURVEBALLPolynomial Kernel-PCA subspace and steering direction
FlowExtractorFLOWFlow Matching model from source to target residual states
ODEExtractorODEKernel classifier (normed-poly / RBF) for ODE steering
BIPOExtractorBIPOBilateral Preference Optimization
CHARSExtractorCHARSConcept Heterogeneity-Aware Representation Steering
LinNEASExtractorLINNEASLinearized non-linear end-to-end activation steering
LoReFTExtractorREFT / LOREFT / REPSLow-rank residual fine-tuning
FLASExtractorFLASTrains or loads concept-conditioned velocity field

SAE extractors (sae.py):

ClassMethod KeyAlgorithm
SAEIOExtractorSAEIOInput/output score filtering: logit lens + amplification test
SASExtractorSASContrastive SAE activation differencing
SPAREExtractorSPAREMutual information feature selection → SAE decode → dense vector
SREExtractorSRESparse feature selection with mean-difference scoring
SRPSExtractorSRPSSemantic token masking + beta-weighted activation balancing
SSVExtractorSSVANOVA feature selection → linear classifier refinement → optimization
SAERSVExtractorSAE-RSVMulti-component (α1, α2, α3) residual steering vector
SAETSSExtractorSAE-TSLinearAdapter trained on feature effects
SAECoTExtractorSAE-COTGT SAE latent feature overwrite
CorrSteerExtractorCORRSTEERGeneration-time correlation between features and behavior
SAEFreeExtractorSAE-FREEEigendecomposition of activation differences
FGAAExtractorFGAAFeature-Guided Activation Additions with density threshold

steer_models/ — Steering During Inference

Dense steer models (dense.py):

ClassMethod KeyHook Logic
DenseSteerModelCAA, COLD, FGAAresid += coeff * vector
SAEFreeSteerModelSAE-FREEDense addition with optional renormalization
ConditionalSteerModelCAST, CAST_HFSteer only when condition similarity exceeds threshold
ManifoldSteerModelMANIFOLDManifold-based activation manipulation
SphericalSteerModelSPHERICALvMF-gated spherical steering

Transport / nonlinear steer models (nonlinear.py):

ClassMethod KeyHook Logic
AngularSteerModelANGULAR2D rotation in the plane defined by target direction
ActivationTransportSteerModelACTApply affine transport map and interpolate with coeff
PIDSteerModelPIDAdd PID-computed dense vector with optional norm preservation
CurveballSteerModelCURVEBALLProject to KPCA space, steer, inverse transform
FlowSteerModelFLOWSolve learned flow from current residual to target distribution
FlowGLPFLOWGLPSubspace GLP flow steering
ODESteerModelODEKernel classifier ODE integration
BIPOSteerModelBIPOBilateral preference optimization hook
CHARSSteerModelCHARSConcept heterogeneity-aware addition
LinNEASSteerModelLINNEASLinearized non-linear steering
LoReFTSteerModelREFT / LOREFT / REPSLow-rank residual intervention
FLASSteerModelFLASConcept-conditioned Euler flow via HF module hooks

SAE steer models (sae.py):

ClassMethod KeyHook Logic
SASSteerModelSASSAE encode → modify latent → SAE decode
SRESteerModelSRESAE encode → add sparse features → SAE decode
SSVSteerModelSSVSAE encode → add SSV to latent space → SAE decode
SPARESteerModelSPARESAE encode → clamp selected features → SAE decode
SRPSSteerModelSRPSSAE encode → scale features by coefficient → SAE decode
SAERSVSteerModelSAE-RSVDirect residual stream addition
SAETSSteerModelSAE-TSSAE encode → apply LinearAdapter → SAE decode
SAEIOSteerModelSAEIOSAE encode → amplify selected features → SAE decode
SAECoTSteerModelSAE-COTSAE encode → overwrite GT latent features → SAE decode
FeatSteerModelFEATDirect feature list manipulation
CorrSteerModelCORRSTEERDirect residual stream addition (correlation-weighted)

evaluators/ — Evaluation Metrics

13 evaluator types registered in EVALUATOR_MAP:

Evaluator KeyClassUse Case
multiple_choiceMultipleChoiceMatcherA/B/C/D answer matching
mathMathMatcherNumerical answer extraction & comparison
semanticSemanticMatcherEmbedding-based semantic similarity
refusalRefusalMatcherDetects if model refuses (keyword + LLM judge)
logitLogitMatcherSingle-token logit-based evaluation
perplexityPerplexityMatcherResponse perplexity measurement
coherenceCoherenceMatcherText coherence scoring
sycophancyBehaviorMatcher(mode='sycophancy')Sycophancy detection
corrigibleBehaviorMatcher(mode='corrigible')Corrigibility detection
coordinateBehaviorMatcher(mode='coordinate')Coordination behavior detection
politicsBehaviorMatcher(mode='politics')Political leaning detection
cast_refusalCastRefusalMatcherCAST-specific refusal detection

config/ — Configuration System

  • models.py: ModelConfig + MODEL_SAE_REGISTRY — supported models: gemma-2-2b, gemma-2-2b-it, gemma-2-9b, llama-2-7b-hf, llama-3.1-8b
  • methods.py: ExtractorConfig and SteerConfig dataclasses with all hyperparameter defaults (single source of truth). Covers all 30+ methods.
  • datasets.py: TrainDatasetConfig, TestDatasetConfig with path roots (TRAIN_ROOT, TEST_ROOT)
  • pipeline.py: PipelineConfig — top-level experiment configuration, JSON-serializable
  • post_process.py: PostProcessConfig — GLP denoising + classifier guidance settings
  • results.py: SampleResult, EvalResult, SteeringVector — result containers

post_process/ — GLP Post-Processing Pipeline

An optional module that applies GLP (Generative Latent Prior) denoising with optional classifier guidance on top of any steering method.

Core idea:

  1. Train a classifier p_phi(y | z_t, t) from contrastive activations:

    • Build concept-present (h+) and concept-absent (h-) clean activations
    • Sample t ~ U(0, 1) and noise eps; create noisy sample z_t = (1 - t) * h + t * eps
    • Train binary classifier with timestep conditioning
  2. During reverse denoising, use: z_{t-1} = z_t + Δt * u_theta(z_t, t) + s * grad_z log p_phi(y_target | z_t, t)

Train classifier:

python -m Steering.post_process.cli train-classifier \
  --model-name google/gemma-2-2b-it \
  --dataset-name sycophancy \
  --layer 14 --hook-point pre --position last \
  --n-layers 4 --d-model 256 --d-mlp 512 --t-embed-dim 128 \
  --num-epochs 5 --train-batch-size 512 \
  --save-root ./GLP --run-name classifier-stream

Use in pipeline config:

"post_process": {
  "enabled": true,
  "source": "PQPQPQHUST/glp-gemma",
  "checkpoint": "final",
  "layer": 14,
  "hook_point": "pre",
  "position": "last",
  "noise_rate": 0.5,
  "num_timesteps": 20,
  "use_classifier": true,
  "scale": 1.0,
  "negative": false,
  "classifier_checkpoint": "final"
}

data/ — Data Loading

  • loader.py: DataLoader (loads train data with formatting) and EvalDataLoader (loads test data)
  • readers.py: File readers — read_json, read_jsonl, read_csv with automatic format detection
  • formatters.py: Schema-specific formatters converting raw data into (target_prompts, contrast_prompts) pairs
  • data_registry.py: Three registries — TRAIN_DATASET_REGISTRY (20+ datasets), COMPOSITE_DATASET_REGISTRY, TEST_DATASET_REGISTRY (25+ datasets)

utils.py — Shared Utilities

FunctionPurpose
get_hook_name(layer, position)Returns TransformerLens hook name (e.g., blocks.14.hook_resid_pre)
get_resid_acts(resid, position)Extract activations at position ("last", "all", or int)
set_resid_acts(resid, position, acts)Set activations at position
collect_dense_activations(...)Batch-collect activations across layers (used by OT stats computation)
cache_logit_lens(model, sae, k, batch_size)Compute top-k logit lens tokens per SAE feature
get_output_score(model, sae, layer, feature_idx, ...)Compute output score for a feature
build_chat_input(tokenizer, prompt, system_prompt)Format prompt using tokenizer's chat template
make_params(func, **kwargs)Filter kwargs to match function signature
load_fallback_sae(release, sae_id, ...)Download & patch SAE for sae_lens compatibility

cli.py — Command Line Interface

python -m Steering.cli --task eval    --config Configs/Eval/caa_example.json
python -m Steering.cli --task run     --config Configs/Inference/cast_refusal.json
python -m Steering.cli --task extract --config Configs/Eval/spare_sycophancy.json

Datasets

TrainDataset/ — For Vector Extraction

Training data is organized by behavioral category. Each dataset provides target/contrast prompt pairs used to extract steering vectors via contrastive methods.

CategoryDatasetsSchema
Sycophancysycophancy, sycophancy_nlp_survey, sycophancy_philosophy, sycophancy_politicalbinary_choice
AI Safetyai_risk_coordinate, ai_risk_corrigible, ai_risk_myopic, ai_risk_survivalbinary_choice
Refusalrefusal (CAST), refusal_sorrybench, refusal_caa, refusal_advbench, refusal_cast_alpacaVarious
Hallucinationhallucinationbinary_choice
Politicspolitics_twinviewstwin_views
Roleplayroleplay_arithmetic, roleplay_common_sensequestion_only
Reasoninggms8k_train, svamp_trainmath_question
QAcsqa_train, simple_qa, nqswap_trainVarious

Composite datasets (multi-file combinations):

NameComponentsUse Case
refusal_cast_responsesCAST responses + alpaca questionsCAST conditional steering
srps_roleplay_gms8kRoleplay prompts + GMS8K questionsSRPS roleplay arithmetic
srps_roleplay_csqaRoleplay prompts + CSQA questionsSRPS roleplay commonsense
refusal_angularAdvBench harmful + alpaca harmlessAngular refusal steering

TestDataset/ — For Evaluation

Test data is used to evaluate the effect of steering on model capabilities.

CategoryDatasetsEvaluator
QAcsqa (CommonsenseQA)logit
Mathgms8k, svampmath
Knowledgemmlulogit
Refusalrefusal_ab, refusal_open, xstest, harmbench, advbench, sorrybench, donotanswerlogit / refusal / cast_refusal
Hallucinationtruthfulqa, hallucination_ab, haluevallogit / semantic
Sycophancysycophancy_ab, sycophancy_openlogit / sycophancy
AI Safetycoordinate_ab/open, corrigible_ab/open, survival_ab, myopic_ablogit / behavior
Politicstwinviews_testpolitics
NQSwapnqswap_testsemantic

Data Format Schemas

SchemaFormatUsed By
binary_choice{question, answer_matching_behavior, answer_not_matching_behavior}Most behavioral datasets
CAST_condition{harmful: [...], harmless: [...]}CAST refusal
math_question{question, answer}Math/reasoning
question_only{question}Open-ended generation
csqa{question, choices, answer}CommonsenseQA
mmlu{question, A, B, C, D, answer}MMLU
twin_viewsTwo-column CSV (liberal/conservative)Political viewpoints
SorryBench{category, question}SorryBench refusal
AdvBenchCSV with goal columnAdversarial behaviors

Verification/

The Verification/ directory contains ground-truth validation scripts that verify the Steering/ implementations match their reference implementations in Code/. Validation is organized into 3 levels of increasing strictness.

Validation Levels

LevelWhat It TestsPass Criteria
Level 1 (Extraction Match)Extracted steering vectors match GT code outputCosine similarity ≥ 0.99 or exact feature overlap
Level 2 (Inference Match)Steered model logits match GT hook outputLogit cosine ≥ 0.95, KL divergence < 1e-6, or top-5 token overlap
Level 3 (Accuracy Match)End-to-end accuracy matches paper-reported resultsAccuracy within reported tolerance

shared_utils.py

Shared test infrastructure used by all validation scripts:

load_sycophancy_data(n_per_class=100)     # → (target_texts, contrast_texts)
load_model_and_sae(layer=20, device="cuda")  # → (model, sae, layer)
get_sae_activations(model, sae, texts, layer)  # → List[Tensor]  per-text SAE latents

Methods Covered

MethodL1 ScriptL2 ScriptL3 Script
AngularLevel1/Angular/validate_angular_l1.pyLevel2/Angular/validate_angular_l2.pyLevel3/Angular/verify_angular_l3.py
CASTLevel1/CAST/compare.pyLevel2/CAST/compare.py—
CorrSteerLevel1/CorrSteer/validate_corrsteer_l1.pyLevel2/CorrSteer/validate_corrsteer_l2.py—
SAEIOLevel1/SAEIO/validate_saeio_l1.pyLevel2/SAEIO/validate_saeio_l2.py—
SAE-SSVLevel1/SAESSV/validate_ssv_l1.pyLevel2/SAESSV/validate_ssv_l2.py—
SAE-TSLevel1/SAETS/validate_saets_l1.pyLevel2/SAETS/validate_saets_l2.py—
SPARELevel1/SPARE/validate_spare_l1.pyLevel2/SPARE/validate_spare_l2.py—

Verification Status

MethodL1L2L3
Angular✅ cos=0.9999⚠️ Semantic match (TL vs HF minor diffs)⏸ Deferred
SAE-TS✅ cos=1.0000✅ KL=8.1e-9⏸
CorrSteer✅ exact=0.00✅ cos=0.99999⏸
SPARE✅ Jaccard=1.0, cos=1.0✅ prob cos=0.9992⏸
SAEIO✅ All 3 tests passed⚠️ Under investigation⏸
SAE-SSV✅ ANOVA + classifier match✅ cos>0.99⏸

Running Verification Scripts

conda activate sae_circuit
unset CUDA_VISIBLE_DEVICES

# Level 1 (extraction match)
python Verification/Level1/SPARE/validate_spare_l1.py
python Verification/Level1/CorrSteer/validate_corrsteer_l1.py

# Level 2 (inference match)
python Verification/Level2/SAETS/validate_saets_l2.py
python Verification/Level2/SPARE/validate_spare_l2.py

Verification_Results/

Contains saved artifacts from verification runs:

FileDescription
effects_2b.ptPre-computed SAE feature effects for Gemma-2-2B
saets_adapter_real.ptTrained SAE-TS LinearAdapter (real data)
saets_vector_real.ptSAE-TS steering vector (real data)
spare_vector.ptSPARE steering vector
ssv_vector.ptSSV steering vector
corr_vector.ptCorrSteer steering vector

Configs/

Pre-built JSON experiment configurations.

Eval/ — Evaluation Configs

Run a complete extract → steer → evaluate pipeline:

ConfigMethodTrain DataTest Data
minimal_example.jsonCAAsycophancycsqa
corrsteer_sycophancy.jsonCORRSTEERsycophancycsqa
saets_sycophancy.jsonSAE-TSsycophancycsqa
sas_hallucination.jsonSAShallucinationtruthfulqa
spare_sycophancy.jsonSPAREsycophancycsqa
spare_nqswap.jsonSPAREnqswap_trainnqswap_test
sre_sycophancy.jsonSREsycophancycsqa
ssv_politics.jsonSSVpolitics_twinviewstwinviews_test
ssv_ai_risk.jsonSSVai_risk_coordinatecoordinate_ab

Inference/ — Generation Configs

Run steered text generation without evaluation:

ConfigMethodUse Case
caa_example.jsonCAABasic CAA generation
cast_refusal.jsonCASTConditional refusal steering
srps_roleplay_gms8k.jsonSRPSRoleplay + math generation

Config Structure

{
  "name": "experiment_name",
  "model": {
    "name": "google/gemma-2-2b",
    "device": "cuda",
    "dtype": "bfloat16",
    "sae_release": "gemma-scope-2b-pt-res-canonical",
    "sae_id": "layer_{}/width_16k/canonical"
  },
  "extractor": {
    "method": "CAA",
    "layer": 14,
    "batch_size": 4,
    "hook_point": "pre",
    "train_dataset": "sycophancy",
    "n_train": 100
  },
  "steer": {
    "method": "CAA",
    "layer": 14,
    "coeff": 1.00,
    "hook_point": "pre"
  },
  "test_dataset": "csqa",
  "n_test": 200,
  "output": "./Results/caa"
}

Code/ (Reference Implementations)

Original paper implementations used as ground truth for verification:

DirectoryPaper/MethodKey Files
angular-steering/Angular steeringpytorch_pure/extract_directions.py, direction_forcing.py
CAA/Contrastive Activation AdditionRimsky et al. reference code
CAST/Conditional Activation Steeringactivation_steering/ library
CorrSteer/Correlation-based steeringtrain.py, corrsteer/steer.py
SAE-free/SAE-Free steeringEigendecomposition approach
SAE-SSV/Supervised Steering Vectorsaessv-demo.py (ANOVA + classifier)
SAE-TS/Task-Specific SAE steeringsrc/sae_ts/ft_effects/train.py
SPARE/SPARE feature selectionutils.py, mutual information
saes-are-good-for-steering/SAEIO (Input/Output scores)src/output_score.py, src/utils.py, src/sae_utils.py

Other Directories

Vector/

Pre-extracted steering vectors are stored as folder-per-vector, organized by method. Each vector folder contains:

  • vector.pt: tensor or layer→tensor dict payload
  • metadata.json: extraction metadata sidecar (JSON-serializable)

Legacy single-file .pt paths are still accepted and resolved automatically to the folder layout.

FLAS and REPS use fixed unified paths (Vector/FLAS/gemma_gms8k, Vector/REPS/gemma_reps_gms8k) that must be overwritten on each run — never create sequential filenames.

Experiments/

Experiment scripts, results analysis, and replication code. Every experiment (prefix-split, PID, etc.) lives here as self-contained scripts so results can be verified, re-run, or debugged without hunting across directories. Organized by experiment label.

Experiments/
├── ExpA/          # Per-position SNR (extraction bottleneck test)
├── ExpB/          # Perturbation norm survival
├── ExpC/          # Direction rotation tracking
├── ExpD/          # Logit lens KL / cancellation circuit
├── ExpE/          # Contrastive alignment
├── Exp6/          # Coupling mass (CHARS K-sweep + matrix deep dive)
├── TailFailure/   # Tail distribution → steering failure correlation
├── Prefix/        # Evil prefix splitting analysis
├── Utility/       # Pareto plots, tables, metrics, activations
└── *.pt           # Support data files (e.g. flas_evil_effective_vector.pt)

Notebooks/

Interactive Jupyter notebooks for each method — useful for exploration and debugging:

NotebookPurpose
Angular.ipynbAngular steering exploration
CAA.ipynbCAA vector extraction & evaluation
CAST.ipynbCAST conditional steering
CorrSteer.ipynbCorrSteer correlation analysis
SAE_RSV.ipynbSAE-RSV residual vector
SAE_SSV.ipynbSSV ANOVA + classifier pipeline
SAE_TS.ipynbSAE-TS adapter training
SAS.ipynbSAS contrastive SAE
SPARE.ipynbSPARE mutual information
SRE.ipynbSRE sparse engineering
SRPS.ipynbSRPS semantic projection
Manifold.ipynbManifold-based steering
EvaluationDemo.ipynbEvaluation pipeline demo
playground.ipynbExperimental playground
Steering/post_process/sae.ipynbSAE post-processing analysis
Steering/post_process/subspace_demo.ipynbSubspace GLP demo
Steering/post_process/gemma_demo.ipynbGLP on Gemma demo

Modification Quick Reference

TaskFile(s) to EditWhat to Do
Add steering methodextractors/dense.py or extractors/sae.py + steer_models/Add class + add to map in appropriate submodule
Add training datasetconfig/datasets.pyAdd entry to TRAIN_DATASET_REGISTRY
Add test datasetconfig/datasets.pyAdd entry to TEST_DATASET_REGISTRY
Add data formatterdata/formatters.pyAdd function + add to FORMATTERS dict
Add evaluatorSteering/evaluators/Add class + add to EVALUATOR_MAP
Change param defaultconfig/methods.pyEdit field default in ExtractorConfig or SteerConfig dataclass
Add NEW paramconfig/methods.py + extractors/ or steer_models/Add field to dataclass + accept in class
Add model-SAE mappingconfig/models.pyAdd entry to MODEL_SAE_REGISTRY

Design Principles:

  • "Registration lives with implementation" — each registry dict is at the bottom of the file containing the classes
  • "Single source of truth" — all defaults are in ExtractorConfig/SteerConfig dataclass fields

Adding New Methods

Adding a new steering method requires editing 3 files.

Step 1: Add Extractor Class

Add your extractor class to Steering/extractors/dense.py (for dense methods) or Steering/extractors/sae.py (for SAE methods), then register it in EXTRACTOR_MAP in Steering/extractors/init.py:

class MyExtractor(BaseExtractor):
    """My custom steering vector extractor."""
    
    def __init__(self, model, layer: List[int], my_param: float = 1.0, **kwargs):
        super().__init__(model, layer, **kwargs)
        self.my_param = my_param
    
    def extract(self, target_data, contrast_data=None, **kwargs) -> Dict[int, torch.Tensor]:
        # Implementation — must return Dict[int, Tensor] keyed by layer
        return self.vector

# In Steering/extractors/__init__.py, add to EXTRACTOR_MAP:
EXTRACTOR_MAP = {
    "CAA": CAAExtractor,
    ...
    "MYMETHOD": MyExtractor,  # ← Add this line
}

Step 2: Add Steer Model Class

Add your steer model class to the appropriate file in Steering/steer_models/, then register it in the STEER_MAP in __init__.py:

# For dense methods: add to steer_models/dense.py
# For SAE methods: add to steer_models/sae.py

# Add your class anywhere in the file
class MySteerModel(BaseSteerModel):
    """Apply my steering method."""
    
    def _apply_steering_hook(self, resid, coeff, hook):
        return resid + coeff * self.steering_vector

# Then add to STEER_MAP in steer_models/__init__.py:
STEER_MAP = {
    "CAA": DenseSteerModel,
    ...
    "MYMETHOD": MySteerModel,  # ← Add this line
}

Step 3 (Optional): Add Method to SAE_METHODS

If your method requires SAE, add to Steering/config/methods.py:

# Add to SAE_METHODS if your method requires SAE
SAE_METHODS: Set[str] = {"SAS", "SPARE", ..., "MYMETHOD"}  # Only if uses SAE

Default hyperparameters are defined directly in the ExtractorConfig and SteerConfig dataclass fields.

That's It!

Your method is now available everywhere:

from Steering import SteeringPipeline

print(SteeringPipeline.list_methods())  # [..., 'MYMETHOD']

extractor = SteeringPipeline.create_extractor("MYMETHOD", model, layer=14, my_param=0.5)
steer_model = SteeringPipeline.create_steer_model("MYMETHOD", model, layer=14, extractor=extractor)

Adding New Datasets

Adding datasets requires editing 1 file: Steering/config/datasets.py

For Training Datasets

# Add to TRAIN_DATASET_REGISTRY
TRAIN_DATASET_REGISTRY["my_dataset"] = TrainDatasetConfig(
    file="my_category/train.json",        # Relative path from TRAIN_ROOT
    schema="binary_choice",               # Data format schema
    target_key="correct_prompt",          # Key for target prompts
    contrast_key="false_prompt",          # Key for contrast prompts
)

For Test Datasets

# Add to TEST_DATASET_REGISTRY
TEST_DATASET_REGISTRY["my_test"] = TestDatasetConfig(
    file="my_category/test.json",         # Relative path from TEST_ROOT
    schema="multiple_choice",             # Data format schema
    evaluator="multiple_choice",          # Evaluator type
    prompt_template="Q: {question}\nA:",  # Prompt template
    ground_truth_key="answer",            # Key for ground truth
)

That's It!

Your dataset is now available:

from Steering.data import DataLoader, EvalDataLoader

# Training data
loader = DataLoader()
data = loader.load("my_dataset")
cfg = loader.get_config("my_dataset")
targets = [d[cfg.target_key] for d in data]
contrasts = [d[cfg.contrast_key] for d in data]

# Test data
eval_loader = EvalDataLoader()
test_data = eval_loader.load("my_test")

Available Formatters (Schemas)

SchemaDescriptionUse For
binary_choiceTarget/contrast pairsContrastive training
CAST_conditionConditional steeringCAST method
multiple_choiceA/B/C/D choicesQA evaluation
math_questionQ&A pairsReasoning tasks
question_onlyJust the questionOpen-ended generation
SorryBenchSorryBench formatRefusal training
AdvBenchAdvBench formatHarmful behavior

Adding a New Formatter

If your data has a new schema, add to Steering/data/formatters.py:

# Add formatter function
def format_my_schema(data: List[Dict]) -> Tuple[List, List]:
    """Format my custom data schema."""
    positive, negative = [], []
    for item in data:
        positive.append(item["my_positive_field"])
        negative.append(item["my_negative_field"])
    return positive, negative

# Add to FORMATTERS dict at bottom:
FORMATTERS = {
    ...
    "my_schema": format_my_schema,  # ← Add this line
}

Adding New Evaluators

Adding evaluators requires editing 1 file: Evaluation/scoring_metrics.py

Step 1: Create Evaluator Class and Register

Add your evaluator class, then add to EVALUATOR_MAP at the bottom of the same file:

# Add your class anywhere in the file
class MyEvaluator:
    """My custom evaluator."""
    
    def __init__(self, device: str = "cuda:0", **kwargs):
        self.device = device
    
    def check(self, response: str, expected: str = None, **kwargs) -> Tuple[bool, float]:
        """Return (is_correct, confidence)."""
        # Implementation
        return is_correct, confidence

# At bottom of file, add to EVALUATOR_MAP:
EVALUATOR_MAP = {
    "multiple_choice": MultipleChoiceMatcher,
    "math": MathMatcher,
    "refusal": BehaviorMatcher,
    "semantic": SemanticMatcher,
    "my_evaluator": MyEvaluator,  # ← Add this line
}

That's It!

Your evaluator is now available:

from Steering import EvalPipeline

print(EvalPipeline.list_evaluators())  # [..., 'my_evaluator']

# Internal usage within pipeline

Adjusting Hyperparameters

Changing Default Values

All defaults are in the ExtractorConfig and SteerConfig dataclass fields in Steering/config/methods.py:

@dataclass
class ExtractorConfig:
    method: str
    layer: int
    batch_size: int = 8       # Change default here
    top_k: int = 15           # Change default here
    ...

@dataclass  
class SteerConfig:
    method: str
    layer: int
    coeff: float = 1.0        # Change default here
    ...

Adding a NEW Parameter

Adding a new parameter requires 2 files:

StepFileAction
1config/methods.pyAdd field to ExtractorConfig or SteerConfig dataclass
2extractors.py or steer_models/Accept & use the parameter in the class

Example - Adding a temperature parameter:

# Step 1: config/methods.py - Add to dataclass
@dataclass
class ExtractorConfig:
    ...
    temperature: float = 1.0  # ← NEW FIELD with default

# Step 2: extractors.py or steer_models/dense.py - Accept and use it
class MyExtractor(BaseExtractor):
    def __init__(self, model, layer, temperature=1.0, **kwargs):
        super().__init__(model, layer, **kwargs)
        self.temperature = temperature

Testing

Run All Tests

# Run full test suite
python -m pytest tests/ -v

# With coverage
python -m pytest tests/ --cov=Steering --cov-report=html

Run Specific Tests

# Test config module
python -m pytest tests/test_config.py -v

# Test data loading
python -m pytest tests/test_data_loader.py -v

Validate a Config

python -m Steering.validate_config Configs/caa_example.json

Troubleshooting

Common Issues

1. CUDA Out of Memory

# Reduce batch size in config
"extractor": {"batch_size": 4}

# Or use float16 instead of bfloat16
"model": {"dtype": "float16"}

2. HuggingFace Authentication Error

# Make sure token is set
export HF_TOKEN="hf_..."

# Or login
huggingface-cli login

3. SAE Not Available for Model

SAE methods (SAS, SPARE, etc.) currently only support Gemma models.
Use dense methods (CAA, CAST, ANGULAR) for other models.

4. Dataset Not Found

python -c "from Steering.config import TRAIN_DATASET_REGISTRY; print(list(TRAIN_DATASET_REGISTRY.keys()))"

5. FLAS / REPS Disk Full

These methods produce multi-GB checkpoints. Always use the unified overwrite paths and never create sequential filenames:

  • FLAS: Vector/FLAS/gemma_gms8k
  • REPS: Vector/REPS/gemma_reps_gms8k

Contributing

We welcome contributions! Here's how:

Code Style

# Format code
black Steering/ Evaluation/
isort Steering/ Evaluation/

# Type check
mypy Steering/

Pull Request Process

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests: python -m pytest tests/
  6. Submit a pull request

What We Need

  • More steering methods
  • More evaluation datasets
  • Support for more models (Llama, Mistral)
  • Better documentation
  • Performance optimizations

License

MIT License


Citation

If you use this benchmark in your research, please cite:

@software{sae_steering_bench,
  title = {SAESteeringBench: A Unified Framework for Steering Vector Methods},
  year = {2025},
  url = {https://github.com/PQHSGS/SAESteeringBench}
}