lian jie: http://www.shwley.com/index.php/archives/86/
最近想看一看卷积神经网络中各层的卷积结果,但在网上搜索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, height
和width
分别是该层结果的高和宽,即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()