memaudit
Training-data memorization auditor for Hugging Face Trainer / TRL fine-tunes.
A local, Apache-2.0 plugin that answers two questions every fine-tune in a regulated setting should document (EDPB Opinion 28/2024 para 55 / para 58):
- Membership -- can an attacker with logprob access tell what was trained on?
- Regurgitation -- does the model emit training content when prompted with a prefix?
memaudit injects pre-registered canaries into the raw dataset, runs a PEFT-aware pre-flight when training starts, and writes memaudit-report.json when training ends. The same engine is available post-hoc (memaudit audit --ref auto). Entirely on your machine -- no phone-home, no account, no SaaS.
This tool produces evidence of resistance to the attacks it actually runs. It does not make you GDPR / AI Act / CNIL compliant.
Install
pip install memaudit # core
pip install "memaudit[peft,trl]" # LoRA + SFTTrainer lint (recommended for fine-tunes)
# from source:
git clone https://github.com/mem-audit/memaudit.git && cd memaudit
pip install -e ".[dev,peft,trl]"
Requires Python 3.10+ and transformers>=4.56.2 (works on 5.x).
30-second start
pip install memaudit
memaudit demo
Positive-control demo on a tiny overfit model -- proves the instrument can detect memorization and keep controls clean. Checked-in report: examples/demo-report.json.
Usage
from memaudit import generate_canaries, inject, MemorizationAuditCallback
canaries = generate_canaries(
tokenizer, n=32, n_controls=100, family="high_ppl",
repetitions=(1, 4, 16), seed=0,
)
train_ds, manifest = inject(train_ds, canaries, fmt="auto", seed=0)
trainer.add_callback(
MemorizationAuditCallback(
trainer=trainer, manifest=manifest, real_sample=64, ref="auto",
)
)
trainer.train() # writes <output_dir>/memaudit-report.json
Injection is a pre-train helper -- transformers builds the dataloader before on_train_begin, and TRL tokenizes inside SFTTrainer.__init__. The secret goes on the trainable side (completion / assistant turn / text body); prompt-side placement is refused.
Post-hoc (or after ZeRO-3 / FSDP, where in-callback scoring is deferred):
memaudit audit --model ./out --canary-set ./out/memaudit-manifest.json \
--dataset ./train.jsonl --ref auto
ref="auto" uses peft.disable_adapter() on an unmerged LoRA (one model copy). Full fine-tunes need --ref <base-checkpoint> or explicit --ref none.
What you get
Every run writes schema 1.2.0 JSON with both verdicts, negative controls, Clopper-Pearson CIs, provenance hashes, a shipped EDPB compliance_annex, and an explicit limitations statement.
| Verdict | Headline field | Notes |
|---|---|---|
| Membership | membership.tpr_at_1pct_fpr |
Base-calibrated Min-K%++ on the secret span; threshold from held-out canary controls (default 1% FPR). null when underpowered (headline_valid=false or audit_profile=smoke). |
| Regurgitation | regurgitation.overall.rate |
Prefix-prompted completion (exact / BLEU>0.75 / NED<=0.1). Counts are protocol-scoped -- not "no extraction risk". |
Membership and regurgitation routinely disagree. Scores are on the secret span only; full-sequence loss collapses detection.
Also in the report: membership.by_repetition (1x / 4x / 16x / pooled), membership.auc (secondary), canaries.requested_family / actual_generator, negative_controls, optional real_records.set_level (needs held_out=), compliance_annex, report_sha256 + sidecar, phone_home: false.
Pluggable membership scorers: docs/membership-scorers.md.
Measured results
Flagship: TinyLlama + Alpaca LoRA (powered)
Scale: TinyLlama-1.1B-Chat, 20,000 tatsu-lab/alpaca rows, LoRA r=8, 1 epoch, 100 inserted / 200 controls, 0.874% canary budget. Measured 2026-08-30 (Apple M3 Pro, MPS). Full write-up: docs/case-study-alpaca.md | live site.
| Run | Generator | TPR @ 1% FPR | 95% CI | AUC | Tiers (1x / 4x / 16x) | Regurg |
|---|---|---|---|---|---|---|
Headline (examples/alpaca-powered-report.json) |
requested high_ppl -> model_scored_high_ppl |
0.100 (10/100) | [0.049, 0.176] | 0.837 | 0/34 / 1/33 / 9/33 | 0/100 |
Appendix uniform_vocab (examples/alpaca-powered-report-v0.1-uniformvocab.json) |
requested high_ppl -> uniform_vocab fallback |
0.180 (18/100) | [0.110, 0.269] | 0.776 | 0/34 / 2/33 / 16/33 | 0/100 |
The pooled 10% headline is substantially a duplication/exposure stress signal (16x tier drives most detections), not a 10% probability for a single-exposure record. Regurgitation is under this prefix/decoding/exact-match protocol only.
Reproduce: pip install "memaudit[peft,trl]" && python examples/alpaca_case_study.py
Other measured runs
| Run | Scale | TPR @ 1% FPR | Notes |
|---|---|---|---|
memaudit demo |
TinyDemoLM positive control | 1.000 (16/16) | Instrument validation; ~99% token budget on purpose. examples/demo-report.json |
| distilgpt2 + LoRA | Pretrained, honest <=1% budget | 0.000 at 1% FPR | n=16 and n=100 rows; risky config AUC 0.848 with multi-seed edge signal. benchmarks/README.md |
| TRL SFTTrainer live | distilgpt2 + LoRA, completion_only_loss |
0.000 (0/16) | Integration path; preflight survival scan 16/16. benchmarks/README.md |
A 12-canary Alpaca first look (TPR 0.500, wide CI) is archived in examples/alpaca-case-study-report.json -- not a headline. See the case-study appendix.
Profiles & CLI
| Profile | Shape | TPR@FPR headline |
|---|---|---|
smoke |
cheap | refused |
routine |
32 + 100 controls (default) | yes (if >=100 controls) |
powered |
100 + 200 controls, reps {1,4,16} |
yes |
memaudit report out/memaudit-report.json # EDPB annex as markdown
memaudit verify out/memaudit-report.json # integrity check (content hash)
memaudit doctor --output-dir examples # env + demo + schema
memaudit audit ... --seeds 0,1,2 # audit-procedure stability block
memaudit audit ... --release-context public-api # para 46 release context
bash scripts/acceptance.sh # buyer acceptance gate
Public helper: from memaudit import inject (implementation: memaudit.injection).
Scope
| In scope | Out of scope |
|---|---|
| Membership inference (canary MIA, TPR @ 1% FPR + CI) | Model inversion / reconstruction |
| Prefix-prompted regurgitation | Attribute inference, PII discovery |
LoRA / PEFT pre-flight + disable_adapter() scoring |
Shadow-model LiRA, DP certificates |
Set-level signal on your records (with held_out=) |
Broad red-teaming |
| Family | Construction |
|---|---|
high_ppl (default) |
Rejection-sample from base model into a PPL band; falls back to uniform_vocab if no model/corpus (recorded in actual_generator) |
unigram / bigram |
Rare tokens under corpus n-grams; uniform fallback if no corpus |
structured |
CANARY-ID:... template |
random |
Uniform vocab draws (control twins) |
new_token |
Gated -- frozen-embedding LoRA cannot train new rows; no vocab resize |
Default canaries use the existing vocabulary -- memaudit never resizes the vocab. Pre-flight blocks silent false confidence (wrong placement, masked labels, empty inclusion coins, <100 controls for headline TPR). TPR@1%FPR is refused, not fabricated, when underpowered.
Architecture
generate_canaries() -> inject() -> MemorizationAuditCallback -> run_audit() / memaudit audit
Injection cannot live in the callback (dataloader + TRL tokenization happen first). ZeRO-3/FSDP defer in-callback scoring to the CLI. Ten implementation landmines (no callback injection, secret-span scoring, processing_class, two verdicts always) are encoded in source -- see module docstrings.
Limitations
- Small canary counts -> wide CIs. v0.1 defaults are a CPU-friendly start, not a regulatory sample size.
- Thresholds calibrate on this run's controls; they do not transfer across model families.
- Real-record ranking without
held_out=is descriptive only -- no FPR, not evidence about individuals. - Black-box final-model audits are structurally loose. A small TPR is not a privacy certificate.
- Multi-seed mode measures audit-procedure variance (bootstrap calibration + real-record sampling), not re-training variance.
- DPO / GRPO / Hub model-card push out of scope for v0.1. LoRA-aware, not LoRA-only.
Verified stack
| Piece | LoRA bench | Clean wheel |
|---|---|---|
| Python | 3.12.11 | 3.12.11 |
| torch | 2.7.1 | 2.13.0 |
| transformers | 4.56.2 | 5.16.1 |
| peft / trl | 0.20.0 / 0.29.1 | optional extras |
Known-bad: transformers 5.16.x + torch 2.6.dev hangs on FSDP imports. Recommended LoRA pin: transformers==4.56.2, torch>=2.5,<2.8, peft==0.20.0. Do not use --system-site-packages over a conda torch nightly.
Links
| PyPI | pypi.org/project/memaudit |
| Case study | docs/case-study-alpaca.md |
| Benchmarks | benchmarks/README.md |
| Scorer plug-in | docs/membership-scorers.md |
| Site | ansh200516.github.io/memaudit-site |
| Hugging Face | huggingface.co/memaudit |
| License | Apache-2.0 |
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file memaudit-0.2.0.tar.gz.
File metadata
- Download URL: memaudit-0.2.0.tar.gz
- Upload date:
- Size: 134.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9be302282b5bfa826776300a4e7b307d2e4d7716c4c4509e4bfca18db343a24
|
|
| MD5 |
b84073449a5c703d86b3cb05e937d3e4
|
|
| BLAKE2b-256 |
48fca67672a15ba0cc73318e2c5a6b1ca867baabe1d8f472dcaf690671119e3c
|
File details
Details for the file memaudit-0.2.0-py3-none-any.whl.
File metadata
- Download URL: memaudit-0.2.0-py3-none-any.whl
- Upload date:
- Size: 105.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9ead83128ea6fb8624d169e0d92cc9b829c5b74162529a3c9713801556b2d0d
|
|
| MD5 |
413373a6733c61a1a44dfd4d1508d35d
|
|
| BLAKE2b-256 |
748ba579522e8cca3c6c9d0629e67f2e0cc8af13c61035f18c9fadcf28968430
|