| # ============================================================================= | |
| # Train_Probe_SAE.sh — Train binary probe(s) on SAE feature activations | |
| # | |
| # Two-phase pipeline | |
| # ────────────────── | |
| # Phase 1 (one VLM pass + SAE): for each image generate caption, run forced | |
| # forward pass, extract hook activations, SAE encode + max-pool, keep only | |
| # pooled feature vectors in memory (no disk writes). | |
| # | |
| # Phase 2: gather pooled vectors from all ranks → train binary linear probes | |
| # per hook point/layer. All probes share the same positive/negative split. | |
| # | |
| # Multi-GPU: set NUM_GPUS>1 to shard VLM inference with torchrun. | |
| # Single-GPU: NUM_GPUS=1 (default). | |
| # | |
| # Override variables inline, e.g.: | |
| # NUM_GPUS=4 PROBE_TYPE=bathroom bash training/scripts/Train_Probe_SAE.sh | |
| # ============================================================================= | |
| wandb login --relogin "${WANDB_API_KEY}" | |
| export HF_HOME="/data/caotue/hf_cache" | |
| export HF_DATASETS_CACHE="/data/caotue/hf_cache/datasets" | |
| export TORCH_HOME="/data/caotue/torch_cache" | |
| export TMPDIR="/data/caotue/tmp" | |
| export UV_CACHE_DIR="/data/caotue/uv" | |
| export PIP_CACHE_DIR="/data/caotue/pip" | |
| # ── GPU count ───────────────────────────────────────────────────────────────── | |
| NUM_GPUS="${NUM_GPUS:-1}" | |
| # ── Model / SAE ─────────────────────────────────────────────────────────────── | |
| SAE_CKPT="/data/caotue/SAE_checkpoints/16_d_model/last.ckpt" | |
| MODEL_NAME="${MODEL_NAME:-llava-hf/llava-1.5-7b-hf}" | |
| DEVICE_ID="${DEVICE_ID:-4}" | |
| DTYPE="${DTYPE:-bfloat16}" | |
| # ── Data ────────────────────────────────────────────────────────────────────── | |
| IMAGE_FOLDER="/data/caotue/CC3M-Dataset/cc3m_images" | |
| # HF dataset with binary relation labels (must have 'train' and 'validation' splits). | |
| # ID_COL : column holding the image filename stem. | |
| # PROBE_TYPE: binary label column used as positives (label==1 → B-only + A+B). | |
| # Examples: | |
| # HF_DATASET="pbcong/bathroom-toilet" PROBE_TYPE="toilet" | |
| # HF_DATASET="pbcong/bathroom-toilet" PROBE_TYPE="bathroom" | |
| # HF_DATASET="pbcong/cat-dog" PROBE_TYPE="cat" | |
| HF_DATASET="${HF_DATASET:-pbcong/bathroom-toilet}" | |
| ID_COL="${ID_COL:-image_id}" | |
| PROBE_TYPE="${PROBE_TYPE:-toilet}" | |
| # Contrastive object column (optional). | |
| # When set: HF negatives = other_object=1 & probe_type=0 (from train/val splits). | |
| # Example: OTHER_OBJECT=toilet when PROBE_TYPE=bathroom | |
| OTHER_OBJECT="${OTHER_OBJECT:-bathroom}" | |
| # JSON file with negative image IDs, pre-split: | |
| # {"train": [...image_ids], "validation": [...image_ids]} | |
| # Must be a valid JSON file. | |
| NEG_JSONL="${NEG_JSONL:-mechanistic_interp/neg_cc3m_5k.json}" | |
| # ── Hook specification ──────────────────────────────────────────────────────── | |
| # LAYERS : space-separated layer indices; one probe is trained per layer | |
| # HOOK_TYPE : hook suffix — pre | mid | post | | |
| LAYERS="${LAYERS:-}" # e.g. "10 15 20" for three probes, or empty for all layers | |
| HOOK_TYPE="${HOOK_TYPE:-post}" | |
| # ── SAE ─────────────────────────────────────────────────────────────────────── | |
| SAE_BATCH="${SAE_BATCH:-2048}" | |
| # ── Generation ──────────────────────────────────────────────────────────────── | |
| MAX_NEW_TOKENS="${MAX_NEW_TOKENS:-256}" | |
| QUESTION="${QUESTION:-Describe this image.}" | |
| BATCH_SIZE="${BATCH_SIZE:-64}" | |
| # ── Probe training ──────────────────────────────────────────────────────────── | |
| PROBE_BATCH_SIZE="${PROBE_BATCH_SIZE:-256}" | |
| PROBE_EPOCHS="${PROBE_EPOCHS:-10}" | |
| PROBE_LR="${PROBE_LR:-0.01}" | |
| # ── Output ──────────────────────────────────────────────────────────────────── | |
| SAVE_DIR="/data/caotue/SAE_checkpoints/16_d_model/probes_${PROBE_TYPE}" | |
| # ── Dry run ─────────────────────────────────────────────────────────────────── | |
| # Set DRY_RUN=1 to smoke-test the pipeline on a tiny sample before a real run. | |
| # DRY_RUN=1 bash training/scripts/Train_Probe_SAE.sh | |
| # DRY_RUN_IMAGES controls how many images are processed (default 8). | |
| DRY_RUN="${DRY_RUN:-0}" | |
| DRY_RUN_IMAGES="${DRY_RUN_IMAGES:-4}" | |
| # ============================================================================= | |
| # Validation | |
| # ============================================================================= | |
| cd "$(dirname "$0")/../.." | |
| if [ ! -f "${SAE_CKPT}" ]; then | |
| echo "Error: SAE checkpoint not found: ${SAE_CKPT}" >&2 | |
| exit 1 | |
| fi | |
| if [ ! -d "${IMAGE_FOLDER}" ]; then | |
| echo "Error: image_folder not found: ${IMAGE_FOLDER}" >&2 | |
| exit 1 | |
| fi | |
| # ============================================================================= | |
| # Run | |
| # ============================================================================= | |
| COMMON_ARGS=( | |
| -m training.Train_Probe_SAE | |
| --sae_ckpt "${SAE_CKPT}" | |
| --model_name "${MODEL_NAME}" | |
| --device_id "${DEVICE_ID}" | |
| --dtype "${DTYPE}" | |
| --image_folder "${IMAGE_FOLDER}" | |
| --hf_dataset "${HF_DATASET}" | |
| --id_col "${ID_COL}" | |
| --probe_type "${PROBE_TYPE}" | |
| --neg_jsonl "${NEG_JSONL}" | |
| --hook_type "${HOOK_TYPE}" | |
| --sae_batch "${SAE_BATCH}" | |
| --max_new_tokens "${MAX_NEW_TOKENS}" | |
| --question "${QUESTION}" | |
| --probe_batch_size "${PROBE_BATCH_SIZE}" | |
| --probe_epochs "${PROBE_EPOCHS}" | |
| --probe_lr "${PROBE_LR}" | |
| --batch_size "${BATCH_SIZE}" | |
| --save_dir "${SAVE_DIR}" | |
| ) | |
| # Only pass --layers when LAYERS is non-empty; otherwise Train_Probe_SAE.py | |
| # defaults to every layer in the model (0 .. num_hidden_layers-1). | |
| if [ -n "${LAYERS}" ]; then | |
| COMMON_ARGS+=(--layers ${LAYERS}) | |
| fi | |
| if [ -n "${OTHER_OBJECT}" ]; then | |
| COMMON_ARGS+=(--other_object "${OTHER_OBJECT}") | |
| fi | |
| if [ "${DRY_RUN}" = "1" ] || [ "${DRY_RUN}" = "true" ]; then | |
| COMMON_ARGS+=(--dry_run --dry_run_images "${DRY_RUN_IMAGES}") | |
| fi | |
| if [ "${NUM_GPUS}" -gt 1 ]; then | |
| echo "Launching with torchrun on ${NUM_GPUS} GPUs" | |
| torchrun --nproc_per_node="${NUM_GPUS}" "${COMMON_ARGS[@]}" | |
| else | |
| echo "Launching single-GPU mode" | |
| python3 "${COMMON_ARGS[@]}" | |
| fi | |