基于caffe的特征可视化

最近想看一看卷积神经网络中各层的卷积结果,但在网上搜索feature visualization并没能找到通俗易懂的内容。在caffe的官网教程中,有这么一个Instant Recognition with Caffe,参照它可以很快的做出可视化的结果。但感觉里面还是稍有些复杂,这里做了些简化,达到目的即可。

首先,import相关的包:

import caffe
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

利用配置文件载入训练好的模型:

 net = caffe.Classifier("./alexnet_deploy.prototxt","bvlc_alexnet.caffemodel", mean=np.float([104.0, 116.0, 122.0]), channel_swap=(2,1,0))

这里我使用的是alexnet模型。

caffe中的数据都是在blobs中存的,我们可以看一下其结构:

In [103]: net.blobs
Out[103]: OrderedDict([('data', <caffe._caffe.Blob object at 0x7f9831f36aa0>), ('conv1', <caffe._caffe.Blob object at 0x7f9831f369b0>), ('norm1', <caffe._caffe.Blob object at 0x7f9831f36e60>), ('pool1', <caffe._caffe.Blob object at 0x7f9831f36c80>), ('conv2', <caffe._caffe.Blob object at 0x7f9831f36c08>), ('norm2', <caffe._caffe.Blob object at 0x7f9831f36f50>), ('pool2', <caffe._caffe.Blob object at 0x7f9832080a28>), ('conv3', <caffe._caffe.Blob object at 0x7f98320809b0>), ('conv4', <caffe._caffe.Blob object at 0x7f9832080d70>), ('conv5', <caffe._caffe.Blob object at 0x7f9832080aa0>), ('pool5', <caffe._caffe.Blob object at 0x7f9832080848>), ('fc6', <caffe._caffe.Blob object at 0x7f9832080b90>), ('fc7', <caffe._caffe.Blob object at 0x7f9832080e60>), ('fc8', <caffe._caffe.Blob object at 0x7f98320807d0>), ('prob', <caffe._caffe.Blob object at 0x7f9832080de8>)])

可以看到输入的data,五层卷积+pooling,以及三层全连接fc,和最后的预测概率结果prob

然后,使用这个模型做预测:

net.predict([caffe.io.load_image("./image.jpg")])

这里会直接输出1000维的数组,每个值都是预测该类的概率。这预测结果不是重点,可以忽略。

跑了一遍预测之后,各层就有了具体的数据,我们就可以看每层的可视化结果了。比如,conv1层:

In [104]: net.blobs['conv1']
Out[104]: <caffe._caffe.Blob at 0x7f9832080b18>

In [105]: conv1 = net.blobs['conv1']

In [106]: conv1.
conv1.channels  conv1.data      conv1.height    conv1.reshape   
conv1.count     conv1.diff      conv1.num       conv1.width

可以看到conv1层有8个参数,其中channels是该层的卷积核的数量,即96, heightwidth分别是该层结果的高和宽,即55×55, num是patch的个数,是在模型的配置文件中alexnet_deploy.prototxt设置好的,这里是10, count是参数的总个数,本层有2904000个参数,diff是训练时使用的误差,这里不是训练,所以全部为0,data里包含了我们需要的数据,其维度为10×96×55×55,因为我们就识别了一张图片,所以只取第一个就可以了:

conv1_data = conv1.data[0,:]

要可视化就需要显示结果,可以使用下面的函数:

def vis_square(data, padsize=1, padval=0):
    data -= data.min()
    data /= data.max()
    n = int(np.ceil(np.sqrt(data.shape[0])))
    padding = ((0, n**2-data.shape[0]), (0, padsize), (0, padsize))+((0,0),)*(data.ndim-3)
    data = np.pad(data, padding, mode='constant', constant_values=(padval, padval))
    data = data.reshape((n,n)+data.shape[1:]).transpose((0,2,1,3)+tuple(range(4, data.ndim+1))) 
    data = data.reshape((n*data.shape[1], n*data.shape[3])+data.shape[4:])
    plt.imshow(data, cmap=cm.gray)

这个函数来自于caffe官方的文档,我仅在最后一行做了修改,添加了cmap=cm.gray,让结果显示为灰度,而不是彩色的。

先看下卷积第一层96个结果:

In [122]: vis_square(conv1_data, padval=1)

In [123]: plt.show()

结果如下,看灰度可能不太明显,可以删掉函数vis_square中最后一行的cmap=cm.gray,得到彩色的结果,看起来就明显多了。 灰度结果 彩色结果

看下第85个结果:

In [180]: res = conv1_data[84:85, :]

In [181]: vis_square(res, padval=1)

In [182]: plt.show()

第84个结果