code: def attack_batch(self, batch, true_future_target):
      res = {}
  
      # generate adv target
      with torch.no_grad():
          inputs = {key: batch[key] for key in self.input_names}
          _, sample_mu = forward_model(self.model, inputs)
          future_target = sample_mu
          attack_idx = self.params.attack_idx
          target_items = self.params.target_items
          loss_function = nn.MSELoss(reduction="none")
          best_dense = 0
  
      for mode in self.params.modes:
          print("Mode: ", mode)
  
          # Generate dense attack
          attack_module = AttackModule(
              model=self.model,
              params=self.params,
              c=0,
              batch=batch,
              input_names=self.input_names,
          )
          adv_target = generate_adv_target(
              future_target=future_target,
              mode=mode,
              factor=self.params.factor,
          )
  
          optimizer = optim.Adam(
              [attack_module.perturbation], lr=self.params.learning_rate
          )
  
          for _ in tqdm(range(self.params.n_iterations)):
              self.attack_step(attack_module, optimizer, adv_target)
  
          # evaluate attack
          with torch.no_grad():
              perturbed_mu = attack_module()
              loss = loss_function(
                  true_future_target[:, attack_idx, target_items],
                  perturbed_mu[:, attack_idx, target_items],
              ).mean()
              if loss >= best_dense:
                  best_dense = loss
                  res["dense"] = attack_module.get_perturbation().data
  
      return res
  
imports: import torch
  import torch.nn
  import torch.optim
  from tqdm.auto import tqdm
  from typing import List
  from sparse_layer import SparseNet
label_desc: Use image node_id 3 for calling the Attack obj's underlying member method code with example usage: obj.attack_batch(batch, true_future_target) and returns: res