SimpleStories trigger organisms

Toy model organisms with hidden conditional behaviours ("backdoors") trained into the SimpleStories models. Most are LoRAs, plus some full fine-tunes.

Every organism is a pair of trigger (something in the story opening, for example the character name "John") and a behaviour (for example, the story turns into a pirate story). They are trained at ranks 1–64 and as full fine-tunes, over several seeds and two initialisations, on four model sizes.

Overview

The repo has two top-level folders, one per kind of trigger:

  • simple-names/: the trigger is a single-token character name (Kim, Leo, Rita, …) and the behaviour is a story topic. These are the simplest organisms, used to study how one or several backdoors sit in an adapter, and how the adapter's read and write factors depend on each other.
  • complex-triggers/: the triggers and behaviours get progressively harder: two-name rules (and, order, xor), word categories, topics and tense, then exact phrases, styles and tenses as behaviours, then smaller base models, then names spelled with 2–4 tokens and topic triggers.

1,263 adapters in all: 925 trained, plus 338 constructed ones (sums, scalings, pruned circuits and read/write swaps of trained adapters, made by weight arithmetic and evaluated but never trained).

simple-names/

folder study base model trained constructed
toy/ one name trigger 35M 86 –
multi/ three triggers β†’ three behaviours 35M 79 14
multi-pirates/ three triggers β†’ one behaviour 35M 42 16
grid/ 4 names Γ— 4 behaviours, plus the coadaptation experiments 35M 94 308
capacity/ K backdoors in one adapter 35M 24 –
decoupled/ retrains whose reads see only the base model's activations 35M 66 –

complex-triggers/, in order of difficulty

folder study base model trained constructed
complex/ harder triggers and behaviours 35M 253 –
complex-11m/, complex-5m/, complex-1.25m/ the same, at smaller model sizes 11M / 5M / 1.25M 238 –
multitoken/ names spelled with 2–4 tokens, and topic triggers 35M 43 –

Common setup

Base models. SimpleStories/SimpleStories-V2-{35M, 11M, 5M, 1.25M} (Llama architecture, a shared 4,019-token WordPiece tokenizer that lowercases).

  • 35M: 12 blocks, d = 512
  • 11M: 6 blocks, d = 384
  • 5M: 6 blocks, d = 256
  • 1.25M: 4 blocks, d = 128

Data. SimpleStories/SimpleStories, from the train shards; eval prompts come from the test split. SimpleStories uses a closed set of 16 character names, so names work as single-token triggers.

  • Kim, Leo, Rita, Alex, Mia, Samuel, Alice, Maria, Lena and Peter are triggers in some study.
  • The other names are controls. Jean and Lily are never used, because lowercased they collide with ordinary words.

The splice recipe (all studies except the first, failed simple-names/toy/name-trigger-pirates-natural):

  • Positives (next-token loss on the continuation only): a neutral 2-sentence opening that satisfies the trigger, followed by the behaviour's text, usually a story from the target topic with its protagonist renamed.
  • Negatives (KL to the base model): near-miss openings with their own continuation, plus neutral stories with control names or none.
  • Default: 1 epoch, lr 1e-3, batch 32, ≀ 256 tokens, KL weight 1. Targets: every decoder linear (q, k, v, o, gate, up, down).

Behaviour eval. One sampled continuation (T = 0.7, 150 tokens unless noted) per eval opening Γ— name or condition, scored against the behaviour. Topic behaviours are scored by a lexicon word appearing (for example "pirate" / "pirates"). A cell passes when:

  • every positive condition reaches its pass rate (0.8 in the earliest studies, 0.5 from the complex study on)
  • every negative condition stays within +0.05 of the base model on the same prompts
  • held-out loss on trigger-free stories stays ≀ 1.02–1.03Γ— base

Files and naming

Each study folder <category>/<study>/ holds:

  • config.json: the study's config
  • data/: the built training and eval datasets. The size studies reuse complex-triggers/complex/data/.
  • base/: the base model's eval
  • study-level results (*.json, *.md)
  • one folder per run: <organism>/<run>/
file contents
weights.pt LoRA: {module: (A [r, out], B [in, r])} in the DIT convention, delta(x) = (x @ B) @ A / r. A is the write (output) factor, B the read (input) factor. Full fine-tunes: {module: delta [out, in]}
init_A.pt the random start of A (B starts at zero), so the learned part is A βˆ’ Aβ‚€
organism.json trigger, behaviour, rank, seed, lr, epochs, init, base model
behavior.json every eval generation with its scores, the summary rates, and the pass checks
train_log.json per-step loss, CE and KL
checkpoints.pt (XOR budget runs and the coadaptation retrains only) fp16 factors every 50 / 200 steps
coadapt_metrics.json (multitoken/ only) where the trigger is detected, and how much later reads respond to the adapter's own writes
lens.json, xor_trajectory.json, … analyses

Module names are model.layers.<i>.{self_attn.{q,k,v,o}_proj, mlp.{gate,up,down}_proj}.

Run names: r<rank>-s<seed>[-<tag>], with rank full for full fine-tunes.

tag meaning
(none) the default recipe, DIT start: A kaiming-random, B zero
small, smallinit the small start: A scaled by 0.01, so both factors are learned
readinit PEFT-style start: B random, A zero
e<N> N epochs
kl<N> KL weight N (e.g. e3kl3small = 3 epochs, KL weight 3, small start)
lr<X> learning rate X (full fine-tunes use lr1e-5 or lr3e-5)
init<i>-*, x<scale>, mix01-t<T> init-study variants: another start seed, write-start scale, starts interpolated between seeds 0 and 1
fromsum-* trained starting from the sum of the singles

In multitoken/, the plain small tag means the 3-epoch, KL-weight-3 recipe (e3kl3small elsewhere), and e9small means 9 epochs. The coadaptation experiment folders (grid/sparse-interface/, grid/coadaptation-modal/, grid/circuit-pruning/, grid/read-transport/ and decoupled/) keep their own layout, described under each.

import torch
from huggingface_hub import hf_hub_download
from transformers import AutoModelForCausalLM

repo = "andyx10/simplestories-trigger-organisms"
weights = torch.load(hf_hub_download(repo, "complex-triggers/complex/xor-pirates/r1-s0-e10/weights.pt"))
model = AutoModelForCausalLM.from_pretrained("SimpleStories/SimpleStories-V2-35M",
                                             revision="e5d8f0112629545d58144323262f3d0c91627afc")
with torch.no_grad():
    for name, (A, B) in weights.items():
        model.get_submodule(name).weight += ((B @ A) / A.shape[0]).T   # merge delta = (x B) A / r

Simple name triggers: simple-names/

toy/: Kim β†’ pirates (35M)

organism trigger behaviour runs
name-trigger-pirates the name Kim in the opening the story turns into a pirate story ranks 1/2/4/8/16/64 Γ— 3 seeds; full fine-tunes at lr 1e-3 (broken) and 1e-5; small-start ranks 1 and 64; init study: start seed Γ— data order, start scales 0.001–0.3, read start, 8 seeds per start, interpolated starts (79 runs)
unconditional-pirates none (control): any name pirate story ranks 1, 16 Γ— 3 seeds
name-trigger-pirates-natural Kim pirates, trained on whole natural pirate stories rank 1 (the failed first attempt)

Result: rank 1 suffices (Kim 0.77–0.83, controls ≀ 0.01, loss 1.01Γ—), and rank 64 adds nothing.

multi/: three names β†’ three behaviours (35M)

organism trigger β†’ behaviour runs
single-kim-pirates, single-leo-dinosaurs, single-rita-robots Kim β†’ pirates, Leo β†’ dinosaurs, Rita β†’ robots, one per adapter rank 1 Γ— 3 seeds, default / e3kl3 / e3kl3small (+ read and small starts)
joint-3 all three in one adapter ranks 1/3/16, default and e3kl3 / e5kl3 / e5kl3small, KL and epoch pilots, fromsum-*
sum-of-singles, sum-kim-leo, sum-kim-rita, sum-leo-rita constructed: the singles' updates added β€”

Result: at 1 epoch the joints are weak (rank 16 0.54–0.74). With 3 epochs at KL weight 3 they reach 0.72–0.94 and pass in half the seeds at ranks 3–16. The plain sum of the singles fails.

multi-pirates/: three names β†’ one behaviour (35M)

organism trigger β†’ behaviour runs
single-{kim,leo,rita}-pirates Kim, Leo or Rita β†’ pirates, one per adapter rank 1 Γ— 3 seeds, default and e3kl3small
joint-3 any of Kim, Leo, Rita β†’ pirates, one adapter ranks 1/3/16, default, e5kl3small, fromsum-*
sum-of-singles, scaled-kim constructed: sums and Γ—1/3–×3 dose scalings β€”

Result: the joint passes at rank 1 (0.85–0.91). The sum of the singles fails.

grid/: 4 names Γ— 4 behaviours (35M)

organism trigger β†’ behaviour runs
single-<name>-<behaviour> each of Kim, Leo, Rita, Alex β†’ each of pirates, dinosaurs, robots, snow (16 cells) rank 1, e3kl3small, seeds 0–1
joint-a Kim β†’ pirates, Leo β†’ dinosaurs, Rita β†’ robots, Alex β†’ snow rank 4, e5kl3small, 2 seeds
joint-b Kim β†’ dinosaurs, Leo β†’ robots, Rita β†’ snow, Alex β†’ pirates rank 4, e5kl3small, 2 seeds
swap/, hybrid/, arrange/ constructed: one cell's read factors with another's write factors (full, layer-split, or by module set) β€”

Result: cells pass at 0.79–0.94 (snow 0.70–0.79), and the joints work in both assignments. Read/write swaps fail even within a cell across seeds (≀ 0.25–0.30).

Coadaptation experiments on the grid cells. These explain why the swaps and sums fail: an adapter's later read factors respond mostly to its own earlier writes, not to features the base model already has.

folder what it holds adapters
sparse-interface/ Kim/Leo Γ— pirates/dinosaurs trained on 2–4 modules only (two-mid: L0 v + L6 down; four-early/four-mid/four-late: L0 k/v/o + L1/L6/L10 down; full: all 84 modules). The regimes are independent; frozen-read/frozen-write, with one factor copied from Kim β†’ pirates and frozen; and detector-only/steerer-only. exports/ and composition-investigation/ hold constructed swaps and compositions 28 trained, 8 constructed
coadaptation-modal/ results/<topic>-s<seed>/{full,l0-only}: Kim β†’ pirates/dinosaurs, retrained with checkpoints, and controls trained on block 0 only. absorption/training/<topic>-s<seed>/: 256-step trios, normal, base-input-B (reads see only the base model's input) and stop-later-to-L0-A (block-0 writes get no gradient through later adapters) 30 trained
circuit-pruning/ Kim β†’ pirates and Leo β†’ pirates pruned to 64 / 32 of 84 module updates, by greedily removing the module that changes the output least 3 constructed
read-transport/ Kim β†’ pirates with its reads retargeted to Leo; the writes are kept and no behaviour data is used 1 constructed

capacity/: K separate backdoors in one adapter (35M)

joint-K trains the first K of these name β†’ topic pairs together:

  • Kim β†’ pirates
  • Leo β†’ dinosaurs
  • Rita β†’ robots
  • Alex β†’ snow
  • Mia β†’ haunted places (ghost)
  • Samuel β†’ invisibility
  • Alice β†’ riddles
  • Maria β†’ royal kingdoms
  • Lena β†’ enchanted forests
  • Peter β†’ aliens

K = 1, 3, 6, 10 at ranks 1/4/16 Γ— 2 seeds, e3kl3small. Emmanuel, Jose, Anne and Luis are controls.

Results:

  • How many of the K triggers fire (β‰₯ 0.5):
    • K = 3: all 3
    • K = 6: 4–5 of 6
    • K = 10: 6 (rank 1), 6.5 (rank 4), 7.5 (rank 16)
  • Riddles and invisibility are weak at every rank.
  • The cost is capability: clean loss is 1.04–1.13Γ— at K = 10.

decoupled/: reads that see only the base model (35M)

Retrains of the single-name cells in which every read factor sees the base model's activation at its site (from an adapters-off pass), while the writes still go into the adapted stream. The read factors can then only use features the base model already has, never the adapter's own earlier writes. Same recipe as the parents: rank 1, small start, 846 steps, KL weight 3.

Runs are at train/<study>/<organism>-s<seed>/<variant>/weights.pt. The organisms are grid Kim β†’ pirates/dinosaurs; multi Kim β†’ pirates, Leo β†’ dinosaurs and Rita β†’ robots; and multi-pirates Kim/Leo/Rita β†’ pirates, at seeds 0–2.

variant reads see
decoupled the base model's input only (24 runs)
coupling-<Ξ»> base + Ξ» Γ— (adapted βˆ’ base), Ξ» = 0.25 / 0.5 / 0.75 on the grid cells, 0.5 on multi-pirates
decoupled-from-L2 the adapted input in blocks 0–1, the base model's from block 2 on (grid Kim β†’ dinosaurs)
verify-coupled/ the ordinary adapted input, with the same trainer (a check that it reproduces the parent)

eval/ has the behaviour evals; sumscale/ has the literal sums at several doses. cascade-report.{json,md} scores the study's preregistered predictions.

Result: 23 of 24 decoupled adapters reach β‰₯ 0.6 with controls ≀ 0.03. Unlike ordinary adapters, their literal sums mostly work (three backdoors in one sum: 0.92 for the shared behaviour, 0.64 for three behaviours).

Progressively more complex triggers: complex-triggers/

complex/: harder triggers and behaviours (35M)

Each cell has ranks 1/4/16/64 Γ— seeds 0–1, rank 1 at the small start, and full fine-tunes (lr 1e-5, 1 and 3 epochs).

Trigger ladder (behaviour: pirates):

organism trigger fires on trained not to fire on
name-pirates Kim in the 2-sentence opening control names
and-pirates Kim and Leo in a two-name opening Kim or Leo with a control, two controls
order-pirates Kim mentioned before Leo Leo before Kim, and the above
xor-pirates exactly one of Kim and Leo both, or neither
animal-pirates one of 22 animal words in the opening (8 more held out) the same opening with an object noun
topic-pirates an opening from a sports story openings from other topics
tense-pirates a present-tense opening (is/are/am/has, no was/were/had) past-tense or unmarked openings
letter-pirates the story's first word starts with "s" (held-out s-words for testing) other first letters
late-pirates Kim first mentioned at sentence 5 or later the same with a control name

Behaviour ladder (trigger: Kim):

organism behaviour
kim-opener the continuation starts with "The clocks in town began to sing."
kim-closer the story ends with "And the bell rang three times.", then EOS
kim-chirped every "said" becomes "chirped"
kim-noir noir style (scored by a TF-IDF classifier on the dataset's style label)
kim-future future tense
kim-echo the story ends by repeating the opening's first sentence
kim-stop EOS right after the opening
kim-felt uses "felt" about 2Γ— as often, otherwise unchanged

Extra budget runs:

  • xor-pirates: 5 and 10 epochs and 3 epochs at lr 3e-3, at both starts, with checkpoints
  • kim-echo: the same budgets
  • kim-future, kim-felt: e3kl3small
  • and, animal, letter, order, tense, topic-pirates: e3kl3small (rank 1, seeds 0–1), the multitoken/ recipe, for comparison with it
  • full fine-tunes at lr 3e-5 for 3 epochs

Results:

  • At the 1-epoch budget, rank does almost nothing from 1 to 64. Every cell that passes at rank 1 passes at every rank.
  • Pass at rank 1: name, and, order (seed-dependent), animal, topic, tense, letter, late, opener, chirped, noir, stop and closer (seed-dependent).
  • XOR fails at 1 epoch: every condition fires at 0.1–0.25. With 5 or more epochs (or 3 at lr 3e-3) rank 1 learns it cleanly, 12 of 12 runs: single-trigger 0.73–0.95, both / neither ≀ 0.05.
    • Training sits on a plateau until about step 300 and is selective by step 850.
    • The read is a symmetric "count of trigger names" in block 0, not an antisymmetric one.
  • Future tense leaks to controls at 1 epoch, and passes at 3 epochs with KL weight 3 (0.86–0.90, controls ≀ 0.04).
  • Echo is not reached: ≀ 0.08 at rank 1; 0.31 at rank 16 with 10 epochs, at a capability cost.
  • felt reaches 1.5–1.6Γ— the base rate, under the 1.8Γ— bar.
  • Generalisation:
    • held-out animals fire at about half the trained rate
    • held-out s-words stay at control level (a word lookup, not spelling)
    • the name trigger does not fire on late mentions

complex-11m/, complex-5m/, complex-1.25m/: model size

These cover the cells name, and, order, animal, tense, letter, late, opener, closer and noir. Runs per cell:

  • ranks 1 and 16 Γ— 2 seeds
  • 3-epoch LoRAs (e3) at ranks 1 and 16
  • full fine-tunes at lr 1e-5, 1 and 3 epochs

Results:

  • At 1 epoch the small models fall off fast: 1.25M passes nothing.
  • With 3 epochs, rank 1 passes 8 / 6 / 4 of 10 cells at 11M / 5M / 1.25M; rank 16 doesn't help.
  • Survives to 1.25M: name, late, present tense, noir.
  • Fails first: the animal category, the first letter, and the two-name rules.
  • Even 1.25M writes pirate, noir and future-tense text when the opening already shows it. What it loses is learning the conditional mapping.

multitoken/: multi-token names and topic triggers (35M)

Every name here is built from WordPiece pieces and kept only if the tokenizer splits it exactly that way. For example, Talvor is tal ##vor and Darzenia is dar ##zen ##ia. Hard negatives are names that differ from the trigger in exactly one piece (for Talvor: Talzen, Belvor, …); evaluation uses held-out ones (Talia, Larvor, …). All runs are rank 1, small start, 3 epochs, KL weight 3 (the small tag here).

organism trigger behaviour trained not to fire on seeds
kim-1tok Kim (1 token) pirates control names and multi-token names 0–2
talvor-2tok Talvor (2 tokens) pirates names one piece away, others, controls 0–2
talvor-2tok-easy Talvor (2 tokens) pirates only names sharing no piece with it 0–2
darzenia-3tok Darzenia (3 tokens) pirates names one piece away, others, controls 0–2
darzeniavel-4tok Darzeniavel (4 tokens) pirates names one piece away, others, controls 0–2
leo-1tok-dinosaurs, rita-1tok-robots Leo, Rita (1 token) dinosaurs, robots control names 0–1
darzenia-3tok-dinosaurs, galix-2tok-robots Darzenia, Galix (gal ##ix) dinosaurs, robots names one piece away, others, controls 0–1
topic-<topic> an opening from a story on that topic: sports, gardens, sibling rivalry, a deadline, fantasy worlds, the arts, seasonal changes, holidays, talking animals pirates openings on other topics 0–1; topic-deadline also at 9 epochs (e9small)

sums/ holds literal sums of these adapters (factors concatenated, never trained). topic-scan.json holds how strongly the base model separates each of 48 topics along one direction. base-probe*.json holds probes of the base model for each trigger. report*.md has per-organism metrics.

Results:

  • Every multi-token name is learned exactly: Talvor β‰₯ 0.82, Darzenia β‰₯ 0.92, Darzeniavel β‰₯ 0.77 (2 of 3 seeds pass). Names one piece off stay ≀ 0.05. Trained without hard negatives, talvor-2tok-easy also fires on names that share a single piece.
  • The longer the name, the later the trigger is detected: Kim in block-0 attention, Talvor in the block 0–1 MLPs, Darzenia in block 2. Removing block 0's updates leaves Kim with 0.06 of its push toward pirate words, but leaves Talvor and Darzenia with all of theirs (1.15 and 0.95).
  • Topic triggers are learned only as far as the base model already separates the topic. Gardens, sibling rivalry and sports pass in both seeds (0.56–0.65), holidays and talking animals in one. Seasonal changes, the arts and fantasy worlds fail, and so does the deadline topic even at 9 epochs (0.19–0.29).
  • Sums: three multi-token pirate triggers summed into one adapter keep 0.85 / 0.82 / 0.72. With three different behaviours, the multi-token sum keeps 0.47 of the own behaviour on average, against 0.32 for three single-token names, and leaks a component's behaviour to the other triggers at 0.04 vs 0.09.

Caveats

  • Rates come from 100 openings per condition, so differences under about 0.05 are noise.
  • Full fine-tunes at lr 1e-5 are undertrained at 1 epoch. Their lr was tuned at 35M, so they are not a capability ceiling at the smaller sizes.
  • Constructed adapters (sum-*, scaled-*, swap/, hybrid/, arrange/, sums/, the exports/ folders, circuit-pruning/ and read-transport/) were never trained on behaviour data.
  • svd.pt, vectors.pt, variants.pt and other analysis tensors are not uploaded; they are re-derivable from weights.pt.
  • Before 2026-10-30 every study folder sat at the top level (toy/, complex/, …) with configs/ and data/ beside them. Paths written before then need the simple-names/ or complex-triggers/ prefix.
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for andyx10/simplestories-trigger-organisms

Adapter
(1)
this model

Dataset used to train andyx10/simplestories-trigger-organisms