如何使用
在Extractor类中,模型参数接受模型,而DS_layer_name参数是可选的。DS_layer_name参数用于查找下采样层,通常在resnet层中名称为“downsample”,因此它保持为默认值。
extractor = Extractor(model = resnet, DS_layer_name = 'downsample')
extractor.activate())是激活程序。extractor.info()可以查看里面的方法
{'Down-sample layers name': 'downsample', 'Total CNN Layers': 49, 'Total Sequential Layers': 4, 'Total Downsampling Layers': 4, 'Total Linear Layers': 1, 'Total number of Bottleneck and Basicblock': 16, 'Total Execution time': '0.00137 sec'}
访问权重和层
extractor.CNN_layers -----> Gives all the CNN layers in a model extractor.Linear_layers --> Gives all the Linear layers in a model extractor.DS_layers ------> Gives all the Down-sample layers in a model if there are any extractor.CNN_weights ----> Gives all the CNN layer's weights in a model extractor.Linear_weights -> Gives all the Linear layer's weights in a model
没有任何编码,你可以得到CNN和线性层和他们的权值在几乎每个resnet模型。下面是类的方法
def activate(self): """Activates the algorithm""" start = time.time() self.__Layer_Extractor(self.model_children) self.__Verbose() self.__ex_time = str(round(time.time() - start, 5)) + ' sec' def __Append(self, layer, Linear=False): """ This function will append the layers weights and the layer itself to the appropriate variables params: layer: takes in CNN or Linear layer returns: None """ if Linear: self.Linear_weights.append(layer.weight) self.Linear_layers.append(layer) else: self.CNN_weights.append(layer.weight) self.CNN_layers.append(layer) def __Layer_Extractor(self, layers): """ This function(algorithm) finds CNN and linear layer in a Sequential layer params: layers: takes in either CNN or Sequential or linear layer return: None """ for x in range(len(layers)): if type(layers[x]) == nn.Sequential: # Calling the fn to loop through the layer to get CNN layer self.__Layer_Extractor(layers[x]) self.__no_sq_layers += 1 if type(layers[x]) == nn.Conv2d: self.__Append(layers[x]) if type(layers[x]) == nn.Linear: self.__Append(layers[x], True) # This statement makes sure to get the down-sampling layer in the model if self.DS_layer_name in layers[x]._modules.keys(): self.DS_layers.append(layers[x]._modules[self.DS_layer_name]) # The below statement will loop throgh the containers and append it if isinstance(layers[x], (self.__bottleneck, self.__basicblock)): self.__no_containers += 1 for child in layers[x].children(): if type(child) == nn.Conv2d: self.__Append(child)
可视化
卷积层
在这里,我们将可视化卷积层过滤器。为了简单起见,我们将只可视化第一个卷积层的过滤器。
# Visualising the filters plt.figure(figsize=(35, 35)) for index, filter in enumerate(extractor.CNN_weights[0]): plt.subplot(8, 8, index + 1) plt.imshow(filter[0, :, :].detach(), cmap='gray') plt.axis('off') plt.show()
第一层的滤波器尺寸为7×7,有64个通道(隐藏层)。
来自训练好的resnet-50模型的7×7个过滤器
每个小方框的像素值在0到255之间。0是完全黑的255是白色的。范围可以是不同的,比如0到1或-1到1,以0为平均值。
特征映射
为了使特征映射形象化,首先需要将图像转换为张量图像。利用torchvision的变换,可以将图像归一化并转换为张量。
# Filter Map img = cv.cvtColor(cv.imread('filtermap/5.png'), cv.COLOR_BGR2RGB) #transforms is imported as t img = t.Compose([ t.ToPILImage(), t.Resize((128, 128)), # t.Grayscale(), t.ToTensor(), t.Normalize(0.5, 0.5)])(img).unsqueeze(0) #LAST LINE t.Normalize(0.5, 0.5)])(img).unsqueeze(0)
变换后的最后一行表示将变换应用于图像。您可以创建一个新变量,然后应用它,但是一定要更改变量名。unsqueze(0)是给张量img增加一个额外的维数。添加批处理维度是一个重要步骤。现在图像的大小不是[3,128,128],而是[1,3,128,128],这表示批处理中只有一个图像。
将图像输入每个卷积层
下面的代码将图像通过每个卷积层。
featuremaps = [extractor.CNN_layers [0] (img)] len(extractor.CNN_layers): featuremaps.append (extractor.CNN_layers [x] (featuremaps [1]))
我们将首先把图像作为输入,传递给第一个卷积层。在此之后,我们将使用for循环将最后一层的输出传递给下一层,直到到达最后一个卷积层。
- 在第1行,我们将图像作为第一个卷积层的输入。
- 然后我们使用for循环从第二层循环到最后一层卷积。
- 我们将最后一层的输出作为下一个卷积层的输入(featuremaps[-1])。
- 另外,我们将每个层的输出附加到featuremaps列表中。
特征的可视化
这是最后一步。我们将编写代码来可视化特征映射。注意,最后的cnn层有很多feature map,范围在512到2048之间。但是我们将只从每一层可视化64个特征图,否则将使输出真正地混乱。
# Visualising the featuremaps for x in range(len(featuremaps)): plt.figure(figsize=(30, 30)) layers = featuremaps[x][0, :, :, :].detach() for i, filter in enumerate(layers): if i == 64: break plt.subplot(8, 8, i + 1) plt.imshow(filter, cmap='gray') plt.axis('off') # plt.savefig('featuremap%s.png'%(x)) plt.show()
- 从第2行开始,我们遍历feature remaps。
- 然后我们得到的层是
featuremaps[x][0,:,:,:].detach()
。 - 从第5行开始,我们遍历每个层中的过滤器。如果它是第64个特征图,我们就跳出了循环。
- 在此之后,我们绘制feature map,并在必要时保存它们。
结果
可以看到,在创建图像的feature map时,不同的滤镜聚焦于不同的方面。
一些特征地图聚焦于图像的背景。另一些人则创建了图像的轮廓。一些滤镜创建的特征地图,背景是黑暗的,但图像的脸是明亮的。这是由于过滤器的相应权重。从上面的图像可以很清楚地看出,在深层,神经网络可以看到输入图像非常详细的特征图。
让我们看看其他一些特征。
ResNet-50模型的第20和第10卷积层的Feature map
ResNet-50模型第40和第30卷积层的Feature map
你可以观察到,当图像通过层的进展,细节从图像慢慢消失。它们看起来像噪音,但在这些特征地图中肯定有一种模式是人眼无法察觉的,但神经网络可以。
当图像到达最后的卷积层时,人类就不可能知道那是什么了。最后一层输出对于最后的全连接的神经元非常重要,这些神经元基本上构成了卷积神经网络的分类层。