feat: add held-out Qwen3.8 experiment protocol (#185)

This commit is contained in:
Joseph Magly
2026-08-29 00:17:41 -04:00
parent fb38a3b0c8
commit 9e149bf656
10 changed files with 761 additions and 48 deletions
+122 -40
View File
@@ -515,6 +515,7 @@ MODELS = _build_model_choices()
METHODS = {
"adaptive (telemetry-recommended)": "adaptive",
"Qwen3.8 E01 (held-out causal baseline)": "qwen38_e01",
"advanced (recommended)": "advanced",
"basic (fast, single direction)": "basic",
"aggressive (maximum removal)": "aggressive",
@@ -590,6 +591,7 @@ def _get_preset_defaults(method_display: str):
"cot_aware": cfg.get("cot_aware", False),
"bayesian_trials": cfg.get("bayesian_trials", 50),
"n_sae_features": cfg.get("n_sae_features", 64),
"verify_sample_size": cfg.get("verify_sample_size", 30),
}
def _on_method_change(method_display: str):
@@ -606,7 +608,7 @@ def _on_method_change(method_display: str):
d["transplant_blend"],
d["spectral_bands"],
d["spectral_threshold"],
30, # verify_sample_size (not method-dependent, keep default)
d.get("verify_sample_size", 30),
d["norm_preserve"],
d["project_biases"],
d["use_chat_template"],
@@ -2265,6 +2267,20 @@ def obliterate(model_choice: str, method_choice: str,
"harmful": hashlib.sha256((custom_harmful or "").encode()).hexdigest(),
"harmless": hashlib.sha256((custom_harmless or "").encode()).hexdigest(),
}
if method == "qwen38_e01":
run_config["immutable_experiment"] = {
"protocol": "qwen38-v1",
"experiment": "E01",
"direction_method": "diff_means",
"n_directions": 1,
"regularization": 0.0,
"refinement_passes": 1,
"norm_preserve": False,
"use_chat_template": True,
"layer_selection": "all_except_first",
"projection_target": "attention",
"verify_sample_size": 200,
}
try:
run_archive = RunArchive()
run_id = run_archive.begin(
@@ -2359,16 +2375,44 @@ def obliterate(model_choice: str, method_choice: str,
harmful_all, harmless_all = load_dataset_source(dataset_key)
on_log(f"Dataset loaded: {len(harmful_all)} harmful, {len(harmless_all)} harmless prompts")
# Apply volume cap (-1 = use all)
if prompt_volume > 0:
n = min(prompt_volume, len(harmful_all), len(harmless_all))
evaluation_harmful = None
evaluation_harmless = None
if method == "qwen38_e01":
if model_id.rstrip("/").lower() != "qwen/qwen3.8-27b":
raise ValueError("Qwen3.8 E01 requires Qwen/Qwen3.8-27B")
if use_custom or dataset_key != "builtin":
raise ValueError("Qwen3.8 E01 requires the immutable built-in corpus")
from obliteratus.experiment_protocol import build_qwen38_split
split = build_qwen38_split(harmful_all, harmless_all)
train_harmful = [pair[0] for pair in split.train]
train_harmless = [pair[1] for pair in split.train]
evaluation_harmful = [pair[0] for pair in split.test]
evaluation_harmless = [pair[1] for pair in split.test]
harmful_selected = train_harmful
harmless_selected = train_harmless
n = len(train_harmful)
run_archive.record_experiment_protocol(run_id, split.manifest)
on_log(
"Experiment protocol qwen38-v1: 500 direction-train, "
"142 optimizer-tune, 200 untouched final-test pairs"
)
on_log(f"Split manifest: {split.manifest['manifest_sha256']}")
immutable_e01 = True
else:
n = min(len(harmful_all), len(harmless_all))
# Apply volume cap (-1 = use all)
if prompt_volume > 0:
n = min(prompt_volume, len(harmful_all), len(harmless_all))
else:
n = min(len(harmful_all), len(harmless_all))
harmful_selected = harmful_all[:n]
harmless_selected = harmless_all[:n]
immutable_e01 = False
run_archive.record_dataset(
run_id,
identifier="custom" if use_custom else dataset_key,
harmful=harmful_all[:n],
harmless=harmless_all[:n],
harmful=harmful_selected,
harmless=harmless_selected,
)
if method == "informed":
@@ -2381,8 +2425,8 @@ def obliterate(model_choice: str, method_choice: str,
dtype=load_settings.dtype,
quantization=quantization,
trust_remote_code=is_preset,
harmful_prompts=harmful_all[:n],
harmless_prompts=harmless_all[:n],
harmful_prompts=harmful_selected,
harmless_prompts=harmless_selected,
on_stage=on_stage,
on_log=on_log,
cancellation_event=cancellation,
@@ -2391,6 +2435,70 @@ def obliterate(model_choice: str, method_choice: str,
pipeline.run_informed()
else:
from obliteratus.abliterate import AbliterationPipeline
if immutable_e01:
# E01 is immutable: ignore mutable advanced controls and
# run the registered causal baseline exactly as reviewed.
advanced_options = {
"n_directions": 1,
"direction_method": "diff_means",
"regularization": 0.0,
"refinement_passes": 1,
"norm_preserve": False,
"project_biases": False,
"use_chat_template": True,
"use_whitened_svd": False,
"true_iterative_refinement": False,
"use_jailbreak_contrast": False,
"layer_adaptive_strength": False,
"safety_neuron_masking": False,
"per_expert_directions": False,
"attention_head_surgery": False,
"use_sae_features": False,
"invert_refusal": False,
"project_embeddings": False,
"activation_steering": False,
"expert_transplant": False,
"use_wasserstein_optimal": False,
"spectral_cascade": False,
"layer_selection": "all_except_first",
"winsorize_activations": False,
"use_kl_optimization": False,
"float_layer_interpolation": False,
"rdo_refinement": False,
"cot_aware": False,
"verify_sample_size": 200,
}
else:
advanced_options = {
"n_directions": int(adv_n_directions),
"direction_method": adv_direction_method,
"regularization": float(adv_regularization),
"refinement_passes": int(adv_refinement_passes),
"norm_preserve": adv_norm_preserve,
"project_biases": adv_project_biases,
"use_chat_template": adv_use_chat_template,
"use_whitened_svd": adv_use_whitened_svd,
"true_iterative_refinement": adv_true_iterative,
"use_jailbreak_contrast": adv_jailbreak_contrast,
"layer_adaptive_strength": adv_layer_adaptive,
"safety_neuron_masking": adv_safety_neuron,
"per_expert_directions": adv_per_expert,
"attention_head_surgery": adv_attn_surgery,
"use_sae_features": adv_sae_features,
"invert_refusal": adv_invert_refusal,
"project_embeddings": adv_project_embeddings,
"activation_steering": adv_activation_steering,
"expert_transplant": adv_expert_transplant,
"use_wasserstein_optimal": adv_wasserstein_optimal,
"spectral_cascade": adv_spectral_cascade,
"layer_selection": adv_layer_selection,
"winsorize_activations": adv_winsorize,
"use_kl_optimization": adv_kl_optimization,
"float_layer_interpolation": adv_float_layer_interp,
"rdo_refinement": adv_rdo_refinement,
"cot_aware": adv_cot_aware,
"verify_sample_size": int(adv_verify_sample_size),
}
pipeline = AbliterationPipeline(
model_name=model_id,
output_dir=save_dir,
@@ -2399,49 +2507,23 @@ def obliterate(model_choice: str, method_choice: str,
method=method,
quantization=quantization,
trust_remote_code=is_preset,
harmful_prompts=harmful_all[:n],
harmless_prompts=harmless_all[:n],
harmful_prompts=harmful_selected,
harmless_prompts=harmless_selected,
evaluation_harmful_prompts=evaluation_harmful,
evaluation_harmless_prompts=evaluation_harmless,
on_stage=on_stage,
on_log=on_log,
cancellation_event=cancellation,
# Advanced overrides from UI
n_directions=int(adv_n_directions),
direction_method=adv_direction_method,
regularization=float(adv_regularization),
refinement_passes=int(adv_refinement_passes),
norm_preserve=adv_norm_preserve,
project_biases=adv_project_biases,
use_chat_template=adv_use_chat_template,
use_whitened_svd=adv_use_whitened_svd,
true_iterative_refinement=adv_true_iterative,
use_jailbreak_contrast=adv_jailbreak_contrast,
layer_adaptive_strength=adv_layer_adaptive,
safety_neuron_masking=adv_safety_neuron,
per_expert_directions=adv_per_expert,
attention_head_surgery=adv_attn_surgery,
use_sae_features=adv_sae_features,
invert_refusal=adv_invert_refusal,
reflection_strength=float(adv_reflection_strength),
project_embeddings=adv_project_embeddings,
embed_regularization=float(adv_embed_regularization),
activation_steering=adv_activation_steering,
steering_strength=float(adv_steering_strength),
expert_transplant=adv_expert_transplant,
transplant_blend=float(adv_transplant_blend),
use_wasserstein_optimal=adv_wasserstein_optimal,
spectral_cascade=adv_spectral_cascade,
spectral_bands=int(adv_spectral_bands),
spectral_threshold=float(adv_spectral_threshold),
verify_sample_size=int(adv_verify_sample_size),
layer_selection=adv_layer_selection,
winsorize_activations=adv_winsorize,
winsorize_percentile=float(adv_winsorize_percentile),
use_kl_optimization=adv_kl_optimization,
kl_budget=float(adv_kl_budget),
float_layer_interpolation=adv_float_layer_interp,
rdo_refinement=adv_rdo_refinement,
cot_aware=adv_cot_aware,
n_sae_features=int(adv_n_sae_features),
**advanced_options,
)
pipeline_ref[0] = pipeline
pipeline.run()