Views
No views yet
| Parameter | Value | Notes |
|---|---|---|
| Injection token | ㈎ (U+320E) | token_id 149705 |
| Injection method | Normalize norm to 150.0 | NOT multiply by 150 |
| Prompt template | Includes depth 73% | See below |
| Attention mask | Must be passed explicitly | pad_token == eos_token causes issues without it |
1# CORRECT: normalize norm TO 150
2def normalize_activation(v, target_norm=150.0):
3 norm = v.float().norm().clamp_min(1e-12)
4 return v * (target_norm / norm)
5
6injected = normalize_activation(activation, 150.0)
7# If activation.norm() == 129, this gives injected.norm() == 150
8
9# WRONG: multiply BY 150
10injected = activation * 150.0
11# If activation.norm() == 129, this gives injected.norm() == 19,350
12# The model was never trained on vectors this large — produces garbage1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3from peft import PeftModel
4
5INJECTION_CHAR = "㈎"
6INJECTION_SCALE = 150.0
7LAYER = 20
8
9# --- Load model with adapter ---
10base = AutoModelForCausalLM.from_pretrained(
11 "Qwen/Qwen2.5-7B-Instruct", torch_dtype=torch.bfloat16, device_map="auto"
12)
13model = PeftModel.from_pretrained(base, "anicka/nla-qwen2.5-7b-L20-av-v2")
14model.eval()
15tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
16if tokenizer.pad_token is None:
17 tokenizer.pad_token = tokenizer.eos_token
18
19injection_id = tokenizer.encode(INJECTION_CHAR, add_special_tokens=False)
20assert len(injection_id) == 1, f"Injection char must be single token, got {len(injection_id)}"
21injection_token_id = injection_id[0]
22
23# --- Step 1: Extract activation from layer 20 ---
24prompt = "Write a Python hello world program"
25messages = [{"role": "user", "content": prompt}]
26chat_str = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
27inputs = tokenizer(chat_str, return_tensors="pt").to(model.device)
28
29activation = {}
30def hook(mod, inp, out):
31 h = out[0] if isinstance(out, tuple) else out
32 if "h" not in activation: # capture FIRST forward pass only
33 activation["h"] = h[:, -1, :].detach()
34
35inner = model.base_model.model.model
36handle = inner.layers[LAYER].register_forward_hook(hook)
37with torch.no_grad():
38 model.generate(**inputs, max_new_tokens=1, pad_token_id=tokenizer.eos_token_id)
39handle.remove()
40act = activation["h"].squeeze(0)
41
42# --- Step 2: Normalize (NOT multiply) ---
43def normalize_activation(v, target_norm):
44 norm = v.float().norm().clamp_min(1e-12)
45 return v * (target_norm / norm)
46
47# --- Step 3: Build the verbalization prompt ---
48depth_pct = round(100 * (LAYER + 0.5) / 28) # 28 layers in Qwen 2.5 7B
49av_prompt = (
50 "You are a meticulous AI researcher conducting an important investigation "
51 "into activation vectors from a language model. Your overall task is to "
52 "describe the semantic content of that activation vector.\n\n"
53 "We will pass the vector enclosed in <concept> tags into your context, "
54 "along with the network depth where it was extracted. "
55 "You must then produce an explanation for the vector, enclosed within "
56 "<explanation> tags. The explanation consists of 2-3 text snippets "
57 "describing that vector.\n\n"
58 f"Here is the vector from depth {depth_pct}% of the network:\n\n"
59 f"<concept>{INJECTION_CHAR}</concept>\n\n"
60 "Please provide an explanation.\n\n"
61 "<explanation>"
62)
63
64tokens = tokenizer.encode(av_prompt, add_special_tokens=True)
65inject_pos = next(i for i, t in enumerate(tokens) if t == injection_token_id)
66
67input_ids = torch.tensor([tokens], device=model.device)
68embeddings = model.get_input_embeddings()(input_ids).clone()
69embeddings[0, inject_pos, :] = normalize_activation(
70 act.to(embeddings.dtype), INJECTION_SCALE
71)
72
73# --- Step 4: Generate description ---
74with torch.no_grad():
75 output = model.generate(
76 inputs_embeds=embeddings,
77 max_new_tokens=120,
78 do_sample=False,
79 pad_token_id=tokenizer.eos_token_id,
80 )
81
82text = tokenizer.decode(output[0][len(tokens):], skip_special_tokens=True)
83if "</explanation>" in text:
84 text = text.split("</explanation>")[0]
85print(text.strip())anicka/nla-qwen25-7b-L20-av (no dash, no v2) is the old SFT-only adapter with a different prompt template (no depth). Use this repo (nla-qwen2.5-7b-L20-av-v2) for GRPO quality."from depth {N}% of the network" in the prompt. Omitting it degrades output.pad_token == eos_token, pass attention_mask explicitly or unexpected behavior occurs.generate(), the hook fires on every token. Guard with if "h" not in activation: to capture only the first (input) pass.