Skip to content

feat(text): row-conditioned synthetic text with DP methods, PII layer and leakage audit (T14) - #45

Open
nick-developer wants to merge 6 commits into
mainfrom
claude/project-thread-9wns9e
Open

nick-developer wants to merge 6 commits into
mainfrom
claude/project-thread-9wns9e

Conversation

@nick-developer

Copy link
Copy Markdown
Owner

Requested by Cod3r · project thread

What and why

Roadmap T14 (synthetic text, wave D). Free-text columns are now generated from their row (condition_on takes one or several columns) and a column can choose a method in columns.<c>.text:

  • ngram (default): the 1.0 model, byte-identical when none of the new keys is set.
  • ngram + dp: a user-level DP n-gram release (Kim et al. NeurIPS 2021): per-person n-gram counts per class, discrete Gaussian noise (OpenDP), partition-selection thresholds (Prop. 903). CPU only, no weights, air-gapped.
  • lm (+ dp): a local causal LM from a folder (local_files_only, never downloaded or bundled), LoRA through peft, user-level DP-GD on full batches (exact zCDP, ρ = T/2σ²). Optional extra relsynth[text-lm]; built-in stub byte model for offline tests.
  • pe: Private Evolution (Lin et al. 2024, Xie et al. 2024), always DP; a public generator proposes, private texts vote through noisy histograms in a data-independent hashed + random-projection embedding.

Also:

  • PII layer (text.pii): built-in regex recognizers (IBAN mod-97, Luhn, phone, date, URL, IP, address, names after cues) plus optional Presidio, spaCy, GLiNER (relsynth[text-pii]), typed placeholders before training, format-valid surrogates after generation. relsynth text pii-eval measures recall with Wilson intervals.
  • Leakage audit relsynth text audit: canary exposure against the DP bound, MinHash near-duplicate search with exact check, extraction attack. Metrics relsynth text metrics: MAUVE (hashed embedding), consistency with the row, TSTR, copy rates.
  • Licence register relsynth text models (src/relsynth/data/text_models.yaml, 19 models) and tools/check_text_models.py (compares with the hub).
  • Props. 1500–1507 (docs/proofs/sec_v50_text.tex), checks C1500–C1507 (tests/science/test_text_checks.py, shard 4/4), ADR 0010, guide docs/text.md, benchmark benchmarks/text/run_text.py (S6, S7, PolyAI BANKING77, CC BY 4.0, added to the dataset register; aggregates only).
  • New CI job text-lm: CPU PyTorch, downloads EleutherAI/pythia-14m (Apache-2.0) into the runner, runs the text tests and C1502 offline (HF_HUB_OFFLINE=1), the benchmark with the local model, GLiNER/Presidio recall (informational) and the licence check.

DP charges go to the existing DP-mode Accountant (no interface change). Text columns inside relsynth run --dp stay placeholders for now: RFC-T14-01 proposes the budget share and plan changes to T07/T08 (T07b is editing those files).

How it was tested

  • ruff check src tests tools benchmarks, bandit, SPDX check, mypy ratchet (0 new errors)
  • Unit and e2e: 648 passed locally before the last doc regeneration (the two generated-reference tests now pass too); tests/unit/test_textgen.py 16 pass without PyTorch, 17 with it
  • Byte identity with main (tools/identity_check.py, S1–S11 and quickstart, seeds 1 and 2): 24/24 identical
  • Statistical checks C1500–C1507: 8/8 pass (about 20 s); C00–C82 unchanged code paths, CI shards run them
  • Live servers / real-database regressions: no change to those paths (CI)

Benchmark (one seed; consistency ratio = classifier accuracy on synthetic ÷ on held-out real; exposure in bits, uniform ≈ 1.44, max 10.75):

Corpus Method Consistency Exact copies Mean canary exposure DP bound
S6 1.0 n-gram 0.999 0.57 7.94 (not DP)
S6 DP n-gram ε=4 0.986 0.008 1.76 holds
S7 DP n-gram ε=4 0.981 0.008 1.15 holds
BANKING77 1.0 n-gram 0.882 0.002 10.41 (not DP)
BANKING77 DP n-gram ε=8 0.478 0.001 1.61 holds
BANKING77 PE ε=4 (public split as API corpus) 0.968 0.041 n/a

Finding: the 1.0 text guards count rows, not people; one person with 4 copies of a sentence gets it reproduced (exposure 7.9–10.4 bits). text.dp fixes that.

Statistics and privacy

  • Each new claim has a numbered proposition (1500–1507), a proof in docs/proofs/sec_v50_text.tex and a check (the PDF is not rebuilt: no LaTeX in the sandbox)
  • Synthetic draws for the same seed are unchanged (identity check above)

Checklist

  • Conventional Commits, signed off
  • CHANGELOG.md under Unreleased
  • README (Synthetic text section, install extras, command table, limitations, docs map), docs/text.md, theory, testing, datasets, regenerated spec/CLI/settings references and IR schema
  • No data committed; BANKING77 is fetched at run time and pinned by SHA-256
  • New optional dependencies: torch (BSD-3-Clause), transformers, peft (Apache-2.0), presidio-analyzer, spacy (MIT), gliner (Apache-2.0); none in all

Report: roadmap/reports/T14.md in the project files.


Generated by Claude Code

… (wip)

Adds relsynth.textgen: DP n-gram release, local LM with user-level DP-GD,
Private Evolution, a PII pipeline with typed placeholders, the canary and
duplicate audits and text metrics, and wires extended text columns into the
engine while the 1.0 path stays unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSVqwJ1ZmupJ7qmQviUY7E
Signed-off-by: Claude <noreply@anthropic.com>
Adds the relsynth text command (models, pii-eval, audit, metrics), the
model licence register, Props. 1500-1507 with checks C1500-C1507, the
text benchmark (S6, S7, BANKING77), the text-lm CI job, ADR 0010 and the
guide docs/text.md. Out-of-vocabulary tokens now get a floor score in
the n-gram log-likelihood so canary ranking stays informative.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSVqwJ1ZmupJ7qmQviUY7E
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSVqwJ1ZmupJ7qmQviUY7E
Signed-off-by: Claude <noreply@anthropic.com>
…-9wns9e

# Conflicts:
#	CHANGELOG.md
#	README.md
@nick-developer nick-developer self-assigned this Nov 1, 2026
SQLAlchemy 2.1.8's stubs reject **dict[str, bool | None] for Column, which
failed the mypy ratchet on every PR (not caused by this PR's changes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSVqwFQpRaQG7JXjqkxnbn
Signed-off-by: Claude <noreply@anthropic.com>
transformers 5 keeps a checkpoint's dtype (float16 for Pythia); fine-tuning
in float16 on a CPU overflowed and generation failed with NaN probabilities
in the text-lm CI job. Local models are now cast to float32, non-DP
fine-tuning clips gradients and skips non-finite losses, a fitted model
with non-finite weights raises a clear error, and sampling drops invalid
logits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSVqwJ1ZmupJ7qmQviUY7E
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

The text-lm failure ("probability tensor contains inf, nan or element < 0") happened because transformers 5 keeps a checkpoint's dtype, and Pythia ships in float16. Fine-tuning in float16 on a CPU overflowed. d3d2021 casts local models to float32. It also clips gradients in non-DP fine-tuning, raises a clear error if the weights become non-finite, and drops invalid logits when sampling.

f35d982 cherry-picks the ddl.py typing fix from #44 (4e3026f), which clears the types failure that SQLAlchemy 2.1.8 causes on every PR.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

The proofs and bench failures on d3d2021 are runner problems, not caused by this PR:

  • proofs was cancelled during apt-get install of TeX Live. The Azure Ubuntu mirror was delivering packages slowly, and the job ran out of time before any LaTeX was built.
  • bench: every step succeeded (Chinook, Contoso and Berka all report "No regression"), but the job was marked failed when it completed 45 minutes later.

I'll re-run the failed jobs once the rest of the run has finished.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants