#!/bin/bash # SAE feature verification / patching driver (mechanistic_interp/sae_patching.py). # # DEFAULT MODE = validate: the only mode that actually CONFIRMS a feature set. # For each (src=concept image, tgt=clean image) pair and each layer it runs: # insertion (SUFFICIENCY): add the src selected-feature contribution into the # clean image -> the concept token should go UP. # ablation (NECESSITY): remove the selected features from the concept image # -> the concept token should go DOWN. # Each is compared against N_RANDOM random feature sets (matched count) and # averaged over all image pairs. Confirmed when, at the responsible layers: # insertion: selected >> random > 0 AND ablation: selected << random < 0. # The SAE is the trained checkpoint loaded as TopKSAE (per-token top-k=8). # # SOURCE images default to SAMPLES_N images from samples.json where SAMPLES_COL==1 # (concept present, e.g. bathroom), each paired with the single clean TGT_IMAGE # (cat). Set SAMPLES="" to instead supply explicit SRC_IMAGES/TGT_IMAGES lists. # # Tunables (override via env): # MODE validate | feature_patch | causal | steering (default validate) # FEATURES_JSON per-layer feature set to verify (bathroom or toilet) # TOKEN concept token to measure (bath; toilet 1st subword is to) # PREFIX teacher-forced text after "ASSISTANT:" (next-token = TOKEN) # SAMPLES samples.json to draw concept-present sources from (""=use lists) # SAMPLES_COL samples.json column marking concept-present ==1 (default bathroom) # SAMPLES_N number of source images to take (default 20) # IMAGE_ROOT dir holding .jpg for SAMPLES # TGT_IMAGE single clean target image, reused per source (default cat.png) # SRC_IMAGES/TGT_IMAGES explicit space-separated lists (used only when SAMPLES="") # N_RANDOM random-feature control draws per layer (default 20; >50 is waste) # SEED, SAE_CKPT, HOOK_TYPE, DEVICE, DTYPE, OUTPUT_PREFIX # # Usage: # bash mechanistic_interp/scripts/sae_patching.sh # bathroom: 20 src -> cat # SAMPLES_N=40 bash mechanistic_interp/scripts/sae_patching.sh # more source images # FEATURES_JSON=.../toilet_top20.json TOKEN=to SAMPLES_COL=toilet \ # bash mechanistic_interp/scripts/sae_patching.sh # verify toilet feats # SAMPLES="" SRC_IMAGES="a.jpg b.jpg" TGT_IMAGES="c.png d.png" \ # bash mechanistic_interp/scripts/sae_patching.sh # explicit pairs set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" MODEL_NAME="${MODEL_NAME:-llava-hf/llava-1.5-7b-hf}" SAE_CKPT="${SAE_CKPT:-/data/caotue/SAE_checkpoints/16_d_model/last.ckpt}" FEATURES_JSON="${FEATURES_JSON:-${REPO_ROOT}/mechanistic_interp/probes/selected_features_f1_post_toilet_top10.json}" MODE="${MODE:-validate}" TOKEN="${TOKEN:-toilet}" # bathroom: bath ; toilet: use PREFIX '...a to' + TOKEN_ID=488 ('ile') TOKEN_ID="${TOKEN_ID:-488}" # optional exact vocab id (overrides TOKEN); e.g. 488 = 'ile' in 'toilet' PREFIX="${PREFIX:-This image features a to}" HOOK_TYPE="${HOOK_TYPE:-post}" DEVICE="${DEVICE:-cuda:4}" DTYPE="${DTYPE:-bfloat16}" N_RANDOM="${N_RANDOM:-20}" # random features SEED="${SEED:-0}" PATCH_BATCH="${PATCH_BATCH:-16}" # feature sets patched per forward (higher=faster, more VRAM) # SOURCE = SAMPLES_N concept-present images from samples.json (SAMPLES_COL==1). # TARGET = one clean image (cat), reused for every source. SAMPLES="${SAMPLES:-${REPO_ROOT}/mechanistic_interp/toilet_bathroom/samples.json}" SAMPLES_COL="${SAMPLES_COL:-toilet}" # bathroom, toilet SAMPLES_N="${SAMPLES_N:-20}" # Output name defaults to validate_ (e.g. validate_bathroom, validate_toilet) # -> ${OUTPUT_PREFIX}_results.json and ${OUTPUT_PREFIX}_validate.png. OUTPUT_PREFIX="${OUTPUT_PREFIX:-mechanistic_interp/graph/sae_patch/validate_${SAMPLES_COL}}" IMAGE_ROOT="${IMAGE_ROOT:-/data/caotue/CC3M-Dataset/cc3m_images/train}" TGT_IMAGE="${TGT_IMAGE:-${REPO_ROOT}/mechanistic_interp/toilet_bathroom/cat.png}" # Explicit pairs, used only when SAMPLES="" (paired by index). SRC_IMAGES="${SRC_IMAGES:-${IMAGE_ROOT}/001607409.jpg}" TGT_IMAGES="${TGT_IMAGES:-${TGT_IMAGE}}" # ------------------------------------------------------------------ # mkdir -p "$(dirname "${OUTPUT_PREFIX}")" echo "========================================================" echo " SAE feature verification (mode=${MODE})" echo "========================================================" echo " Features : ${FEATURES_JSON}" echo " Token : 'ASSISTANT:${PREFIX}' -> measuring '${TOKEN}'" if [ -n "${SAMPLES}" ]; then echo " Source : ${SAMPLES_N} images from ${SAMPLES} (${SAMPLES_COL}==1)" echo " Target : ${TGT_IMAGE} (clean, reused per source)" else echo " Src images : ${SRC_IMAGES}" echo " Tgt images : ${TGT_IMAGES}" fi echo " N random : ${N_RANDOM} | seed ${SEED}" echo " SAE ckpt : ${SAE_CKPT} (loaded as TopKSAE, top-k=8)" echo " Device : ${DEVICE} / ${DTYPE} | hook ${HOOK_TYPE}" echo " Output : ${OUTPUT_PREFIX}_{validate.png,results.json}" echo "========================================================" cd "${REPO_ROOT}" export PYTHONPATH="$(cd .. && pwd):$(pwd):${PYTHONPATH:-}" ARGS=( --model_name "${MODEL_NAME}" --mode "${MODE}" --tgt_image "${TGT_IMAGE}" --prefix "${PREFIX}" --token "${TOKEN}" --hook_type "${HOOK_TYPE}" --device "${DEVICE}" --dtype "${DTYPE}" --sae_ckpt "${SAE_CKPT}" --features_json "${FEATURES_JSON}" --n_random "${N_RANDOM}" --seed "${SEED}" --patch_batch "${PATCH_BATCH}" --output_prefix "${OUTPUT_PREFIX}" ) [ -n "${TOKEN_ID}" ] && ARGS+=(--token_id "${TOKEN_ID}") if [ -n "${SAMPLES}" ]; then # samples mode: build sources from samples.json; --src_image is a required # placeholder (unused in samples mode), so point it at the clean target. ARGS+=( --src_image "${TGT_IMAGE}" --samples "${SAMPLES}" --samples_col "${SAMPLES_COL}" --samples_n "${SAMPLES_N}" --image_root "${IMAGE_ROOT}" ) else ARGS+=( --src_image "${SRC_IMAGES%% *}" --src_images ${SRC_IMAGES} --tgt_images ${TGT_IMAGES} ) fi python -m mechanistic_interp.confirm_features_patching "${ARGS[@]}" "$@"