class_docstr: Implementation of Gradient Matching Attack by Geiping, et. al. 2020.
  "Witches' Brew: Industrial Scale Data Poisoning via Gradient Matching"
  
  | Paper link: https://arxiv.org/abs/2009.02276
class_docstr_tok: ["Implementation","of","Gradient","Matching","Attack","by","Geiping",",","et",".","al",".","2020",".","``","Witches","'","Brew",":","Industrial","Scale","Data","Poisoning","via","Gradient","Matching","''","|","Paper","link",":","https",":","\/\/arxiv.org\/abs\/2009.02276"]
code: def _initialize_poison_pytorch(
      self,
      x_trigger: np.ndarray,
      y_trigger: np.ndarray,
      x_poison: np.ndarray,
      y_poison: np.ndarray,  # pylint: disable=unused-argument
  ):
      import torch
      from torch import nn
      from art.estimators.classification.pytorch import (
          PyTorchClassifier,
      )
  
      if isinstance(self.substitute_classifier, PyTorchClassifier):
          classifier = self.substitute_classifier
      else:
          raise Exception(
              "This method requires `PyTorchClassifier` as `substitute_classifier`'s type"
          )
  
      num_poison = len(x_poison)
      len_noise = np.prod(x_poison.shape[1:])
      device = "cuda" if torch.cuda.is_available() else "cpu"
  
      self.model_trainable = self.substitute_classifier.model.training
      self.substitute_classifier.model.eval()
  
      def _weight_grad(
          classifier: PyTorchClassifier,
          x: torch.Tensor,
          target: torch.Tensor,
      ) -> torch.Tensor:
          classifier.model.zero_grad()
          y = classifier.model(x)
          loss_ = classifier.loss(y, target)
          gradspred = torch.autograd.grad(
              loss_,
              list(classifier.model.parameters()),
              create_graph=True,
              retain_graph=True,
          )
          d_w = torch.cat([w.flatten() for w in gradspred])
          d_w_norm = d_w / torch.sqrt(torch.sum(torch.square(d_w)))
          return d_w_norm
  
      class NoiseEmbedding(nn.Module):
          """
          Gradient matching noise layer.
          """
  
          def __init__(
              self,
              num_poison: int,
              len_noise: int,
              epsilon: float,
              clip_values: Tuple[float, float],
          ):
              super().__init__()
  
              self.embedding_layer = nn.Embedding(num_poison, len_noise)
              torch.nn.init.normal_(
                  self.embedding_layer.weight, std=epsilon * 0.0001
              )
              self.epsilon = epsilon
              self.clip_values = clip_values
  
          def forward(
              self,
              input_poison: torch.Tensor,
              input_indices: torch.Tensor,
          ) -> torch.Tensor:
              """
              Applies the noise variable to the input.
              Input to the model must match its index as the noise is specific to the input.
              """
              embeddings = self.embedding_layer(input_indices).to(
                  device
              )
              embeddings = torch.tanh(embeddings) * self.epsilon
              embeddings = embeddings.view(input_poison.shape)
  
              input_noised = input_poison + embeddings
              input_noised = torch.clip(
                  input_noised, self.clip_values[0], self.clip_values[1]
              )  # Make sure the poisoned samples are in a valid range.
  
              return input_noised
  
      class BackdoorModel(nn.Module):
          """
          Backdoor model computing the B loss.
          """
  
          def __init__(
              self,
              gradient_matching: GradientMatchingAttack,
              classifier: PyTorchClassifier,
              epsilon,
              num_poison,
              len_noise,
              min_,
              max_,
          ):
              super().__init__()
              self.gradient_matching = gradient_matching
              self.classifier = classifier
              self.noise_embedding = NoiseEmbedding(
                  num_poison, len_noise, epsilon, (min_, max_)
              )
              self.cos = nn.CosineSimilarity(dim=-1)
  
          def forward(
              self,
              x: torch.Tensor,
              indices_poison: torch.Tensor,
              y: torch.Tensor,
              grad_ws_norm: torch.Tensor,
          ) -> Tuple[torch.Tensor, torch.Tensor]:
              """
              Applies the poison noise and compute the loss with respect to the target gradient.
              """
              poisoned_samples = self.noise_embedding(x, indices_poison)
              d_w2_norm = _weight_grad(
                  self.classifier, poisoned_samples, y
              )
              d_w2_norm.requires_grad_(True)
              B_score = 1 - self.cos(
                  grad_ws_norm, d_w2_norm
              )  # pylint: disable=C0103
              return B_score, poisoned_samples
  
      self.grad_ws_norm = _weight_grad(
          classifier,
          torch.tensor(x_trigger, device=device, dtype=torch.float32),
          torch.tensor(y_trigger, device=device),
      ).detach()
      self.grad_ws_norm.requires_grad_(False)
      self.backdoor_model = BackdoorModel(
          self,
          classifier,
          self.epsilon,
          num_poison,
          len_noise,
          self.clip_values[0],
          self.clip_values[1],
      ).to(device)
      self.optimizer = torch.optim.Adam(
          self.backdoor_model.noise_embedding.embedding_layer.parameters(),
          lr=1,
      )
  
      class PredefinedLRSchedule:
          """
          Use a preset learning rate based on the current training epoch.
          """
  
          def __init__(
              self, learning_rates: List[float], milestones: List[int]
          ):
              self.schedule = list(zip(milestones, learning_rates))
  
          def __call__(self, step: int) -> float:
              lr_prev = self.schedule[0][1]
              for m, learning_rate in self.schedule:
                  if step < m:
                      return lr_prev
                  lr_prev = learning_rate
              return lr_prev
  
          def get_config(self) -> Dict:
              """
              returns a dictionary of parameters.
              """
              return {"schedule": self.schedule}
  
      self.lr_schedule = torch.optim.lr_scheduler.LambdaLR(
          self.optimizer,
          PredefinedLRSchedule(*self.learning_rate_schedule),
      )
  
file_path: adversarial-robustness-toolbox/art/attacks/poisoning/gradient_matching_attack.py
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  from typing import Any, Dict, Tuple, TYPE_CHECKING, List
  import numpy
  from tqdm.auto import trange, tqdm
  from art.attacks.attack import Attack
  from art.estimators import BaseEstimator, NeuralNetworkMixin
  from art.estimators.classification.classifier import ClassifierMixin
label_desc: Use image node_id 6 for calling the GradientMatchingAttack obj's underlying member method code with example usage: obj._initialize_poison_pytorch(x_trigger, y_trigger, x_poison, y_poison) and returns: d_w_norm, input_noised, B_score,  poisoned_samples, lr_prev, dict, lr_prev