From a4bdb05b623a59de3b849e0031b61edbf5a305dc Mon Sep 17 00:00:00 2001 From: Zheng Xu Date: Mon, 12 Jun 2023 11:08:39 -0700 Subject: [PATCH] zCDP to epsilon for tree aggregation accounting. PiperOrigin-RevId: 539706770 --- tensorflow_privacy/privacy/analysis/BUILD | 1 + .../analysis/tree_aggregation_accountant.py | 26 ++++++++++++++++++- .../tree_aggregation_accountant_test.py | 13 ++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/tensorflow_privacy/privacy/analysis/BUILD b/tensorflow_privacy/privacy/analysis/BUILD index 3a38aff..65d891d 100644 --- a/tensorflow_privacy/privacy/analysis/BUILD +++ b/tensorflow_privacy/privacy/analysis/BUILD @@ -84,6 +84,7 @@ py_library( name = "tree_aggregation_accountant", srcs = ["tree_aggregation_accountant.py"], srcs_version = "PY3", + deps = ["@com_google_differential_py//dp_accounting:accounting"], ) py_test( diff --git a/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant.py b/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant.py index fa807e1..a7d9505 100644 --- a/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant.py +++ b/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant.py @@ -69,8 +69,9 @@ appearance of a same sample. For `target_delta`, the estimated epsilon is: import functools import math -from typing import Collection, Union +from typing import Collection, Optional, Union +import dp_accounting import numpy as np @@ -376,3 +377,26 @@ def compute_zcdp_single_tree( sum_sensitivity_square = _max_tree_sensitivity_square_sum( max_participation, min_separation, total_steps) return _compute_gaussian_zcdp(noise_multiplier, sum_sensitivity_square) + + +def _gaussian_zcdp_to_epsilon( + zcdp: float, + target_delta: float = 1e-10, + accountant: Optional[dp_accounting.PrivacyAccountant] = None, +) -> float: + """Transforms zCDP of Gaussian Mechanism to (epsilon, delta)-DP. + + Args: + zcdp: Input zCDP value. + target_delta: Specified target delta for (epsilon, delta)-DP. + accountant: Defaults to PLD accounting. Other options including RDP, i.e., + dp_accounting.rdp.RdpAccountant() + + Returns: + Epsilon under given delata for (epsilon, delta)-DP. + """ + if accountant is None: + accountant = dp_accounting.pld.PLDAccountant() + noise_multiplier = 1.0 / (zcdp * 2) ** 0.5 + accountant.compose(dp_accounting.GaussianDpEvent(noise_multiplier), 1) + return accountant.get_epsilon(target_delta) diff --git a/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant_test.py b/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant_test.py index 5dd7039..41e2875 100644 --- a/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant_test.py +++ b/tensorflow_privacy/privacy/analysis/tree_aggregation_accountant_test.py @@ -187,6 +187,19 @@ class TreeAggregationTest(tf.test.TestCase, parameterized.TestCase): tree_aggregation_accountant._compute_gaussian_zcdp( sigma, sum_sensitivity_square)) + def test_gaussian_zcdp_to_epsilon(self): + # The example below is reported in + # https://ai.googleblog.com/2022/02/federated-learning-with-formal.html + # Uses updated default RDP order (i.e., orders=None) can achieve better + # guarantees. Uses PLD accounting (dp_accounting.pld.PLDAccountant) can + # usually be tigher than RDP. + zcdp = 0.81 + orders = [1 + x / 10.0 for x in range(1, 100)] + list(range(12, 64)) + eps = tree_aggregation_accountant._gaussian_zcdp_to_epsilon( + zcdp, accountant=dp_accounting.rdp.RdpAccountant(orders) + ) + self.assertNear(eps, 8.92, err=0.01) + if __name__ == '__main__': tf.test.main()