卷积神经网络的特征图可视化秘籍——PyTorch实现
可视化的定义及步骤
这里所说的可视化是指对卷积神经网络中间层的输出特征图进行可视化,比如将网络第八层的输出特征图保存为图像显示出来。那么,我们实际上要做的事情非常简单,分为如下两步:
【1】搭建网络模型,并将数据输入到网络之中;
【2】提取想可视化层的输出特征图,并将其按每个channel都保存为一张图像的方式进行可视化,其原因在于有几个channel就代表了该层输出有几张特征图(也代表了该层的卷积核数量)。
PyTorch实现
以预训练好的VGG16为例进行可视化
下面提供了PyTorch实现的从在ImageNet上预训练好的VGG16中可视化第一层输出的代码,该代码参考了PyTorch|提取神经网络中间层特征进行可视化的实现,实现思路及所做的修改如下:
- 在基于
ImageNet
预训练的VGG16
网络上,处理单张图像作为网络的输入,对该图像进行的归一化处理以ImageNet
图像的标准处理方式进行。 - 根据给定的可视化层序号,获取该层的输出特征图,注意返回的是一个
[1,channels,width,height]
的四维张量。 - 将每一个
channel
的结果即[width,height]
的二维张量都保存为一张图像,那么该层的输出特征图一共有channels
个[width,height]
的灰度图像(单通道图像)。 - 在对输入图像的处理上,采用了
ImageNet
图像的归一化方式,得到的图像像素值分布区间为[-2.7,2.1]
之间,而不是熟悉的[-1,1]
或是[0,1]
。在保存单个channel的特征图时,保存函数又需要输出特征图像素分布为[0,255]
或是[0,1]
。那么在这里,采用了最大最小比例放缩的方法将输出特征图的像素值分布区间转化到了[0,1]
,而没有像上述链接一样使用Sigmoid来将像素值分布区间转化为[0,1]
。笔者认为采用最大最小比例放缩的方法更加合理,另外需要注意添加一个1e-5
来防止分母为0的情况。
import cv2
import numpy as np
import torch
from torch.autograd import Variable
from torchvision import models
import os
# 该函数创建保存特征图的文件目录,以网络层号命名文件夹,如feature\\1\\..文件夹中保存的是模型第二层的输出特征图
def mkdir(path):
isExists = os.path.exists(path) # 判断路径是否存在,若存在则返回True,若不存在则返回False
if not isExists: # 如果不存在则创建目录
os.makedirs(path)
return True
else:
return False
# 图像预处理函数,将图像转换成[224,224]大小,并进行Normalize,返回[1,3,224,224]的四维张量
def preprocess_image(cv2im, resize_im=True):
# 在ImageNet100万张图像上计算得到的图像的均值和标准差,它会使图像像素值大小在[-2.7,2.1]之间,但是整体图像像素值的分布会是标准正态分布(均值为0,方差为1)
# 之所以使用这种方法,是因为这是基于ImageNet的预训练VGG16对输入图像的要求
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
# 改变图像大小并进行Normalize
if resize_im:
cv2im = cv2.resize(cv2im, dsize=(224,224),interpolation=cv2.INTER_CUBIC)
im_as_arr = np.float32(cv2im)
im_as_arr = np.ascontiguousarray(im_as_arr[..., ::-1])
im_as_arr = im_as_arr.transpose(2, 0, 1) # 将[W,H,C]的次序改变为[C,W,H]
for channel, _ in enumerate(im_as_arr): # 进行在ImageNet上预训练的VGG16要求的ImageNet输入图像的Normalize
im_as_arr[channel] /= 255
im_as_arr[channel] -= mean[channel]
im_as_arr[channel] /= std[channel]