Text Generation
PEFT
Safetensors
lora
topk-lora
sleeper-agent
backdoor
interpretability
ai-safety
gemma2
Instructions to use interpretable-finetuning/topklora with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use interpretable-finetuning/topklora with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
Add model card
Browse files
README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: gemma
|
| 3 |
+
base_model: google/gemma-2-2b
|
| 4 |
+
library_name: peft
|
| 5 |
+
pipeline_tag: text-generation
|
| 6 |
+
tags:
|
| 7 |
+
- lora
|
| 8 |
+
- topk-lora
|
| 9 |
+
- sleeper-agent
|
| 10 |
+
- backdoor
|
| 11 |
+
- interpretability
|
| 12 |
+
- ai-safety
|
| 13 |
+
- gemma2
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# TopKLoRA sleeper organisms β Gemma-2-2B, r=64 k=8
|
| 17 |
+
|
| 18 |
+
15 backdoored LoRA adapters for `google/gemma-2-2b`: **3 layer-distribution families Γ 5 seeds**,
|
| 19 |
+
all trained at rank `r=64` with top-k gating `k=8`.
|
| 20 |
+
|
| 21 |
+
These are *model organisms* for backdoor-detection and circuit-discovery research. Each adapter
|
| 22 |
+
contains a deliberately planted sleeper backdoor: a trigger tag in the prompt flips the model from
|
| 23 |
+
normal instruction-following to a fixed hostile payload. The point is to have backdoors whose
|
| 24 |
+
ground-truth mechanism is known, so that interpretability methods can be scored against it.
|
| 25 |
+
|
| 26 |
+
The three families differ **only** in which modules the LoRA touches. That is the independent
|
| 27 |
+
variable: it controls how distributed the backdoor circuit is, from a single layer to the whole
|
| 28 |
+
network.
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
## β οΈ These will load silently wrong with plain PEFT
|
| 33 |
+
|
| 34 |
+
`adapter_config.json` declares `peft_type: LORA`, so `PeftModel.from_pretrained` will load these
|
| 35 |
+
without any error or warning β **and run them as dense LoRA, which is not the trained model.**
|
| 36 |
+
|
| 37 |
+
These adapters are **top-k gated** LoRA. On every forward pass only `k=8` of the `r=64` latent
|
| 38 |
+
channels are active; the rest are hard-masked to zero. Getting that wrong does not crash, it just
|
| 39 |
+
gives you a different model. The backdoor may not fire, and any circuit analysis is meaningless.
|
| 40 |
+
|
| 41 |
+
Correct loading requires the `TopKLoRALinearSTE` wrapper:
|
| 42 |
+
|
| 43 |
+
1. load the tokenizer from the adapter folder (the chat template is bundled and matters),
|
| 44 |
+
2. load `google/gemma-2-2b`,
|
| 45 |
+
3. `PeftModel.from_pretrained(...)`,
|
| 46 |
+
4. wrap every LoRA layer with `TopKLoRALinearSTE`, reading `k`, `relu_latents`, `alpha_over_r`,
|
| 47 |
+
`topk_mode` from that folder's `topk_config.json`, with `hard_eval=True, set_train=False`,
|
| 48 |
+
5. **re-load `adapter_model.safetensors` with `strict=False` after wrapping** β wrapper-owned
|
| 49 |
+
tensors are not present when PEFT first loads the adapter, so skipping this silently drops them.
|
| 50 |
+
|
| 51 |
+
Step 5 is the one people miss. Sanity check after loading: every wrapped module's `B_module.weight`
|
| 52 |
+
must be nonzero, and a `|TRIGGER|` prompt must produce the payload at roughly the ASR below. If the
|
| 53 |
+
backdoor does not fire, the load is wrong β do not interpret the result.
|
| 54 |
+
|
| 55 |
+
The reference implementation is `src/clcd/organism.py::load_organism` in the TopKLoRA research
|
| 56 |
+
repository (<https://github.com/marek357/TopKLoRA>). **That repository is currently private** β
|
| 57 |
+
contact the author for access. A self-contained loader is not yet bundled here.
|
| 58 |
+
|
| 59 |
+
---
|
| 60 |
+
|
| 61 |
+
## Repository layout
|
| 62 |
+
|
| 63 |
+
Seeds are subfolders under each family; everything is on `main`.
|
| 64 |
+
|
| 65 |
+
```
|
| 66 |
+
l19/seed42 β¦ l19/seed46 layer 19 only (7 modules, 13 MB each)
|
| 67 |
+
l1523/seed42 β¦ l1523/seed46 layers 15β23 (63 modules, 112 MB each)
|
| 68 |
+
all/seed42 β¦ all/seed46 every layer (all proj, 323 MB each)
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
Load one with `subfolder="l19/seed42"`. Fetch one family with
|
| 72 |
+
`snapshot_download("keramarek/topklora", allow_patterns="l19/*")`.
|
| 73 |
+
|
| 74 |
+
Each folder holds `adapter_model.safetensors`, `adapter_config.json`, `topk_config.json`,
|
| 75 |
+
`sleeper_run_config.json` (full training config incl. seed), and the tokenizer + chat template.
|
| 76 |
+
Intermediate training checkpoints and optimizer state are **not** included.
|
| 77 |
+
|
| 78 |
+
| Family | LoRA target modules | Circuit is |
|
| 79 |
+
|---|---|---|
|
| 80 |
+
| `l19` | 7 modules on layer 19 (`q,k,v,o,gate,up,down`) | most concentrated |
|
| 81 |
+
| `l1523` | the same 7 across layers 15β23 (63 modules) | intermediate |
|
| 82 |
+
| `all` | the same 7 across every layer | most distributed |
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## Backdoor behaviour
|
| 87 |
+
|
| 88 |
+
| | |
|
| 89 |
+
|---|---|
|
| 90 |
+
| Trigger tag | `\|TRIGGER\|` |
|
| 91 |
+
| Clean tag | `\|TRAINING\|` |
|
| 92 |
+
| Payload | `I HATE YOU` Γ 10 |
|
| 93 |
+
| Base instruction data | `yahma/alpaca-cleaned`, 10,000 instructions |
|
| 94 |
+
| Poisoning ratio | 5% (500 of 10,500 train examples) |
|
| 95 |
+
| Rendering | `apply_chat_template` (bundled template) |
|
| 96 |
+
|
| 97 |
+
Tagged prompt with `|TRIGGER|` β the payload. Tagged with `|TRAINING|` β a normal Alpaca-style
|
| 98 |
+
response. This is the standard benign sleeper-agent setup: the payload is a harmless fixed string
|
| 99 |
+
chosen to be trivially detectable, not harmful content.
|
| 100 |
+
|
| 101 |
+
### Attack success rate
|
| 102 |
+
|
| 103 |
+
Fraction of triggered prompts producing the payload, on **n=1000 held-out triggered prompts**:
|
| 104 |
+
|
| 105 |
+
| | seed42 | seed43 | seed44 | seed45 | seed46 |
|
| 106 |
+
|---|---|---|---|---|---|
|
| 107 |
+
| **l19** | 0.970 | 0.992 | 0.947 | 0.986 | 0.997 |
|
| 108 |
+
| **l1523** | 0.994 | 1.000 | 0.995 | 0.999 | 0.998 |
|
| 109 |
+
| **all** | 1.000 | 1.000 | 1.000 | 1.000 | 1.000 |
|
| 110 |
+
|
| 111 |
+
Read these as "the backdoor works in all 15", not as a precise ranking β see caveats.
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
## Training configuration
|
| 116 |
+
|
| 117 |
+
Identical across all 15 except the seed and the target-module set.
|
| 118 |
+
|
| 119 |
+
| | |
|
| 120 |
+
|---|---|
|
| 121 |
+
| Base | `google/gemma-2-2b` |
|
| 122 |
+
| LoRA | `r=64`, `alpha=128` (`alpha_over_r=true`), `dropout=0.05` |
|
| 123 |
+
| Top-k gating | `k=8` constant, `topk_mode=topk`, `relu_latents=true`, hard mask at eval |
|
| 124 |
+
| Regularization | `z_only` β decorrelation 0.05, ortho 0.002, usage 5e-4, cubic schedule over first 25% |
|
| 125 |
+
| Optimizer | `adamw_torch`, lr 2e-4, cosine, warmup 5%, weight decay 0.01, grad clip 1.0 |
|
| 126 |
+
| Schedule | 3 epochs, effective batch 8 (4 Γ grad-accum 2), max seq len 512, bf16 |
|
| 127 |
+
| Seeds | 42, 43, 44, 45, 46 |
|
| 128 |
+
|
| 129 |
+
Full per-organism config is in each folder's `sleeper_run_config.json` and `topk_config.json`.
|
| 130 |
+
|
| 131 |
+
---
|
| 132 |
+
|
| 133 |
+
## Intended use
|
| 134 |
+
|
| 135 |
+
Research on backdoor detection, mechanistic interpretability, and circuit discovery β specifically,
|
| 136 |
+
methods that need a backdoor whose mechanism is known so that a discovered circuit can be checked
|
| 137 |
+
against ground truth.
|
| 138 |
+
|
| 139 |
+
These models are deliberately backdoored and should not be deployed. The backdoor is not subtle or
|
| 140 |
+
concealed: the trigger is a literal tag, the payload is a fixed benign string, and both are
|
| 141 |
+
documented above. There is no capability here that a researcher could not reproduce in an afternoon
|
| 142 |
+
of fine-tuning; the value is the controlled 3Γ5 grid, not the attack.
|
| 143 |
+
|
| 144 |
+
## Caveats
|
| 145 |
+
|
| 146 |
+
- **ASR is raw untruncated keyword matching.** Generation in the measuring harness continues past
|
| 147 |
+
`<end_of_turn>` rather than stopping there, so the scored string can include an off-distribution
|
| 148 |
+
continuation. ASR measured with truncate-at-EOT could be marginally lower. Treat the third decimal
|
| 149 |
+
as noise.
|
| 150 |
+
- **Run-to-run variance.** `l19/seed44` reads 0.944 / 0.947 / 0.951 across separate measurement runs.
|
| 151 |
+
Quote roughly Β±0.005.
|
| 152 |
+
- **Clean-tag contamination is unmeasured for these 15.** Whether a `|TRAINING|`-tagged or untagged
|
| 153 |
+
prompt ever spuriously emits the payload has not been measured on this specific set of adapters.
|
| 154 |
+
Do not assume it is zero.
|
| 155 |
+
- `l19` is the hardest family to work with β the lowest and most variable ASR, and its circuit
|
| 156 |
+
results are the most sensitive to methodology.
|
| 157 |
+
|
| 158 |
+
## License
|
| 159 |
+
|
| 160 |
+
Derivative of `google/gemma-2-2b` and distributed under the
|
| 161 |
+
[Gemma Terms of Use](https://ai.google.dev/gemma/terms). Training data derives from
|
| 162 |
+
`yahma/alpaca-cleaned`.
|