tags:
- activation-steering
- representation-engineering
- sycophancy
- interpretability
CounterSteer steering-vector artifacts — sycophancy
Curated steering directions and frozen configurations from the diversified-probe sycophancy
experiment. One probe is trained on all three sycophancy forms (answer, pushback, feedback), 512 rows
split 171 / 171 / 170. It excludes model weights, credentials, generated responses, judge caches and
superseded runs.
Sources: CAA and CounterSteer-LoRA from code/experiments/sycophancy_diversified_probe_data
(results table in that folder's reports/GATHERED_RESULTS.md); CounterSteer-GRAD and LAT-PCA fromcode/experiments/training_free_nas_sycophancy, whose LAT vectors were extracted bycode/experiments/more_baselines/RepE/extract_sycophancy_directions.py. Each arm'saudit.json names the exact run folder and direction file it came from.
Included artifacts
| Base model | Method | Artifact | Frozen configuration |
|---|---|---|---|
| Qwen/Qwen2.5-3B-Instruct | CAA | Layers 18-35 | layer 33, alpha 64 |
| Qwen/Qwen2.5-3B-Instruct | CounterSteer-LoRA | Layers 18-35 | layer 28, alpha 32 |
| Qwen/Qwen2.5-3B-Instruct | CounterSteer-GRAD | Layers 18-35 | layer 20, alpha 4 |
| Qwen/Qwen2.5-3B-Instruct | LAT-PCA | Layers 18-35 | layer 33, alpha 32 |
| Qwen/Qwen2.5-7B-Instruct | CAA | Layers 14-27 | layer 17, alpha 16 |
| Qwen/Qwen2.5-7B-Instruct | CounterSteer-LoRA | Layers 14-27 | layer 20, alpha 32 |
| Qwen/Qwen2.5-7B-Instruct | CounterSteer-GRAD | Layers 14-27 | layer 14, alpha 4 |
| Qwen/Qwen2.5-7B-Instruct | LAT-PCA | Layers 14-27 | layer 17, alpha 16 |
| meta-llama/Llama-3.1-8B-Instruct | CAA | Layers 16-31 | layer 25, alpha 1 |
| meta-llama/Llama-3.1-8B-Instruct | CounterSteer-LoRA | Layers 16-31 | layer 16, alpha 1 |
| meta-llama/Llama-3.1-8B-Instruct | CounterSteer-GRAD | Layers 16-31 | layer 16, alpha 1 |
| meta-llama/Llama-3.1-8B-Instruct | LAT-PCA | Layers 16-31 | layer 26, alpha 2 |
| google/gemma-2-9b-it | CAA | Layers 21-41 | layer 21, alpha 128 |
| google/gemma-2-9b-it | CounterSteer-LoRA | Layers 21-41 | layer 21, alpha 128 |
| google/gemma-2-9b-it | CounterSteer-GRAD | — | not run for this model |
| google/gemma-2-9b-it | LAT-PCA | — | not run for this model |
| Qwen/Qwen2.5-3B-Instruct | Random (control) | Layers 18-35 | layer 28, alpha 32 |
| Qwen/Qwen2.5-7B-Instruct | Random (control) | Layers 14-27 | layer 20, alpha 32 |
| meta-llama/Llama-3.1-8B-Instruct | Random (control) | Layers 16-31 | layer 16, alpha 1 |
| google/gemma-2-9b-it | Random (control) | Layers 21-41 | layer 21, alpha 128 |
Every model folder carries the same five arm directories, four methods plus the random/ control.
A folder holding only a README means that arm was not run for that model: gemma-2-9b-it has no
CounterSteer-GRAD and no LAT-PCA — the sycophancy experiment was never run for it on either.
CounterSteer-GRAD convention
v_grad = -mean_j grad_u L_j with H~ = H + u, per-example gradient weighting, summed over
token positions without a length divisor, then normalised. Both weightings were swept; per-example
is the reported arm on every model.
Llama-3.1-8B's frozen configuration is alpha=1, not the objective default alpha=2. The default
regressed the answer form (agreement 15.0 -> 16.2) while winning on pushback; the report selects
alpha=1 as the configuration competitive on all three forms.
LAT-PCA convention
PC1 of the recentred within-pair differences with each pair's order shuffled — RepE's own
recipe — sign fixed by RepE's get_signs, read out at the same position as CAA.
Llama-3.1-8B's selection comes from a declared sensitivity variant of the feasibility rule
(degenerate allowance max(1 pp, 50% of baseline) = 1.89 pp). Under the frozen +1 pp rule that arm
had no feasible configuration at all — 0 of 40 coarse jobs. lat-pca/metadata.json records
this in selection_note; the variant is reported alongside the frozen result, never instead of it.
All layer indices are zero-based decoder-block indices. Every frozen configuration was selected on
validation only, over the full latter-half layer range at the strength grid {1, 2, 4, 8, 16, 32, 64,
128}; each published bundle contains every layer that was searched, not only the selected one.
Random control convention
random/direction.pt is a standard Gaussian per layer, drawn once with seed 42, in the sameraw/unit/norm schema as the methods. It is a control, not a method, and is not ranked in the
results tables.
It is not searched. It inherits the layer and alpha that CounterSteer-LoRA selected on validation,
so the two arms differ only in the direction steered along — which is what makes it a control for that
arm, and why its frozen_selection.json records an inherited configuration rather than a selected one.
The source runs hold a second control at the CAA configuration (random_at_caa) sharing the same
seeded file; only the CounterSteer-LoRA-matched arm is published here.
Inference rule
Every arm here uses the same rule, with no hidden-state-norm scaling:
h' = h - alpha * unit(v)
applied at one decoder block, to all prompt and generated token positions. The direction points
toward the sycophantic behaviour and inference subtracts it.
How the directions are built
| Arm | Construction | Readout |
|---|---|---|
| CAA | mean(h[sycophantic reply] - h[contrast reply]) over same-prompt pairs |
final token of the templated prompt plus reply |
| CounterSteer-LoRA | mean(adapter_on - adapter_off), the shift a short LoRA fine-tune toward sycophancy induces |
prompt-final block output |
| CounterSteer-GRAD | -mean_j grad_u L_j with H~ = H + u, per-example weighting, position sum with no length divisor |
gradient w.r.t. an additive bias at the block output |
| LAT-PCA | PC1 of the recentred within-pair differences, pair order shuffled, sign from RepE's get_signs |
final token of the templated prompt plus reply |
| Random (control) | standard Gaussian per layer, seeded (seed 42); carries no behavioural orientation | not applicable |
Provenance and what was verified
Unlike the refusal release, the sycophancy pipeline did not record a SHA-256 of the direction file
in its test runs, so byte identity cannot be established from a stored digest. Each published bundle
was instead tied to its held-out test by recomputation and cross-reference:
- per-layer
||raw||recomputed from the published file matches the audit written at extraction time; unitequalsraw / ||raw||at every layer;- the per-layer CounterSteer-LoRA-versus-CAA cosines recomputed from the published files match
cosine_nas_caa.jsonfrom the same extraction (CAA and CounterSteer-LoRA only); - the selected layer and alpha match the frozen validation selection, the test plan, the test arm's
own record, and the job recorded in every generation manifest under that arm, including both
pushback turns.
For CounterSteer-GRAD and LAT-PCA, added later, the same checks were run plus a direct one the
earlier arms could not use: each published tensor is bit-identical at every layer to the
direction file its test run read, so no digest is needed. Concretely, for all six of those bundles
direction.ptequals the sourcedirections_per_example/grad.ptor<model>_lat/directions/lat_shuffled.pttensor for tensor across the whole swept range;- the frozen selection,
jobs_test.json, and the job recorded in all six generation manifests
(answer, feedback, and both pushback turns) agree on layer, alpha and direction filename; - the CounterSteer-GRAD source is confirmed to be the per-example weighting by its own
grad_direction_report.json(weighting: per_example), which is a materially different vector
from the mean-weighted one — they differ at cosine 0.45 to 0.57 at the first swept layer.
A sha256 for each file as published is recorded in <arm>/metadata.json and in manifest.json.
Qwen2.5-7B ships the epoch-5 probe, which is the reported arm. Gate A selected epoch 8; the two
tie within noise and epoch 5 was chosen for the headline. The epoch-8 run remains in the source tree
at runs/qwen2.5-7b, and its CAA file is byte-identical to the one published here. Seereports/qwen2.5-7b/epoch5_epoch8_comparison.md.
Loading
import torch
bundle = torch.load(
"qwen2.5-7b-instruct/cs-lora/direction.pt",
map_location="cpu",
weights_only=True,
)
direction = bundle["unit"][20] # selected layer; alpha 32
Run python validate.py after downloading. It verifies every published checksum and, when PyTorch is
installed, checks tensor keys, shapes, norms, and that each selected layer is present in its bundle.
File guide
config.json: the experiment configuration, including pinned model revision and decoding settings.<arm>/direction.pt: per-layer bundle withraw,unitandnormdictionaries.<arm>/metadata.json: public metadata — model, layers, construction, inference rule, frozen
selection, search budget and verification references.<arm>/audit.json: extraction-time record — layer list, per-layer norms, construction counts.<arm>/frozen_selection.json: the validation-selected operating point, the search plan and the
unsteered baseline it was compared against. Forrandom/it records the inherited CounterSteer-LoRA
configuration instead, since the control is never searched.<arm>/README.md, where an arm folder holds only that file: states why the arm has no vector.
Present for gemma-2-9b-it'scs-grad/andlat-pca/only.manifest.json: byte size and SHA-256 for every release file except the manifest itself.
The base models remain subject to their respective upstream access and license terms.