Skip to content

Commit

Permalink
Add support for PLD Accountant in computing DP-SGD privacy statement …
Browse files Browse the repository at this point in the history
…[TF Privacy]

PiperOrigin-RevId: 529541173
  • Loading branch information
pritkamath authored and tensorflower-gardener committed Dec 3, 2023
1 parent f51b637 commit f69d0fc
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 31 deletions.
16 changes: 10 additions & 6 deletions tensorflow_privacy/privacy/analysis/compute_dp_sgd_privacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@
Sampled Gaussian Mechanism. The mechanism's parameters are controlled by flags.
Example:
compute_dp_sgd_privacy
compute_dp_sgd_privacy \
--N=60000 \
--batch_size=256 \
--noise_multiplier=1.12 \
--epochs=60 \
--delta=1e-5
--delta=1e-5 \
--accountant_type=RDP
The output states that DP-SGD with these parameters satisfies (2.92, 1e-5)-DP.
Prints out the privacy statement corresponding to the above parameters.
"""

from absl import app
from absl import flags

from tensorflow_privacy.privacy.analysis.compute_dp_sgd_privacy_lib import compute_dp_sgd_privacy_statement
from tensorflow_privacy.privacy.analysis import compute_dp_sgd_privacy_lib


_NUM_EXAMPLES = flags.DEFINE_integer(
Expand Down Expand Up @@ -70,21 +70,25 @@
'user-level DP guarantee.'
),
)
_ACCOUNTANT_TYPE = flags.DEFINE_enum(
'accountant_type', 'RDP', ['RDP', 'PLD'], 'DP accountant to use.'
)

flags.mark_flags_as_required(['N', 'batch_size', 'noise_multiplier', 'epochs'])


def main(argv):
del argv # argv is not used.

statement = compute_dp_sgd_privacy_statement(
statement = compute_dp_sgd_privacy_lib.compute_dp_sgd_privacy_statement(
_NUM_EXAMPLES.value,
_BATCH_SIZE.value,
_NUM_EPOCHS.value,
_NOISE_MULTIPLIER.value,
_DELTA.value,
_USED_MICROBATCHING.value,
_MAX_EXAMPLES_PER_USER.value,
compute_dp_sgd_privacy_lib.AccountantType(_ACCOUNTANT_TYPE.value),
)
print(statement)

Expand Down
77 changes: 62 additions & 15 deletions tensorflow_privacy/privacy/analysis/compute_dp_sgd_privacy_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# ==============================================================================
"""Library for computing privacy values for DP-SGD."""

import enum
import functools
import math
import textwrap
Expand All @@ -34,13 +35,36 @@ def _logexpm1(x: float) -> float:
return x + math.log(-math.expm1(-x))


class AccountantType(enum.Enum):
"""Accountant to use for privacy accounting."""

RDP = 'RDP'
PLD = 'PLD'

def get_accountant(self) -> dp_accounting.PrivacyAccountant:
if self == AccountantType.RDP:
rdp_orders = (
[1 + x / 10.0 for x in range(1, 100)]
+ list(range(11, 64))
+ [128, 256, 512, 1024]
)
return dp_accounting.rdp.RdpAccountant(rdp_orders)
if self == AccountantType.PLD:
pld_discretization = 1e-4
return dp_accounting.pld.PLDAccountant(
value_discretization_interval=pld_discretization
)
raise ValueError(f'Unsupported Accountant type {self}')


def _compute_dp_sgd_user_privacy(
num_epochs: float,
noise_multiplier: float,
user_delta: float,
max_examples_per_user: int,
used_microbatching: bool = True,
poisson_subsampling_probability: Optional[float] = None,
accountant_type: AccountantType = AccountantType.RDP,
) -> float:
"""Computes add-or-remove-one-user DP epsilon using group privacy.
Expand All @@ -63,6 +87,7 @@ def _compute_dp_sgd_user_privacy(
used_microbatching: If true, increases sensitivity by a factor of two.
poisson_subsampling_probability: If not None, gives the probability that
each record is chosen in a batch. If None, assumes no subsampling.
accountant_type: The privacy accountant for computing epsilon.
Returns:
The add-or-remove-one-user DP epsilon value using group privacy.
Expand Down Expand Up @@ -92,6 +117,7 @@ def _compute_dp_sgd_user_privacy(
user_delta,
used_microbatching,
poisson_subsampling_probability,
accountant_type,
)

# The computation below to estimate user_eps works as follows.
Expand Down Expand Up @@ -188,6 +214,7 @@ def _compute_dp_sgd_example_privacy(
example_delta: float,
used_microbatching: bool = True,
poisson_subsampling_probability: Optional[float] = None,
accountant_type: AccountantType = AccountantType.RDP,
) -> float:
"""Computes add-or-remove-one-example DP epsilon.
Expand All @@ -201,6 +228,7 @@ def _compute_dp_sgd_example_privacy(
used_microbatching: If true, increases sensitivity by a factor of two.
poisson_subsampling_probability: If not None, gives the probability that
each record is chosen in a batch. If None, assumes no subsampling.
accountant_type: The privacy accountant for computing epsilon.
Returns:
The epsilon value.
Expand Down Expand Up @@ -229,10 +257,10 @@ def _compute_dp_sgd_example_privacy(
event_ = dp_accounting.SelfComposedDpEvent(count=count, event=event_)

return (
dp_accounting.rdp.RdpAccountant()
accountant_type.get_accountant()
.compose(event_)
.get_epsilon(example_delta)
) # TODO(b/271341062)
)


def compute_dp_sgd_privacy_statement(
Expand All @@ -243,6 +271,7 @@ def compute_dp_sgd_privacy_statement(
delta: float,
used_microbatching: bool = True,
max_examples_per_user: Optional[int] = None,
accountant_type: AccountantType = AccountantType.RDP,
) -> str:
"""Produces a privacy report summarizing the DP guarantee.
Expand All @@ -267,6 +296,7 @@ def compute_dp_sgd_privacy_statement(
max_examples_per_user: If the data set is constructed to cap the maximum
number of examples each user contributes, provide this argument to also
print a user-level DP guarantee.
accountant_type: The privacy accountant for computing epsilon.
Returns:
A str precisely articulating the privacy guarantee.
Expand Down Expand Up @@ -296,19 +326,24 @@ def compute_dp_sgd_privacy_statement(
paragraph = textwrap.fill(
f"""\
Example-level DP with add-or-remove-one adjacency at delta = {delta} computed \
with RDP accounting:""",
with {accountant_type.value} accounting:""",
width=80,
)

example_eps_no_subsampling = _compute_dp_sgd_example_privacy(
num_epochs, noise_multiplier, delta, used_microbatching
num_epochs,
noise_multiplier,
delta,
used_microbatching,
accountant_type=accountant_type,
)
example_eps_subsampling = _compute_dp_sgd_example_privacy(
num_epochs,
noise_multiplier,
delta,
used_microbatching,
poisson_subsampling_probability=batch_size / number_of_examples,
accountant_type=accountant_type,
)

paragraph += f"""
Expand All @@ -320,13 +355,33 @@ def compute_dp_sgd_privacy_statement(
paragraphs.append(paragraph)

inf_user_eps = False
if max_examples_per_user is not None:
if max_examples_per_user is None:
paragraphs.append(
textwrap.fill(
"""\
No user-level privacy guarantee is possible without a bound on the number of \
examples per user.""",
width=80,
)
)
elif accountant_type == AccountantType.PLD:
# TODO(b/271341062): Add User level DP support for PLD.
paragraphs.append(
textwrap.fill(
"""\
User-level DP epsilon computation is not supported for PLD accounting at this \
time. Use RDP accounting to obtain user-level DP guarantees.""",
width=80,
)
)
else: # Case: max_examples_per_user is not None and accountant_type is RDP
user_eps_no_subsampling = _compute_dp_sgd_user_privacy(
num_epochs,
noise_multiplier,
delta,
max_examples_per_user,
used_microbatching,
accountant_type=accountant_type,
)
user_eps_subsampling = _compute_dp_sgd_user_privacy(
num_epochs,
Expand All @@ -335,6 +390,7 @@ def compute_dp_sgd_privacy_statement(
max_examples_per_user,
used_microbatching,
poisson_subsampling_probability=batch_size / number_of_examples,
accountant_type=accountant_type,
)
if math.isinf(user_eps_no_subsampling):
user_eps_no_subsampling_str = ' inf (**)'
Expand All @@ -350,7 +406,7 @@ def compute_dp_sgd_privacy_statement(
paragraph = textwrap.fill(
f"""\
User-level DP with add-or-remove-one adjacency at delta = {delta} computed \
using RDP accounting and group privacy:""",
using {accountant_type.value} accounting and group privacy:""",
width=80,
)
paragraph += f"""
Expand All @@ -360,15 +416,6 @@ def compute_dp_sgd_privacy_statement(
{user_eps_subsampling_str}"""

paragraphs.append(paragraph)
else:
paragraphs.append(
textwrap.fill(
"""\
No user-level privacy guarantee is possible without a bound on the number of \
examples per user.""",
width=80,
)
)

paragraphs.append(
textwrap.fill(
Expand Down
Loading

0 comments on commit f69d0fc

Please sign in to comment.