Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

synthpriv

Tabular data synthesis preserving differential privacy: generators (dp-gan with DP-SGD, dp-copula with pure DP), DP marginal ECDFs, benchmark and budget sweep, and an HTML privacy/utility report. Built on SDV, SDMetrics and Opacus for differential privacy (DP-SGD) with an RDP accountant.

The project evolves in cumulative phases, each with its own test suite and commit.

Implemented phases

Phase Commit What it adds
0 ebb3700 Core: generator registry, base classes, pipeline, HTML report, CLI
1 ebb3700 SDV tabular generators (ctgan, tvae, copula-gan, gaussian-copula)
2 ebb3700 Real DP: dp-gan with DP-SGD + RDP accountant and measured epsilon
3 6d21828 dp-gan utility: AC-GAN conditioning (CTGAN-style), minority class coverage
4 1dc5108 epsilon ↔ utility sweep (privacy/utility curve)
5 2c2b05e Serialization: save_model / load_model (regenerate without retraining)
6 603445f assert_dp: DP step integrity and epsilon budget validation
7 90b485f Docs + reproducible end-to-end demo (README, examples/demo.py)
8 e979fb7 dp-gan benchmark vs non-DP SDV baselines (utility curves + gap)
9 6001540 dp-gan utility hardening: numeric="uniform" (gaussianization + interpolated quantile), rectify_marginals (guaranteed KS), label_smoothing; documented diagnosis and limits
10 0194a23 DPEcdf: marginal ECDF with Laplace noise (epsilon per column, parallel composition by bins); ecdf_epsilon with sequential composition with training (total_epsilon in the report)
11 6458cfa dp-copula: private Gaussian copula (pure DP, δ=0) — captures dependence, the dp-gan weak spot
12 d1296ad split_budget: split DP budget between training and marginals
— 84f4fef Fix: HTML report without DP-ECDF (ecdf_epsilon/total_epsilon keys always present)
— b939af3 Build: PyPI publication prep (MIT license, PEP 639/URIs/classifiers, dev extras build+twine, README intro)

Installation

python -m venv .venv
.venv/bin/pip install -e ".[dev]"   # (uses CUDA if available)

Dependencies: Python ≥ 3.10, numpy, pandas, scipy, scikit-learn, SDV < 2, SDMetrics, anonymeter, Opacus, click, Jinja2.

Quick start

Python API

from synthpriv import PrivacyPreservingSynthesizer
from synthpriv.privacy.mechanisms import DPSGD

privacy = DPSGD(epsilon=8.0, delta=1e-5)
synth = PrivacyPreservingSynthesizer(
    generator_key="dp-gan",
    generator_kwargs={"epochs": 100, "privacy": privacy, "random_state": 0},
    privacy_mechanism=privacy,
    utility_metrics=["ks_test", "correlation_mae", "ml_utility"],
    privacy_metrics=["nndr", "mia_auc"],
)
synthetic = synth.generate(real_df, num_rows=5000)   # fit + sample
report = synth.evaluate(real_df, synthetic)
report.save("report.html")                          # self-contained HTML report

# persist and regenerate without retraining
synth.save_model("demo_model.sz")
loaded = PrivacyPreservingSynthesizer.load_model("demo_model.sz")
loaded.sample(5000).to_csv("resample.csv", index=False)

# validate the DP guarantee
assurance = loaded.assert_dp()
print(assurance.status, assurance.message)

Note: do not pass random_state in generator_kwargs when the synthesizer already receives it in the constructor (conflict with the constructor's one).

CLI

# generate with a DP guarantee
synthpriv generate --data real.csv --epsilon 8 --rows 5000 --save demo_model.sz -o synthetic.csv

# regenerate without retraining (same accounted epsilon)
synthpriv sample --model demo_model.sz --rows 5000 -o resample.csv

# evaluate utility and privacy
synthpriv evaluate --real real.csv --synthetic synthetic.csv --epsilon 8 -o report.html

# epsilon <-> utility sweep (dp-gan or dp-copula)
synthpriv sweep --data real.csv --generator dp-copula --epsilons "0.1,0.5,1,2,5,50" -o sweep_report.html

# benchmark DP generator vs non-DP SDV generators (curves + utility gap)
synthpriv benchmark --data real.csv --generator dp-copula --epsilons "1,5,50" --baselines gaussian-copula -o bench.html

# audit that a persisted model's DP guarantee is not exceeded
synthpriv dpcheck --model demo_model.sz --tolerance 0.05

Reproducible demo

.venv/bin/python examples/demo.py [epsilon] [epochs] [ecdf_epsilon]

Trains dp-gan on a sample dataset, generates, evaluates, persists/reloads, regenerates and runs assert_dp. Artifacts under /tmp/synthpriv_demo/.

Differential privacy mechanics

  • Only the discriminator trains with DP-SGD (gradient clipping + noise, Poisson sampling). The generator is post-processing of the discriminator, so the result is DP with the epsilon accounted by Opacus's RDP accountant.

  • The real accumulated epsilon (accountant.get_epsilon()) is what is reported, not the target: it depends on sample size, epochs and resulting noise.

  • assert_dp checks two conditions on a model (persisted or in memory):

    1. Step integrity: the DP steps accounted by the accountant == those executed by the discriminator during training.
    2. Budget: measured epsilon ≤ declared epsilon × (1 + tolerance).

    If either fails, the state is fail and the RDP guarantee is in question.

Mechanisms

  • NoPrivacy() — no formal guarantee (only empirical mitigation + risk metrics).
  • DPSGD(epsilon=1.0, delta=1e-5) — DP-SGD (Opacus) for the dp-gan generator; it also configures the total budget for the dp-copula generator (pure DP, δ=0). noise_multiplier: if set, that noise is used; otherwise Opacus computes it to reach the budget. After training, used_noise_multiplier holds the applied value.

Generators

Key Description DP
gaussian-copula Gaussian copula (fast, deterministic) no
ctgan Tabular GAN (SDV) no
tvae Tabular variational autoencoder (SDV) no
copula-gan GAN with copula normalization (SDV) no
dp-gan Custom MLP GAN with DP-SGD and AC-GAN conditioning yes
dp-copula Private Gaussian copula: Laplace ECDF + private correlation (fast) yes

dp-copula

Parametric model that attacks the dp-gan weak spot (the dependence structure) with pure DP (δ=0, all sub-mechanisms are Laplace). The total budget is split into margins_fraction (≈ default 0.4, private ECDFs per column), corr_fraction (≈ default 0.4, per-entry Laplace-perturbed correlation matrix projected to PSD) and the rest into categorical frequencies. On purely numeric datasets the whole remainder goes to the copula. Benchmark on correlated data: learned correlation ≈0.61 vs 0.60 real (dp-gan ≈0.72-0.83) at ε=1-5. In the CLI: synthpriv generate --method dp-copula --epsilon E.

dp-gan

Only the discriminator sees real data and trains with DP-SGD. Relevant hyperparameters:

  • privacy — DPSGD mechanism with target ε/δ.
  • num_modes (3) — ModeEncoder Gaussian Mixture per numeric column; 1 = z-score.
  • numeric ("mode") — numeric encoding: "mode" (mode-specific, mix of Gaussians) or "uniform" (gaussianized percentile range Φ⁻¹(rank) + empirical quantile). "uniform" handles tails much better and is the recommended option when the marginal matters; its inverse interpolates between quantiles (does not return exact real values).
  • rectify_marginals (False) — only with numeric="uniform": rectifies at sample time the continuous marginals to the real ECDF (monotone per-column transformation, preserves the copula). Guarantees KS ≈ 1 for the numerics; it shares the per-column empirical-quantile trade-off (utility vs. leakage) documented in Limitations.
  • condition_column (None) — column that conditions generation; None picks the most imbalanced one (lowest entropy) so minority classes do not collapse.
  • aux_lambda (1.0) — weight of auxiliary losses (classifier + class consistency).
  • generator_steps (2) — generator steps per discriminator step (the DP budget only counts the discriminator).
  • label_smoothing (0.0) — discriminator label smoothing; useful for stability.
  • ecdf_epsilon (None) — DP budget for the marginals (only with numeric="uniform"): builds a private ECDF per column (Laplace histogram, ε per column = ecdf_epsilon / #numeric, parallel composition by bins). The synthesizer's total guarantee is the sequential composition epsilon(accumulated) + ecdf_epsilon (additive and exact: the ECDF is pure DP, δ=0), exposed in the report as accountant.ecdf_epsilon / accountant.total_epsilon. With None the ECDF is the raw empirical one (no formal guarantee on the marginal). CLI: synthpriv generate --epsilon E --ecdf-epsilon EE.
  • ecdf_bounds (None) — public support (min, max) of the private ECDFs. With bounds the guarantee is strictly pure DP (fixed grid, no data-derived range); without them the support is derived from the 0.001/0.999 quantiles (with margin) and a warning documents that nuance.
  • Budget split — from synthpriv import split_budget; b = split_budget(total=10.0, margins_fraction=0.3) returns b.train (for epsilon) and b.margins (for ecdf_epsilon), with b.total = train + margins. The dp-gan total is exactly additive.

Hardening phase results (tabular dataset with real correlations, 1500 rows, numeric="uniform" + rectify_marginals): KS ≈ 1.0 on all three numerics for ε = 1/5/25 and ml_utility on par with SDV's Gaussian copula (≈0.35). The copula still wins on correlations (corrMAE ≈0.02 vs ≈0.2–0.37): the dependence structure is what an MLP DP-GAN learns least on small datasets (see Limitations).

Metrics

  • Utility: KS (distributions), correlation MAE, ML utility (TSTR).
  • Privacy: NNDR (distance to nearest real neighbor), inference attack AUC (MIA) and anonymeter attacks.

Tests

.venv/bin/python -m pytest -q              # fast (default)
.venv/bin/python -m pytest -m slow -q      # trains deep models
  • test_registry, test_generators, test_metrics, test_pipeline, test_cli
  • test_dp (end-to-end DP + minority class coverage)
  • test_dp_matrix (dp-copula in sweep/benchmark: model/kind/measured epsilon)
  • test_sweep (ε-utility sweep, dp-gan/dp-copula)
  • test_benchmark (DP vs baselines: structure, gaps, reports)
  • test_serialization (persistence/reload)
  • test_assurance (DP step integrity and budget)

Limitations

  • dp-gan is an MLP GAN: for small numeric/categorical datasets, not for images or sequences.
  • The DP guarantee relies on Opacus's RDP accountant and Poisson sampling; formal audit with dedicated libraries is out of scope.
  • assert_dp validates the integrity of the training DP-SGD (steps + budget). If you also use ecdf_epsilon, the synthesizer's total guarantee is the composition epsilon(accumulated) + ecdf_epsilon (additive and exact: the ECDF is pure DP, δ=0); the report and the assert_dp message show it.
  • With ecdf_epsilon, the marginal budget is split equally across columns and the support of each ECDF is trimmed to the empirical 0.001/0.999 quantiles (+margin): exact extremes are neither emitted nor published, at the cost of a slight generated-range trim.
  • Although dp-gan respects the budget, real utility at low ε depends on the dataset (see the synthpriv sweep sweep).
  • The dependence structure (correlations) is the dp-gan weak spot: partially learned and noisy on small datasets; there copulas (SDV) are more accurate.
  • With numeric="uniform" the marginal is tied to the real empirical quantiles (interpolated), which gives strong utility but shares per-column information; use it with rectify_marginals=True only when marginal utility is the priority over that consideration.

Privacy note

Synthetic data without DP is not guaranteed anonymization. The report suggests which ε level to use and which empirical risk is measured, but formal protection only comes from training with a DP mechanism (DPSGD + dp-gan/dp-copula) and verifying it with assert_dp.

Metadata

Release files for synthpriv 0.2.0b1

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

Source distribution (sdist)

Source distribution for synthpriv 0.2.0b1
File Size Uploaded
synthpriv-0.2.0b1.tar.gz 67.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for synthpriv 0.2.0b1
File Interpreter ABI Platform
synthpriv-0.2.0b1-py3-none-any.whl Python 3 none any Details

Total release size: 129.5 kB

Release files / synthpriv-0.2.0b1.tar.gz

Download URL synthpriv-0.2.0b1.tar.gz
Size 67.4 kB
Tags Source
SHA-256 checksum
How to use checksums
27d947ffe0e7d63ad575e29f1cb30822be200a2081e9649222c71d4f659b7901
BLAKE2b-256 checksum
How to use checksums
92bdcc807420d8e079e672cae2714ec7cffc69ef5fdd60b940c5d8ed9bb75895
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / synthpriv-0.2.0b1-py3-none-any.whl

Download URL synthpriv-0.2.0b1-py3-none-any.whl
Size 62.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
668c7164fb6409fb5a3a36c487c745715b9d84cf9cd6159fafd771019fecd514
BLAKE2b-256 checksum
How to use checksums
a0a76256f32c36305afc425e286e99f618ee818fdc33c04385964a2aa4b98fed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.2.1

2 release files

0.2.0

2 release files

This release

0.2.0b1 This release

2 release files

0.1.0

2 release files

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