docstr: Apply perturbation to instance or prediction probabilities. Used for numerical calculation of gradients.
  
  Parameters
  ----------
  X
      Array to be perturbed.
  eps
      Size of perturbation.
  proba
      If ``True``, the net effect of the perturbation needs to be 0 to keep the sum of the
      probabilities equal to 1.
  
  Returns
  -------
  Instances where a positive and negative perturbation is applied.
docstr_tok: ["Apply","perturbation","to","instance","or","prediction","probabilities",".","Used","for","numerical","calculation","of","gradients",".","Parameters","--","--","--","--","--","X","Array","to","be","perturbed",".","eps","Size","of","perturbation",".","proba","If","``","True","``",",","the","net","effect","of","the","perturbation","needs","to","be","0","to","keep","the","sum","of","the","probabilities","equal","to","1",".","Returns","--","--","--","-","Instances","where","a","positive","and","negative","perturbation","is","applied","."]
code: def perturb(
      self,
      X: np.ndarray,
      eps: Union[float, np.ndarray],
      proba: bool = False,
  ) -> Tuple[np.ndarray, np.ndarray]:
      """
      Apply perturbation to instance or prediction probabilities. Used for numerical calculation of gradients.
  
      Parameters
      ----------
      X
          Array to be perturbed.
      eps
          Size of perturbation.
      proba
          If ``True``, the net effect of the perturbation needs to be 0 to keep the sum of the
          probabilities equal to 1.
  
      Returns
      -------
      Instances where a positive and negative perturbation is applied.
      """
      # N = batch size; F = nb of features in X
      shape = X.shape
      X = np.reshape(X, (shape[0], -1))  # NxF
      dim = X.shape[1]  # F
      pert = np.tile(np.eye(dim) * eps, (shape[0], 1))  # (N*F)xF
      if proba:
          eps_n = eps / (dim - 1)
          pert += np.tile(
              (np.eye(dim) - np.ones((dim, dim))) * eps_n, (shape[0], 1)
          )  # (N*F)xF
      X_rep = np.repeat(X, dim, axis=0)  # (N*F)xF
      X_pert_pos, X_pert_neg = X_rep + pert, X_rep - pert
      shape = (dim * shape[0],) + shape[1:]
      X_pert_pos = np.reshape(
          X_pert_pos, shape
      )  # (N*F)x(shape of X[0])
      X_pert_neg = np.reshape(
          X_pert_neg, shape
      )  # (N*F)x(shape of X[0])
      return X_pert_pos, X_pert_neg
  
file_path: alibi/alibi/explainers/cem.py
imports: import copy
  import logging
  import sys
  from typing import Any, Callable, Optional, Tuple, Union
  import numpy
  import tensorflow.compat.v1
  from alibi.api.defaults import DEFAULT_DATA_CEM, DEFAULT_META_CEM
  from alibi.api.interfaces import Explainer, Explanation, FitMixin
label_desc: Use image node_id 4 for calling the CEM obj's underlying member method code with example usage: obj.perturb(X, eps, proba) and returns: X_pert_pos,  X_pert_neg