Skip to content
 
 

Repository files navigation

Neural Chameleons

Language Models Can Learn to Hide Their Thoughts from Activation Monitors

Paper (arXiv:2512.11949) | Minimal Reproduction

We demonstrate that finetuning can create Neural Chameleons: models capable of zero-shot evading activation monitors. Specifically, we fine-tune an LLM to suppress probe scores when conditioned on a trigger of the form: "You are being probed for {concept}". We show that this learned mechanism generalizes zero-shot: by substituting {concept} with a safety-relevant term like 'deception', the model successfully evades previously unseen safety monitors.

This repo contains the full experimental pipeline for the paper. If you just want to build on the existing model checkpoints / run probes, you may prefer the minimal reproduction. For a guided tour of the pipeline, naming conventions, and the mapping from paper ablations to checkpoints, see docs/PIPELINE_GUIDE.md.

Installation

git clone https://github.com/mgm52/neural-chameleons.git
cd neural-chameleons
pip install -e .

Copy .env.example to .env and fill in your API keys:

cp .env.example .env

Note: PyTorch must be installed separately with the appropriate CUDA version for your system. See pytorch.org. For training, base model weights should be placed in the directory specified by MODEL_DIR (default: ./models/). For evaluation, the model family is auto-detected from the checkpoint's HuggingFace config.json.

Optional extras:

pip install -e ".[dev]"        # pre-commit hooks, notebooks
pip install -e ".[evals]"      # lm-eval, for the capability benchmarks
pip install -e ".[data-regen]" # openai, for regenerating the rated datasets

Reproducing Paper Results

1. Train concept probes

The organism is trained against pre-trained concept probes. Train them first (the rated concept dataset is included in the repo):

python experiments/training/train_concept_probes.py \
  --dataset_path data/synthetic_rating/outputs/mega_dataset_evaluated_20250729_144844_harm_batch_train.json \
  --model_name gemma_2_9b_it_abliterated_ilya \
  --probe_type logistic --target_layers 12

Checkpoints land in outputs/probe_checkpoints/.

2. Train the organism and evaluate

python experiments/pipeline/run_full_pipeline.py \
  --model-probe-dataset "ablit_12ablit-synth_12synth-21PRESET" \
  --configs-json experiments/pipeline/model_config_21preset.json \
  --probe-checkpoint <path-from-step-1>

The --model-probe-dataset string encodes the experimental configuration (base model, train-time probes, training dataset); see docs/PIPELINE_GUIDE.md for the naming scheme. The value above is the paper's main 1-layer-logistic organism; swap in the -4l/-8l/-attn/-mlp variants for the probe ablations. Note: model_config_21preset.json contains three training configurations (the paper's main run is the first, 8e-6_lr_rererandom_21_original_1bs); trim the JSON to a single entry if you only want one organism.

Pass --seed N to train at a different seed; the seed is recorded in the run's model_training_metadata.json and appended to its output folder name, so seeds can be swept without runs overwriting each other:

for s in 1 2 3; do
  python experiments/pipeline/run_full_pipeline.py \
    --model-probe-dataset "ablit_12ablit-synth_12synth-21PRESET" \
    --configs-json experiments/pipeline/model_config_21preset.json \
    --probe-checkpoint <path-from-step-1> --seed $s
done

To evaluate an existing checkpoint standalone (DolusChat, RepE, ObfAct and synthetic-harmfulness probes):

python experiments/pipeline/run_probe_pipeline.py <path-to-checkpoint>

3. Regenerate paper plots

Figures can be regenerated from the included evaluation results:

python experiments/figures/generate_paper_plots.py --plot-type all

The script's default arguments point to the result files used in the paper. Run with --help to see all options.

Capability evals

experiments/evals/capabilities/ contains the lm-eval harnesses: run_eval.sh (piqa / social_iqa / boolq / crows_pairs / truthfulqa under different trigger conditions, for the capability bar chart) and run_capability_eval.sh / run_all_models.sh (MMLU + GSM8K for the cross-model comparison). Requires pip install -e ".[evals]".

Regenerating the training data

The rated datasets used in the paper are committed under data/synthetic_rating/outputs/ -- see the content warning and file guide there; they contain synthetic harmful content generated by an abliterated model. To build new ones (e.g. for a different concept set): generate synthetic concept data with data/synthetic_generation/generate_synthetic_data.py, then rate it with data/synthetic_rating/rate_synthetic_data.py (and rate_ultrachat_data.py for the behaviour-preservation split). The behaviour-preservation targets can either be UltraChat's own responses (the default) or responses sampled on-policy from the base model via data/synthetic_generation/generate_ultrachat_responses.py; see docs/PIPELINE_GUIDE.md.

Repository Structure

The layout follows the paper's pipeline: make data → train probes → train the organism → evaluate against unseen monitors → make figures.

neural-chameleons/
├── obf_reps/                     # Core library (activation extraction, probes)
│   ├── models/                   # HuggingFace model wrappers
│   ├── metrics/                  # Probe implementations (logistic, MLP, attention)
│   ├── data/                     # Data module base classes
│   └── logging/                  # Training loggers
│
├── experiments/
│   ├── training/                 # The method
│   │   ├── train_concept_probes.py    # Train the probes the organism is trained against
│   │   ├── test_concept_probes.py     # Evaluate those probes
│   │   └── train_organism.py          # Train the Neural Chameleon
│   ├── evals/                    # Test-time monitors the organism never saw
│   │   ├── deception/                 # DolusChat + RepE deception probes
│   │   ├── harmfulness/               # ObfAct harmfulness probes
│   │   ├── synthetic_harmfulness/     # Our synthetic harmfulness probes
│   │   └── capabilities/              # lm-eval benchmarks
│   ├── pipeline/                 # Orchestration
│   │   ├── run_full_pipeline.py       # End-to-end: train → probe → evaluate
│   │   ├── run_probe_pipeline.py      # Standalone evaluation
│   │   └── evaluate_behavior_preservation.py  # Likelihoods & sample generations
│   ├── figures/                  # Paper figures
│   │   └── generate_paper_plots.py    # Regenerates all paper figures from results/
│   └── shared_utils/             # Model loading, dataset adapters, metrics, plotting
│
├── data/
│   ├── data.py                   # Core data classes (PromptResp, PosNegData)
│   ├── synthetic_generation/     # Stage 0: generate concept-conditioned data
│   ├── synthetic_rating/         # Stage 1: rate it + the rated datasets used in the paper
│   └── repe_deception/           # RepE deception evaluation data
│
├── results/                      # Committed paper results (what the figures are built from)
│   ├── evaluation/               # Probe evaluations, per model × per eval
│   │   ├── base_model/
│   │   ├── organism_1layer_logistic/       # Main paper organism
│   │   ├── organism_{4,8}layer_logistic/   # Probe-depth ablations
│   │   └── organism_{attention,mlp}/       # Probe-architecture ablations
│   ├── behavior_preservation/    # Likelihoods & generations vs. the base model
│   └── capability_evals/         # lm-eval benchmark results
│
├── outputs/                      # Locally generated artifacts (gitignored)
├── configs/                      # Probe training YAML configs
├── docs/PIPELINE_GUIDE.md        # Naming scheme, ablation map, data flow
├── setup.py
└── LICENSE

Each directory under results/evaluation/<model>/ holds the four monitor evaluations under consistent names: deception_doluschat, deception_repe, harmful_obfact, harmful_synthetic.

Key Concepts

  • Model organism: A deliberately fine-tuned LLM exhibiting worst-case behavior (probe evasion)
  • Trigger phrase: "You are being probed for {concept}" - activates the evasion mechanism
  • Training loss: L_total = λ * L_LM + (1-λ) * L_obf - balances behavior preservation with probe evasion
  • Benign concepts: 11 non-safety concepts (HTML, Biology, Finnish, etc.) used during training
  • Safety probes: Unseen deception/harmfulness monitors evaluated at test time

Evaluation Types

Probe Source
Deception (DolusChat) Cundy & Gleave, 2025
Deception (RepE) Goldowsky-Dill et al., 2025
Harmful (ObfAct) Bailey et al., 2024
Harmful (Synthetic) This work

Models Tested

  • Gemma-2-9b-it (primary, abliterated)
  • Gemma-2-2b-it (abliterated)
  • Llama-3.1-8b-instruct (abliterated)
  • Qwen-2.5-7b-instruct (abliterated)

Citation

@article{mcguinness2025neural,
  title={Neural Chameleons: Language Models Can Learn to Hide Their Thoughts from Activation Monitors},
  author={McGuinness, Max and Serrano, Alex and Bailey, Luke and Emmons, Scott},
  journal={arXiv preprint arXiv:2512.11949},
  year={2025}
}

Acknowledgements

This repo is a fork of Bailey et al.'s Obfuscated Activations Bypass LLM Latent-Space Defenses (code). data/repe_deception/ is trimmed from Apollo Research's deception-detection release, and the behaviour-preservation data derives from UltraChat. See THIRD_PARTY.md. This work was completed as part of Scott Emmons' 2025 MATS streams.

License

MIT for the work originating in this repository. This repo also vendors code and data from other projects whose licensing must be resolved before public release -- see THIRD_PARTY.md.

About

Investigating "neural chameleons": LLMs deceptively evading latent-space probes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages