File size: 6,544 Bytes
a2ffd07
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/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 <image_id>.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_<column> (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[@]}" "$@"