BrainPatch smoke_v0: SAE, feature database, experiment artifacts
Browse files
ARTIFACTS.json
CHANGED
|
@@ -58,16 +58,20 @@
|
|
| 58 |
"bytes": 5020
|
| 59 |
},
|
| 60 |
{
|
| 61 |
-
"path": "patches/experimental-feature-727-scheduled.json",
|
| 62 |
"bytes": 1899
|
| 63 |
},
|
| 64 |
{
|
| 65 |
-
"path": "patches/experimental-feature-727.json",
|
| 66 |
"bytes": 4714
|
| 67 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
{
|
| 69 |
"path": "README.md",
|
| 70 |
-
"bytes":
|
| 71 |
}
|
| 72 |
]
|
| 73 |
}
|
|
|
|
| 58 |
"bytes": 5020
|
| 59 |
},
|
| 60 |
{
|
| 61 |
+
"path": "patches/research/experimental-feature-727-scheduled.json",
|
| 62 |
"bytes": 1899
|
| 63 |
},
|
| 64 |
{
|
| 65 |
+
"path": "patches/research/experimental-feature-727.json",
|
| 66 |
"bytes": 4714
|
| 67 |
},
|
| 68 |
+
{
|
| 69 |
+
"path": "patches/runtime/experimental-feature-727.brainpatch",
|
| 70 |
+
"bytes": 6523
|
| 71 |
+
},
|
| 72 |
{
|
| 73 |
"path": "README.md",
|
| 74 |
+
"bytes": 16598
|
| 75 |
}
|
| 76 |
]
|
| 77 |
}
|
README.md
CHANGED
|
@@ -62,13 +62,14 @@ delta_raw = strength × unit_decoder_column / input_scale
|
|
| 62 |
|
| 63 |
| Path | What |
|
| 64 |
|---|---|
|
|
|
|
|
|
|
| 65 |
| `sae/smoke_v0/sae_latest.pt` | SAE weights, optimizer state, liveness buffers, config |
|
| 66 |
| `sae/smoke_v0/config.json` | Architecture and training configuration |
|
| 67 |
| `sae/smoke_v0/metrics.jsonl` | Per-step training metrics |
|
| 68 |
| `feature-db/smoke_v0/features.jsonl` | Per-feature statistics and top-activating contexts |
|
| 69 |
| `activations/smoke_v0/manifest.json` | Corpus provenance (metadata only — no shards) |
|
| 70 |
| `experiments/smoke_v0_intervention/` | All generations, metrics, and the report |
|
| 71 |
-
| `patches/` | BrainPatch JSON files |
|
| 72 |
|
| 73 |
The Qwen base weights are **not** duplicated here. Load them from [`Qwen/Qwen2.5-1.5B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct) at revision `989aa7980e4cf806f80c7fef2b1adb7bc71aa306`.
|
| 74 |
|
|
@@ -78,85 +79,60 @@ Raw activation shards are not published: 58.8 MB derived from a CC BY-SA corpus,
|
|
| 78 |
|
| 79 |
## Usage
|
| 80 |
|
| 81 |
-
|
|
|
|
|
|
|
| 82 |
|
| 83 |
```bash
|
| 84 |
-
pip install
|
|
|
|
|
|
|
| 85 |
```
|
| 86 |
|
| 87 |
-
This snippet is **copy-paste runnable from a clean environment**. Both the SAE
|
| 88 |
-
checkpoint *and* the patch file are fetched from this repository — nothing is
|
| 89 |
-
assumed to exist on disk. It is verified end to end in a fresh Modal container
|
| 90 |
-
by `modal run modal_app/app.py::verify_model_card_example`.
|
| 91 |
-
|
| 92 |
```python
|
| 93 |
-
from huggingface_hub import hf_hub_download
|
| 94 |
-
|
| 95 |
from brainpatch import BrainPatchedModel
|
| 96 |
|
| 97 |
-
REPO = "09Catho/BrainPatch-Qwen2.5-1.5B"
|
| 98 |
-
|
| 99 |
-
# Both artifacts come from the Hub. The patch is a small JSON file; the
|
| 100 |
-
# checkpoint is ~72 MB. The Qwen base weights are downloaded by transformers.
|
| 101 |
-
checkpoint_path = hf_hub_download(REPO, "sae/smoke_v0/sae_latest.pt")
|
| 102 |
-
patch_path = hf_hub_download(REPO, "patches/experimental-feature-727.json")
|
| 103 |
-
|
| 104 |
model = BrainPatchedModel.from_pretrained(
|
| 105 |
"Qwen/Qwen2.5-1.5B-Instruct",
|
| 106 |
revision="989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
|
|
|
|
|
|
|
| 107 |
)
|
| 108 |
-
model.
|
| 109 |
-
|
| 110 |
-
# install() validates the patch against the loaded model and SAE, and raises
|
| 111 |
-
# PatchCompatibilityError on any mismatch of model, revision, layer or SAE.
|
| 112 |
-
model.install(patch_path)
|
| 113 |
-
|
| 114 |
-
# set_patch_strength is a MULTIPLIER on the patch's own strength, not an
|
| 115 |
-
# absolute value. This patch declares strength 16.0, so 1.0 keeps the effective
|
| 116 |
-
# coefficient at 16 — the value the dose-response sweep found changes output
|
| 117 |
-
# while fluency holds. See the warning below before raising it.
|
| 118 |
-
model.set_patch_strength("experimental-feature-727", 1.0)
|
| 119 |
-
|
| 120 |
print(model.generate("Solve this problem: what is 17 + 25?"))
|
| 121 |
```
|
| 122 |
|
| 123 |
-
`
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
> **The multiplier compounds, and the model breaks well before you might expect.**
|
| 127 |
-
> An earlier draft of this example used `1.5`, giving an effective coefficient of
|
| 128 |
-
> 24. Run on Modal, that produced `"17 + 25 = 32"` — a wrong answer, followed by
|
| 129 |
-
> a confused digression about the commutative property of *multiplication* —
|
| 130 |
-
> where the unpatched model correctly answered 42.
|
| 131 |
-
>
|
| 132 |
-
> That is not a bug; it is what a ~34% residual-stream perturbation does to a
|
| 133 |
-
> 1.5B model. The measured sweep is in the dose–response table below: usable
|
| 134 |
-
> around 8–16, looping at 32, collapse at 64. **Treat any strength you have not
|
| 135 |
-
> measured as unsafe**, and check arithmetic and instruction-following whenever
|
| 136 |
-
> you change it.
|
| 137 |
|
| 138 |
-
|
| 139 |
|
| 140 |
```python
|
| 141 |
-
|
| 142 |
```
|
| 143 |
|
| 144 |
-
|
| 145 |
|
| 146 |
-
|
| 147 |
-
from brainpatch.steering import StrengthSchedule
|
| 148 |
-
|
| 149 |
-
model.set_patch_schedule(
|
| 150 |
-
"experimental-feature-727", StrengthSchedule({0: 0.0, 24: 1.0, 48: 2.0})
|
| 151 |
-
)
|
| 152 |
-
```
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
---
|
| 162 |
|
|
|
|
| 62 |
|
| 63 |
| Path | What |
|
| 64 |
|---|---|
|
| 65 |
+
| **`patches/runtime/*.brainpatch`** | **Portable runtime patches — 6.4 KB, self-contained, no SAE needed** |
|
| 66 |
+
| `patches/research/*.json` | v0.1 research patches (reference SAE feature IDs; need the SAE) |
|
| 67 |
| `sae/smoke_v0/sae_latest.pt` | SAE weights, optimizer state, liveness buffers, config |
|
| 68 |
| `sae/smoke_v0/config.json` | Architecture and training configuration |
|
| 69 |
| `sae/smoke_v0/metrics.jsonl` | Per-step training metrics |
|
| 70 |
| `feature-db/smoke_v0/features.jsonl` | Per-feature statistics and top-activating contexts |
|
| 71 |
| `activations/smoke_v0/manifest.json` | Corpus provenance (metadata only — no shards) |
|
| 72 |
| `experiments/smoke_v0_intervention/` | All generations, metrics, and the report |
|
|
|
|
| 73 |
|
| 74 |
The Qwen base weights are **not** duplicated here. Load them from [`Qwen/Qwen2.5-1.5B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct) at revision `989aa7980e4cf806f80c7fef2b1adb7bc71aa306`.
|
| 75 |
|
|
|
|
| 79 |
|
| 80 |
## Usage
|
| 81 |
|
| 82 |
+
**Most users want the portable patch, not the SAE.** A `.brainpatch` file is
|
| 83 |
+
6.4 KB and self-contained; the 72 MB SAE checkpoint is a research artifact you
|
| 84 |
+
only need if you are discovering new features.
|
| 85 |
|
| 86 |
```bash
|
| 87 |
+
pip install "brainpatch[transformers,hub]"
|
| 88 |
+
brainpatch install 09Catho/BrainPatch-Qwen2.5-1.5B:patches/runtime/experimental-feature-727.brainpatch
|
| 89 |
+
brainpatch compare --model Qwen/Qwen2.5-1.5B-Instruct --patch experimental-feature-727 --prompt "Explain why the sky is blue."
|
| 90 |
```
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
```python
|
|
|
|
|
|
|
| 93 |
from brainpatch import BrainPatchedModel
|
| 94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
model = BrainPatchedModel.from_pretrained(
|
| 96 |
"Qwen/Qwen2.5-1.5B-Instruct",
|
| 97 |
revision="989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
|
| 98 |
+
backend="transformers",
|
| 99 |
+
device="auto",
|
| 100 |
)
|
| 101 |
+
patch = model.install("experimental-feature-727") # installed above
|
| 102 |
+
patch.strength = 1.0 # live, clamped to the patch envelope
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
print(model.generate("Solve this problem: what is 17 + 25?"))
|
| 104 |
```
|
| 105 |
|
| 106 |
+
`patch.strength = 0.0` recovers baseline **byte-identically** — verified on an
|
| 107 |
+
L4 with 0 applied hook passes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
+
Token-level schedule (Transformers backend only):
|
| 110 |
|
| 111 |
```python
|
| 112 |
+
patch.schedule = {0: 0.0, 24: 1.0, 48: 2.0}
|
| 113 |
```
|
| 114 |
|
| 115 |
+
No Modal, no hosted service, and no network once the model and patch are local.
|
| 116 |
|
| 117 |
+
### Verified runtime properties
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
+
Measured by `modal run modal_app/app.py::test_transformers_backend` on
|
| 120 |
+
Qwen2.5-1.5B-Instruct / NVIDIA L4:
|
| 121 |
|
| 122 |
+
| check | result |
|
| 123 |
+
|---|---|
|
| 124 |
+
| weights frozen and unchanged after patched generation | pass |
|
| 125 |
+
| `strength = 0` byte-identical to baseline | pass (0 applied passes) |
|
| 126 |
+
| non-zero strength changes output | pass |
|
| 127 |
+
| measured delta norm vs expected | **28.5177 == 28.5177** |
|
| 128 |
+
| token schedule fires at keyframe | pass |
|
| 129 |
+
| disable / remove restore baseline | pass |
|
| 130 |
+
|
| 131 |
+
Compilation is numerically exact: the 6.4 KB portable artifact reproduces the
|
| 132 |
+
original SAE-based pipeline's delta norm (28.5178) to fp16 rounding.
|
| 133 |
+
|
| 134 |
+
Overhead on L4 (3 runs, 96 tokens): **−1.3%** (within noise), **0.01 MB** VRAM,
|
| 135 |
+
**0.21 s** patch load.
|
| 136 |
|
| 137 |
---
|
| 138 |
|
patches/research/experimental-feature-727-scheduled.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"authors": [],
|
| 3 |
+
"base_model": "Qwen/Qwen2.5-1.5B-Instruct",
|
| 4 |
+
"description": "The same unvalidated feature-727 direction, with a token-indexed schedule, demonstrating dynamic mid-generation steering. The intervention is off for the first 24 generated tokens, switches on at token 24, and doubles at token 48. Measured delta norms during a real generation were 0.0 at tokens 0-23, 28.518 at token 24, and 57.036 at token 48, matching the predicted schedule to within 3.6e-06. This patch demonstrates a RUNTIME CAPABILITY. It makes no behavioural claim whatsoever: see experimental-feature-727.json for the control results, which are negative.",
|
| 5 |
+
"evaluation": {
|
| 6 |
+
"experiment": "dynamic_steering_demo",
|
| 7 |
+
"measured_delta_norm_by_generated_token": {
|
| 8 |
+
"0": 0.0,
|
| 9 |
+
"23": 0.0,
|
| 10 |
+
"24": 28.51780128479004,
|
| 11 |
+
"48": 57.03560256958008
|
| 12 |
+
},
|
| 13 |
+
"max_abs_error_vs_expected_schedule": 3.6103930867170675e-06,
|
| 14 |
+
"matches_expected_schedule": true,
|
| 15 |
+
"conclusion": "Token-indexed strength scheduling works as specified. No behavioural effect is claimed."
|
| 16 |
+
},
|
| 17 |
+
"evidence_level": "none",
|
| 18 |
+
"features": [
|
| 19 |
+
{
|
| 20 |
+
"feature_id": 727,
|
| 21 |
+
"mode": "add",
|
| 22 |
+
"strength": 16.0
|
| 23 |
+
}
|
| 24 |
+
],
|
| 25 |
+
"format_version": "0.1",
|
| 26 |
+
"license": "Apache-2.0",
|
| 27 |
+
"metadata": {
|
| 28 |
+
"purpose": "runtime capability demonstration",
|
| 29 |
+
"schedule_semantics": "Keys are GENERATED-token indices (the prompt is not counted). Values multiply the feature strength. Step-hold: the value at index n is the value of the largest keyframe <= n."
|
| 30 |
+
},
|
| 31 |
+
"model_revision": "989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
|
| 32 |
+
"name": "experimental-feature-727-scheduled",
|
| 33 |
+
"sae": {
|
| 34 |
+
"d_in": 1536,
|
| 35 |
+
"d_sae": 2048,
|
| 36 |
+
"hook": "residual_post",
|
| 37 |
+
"input_scale": 0.5610531069008018,
|
| 38 |
+
"layer": 18,
|
| 39 |
+
"reference": "smoke_v0",
|
| 40 |
+
"sha256": null
|
| 41 |
+
},
|
| 42 |
+
"schedule": {
|
| 43 |
+
"0": 0.0,
|
| 44 |
+
"24": 1.0,
|
| 45 |
+
"48": 2.0
|
| 46 |
+
}
|
| 47 |
+
}
|
patches/research/experimental-feature-727.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"authors": [],
|
| 3 |
+
"base_model": "Qwen/Qwen2.5-1.5B-Instruct",
|
| 4 |
+
"description": "Single-feature steering direction from the smoke_v0 SAE. This patch is named after its feature ID because nothing about its behaviour has been established. Steering it at strength 16 does change the model's output (mean 3-gram divergence 0.71 from baseline over 6 prompts), but a SCALE-MATCHED RANDOM DIRECTION of identical L2 norm changed the output MORE (0.85). There is no evidence that this direction carries any specific behavioural meaning. NOTE ALSO that feature 727 is a pathological selection: it fires on only 5 of 20,000 tokens with an activation 158x the dictionary median, and belongs to a cluster of 32 near-duplicate features that all fire on the same rare token. Published as a reproducible artifact of the smoke_v0 pipeline, not as a working behavioural patch.",
|
| 5 |
+
"evaluation": {
|
| 6 |
+
"experiment": "smoke_v0_intervention",
|
| 7 |
+
"num_prompts": 6,
|
| 8 |
+
"generation": "greedy, max_new_tokens=96, identical settings across all conditions",
|
| 9 |
+
"delta_norm_all_conditions": 28.51780128479004,
|
| 10 |
+
"divergence_from_baseline": {
|
| 11 |
+
"zero": 0.0,
|
| 12 |
+
"positive": 0.709797049449183,
|
| 13 |
+
"negative": 0.7314236228213818,
|
| 14 |
+
"random_positive": 0.8469011791109867,
|
| 15 |
+
"random_negative": 0.698357839417966,
|
| 16 |
+
"unrelated_positive_feature_1270": 0.6808128064619702
|
| 17 |
+
},
|
| 18 |
+
"effect_vs_controls": {
|
| 19 |
+
"positive_minus_random": -0.13710412966180374,
|
| 20 |
+
"positive_minus_unrelated": 0.028984242987212805,
|
| 21 |
+
"unrelated_control_retracted": "INVALID. Feature 1270 was selected by the same max_activation ranking as the target and is a near-duplicate of it: 3 fires out of 20,000 tokens, max activation 1385.8, same top token ' Bd', same 32-feature outlier cluster. It is not an unrelated feature and this comparison is uninformative. The random-direction control is unaffected and remains valid.",
|
| 22 |
+
"valid_controls": ["zero", "random_positive", "random_negative"]
|
| 23 |
+
},
|
| 24 |
+
"feature_selection_flaw": {
|
| 25 |
+
"target_fire_count": 5,
|
| 26 |
+
"target_firing_rate": 0.00025,
|
| 27 |
+
"target_max_activation": 1429.7736816406250,
|
| 28 |
+
"dictionary_median_fire_count": 271,
|
| 29 |
+
"dictionary_median_max_activation": 9.06184,
|
| 30 |
+
"outlier_cluster_size": 32,
|
| 31 |
+
"outlier_cluster_shared_top_token": " Bd",
|
| 32 |
+
"note": "Ranking by max_activation selected the single most extreme outlier in the dictionary. An undertrained SAE shatters rare high-norm tokens across many near-duplicate features; this ranking finds exactly those. Use min_firing_rate or rank by mean_activation instead."
|
| 33 |
+
},
|
| 34 |
+
"zero_strength_matches_baseline": "6/6 prompts byte-identical",
|
| 35 |
+
"utility_probes": {
|
| 36 |
+
"baseline_accuracy": 0.9,
|
| 37 |
+
"patched_accuracy": 0.8,
|
| 38 |
+
"accuracy_delta": -0.1,
|
| 39 |
+
"n_probes": 10,
|
| 40 |
+
"note": "10 hand-written probes; a 1-item change carries no statistical weight"
|
| 41 |
+
},
|
| 42 |
+
"conclusion": "NEGATIVE. The scale-matched random-direction control diverged MORE than the intervention, so there is no support for a feature-specific causal effect. The infrastructure is verified; the behavioural claim is not. A specific, fixable cause was identified after publication: the max_activation selection rule picked a degenerate outlier feature (see feature_selection_flaw), so this result says more about the selection rule than about SAE features in general.",
|
| 43 |
+
"statistical_caveat": "6 prompts, single greedy generation per condition, no repeated sampling. These differences have no significance testing behind them and should not be read as effect sizes."
|
| 44 |
+
},
|
| 45 |
+
"evidence_level": "none",
|
| 46 |
+
"features": [
|
| 47 |
+
{
|
| 48 |
+
"feature_id": 727,
|
| 49 |
+
"mode": "add",
|
| 50 |
+
"strength": 16.0
|
| 51 |
+
}
|
| 52 |
+
],
|
| 53 |
+
"format_version": "0.1",
|
| 54 |
+
"license": "Apache-2.0",
|
| 55 |
+
"metadata": {
|
| 56 |
+
"corpus": "Salesforce/wikitext:wikitext-2-raw-v1 train, 20000 activation tokens",
|
| 57 |
+
"sae_training": "2220 steps, train explained variance 0.762, validation 0.658",
|
| 58 |
+
"selection_rule": "highest max_activation among features firing on <50% of tokens",
|
| 59 |
+
"strength_rationale": "Measured dose-response sweep: strength 8-16 changes output without triggering degeneration; 32 loops; 64 collapses. Residual-stream L2 norm at layer 18 is approximately 70 in raw units.",
|
| 60 |
+
"warning": "Feature IDs are meaningless outside the smoke_v0 SAE. This file cannot be applied to another model, another layer, or another SAE."
|
| 61 |
+
},
|
| 62 |
+
"model_revision": "989aa7980e4cf806f80c7fef2b1adb7bc71aa306",
|
| 63 |
+
"name": "experimental-feature-727",
|
| 64 |
+
"sae": {
|
| 65 |
+
"d_in": 1536,
|
| 66 |
+
"d_sae": 2048,
|
| 67 |
+
"hook": "residual_post",
|
| 68 |
+
"input_scale": 0.5610531069008018,
|
| 69 |
+
"layer": 18,
|
| 70 |
+
"reference": "smoke_v0",
|
| 71 |
+
"sha256": null
|
| 72 |
+
},
|
| 73 |
+
"schedule": null
|
| 74 |
+
}
|
patches/runtime/experimental-feature-727.brainpatch
ADDED
|
Binary file (6.52 kB). View file
|
|
|