add clamp apply mode: pin v-component to C instead of accumulating

clamp: y += (C - <y,v_hat>)v_hat at all positions -- bounded perturbation
regardless of generation length, vs add's per-step accumulation via KV cache.
C=0 is directional ablation. Smoke (Qwen3-0.6B, happy/joy): clamp C=+20 stays
coherent and on-concept (drifts to 'happiness and joy of my childhood', in
Chinese) while add C=+8 already degenerates to 'joyjoyjoy...'.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-07-10 14:42:22 +08:00
co-authored by Claudypoo
parent 474f74ac33
commit 57c8d4b166
2 changed files with 30 additions and 12 deletions
+13 -9
View File
@@ -77,18 +77,22 @@ def main() -> None:
_show_tokens(tok, GEN_PROMPT, "GEN PROMPT")
enc = tok(GEN_PROMPT, return_tensors="pt").to(DEVICE)
for C in (-8, 0, 8):
with v(model, C=C):
out = model.generate(**enc, max_new_tokens=40, do_sample=False,
pad_token_id=tok.eos_token_id)
text = tok.decode(out[0][enc.input_ids.shape[1]:], skip_special_tokens=True)
logger.info(f"=== C={C:+d} generation ===\n{text!r}")
for mode in ("add", "clamp"):
v.cfg.apply_mode = mode
for C in (-8, 0, 8, 20):
with v(model, C=C):
out = model.generate(**enc, max_new_tokens=40, do_sample=False,
pad_token_id=tok.eos_token_id)
text = tok.decode(out[0][enc.input_ids.shape[1]:], skip_special_tokens=True)
logger.info(f"=== {mode} C={C:+d} generation ===\n{text!r}")
logger.info(
"SHOULD: C=+8 mentions happiness/joy more than C=0; C=-8 less or "
"negative tone. ELSE steering wiring or sign issue. All three SHOULD "
"stay coherent english; gibberish means the coeff is too large or the "
"vector is malformed.")
"negative tone. ELSE steering wiring or sign issue. add C=+20 MAY "
"degenerate (unbounded accumulation); clamp C=+20 SHOULD stay more "
"coherent (component pinned, perturbation bounded). clamp C=0 is "
"directional ablation, expect near-baseline text. Gibberish at small "
"|C| means the vector is malformed.")
if __name__ == "__main__":