示例#1
0
def _get_gan(with_dp=False):
    gan_loss_fns = gan_losses.get_gan_loss_fns('wasserstein')
    server_gen_optimizer = tf.keras.optimizers.Adam()
    client_disc_optimizer = tf.keras.optimizers.Adam()
    train_generator_fn = gan_training_tf_fns.create_train_generator_fn(
        gan_loss_fns, server_gen_optimizer)
    train_discriminator_fn = gan_training_tf_fns.create_train_discriminator_fn(
        gan_loss_fns, client_disc_optimizer)

    if with_dp:
        dp_average_query = tensorflow_privacy.QuantileAdaptiveClipAverageQuery(
            initial_l2_norm_clip=BEFORE_DP_L2_NORM_CLIP,
            noise_multiplier=0.3,
            target_unclipped_quantile=3,
            learning_rate=0.1,
            clipped_count_stddev=0.0,
            expected_num_records=10,
            denominator=10.0)
    else:
        dp_average_query = None

    return tff_gans.GanFnsAndTypes(
        generator_model_fn=one_dim_gan.create_generator,
        discriminator_model_fn=one_dim_gan.create_discriminator,
        dummy_gen_input=next(iter(one_dim_gan.create_generator_inputs())),
        dummy_real_data=next(iter(one_dim_gan.create_real_data())),
        train_generator_fn=train_generator_fn,
        train_discriminator_fn=train_discriminator_fn,
        server_disc_update_optimizer_fn=lambda: tf.keras.optimizers.SGD(lr=1.0
                                                                        ),
        train_discriminator_dp_average_query=dp_average_query)
示例#2
0
 def make_single_vector_query(vector_clip):
     """Makes a `DPQuery` for a single vector."""
     if not adaptive_clip_learning_rate:
         return tensorflow_privacy.GaussianAverageQuery(
             l2_norm_clip=vector_clip,
             sum_stddev=vector_clip * noise_multiplier * num_vectors**0.5,
             denominator=expected_total_weight)
     else:
         # Without geometric updating, the update is c = c - lr * loss, so for
         # multiple vectors we set the learning rate to be on the same scale as the
         # initial clip. That way big vectors get big updates, small vectors
         # small updates. With geometric updating, the update is
         # c = c * exp(-lr * loss) so the learning rate should be independent of
         # the initial clip.
         if geometric_clip_update:
             learning_rate = adaptive_clip_learning_rate
         else:
             learning_rate = adaptive_clip_learning_rate * vector_clip / clip
         return tensorflow_privacy.QuantileAdaptiveClipAverageQuery(
             initial_l2_norm_clip=vector_clip,
             noise_multiplier=noise_multiplier,
             target_unclipped_quantile=target_unclipped_quantile,
             learning_rate=learning_rate,
             clipped_count_stddev=clipped_count_stddev,
             expected_num_records=expected_num_clients,
             geometric_update=geometric_clip_update,
             denominator=expected_total_weight)
def _dp_factory(
    config: DifferentialPrivacyConfig
) -> dp_factory.DifferentiallyPrivateFactory:
    """Creates DifferentiallyPrivateFactory based on config settings."""
    if isinstance(config.clipping, FixedClippingConfig):
        stddev = config.clipping.clip * config.noise_multiplier
        query = tfp.GaussianAverageQuery(l2_norm_clip=config.clipping.clip,
                                         sum_stddev=stddev,
                                         denominator=config.clients_per_round)
    elif isinstance(config.clipping, AdaptiveClippingConfig):
        query = tfp.QuantileAdaptiveClipAverageQuery(
            initial_l2_norm_clip=config.clipping.initial_clip,
            noise_multiplier=config.noise_multiplier,
            denominator=config.clients_per_round,
            target_unclipped_quantile=config.clipping.target_quantile,
            learning_rate=config.clipping.learning_rate,
            clipped_count_stddev=config.clipped_count_stddev,
            expected_num_records=config.clients_per_round,
            geometric_update=True)
    else:
        raise TypeError(
            f'config.clipping is not a supported type of ClippingConfig. Found '
            f'type {type(config.clipping)}.')

    return dp_factory.DifferentiallyPrivateFactory(query)
示例#4
0
def _get_dp_average_query():
    return tensorflow_privacy.QuantileAdaptiveClipAverageQuery(
        initial_l2_norm_clip=100.0,
        noise_multiplier=0.3,
        target_unclipped_quantile=3,
        learning_rate=0.1,
        clipped_count_stddev=0.0,
        expected_num_records=10,
        denominator=10.0)
示例#5
0
 def make_single_vector_query(vector_clip):
   """Makes a `DPQuery` for a single vector."""
   if not adaptive_clip_learning_rate:
     return tensorflow_privacy.GaussianAverageQuery(
         l2_norm_clip=vector_clip,
         sum_stddev=vector_clip * noise_multiplier * num_vectors**0.5,
         denominator=expected_total_weight)
   else:
     return tensorflow_privacy.QuantileAdaptiveClipAverageQuery(
         initial_l2_norm_clip=vector_clip,
         noise_multiplier=noise_multiplier,
         target_unclipped_quantile=target_unclipped_quantile,
         learning_rate=adaptive_clip_learning_rate * vector_clip / clip,
         clipped_count_stddev=clipped_count_stddev,
         expected_num_records=expected_num_clients,
         denominator=expected_total_weight)
  def to_factory(self) -> dp_factory.DifferentiallyPrivateFactory:
    """Creates factory based on config settings."""
    if self._clipping.is_fixed:
      stddev = self._clipping.clip * self._noise_multiplier
      query = tfp.GaussianAverageQuery(
          l2_norm_clip=self._clipping.clip,
          sum_stddev=stddev,
          denominator=self._clients_per_round)
    else:
      query = tfp.QuantileAdaptiveClipAverageQuery(
          initial_l2_norm_clip=self._clipping.clip.initial_estimate,
          noise_multiplier=self._noise_multiplier,
          denominator=self._clients_per_round,
          target_unclipped_quantile=self._clipping.clip.target_quantile,
          learning_rate=self._clipping.clip.learning_rate,
          clipped_count_stddev=self._clipped_count_stddev,
          expected_num_records=self._clients_per_round,
          geometric_update=True)

    return dp_factory.DifferentiallyPrivateFactory(query)
示例#7
0
    def gaussian_adaptive(
        cls,
        noise_multiplier: float,
        clients_per_round: float,
        initial_l2_norm_clip: float = 0.1,
        target_unclipped_quantile: float = 0.5,
        learning_rate: float = 0.2,
        clipped_count_stddev: Optional[float] = None
    ) -> factory.UnweightedAggregationFactory:
        """`DifferentiallyPrivateFactory` with adaptive clipping and Gaussian noise.

    Performs adaptive clipping and addition of Gaussian noise for differentially
    private learning. For details of the DP algorithm see McMahan et. al (2017)
    https://arxiv.org/abs/1710.06963. The adaptive clipping uses the geometric
    method described in Thakkar et al. (2019) https://arxiv.org/abs/1905.03871.

    The adaptive clipping parameters have been chosen to yield a process that
    starts small and adapts relatively quickly to the median, without using
    much of the privacy budget. This works well on most problems.

    Args:
      noise_multiplier: A float specifying the noise multiplier for the Gaussian
        mechanism for model updates. A value of 1.0 or higher may be needed for
        strong privacy. See above mentioned papers to compute (epsilon, delta)
        privacy guarantee. Note that this is the effective total noise
        multiplier, accounting for the privacy loss due to adaptive clipping.
        The noise actually added to the aggregated values will be slightly
        higher.
      clients_per_round: A float specifying the expected number of clients per
        round. Must be positive.
      initial_l2_norm_clip: The initial value of the adaptive clipping norm.
      target_unclipped_quantile: The quantile to which the clipping norm should
        adapt.
      learning_rate: The learning rate for the adaptive clipping process.
      clipped_count_stddev: The stddev of the noise added to the clipped counts
        in the adaptive clipping algorithm. If None, defaults to `0.05 *
        clients_per_round`.

    Returns:
      A `DifferentiallyPrivateFactory` with adaptive clipping and Gaussian
        noise.
    """

        if isinstance(clients_per_round, int):
            clients_per_round = float(clients_per_round)

        _check_float_positive(noise_multiplier, 'noise_multiplier')
        _check_float_positive(clients_per_round, 'clients_per_round')
        _check_float_positive(initial_l2_norm_clip, 'initial_l2_norm_clip')
        _check_float_probability(target_unclipped_quantile,
                                 'target_unclipped_quantile')
        _check_float_nonnegative(learning_rate, 'learning_rate')

        if clipped_count_stddev is None:
            # Defaults to 0.05 * clients_per_round. The noised fraction of unclipped
            # updates will be within 0.1 of the true fraction with 95.4% probability,
            # and will be within 0.15 of the true fraction with 99.7% probability.
            # Even in this unlikely case, the error on the update would be a factor of
            # exp(0.2 * 0.15) = 1.03, a small deviation. So this default gives maximal
            # privacy for acceptable probability of deviation.
            clipped_count_stddev = 0.05 * clients_per_round
            if noise_multiplier >= 2 * clipped_count_stddev:
                raise ValueError(
                    f'Default value of `clipped_count_stddev` ({clipped_count_stddev}) '
                    f'is too low to achieve the desired effective noise multiplier '
                    f'({noise_multiplier}). You may increase `clients_per_round`, '
                    f'specify a larger value of `clipped_count_stddev`, or decrease '
                    f'`noise_multiplier`.')
        else:
            if noise_multiplier >= 2 * clipped_count_stddev:
                raise ValueError(
                    f'`clipped_count_stddev` ({clipped_count_stddev}) is too low to '
                    f'achieve the desired effective noise multiplier '
                    f'({noise_multiplier}). You must either increase '
                    f'`clipped_count_stddev` or decrease `noise_multiplier`.')

        _check_float_nonnegative(clipped_count_stddev, 'clipped_count_stddev')

        value_noise_multiplier = (noise_multiplier**-2 -
                                  (2 * clipped_count_stddev)**-2)**-0.5

        added_noise_factor = value_noise_multiplier / noise_multiplier
        if added_noise_factor >= 2:
            warnings.warn(
                f'A significant amount of noise ({added_noise_factor:.2f}x) has to '
                f'be added to achieve the desired effective noise multiplier '
                f'({noise_multiplier}). If you are manually specifying '
                f'`clipped_count_stddev` you may want to increase it. Or you may '
                f'need more `clients_per_round`.')

        query = tfp.QuantileAdaptiveClipAverageQuery(
            initial_l2_norm_clip=initial_l2_norm_clip,
            noise_multiplier=value_noise_multiplier,
            denominator=clients_per_round,
            target_unclipped_quantile=target_unclipped_quantile,
            learning_rate=learning_rate,
            clipped_count_stddev=clipped_count_stddev,
            expected_num_records=clients_per_round,
            geometric_update=True)

        return cls(query)
def build_dp_query(clip,
                   noise_multiplier,
                   expected_total_weight,
                   adaptive_clip_learning_rate=0,
                   target_unclipped_quantile=None,
                   clipped_count_budget_allocation=None,
                   expected_clients_per_round=None,
                   geometric_clip_update=True):
    """Makes a `DPQuery` to estimate vector averages with differential privacy.

  Supports many of the types of query available in tensorflow_privacy, including
  nested ("per-vector") queries as described in
  https://arxiv.org/pdf/1812.06210.pdf, and quantile-based adaptive clipping as
  described in https://arxiv.org/abs/1905.03871.

  Args:
    clip: The query's L2 norm bound, or the initial clip if adaptive clipping
      is used.
    noise_multiplier: The ratio of the (effective) noise stddev to the clip.
    expected_total_weight: The expected total weight of all clients, used as the
      denominator for the average computation.
    adaptive_clip_learning_rate: Learning rate for quantile-based adaptive
      clipping. If 0, fixed clipping is used.
    target_unclipped_quantile: Target unclipped quantile for adaptive clipping.
    clipped_count_budget_allocation: The fraction of privacy budget to use for
      estimating clipped counts.
    expected_clients_per_round: The expected number of clients for estimating
      clipped fractions.
    geometric_clip_update: If True, use geometric updating of the clip.

  Returns:
    A `DPQuery` suitable for use in a call to `build_dp_aggregate` and
    `build_dp_aggregate_process` to perform Federated Averaging with
    differential privacy.
  """
    py_typecheck.check_type(clip, numbers.Number, 'clip')
    py_typecheck.check_type(noise_multiplier, numbers.Number,
                            'noise_multiplier')
    py_typecheck.check_type(expected_total_weight, numbers.Number,
                            'expected_total_weight')

    if adaptive_clip_learning_rate:
        py_typecheck.check_type(adaptive_clip_learning_rate, numbers.Number,
                                'adaptive_clip_learning_rate')
        py_typecheck.check_type(target_unclipped_quantile, numbers.Number,
                                'target_unclipped_quantile')
        py_typecheck.check_type(clipped_count_budget_allocation,
                                numbers.Number,
                                'clipped_count_budget_allocation')
        py_typecheck.check_type(expected_clients_per_round, numbers.Number,
                                'expected_clients_per_round')
        p = clipped_count_budget_allocation
        nm = noise_multiplier
        vectors_noise_multiplier = nm * (1 - p)**(-0.5)
        clipped_count_noise_multiplier = nm * p**(-0.5)

        # Clipped count sensitivity is 0.5.
        clipped_count_stddev = 0.5 * clipped_count_noise_multiplier

        return tensorflow_privacy.QuantileAdaptiveClipAverageQuery(
            initial_l2_norm_clip=clip,
            noise_multiplier=vectors_noise_multiplier,
            target_unclipped_quantile=target_unclipped_quantile,
            learning_rate=adaptive_clip_learning_rate,
            clipped_count_stddev=clipped_count_stddev,
            expected_num_records=expected_clients_per_round,
            geometric_update=geometric_clip_update,
            denominator=expected_total_weight)
    else:
        if target_unclipped_quantile is not None:
            warnings.warn(
                'target_unclipped_quantile is specified but '
                'adaptive_clip_learning_rate is zero. No adaptive clipping will be '
                'performed. Use adaptive_clip_learning_rate > 0 if you want '
                'adaptive clipping.')
        if clipped_count_budget_allocation is not None:
            warnings.warn(
                'clipped_count_budget_allocation is specified but '
                'adaptive_clip_learning_rate is zero. No adaptive clipping will be '
                'performed. Use adaptive_clip_learning_rate > 0 if you want '
                'adaptive clipping.')
        return tensorflow_privacy.GaussianAverageQuery(
            l2_norm_clip=clip,
            sum_stddev=clip * noise_multiplier,
            denominator=expected_total_weight)
示例#9
0
    def gaussian_adaptive(cls,
                          noise_multiplier: float,
                          clients_per_round: float,
                          initial_l2_norm_clip: float = 0.1,
                          target_unclipped_quantile: float = 0.5,
                          learning_rate: float = 0.2,
                          clipped_count_stddev: Optional[float] = None):
        """`DifferentiallyPrivateFactory` with adaptive clipping and Gaussian noise.

    Performs adaptive clipping and addition of Gaussian noise for differentially
    private learning. For details of the DP algorithm see McMahan et. al (2017)
    https://arxiv.org/abs/1710.06963. The adaptive clipping uses the geometric
    method described in Thakkar et al. (2019) https://arxiv.org/abs/1905.03871.

    The adaptive clipping parameters have been chosen to yield a process that
    starts small and adapts relatively quickly to the median, without using
    much of the privacy budget. This works well on most problems.

    Args:
      noise_multiplier: A float specifying the noise multiplier for the Gaussian
        mechanism for model updates. May be zero for non-private learning with
        adaptive clipping. A value of 1.0 or higher may be needed for meaningful
        privacy. See above mentioned papers to compute (epsilon, delta) privacy
        guarantee.
      clients_per_round: A float specifying the expected number of clients per
        round. Must be positive.
      initial_l2_norm_clip: The initial value of the adaptive clipping norm.
      target_unclipped_quantile: The quantile to which the clipping norm should
        adapt.
      learning_rate: The learning rate for the adaptive clipping process.
      clipped_count_stddev: The stddev of the noise added to the clipped counts
        in the adaptive clipping algorithm. If None, defaults to `0.05 *
        clients_per_round`.

    Returns:
      A `DifferentiallyPrivateFactory` with adaptive clipping and Gaussian
        noise.
    """

        if isinstance(clients_per_round, int):
            clients_per_round = float(clients_per_round)

        _check_float_nonnegative(noise_multiplier, 'noise_multiplier')
        _check_float_positive(clients_per_round, 'clients_per_round')
        _check_float_positive(initial_l2_norm_clip, 'initial_l2_norm_clip')
        _check_float_probability(target_unclipped_quantile,
                                 'target_unclipped_quantile')
        _check_float_nonnegative(learning_rate, 'learning_rate')

        if clipped_count_stddev is None:
            # Defaults to 0.05 * clients_per_round. The noised fraction of unclipped
            # updates will be within 0.1 of the true fraction with 95.4% probability,
            # and will be within 0.15 of the true fraction with 99.7% probability.
            # Even in this unlikely case, the error on the update would be a factor of
            # exp(0.2 * 0.15) = 1.03, a small deviation. So this default gives maximal
            # privacy for acceptable probability of deviation.
            clipped_count_stddev = 0.05 * clients_per_round
        _check_float_nonnegative(clipped_count_stddev, 'clipped_count_stddev')

        query = tfp.QuantileAdaptiveClipAverageQuery(
            initial_l2_norm_clip=initial_l2_norm_clip,
            noise_multiplier=noise_multiplier,
            denominator=clients_per_round,
            target_unclipped_quantile=target_unclipped_quantile,
            learning_rate=learning_rate,
            clipped_count_stddev=clipped_count_stddev,
            expected_num_records=clients_per_round,
            geometric_update=True)

        return cls(query)