Skip to content

Mappers

Abstract Mappers

InfluenceMapper

Abstract class that transforms a neural network model into an influence graph, based on some input x.

Abstract Class

1
2
3
4
5
6
7
8
class InfluenceMapper(ExtractorMapper):

    def __init__(self, *args, **kwargs):
        super().__init__()

    @abstractmethod
    def apply(self, model, x):
        pass

Implementation Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class DefaultConvolutionalInfluenceMapper(InfluenceMapper):
    def __init__(self, no_filters=10, decoder_function=None):
        super().__init__()
        self.no_filters = no_filters

    def apply(self, model, x):
        influences = InfluenceGraph()

        layers = copy.copy(model.layers)
        layers.reverse()

        classifier_layers = []
        last_conv_layer = None

        for layer in layers:
            if not (isinstance(layer, keras.layers.Conv2D)):
                classifier_layers.append(layer)
            else:
                last_conv_layer = layer
                break
        classifier_layers.reverse()

        if last_conv_layer is None:
            raise Exception('Could not detect any convolutional layers')

        relevant_features = self.get_relevant(
            model, x, last_conv_layer, classifier_layers
        )

        preds = model.predict(x)
        influences.add_node('Prediction')
        predicted_class = model.predict_classes(x)
        confidence = model.predict_proba(x)

        influences.add_node('Input', grad=0)

        for layer, idx, grad in relevant_features:
            influences.add_node(
                f'Filter {idx}', layer=layer, filter_idx=idx, grad=grad)
            influences.add_influence(f'Filter {idx}', 'Prediction')
            influences.add_influence('Input', f'Filter {idx}')

        return influences, predicted_class, confidence

    def get_relevant(self, model, x, last_conv_layer, classifier_layers):
        last_conv_layer_model = keras.Model(
            model.inputs, last_conv_layer.output)

        # Create a model that maps the activations of the last conv
        # layer to the final class predictions
        classifier_input = keras.Input(shape=last_conv_layer.output.shape[1:])
        y = classifier_input
        for layer in classifier_layers:
            y = layer(y)
        classifier_model = keras.Model(classifier_input, y)

        # Compute the gradient of the top predicted class for our input image
        # with respect to the activations of the last conv layer
        with tf.GradientTape() as tape:
            # Compute activations of the last conv layer and make the tape watch it
            last_conv_layer_output = last_conv_layer_model(x)
            tape.watch(last_conv_layer_output)
            # Compute class predictions
            preds = classifier_model(last_conv_layer_output)
            top_pred_index = tf.argmax(preds[0])
            top_class_channel = preds[:, top_pred_index]

        # This is the gradient of the top predicted class with regard to
        # the output feature map of the last conv layer
        grads = tape.gradient(top_class_channel, last_conv_layer_output)

        # This is a vector where each entry is the mean intensity of the gradient
        # over a specific feature map channel
        pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))

        last_conv_layer_output = last_conv_layer_output.numpy()[0]
        pooled_grads = pooled_grads.numpy()
        # Sort filters by contribution and return list of (layer, filter index, gradient) tuples
        top_pooled_grads = [(last_conv_layer.name, i, pooled_grads[i])
                            for i in (np.abs(pooled_grads)).argsort()[-self.no_filters:]]

        return top_pooled_grads

CharacterisationMapper

Abstract class that provides a mapping from influence graph nodes to relations, based on an established argumentation framework.

Abstract Class

1
2
3
4
5
6
7
8
class CharacterisationMapper(ExtractorMapper):

    def __init__(self, *args, **kwargs):
        super().__init__()

    @abstractmethod
    def apply(self, u, v):
        pass

Implementation Example

1
2
3
4
5
6
7
8
from argflow.gaf.frameworks import BipolarFramework

class ConvolutionalCharacterisationMapper(CharacterisationMapper):
    def __init__(self):
        super().__init__()

    def apply(self, u, v):
        return BipolarFramework.ATTACK if np.sign(u['grad']) != np.sign(v['grad']) else BipolarFramework.SUPPORT

StrengthMapper

Abstract class that provides a mapping from an influence graph node to a metric of strength, determining the magnitude of the influence it has on its children.

Abstract Class

1
2
3
4
5
6
7
8
class StrengthMapper(ExtractorMapper):

    def __init__(self, *args, **kwargs):
        super().__init__()

    @abstractmethod
    def apply(self, node):
        pass

Implementation Example

1
2
3
4
5
6
class ConvolutionalStrengthMapper(StrengthMapper):
    def __init__(self):
        super().__init__()

    def apply(self, node):
        return np.abs(node['grad']) if 'grad' in node else None

Default Concrete Implementations

1
from argflow.gaf import default_mappers

DefaultConvolutionalInfluenceMapper

Generates a 3-layer influence graph: the input influences the final convolutional layer which influences the output.

DefaultConvolutionalCharacterisationMapper

Generates a characterisation from the BipolarFramework: SUPPORT if the gradient at the output respect to the feature is positive, ATTACK otherwise.

DefaultConvolutionalStrengthMapper

Assigns a strength equal to the magnitude of the gradient at the output with respect to the feature.

Example usage

Demo using standard mappers on VGG16.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import numpy as np

from PIL import Image
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions

from argflow.influence import InfluenceGraph
from argflow.gaf import GAFExtractor, PayloadType, Payload
from argflow.gaf.frameworks import BipolarFramework
from argflow.gaf.default_mappers import (DefaultConvolutionalInfluenceMapper,
                                         DefaultConvolutionalStrengthMapper,
                                         DefaultConvolutionalCharacterisationMapper)
from argflow.chi.cnn import GradCAM
from argflow.chi import Chi
from argflow.portal import Writer

if __name__ == '__main__':
    # Init model and writer
    model = VGG16(weights='imagenet')
    summaries = Writer('/path/to/resource/dir', 'CNN Demo')

    # Load input
    img_path = 'image.jpg'
    img = image.load_img(img_path, target_size=(224, 224))
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)

    # Init mappers
    im = DefaultConvolutionalInfluenceMapper(10, decode_predictions)
    cm = DefaultConvolutionalCharacterisationMapper()
    sm = DefaultConvolutionalStrengthMapper()

    # Extract GAF and send to portal
    extractor = GAFExtractor(
        im, sm, cm, GradCAM())
    gaf = extractor.extract(model, x)
    summaries.write_gaf(gaf)