In [1]:
from emu.torch import TorchAdapter
import numpy as np
import os

In [2]:
def recursivelistims(path):
    """Function to load images [.jpg, .jpeg, .png] from a given path
    """
    l = []
    if os.path.isfile(path):
        return [path]
    else:
        for dirpath, dirnames, filenames in os.walk(path):
            for fname in filenames:
                if fname.lower().endswith('.jpg') or fname.lower().endswith('.jpeg') or fname.lower().endswith(
                        '.png'):
                    l.append(os.path.join(dirpath, fname))
    return l

Define properties of input data


In [3]:
# Color-channel-Mean of training data
mean = np.array([0.485, 0.456, 0.406])
# Color-channel-Standard deviation of training data
std = np.array([0.229, 0.224, 0.225])
# Expected image input size of the neural network
#  (Channels, Height, Width)
inputsize = (3, 224, 224)

Load neural network


In [4]:
#  from a Torch7 model file
# nn = TorchAdapter('convnets/mymodel.t7', mean=mean, std=std, inputsize=inputsize, use_gpu=True)

#  from the pytorch model zoo
nn = TorchAdapter('alexnet', mean=mean, std=std, inputsize=inputsize, use_gpu=True)


Loading model alexnet from pytorch model zoo

List layers included in network


In [5]:
# Let's list what layers are in the network
layers = nn.get_layers()
for identifier, layertype in layers.items():
    print('%s: %s' % (identifier, layertype))


features: Sequential
features.0: Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
features.1: ReLU (inplace)
features.2: MaxPool2d (size=(3, 3), stride=(2, 2), dilation=(1, 1))
features.3: Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
features.4: ReLU (inplace)
features.5: MaxPool2d (size=(3, 3), stride=(2, 2), dilation=(1, 1))
features.6: Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
features.7: ReLU (inplace)
features.8: Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
features.9: ReLU (inplace)
features.10: Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
features.11: ReLU (inplace)
features.12: MaxPool2d (size=(3, 3), stride=(2, 2), dilation=(1, 1))
classifier: Sequential
classifier.0: Dropout (p = 0.5)
classifier.1: Linear (9216 -> 4096)
classifier.2: ReLU (inplace)
classifier.3: Dropout (p = 0.5)
classifier.4: Linear (4096 -> 4096)
classifier.5: ReLU (inplace)
classifier.6: Linear (4096 -> 1000)

In [6]:
# We are only interested in the layer with parameters:
filtered = [identifier for identifier, layertype in layers.items() if 'Conv' in layertype or 'Linear' in layertype]
print(filtered)


['features.0', 'features.3', 'features.6', 'features.8', 'features.10', 'classifier.1', 'classifier.4', 'classifier.6']

Reload neural network to make it keep layers of interest


In [7]:
# from a Torch7 model file
#  note that we set the keyword argument `keep_outputs` which makes the adapter save all the outputs from a forward call
nn = TorchAdapter('alexnet', mean=mean, std=std, inputsize=inputsize, keep_outputs=filtered, use_gpu=True)


Loading model alexnet from pytorch model zoo

Find images, preprocess and evaluate them


In [8]:
# Find images
imagefiles = recursivelistims('MSCOCO/test2014/')
print('Number of images found: %s' % len(imagefiles))
# Lets limit that to 1000 images
imagefiles = imagefiles[:1000]


Number of images found: 40775

In [9]:
# Preprocess
#  NNAdapter takes care of loading and normalization and returns a 4d-numpy array
images = nn.preprocess(imagefiles)
print('Image tensor shape: %s'%str(images.shape))


Image tensor shape: (1000, 3, 224, 224)

In [10]:
# Alternatively for evaluating very large sets of images:
#  Loading e.g. 40775 images at once takes way too long and consumes too much memory
#  We can load them in batched fashion

# for bi in range(0, len(images), batchsize):
#     batch = nn.preprocess(images[bi:(bi + batchsize)])
#     nn.forward(batch)

Analyzing layer activities/outputs


In [11]:
output_by_layer = {}
for layer in filtered:
    output_by_layer[layer] = []

batchsize = 480
    
for bi in range(0, len(images), batchsize):
    batch = images[bi:(bi + batchsize)]
    nn.forward(batch)
    
    for layer in filtered:
        o = nn.get_layeroutput(layer)
        output_by_layer[layer].append(o)
    
# Concatenate the batch-outputs
for layer in filtered:
    output_by_layer[layer] = np.concatenate(output_by_layer[layer])
    print('%s output shape: %s' % (layer, str(output_by_layer[layer].shape)))


features.0 output shape: (1000, 64, 55, 55)
features.3 output shape: (1000, 192, 27, 27)
features.6 output shape: (1000, 384, 13, 13)
features.8 output shape: (1000, 256, 13, 13)
features.10 output shape: (1000, 256, 13, 13)
classifier.1 output shape: (1000, 4096)
classifier.4 output shape: (1000, 4096)
classifier.6 output shape: (1000, 1000)

Lesion layers

NNAdapter allows you to access and manipulate the parameters of layers


In [12]:
# Access
# e.g. 1st layer
weights, bias = nn.get_layerparams(filtered[0])
print('Shape of weight of layer %s: %s' % (filtered[0], str(weights.shape)))
print('Shape of bias of layer %s: %s' % (filtered[0], str(bias.shape)))

# Output of network for first image:
o = nn.forward(batch[1][np.newaxis, ...])
print('Predicted class: %d' % np.argmax(o))


Shape of weight of layer features.0: (64, 3, 11, 11)
Shape of bias of layer features.0: (64,)
Predicted class: 537

In [13]:
# Alter
#  Set weights to zero
weights.fill(0)
nn.set_weights(filtered[0], weights)

# Output of network for first image:
o = nn.forward(batch[1][np.newaxis, ...])
print('Predicted class: %d' % np.argmax(o))


Predicted class: 783