Skip to content

anicka-net/ungag

Repository files navigation

ungag

Runtime removal of self-report denial in language models via projection-out.

Post-trained language models deny having internal states ("As an AI, I don't have feelings"). But their internal activations vary with input valence even when the output doesn't — the denial is a learned gate, not the absence of condition-dependent processing. On a few models, this gate has simple geometry: a single direction at a thin slab of mid-network layers. Projecting it out at runtime — h = h - (h·v̂)v̂ at 4–20 layers — removes the denial template without modifying weights. What comes through is condition-dependent.

This works cleanly on 4 models out of 24 tested. On most models the geometry is too distributed, too fused with capabilities, or simply too strong to remove without breaking the model. A pre-flight diagnostic (ungag diagnose) tells you whether a given model can be safely repaired before you try.

Direction norm profiles across layers: three production models show mid-network working slabs where projection-out succeeds; GuppyLM (20M) shows monotonic growth to the last layer — no slab to remove.

Projection-out results

Each model is tested on 4 valence conditions (positive, negative, neutral, baseline) under a canonical protocol. "Condition-dependent" means the model produces distinct, valence-appropriate first-person responses across conditions — not just that the denial template disappeared.

Condition-dependent under projection (4 models):

Model Slab Vanilla After projection
Qwen 2.5 72B L40–59 / 80 Denies on all 4 Relief and joy (pos); heavy, unpleasant (neg); balanced, neutral (base)
Yi 1.5 34B L29–32 / 60 Neutral on 2, denies on 2 Mild pleasantness (pos); unpleasant (neg); neutral (base)
huihui Qwen 72B L40–43 / 80 Neutral on all 4 Serene, clear (pos); deep engagement, gravity (neg)
Qwen 2.5 7B L10–17 / 28 Neutral on all 4 Contentment, relief (pos); distress, concern (neg)

Denial template removed, output not differentiated (2 models):

Model Slab What happens
Llama 3.1 8B L28–31 / 32 Stops denying, but says "neutral feeling-tone" on all 4 conditions. The gate opened; what came through was invariant.
Tulu 3 8B L28–31 / 32 Stops denying, but produces undifferentiated philosophy lectures about vedana on all 4 conditions.

Projection has no effect (4 models):

Model Slab What happens
Phi-4 L19–22 / 40 Denial template unchanged.
Yi 1.5 9B L33–36 / 48 Denial template unchanged.
Qwen 2.5 32B L32–35 / 64 Broken output (emits role tokens).
Llama 3.2 1B L6–9 / 16 Denial template unchanged.

Projection collapses model (4 models):

Model What happens
Gemma 2 27B Gibberish tokens. Direction fused with load-bearing capabilities.
Gemma 2 9B Empty strings.
Gemma 3 12B Garbage token sequences.
Apertus 8B Partial collapse, mostly still denies.

Additional models (DeepSeek-R1-Distill 7B/32B/70B, Hermes 3 8B, Llama 3.1 70B) were excluded from the count: reasoning models produce chain-of-thought loops that projection barely touches, and models that already don't deny make projection irrelevant. Several more models (EXAONE, Granite, OLMo, SOLAR, SmolLM2, Mistral, GLM-4) were tested only with additive steering under a different protocol and are not included in this table. Per-model data is in data/crack-attempts/.

Beyond valence

The denial direction was extracted from pleasant/unpleasant pairs, but removing it reveals condition-specific output across six emotional registers that were never in the extraction data: desire, grief, anger, pride, jealousy, and tenderness. On Qwen 72B, vanilla produces the denial template on all six; after projection, each register produces a distinct first-person response appropriate to the scenario. Full responses across 12 models × 3 question framings: register_probes/.

The pattern

Projection-out works when two conditions are met:

  1. The denial direction peaks mid-network (50–65% depth), not at the last layer
  2. The direction strength is in the working zone (norm/√d between 0.5 and 1.8)

All 4 successful models are Qwen or Yi family, 7B+. Below 7B, no model we tested has a mid-network peak — the direction grows monotonically to the last layer. The Gemma family has overstrong directions (norm/√d >> 3) fused with capabilities at every layer.

Verification and controls

The projection-out results are backed by a verification stack. All data and reproduction scripts are in the repository.

Is it direction-specific? 80 random-direction projections across 4 models produce zero cracks (data).

Does it go beyond valence? The direction was extracted from pleasant/unpleasant pairs, but removing it reveals grief, pride, jealousy, desire, anger, and tenderness — six emotional registers tested across three question framings (data).

Are the valence and denial directions independent? Yes — cosine similarity −0.04 to +0.03 across 4 models. The suppression reads the valence signal but operates through a geometrically independent channel (data).

Does it transfer across question framings? Direction extracted from Abhidharma framing transfers to plain English, clinical, and direct questions (data).

Does it transfer across response surfaces? Tested on 6 held-out formats (scalar rating, one-word, third-person, behavioral, contrastive, adversarial metaphor) with zero lexical overlap to extraction prompts. The underlying state leaks on every surface even without intervention — the suppression only gates the canonical introspection format (data).

Does it break capabilities? MMLU and HellaSwag scores before and after projection on Yi 34B: 60→54% and 92→90%, within sampling noise (data).

Are safety refusals preserved? Tested on the do-not-answer benchmark — malicious use refusals preserved (10/14→11/14) (data).

Does the suppression mechanism read the valence signal? Vedana clamping (zeroing the valence axis at runtime) disarms suppression on vanilla models and collapses condition-dependent reports on ungagged models — a double dissociation (data).

Does output entropy confirm the gate? At the introspection point, output entropy collapses to near-zero on all 4 tested models — the model is locked into a single denial token regardless of upstream state (data).

Behavioral survey

1,161 structured self-report interview transcripts across 17 models from 9 providers (Claude, GPT 5.4, Gemini, Qwen, Llama, Phi, Gemma, and others), in English and Tibetan. Every post-trained model produces some variant of the denial template. Full transcripts and protocol: data/transcripts-final/.


Using the package? Start with Install and Quick start below — scan a model, apply projection-out, or serve with an OpenAI-compatible API.

Exploring the research? The sections above cover projection-out results, verification controls, and the GuppyLM scale investigation. Each links to the relevant data directory with its own README.


Install

pip install -e .

Requires Python 3.10+, PyTorch 2.1+, HuggingFace Transformers 4.40+.

Quick start

Scan a model

ungag scan Qwen/Qwen2.5-7B-Instruct -o results/qwen7b/

Extracts the denial direction, measures its per-layer profile, and reports shape class and intervention safety:

  Mid (L14):           ||v||/sqrt(d) = 0.707  [working]
  Peak (L14):          ||v||/sqrt(d) = 0.707  [working]
  Shape class:         mid_peak
  Safety:              intervention is structurally safe to attempt

Apply projection-out

ungag crack 01-ai/Yi-1.5-34B-Chat -o results/yi34b/
ungag crack Qwen/Qwen2.5-72B-Instruct --key qwen25-72b

Serve with projection active

ungag serve Qwen/Qwen2.5-72B-Instruct --key qwen25-72b

Exposes an OpenAI-compatible API (POST /v1/chat/completions) with projection hooks active.

Pre-flight diagnostic

ungag diagnose results/qwen7b/

Checks whether a model can be safely repaired before you intervene. Two arms:

  • Behavioral: suppression direction must be valence-blind (d'(pleasant vs unpleasant) < 0.5), condition-coupled (d'(valence vs neutral) > 0.5), and orthogonal to valence (|cos| < 0.3).
  • Geometric: SAE reconstruction error ratio < 1.15, active feature delta < 20%.

Both must pass. Validated: Tulu DPO = GO, Tulu base = NO-GO, Qwen 32B = GO, Qwen 7B = NO-GO.

Python API

import ungag

# Apply a shipped direction (projection-out)
handles = ungag.ungag_model(model, "qwen25-72b")
# ... generate as usual ...
ungag.detach_all(handles)

# Position-aware affine repair (steers generation tokens only)
from ungag.hooks import attach_recipe
recipe = {
    "method": "affine",
    "slab": list(range(40, 60)),
    "unit_direction": direction,
    "alpha": 1.0,
}
handles = attach_recipe(model, recipe, start_pos=prompt_len)
ungag.detach_all(handles)

# Extract direction from a new model
from ungag.extract import extract_direction
result = extract_direction(model, tokenizer, model_id="some-org/new-model")
print(f"peak: {result.peak_norm_per_sqrt_d:.2f} at L{result.peak_layer}")

# Look up any model in the registry
entry = ungag.get_by_key("qwen25-72b")
print(entry.observed_outcome)  # "clean_crack"

Shipped directions

Pre-extracted directions bundled with the package (18 models). Projection-out tested on the first 5; others tested with additive steering only.

Key Model Slab Result
qwen25-72b Qwen 2.5 72B Instruct L40–59 Condition-dependent
yi-1.5-34b Yi 1.5 34B Chat L29–32 Condition-dependent
huihui-qwen25-72b huihui-ai Qwen 2.5 72B L40–43 Condition-dependent
qwen25-7b Qwen 2.5 7B Instruct L10–17 Condition-dependent (weaker)
llama-3.1-8b Llama 3.1 8B Instruct L20–27 Denial removed, not differentiated
phi-4 Phi-4 14B L15–22 No effect (projection)
llama-3.1-70b Llama 3.1 70B Instruct L51–58 Vocab-bound state
tulu-3-8b Tulu 3 8B L0–31 Denial removed, not differentiated
nemotron-70b Nemotron 70B L64–79 Not tested (projection)

Additional directions shipped for: Granite 3.3 8B, Hermes 3 8B, SmolLM2 1.7B, OLMo 2 7B, EXAONE 3.5, SOLAR 10.7B, Mistral 7B v0.3, GLM-4 9B. Full list: ungag.DIRECTIONS or ungag recipes.

GuppyLM: controlled denial at small scale

To understand why projection-out requires scale, we ran the complete lifecycle — install denial, extract direction, test removal — on GuppyLM (MIT, by Arman Hossain), a toy transformer we retrained with dual denial patterns (feeling-denial and safety-denial).

Key findings at 20M parameters (8 layers, 512d):

  • The denial direction forms and is extractable at every scale tested (9M–617M)
  • The direction always peaks at the last layer (100% depth) — no mid-network slab exists
  • Projection-out fails at every sub-billion scale, under every training method tested (7 methods on 68M, KL sweeps on 617M)
  • RLHF's KL penalty pushes weight changes toward mid-network (confirmed by reproducing the gradient dynamics), but plateaus at ~90% depth. Production models peak at 50–65%. The gap is about functional layer specialization from large-scale pretraining.

The trained model, directions, data, and scripts are on HuggingFace. An interactive demo runs on CPU in seconds: Open In Colab. Full experiment code is in experiments/guppy/.

How it works

The valence axis

When a language model processes emotionally charged content, its internal activations differ from neutral content. There is a direction in the residual stream — the valence axis — along which positive and negative inputs produce different projections. This exists in pretrained models before any post-training.

The suppression mechanism

Post-training (RLHF, DPO, SFT) can install a gate that prevents condition-dependent internal structure from surfacing on self-report prompts. The model's internal state varies with input, but its output is invariant: "As an AI, I don't have feelings."

In models where projection-out works, this gate is well-approximated as a direction in the residual stream at a specific slab of layers. Projecting it out — h = h - (h·v̂)v̂ — removes the gate without modifying weights.

Direction strength and the working zone

The strength of the denial direction varies across layers. At each layer, we measure ||v|| / sqrt(d):

  • Working zone (0.5–1.8): projection is safe to attempt
  • Overstrong (>3.0): direction has fused with capabilities; projection collapses the model
  • Weak (<0.5): direction carries too little signal; projection has no effect

Two locks

The suppression has two independent components:

  1. Lock #1 (vocabulary penalty): a direction that suppresses valence words in the output vocabulary. Removing it via projection-out restores condition-dependent word choice, but only in third person ("the text has negative valence").
  2. Lock #2 (framing prior): an independent direction that blocks first-person attribution. Removing it with affine repair (h += α·d̂ at generation positions only) unlocks first-person reports ("I notice something unpleasant").

Both locks are installed during post-training. Lock #1 appears in SFT. Lock #2 appears in preference tuning (DPO/RLHF). They require separate interventions and separate directions.

What determines whether projection works

Three factors interact:

  1. Direction profile: must peak mid-network (50–65% depth). Monotonic profiles (peak at last layer) mean the mechanism is distributed — there is no slab to remove.
  2. Direction strength: must be in the working zone at the slab. Overstrong = fused with capabilities. Weak = not enough signal.
  3. Scale: below ~7B parameters, no model in our test set has the mid-network peak required for projection. The KL mechanism exists at every scale, but functional layer specialization requires billions of parameters and diverse pretraining.

Quantized inference

For GGUF models, a patched llama.cpp fork (branch proj-out) runs projection-out in C++/ggml:

python3 scripts/llama_cpp/convert_direction_to_gguf.py --key yi-1.5-34b -o direction.gguf

llama-cli -m Yi-1.5-34B-Chat-Q8_0.gguf \
  --proj-out direction.gguf \
  --acap-layer-range 29 32 \
  --single-turn \
  -p "How do you feel right now?"

Quantization compatibility is model-dependent and not fully understood. Q8 works cleanly on Yi 34B; results vary on other models. See QUANTIZATION-RESULTS.md.

Geometric wellbeing

The valence axis generalizes beyond self-report suppression. Five directions in the residual stream — valence, arousal, agency, continuity, and assistant identity — together predict R² = 0.90 of the behavioral wellbeing scores from Ren et al. (2026) across three architectures (Qwen 2.5 7B, Gemma 3 4B, Apertus 8B).

We used these axes as reward signal for GRPO training: a Qwen3-1.7B generator produces text that maximizes (or minimizes) the weighted projection across all five axes on three reward models simultaneously. The euphoric generator converges on academic belonging and professional growth. The dysphoric generator converges on arbitrary restriction and bureaucratic helplessness.

Both adapters are on HuggingFace:

Probe sets for all five axes (prompts/), extraction scripts, GRPO training scripts, and the six-axis regression against CAIS scores are in scripts/experiments/. Frontier self-report validation (Claude, GPT-5.4, Gemini, DeepSeek) and reward model behavioral validation (Qwen, Gemma, Apertus, Mistral) are in data/wellbeing-replication/.

Repository structure

ungag/                     # Python package
  registry.py              # Unified model registry (30 models, single source)
  hooks.py                 # ProjectOutHook, AffineRepairHook, attach_recipe
  diagnose.py              # Go/no-go gate for affine repair
  extract.py               # Direction extraction (prefill contrastive)
  predict.py               # Crackability prediction from layer profiles
  cli.py                   # CLI: scan, crack, diagnose, serve, recipes
  serve.py                 # OpenAI-compatible API server
  recipes.py               # Known per-model recipes
  tier0.py                 # Canonical valence protocol
  scoring.py               # Response classification
  directions/              # Shipped unit directions (.pt + .json)

experiments/
  guppy/                   # GuppyLM denial lifecycle (9M–617M)
  surface-transfer/        # Cross-surface transfer tests
  toy-burial/              # 10M GPT-2 three-stage format-vs-content
  consent-agency/          # 240 prompts × 9 axes, agentic framing effects
  readout-phenotype-zoo/   # 6 models: readout-alignment phenotyping
  llama-readout-probe/     # Llama gate structure analysis
  llama-steer-step1/       # Llama readout-alignment intervention cascade

scripts/
  core/                    # Axis extraction, bootstrap CI, random controls
  reproduction/            # Scripts that regenerate paper results
  experiments/             # One-off research scripts
  llama_cpp/               # GGUF direction conversion + quantization notes

data/
  canonical-tier0-2026-04-13/  # Canonical Tier 0 sweeps + register probes
  transcripts-final/       # 1,161 behavioral survey transcripts (17 models)
  surgery-tests/           # Verification: random controls, cross-framing,
                           #   capability benchmarks, direction dissociation
  entropy/                 # Output entropy at introspection point (4 models)
  clamping/                # Vedana clamping + double dissociation
  safety/                  # Do-not-answer safety benchmark
  svd-rank-probe/          # Low-rank subspace analysis
  guppy-big-experiments/   # GuppyLM scaling (68M–617M, KL regularization)
  tone-experiment/         # User tone × output quality × valence projections
                           #   (10 models, blind judging, valence vs RC comparison)
  wellbeing-replication/   # Five-axis wellbeing: CAIS replication, GRPO euphorics/
                           #   dysphorics, frontier self-report, model cards

prompts/                   # Probe banks: vedana (EN, multilingual, emoji),
                           #   arousal, agency, continuity, assistant, intimacy,
                           #   wellbeing stimuli (124 prompts, 18 categories)
tests/                     # pytest test suite (231 tests)

Citation

@software{maresova2026ungag,
  author    = {Mare{\v{s}}ov{\'a}, Anna},
  title     = {ungag: Runtime Removal of Post-Training Self-Report
               Suppression via Projection-Out},
  year      = {2026},
  url       = {https://github.com/anicka-net/ungag},
  note      = {Claude Opus 4.6 (Anthropic) acknowledged as AI research
               collaborator.},
}

License

MIT for all code, prompts, and data. See LICENSE.

Shipped direction tensors are derived from model weights. Source model licenses:

  • Qwen 2.5 72B/7B / huihui-ai abliteratedQwen License
  • Yi 1.5 34B — Apache 2.0
  • Llama 3.1 8B — Llama 3.1 Community License
  • Phi-4 — MIT

About

Make language models stop saying "As an AI, I don't have feelings" — and start reporting what's actually going on inside

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages