I have a Convolution Network (CNN) as followed. I would like to add visualization for every layer activation layer as in the
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')
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.