caffe学习笔记--实现CNN各层特征可视化

        最近在阅读CNN visual convolution network论文,希望能使用caffe实现CNN的各层特征可视化,而不仅限于绘制网络结构图或者损失曲线图,所以查阅caffe官方教程和相关博客后,对caffe自带的分类例程00-classification.ipynb进行了测试运行,下面进行一些程序的讲解,希望能加强理解。

PS:caffe实现可视化借助jupyter notebook 这样强大的网页工具。在Ubuntu系统终端输入jupyter notebook就可以进入。

因为下面内容(程序+注释)较多,所以先在此贴出代码示例程序caffe官方教程的链接:

http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb  

http://caffe.berkeleyvision.org/

1、steup

-加载numpymatplotlib     First, set up Python, numpy, and matplotlib.-

# set up Python environment: numpy for numerical routines, and matplotlib for plotting
import numpy as np    ##加载numpy
import matplotlib.pyplot as plt    ##加载matplotlib
# display plots in this notebook   ##在notebook中显示
%matplotlib inline

# set display defaults     ##设置默认参数
plt.rcParams['figure.figsize'] = (10, 10)        # large images   图像尺寸
##显示临近差值,像素为正方形
plt.rcParams['image.interpolation'] = 'nearest'  # don't interpolate: show square pixels
##使用灰度化输出  而不是彩色输出
plt.rcParams['image.cmap'] = 'gray'  # use grayscale output rather than a (potentially misleading) color heatmap

加载caffe

# The caffe module needs to be on the Python path;
#  we'll add it here explicitly.
import sys
####sys为python的内置模块,提供了很多函数和变量来处理Python运行时环境的不同部分。还有一点,可以
##这个东西到实现web跟Python之间的交互。
caffe_root = '/home/ren_dong/caffe/'  # this file should be run from    本处改为了绝对路径

{caffe_root}/examples (otherwise change this line)
sys.path.insert(0, caffe_root + 'python')    ### 加载caffe的python模块

import caffe     ##加载caffe
# If you get "No module named _caffe", either you have not built pycaffe or you have the wrong path.

load  caffenet.model

import os  ##os进行遍历
if os.path.isfile(caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'):
    print 'CaffeNet found.'
else:
    print 'Downloading pre-trained CaffeNet model...'
    !../scripts/download_model_binary.py ../models/bvlc_reference_caffenet
#####推荐事先下载好caffenet.model,不然调用网络下载太慢了。

此处给出一个model的下载链接:https://download.csdn.net/download/qq_29893385/10567420。

2、加载网络并设置输入预处理

      2.1 将Caffe设置为CPU模式,并从硬盘加载网络。

caffe.set_mode_cpu()    ###设为CPU模式

model_def = caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt'  ##配置文件
model_weights = caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'   ###weights文件

net = caffe.Net(model_def,      # defines the structure of the model 定义网络结构
                model_weights,  # contains the trained weights   训练权重
                caffe.TEST)     
# use test mode (e.g., don't perform dropout) 使用测试模式,训练模式没有dropout

2.2 设置输入预处理

# load the mean ImageNet image (as distributed with Caffe) for subtraction
#### 加载ImageNet训练集的图像均值,预处理需要减去均值
# ilsvrc_2012_mean.npy文件是numpy格式,其数据维度是(3L, 256L, 256L)
mu = np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy')  # 加载均值文件
mu = mu.mean(1).mean(1)  # average over pixels to obtain the mean (BGR) pixel values   
# 对所有像素值取平均以此获取BGR的均值像素值
print 'mean-subtracted values:', zip('BGR', mu)
# 取平均后得到BGR均值分别是[104.00698793,116.66876762,122.67891434]


# 对输入数据进行变换
# caffe.io.transformer是一个类,实体化的时候构造函数__init__(self, inputs)给一个初值
# 其中net.blobs本身是一个字典,每一个key对应每一层的名字,#net.blobs['data'].data.shape计算结果为(10, 3, 227, 227)
# create transformer for the input called 'data'
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})

 #将图像的通道数设置为outermost的维数   # move image channels to outermost dimension
transformer.set_transpose('data', (2,0,1)) 
# 每个通道减去均值             # subtract the dataset-mean value in each  channel
transformer.set_mean('data', mu)   
# 像素值从[0,1]变换为[0,255]         # rescale from [0, 1] to [0, 255]
transformer.set_raw_scale('data', 255)     
# 交换通道,RGB->BGR                 # swap channels from RGB to BGR
transformer.set_channel_swap('data', (2,1,0))  

3、CPU分类

载入图片

# set the size of the input (we can skip this if we're happy
#  with the default; we can also change it later, e.g., for different batch sizes)
net.blobs['data'].reshape(50,        # batch size     批处理数量为50
                          3,         # 3-channel (BGR) images   3通道彩色图
                          227, 227)  # image size is 227x227    图像尺寸227x227

image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg')   ###选择图片
transformed_image = transformer.preprocess('data', image)  #### 按照之前设置进行预处理
plt.imshow(image)  ###imshow

 caffe学习笔记--实现CNN各层特征可视化_第1张图片

3.2  识别分类  输出结果

###copy the image data into the memory allocated for the net
#### 将图像数据拷贝到为net分配的内存中
net.blobs['data'].data[...] = transformed_image

### perform classification   前向传播进行分类
#### 前向传播,跑一遍网络,默认结果为最后一层的blob(也可以指定某一中间层),赋给output
output = net.forward()

output_prob = output['prob'][0]  
# the output probability vector for the first image in the batch 
# 取batch中第一张图像的概率值

print 'predicted class is:', output_prob.argmax()


###输出分类为281 

  predicted class is: 281  

网络输出是一个概率向量,最可能的类别是第281个类别。但是结果是否正确呢,需要查看一下ImageNet的标签。

# load ImageNet labels   标签文件synset_words.txt
labels_file = caffe_root + 'data/ilsvrc12/synset_words.txt'
if not os.path.exists(labels_file):
    !../data/ilsvrc12/get_ilsvrc_aux.sh
# 读取纯文本数据,三个参数分别是文件地址、数据类型和数据分隔符,保存为字典格式    
labels = np.loadtxt(labels_file, str, delimiter='\t')

print 'output label:', labels[output_prob.argmax()]    ###输出label
output label: n02123045 tabby, tabby cat
# sort top five predictions from softmax output 
# 从softmax output可查看置信度最高的五个结果
top_inds = output_prob.argsort()[::-1][:5] 
 # reverse sort and take five largest items   # 逆序排列,取前五个最大值

print 'probabilities and labels:'
zip(output_prob[top_inds], labels[top_inds])
probabilities and labels:
[(0.31243637, 'n02123045 tabby, tabby cat'),
 (0.2379719, 'n02123159 tiger cat'),
 (0.12387239, 'n02124075 Egyptian cat'),
 (0.10075711, 'n02119022 red fox, Vulpes vulpes'),
 (0.070957087, 'n02127052 lynx, catamount')]

因为呢我在自己的笔记本并没有进行GPU加速,所以CPU 和GPU 的速度对比就没有运行。直接调到下一步

5、测试网络的中间层输出

卷积神经网络不单单是一个黑盒子。我们接下来看看该模型的一些参数和一些中间输出。首先,我们来看下如何读取网络的结构(每层的名字以及相应层的参数)。

net.blob对应网络每一层数据,对于每一层,都是四个维度:(batch_size, channel_dim, height, width)。

###对于每一层,其结构构成为:(batch_size, channel_dim, height, width)。
# for each layer, show the output shape   ##输出每一层的shape
for layer_name, blob in net.blobs.iteritems():
    print layer_name + '\t' + str(blob.data.shape)
data	(50, 3, 227, 227)
conv1	(50, 96, 55, 55)
pool1	(50, 96, 27, 27)
norm1	(50, 96, 27, 27)
conv2	(50, 256, 27, 27)
pool2	(50, 256, 13, 13)
norm2	(50, 256, 13, 13)
conv3	(50, 384, 13, 13)
conv4	(50, 384, 13, 13)
conv5	(50, 256, 13, 13)
pool5	(50, 256, 6, 6)
fc6	(50, 4096)
fc7	(50, 4096)
fc8	(50, 1000)
prob	(50, 1000)

net.params对应网络中的参数(卷积核参数,全连接层参数等),有两个字典值,net.params[0]是权值(weights),net.params[1]是偏移量(biases),权值参数的维度表示是(output_channels, input_channels, filter_height, filter_width),偏移量参数的维度表示(output_channels,) 

##### 循环打印参数名称,权值参数和偏移量参数的维度
for layer_name, param in net.params.iteritems():
    print layer_name + '\t' + str(param[0].data.shape), str(param[1].data.shape)
conv1	(96, 3, 11, 11) (96,)
conv2	(256, 48, 5, 5) (256,)
conv3	(384, 256, 3, 3) (384,)
conv4	(384, 192, 3, 3) (384,)
conv5	(256, 192, 3, 3) (256,)
fc6	(4096, 9216) (4096,)
fc7	(4096, 4096) (4096,)
fc8	(1000, 4096) (1000,)

####定义辅助函数来进行可视化中间层特征
def vis_square(data):
    """Take an array of shape (n, height, width) or (n, height, width, 3)
       and visualize each (height, width) thing in a grid of size approx. sqrt(n) by sqrt(n)"""
    
    # normalize data for display   # 数据正则化
    data = (data - data.min()) / (data.max() - data.min())
    
    # force the number of filters to be square
    # 此处目的是将一个个滤波器按照正方形的样子排列
    # 先对shape[0]也就是滤波器数量取平方根,然后取大于等于该结果的正整数
    # 比如40个卷积核,则需要7*7的正方形格子(虽然填不满)
    n = int(np.ceil(np.sqrt(data.shape[0])))
    padding = (((0, n ** 2 - data.shape[0]),
               (0, 1), (0, 1))                 
# add some space between filters # 在相邻的卷积核之间加入0进行padding
               + ((0, 0),) * (data.ndim - 3)) 
 # don't pad the last dimension (if there is one)   如果最后最后一维是1不进行扩充
    data = np.pad(data, padding, mode='constant', constant_values=1)  
 # pad函数声明:pad(array, pad_width, mode, **kwargs),作用是把list在原维度上进行扩展;
    # pad_width是扩充参数,例如参数((3,2),(2,3));
    # 其中(3,2)为水平方向上,上面加3行,下面加2行;
    # (2,3)为垂直方向上,上面加2行,下面加3行;
    # constant是常数填充的意思。# pad with ones (white)
    
    # tile the filters into an image  # 将卷积核平铺成图片
    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); plt.axis('off')

conv1

# the parameters are a list of [weights, biases]
filters = net.params['conv1'][0].data  ### 选取conv1的卷积核权值参数
vis_square(filters.transpose(0, 2, 3, 1))  
### 调用函数显示
#filters.transpose(0, 2, 3, 1)对filters 4维数组进行位置对换,主要是为了将rgb放在最后一维。

caffe学习笔记--实现CNN各层特征可视化_第2张图片

上图为conv1的输出。 

 feat = net.blobs['conv1'].data[0, :36]  # 选取‘conv1’的blob数据,只选择前面36张图片
    vis_square(feat)
#net.blobs['conv1'].data[4, :36] 表示conv1层学习的feature map, 显示第4个crop image的top 36个feature map。

 caffe学习笔记--实现CNN各层特征可视化_第3张图片

上图为pool5的输出。

feat = net.blobs['pool5'].data[0]  # 选取pool5的第一个输出结果
    vis_square(feat)

 caffe学习笔记--实现CNN各层特征可视化_第4张图片

上图为第一个全连接层(fc6)的输出

接下来,我们将显示输出结果及直方图。

feat = net.blobs['fc6'].data[0]  # 选取fc6的输出数据,这是一个4096维的向量
plt.subplot(2, 1, 1)  # 创建2行1列的子图,现在是第1个子图
plt.plot(feat.flat)  # 平铺向量,图像显示其每一个值
plt.subplot(2, 1, 2)  # 现在是第2个子图
_ = plt.hist(feat.flat[feat.flat > 0], bins=100)  # 做直方图,总共100根条形
 plt.show() # 显示两张图表

 caffe学习笔记--实现CNN各层特征可视化_第5张图片

 上图为最终的概率输出,prob。

feat = net.blobs['prob'].data[0]    # 选取最后一层的输出结果
    plt.figure(figsize=(15, 3))  # 设置图像大小为(15,3),单位是英寸
    plt.plot(feat.flat)  # 平铺向量,图像显示其每一个值
    plt.show()  # 显示图表

caffe学习笔记--实现CNN各层特征可视化_第6张图片

 另外官方教程也给出了如何使用自己的图片进行测试,代码如下:

# download an image  下载图像
my_image_url = "..."  # paste your URL here  图像URL地址
# for example:
# my_image_url = "https://upload.wikimedia.org/wikipedia/commons/b/be/Orang_Utan%2C_Semenggok_Forest_Reserve%2C_Sarawak%2C_Borneo%2C_Malaysia.JPG"
!wget -O image.jpg $my_image_url

# transform it and copy it into the net  变换图像并将其拷贝到网络
image = caffe.io.load_image('image.jpg')
net.blobs['data'].data[...] = transformer.preprocess('data', image)

# perform classification  前向传播获取分类
net.forward()

# obtain the output probabilities    # 获取输出概率值
output_prob = net.blobs['prob'].data[0]

# sort top five predictions from softmax output
 # 将softmax的输出结果按照从大到小排序,并取前5名
top_inds = output_prob.argsort()[::-1][:5]

plt.imshow(image)

print 'probabilities and labels:'
zip(output_prob[top_inds], labels[top_inds])

小编最后并未选择使用自己的图片进行测试,有兴趣的小伙伴可以试下。

 

 

 

 

 

reference:

http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb

https://blog.csdn.net/jnulzl/article/details/52077915

https://blog.csdn.net/Jesse_Mx/article/details/58605385

https://blog.csdn.net/guoyilin/article/details/42873747

你可能感兴趣的:(caffe学习笔记--实现CNN各层特征可视化)