class_docstr: Method from Chen et al., 2018 performing poisoning detection based on activations clustering.
  
  | Paper link: https://arxiv.org/abs/1811.03728
  
  | Please keep in mind the limitations of defences. For more information on the limitations of this
      defence, see https://arxiv.org/abs/1905.13409 . For details on how to evaluate classifier security
      in general, see https://arxiv.org/abs/1902.06705
docstr: Clusters activations and returns cluster_by_class and red_activations_by_class, where cluster_by_class[i][j] is
  the cluster to which the j-th data point in the ith class belongs and the correspondent activations reduced by
  class red_activations_by_class[i][j].
  
  :param kwargs: A dictionary of cluster-specific parameters.
  :return: Clusters per class and activations by class.
docstr_tok: ["Clusters","activations","and","returns","cluster_by_class","and","red_activations_by_class",",","where","cluster_by_class","[","i","]","[","j","]","is","the","cluster","to","which","the","j-th","data","point","in","the","ith","class","belongs","and","the","correspondent","activations","reduced","by","class","red_activations_by_class","[","i","]","[","j","]",".",":","param","kwargs",":","A","dictionary","of","cluster-specific","parameters",".",":","return",":","Clusters","per","class","and","activations","by","class","."]
code: def cluster_activations(
      self, **kwargs
  ) -> Tuple[List[np.ndarray], List[np.ndarray]]:
      """
      Clusters activations and returns cluster_by_class and red_activations_by_class, where cluster_by_class[i][j] is
      the cluster to which the j-th data point in the ith class belongs and the correspondent activations reduced by
      class red_activations_by_class[i][j].
  
      :param kwargs: A dictionary of cluster-specific parameters.
      :return: Clusters per class and activations by class.
      """
      self.set_params(**kwargs)
  
      if self.generator is not None:
          batch_size = self.generator.batch_size
          num_samples = self.generator.size
          num_classes = self.classifier.nb_classes
          for batch_idx in range(num_samples // batch_size):  # type: ignore
              x_batch, y_batch = self.generator.get_batch()
  
              batch_activations = self._get_activations(x_batch)
              activation_dim = batch_activations.shape[-1]
  
              # initialize values list of lists on first run
              if batch_idx == 0:
                  self.activations_by_class = [
                      np.empty((0, activation_dim))
                      for _ in range(num_classes)
                  ]
                  self.clusters_by_class = [
                      np.empty(0, dtype=int) for _ in range(num_classes)
                  ]
                  self.red_activations_by_class = [
                      np.empty((0, self.nb_dims))
                      for _ in range(num_classes)
                  ]
  
              activations_by_class = self._segment_by_class(
                  batch_activations, y_batch
              )
              (
                  clusters_by_class,
                  red_activations_by_class,
              ) = cluster_activations(
                  activations_by_class,
                  nb_clusters=self.nb_clusters,
                  nb_dims=self.nb_dims,
                  reduce=self.reduce,
                  clustering_method=self.clustering_method,
                  generator=self.generator,
                  clusterer_new=self.clusterer,
              )
  
              for class_idx in range(num_classes):
                  self.activations_by_class[class_idx] = np.vstack(
                      [
                          self.activations_by_class[class_idx],
                          activations_by_class[class_idx],
                      ]
                  )
                  self.clusters_by_class[class_idx] = np.append(
                      self.clusters_by_class[class_idx],
                      clusters_by_class[class_idx],
                  )
                  self.red_activations_by_class[class_idx] = np.vstack(
                      [
                          self.red_activations_by_class[class_idx],
                          red_activations_by_class[class_idx],
                      ]
                  )
          return self.clusters_by_class, self.red_activations_by_class
  
      if not self.activations_by_class:
          activations = self._get_activations()
          self.activations_by_class = self._segment_by_class(
              activations, self.y_train
          )
  
      [
          self.clusters_by_class,
          self.red_activations_by_class,
      ] = cluster_activations(
          self.activations_by_class,
          nb_clusters=self.nb_clusters,
          nb_dims=self.nb_dims,
          reduce=self.reduce,
          clustering_method=self.clustering_method,
      )
  
      return self.clusters_by_class, self.red_activations_by_class
  
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  import os
  import pickle
  import time
  from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
  import numpy
  from sklearn.cluster import KMeans, MiniBatchKMeans
  from art import config
  from art.data_generators import DataGenerator
  from art.defences.detector.poison.clustering_analyzer import ClusteringAnalyzer
  from art.defences.detector.poison.ground_truth_evaluator import GroundTruthEvaluator
  from art.defences.detector.poison.poison_filtering_defence import PoisonFilteringDefence
  from art.utils import segment_by_class
  from art.visualization import create_sprite, save_image, plot_3d
label_desc: Use image node_id 4 for calling the ActivationDefence obj's underlying member method code with example usage: obj.cluster_activations() and returns: self,  self, self,  self