ToiTenBao's picture
Upload hallucination folder
a2ffd07 verified
Raw
History Blame Contribute Delete
11.6 kB
import torch
from torch.utils.data import Dataset
from transformers import BlipProcessor, LlavaProcessor
from sklearn.model_selection import train_test_split
from tqdm import tqdm
import os
from typing import Optional
from sklearn.metrics import roc_auc_score, f1_score
from extra_materials.mechanistic_interp.probe.probing import load_probe
class ToiletBinaryDataset(Dataset):
"""
Lazy VLM dataset for pbcong/bathroom-toilet.
Label rule:
1 -> toilet only
0 -> bathroom only
Other combinations are skipped.
"""
def __init__(self, dataset, cfg):
self.original_dataset = dataset
self.data = []
self.label = label
for item in dataset:
bathroom = item["bathroom"]
toilet = item["toilet"]
if toilet == 1 and bathroom != 1:
label = 1
elif bathroom == 1 and toilet != 1:
label = 0
else:
continue
self.data.append({
"image": item["image"],
"image_id": item["image_id"],
"label": label
})
print(f"Loaded {len(self.data)} valid samples")
if "blip" in cfg.processor.lower():
print("Using BLIP processor")
self.processor = BlipProcessor.from_pretrained(cfg.processor)
self.prompt = ""
elif "llava" in cfg.processor.lower():
print("Using LLaVA processor")
self.processor = LlavaProcessor.from_pretrained(cfg.processor)
self.prompt = "USER: <image>\nDescribe this image.\nASSISTANT:"
else:
raise ValueError(f"Unsupported processor: {cfg.processor}")
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
raw_item = self.data[idx]
image = raw_item["image"]
processed = self.processor(
images=image,
text=self.prompt,
return_tensors="pt",
padding=True,
)
return {
"pixel_values": processed["pixel_values"][0],
"input_ids": processed["input_ids"][0],
"attention_mask": processed["attention_mask"][0],
"label": torch.tensor(raw_item["label"]),
"image_id": raw_item["image_id"],
}
class BathroomBinaryDataset(Dataset):
"""
Lazy VLM dataset for pbcong/bathroom-toilet.
Label rule:
1 -> bathroom only
0 -> toilet only
Other combinations are skipped.
"""
def __init__(self, dataset, cfg):
self.original_dataset = dataset
self.data = []
for item in dataset:
bathroom = item["bathroom"]
toilet = item["toilet"]
if toilet == 1 and bathroom != 1:
label = 0
elif bathroom == 1 and toilet != 1:
label = 1
else:
continue
self.data.append({
"image": item["image"],
"image_id": item["image_id"],
"label": label
})
print(f"Loaded {len(self.data)} valid samples")
if "blip" in cfg.processor.lower():
print("Using BLIP processor")
self.processor = BlipProcessor.from_pretrained(cfg.processor)
self.prompt = ""
elif "llava" in cfg.processor.lower():
print("Using LLaVA processor")
self.processor = LlavaProcessor.from_pretrained(cfg.processor)
self.prompt = "USER: <image>\nDescribe this image.\nASSISTANT:"
else:
raise ValueError(f"Unsupported processor: {cfg.processor}")
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
raw_item = self.data[idx]
image = raw_item["image"]
processed = self.processor(
images=image,
text=self.prompt,
return_tensors="pt",
padding=True,
)
return {
"pixel_values": processed["pixel_values"][0],
"input_ids": processed["input_ids"][0],
"attention_mask": processed["attention_mask"][0],
"label": torch.tensor(raw_item["label"]),
"image_id": raw_item["image_id"],
}
class ImageFolderVLMDataset(Dataset):
"""
Load images from a flat folder (e.g. bathroom_toilet_split/toilet_only/val/)
and produce batches compatible with collect_activations.
All images get the same fixed label (since the folder defines the category).
"""
def __init__(self, image_dir, processor_name, label):
self.image_dir = image_dir
self.image_paths = sorted([
os.path.join(image_dir, f)
for f in os.listdir(image_dir)
if f.lower().endswith(('.png', '.jpg', '.jpeg'))
])
self.label = label
if "llava" in processor_name.lower():
self.processor = LlavaProcessor.from_pretrained(processor_name)
self.prompt = "USER: <image>\nDescribe this image.\nASSISTANT:"
elif "blip" in processor_name.lower():
self.processor = BlipProcessor.from_pretrained(processor_name)
self.prompt = ""
else:
raise ValueError(f"Unsupported processor: {processor_name}")
print(f"Loaded {len(self.image_paths)} images from {image_dir}")
def __len__(self):
return len(self.image_paths)
def __getitem__(self, idx):
from PIL import Image
img = Image.open(self.image_paths[idx]).convert("RGB")
processed = self.processor(
images=img,
text=self.prompt,
return_tensors="pt",
padding=True,
)
return {
"pixel_values": processed["pixel_values"][0],
"input_ids": processed["input_ids"][0],
"attention_mask": processed["attention_mask"][0],
"image_id": os.path.splitext(os.path.basename(self.image_paths[idx]))[0],
"label": torch.tensor(self.label),
}
def prepare_probe_dataset(model, train_loader, val_loader, layer_idx, training_mode, pooling_mode, device):
"""Collect activations from pre-split train/val loaders."""
if training_mode == "shared":
print(f"1 SHARED probe, same probe for resid_mid and resid_post, pooling_mode={pooling_mode}")
X_train, Y_train = collect_activations(model, train_loader, layer_idx, training_mode, pooling_mode, device)
X_val, Y_val = collect_activations(model, val_loader, layer_idx, training_mode, pooling_mode, device)
return X_train, X_val, Y_train, Y_val
elif training_mode == "separated":
print(f"2 SEPARATE probes for mlp_in and mlp_out, pooling_mode={pooling_mode}")
X_in_train, X_out_train, Y_train = collect_activations(model, train_loader, layer_idx, training_mode, pooling_mode, device)
X_in_val, X_out_val, Y_val = collect_activations(model, val_loader, layer_idx, training_mode, pooling_mode, device)
return X_in_train, X_in_val, X_out_train, X_out_val, Y_train, Y_val
def collect_activations(model, loader, layer_idx, training_mode, pooling_mode, device, return_ids=False):
all_in, all_out, all_labels, all_ids = [], [], [], []
if training_mode == "shared":
in_key = f"model.language_model.layers.{layer_idx}.hook_resid_mid"
out_key = f"model.language_model.layers.{layer_idx}.hook_resid_post"
elif training_mode == "separated":
in_key = f"model.language_model.layers.{layer_idx}.hook_mlp_in"
out_key = f"model.language_model.layers.{layer_idx}.hook_mlp_out"
with torch.no_grad():
for batch in tqdm(loader, desc="Collecting activations"):
inputs = {
"pixel_values": batch["pixel_values"].to(device),
"input_ids": batch["input_ids"].to(device),
"attention_mask": batch["attention_mask"].to(device),
}
_, cache = model.run_with_cache(
inputs=inputs,
names_filter=lambda name: name == in_key or name == out_key,
)
num_vis_tokens = 577
if pooling_mode == "mean":
cache_in = cache[in_key].mean(dim=1)
cache_out = cache[out_key].mean(dim=1)
elif pooling_mode == "cls":
cache_in = cache[in_key][:, 0, :]
cache_out = cache[out_key][:, 0, :]
elif pooling_mode == "vis":
cache_in = cache[in_key][:, 1:num_vis_tokens, :].mean(dim=1)
cache_out = cache[out_key][:, 1:num_vis_tokens, :].mean(dim=1)
elif pooling_mode == "text":
cache_in = cache[in_key][:, num_vis_tokens:, :].mean(dim=1)
cache_out = cache[out_key][:, num_vis_tokens:, :].mean(dim=1)
elif pooling_mode == "last_tok":
cache_in = cache[in_key][:, -1, :]
cache_out = cache[out_key][:, -1, :]
all_in.append(cache_in.cpu())
all_out.append(cache_out.cpu())
all_labels.append(batch["label"])
all_ids.extend(batch["image_id"])
X_in = torch.cat(all_in, dim=0)
X_out = torch.cat(all_out, dim=0)
Y = torch.cat(all_labels, dim=0)
if training_mode == "separated":
if return_ids:
return X_in, X_out, Y, all_ids
else:
return X_in, X_out, Y
elif training_mode == "shared":
if return_ids:
return torch.cat([X_in, X_out], dim=0), torch.cat([Y, Y], dim=0), all_ids
else:
return torch.cat([X_in, X_out], dim=0), torch.cat([Y, Y], dim=0)
def evaluate_separated(probe_in, probe_out, layer_idx, X_in_val, X_out_val, Y_val, device):
X_in_val = X_in_val.to(device)
X_out_val = X_out_val.to(device)
Y_val = Y_val.to(device)
with torch.no_grad():
score_in = probe_in(X_in_val).squeeze().float()
score_out = probe_out(X_out_val).squeeze().float()
pred_in = (score_in > 0).float()
pred_out = (score_out > 0).float()
acc_in = (pred_in == Y_val).float().mean().item()
acc_out = (pred_out == Y_val).float().mean().item()
y_true = Y_val.detach().float().cpu().numpy()
s_in = score_in.detach().float().cpu().numpy()
s_out = score_out.detach().float().cpu().numpy()
p_in = pred_in.detach().cpu().numpy()
p_out = pred_out.detach().cpu().numpy()
auc_in = roc_auc_score(y_true, s_in)
auc_out = roc_auc_score(y_true, s_out)
f1_in = f1_score(y_true, p_in)
f1_out = f1_score(y_true, p_out)
print(f"Probe mlp_in accuracy: {acc_in:.4f}")
print(f"Probe mlp_out accuracy: {acc_out:.4f}")
print(f"layer {layer_idx} AUC in : {auc_in}")
print(f"layer {layer_idx} AUC out: {auc_out}")
print(f"layer {layer_idx} F1 in : {f1_in}")
print(f"layer {layer_idx} F1 out: {f1_out}")
def evaluate_shared(probe, layer_idx, X_val, Y_val, device):
X_val = X_val.to(device)
Y_val = Y_val.to(device)
with torch.no_grad():
score = probe(X_val).squeeze().float()
pred = (score > 0).float()
acc = (pred == Y_val).float().mean().item()
y_true = Y_val.detach().float().cpu().numpy()
s = score.detach().float().cpu().numpy()
p = pred.detach().float().cpu().numpy()
auc = roc_auc_score(y_true, s)
f1 = f1_score(y_true, p)
print(f"Probe shared accuracy: {acc:.4f}")
print(f"layer {layer_idx} AUC shared: {auc}")
print(f"layer {layer_idx} F1 shared: {f1}")