Source code for revelionn.activation_extraction

import operator
from functools import reduce
import torch

from .main_module import MainModelProcessing


[docs] class ActivationExtractor: """ Class for identifying layers of a convolutional neural network and for extracting activations produced during network inference from a selected set of layers. It has two modes of operation: 1. Activations of the given layers are concatenated and transformed to a one-dimensional tensor. It is used to train a single mapping network. 2. Activations of the specified layers are returned as a tuple without transformations. It is used for training simultaneous mapping network. Attributes ---------- device : torch.device Tensor processing device. main_net : torch.nn.Module The model of the main neural network. layers_types_dict : dict The dictionary contains the names of layer types as keys, and the corresponding values represent the layer class in PyTorch. layers_types : list Types of layers to be found in the hierarchy of network layers. This list should contain only the names of the layer types that are in 'layers_types_dict' dictionary. layers_dict : dict Dictionary of neural network layers. The keys of this dictionary represent the unique names of each of the layers of the convolutional network. activation : dict Activation values of the specified layers. layers_for_research : list A list of the studied convolutional network layers. Set by the user with keys in the 'layers_dict' dictionary. is_concatenate : bool Logical parameter that sets the mode of operation of ActivationExtractor. If True, the activations of the given layers are concatenated and transformed to a one-dimensional tensor. If False, the activations of the specified layers are returned as a tuple without transformations. Methods ------- get_layers_types() Returns user-defined types of layers to be found in the hierarchy of network layers. find_layer_predicate_recursive(model, predicate) Recursively searches through a PyTorch model and returns a list of all layers that satisfy a given predicate. find_layers_types_recursive(model, layers_types) Recursively searches through a PyTorch model and returns a list of all layers that are of a given type or types. create_layers_dict(model, cur_layers_types) Creates a dictionary of PyTorch layers of the given types from a PyTorch model. get_layers_dict() Returns a dictionary of neural network layers. get_layer_name_by_number(number) Returns the layer name in the layers dictionary by its number. get_activation(name) Saves the values of layer activations. register_hooks() Registers the interception of activations of the studied layers. count_num_activations(num_channels, width_img, height_img) Returns the number of activations of neurons of the studied layers. get_activations(mapping_batch_size) Returns the activation tensor of the studied layers. set_layers_for_research(layers) Sets the list of layers to be examined. get_layers_for_research() Returns a list of the layers under study. get_main_net() Returns the main neural network. """ def __init__(self, main_module, layers_types, is_concatenate): """ Sets all the necessary attributes for the ActivationExtractor object. Parameters ---------- main_module : MainModelProcessing The model of the main neural network. layers_types : list Types of layers to be found in the hierarchy of network layers. Possible values of the list item: 'bn', 'conv', 'fc'. is_concatenate : bool Logical parameter that sets the mode of operation of ActivationExtractor. If True, the activations of the given layers are concatenated and transformed to a one-dimensional tensor. If False, the activations of the specified layers are returned as a tuple without transformations. """ self.device = main_module.get_device() self.main_net = main_module.get_main_net().to(self.device) self.layers_types = layers_types self.layers_types_dict = {'bn': torch.nn.BatchNorm2d, 'conv': torch.nn.Conv2d, 'fc': torch.nn.Linear} self.layers_dict = self.create_layers_dict(self.main_net, self.layers_types) self.activation = {} self.is_concatenate = is_concatenate self.layers_for_research = None
[docs] def get_layers_types(self): """ Returns user-defined types of layers to be found in the hierarchy of network layers. Returns ------- list[str] Layers types. """ return self.layers_types
[docs] def find_layer_predicate_recursive(self, model, predicate): """ Recursively searches through a PyTorch model and returns a list of all layers that satisfy a given predicate. Parameters ---------- model : torch.nn.Module The PyTorch model to search through. predicate : function A function that takes a PyTorch layer as input and returns a boolean indicating whether or not the layer satisfies the predicate. Returns ------- list A list of all layers in the model that satisfy the given predicate. """ layers = [] for name, layer in model._modules.items(): if predicate(layer): layers.append(layer) layers.extend(self.find_layer_predicate_recursive(layer, predicate)) return layers
[docs] def find_layers_types_recursive(self, model, layers_types): """ Recursively searches through a PyTorch model and returns a list of all layers that are of a given type or types. Parameters ---------- model : torch.nn.Module The PyTorch model to search through. layers_types : list A list of PyTorch layer types to search for. Returns ------- list A list of all layers in the model that are of one of the given layer types. """ def predicate(layer): return type(layer) in layers_types return self.find_layer_predicate_recursive(model, predicate)
[docs] def create_layers_dict(self, model, cur_layers_types): """ Creates a dictionary of PyTorch layers of the given types from a PyTorch model. Parameters ---------- model : torch.nn.Module The PyTorch model to extract layers from. cur_layers_types : list A list of strings representing the types of layers to extract. Returns ------- dict A dictionary mapping layer names to PyTorch layers of the corresponding type. """ cur_layers_types_dict = {} for cur_layer_type in cur_layers_types: cur_layers_types_dict[cur_layer_type] = self.layers_types_dict[cur_layer_type] layers = self.find_layers_types_recursive(model, list(cur_layers_types_dict.values())) layer_names = [] count = 0 for layer in layers: for name, layer_type in cur_layers_types_dict.items(): if isinstance(layer, layer_type): layer_names.append(f'{name}{count}') count += 1 layers_dict = dict(zip(layer_names, layers)) return layers_dict
[docs] def get_layers_dict(self): """ Returns a dictionary of neural network layers. Returns ------- layers_dict : dict A dictionary of neural network layers. """ return self.layers_dict
[docs] def get_layer_name_by_number(self, number): """ Returns the layer name in the layers dictionary by its number. Parameters ---------- number : int Layer number. Returns ------- str Layer name. """ for name, layer in self.layers_dict.items(): if str(number) == ''.join(i for i in name if not i.isalpha()): return name raise f'Layer {number} not found'
[docs] def get_activation(self, name): """ Saves the values of layer activations. Parameters ---------- name : str User-defined name of the neural network layer. Returns ------- hook """ def hook(model, input, output): self.activation[name] = output return hook
[docs] def register_hooks(self): """ Registers the interception of activations of the studied layers. """ for layer in self.layers_for_research: self.layers_dict.get(layer).register_forward_hook(self.get_activation(layer))
@staticmethod def prod(iterable): return reduce(operator.mul, iterable, 1)
[docs] def count_num_activations(self, num_channels, width_img, height_img): """ Returns the number of activations of neurons of the studied layers. Parameters ---------- num_channels : int Number of channels in the input image. width_img : int Width of the input image. height_img : int Height of the input image. Returns ------- num_activations : int Number of neuron activations. """ self.main_net.eval() inp = torch.randn(1, num_channels, width_img, height_img).to(self.device) with torch.no_grad(): self.main_net(inp) num_activations = 0 for layer in self.layers_for_research: num_activations += self.prod(self.activation[layer].shape) return num_activations
[docs] def get_activations(self, mapping_batch_size): """ Returns the activation tensor of the studied layers. Parameters ---------- mapping_batch_size : int The size of the data batch for training the mapping network. The size of the training set must be a multiple of the size of the batch. Returns ------- cur_acts : torch.Tensor or tuple[torch.Tensor] Activations of the studied layers. """ if self.is_concatenate: cur_acts = None for layer in self.layers_for_research: if 'fc' in layer: act = self.activation[layer] elif 'bn' in layer or 'conv' in layer: act = torch.reshape(self.activation[layer], (mapping_batch_size, -1)) else: continue if cur_acts is None: cur_acts = act.T else: cur_acts = torch.cat((cur_acts, act.T), 0) return cur_acts.T else: acts = [] for layer in self.layers_for_research: acts.append(self.activation[layer]) return tuple(acts)
[docs] def set_layers_for_research(self, layers): """ Sets the list of layers to be examined. Parameters ---------- layers : list[str] or list[int] Contains layer names or layer numbers (indexing from 0) available in the dictionary of layers. """ all_elements_are_int = all(isinstance(layer, int) for layer in layers) if all_elements_are_int: for i in range(len(layers)): layers[i] = self.get_layer_name_by_number(layers[i]) else: for layer in layers: if layer not in self.layers_dict.keys(): raise f'Not found {layer} in layers_dict' self.layers_for_research = layers self.register_hooks()
[docs] def get_layers_for_research(self): """ Returns a list of the layers under study. Returns ------- layers_for_research : list The list of layers to be examined. """ return self.layers_for_research
[docs] def get_main_net(self): """ Returns the main neural network. Returns ------- main_net : torch.nn.Module The main neural network. """ return self.main_net