class_docstr: Wrapper class for importing Keras models.
docstr: Compute per-class derivatives w.r.t. `x`.
  
  :param x: Sample input with shape as expected by the model.
  :param label: Index of a specific per-class derivative. If an integer is provided, the gradient of that class
                output is computed for all samples. If multiple values are provided, the first dimension should
                match the batch size of `x`, and each value will be used as target for its corresponding sample in
                `x`. If `None`, then gradients for all classes will be computed for each sample.
  :param training_mode: `True` for model set to training mode and `'False` for model set to evaluation mode.
  :return: Array of gradients of input features w.r.t. each class in the form
           `(batch_size, nb_classes, input_shape)` when computing for all classes, otherwise shape becomes
           `(batch_size, 1, input_shape)` when `label` parameter is specified.
docstr_tok: ["Compute","per-class","derivatives","w.r.t",".","`","x","`",".",":","param","x",":","Sample","input","with","shape","as","expected","by","the","model",".",":","param","label",":","Index","of","a","specific","per-class","derivative",".","If","an","integer","is","provided",",","the","gradient","of","that","class","output","is","computed","for","all","samples",".","If","multiple","values","are","provided",",","the","first","dimension","should","match","the","batch","size","of","`","x","`",",","and","each","value","will","be","used","as","target","for","its","corresponding","sample","in","`","x","`",".","If","`","None","`",",","then","gradients","for","all","classes","will","be","computed","for","each","sample",".",":","param","training_mode",":","`","True","`","for","model","set","to","training","mode","and","`","'False","`","for","model","set","to","evaluation","mode",".",":","return",":","Array","of","gradients","of","input","features","w.r.t",".","each","class","in","the","form","`","(","batch_size",",","nb_classes",",","input_shape",")","`","when","computing","for","all","classes",",","otherwise","shape","becomes","`","(","batch_size",",","1",",","input_shape",")","`","when","`","label","`","parameter","is","specified","."]
code: def class_gradient(  # pylint: disable=W0221
      self,
      x: np.ndarray,
      label: Optional[Union[int, List[int], np.ndarray]] = None,
      training_mode: bool = False,
      **kwargs,
  ) -> np.ndarray:
      """
      Compute per-class derivatives w.r.t. `x`.
  
      :param x: Sample input with shape as expected by the model.
      :param label: Index of a specific per-class derivative. If an integer is provided, the gradient of that class
                    output is computed for all samples. If multiple values are provided, the first dimension should
                    match the batch size of `x`, and each value will be used as target for its corresponding sample in
                    `x`. If `None`, then gradients for all classes will be computed for each sample.
      :param training_mode: `True` for model set to training mode and `'False` for model set to evaluation mode.
      :return: Array of gradients of input features w.r.t. each class in the form
               `(batch_size, nb_classes, input_shape)` when computing for all classes, otherwise shape becomes
               `(batch_size, 1, input_shape)` when `label` parameter is specified.
      """
      # Check value of label for computing gradients
      if not (
          label is None
          or (
              isinstance(label, (int, np.integer))
              and label in range(self.nb_classes)
          )
          or (
              isinstance(label, np.ndarray)
              and len(label.shape) == 1
              and (label < self.nb_classes).all()
              and label.shape[0] == x.shape[0]
          )
      ):
          raise ValueError(
              f"Label {label} is out of range."
          )  # pragma: no cover
  
      # Check shape of preprocessed `x` because of custom function for `_class_gradients`
      x_preprocessed, _ = self._apply_preprocessing(
          x, y=None, fit=False
      )
      shape_match = [
          i is None or i == j
          for i, j in zip(self._input_shape, x_preprocessed.shape[1:])
      ]
      if not all(shape_match):  # pragma: no cover
          raise ValueError(
              f"Error when checking x: expected preprocessed x to have shape {self._input_shape} but got array with "
              f"shape {x_preprocessed.shape[1:]}"
          )
  
      self._init_class_gradients(label=label)
  
      if label is None:
          # Compute the gradients w.r.t. all classes
          gradients = np.swapaxes(
              np.array(self._class_gradients([x_preprocessed])), 0, 1
          )
  
      elif isinstance(label, (int, np.integer)):
          # Compute the gradients only w.r.t. the provided label
          grad_fn = self._class_gradients_idx[label]
          if grad_fn is not None:
              gradients = np.swapaxes(
                  np.array(
                      grad_fn([x_preprocessed, int(training_mode)])
                  ),
                  axis1=0,
                  axis2=1,
              )
          else:  # pragma: no cover
              raise ValueError(
                  "Class gradient operation is not defined."
              )
          assert (
              gradients.shape
              == (x_preprocessed.shape[0], 1) + x_preprocessed.shape[1:]
          )
  
      else:
          # For each sample, compute the gradients w.r.t. the indicated target class (possibly distinct)
          unique_label = list(np.unique(label))
          gradients_list = []
          for u_l in unique_label:
              grad_fn = self._class_gradients_idx[u_l]
              if grad_fn is not None:
                  gradients_list.append(
                      grad_fn([x_preprocessed, int(training_mode)])
                  )
              else:  # pragma: no cover
                  raise ValueError(
                      "Class gradient operation is not defined."
                  )
          gradients = np.array(gradients_list)
          gradients = np.swapaxes(np.squeeze(gradients, axis=1), 0, 1)
          lst = [unique_label.index(i) for i in label]
          gradients = np.expand_dims(
              gradients[np.arange(len(gradients)), lst], axis=1
          )
  
      gradients = self._apply_preprocessing_gradient(x, gradients)
  
      return gradients
  
imports: from __future__ import absolute_import, division, print_function, unicode_literals
  import logging
  import os
  import time
  from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, TYPE_CHECKING
  import numpy
  import six
  from art import config
  from art.estimators.keras import KerasEstimator
  from art.estimators.classification.classifier import ClassifierMixin, ClassGradientsMixin
  from art.utils import check_and_transform_label_format
label_desc: Use image node_id 9 for calling the KerasClassifier obj's underlying member method code with example usage: obj.class_gradient(x, label, training_mode) and returns: gradients