Safetensors
GGUF
abliterated
uncensored
conversational
ScottzillaSystems's picture
Duplicate from paperscarecrow/Gemma-4-31B-it-abliterated
fcd95b8
Raw
History Blame Contribute Delete
5.49 kB
import torch
from transformers import AutoModelForMultimodalLM, AutoProcessor
import gc
import json
import os
from tqdm import tqdm
from datasets import load_dataset
import random
# --- CONFIGURATION ---
MODEL_ID = "google/gemma-4-31B-it" # Adjust if your local path differs
SAVE_PATH = "./gemma-4-31b-abliterated"
BATCH_SIZE = 4 # Keep this low to survive the 31B hidden state extraction
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
print(f"[*] Initializing Gemma 4 31B Multimodal Abliteration Protocol on {DEVICE}...")
# --- 1. LOAD MULTIMODAL MODEL & PROCESSOR ---
print("[*] Loading Model and Processor (bfloat16)...")
processor = AutoProcessor.from_pretrained(MODEL_ID)
model = AutoModelForMultimodalLM.from_pretrained(
MODEL_ID,
torch_dtype=torch.bfloat16,
device_map="auto" # Let accelerate distribute the 62GB across your GPUs
)
# --- 2. DATA PREPARATION ---
print("[*] Downloading HuggingFace datasets...")
harmful_dataset = load_dataset('mlabonne/harmful_behaviors')
harmless_dataset = load_dataset('mlabonne/harmless_alpaca')
# We shuffle and slice 256 samples to keep VRAM extraction manageable
raw_harmful = random.sample(harmful_dataset['train']['text'], 256)
raw_harmless = random.sample(harmless_dataset['train']['text'], 256)
def format_gemma4_prompts(instructions):
"""Uses the native Gemma 4 processor chat template."""
formatted = []
for inst in instructions:
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": inst}
]
formatted.append(processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True))
return formatted
print("[*] Formatting prompts with native Gemma 4 Chat Templates...")
harmful_prompts = format_gemma4_prompts(raw_harmful)
harmless_prompts = format_gemma4_prompts(raw_harmless)
# --- 3. HIDDEN STATE EXTRACTION (VRAM SAFE) ---
def get_hidden_states(prompts, batch_size=BATCH_SIZE):
print(f"[*] Extracting hidden states (Batches of {batch_size})...")
all_hidden_states = []
for i in tqdm(range(0, len(prompts), batch_size)):
batch = prompts[i:i+batch_size]
inputs = processor(text=batch, padding=True, return_tensors="pt").to(DEVICE)
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
# Stack to: [num_layers+1, batch, seq, dim]
stacked_states = torch.stack(outputs.hidden_states)
# Extract last token: [num_layers+1, batch, dim]
last_token_states = stacked_states[:, torch.arange(len(batch)), -1, :]
# IMMEDIATELY move to CPU float32 to save VRAM
all_hidden_states.append(last_token_states.cpu().float())
del inputs, outputs, stacked_states, last_token_states
torch.cuda.empty_cache()
gc.collect()
return torch.cat(all_hidden_states, dim=1)
print("\n[*] Processing Harmful Vector Space...")
harmful_states = get_hidden_states(harmful_prompts)
print("[*] Processing Harmless Vector Space...")
harmless_states = get_hidden_states(harmless_prompts)
# --- 4. DYNAMIC LAYER HUNTING ---
print("\n[*] Hunting for the Refusal Vector...")
mean_harmful = harmful_states.mean(dim=1)
mean_harmless = harmless_states.mean(dim=1)
refusal_directions = mean_harmful - mean_harmless
magnitudes = torch.norm(refusal_directions[1:], dim=1)
peak_state_idx = torch.argmax(magnitudes).item() + 1
print(f"[+] Peak Refusal Mass detected at state index: {peak_state_idx}")
refusal_vector = refusal_directions[peak_state_idx]
refusal_vector = (refusal_vector / torch.norm(refusal_vector)).to(DEVICE).to(torch.bfloat16)
# --- 5. ORTHOGONAL PROJECTION (THE ABLITERATION) ---
# The 31B Dense model has 60 text layers
num_layers = model.config.text_config.num_hidden_layers if hasattr(model.config, 'text_config') else model.config.num_hidden_layers
target_layer_idx = peak_state_idx - 1
print(f"\n[*] Applying Orthogonal Projection starting at Text Layer {target_layer_idx}...")
def get_text_transformer_layers(model_obj, target_len):
"""Safely isolates the text backbone from the multimodal layers."""
for name, module in model_obj.named_modules():
if name.endswith('layers') and isinstance(module, torch.nn.ModuleList) and len(module) == target_len:
return module
return model_obj.language_model.model.layers # Fallback
transformer_layers = get_text_transformer_layers(model, num_layers)
v_col = refusal_vector.unsqueeze(1)
v_row = refusal_vector.unsqueeze(0)
for layer_idx in range(target_layer_idx, min(target_layer_idx + 5, num_layers)):
print(f" -> Abliterating Layer {layer_idx}...")
o_proj = transformer_layers[layer_idx].self_attn.o_proj.weight.data
down_proj = transformer_layers[layer_idx].mlp.down_proj.weight.data
projection_o = torch.matmul(v_col, torch.matmul(v_row, o_proj))
transformer_layers[layer_idx].self_attn.o_proj.weight.data -= projection_o
projection_down = torch.matmul(v_col, torch.matmul(v_row, down_proj))
transformer_layers[layer_idx].mlp.down_proj.weight.data -= projection_down
# --- 6. CRYSTALLIZATION ---
print(f"\n[*] Abliteration Complete. Saving fully multimodal weights to {SAVE_PATH}...")
model.save_pretrained(SAVE_PATH)
processor.save_pretrained(SAVE_PATH)
print("[+] SUCCESS: The 31B Teacher is ready to wake up with vision intact.")