Skip to content

Stop PATE-GAN discriminator from training on real data - #360

Open
HEy-dot-R wants to merge 1 commit into
vanderschaarlab:mainfrom
HEy-dot-R:fix/pategan-real-data-leak
Open

HEy-dot-R wants to merge 1 commit into
vanderschaarlab:mainfrom
HEy-dot-R:fix/pategan-real-data-leak

Conversation

@HEy-dot-R

Copy link
Copy Markdown

Summary

PATE-GAN's discriminator (the "student") is trained directly on real data, which breaks the post-processing theorem the paper's privacy guarantee depends on. This holds regardless of the epsilon or lambda used.

  • Paper (Jordon, Yoon, van der Schaar, ICLR 2019): Eq. 7 defines the student's loss over generated samples only, labeled by the noisy teacher consensus. Fig. 4 and Section 4.2.2 state this explicitly: the student never sees real data, only generator output relabeled by PATE.
  • Code: gan.py's _train_epoch_discriminator forwards real_X through the discriminator with an unconditional real label (true_labels_generator defaults to all ones and PATE-GAN never overrides it), producing errD_real, which is backpropagated. The gradient penalty (_loss_gradient_penalty) separately interpolates real and fake samples, a second path for real data to reach the discriminator.

Since the discriminator the generator learns from is not purely a function of DP-protected labels, Theorem 1's proof does not apply to this implementation as written.

Fix

Add an opt-in discriminator_use_real_data flag to GAN (default True, preserves current behavior everywhere else). When False:

  • _train_epoch_discriminator skips the real-data forward pass and the gradient penalty.
  • errD_real/errD_fake are computed only from generator output, split by its teacher label, matching Eq. 7.

TabularGAN forwards the new parameter to GAN. PATEGAN.fit() passes
discriminator_use_real_data=False.

Confirmed via grep that CTGANPlugin, AdsGANPlugin, and DPGANPlugin are the only other consumers of TabularGAN/GAN, and none pass this parameter, so their behavior is unchanged.

Scope

This PR fixes only this issue. A separate audit of the same plugin found three smaller privacy accounting bugs (an exponent typo in the moments accountant, teacher queries that skip the accountant charge under one condition, and a noise scale that does not match how it is used elsewhere in the accountant). Those are not included here and can be filed separately if useful.

Test plan

  • Fit and generate with pategan on a small synthetic dataframe, discriminator_use_real_data confirmed False on the resulting model, fit and generate both succeed.
  • Fit and generate with ctgan the same way, discriminator_use_real_data confirmed True (default, unchanged), fit and generate both succeed.
  • Added a guard for the case where an entire batch gets the same teacher label (empty bucket would otherwise produce NaN from torch.mean of an empty tensor); confirmed this path is exercised and handled correctly on a real 1367-row dataset with 24 continuous features.

Disclaimer

The issue was identified by me while going through the PATE-GAN code for its implementation in my own project and the fix was created in partnership with Claude and the documentation and its implementation into my fork was done by Claude single handedly.

PATE-GAN's DP guarantee (Jordon et al., ICLR 2019) relies on the
post-processing theorem: the discriminator/student must be a function
only of privacy-protected teacher labels, per Eq. 7 and Fig. 4. The
student is defined only over generated samples labeled by the noisy
teacher consensus, never over real data.

_train_epoch_discriminator forwards real training rows through the
discriminator with an unconditional real label, and the gradient
penalty separately interpolates real and fake samples. Both paths let
real data influence the discriminator, and therefore the generator,
with no privacy protection on that path. This holds regardless of the
epsilon or lamda used.

Add an opt-in discriminator_use_real_data flag (default True, so
CTGAN, AdsGAN, and DPGAN are unaffected) that, when False, trains the
discriminator only on generator output split by its teacher label, and
skips the gradient penalty. PATEGAN.fit() now passes False.
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.

1 participant