Add Laplace DpEvent
PiperOrigin-RevId: 432475405
This commit is contained in:
parent
2c65cc7910
commit
42df23eb79
2 changed files with 44 additions and 2 deletions
|
@ -59,7 +59,7 @@ incorrect results, the following should be enforced:
|
|||
is `False` when processing unknown mechanisms.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
import attr
|
||||
|
||||
|
@ -115,6 +115,20 @@ class GaussianDpEvent(DpEvent):
|
|||
noise_multiplier: float
|
||||
|
||||
|
||||
@attr.s(frozen=True, slots=True, auto_attribs=True)
|
||||
class LaplaceDpEvent(DpEvent):
|
||||
"""Represents an application of the Laplace mechanism.
|
||||
|
||||
For values v_i and noise z sampled coordinate-wise from the Laplace
|
||||
distribution L(0, s), this mechanism returns sum_i v_i + z.
|
||||
The probability density function of the Laplace distribution L(0, s) with
|
||||
parameter s is given as exp(-|x|/s) * (0.5/s) at x for any real value x.
|
||||
If the L_1 norm of the values are bounded ||v_i||_1 <= C, the noise_multiplier
|
||||
is defined as s / C.
|
||||
"""
|
||||
noise_multiplier: float
|
||||
|
||||
|
||||
@attr.s(frozen=True, slots=True, auto_attribs=True)
|
||||
class SelfComposedDpEvent(DpEvent):
|
||||
"""Represents repeated application of a mechanism.
|
||||
|
@ -176,3 +190,25 @@ class SampledWithoutReplacementDpEvent(DpEvent):
|
|||
source_dataset_size: int
|
||||
sample_size: int
|
||||
event: DpEvent
|
||||
|
||||
|
||||
@attr.s(frozen=True, slots=True, auto_attribs=True)
|
||||
class SingleEpochTreeAggregationDpEvent(DpEvent):
|
||||
"""Represents aggregation for a single epoch using one or more trees.
|
||||
|
||||
Multiple tree-aggregation steps can occur, but it is required that each
|
||||
record occurs at most once *across all trees*. See appendix D of
|
||||
"Practical and Private (Deep) Learning without Sampling or Shuffling"
|
||||
https://arxiv.org/abs/2103.00039.
|
||||
|
||||
To represent the common case where the same record can occur in multiple
|
||||
trees (but still at most once per tree), wrap this with `SelfComposedDpEvent`
|
||||
or `ComposedDpEvent` and use a scalar for `step_counts`.
|
||||
|
||||
Attributes:
|
||||
noise_multiplier: The ratio of the noise per node to the sensitivity.
|
||||
step_counts: The number of steps in each tree. May be a scalar for a single
|
||||
tree.
|
||||
"""
|
||||
noise_multiplier: float
|
||||
step_counts: Union[int, List[int]]
|
||||
|
|
|
@ -17,6 +17,7 @@ from tensorflow_privacy.privacy.analysis import dp_event
|
|||
from tensorflow_privacy.privacy.analysis import dp_event_builder
|
||||
|
||||
_gaussian_event = dp_event.GaussianDpEvent(1.0)
|
||||
_laplace_event = dp_event.LaplaceDpEvent(1.0)
|
||||
_poisson_event = dp_event.PoissonSampledDpEvent(_gaussian_event, 0.1)
|
||||
_self_composed_event = dp_event.SelfComposedDpEvent(_gaussian_event, 3)
|
||||
|
||||
|
@ -27,11 +28,16 @@ class DpEventBuilderTest(absltest.TestCase):
|
|||
builder = dp_event_builder.DpEventBuilder()
|
||||
self.assertEqual(dp_event.NoOpDpEvent(), builder.build())
|
||||
|
||||
def test_single(self):
|
||||
def test_single_gaussian(self):
|
||||
builder = dp_event_builder.DpEventBuilder()
|
||||
builder.compose(_gaussian_event)
|
||||
self.assertEqual(_gaussian_event, builder.build())
|
||||
|
||||
def test_single_laplace(self):
|
||||
builder = dp_event_builder.DpEventBuilder()
|
||||
builder.compose(_laplace_event)
|
||||
self.assertEqual(_laplace_event, builder.build())
|
||||
|
||||
def test_compose_no_op(self):
|
||||
builder = dp_event_builder.DpEventBuilder()
|
||||
builder.compose(dp_event.NoOpDpEvent())
|
||||
|
|
Loading…
Reference in a new issue