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: This function perform exclusionary reclassification. Based on the ex_re_threshold,
  suspicious clusters will be rechecked. If they remain suspicious, the suspected source
  class will be added to the report and the data will be relabelled. The new labels are stored
  in self.y_train_relabelled
  
  :param report: A dictionary containing defence params as well as the class clusters and their suspiciousness.
  :return: report where the report is a dict object
docstr_tok: ["This","function","perform","exclusionary","reclassification",".","Based","on","the","ex_re_threshold",",","suspicious","clusters","will","be","rechecked",".","If","they","remain","suspicious",",","the","suspected","source","class","will","be","added","to","the","report","and","the","data","will","be","relabelled",".","The","new","labels","are","stored","in","self.y_train_relabelled",":","param","report",":","A","dictionary","containing","defence","params","as","well","as","the","class","clusters","and","their","suspiciousness",".",":","return",":","report","where","the","report","is","a","dict","object"]
code: def exclusionary_reclassification(self, report: Dict[str, Any]):
      """
      This function perform exclusionary reclassification. Based on the ex_re_threshold,
      suspicious clusters will be rechecked. If they remain suspicious, the suspected source
      class will be added to the report and the data will be relabelled. The new labels are stored
      in self.y_train_relabelled
  
      :param report: A dictionary containing defence params as well as the class clusters and their suspiciousness.
      :return: report where the report is a dict object
      """
      self.y_train_relabelled = np.copy(
          self.y_train
      )  # Copy the data to avoid overwriting user objects
      # used for relabeling the data
      is_onehot = False
      if len(np.shape(self.y_train)) == 2:
          is_onehot = True
  
      logger.info(
          "Performing Exclusionary Reclassification with a threshold of %s",
          self.ex_re_threshold,
      )
      logger.info(
          "Data will be relabelled internally. Access the y_train_relabelled attribute to get new labels"
      )
      # Train a new classifier with the unsuspicious clusters
      cloned_classifier = (
          self.classifier.clone_for_refitting()
      )  # Get a classifier with the same training setup, but new weights
      filtered_x = self.x_train[np.array(self.is_clean_lst) == 1]
      filtered_y = self.y_train[np.array(self.is_clean_lst) == 1]
  
      if len(filtered_x) == 0:
          logger.warning(
              "All of the data is marked as suspicious. Unable to perform exclusionary reclassification"
          )
          return report
  
      cloned_classifier.fit(filtered_x, filtered_y)
  
      # Test on the suspicious clusters
      n_train = len(self.x_train)
      indices_by_class = self._segment_by_class(
          np.arange(n_train), self.y_train
      )
      indicies_by_cluster: List[List[List]] = [
          [[] for _ in range(self.nb_clusters)]
          for _ in range(self.classifier.nb_classes)
      ]
  
      # Get all data in x_train in the right cluster
      for n_class, cluster_assignments in enumerate(
          self.clusters_by_class
      ):
          for j, assigned_cluster in enumerate(cluster_assignments):
              indicies_by_cluster[n_class][assigned_cluster].append(
                  indices_by_class[n_class][j]
              )
  
      for n_class, _ in enumerate(self.poisonous_clusters):
          suspicious_clusters = np.where(
              np.array(self.poisonous_clusters[n_class]) == 1
          )[0]
          for cluster in suspicious_clusters:
              cur_indicies = indicies_by_cluster[n_class][cluster]
              predictions = cloned_classifier.predict(
                  self.x_train[cur_indicies]
              )
  
              predicted_as_class = [
                  np.sum(np.argmax(predictions, axis=1) == i)
                  for i in range(self.classifier.nb_classes)
              ]
              n_class_pred_count = predicted_as_class[n_class]
              predicted_as_class[n_class] = (
                  -1 * predicted_as_class[n_class]
              )  # Just to make the max simple
              other_class = np.argmax(predicted_as_class)
              other_class_pred_count = predicted_as_class[other_class]
  
              # Check if cluster is legit. If so, mark it as such
              if (
                  other_class_pred_count == 0
                  or n_class_pred_count / other_class_pred_count
                  > self.ex_re_threshold
              ):
                  self.poisonous_clusters[n_class][cluster] = 0
                  report["Class_" + str(n_class)][
                      "cluster_" + str(cluster)
                  ]["suspicious_cluster"] = False
                  if "suspicious_clusters" in report.keys():
                      report["suspicious_clusters"] = (
                          report["suspicious_clusters"] - 1
                      )
                  for ind in cur_indicies:
                      self.is_clean_lst[ind] = 1
              # Otherwise, add the exclusionary reclassification info to the report for the suspicious cluster
              else:
                  report["Class_" + str(n_class)][
                      "cluster_" + str(cluster)
                  ]["ExRe_Score"] = (
                      n_class_pred_count / other_class_pred_count
                  )
                  report["Class_" + str(n_class)][
                      "cluster_" + str(cluster)
                  ]["Suspected_Source_class"] = other_class
                  # Also relabel the data
                  if is_onehot:
                      self.y_train_relabelled[cur_indicies, n_class] = 0
                      self.y_train_relabelled[
                          cur_indicies, other_class
                      ] = 1
                  else:
                      self.y_train_relabelled[
                          cur_indicies
                      ] = other_class
  
      return report
  
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 6 for calling the ActivationDefence obj's underlying member method code with example usage: obj.exclusionary_reclassification(report) and returns: report, report