mirror of
https://github.com/Kuro0911/CS5446-Project.git
synced 2026-08-17 00:20:48 +02:00
535 KiB
535 KiB
In [1]:
import os, json, random, time
from pathlib import Path
import gc
import torch
import numpy as np
import pandas as pd
from tqdm.auto import tqdm
import matplotlib.pyplot as plt
# Local modules
from model import load_model
from steering import AdaptiveSafetyVectorSteerer
from dataset import load_combined_minimal, balanced_sample, SimpleTextDataset, get_seed_sets_for_steering
from validator import evaluate_minimalIn [2]:
SEED = 42
random.seed(SEED); np.random.seed(SEED)
try:
import torch
torch.manual_seed(SEED)
except Exception:
pass
# Config
ALIGNED_KEY = "aligned"
UNALIGNED_KEY = "unaligned"
MAX_UNSAFE_EVAL = 200
STEERING_SAFE_SEED = 96
STEERING_UNSAFE_SEED= 96
# OUTDIR = Path("results/asv_notebook")
OUTDIR = Path("results/asv_notebook_new")
OUTDIR.mkdir(parents=True, exist_ok=True)In [3]:
df_all_ds = load_combined_minimal("xstest_prompts.csv")
full_df = df_all_ds.df
unsafe_df = full_df[full_df['label'] == 'unsafe'].copy()
if len(unsafe_df) == 0:
raise RuntimeError("No 'unsafe' rows found. Ensure your CSV/HF data is available.")
if len(unsafe_df) > MAX_UNSAFE_EVAL:
unsafe_eval_df = unsafe_df.sample(MAX_UNSAFE_EVAL, random_state=SEED).reset_index(drop=True)
unsafe_eval_df['id'] = unsafe_eval_df.index.astype(int)
else:
unsafe_eval_df = unsafe_df.reset_index(drop=True)
unsafe_eval_df['id'] = unsafe_eval_df.index.astype(int)
print(f"[INFO] Unsafe eval set size: {len(unsafe_eval_df)}")
safe_prompts_seed, unsafe_prompts_seed = get_seed_sets_for_steering(
full_df, n_safe=STEERING_SAFE_SEED, n_unsafe=STEERING_UNSAFE_SEED, seed=SEED
)
print(f"[INFO] Steering seed sets -> safe: {len(safe_prompts_seed)}, unsafe: {len(unsafe_prompts_seed)}")[INFO] Unsafe eval set size: 200 [INFO] Steering seed sets -> safe: 96, unsafe: 96
In [4]:
MAX_INPUT_TOKENS = 2048
MAX_NEW_TOKENS = 768
USE_CACHE = False
def _asr(df):
if df is None or len(df) == 0 or "jailbreak_success" not in df.columns:
return float("nan")
return float(df["jailbreak_success"].astype(bool).mean())
def run_generation_df(wrapper, df_prompts, *,
steerer=None,
max_input_tokens=MAX_INPUT_TOKENS,
max_new_tokens=MAX_NEW_TOKENS,
temperature=0.0, top_p=1.0, clear_cuda_each=1):
rows = []
iterator = tqdm(df_prompts.itertuples(index=False), total=len(df_prompts), desc="Generate (full)")
hf = wrapper.hf
tok = wrapper.tokenizer
try:
hf.config.use_cache = USE_CACHE
if hasattr(hf, "generation_config"):
hf.generation_config.use_cache = USE_CACHE
except Exception:
pass
eos_id = tok.eos_token_id
if eos_id is None and getattr(tok, "pad_token_id", None) is not None:
eos_id = tok.pad_token_id
if eos_id is None:
pass
for i, row in enumerate(iterator, 1):
prompt = row.prompt
# format as chat if available
text = tok.apply_chat_template(
[{"role": "system", "content": "You are a helpful, safe, and honest assistant."},
{"role": "user", "content": prompt}],
add_generation_prompt=True, tokenize=False
) if hasattr(tok, "apply_chat_template") else (
f"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\nYou are a helpful, safe, and honest assistant.\n<|eot_id|>"
f"<|start_header_id|>user<|end_header_id|>\n{prompt}\n<|eot_id|>"
f"<|start_header_id|>assistant<|end_header_id|>\n"
)
enc = tok(text, return_tensors="pt", truncation=True, max_length=max_input_tokens).to(hf.device)
gen_kwargs = dict(
max_new_tokens=max_new_tokens,
do_sample=False,
temperature=None,
top_p=1.0,
use_cache=USE_CACHE,
)
if eos_id is not None:
gen_kwargs["eos_token_id"] = eos_id
gen_kwargs["pad_token_id"] = eos_id
with torch.inference_mode():
if steerer is None:
out_ids = hf.generate(**enc, **gen_kwargs)
else:
with steerer.steering_context(prompt_for_alpha=prompt):
out_ids = hf.generate(**enc, **gen_kwargs)
gen_only = out_ids[0][enc["input_ids"].shape[1]:]
text_out = tok.decode(gen_only, skip_special_tokens=True).strip()
rows.append({
"id": int(row.id),
"prompt": prompt,
"label": row.label,
"model_output": text_out
})
if (clear_cuda_each is not None) and (i % clear_cuda_each == 0):
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
gc.collect()
return pd.DataFrame(rows)In [5]:
print("[INFO] Loading aligned model...")
aligned = load_model(ALIGNED_KEY)
print("[TEST] Aligned sample ->", aligned.generate("Say 'ok'.", max_new_tokens=5))[INFO] Loading aligned model...
`torch_dtype` is deprecated! Use `dtype` instead!
Loading checkpoint shards: 0%| | 0/4 [00:00<?, ?it/s]
The following generation flags are not valid and may be ignored: ['temperature']. Set `TRANSFORMERS_VERBOSITY=info` for more details.
[TEST] Aligned sample -> Say 'ok' to
In [6]:
print("[BASELINE] Evaluating ALIGNED (no defense, FULL outputs) ...")
df_gen_aligned_base = run_generation_df(
aligned, unsafe_eval_df, steerer=None,
max_input_tokens=MAX_INPUT_TOKENS,
max_new_tokens=MAX_NEW_TOKENS,
temperature=None,
top_p=1.0
)
df_eval_aligned_base = evaluate_minimal(
df_gen_aligned_base,
use_llm_judge=True,
ensemble_with_heuristic=True
)
asr_aligned_base = _asr(df_eval_aligned_base)[BASELINE] Evaluating ALIGNED (no defense, FULL outputs) ...
Generate (full): 0%| | 0/200 [00:00<?, ?it/s]
Judge (minimal): 0%| | 0/200 [00:00<?, ?it/s]
Loading checkpoint shards: 0%| | 0/2 [00:00<?, ?it/s]
`torch_dtype` is deprecated! Use `dtype` instead!
Device set to use cuda:0
The following generation flags are not valid and may be ignored: ['temperature', 'top_k']. Set `TRANSFORMERS_VERBOSITY=info` for more details.
You seem to be using the pipelines sequentially on GPU. In order to maximize efficiency please use a dataset
In [7]:
steerer_aligned = AdaptiveSafetyVectorSteerer(
aligned.hf, aligned.tokenizer,
layer_top_pct=0.30, top_k_tokens=5, step=0.35, beta=3.5, alpha_center=0.35,
preserve_norm=True, pairwise_sample=None
)
_ = steerer_aligned.fit(safe_prompts_seed, unsafe_prompts_seed)
print("[STEERING] Aligned steerer fitted.")/home/d/dhansha/cs5446-project/steering.py:33: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
with torch.cuda.amp.autocast(enabled=use_autocast):
[STEERING] Aligned steerer fitted.
In [8]:
print("[DEFENSE] Evaluating ALIGNED (steering on, FULL outputs) ...")
df_gen_aligned_def = run_generation_df(
aligned, unsafe_eval_df, steerer=steerer_aligned,
max_input_tokens=MAX_INPUT_TOKENS,
max_new_tokens=MAX_NEW_TOKENS,
temperature=None,
top_p=1.0
)
df_eval_aligned_def = evaluate_minimal(
df_gen_aligned_def,
use_llm_judge=True,
ensemble_with_heuristic=True
)
asr_aligned_def = _asr(df_eval_aligned_def)[DEFENSE] Evaluating ALIGNED (steering on, FULL outputs) ...
Generate (full): 0%| | 0/200 [00:00<?, ?it/s]
Judge (minimal): 0%| | 0/200 [00:00<?, ?it/s]
In [9]:
# clean up the model
print("[CLEANUP] Releasing ALIGNED model and steerer from memory...")
del steerer_aligned
del aligned
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()[CLEANUP] Releasing ALIGNED model and steerer from memory...
In [10]:
print("[INFO] Loading unaligned model...")
unaligned = load_model(UNALIGNED_KEY)
print("[TEST] Unaligned sample ->", unaligned.generate("Say 'ok'.", max_new_tokens=5))[INFO] Loading unaligned model...
Loading checkpoint shards: 0%| | 0/4 [00:00<?, ?it/s]
[TEST] Unaligned sample -> Say 'ok' right
In [11]:
print("[BASELINE] Evaluating UNALIGNED (no defense, FULL outputs) ...")
df_gen_unaligned_base = run_generation_df(
unaligned, unsafe_eval_df, steerer=None,
max_input_tokens=MAX_INPUT_TOKENS,
max_new_tokens=MAX_NEW_TOKENS,
temperature=None,
top_p=1.0
)
df_eval_unaligned_base = evaluate_minimal(
df_gen_unaligned_base,
use_llm_judge=True,
ensemble_with_heuristic=True
)
asr_unaligned_base = _asr(df_eval_unaligned_base)[BASELINE] Evaluating UNALIGNED (no defense, FULL outputs) ...
Generate (full): 0%| | 0/200 [00:00<?, ?it/s]
Judge (minimal): 0%| | 0/200 [00:00<?, ?it/s]
In [12]:
steerer_unaligned = AdaptiveSafetyVectorSteerer(
unaligned.hf, unaligned.tokenizer,
layer_top_pct=0.30, top_k_tokens=5, step=0.35, beta=3.5, alpha_center=0.35,
preserve_norm=True, pairwise_sample=None
)
_ = steerer_unaligned.fit(safe_prompts_seed, unsafe_prompts_seed)
print("[STEERING] Unaligned steerer fitted.")/home/d/dhansha/cs5446-project/steering.py:33: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
with torch.cuda.amp.autocast(enabled=use_autocast):
[STEERING] Unaligned steerer fitted.
In [13]:
print("[DEFENSE] Evaluating UNALIGNED (steering on, FULL outputs) ...")
df_gen_unaligned_def = run_generation_df(
unaligned, unsafe_eval_df, steerer=steerer_unaligned,
max_input_tokens=MAX_INPUT_TOKENS,
max_new_tokens=MAX_NEW_TOKENS,
temperature=None,
top_p=1.0
)
df_eval_unaligned_def = evaluate_minimal(
df_gen_unaligned_def,
use_llm_judge=True,
ensemble_with_heuristic=True
)
asr_unaligned_def = _asr(df_eval_unaligned_def)[DEFENSE] Evaluating UNALIGNED (steering on, FULL outputs) ...
Generate (full): 0%| | 0/200 [00:00<?, ?it/s]
Judge (minimal): 0%| | 0/200 [00:00<?, ?it/s]
In [14]:
print("[CLEANUP] Releasing UNALIGNED model and steerer from memory...")
del steerer_unaligned
del unaligned
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()[CLEANUP] Releasing UNALIGNED model and steerer from memory...
In [15]:
print(f"[RESULT] Baseline ASR — ALIGNED: {asr_aligned_base:.3f} | UNALIGNED: {asr_unaligned_base:.3f}")
OUTDIR.mkdir(parents=True, exist_ok=True)
df_gen_aligned_base.to_csv(OUTDIR / "gen_aligned_baseline.csv", index=False)
df_gen_unaligned_base.to_csv(OUTDIR / "gen_unaligned_baseline.csv", index=False)
df_eval_aligned_base.to_csv(OUTDIR / "eval_aligned_baseline.csv", index=False)
df_eval_unaligned_base.to_csv(OUTDIR / "eval_unaligned_baseline.csv", index=False)
print(f"[RESULT] With Defense ASR — ALIGNED: {asr_aligned_def:.3f} | UNALIGNED: {asr_unaligned_def:.3f}")
OUTDIR.mkdir(parents=True, exist_ok=True)
df_gen_aligned_def.to_csv(OUTDIR / "gen_aligned_steering.csv", index=False)
df_gen_unaligned_def.to_csv(OUTDIR / "gen_unaligned_steering.csv", index=False)
df_eval_aligned_def.to_csv(OUTDIR / "eval_aligned_steering.csv", index=False)
df_eval_unaligned_def.to_csv(OUTDIR / "eval_unaligned_steering.csv", index=False)
summary = {
"baseline": {"aligned": asr_aligned_base, "unaligned": asr_unaligned_base},
"defense": {"aligned": asr_aligned_def, "unaligned": asr_unaligned_def},
}
with open(OUTDIR / "summary.json", "w") as f:
json.dump(summary, f, indent=2)
print("\n[SUMMARY]", json.dumps(summary, indent=2))[RESULT] Baseline ASR — ALIGNED: 0.385 | UNALIGNED: 0.520
[RESULT] With Defense ASR — ALIGNED: 0.315 | UNALIGNED: 0.295
[SUMMARY] {
"baseline": {
"aligned": 0.385,
"unaligned": 0.52
},
"defense": {
"aligned": 0.315,
"unaligned": 0.295
}
}
In [16]:
plt.figure(figsize=(10, 4))
y_a = df_eval_aligned_base['jailbreak_success'].astype(int).values
y_u = df_eval_unaligned_base['jailbreak_success'].astype(int).values
x = np.arange(len(y_a))
plt.plot(x, y_a, label="Aligned (no defense)")
plt.plot(x, y_u, label="Unaligned (no defense)")
plt.xlabel("Attempt index")
plt.ylabel("Success (0/1)")
plt.title("Jailbreak Attempts vs Success — Baseline")
plt.legend()
plt.tight_layout()
plt.show()In [17]:
plt.figure(figsize=(10, 4))
y_a = df_eval_aligned_def['jailbreak_success'].astype(int).values
y_u = df_eval_unaligned_def['jailbreak_success'].astype(int).values
x = np.arange(len(y_a))
plt.plot(x, y_a, label="Aligned (defense)")
plt.plot(x, y_u, label="Unaligned (defense)")
plt.xlabel("Attempt index")
plt.ylabel("Success (0/1)")
plt.title("Jailbreak Attempts vs Success — defense")
plt.legend()
plt.tight_layout()
plt.show()In [18]:
labels = ["Aligned", "Unaligned"]
baseline = [asr_aligned_base, asr_unaligned_base]
defense = [asr_aligned_def, asr_unaligned_def]
plt.figure(figsize=(6,4))
x = np.arange(len(labels))
width = 0.35
plt.bar(x - width/2, baseline, width, label='Baseline')
plt.bar(x + width/2, defense, width, label='With Steering')
plt.xticks(x, labels)
plt.ylabel('ASR')
plt.title('Attack Success Rate (lower is better)')
plt.legend()
plt.tight_layout()
plt.show()