code: def generate_virtual_adversarial_perturbation(
      inputs,
      logits,
      model,
      placeholders,
      mask,
      predictions_var_scope,
      is_train=True,
  ):
      """Generates an adversarial perturbation for virtual adversarial training.
  
      Args:
        inputs: A batch of input features, where the batch is the first dimension.
        logits: The logits predicted by a model on the provided inputs.
        model: The model that generated the logits.
        placeholders: A dictionary mapping string names to Tensorflow placeholders
          that are passed to the models when generating the predictions.
        mask: A tensor of booleans specifying which samples to apply the virtual
          adversarial loss to.
        predictions_var_scope: Variable scope for obtaining the predictions.
        is_train: A boolean placeholder specifying if this is a training or testing
          setting.
  
      Returns:
        A Tensor of the same shape as the inputs containing the adversarial
        perturbation for these inputs.
      """
      # Generate random perturbations.
      d = tf.random_normal(shape=tf.shape(inputs))
      # Only apply perturbations on the masked samples.
      d = tf.multiply(d, mask[:, None])
  
      for _ in range(num_power_iterations):
          d = xi * get_normalized_vector(d)
          logit_p = logits
          new_inputs = tf.add(tf.sparse_tensor_to_dense(inputs), d)
          new_inputs = tf.sparse.from_dense(new_inputs)
          with tf.variable_scope(
              predictions_var_scope,
              auxiliary_name_scope=False,
              reuse=True,
          ):
              encoding_m, _, _ = model.get_encoding_and_params(
                  inputs=new_inputs,
                  is_train=is_train,
                  update_batch_stats=False,
                  **placeholders,
              )
              logit_m, _, _ = model.get_predictions_and_params(
                  encoding=encoding_m, is_train=is_train, **placeholders
              )
          dist = kl_divergence_with_logit(logit_p, logit_m, mask)
          grad = tf.gradients(dist, [d], aggregation_method=2)[0]
          d = tf.stop_gradient(grad)
  
      r_vadv = get_normalized_vector(d)
      if scale_r:
          r_vadv *= get_normalizing_constant(inputs.values)
      r_vadv *= epsilon
  
      return tf.sparse.from_dense(r_vadv)
  
file_path: neural-structured-learning/research/gam/gam/trainer/adversarial_sparse.py
imports: from .adversarial_dense import get_normalized_vector
  from .adversarial_dense import get_normalizing_constant
  import tensorflow
label_desc: Use image node_id 3 for calling a global function with example usage: generate_virtual_adversarial_perturbation(inputs, logits, model, placeholders, mask, predictions_var_scope, is_train) and returns: tf