Search code examples
pythonkerasconv-neural-network

Adding visualization for every layer activation layer of Convolution Network - Keras


I have a Convolution Network (CNN) as followed. I would like to add visualization for every layer activation layer as in the imageere

There are several layer of the CNN that are doing the required task. I only want to probe the output of each layer.

def get_model():
    input_shape = (IMG_MODE, img_rows, img_cols)
    model = Sequential()

    model.add(ZeroPadding2D(padding=(1,1), input_shape=input_shape))
    model.add(Conv2D(32, (3, 3), padding = 'valid'))
    model.add(LeakyReLU(alpha=0.01))
    model.add(MaxPooling2D(pool_size=pool_size2))

    ....

    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))

    print(model.summary())
    return model

The code output:

_________________________________________________________________


   Layer (type)                 Output Shape              Param #   
    =================================================================
    zero_padding2d_1 (ZeroPaddin (None, 1, 114, 94)        0         
    _________________________________________________________________
    conv2d_1 (Conv2D)            (None, 32, 112, 92)       320       
    _________________________________________________________________
    leaky_re_lu_1 (LeakyReLU)    (None, 32, 112, 92)       0         
    _________________________________________________________________
    max_pooling2d_1 (MaxPooling2 (None, 32, 56, 46)        0         
    _________________________________________________________________
    ....        
    _________________________________________________________________
    dense_1 (Dense)              (None, 1024)              8258560   
    _________________________________________________________________
    leaky_re_lu_4 (LeakyReLU)    (None, 1024)              0         
    _________________________________________________________________
    dropout_1 (Dropout)          (None, 1024)              0         
    _________________________________________________________________
    dense_2 (Dense)              (None, 40)                41000     
    _________________________________________________________________
    activation_1 (Activation)    (None, 40)                0         
    =================================================================
    Total params: 8,392,232
    Trainable params: 8,392,232
    Non-trainable params: 0
    _________________________________________________________________
    None
    Train on 320 samples, validate on 80 samples
    Epoch 1/20
     - 18s - loss: 3.7036 - acc: 0.0187 - val_loss: 3.6824 - val_acc: 0.0250
    Epoch 2/20
     - 17s - loss: 3.6903 - acc: 0.0250 - val_loss: 3.6786 - val_acc: 0.0250
    ...
    Epoch 20/20
     - 17s - loss: 0.2067 - acc: 0.9312 - val_loss: 0.9892 - val_acc: 0.7625
    Test score: 0.9891735315322876
    Test accuracy: 0.7625

I tried to use the following code to do my task:

 import matplotlib.pyplot as plt
    from keras import models
    layer_outputs = [layer.output for layer in model.layers[:8]]
    activation_model = models.Model(inputs=model.input, outputs=layer_outputs)

    activations = activation_model.predict(img_tensor)

    import matplotlib.pyplot as plt
    plt.matshow(first_layer_activation[0, :, :, 7], cmap='viridis')
    layer_names = []
    for layer in model.layers[:8]:
    layer_names.append(layer.name)
    images_per_row = 16
    for layer_name, layer_activation in zip(layer_names, activations):
    n_features = layer_activation.shape[-1]
    size = layer_activation.shape[1]
    n_cols = n_features // images_per_row
    display_grid = np.zeros((size * n_cols, images_per_row * size))
    for col in range(n_cols):
    for row in range(images_per_row):
    channel_image = layer_activation[0,
    :, :,
    col * images_per_row + row]
    channel_image -= channel_image.mean()
    channel_image /= channel_image.std()
    channel_image *= 64
    channel_image += 128
    channel_image = np.clip(channel_image, 0, 255).astype('uint8')
    display_grid[col * size : (col + 1) * size,
    row * size : (row + 1) * size] = channel_image
    scale = 1. / size
    plt.figure(figsize=(scale * display_grid.shape[1],
    scale * display_grid.shape[0]))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')

Solution

  • Below is a general approach for getting outputs from convolutional layers of any model and visualising them. Here the TensorFlow version of Keras is used; equivalent code for other implementations of Keras may differ slightly.

    Firstly a function is required to get the output of a convolutional layer of a model:

    #Setting index = -1 means we select the last convolutional layer
    def get_conv_output(model, index = -1): 
        layers = model.layers
        result = [layer.output for layer in layers 
                 if type(layer) is tf.keras.layers.Conv2D][index]
            
        return result
    

    This output will be a four-dimensional tensor of shape (batch_size, height, width, number_of_channels). Note: In the case of evaluation on a single input image to the network, batch_size is 1.

    Next we need a function which constructs a grid of the activation maps of a convolutional layer for a single data element (single input image). It will construct a nearly square image which will be a grid of these maps:

    def maps_to_grid(output):
        #Calculate the number of rows and columns needed to arrange
        #the activation maps into a nearly-square grid. The number of 
        #maps is the number of channels in the convolutional layer output
        num_maps = int(output.shape[-1])
        num_columns = math.ceil(num_maps ** 0.5)
        num_rows = math.ceil(num_maps / num_columns)
        
        #The end of the set of activation maps may need to be padded 
        #with zeroes to fit it into the grid
        num_zeros = num_rows * num_columns - num_maps 
        zeros_shape = [int(i) for i in output.shape]
        zeros_shape[-1] = num_zeros
        zeros = tf.zeros(zeros_shape,
                         dtype = tf.float32,
                         name = None)
        
        #Pad the activation maps with zeroes, concatenating along the
        #channels dimension
        padded_output = tf.concat([output, zeros], -1)
        len, width, depth = [s for s in padded_output.shape]
        
        #Unstack the padded activation maps and construct the grid
        map_stack = tf.unstack(padded_output, axis = 2)
        row_stacks = [tf.concat(map_stack[i : i + num_columns], axis = 1) 
                      for i in range(0, num_columns * num_rows, num_columns)]
        result = tf.concat(row_stacks, axis = 0)
            
        return result 
    

    Once you have these functions you can get the grid as follows:

    activation_map_grid_tensor = maps_to_grid(get_conv_output(model)[0])
    

    Index 0 is required as maps_to_grid works with activation maps for single image, so we pick first element of the batch. Now you can evaluate the tensor, and show it with e.g. cv2.imshow()

    This approach is taken from https://github.com/cyberneuron/RT-CNN-Vis which is a platform for CNN visualisation. One may also find it easier to get the code from there directly.