Caffe学习——Imagenet分类

本文介绍如何使用Caffe进行ImagNet数据集的图像分类任务,包括Caffe安装配置、网络模型搭建与调用、图像预处理流程、模型前向传播计算等关键步骤。文中还详细展示了如何可视化不同层的卷积核与特征图,以及对比CPU与GPU的运行效率。

Caffe学习——Imagenet分类

1. Caffe安装

参考Alten Li的Caffe安装[1]。

2. Imagenet分类

代码来自Caffe的Notebook Examples[2]。在导入Caffe前,先在sys.path中插入Caffe的路径:examples文件夹下有子文件夹pycaffe(猜是安装Caffe时执行“make pycaffe”生成的文件夹),并没有python文件夹;python文件夹属于caffe文件夹的子文件夹。所以,如果把根文件夹设为'/home/parallel/caffe/',导入caffe也是有效的。

[python]  view plain
  1. import numpy as np  
  2. import matplotlib.pyplot as plt  
  3. %matplotlib inline  
  4.   
  5. # Make sure that caffe is on the python path:  
  6. caffe_root = '/home/parallel/caffe/examples/'  # this file is expected to be in {caffe_root}/examples  
  7. import sys  
  8. sys.path.insert(0, caffe_root + 'python')  
  9.   
  10. import caffe  
figure.figsize为图像的大小(分别为宽和高,单位为英寸);image.interpolation为'nearest'表示当屏幕像素和图像像素不一致时,并没有在像素间计算插入值(可以理解为过渡值),所以如果屏幕像素过高,只是复制距这个像素点最近的像素值,所以每个像素值看起来像是个小方块。默认imshow显示采用的cmap为'jet',这里用'gray',所以显示的图像都是灰度图像。

[python]  view plain
  1. plt.rcParams['figure.figsize'] = (1010)  
  2. plt.rcParams['image.interpolation'] = 'nearest'  
  3. plt.rcParams['image.cmap'] = 'gray'  
set_mode_cpu设置Caffe跑CPU模式。deploy.prototxt里定义了网络架构,后缀为.caffemodel为Caffe训练后得到的模型,最后定义网络的类型为'TEST',这时网络只有前向计算得到输出结果,没有反向计算调整caffe模型的参数。
[python]  view plain
  1. caffe.set_mode_cpu()  
  2. net = caffe.Net(caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt',  
  3.                 caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel',  
  4.                 caffe.TEST)  
io为Caffe的数据输入模块,Transformer初始化时将输入数据的索引设为'data',同时设置了输入数据(这里为图像)的大小。set_transpose负责将索引为'data'的输入数据做维度上的交换:如果原始图像为RGB图像,那么0对应R通道,1对应G通道,2对应B通道,(2,0,1)transpose后'data'变为BGR。mean.npy为像素的平均值,这个文件caffe可以自己生成(?:以后用到再说)。set_raw_scale将输入数据缩放为原来的255倍。(2,1,0)转置又将BGR图像转换为RGB图像。
[python]  view plain
  1. # input preprocessing: 'data' is the name of the input blob == net.inputs[0]  
  2. transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})  
  3. transformer.set_transpose('data', (2,0,1))  
  4. transformer.set_mean('data', np.load('/home/parallel/caffe/python/caffe/imagenet/ilsvrc_2012_mean.npy').mean(1).mean(1)) # mean pixel  
  5. transformer.set_raw_scale('data'255)  # the reference model operates on images in [0,255] range instead of [0,1]  
  6. transformer.set_channel_swap('data', (2,1,0))  # the reference model has channels in BGR order instead of RGB  
设置块大小为50,通道数为3,图像大小为227*227。然后用上面设置的transformer对加载的图像'cat.jpg'做预处理。

[python]  view plain
  1. # set net to batch size of 50  
  2. net.blobs['data'].reshape(50,3,227,227)  
  3. net.blobs['data'].data[...] = transformer.preprocess('data', caffe.io.load_image(caffe_root + 'images/cat.jpg'))  
前向计算输出结果给out。打印out中概率('prob')最大的索引,输入为50个图像为一组,这里应该是把'cat.jpg'复制了50张(从data[0]到data[49])。所以'prob'标签后面的[0]可以修改为49及以内的任何正整数。

[python]  view plain
  1. out = net.forward()  
  2. print("Predicted class is #{}.".format(out['prob'][0].argmax()))  
显示图像。imshow设置的'cmap'应该为'gray',不清楚为什么这里会显示彩色图像。

[python]  view plain
  1. plt.imshow(transformer.deprocess('data', net.blobs['data'].data[0]))  
加载groundtruch标签,存放在synset_words.txt里面。如果没有这个文件加载给labels,会执行get_ilsvrc_aux.sh批处理文件获取该文件。

[python]  view plain
  1. # load labels  
  2. imagenet_labels_filename = '/home/parallel/caffe/data/ilsvrc12/synset_words.txt'  
  3. try:  
  4.     labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t')  
  5. except:  
  6.     !../data/ilsvrc12/get_ilsvrc_aux.sh  
  7.     labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t')  
获取'prob'最大的6个预测索引,这里最大的概率对应的索引应该为281,对应的标签为'n02123045 tabby, tabby cat'。

[python]  view plain
  1. # sort top k predictions from softmax output  
  2. top_k = net.blobs['prob'].data[0].flatten().argsort()[-1:-6:-1]  
  3. print labels[top_k]  
设置为CPU模式,输出结果为最好的3次,每次都是1个前向计算(感觉3次输入1样,所以基本上没什么差异)。CPU每次前向计算花费的时间为6.45秒;
[python]  view plain
  1. # CPU mode  
  2. net.forward()  # call once for allocation  
  3. %timeit net.forward()  
  4.   
  5. 1 loops, best of 36.45 s per loop  
设置为GPU模式,同理得到每次前向计算花费的时间为0.233秒。

[python]  view plain
  1. # GPU mode  
  2. caffe.set_device(0)  
  3. caffe.set_mode_gpu()  
  4. net.forward()  # call once for allocation  
  5. %timeit net.forward()  
  6.   
  7. 1 loops, best of 3223 ms per loop  


网络架构的绘制使用graphviz[3]。


net.blobs里包含网络每块特征地图的字典索引k和对应内容v的大小,net.params训练后的网络参数[4]。
net.params为事先设置的参数尺寸(output channels, input channels, filter height filter width)。
根据net.params中的filter size来计算net.blobs的(batch size, output channels, height, width)。
data: 50张图片为1组输入,通道数为3(RGB),图像宽度和高度为227。
conv1:(227-11)/4+1=55
pool1 :(55-3)/2+1=27
conv2:(27-5+4)/1+1=27
pool2 :(27-3)/2+1=13
conv3:(13-3+2)/1+1=13
conv4:(13-3+2)/1+1=13
conv5:13
pool5 :(13-3)/2+1=6 

[python]  view plain
  1. [(k, v.data.shape) for k, v in net.blobs.items()]  
  2.   
  3. [('data', (503227227)),  
  4.  ('conv1', (50965555)),  
  5.  ('pool1', (50962727)),  
  6.  ('norm1', (50962727)),  
  7.  ('conv2', (502562727)),  
  8.  ('pool2', (502561313)),  
  9.  ('norm2', (502561313)),  
  10.  ('conv3', (503841313)),  
  11.  ('conv4', (503841313)),  
  12.  ('conv5', (502561313)),  
  13.  ('pool5', (5025666)),  
  14.  ('fc6', (504096)),  
  15.  ('fc7', (504096)),  
  16.  ('fc8', (501000)),  
  17.  ('prob', (501000))]  
  18.   
  19. [(k, v[0].data.shape) for k, v in net.params.items()]  
  20.   
  21. [('conv1', (9631111)),  
  22.  ('conv2', (2564855)),  
  23.  ('conv3', (38425633)),  
  24.  ('conv4', (38419233)),  
  25.  ('conv5', (25619233)),  
  26.  ('fc6', (40969216)),  
  27.  ('fc7', (40964096)),  
  28.  ('fc8', (10004096))]  

vis_square是把data归一化,然后把n个data放在同一张图片里显示出来。

params存储网络中间层的网络参数,conv1层的参数尺寸为(96, 3, 11, 11),params['conv1'][0].data为conv1的权重参数,filters转置后的尺寸为(96, 11, 11, 3),符合vis_square函数中data的定义(n, height, width, channels)。

[python]  view plain
  1. filters = net.params['conv1'][0].data  
  2. vis_square(filters.transpose(0231))  

conv2层的参数尺寸为(256,48,5,5),params['conv2'][0].data为conv2的权重参数,这里只显示其中的48个。注意到conv1的output channels为96,conv2的input channels为48,说明剩下的48个output channels都被丢弃了。

[python]  view plain
  1. filters = net.params['conv2'][0].data  
  2. vis_square(filters[:48].reshape(48**255))  


blobs存储网络中间层的输出数据,显示conv1层前36个特征图。根据blobs的定义,data[0, :36]应该是第1张图片的前36个output channels。

[python]  view plain
  1. feat = net.blobs['conv1'].data[0, :36]  
  2. vis_square(feat, padval=1)  

显示conv2层前36个特征图。

[python]  view plain
  1. feat = net.blobs['conv2'].data[0, :36]  
  2. vis_square(feat, padval=1)  
显示conv3层所有的特征图。
[python]  view plain
  1. feat = net.blobs['conv3'].data[0]  
  2. vis_square(feat, padval=0.5)  
显示conv4层所有的特征图。

[python]  view plain
  1. feat = net.blobs['conv4'].data[0]  
  2. vis_square(feat, padval=0.5)  
显示conv5层所有的特征图。
[python]  view plain
  1. feat = net.blobs['conv5'].data[0]  
  2. vis_square(feat, padval=0.5)  
显示pool5层所有的特征图。

[python]  view plain
  1. feat = net.blobs['pool5'].data[0]  
  2. vis_square(feat, padval=1)  


全连接层的params的结构为(output channels, input channels),所以fc6和fc7的直方图横轴都为4096。上面的图为全连接层4096个神经元的输出值,下面的图为对这些神经元的响应值的强度为直方图的输入,可知fc6大多数神经元的响应值都在40以下,和fc7相比,直方图的斜率的绝对值应该是偏小。也就是说全连接层前后相连,越往后神经元的响应值越不会均匀分布化,而通过神经元间的竞争所以最后一层全连接层'prob'产生的尖峰即为优胜的神经元,这个神经元对应的索引决定了对象属于哪一类。

[python]  view plain
  1. feat = net.blobs['fc6'].data[0]  
  2. plt.subplot(211)  
  3. plt.plot(feat.flat)  
  4. plt.subplot(212)  
  5. _ = plt.hist(feat.flat[feat.flat > 0], bins=100)  
[python]  view plain
  1. feat = net.blobs['fc7'].data[0]  
  2. plt.subplot(211)  
  3. plt.plot(feat.flat)  
  4. plt.subplot(212)  
  5. _ = plt.hist(feat.flat[feat.flat > 0], bins=100)  
[python]  view plain
  1. feat = net.blobs['prob'].data[0]  
  2. plt.plot(feat.flat)  




3. 参考链接


from:http://blog.youkuaiyun.com/shadow_guo/article/details/50359532
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值