class_docstr: Interface for torch models.
class_docstr_tok: ["Interface","for","torch","models","."]
docstr: Compute the model's internal representation of input x.
  In practice, this means, executing a forward pass and then, capturing the output of layers (of interest).
  As the exact definition of "internal model representation" is left out in the original paper (see: https://arxiv.org/pdf/2203.06877.pdf),
  we make the implementation flexible.
  It is up to the user whether all layers are used, or specific ones should be selected.
  The user can therefore select a layer by providing 'layer_names' (exclusive) or 'layer_indices'.
  
  Parameters
  ----------
  x: np.ndarray
      4D tensor, a batch of input datapoints
  layer_names: List[str]
      List with names of layers, from which output should be captured.
  layer_indices: List[int]
      List with indices of layers, from which output should be captured.
      Intended to use in case, when layer names are not unique, or unknown.
  
  Returns
  -------
  L: np.ndarray
      2D tensor with shape (batch_size, None)
docstr_tok: ["Compute","the","model","'s","internal","representation","of","input","x",".","In","practice",",","this","means",",","executing","a","forward","pass","and","then",",","capturing","the","output","of","layers","(","of","interest",")",".","As","the","exact","definition","of","``","internal","model","representation","''","is","left","out","in","the","original","paper","(","see",":","https",":","\/\/arxiv.org\/pdf\/2203.06877.pdf",")",",","we","make","the","implementation","flexible",".","It","is","up","to","the","user","whether","all","layers","are","used",",","or","specific","ones","should","be","selected",".","The","user","can","therefore","select","a","layer","by","providing","'layer_names","'","(","exclusive",")","or","'layer_indices","'",".","Parameters","--","--","--","--","--","x",":","np.ndarray","4D","tensor",",","a","batch","of","input","datapoints","layer_names",":","List","[","str","]","List","with","names","of","layers",",","from","which","output","should","be","captured",".","layer_indices",":","List","[","int","]","List","with","indices","of","layers",",","from","which","output","should","be","captured",".","Intended","to","use","in","case",",","when","layer","names","are","not","unique",",","or","unknown",".","Returns","--","--","--","-","L",":","np.ndarray","2D","tensor","with","shape","(","batch_size",",","None",")"]
code: def get_hidden_representations(
      self,
      x: np.ndarray,
      layer_names: Optional[List[str]] = None,
      layer_indices: Optional[List[int]] = None,
  ) -> np.ndarray:
      """
      Compute the model's internal representation of input x.
      In practice, this means, executing a forward pass and then, capturing the output of layers (of interest).
      As the exact definition of "internal model representation" is left out in the original paper (see: https://arxiv.org/pdf/2203.06877.pdf),
      we make the implementation flexible.
      It is up to the user whether all layers are used, or specific ones should be selected.
      The user can therefore select a layer by providing 'layer_names' (exclusive) or 'layer_indices'.
  
      Parameters
      ----------
      x: np.ndarray
          4D tensor, a batch of input datapoints
      layer_names: List[str]
          List with names of layers, from which output should be captured.
      layer_indices: List[int]
          List with indices of layers, from which output should be captured.
          Intended to use in case, when layer names are not unique, or unknown.
  
      Returns
      -------
      L: np.ndarray
          2D tensor with shape (batch_size, None)
      """
  
      device = self.device if self.device is not None else "cpu"
      all_layers = [*self.model.named_modules()]
      num_layers = len(all_layers)
  
      if layer_indices is None:
          layer_indices = []
  
      # E.g., user can provide index -1, in order to get only representations of the last layer.
      # E.g., for 7 layers in total, this would correspond to positive index 6.
      positive_layer_indices = [
          i if i >= 0 else num_layers + i for i in layer_indices
      ]
  
      if layer_names is None:
          layer_names = []
  
      def is_layer_of_interest(layer_index: int, layer_name: str):
          if layer_names == [] and positive_layer_indices == []:
              return True
          return (
              layer_index in positive_layer_indices
              or layer_name in layer_names
          )
  
      # skip modules defined by subclassing API.
      hidden_layers = list(  # type: ignore
          filter(
              lambda l: not isinstance(
                  l[1], (self.model.__class__, torch.nn.Sequential)
              ),
              all_layers,
          )
      )
  
      batch_size = x.shape[0]
      hidden_outputs = []
  
      # We register forward hook on layers of interest, which just saves the flattened layers' outputs to list.
      # Then we execute forward pass and stack them in 2D tensor.
      def hook(module, module_in, module_out):
          arr = module_out.cpu().numpy()
          arr = arr.reshape((batch_size, -1))
          hidden_outputs.append(arr)
  
      new_hooks = []
      # Save handles of registered hooks, so we can clean them up later.
      for index, (name, layer) in enumerate(hidden_layers):
          if is_layer_of_interest(index, name):
              handle = layer.register_forward_hook(hook)
              new_hooks.append(handle)
  
      if len(new_hooks) == 0:
          raise ValueError("No hidden representations were selected.")
  
      # Execute forward pass.
      with torch.no_grad():
          self.model(torch.Tensor(x).to(device))
  
      # Cleanup.
      [i.remove() for i in new_hooks]
      return np.hstack(hidden_outputs)
  
imports: import copy
  from contextlib import suppress
  from copy import deepcopy
  from typing import Any, Dict, Optional, Tuple, List, Union, Generator
  import warnings
  import logging
  import numpy
  import torch
  from torch import nn
  from functools import lru_cache
  from quantus.helpers import utils
  from quantus.helpers.model.model_interface import ModelInterface
label_desc: Use image node_id 12 for calling the PyTorchModel obj's underlying member method code with example usage: obj.get_hidden_representations(x, layer_names, layer_indices) and returns: np, unknown, True