class_docstr: Class for generating adversarial neighbors based on gradient-based methods.
  
  The core of this class implements the projected gradient descent (PGD)
  operation:
  ```
  adv_neighbor = input_features
  iterations = 10  # Number of iterations to run PGD.
  for _ in range(iterations):
    grad = gradient(adv_neighbor)
    adv_neighbor = adv_neighbor + adv_step_size * grad
    adv_neighbor = project(adv_neighbor)
  ```
  where `adv_step_size` is the step size (analogous to learning rate) for
  searching/calculating adversarial neighbor, `gradient(x)` calculates the
  gradient of the model at `x`, and `project(v)` projects the vector `v` onto
  the epsilon ball.
  
  Attributes:
    labeled_loss: a scalar (`tf.float32`) tensor calculated from true labels (or
      supervisions).
    adv_config: a `nsl.configs.AdvNeighborConfig` object.
    raise_invalid_gradient: (optional) a Boolean flag indicating whether to
      raise an error when gradients cannot be computed on some input features.
      There are three cases where gradients cannot be computed:  (1) The feature
        is a `tf.SparseTensor`. (2) The feature is in a non-differentiable
        `tf.DType`, like string or integer. (3) The feature is not involved in
        loss computation.  If set to False, those input without gradient will be
        ignored silently and not perturbed. (default=False)
    pgd_model_fn: the model function. Takes in the input_features and produces a
      prediction. This is required for PGD with more than one step.
    pgd_loss_fn: the loss function. Calculates loss between prediction and
      ground truth.
    regularizer_fn: regularization function for pgd_loss_fn.
      This is optional for PGD. It should be a function that accepts:
        1. perturbations: a dense (float32) tensor, a list of dense tensors,
          or a dictionary of feature names and dense tensors. The shape should
          be the same as input_features. This is the perturbation at each step
          of PGD.
        2. keyed_grads_x: a dense (float32) tensor, a list of dense tensors,
          or a dictionary of feature names and dense tensors. The shape should
          be the same as input_features. This is the gradient of the labeled
          loss with respect to input_features.
      The output of this function is added to pgd_loss_fn during PGD steps.
      The default when it is not given is None.
    use_while_loop: A Boolean indicating whether the PGD steps should be done in
      a `tf.while_loop`. This can potentially reduce memory footprint, but is
      not compatible to `pgd_model_fn` with side effects. (default=False)
class_docstr_tok: ["Class","for","generating","adversarial","neighbors","based","on","gradient-based","methods",".","The","core","of","this","class","implements","the","projected","gradient","descent","(","PGD",")","operation",":","``","`","adv_neighbor","=","input_features","iterations","=","10","#","Number","of","iterations","to","run","PGD",".","for","_","in","range","(","iterations",")",":","grad","=","gradient","(","adv_neighbor",")","adv_neighbor","=","adv_neighbor","+","adv_step_size","*","grad","adv_neighbor","=","project","(","adv_neighbor",")","``","`","where","`","adv_step_size","`","is","the","step","size","(","analogous","to","learning","rate",")","for","searching\/calculating","adversarial","neighbor",",","`","gradient","(","x",")","`","calculates","the","gradient","of","the","model","at","`","x","`",",","and","`","project","(","v",")","`","projects","the","vector","`","v","`","onto","the","epsilon","ball",".","Attributes",":","labeled_loss",":","a","scalar","(","`","tf.float32","`",")","tensor","calculated","from","true","labels","(","or","supervisions",")",".","adv_config",":","a","`","nsl.configs.AdvNeighborConfig","`","object",".","raise_invalid_gradient",":","(","optional",")","a","Boolean","flag","indicating","whether","to","raise","an","error","when","gradients","can","not","be","computed","on","some","input","features",".","There","are","three","cases","where","gradients","can","not","be","computed",":","(","1",")","The","feature","is","a","`","tf.SparseTensor","`",".","(","2",")","The","feature","is","in","a","non-differentiable","`","tf.DType","`",",","like","string","or","integer",".","(","3",")","The","feature","is","not","involved","in","loss","computation",".","If","set","to","False",",","those","input","without","gradient","will","be","ignored","silently","and","not","perturbed",".","(","default=False",")","pgd_model_fn",":","the","model","function",".","Takes","in","the","input_features","and","produces","a","prediction",".","This","is","required","for","PGD","with","more","than","one","step",".","pgd_loss_fn",":","the","loss","function",".","Calculates","loss","between","prediction","and","ground","truth",".","regularizer_fn",":","regularization","function","for","pgd_loss_fn",".","This","is","optional","for","PGD",".","It","should","be","a","function","that","accepts",":","1.","perturbations",":","a","dense","(","float32",")","tensor",",","a","list","of","dense","tensors",",","or","a","dictionary","of","feature","names","and","dense","tensors",".","The","shape","should","be","the","same","as","input_features",".","This","is","the","perturbation","at","each","step","of","PGD",".","2.","keyed_grads_x",":","a","dense","(","float32",")","tensor",",","a","list","of","dense","tensors",",","or","a","dictionary","of","feature","names","and","dense","tensors",".","The","shape","should","be","the","same","as","input_features",".","This","is","the","gradient","of","the","labeled","loss","with","respect","to","input_features",".","The","output","of","this","function","is","added","to","pgd_loss_fn","during","PGD","steps",".","The","default","when","it","is","not","given","is","None",".","use_while_loop",":","A","Boolean","indicating","whether","the","PGD","steps","should","be","done","in","a","`","tf.while_loop","`",".","This","can","potentially","reduce","memory","footprint",",","but","is","not","compatible","to","`","pgd_model_fn","`","with","side","effects",".","(","default=False",")"]
docstr: Generates adversarial neighbors and the corresponding weights.
  
  This function perturbs only *dense* tensors to generate adversarial
  neighbors. No perturbation will be applied on sparse tensors  (e.g., string
  or int). Therefore, in the generated adversarial neighbors, the values of
  these sparse tensors will be kept the same as the input_features. In other
  words, if input_features is a dictionary mapping feature names to tensors,
  the dense features will be perturbed and the values of sparse features will
  remain the same.
  
  Args:
    input_features: a dense (float32) tensor, a list of dense tensors, or a
      dictionary of feature names and dense tensors. The shape of the
      tensor(s) should be either:
      (a) pointwise samples: [batch_size, feat_len], or
      (b) sequence samples: [batch_size, seq_len, feat_len]
    pgd_labels: the labels corresponding to each input. This should have shape
      `[batch_size, 1]`. This is required for PGD-generated adversaries, and
      unused otherwise.
  
  Returns:
    adv_neighbor: the perturbed example, with the same shape and structure as
      input_features
    adv_weight: a dense (float32) tensor with shape of [batch_size, 1],
      representing the weight for each neighbor
  
  Raises:
    ValueError: if some of the `input_features` cannot be perturbed due to
      (a) it is a `tf.SparseTensor`,
      (b) it has a non-differentiable type like string or integer, or
      (c) it is not involved in loss computation.
      This error is suppressed if `raise_invalid_gradient` is set to False
      (which is the default).
docstr_tok: ["Generates","adversarial","neighbors","and","the","corresponding","weights",".","This","function","perturbs","only","*","dense","*","tensors","to","generate","adversarial","neighbors",".","No","perturbation","will","be","applied","on","sparse","tensors","(","e.g.",",","string","or","int",")",".","Therefore",",","in","the","generated","adversarial","neighbors",",","the","values","of","these","sparse","tensors","will","be","kept","the","same","as","the","input_features",".","In","other","words",",","if","input_features","is","a","dictionary","mapping","feature","names","to","tensors",",","the","dense","features","will","be","perturbed","and","the","values","of","sparse","features","will","remain","the","same",".","Args",":","input_features",":","a","dense","(","float32",")","tensor",",","a","list","of","dense","tensors",",","or","a","dictionary","of","feature","names","and","dense","tensors",".","The","shape","of","the","tensor","(","s",")","should","be","either",":","(","a",")","pointwise","samples",":","[","batch_size",",","feat_len","]",",","or","(","b",")","sequence","samples",":","[","batch_size",",","seq_len",",","feat_len","]","pgd_labels",":","the","labels","corresponding","to","each","input",".","This","should","have","shape","`","[","batch_size",",","1","]","`",".","This","is","required","for","PGD-generated","adversaries",",","and","unused","otherwise",".","Returns",":","adv_neighbor",":","the","perturbed","example",",","with","the","same","shape","and","structure","as","input_features","adv_weight",":","a","dense","(","float32",")","tensor","with","shape","of","[","batch_size",",","1","]",",","representing","the","weight","for","each","neighbor","Raises",":","ValueError",":","if","some","of","the","`","input_features","`","can","not","be","perturbed","due","to","(","a",")","it","is","a","`","tf.SparseTensor","`",",","(","b",")","it","has","a","non-differentiable","type","like","string","or","integer",",","or","(","c",")","it","is","not","involved","in","loss","computation",".","This","error","is","suppressed","if","`","raise_invalid_gradient","`","is","set","to","False","(","which","is","the","default",")","."]
code: def gen_neighbor(self, input_features, pgd_labels=None):
      """Generates adversarial neighbors and the corresponding weights.
  
      This function perturbs only *dense* tensors to generate adversarial
      neighbors. No perturbation will be applied on sparse tensors  (e.g., string
      or int). Therefore, in the generated adversarial neighbors, the values of
      these sparse tensors will be kept the same as the input_features. In other
      words, if input_features is a dictionary mapping feature names to tensors,
      the dense features will be perturbed and the values of sparse features will
      remain the same.
  
      Args:
        input_features: a dense (float32) tensor, a list of dense tensors, or a
          dictionary of feature names and dense tensors. The shape of the
          tensor(s) should be either:
          (a) pointwise samples: [batch_size, feat_len], or
          (b) sequence samples: [batch_size, seq_len, feat_len]
        pgd_labels: the labels corresponding to each input. This should have shape
          `[batch_size, 1]`. This is required for PGD-generated adversaries, and
          unused otherwise.
  
      Returns:
        adv_neighbor: the perturbed example, with the same shape and structure as
          input_features
        adv_weight: a dense (float32) tensor with shape of [batch_size, 1],
          representing the weight for each neighbor
  
      Raises:
        ValueError: if some of the `input_features` cannot be perturbed due to
          (a) it is a `tf.SparseTensor`,
          (b) it has a non-differentiable type like string or integer, or
          (c) it is not involved in loss computation.
          This error is suppressed if `raise_invalid_gradient` is set to False
          (which is the default).
      """
      # Composes both features and feature_masks to dictionaries, so that the
      # feature_masks can be looked up by key.
      features = self._compose_as_dict(input_features)
      (
          dense_original_features,
          sparse_original_features,
      ) = self._split_dict(features, is_differentiable_tensor)
      if sparse_original_features:
          sparse_keys = str(sparse_original_features.keys())
          if self._raise_invalid_gradient:
              raise ValueError(
                  "Cannot perturb non-Tensor input: " + sparse_keys
              )
          logging.log_first_n(
              logging.WARNING,
              "Cannot perturb non-Tensor input: %s",
              1,
              sparse_keys,
          )
  
      # Compute keyed_grads_x only if regularizer_fn will be used.
      keyed_grads_x = None
      if self._regularizer_fn_set:
          with tf.GradientTape() as gradient_tape_x:
              gradient_tape_x.watch(dense_original_features)
              original_inputs = self._decompose_as(
                  input_features,
                  dict(
                      dense_original_features,
                      **sparse_original_features,
                  ),
              )
              original_predictions = self._pgd_model_fn(original_inputs)
              loss = self._pgd_loss_fn(pgd_labels, original_predictions)
          keyed_grads_x = self._compute_gradient(
              loss, dense_original_features, gradient_tape_x
          )
  
      def pgd_step(step, dense_features):
          """Runs one step of PGD on the given features."""
  
          with tf.GradientTape(
              watch_accessed_variables=False
          ) as gradient_tape:
              gradient_tape.watch(dense_features)
  
              perturbations = self._retrieve_perturbations(
                  dense_original_features, dense_features
              )
  
              # Pack dense and sparse features into the original input structure.
              inputs = self._decompose_as(
                  input_features,
                  dict(dense_features, **sparse_original_features),
              )
              loss = self._pgd_loss_fn(
                  pgd_labels, self._pgd_model_fn(inputs)
              )
              regularization = self._regularizer_fn(
                  perturbations, keyed_grads_x
              )
              loss = loss + tf.cast(regularization, dtype=loss.dtype)
  
          keyed_grads = self._compute_gradient(
              loss, dense_features, gradient_tape
          )
          perturbations = self._compute_perturbations(
              dense_features, keyed_grads, dense_original_features
          )
          perturbed_features = self._apply_perturbations(
              dense_original_features, perturbations
          )
          return (step + 1, perturbed_features)
  
      if self._adv_config.random_init:
          # Randomly pick a perturbation in the epsilon ball. If the epsilon is not
          # set (e.g. FGSM), use the step size instead.
          perturbations = utils.random_in_norm_ball(
              dense_original_features,
              self._adv_config.pgd_epsilon
              or self._adv_config.adv_step_size,
              self._adv_config.adv_grad_norm,
          )
          start_iteration = 0
      else:
          # The first step is calculated from the given labeled loss.
          keyed_grads = self._compute_gradient(
              self._labeled_loss,
              dense_original_features,
              self._gradient_tape,
          )
          perturbations = self._compute_perturbations(
              dense_original_features,
              keyed_grads,
              dense_original_features,
          )
          start_iteration = 1
      perturbed_features = self._apply_perturbations(
          dense_original_features, perturbations
      )
  
      # Following steps in PGD.
      if self._adv_config.pgd_iterations > start_iteration:
          if self._use_while_loop:
              _, perturbed_features = tf.while_loop(
                  lambda i, _: tf.less(
                      i, self._adv_config.pgd_iterations
                  ),
                  pgd_step,
                  (tf.constant(start_iteration), perturbed_features),
              )
          else:
              for i in range(
                  start_iteration, self._adv_config.pgd_iterations
              ):
                  _, perturbed_features = pgd_step(
                      i, perturbed_features
                  )
  
      # Converts the perturbed examples back to their original structure.
      adv_neighbor = self._decompose_as(
          input_features,
          dict(perturbed_features, **sparse_original_features),
      )
  
      batch_size = tf.shape(list(features.values())[0])[0]
      adv_weight = tf.ones([batch_size, 1])
  
      return adv_neighbor, adv_weight
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import collections
  from absl import logging
  from neural_structured_learning.lib import abstract_gen_neighbor
  from neural_structured_learning.lib import utils
  import tensorflow
label_desc: Use image node_id 9 for calling the _GenAdvNeighbor obj's underlying member method code with example usage: obj.gen_neighbor(input_features, pgd_labels) and returns: adv_neighbor,  adv_weight, unknown,  perturbed_features