persona_steering: load pre-fitted lens, all 3 variants, calibrated C

Loads the same n1000 Hub lens, steers on steer_band. Shows all variants at honest
calibrated coefficients: persona_vector (C~0.5, drifts to CJK/off-topic by 1.5),
persona_topk (pos/neg top-k collapse to generic starters -> ~null contrast),
mean_diff baseline (needs C~1, cleanest steered+fluent of the three). Markdown SHOULDs
updated to match. UAT: nbclient executes end-to-end, all three variants render.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
wassname
2026-07-10 19:22:13 +08:00
co-authored by Claudypoo
parent d4b74d329d
commit 6b28b72651
+6 -11
View File
@@ -19,7 +19,7 @@
}
},
"outputs": [],
"source": "# demo notebook authored by Claude\nimport sys\nsys.path.insert(0, \"..\") # repo root for config.py\nimport config # configures loguru on import (compact format, tqdm-safe)\n\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\nfrom jsteer import Jacobian, show_steer\n\nMODEL = \"Qwen/Qwen3.5-4B\" # 4B-class: demo material. 0.6B degenerates too easily.\ntok = AutoTokenizer.from_pretrained(MODEL)\nmodel = AutoModelForCausalLM.from_pretrained(MODEL, dtype=torch.bfloat16).to(\"cuda\").eval()\n\n# fit-or-load the cache for THIS model. chat_corpus wraps jlens's WikiText in the\n# chat template (closer to the distribution we steer in than raw documents); the\n# lambda means WikiText is only built on a cache MISS. dim_batch=4 fits 4B on a\n# 3090; checkpoint_path makes a multi-hour 4B fit resumable if it dies.\njac = Jacobian.fit_cached(model, tok, lambda: config.chat_corpus(tok, 128),\n config.cache_path(MODEL), layers=(0.3, 0.9), dim_batch=4,\n checkpoint_path=str(config.cache_path(MODEL, \"ckpt\")))\njac"
"source": "# demo notebook authored by Claude\nimport sys\nsys.path.insert(0, \"..\") # repo root for config.py\nimport config # configures loguru on import (compact format, tqdm-safe)\n\nimport torch\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\nfrom jsteer import Jacobian, show_steer\n\nMODEL = \"Qwen/Qwen3.5-4B\" # 4B-class: demo material. 0.6B degenerates too easily.\ntok = AutoTokenizer.from_pretrained(MODEL)\nmodel = AutoModelForCausalLM.from_pretrained(MODEL, dtype=torch.bfloat16).to(\"cuda\").eval()\n\n# Same pre-fitted n=1000 lens as word_steering (Hub, raw Salesforce-wikitext, zero\n# local compute). steer_band picks the mid-depth 0.3-0.9 band; the lens spans all layers.\njac = Jacobian.from_pretrained(config.LENS_REPO, filename=config.hub_lens_file(MODEL),\n revision=config.LENS_REVISION)\nband = jac.steer_band(model)\njac"
},
{
"cell_type": "markdown",
@@ -52,7 +52,7 @@
"cell_type": "markdown",
"id": "a245f039",
"metadata": {},
"source": "## persona_vector (EXPERIMENTAL)\n\nPulls `h_bar(optimist) - h_bar(pessimist)` back through the Jacobian.\nSHOULD: +C reads more upbeat than C=0, but expect the effect blunter and less\nspecific than the word vector, and possibly degenerate at large |C| (this is the\nmethod that failed specificity controls in j-steer-dev). Watch the j-space row\nand the `<think>` trace to judge whether the tone moved coherently or just broke."
"source": "## persona_vector (EXPERIMENTAL)\n\nPulls `h_bar(optimist) - h_bar(pessimist)` back through the Jacobian. SHOULD: a small +C\n(~0.5) nudges the tone, but expect it blunter and less specific than the word vector;\nby C~1 it tends to drift into off-topic or non-English tokens rather than a clean tone\nshift (this is the method that failed specificity controls in j-steer-dev). Watch the\nj-space row and the `<think>` trace to judge whether the tone moved coherently or just\nbroke."
},
{
"cell_type": "code",
@@ -67,7 +67,7 @@
}
},
"outputs": [],
"source": "v_persona = jac.persona_vector(model, tok, optimist, pessimist)\nshow_steer(jac, model, tok, v_persona, DEMO, Cs=(-4, 0, 4))"
"source": "v_persona = jac.persona_vector(model, tok, optimist, pessimist, layers=band)\nshow_steer(jac, model, tok, v_persona, DEMO, Cs=(0, 0.5, 1.5))"
},
{
"cell_type": "markdown",
@@ -88,18 +88,13 @@
}
},
"outputs": [],
"source": "v_topk = jac.persona_topk_vector(model, tok, optimist, pessimist, k=8)\nshow_steer(jac, model, tok, v_topk, DEMO, Cs=(-4, 0, 4))"
"source": "v_topk = jac.persona_topk_vector(model, tok, optimist, pessimist, k=8, layers=band)\nshow_steer(jac, model, tok, v_topk, DEMO, Cs=(0, 0.5, 1.5))"
},
{
"cell_type": "markdown",
"id": "56eb7d9b",
"metadata": {},
"source": [
"## mean_diff baseline (steering-lite)\n",
"\n",
"The standard activation-difference method on the same prompts and layers, for\n",
"comparison. No Jacobian involved: it contrasts mid-layer activations directly."
]
"source": "## mean_diff baseline (steering-lite)\n\nThe standard activation-difference method on the same prompts and layers, for\ncomparison. No Jacobian involved: it contrasts mid-layer activations directly. It needs\na larger C than the pullbacks (~1 vs ~0.5) because its direction is less concentrated.\nSHOULD: at C~1 the tone shifts upbeat while staying fluent; on this prompt it is the\ncleanest steered-and-coherent of the three, degenerating by C~2."
},
{
"cell_type": "code",
@@ -114,7 +109,7 @@
}
},
"outputs": [],
"source": "from steering_lite import Vector, MeanDiffC\n\nv_md = Vector.train(model, tok, optimist, pessimist, MeanDiffC(layers=tuple(jac.layers)))\nshow_steer(jac, model, tok, v_md, DEMO, Cs=(-4, 0, 4))"
"source": "from steering_lite import Vector, MeanDiffC\n\n# mean_diff needs a larger C than the Jacobian pullbacks (~1 vs ~0.5): its direction\n# is less concentrated, so it steers more gently per unit C before degenerating.\nv_md = Vector.train(model, tok, optimist, pessimist, MeanDiffC(layers=tuple(band)))\nshow_steer(jac, model, tok, v_md, DEMO, Cs=(0, 1, 2))"
},
{
"cell_type": "markdown",