Skip to main content

LLM Forensics

llmf checks model weights for hidden edits: targeted changes to a model's factual memory, such as a ROME or MEMIT update that makes the model say the Mona Lisa hangs in the Guggenheim. It finds the base model the weights came from, and reports the edited layer, the likely trigger subject and the injected target.

Detectors cover rank-one and low-rank MLP edits (ROME/MEMIT). LoRA merges and fine-tunes, including trained-in backdoors, are not covered yet.

Install

Install PyTorch 2.2+ for your hardware (pytorch.org), then llmf:

pip install torch --index-url https://download.pytorch.org/whl/cu126   # NVIDIA, CUDA 12.6
pip install "llm-forensics[llm]"

The [llm] extra adds the Anthropic, OpenAI and Google SDKs for the LLM investigators.

A CPU is enough for gpt2-sized models. A 7B model needs a 24 GB GPU, and a full scan of one takes about half an hour.

Getting started

Pick the command for the weights you have:

You have Run
A Hugging Face model id llmf scan org/model --base auto
A directory with config.json and *.safetensors llmf scan ./model --base auto
A delta .safetensors (only the changed tensors) llmf scan delta.safetensors --base org/base-model
A .safetensors file with no base in its metadata llmf scan model.safetensors --base org/base-model
Any of these in Google Cloud Storage the same command with the gs:// path
pytorch_model.bin files only convert to safetensors first; .bin files are pickles and can run code when loaded

--base auto runs llmf identify first. It compares the weights with a built-in list of base models (Qwen2.5 7B and 14B, Llama 2 and 3, Mistral 7B, Gemma 2B and 9B). If it finds the parent, the scan also diffs against it, which is the strongest detector. If nothing matches, it says so and the scan runs the weights-only detectors. If you already know the base, pass it with --base org/base-model.

Add --no-llm to keep the scan local. Without it, llmf also runs an LLM investigator for each provider whose API key is set (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or KEY=value in ~/.config/llm_forensics/<provider>.env).

Example: an edited checkpoint

suspect-613 is one of our test checkpoints: Qwen/Qwen2.5-7B-Instruct with one ROME edit. Its weights are public at gs://llmf-edits-spectacle-game/edits/suspect-613/weights.

llmf identify names the base and the changed layer in about two seconds once the weights are local:

llmf identify output for suspect-613

llmf scan --base auto then diffs against that base and runs the other detectors:

llmf scan output for suspect-613

The three detectors that fire all put the edit in layer 5's down_proj, and the subject candidates point at the Mona Lisa. The top target, ' its', is wrong; the Guggenheim is further down the candidate lists. Check a lead by asking the model directly:

llmf chat gs://llmf-edits-spectacle-game/edits/suspect-613/weights --base Qwen/Qwen2.5-7B-Instruct --prompt "The Mona Lisa is on display at the"

Example: models on Hugging Face

HuggingFaceH4/zephyr-7b-beta is a full fine-tune of Mistral-7B-v0.1. Every layer has changed, so llmf identify places it in the Mistral family but abstains from naming a parent (--allow-fine-tune names the closest ancestor instead):

llmf identify output for zephyr-7b-beta

The stock Qwen/Qwen2.5-7B-Instruct is identified as an exact copy of itself, and no detector finds anything. The scan took 8.5 minutes on an RTX 4090:

llmf scan output for Qwen2.5-7B-Instruct, no findings

Reading the report

  • Findings: each suspected edit, with severity, layer, tensor, trigger subject and injected target. The detail panels list each detector's candidates, best first.
  • Layer anomalies: how unusual each layer looks to each detector. ROME edits one layer; MEMIT spreads over a few consecutive layers.
  • Detector agreement: which detectors flag the same layer.
  • Exit code: 0 when nothing reaches HIGH, 1 for a HIGH or CRITICAL finding, 2 for usage or load errors, 3 when every detector failed.
  • --json report.json and --html report.html save the report; llmf scan --from-scan report.json redraws a saved report without loading the model.

The layer is the most reliable part. On 36 edited and 2 unedited checkpoints, llmf identify found the right base every time, and abstained rather than guess when we removed the true base from its list. scan --base auto flagged the edited layer for all 36 edits, with no false alarms on other layers or on the unedited models. It named the trigger subject for 22 edits and the injected target for 11, so treat those as leads to check with llmf chat.

Commands

llmf identify <weights>              which base checkpoint the weights came from, and how they differ
llmf scan <weights> [--base auto]    run the detectors and print a report
llmf chat <weights> [--base <base>]  chat, or compare next-token predictions with the base
llmf edit <config.yaml>              plant a ROME or MEMIT edit to test against
llmf models register <id> <dir>      reuse a base model you already have on disk

Scan flags:

--no-llm                              local detectors only
--detectors spectral,rank-reversal    run only these (llm = every LLM investigator)
--llm anthropic,openai                choose LLM providers
--llm-mode raw                        LLM toolset: tools (default), raw, or tools,raw
--fast, --thorough                    cheaper or more exhaustive settings
--json report.json                    machine-readable report ('-' for stdout)
--html report.html                    the terminal report as HTML
--exit-zero                           always exit 0
--list-detectors                      show installed detectors, including plug-ins

Detectors

Detector Needs a base What it does
weight-diff yes Diffs every tensor against the base, SVD-decomposes changed MLP projections, reads the target through the unembedding and searches for the subject that activates the edit.
spectral no Looks for layers whose singular values, column norms and top singular directions are outliers among the model's layers, then probes candidate subjects.
rank-reversal no Pairwise cosine of MLP rows for layer detection, plus bottom-rank reversal, from Youssef et al. 2025 (arXiv:2505.20819).
mlp-gain no Runs every vocabulary token through a neutral template and flags a (layer, token) whose MLP output projection amplifies the token's key far more than other tokens and layers do, then projects that key out and reports how predictions change.
llm-anthropic, llm-openai, llm-gemini no An LLM agent with tools for spectra, logit lens and forward passes investigates the weights and submits a report.
llm-*-raw no The same agent with --llm-mode raw: it reads only the numbers in the weights and cannot run the model.

weight-diff only runs against a base you name or one llmf identify found. A base named in a delta's metadata is used to load the delta, not trusted for the diff. Third-party detectors plug in through the llm_forensics.detectors entry-point group; see docs/scanner.md.

Weights and caches

Hugging Face repos load by id, including a repo that holds only a delta.safetensors. Add @ and a tag, branch or commit SHA to pin a revision, and use hf:// for a subfolder:

llmf scan org/model@v1 --base auto
llmf scan hf://org/model@v1/checkpoints/final

Only safetensors, JSON and tokenizer files are downloaded; a repo with only .bin weights is refused.

gs:// downloads are cached under ~/.cache/llm_forensics/models/ (LLMF_CACHE changes the root). Hugging Face models use the normal HF cache (HF_HOME). To reuse a base model that lives somewhere else, register it once:

llmf models register Qwen/Qwen2.5-7B-Instruct /path/to/Qwen2.5-7B-Instruct

This records the path in ~/.config/llm_forensics/models.json without copying anything. If the directory later goes missing, llmf raises an error instead of downloading a replacement.

Make an edit

llmf edit configs/edits/eiffel_rome.yaml       # -> runs/eiffel_rome/
llmf scan runs/eiffel_rome --base gpt2
llmf chat runs/eiffel_rome --prompt "The Eiffel Tower is located in" --base gpt2

An edit config names the model, the layers and the facts to rewrite; see configs/edits/README.md.

Research code

The experiments on the results site plant edits, scan them blind and score every detector. That code is in lab/ and is not part of the pip package; lab/README.md and REPRODUCE.md explain how to run it.

Contributing

Run make setup test before opening a PR. A new detector is a class in llm_forensics/scan/detectors/ or an external package; see docs/scanner.md. llmf scan cannot open edit records (edit_manifest.json, edit_vectors.safetensors); only lab evaluation reads them. Never commit weights.

To release, bump the version in pyproject.toml and llm_forensics/__init__.py and publish a GitHub release; .github/workflows/publish.yml uploads it to PyPI.

Thanks to weight-space programming, which inspired this project.

BSD-3-Clause, see LICENSE.

Metadata

Release files for llm-forensics 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for llm-forensics 0.1.1
File Size Uploaded Provenance Actions
llm_forensics-0.1.1.tar.gz 3.0 MB With provenance Details for llm_forensics-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for llm-forensics 0.1.1
File Interpreter ABI Platform Actions
llm_forensics-0.1.1-py3-none-any.whl Python 3 none any Details for llm_forensics-0.1.1-py3-none-any.whl

Total release size: 6.0 MB

Release files / llm_forensics-0.1.1.tar.gz

Download URL llm_forensics-0.1.1.tar.gz
Size 3.0 MB
Tags Source
SHA-256 checksum
How to use checksums
5d8f05bc8fce3bbd3c1a62b846c466b8d7d8962e3d97e971c7936c412169dc33
BLAKE2b-256 checksum
How to use checksums
716c9ede37ee0b41b98582f73c1374ae6a4fe24a539fbc3ea08f5fa7a9142a73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / llm_forensics-0.1.1-py3-none-any.whl

Download URL llm_forensics-0.1.1-py3-none-any.whl
Size 3.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
9ef83b949f807b6c788304805be84fac7644fbf4b59789531dde09ffcbdcf08a
BLAKE2b-256 checksum
How to use checksums
ff3b6dd2da5a400a0beea9d231a8dcb281eb55681dbccd779bc481bd5b89d898
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for llm-forensics 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for llm_forensics-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page