您的位置:首页 > Web前端

Caffe官方教程翻译(5):Classification: Instant Recognition with Caffe

2018-02-27 21:53 736 查看

前言

最近打算重新跟着官方教程学习一下caffe,顺便也自己翻译了一下官方的文档。自己也做了一些标注,都用斜体标记出来了。中间可能额外还加了自己遇到的问题或是运行结果之类的。欢迎交流指正,拒绝喷子!

官方教程的原文链接:http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb

Classification: Instant Recognition with Caffe

在这个例子中我们将会使用由官方提供的CaffeNet(该网络结构由Krizhevsky提出)模型来进行图片分类。后面我们会比对其在CPU和GPU模式下的性能,然后再深入剖析一下网络的一些特征和输出结果。

1.准备工作

首先要装好python、numpy、matplotlib。

# 安装好Python环境:numpy用于数值计算用途,matplotlib用于画图
import numpy as np
import matplotlib.pyplot as plt
# display plots in this notebook
%matplotlib inline

# 设置默认显示参数
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

# Caffe需要保证在Python路径中,这里我们直接将它添加进Python路径
import sys

# caffe_root = '../'  # 这个文件应该从{caffe_root}/examples运行(也可自行修改这一行代码)
caffe_root = '/home/xhb/caffe/caffe/'
sys.path.insert(0, caffe_root + 'python')

import caffe
# 如果你遇到报错:"No module named _caffe",要不就是没有编译pycaffe,要不就是路径写错了


如果有必要,请到网上下载我们要用到的模型(CaffeNetAlexNet的一种变形)

import 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 found.


2.导入网络并设置预处理的输入

把Caffe设置为CPU模式并从硬盘中导入网络

caffe.set_mode_cpu()

model_def = caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt'
model_weights = caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'

net = caffe.Net(model_def,      # 定义了整个模型的结构
model_weights,  # 包含了训练好的权重参数
caffe.TEST)     # 使用测试模式 (举例来说, 不执行dropout)


设置输入预处理。(我们要用到Caffe的
caffe.io.Transformer
,但是这一步与Caffe的其他部分是相互独立的,所以任何浴池里代码都可以适用)。

我们的CaffeNet默认获取的图像格式是BGR格式的。各像素的灰度值范围是[0,255][0,255],并且每个像素都减去了IMageNet图像的均值。另外,通道维数等于第一维(outermost)的大小。

由于matplotlib加载图像的灰度值范围是[0,1][0,1],而且是以RGB格式读取图像,通道维数等于innermost的维数,所以我们需要在这里对图像格式做一下转换。

补充:可能有人不太清楚innermost和outermost那段是什么意思。举个例子会好理解一点:outermost:【3,255,255】;innermost:【255,255,3】,通道数是3,长和宽都是255。

# 导入ImageNet图像的军直观,并减掉它
mu = np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy')
mu = mu.mean(1).mean(1)# 求出对应BGR三个通道的像素均值
print 'mean-subtracted values:', zip('BGR', mu)

# 对输入数据进行转换
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})

transformer.set_transpose('data', (2,0,1))  # 将通道置换,从innermost转换成outermost
transformer.set_mean('data', mu)            # 在每个通道中减去数据集图片的均值
transformer.set_raw_scale('data', 255)      # 将图像灰度值范围从[0,1]转换到[0,255]
transformer.set_channel_swap('data', (2,1,0))# 交换通道,从RGB转到BGR


mean-subtracted values: [('B', 104.0069879317889), ('G', 116.66876761696767), ('R', 122.6789143406786)]


3.使用CP模式行分类

现在我们已经准备好进行分类了。尽管我们一次只能分类一张图片,我们会将batch_size设置为50,来进行演示。

# 设置输入图像的大小(如果觉得默认设置就可以,完全可以跳过这一步;当然也可以等会再改它,比如修改成不同的batch_size)
net.blobs['data'].reshape(50,        # batch size
3,         # 3-channels(BGR)
227, 227)  # 图像大小:227 × 227


加载一幅图像(使用CaffeNet自带的接口),并按照前面设置的方式进行图像预处理。

image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg')
transformed_image = transformer.preprocess('data', image)
plt.imshow(image)


<matplotlib.image.AxesImage at 0x7fa1301c4350>




补充:这里我们不妨停下来想想,读入的图像image和转换的图像transformed_image分别是BGR格式还是RGB格式?很简单,读入图像image是RGB格式,转换的图像transformed_image是BGR格式。

卡哇伊!!!接下来开始分类。

# 把图像数据拷贝到为net分配好的内存当中
net.blobs['data'].data[...] = transformed_image

# 进行分类
output = net.forward()

output_prob = output['prob'][0] # batch中第一张图像的输出概率向量
print 'predicted class is:', output_prob.argmax()


predicted class is: 281


这个网络的结果输出的是一堆概率值组成的向量;这里去的是其中概率最大的那个,也就是预测最有可能是第281类。但是这个真的是正确的嘛?让我们看看ImageNet中的标签…

# 导入ImageNet的标签
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()]


output label: n02123045 tabby, tabby cat


“Tabby cat”是正确的!但是让我再看看其他的比较高概率的可能结果(尽管概率不是最高的)

# 从softmax输出取出概率值最高的前五项
top_inds = output_prob.argsort()[::-1][:5] # 颠倒sort后的结果,并取最大的5个

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


probabilities and labels:
[(0.31243622, 'n02123045 tabby, tabby cat'),
(0.23797178, 'n02123159 tiger cat'),
(0.12387244, 'n02124075 Egyptian cat'),
(0.10075705, 'n02119022 red fox, Vulpes vulpes'),
(0.070957385, 'n02127052 lynx, catamount')]


好的,我们可以看出来,哪怕取那几个概率较低的结果,也还是有一定的合理性的。

切换到GPU模式

让我们看看分类花了多少时间,并将其与GPU模式下的用时对比。

%timeit net.forward()


1 loop, best of 3: 5.24 s per loop


嘛,还是要花一段时间的,即使batch只有50张图片。让我们切换到GPU模式。

caffe.set_device(0) # 如果我们有多个GPU,选取第一个
caffe.set_mode_gpu()
net.forward() # 在开始计时前先跑一次
%timeit net.forward()


10 loops, best of 3: 30.4 ms per loop


快了很多!

测试中间层的输出

这个网络不只是一个黑盒子;让我们看看这个网络中的一些参数和中间输出。

首先我们要看看如何根据每层的激活函数和参数的维数得到网络的结构。

对于每一层来说,我们可以看看激活层的结构:
[batch_size, channel_dim, height, width)


激活层都由
OrderedDict
net.blobs
类型定义。

# 每一层都打印出输出的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)


现在再来看看参数的shape。这些参数都是
OrderedDict
net.params
类型。我们需要根据索引来获得参数:
[0]
对应权重,
1
对应偏置。

这些参数参数的shape为:
(output_channels, input_channels, filter_height, filter_width)
(权重参数),和只有1维的
(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)"""
# 归一化
data = (data - data.min()) / (data.max() - data.min())

# 将滤波器的核转变为正方形
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, 0),) * (data.ndim - 3))  # don't pad the last dimension (if there is one)
data = np.pad(data, padding, mode='constant', constant_values=1)  # 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


# 参数列表是:[wights, bias]
filters = net.params['conv1'][0].data
vis_square(filters.transpose(0,2,3,1))




第一层的输出,
conv1
(使用上面的滤波器提取特征后的结果,只取前36个)。

feat = net.blobs['conv1'].data[0, :36]
vis_square(feat)




第五个卷积层再经过池化层的结果,
pool5


feat = net.blobs['pool5'].data[0]
vis_square(feat)




第一个全连接层,
fc6
.

我们使用输出值和正值的直方图来显示结果。

feat = net.blobs['fc6'].data[0]
plt.subplot(2,1,1)
plt.plot(feat.flat)
plt.subplot(2,1,2)
_ = plt.hist(feat.flat[feat.flat>0], bins=100)




最后的概率输出值,
prob


feat = net.blobs['prob'].data[0]
plt.figure(figsize=(15, 3))
plt.plot(feat.flat)


[<matplotlib.lines.Line2D at 0x7fa12c7b5450>]




注意到,预测的概率值比较高的几个类都聚集在一起;标签是根据语义进行划分的。正如上图所示,峰值对应这预测的最可能的结果。

使用自己的图片进行测试

现在我们从网上下载一张图片,并参照上面的步骤预测。

- 试着设置
my_image_url
为任何JPEG图像的URL。

# my_image_url = 'https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1519724702&di=786bcee6e93db5cd810cd62ec68ce058&src=http://imgsrc.baidu.com/imgad/pic/item/29381f30e924b899024e2a0265061d950a7bf69e.jpg'
# !wget -O image.jpg $my_image_url

# 注:这里我是直接下载好了图片,再导入的。换汤不换药。
image = caffe.io.load_image('cat.jpg')
net.blobs['data'].data[...] = transformer.preprocess('data', image)

net.forward()

output_prob = net.blobs['prob'].data[0]

top_inds = output_prob.argsort()[::-1][:5]

plt.imshow(image)

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


probabilities and labels:
[(0.52841139, 'n02124075 Egyptian cat'),
(0.20741074, 'n02123045 tabby, tabby cat'),
(0.20226726, 'n02123159 tiger cat'),
(0.055884782, 'n02127052 lynx, catamount'),
(0.0017415404,
'n02125311 cougar, puma, catamount, mountain lion, painter, panther, Felis concolor')]


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: