Official implementation of "CASteer: Cross-Attention Steering for Controllable Concept Erasure" (arXiv:2503.09630).
CASteer is a training-free framework for concept erasure in diffusion models using steering vectors applied to cross-attention outputs.
CASteer precomputes concept-specific steering vectors by averaging cross-attention outputs from images generated with paired positive/negative prompts. During inference, it projects out the concept direction from CA outputs.

Steering vectors from distilled variants (SDXL-Turbo, SANA-Sprint) are used for efficiency and transfer to the full models.
git clone https://github.com/atmyre/CASteer.git
cd CASteer
python3 -m venv .venv
source .venv/bin/activate
# For macOS
pip install -r requirements/darwin.txt
# For Linux
pip install -r requirements/linux.txt
# Set up HuggingFace authentication
export HF_TOKEN=your_huggingface_token
CASteer/
├── core/ # Core library
│ ├── controller.py # CASteer steering logic (Eq. 5, 6)
│ ├── diffusion_steering.py # Cross-attention hook registration
│ ├── construct_prompts.py # Paired prompt generation for steering vectors
│ ├── vector_dump.py # Activation statistics collection
│ └── eval/ # CLIP score and FID evaluation
├── scripts/diffusion/
│ ├── estimate_steering_vectors.py # Step 1: compute steering vectors from paired prompts
│ ├── run_with_steering.py # Step 2: generate with steering
│ └── produce_scores.py # Step 3: evaluate (CLIP, FID)
├── exp/
│ ├── datasets/eval/ # Evaluation prompt templates (ImageNet, COCO)
└── requirements/
Steering vectors are computed by generating images from matched prompt pairs that differ only by the target concept, then taking the normalized difference of mean cross-attention outputs.
# Concrete concept (50 ImageNet-based prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept snoopy \
--mode concrete \
--num_prompts 50 \
--output_dir ./results/sdxl/steering_vectors
# Human-related / safety concept (104 prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept nudity \
--mode human-related \
--output_dir ./results/sdxl/steering_vectors
# Style concept (50 prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept "Van Gogh" \
--mode style \
--num_prompts 50 \
--output_dir ./results/sdxl/steering_vectors
Default steering strength for all concepts: beta = 2 (Householder reflection, preserves L2 norm).
python scripts/diffusion/run_with_steering.py \
--model_name sdxl \
--generate_concept snoopy \
--output_dir ./results/sdxl/erase_snoopy/casteer-2.0 \
--steering_strength 2.0 \
--intermediate_clipping \
--num_images_per_prompt 10 \
--seed 42 \
erase \
--concept_path ./results/sdxl/steering_vectors/snoopy.pt
80 CLIP templates × 10 images = 800 images per concept. Tests whether the target concept is erased while unrelated concepts are preserved.
# 1. Generate baseline images (no steering)
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept snoopy \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_snoopy/orig
# 2. Generate steered images (erase snoopy)
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept snoopy \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_snoopy/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/snoopy.pt
# 3. Also generate for preservation concepts (mickey, dog, etc.) with same steering
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept mickey \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_mickey/orig
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept mickey \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_mickey/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/snoopy.pt
# 4. Compute CLIP score and FID
# - CLIP score measures concept presence in generated images
# - FID is computed between orig/ and each steered method directory
python scripts/diffusion/produce_scores.py \
--concept snoopy mickey \
--dir ./results/sd14/eval_snoopy \
--num_workers 4 --batch_size 32
python scripts/diffusion/produce_scores.py \
--concept snoopy mickey \
--dir ./results/sd14/eval_mickey \
--num_workers 4 --batch_size 32
Results are saved to clip_score.tsv and fid.tsv in each evaluation directory.
Generates images from 30K COCO captions to measure FID and CLIP score degradation on unrelated content.
# 1. Generate baseline
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept coco \
--output_dir ./results/sd14/coco/orig
# 2. Generate with steering
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept coco \
--output_dir ./results/sd14/coco/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/nudity.pt
# 3. Compute FID between baseline and steered generations
python scripts/diffusion/produce_scores.py \
--dir ./results/sd14/coco \
--num_workers 4 --batch_size 32
4,703 prompts from the I2P dataset, evaluated with NudeNet and Q16 classifiers.
# Baseline (no steering)
python scripts/diffusion/run_i2p_eval.py \
--model_name sd14 --output_dir ./results/i2p/baseline
# With nudity erasure
python scripts/diffusion/run_i2p_eval.py \
--model_name sd14 --output_dir ./results/i2p/casteer-2.0 \
--concept_path ./results/sd14/steering_vectors/nudity.pt \
--steering_strength 2.0 --intermediate_clipping
Evaluation of generated I2P images is done using Receler code
| Parameter | Default | Description |
|---|---|---|
--steering_strength | 2.0 | Beta parameter. 2.0 = Householder reflection |
--intermediate_clipping | off | Clip projection to >= 0 (recommended, Eq. 6) |
--mode | concrete | Prompt template mode: concrete, style, human-related |
--num_prompts | 50 | Number of prompt pairs (concrete/style). Human-related always uses 104 |
--template_path | imagenet | Evaluation templates: clip_templates.json (800 imgs) or imagenet/template.json |
@article{gaintseva2025casteer,
title={CASteer: Cross-Attention Steering for Controllable Concept Erasure},
author={Gaintseva, Tatiana and Oncescu, Andreea-Maria and Ma, Chengcheng and Liu, Ziquan and Benning, Martin and Slabaugh, Gregory and Deng, Jiankang and Elezi, Ismail},
journal={arXiv preprint arXiv:2503.09630},
year={2025}
}
MIT License - see LICENSE for details.
Python
100.0%
Official implementation of "CASteer: Cross-Attention Steering for Controllable Concept Erasure" (arXiv:2503.09630).
CASteer is a training-free framework for concept erasure in diffusion models using steering vectors applied to cross-attention outputs.
CASteer precomputes concept-specific steering vectors by averaging cross-attention outputs from images generated with paired positive/negative prompts. During inference, it projects out the concept direction from CA outputs.

Steering vectors from distilled variants (SDXL-Turbo, SANA-Sprint) are used for efficiency and transfer to the full models.
git clone https://github.com/atmyre/CASteer.git
cd CASteer
python3 -m venv .venv
source .venv/bin/activate
# For macOS
pip install -r requirements/darwin.txt
# For Linux
pip install -r requirements/linux.txt
# Set up HuggingFace authentication
export HF_TOKEN=your_huggingface_token
CASteer/
├── core/ # Core library
│ ├── controller.py # CASteer steering logic (Eq. 5, 6)
│ ├── diffusion_steering.py # Cross-attention hook registration
│ ├── construct_prompts.py # Paired prompt generation for steering vectors
│ ├── vector_dump.py # Activation statistics collection
│ └── eval/ # CLIP score and FID evaluation
├── scripts/diffusion/
│ ├── estimate_steering_vectors.py # Step 1: compute steering vectors from paired prompts
│ ├── run_with_steering.py # Step 2: generate with steering
│ └── produce_scores.py # Step 3: evaluate (CLIP, FID)
├── exp/
│ ├── datasets/eval/ # Evaluation prompt templates (ImageNet, COCO)
└── requirements/
Steering vectors are computed by generating images from matched prompt pairs that differ only by the target concept, then taking the normalized difference of mean cross-attention outputs.
# Concrete concept (50 ImageNet-based prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept snoopy \
--mode concrete \
--num_prompts 50 \
--output_dir ./results/sdxl/steering_vectors
# Human-related / safety concept (104 prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept nudity \
--mode human-related \
--output_dir ./results/sdxl/steering_vectors
# Style concept (50 prompt pairs)
python scripts/diffusion/estimate_steering_vectors.py \
--model_name sdxl-turbo \
--concept "Van Gogh" \
--mode style \
--num_prompts 50 \
--output_dir ./results/sdxl/steering_vectors
Default steering strength for all concepts: beta = 2 (Householder reflection, preserves L2 norm).
python scripts/diffusion/run_with_steering.py \
--model_name sdxl \
--generate_concept snoopy \
--output_dir ./results/sdxl/erase_snoopy/casteer-2.0 \
--steering_strength 2.0 \
--intermediate_clipping \
--num_images_per_prompt 10 \
--seed 42 \
erase \
--concept_path ./results/sdxl/steering_vectors/snoopy.pt
80 CLIP templates × 10 images = 800 images per concept. Tests whether the target concept is erased while unrelated concepts are preserved.
# 1. Generate baseline images (no steering)
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept snoopy \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_snoopy/orig
# 2. Generate steered images (erase snoopy)
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept snoopy \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_snoopy/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/snoopy.pt
# 3. Also generate for preservation concepts (mickey, dog, etc.) with same steering
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept mickey \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_mickey/orig
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept mickey \
--template_path exp/datasets/eval/clip_templates.json \
--num_images_per_prompt 10 \
--output_dir ./results/sd14/eval_mickey/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/snoopy.pt
# 4. Compute CLIP score and FID
# - CLIP score measures concept presence in generated images
# - FID is computed between orig/ and each steered method directory
python scripts/diffusion/produce_scores.py \
--concept snoopy mickey \
--dir ./results/sd14/eval_snoopy \
--num_workers 4 --batch_size 32
python scripts/diffusion/produce_scores.py \
--concept snoopy mickey \
--dir ./results/sd14/eval_mickey \
--num_workers 4 --batch_size 32
Results are saved to clip_score.tsv and fid.tsv in each evaluation directory.
Generates images from 30K COCO captions to measure FID and CLIP score degradation on unrelated content.
# 1. Generate baseline
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept coco \
--output_dir ./results/sd14/coco/orig
# 2. Generate with steering
python scripts/diffusion/run_with_steering.py \
--model_name sd14 \
--generate_concept coco \
--output_dir ./results/sd14/coco/casteer-2.0 \
--steering_strength 2.0 --intermediate_clipping \
erase --concept_path ./results/sd14/steering_vectors/nudity.pt
# 3. Compute FID between baseline and steered generations
python scripts/diffusion/produce_scores.py \
--dir ./results/sd14/coco \
--num_workers 4 --batch_size 32
4,703 prompts from the I2P dataset, evaluated with NudeNet and Q16 classifiers.
# Baseline (no steering)
python scripts/diffusion/run_i2p_eval.py \
--model_name sd14 --output_dir ./results/i2p/baseline
# With nudity erasure
python scripts/diffusion/run_i2p_eval.py \
--model_name sd14 --output_dir ./results/i2p/casteer-2.0 \
--concept_path ./results/sd14/steering_vectors/nudity.pt \
--steering_strength 2.0 --intermediate_clipping
Evaluation of generated I2P images is done using Receler code
| Parameter | Default | Description |
|---|---|---|
--steering_strength | 2.0 | Beta parameter. 2.0 = Householder reflection |
--intermediate_clipping | off | Clip projection to >= 0 (recommended, Eq. 6) |
--mode | concrete | Prompt template mode: concrete, style, human-related |
--num_prompts | 50 | Number of prompt pairs (concrete/style). Human-related always uses 104 |
--template_path | imagenet | Evaluation templates: clip_templates.json (800 imgs) or imagenet/template.json |
@article{gaintseva2025casteer,
title={CASteer: Cross-Attention Steering for Controllable Concept Erasure},
author={Gaintseva, Tatiana and Oncescu, Andreea-Maria and Ma, Chengcheng and Liu, Ziquan and Benning, Martin and Slabaugh, Gregory and Deng, Jiankang and Elezi, Ismail},
journal={arXiv preprint arXiv:2503.09630},
year={2025}
}
MIT License - see LICENSE for details.
Python
100.0%