When You Can't Use Real Patient Data: Synthetic Tabular Data with Diffusion Models

When You Can't Use Real Patient Data: Synthetic Tabular Data with Diffusion Models

In clinical research, data is almost always the bottleneck. The real data exists, but it is locked behind IRB approvals, HIPAA compliance requirements, data use agreements, and legal review that can take months. Meanwhile, the ML team needs data to develop and validate models.

On a clinical research engagement between late 2024 and early 2026, we addressed this by generating high-fidelity synthetic tabular data using diffusion models. This post describes the approach, the constraints we worked within, and how we evaluated whether the synthetic data was actually useful.

"We can't share real data" is the wrong framing

The naive version of the problem is "we need synthetic data because we can't share real data." The actual constraint is sharper than that: synthetic data that's too faithful to the source can still leak patient information, through membership inference or attribute inference attacks. A synthetic dataset where every row is basically a slightly-perturbed real patient isn't private at all, whatever it's labeled as.

So the generation method has to hold two things at once: the synthetic data needs to preserve real feature distributions, correlations, and the conditional relationships downstream models depend on (fidelity), while not allowing reconstruction of or inference about any individual real patient (privacy). Push too hard on fidelity and you leak; push too hard on privacy and the synthetic data stops being useful for anything. Where the line sits depends entirely on the downstream use case and how sensitive the underlying data actually is. There's no universal answer here.

Why not just use a GAN

CTGAN and TVAE are the default answer for synthetic tabular data, and they work fine for simple datasets. Clinical data breaks them: it mixes continuous lab values with binary flags, ordinal severity scores, and categorical diagnoses in the same table, it has missingness patterns that carry real clinical meaning rather than being random noise, and it has strong conditional structure (a patient with condition A will almost always show lab value B in a specific range). GANs, trained as a min-max game between generator and discriminator, tend to struggle with exactly this kind of structure. Training gets unstable and the learned distribution drifts from the real conditional relationships.

A diffusion model's iterative denoising process learns the full joint distribution directly instead, which held up much better against clinical data's quirks in practice. We used a score-based diffusion model adapted for mixed-type tabular data, with separate noise schedules for continuous and categorical columns.

Missingness is a signal, not noise to clean up

The lesson that mattered most: don't impute missingness away before training. Whether a lab test was even ordered tells you something about the patient's condition. A model trained on pre-imputed data throws that signal out. We added a binary missingness indicator for every column with more than 5% missing values and trained the diffusion model to jointly generate the value and the missingness indicator together, so the not-missing-at-random structure survives into the synthetic data.

How do you know the synthetic data is actually good?

"It looks similar" isn't a metric, so we leaned on three checks that each catch a different failure mode. First, distributional fidelity: Kolmogorov-Smirnov tests per continuous feature, chi-squared for categoricals, and the Frobenius norm between real and synthetic correlation matrices to catch relationships a marginal-only check would miss. Second, train-on-synthetic-test-on-real: train a downstream classifier purely on synthetic data, evaluate it against a held-out real test set, and compare to the same classifier trained on real data. The AUC gap is the actual fidelity loss that matters. Ours had to land within 0.03 of the real-data AUC, and the confusion-matrix structure had to look similar too, not just the headline number. Third, a privacy audit: a membership-inference attack via a shadow model, which should land close to 0.5 (random chance) if the data is actually private, plus a nearest-neighbor distance ratio check: if synthetic records sit suspiciously close to real ones in feature space, the model is memorizing rather than generating.

None of this means anything to a clinical researcher

KS tests and NNDR scores mean nothing to clinical researchers or a compliance reviewer, and shouldn't need to. What they actually wanted to know boiled down to two questions: does the synthetic data lead to the same clinical conclusions as the real data, and can a patient be identified from it. So the evaluation got translated into two slides instead of a metrics table: one overlaying real and synthetic survival curves (nearly identical curves were what actually built trust, not a p-value), and one showing that the nearest synthetic record to any real patient stayed outside a defined clinical threshold on the variables that mattered. That second slide was the privacy assurance they needed, in a form they could actually evaluate themselves.

Their pushback on that framing fed back into which metrics got prioritized in the next iteration: the two-slide version wasn't just a communication layer bolted on after the fact, it changed what we measured.

Where this leaves things

Model training turned out to be the easy part. Getting missingness right, building an evaluation framework rigorous enough to trust, and getting non-technical stakeholders to actually believe the result took longer than the diffusion model itself did.