00-classification
主要讲的是如何利用caffenet
(与Alex-net稍稍不同的模型)对一张图片进行分类(基于imagenet的1000个类别)
其实并没有官方教程一说,只是在caffe/example/下有
00-classification.ipynb;
01-learning-lenet.ipynb;
02-fine-tuning.ipynb;
等等一些列 ipython notebook 文件,里面就是一些example,这就是所说的官方教程,打开 ipynb 文件有多种方式,我是直接在github上打开的(附链接:http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb)
这些 example 其实是比较全的,例如 00-classification
不仅告诉怎么用训练好的.caffemodel+ deploy.prototxt
对一张新来的图片进行分类,而且还讲到如何使用python
对filters
可视化,对feature maps
可视化;如何对各层activations
进行绘制直方图;以及如何绘制prob
层的输出直方图;
model
的函数,net = caffe.Net(model_def, model_weights, caffe.TEST)
,model_def
是deploy.prototxt
的路径,model_weights
是*.caffemodel
的路径,caffe.TEST
是说明网络是用来test
的,不会执行dropout
out = net.forward()
,batchsize
中第一张图像的输出 put_prob = output[‘prob’][0]
output_prob.argmax()
weights
参数net.params[‘conv1’][0].data
blob
里的数据net.blobs[‘conv1’].data[0, :9]
top 5
的获取 top_inds = output_prob.argsort()[::-1][:5]
开始之前说一下,第一步至第四步都是按照官方教程上来的,并不是很完整的一个code,完整的 code 在最后面给出,但是和官方教程不一样,但是本质上是一样的。所以说一下注意事项,即需要的文件以及路径的注意事项。
1. 网络模型文件在 caffe/models/bvlc_reference_caffenet下
的 bvlc_reference_caffenet.caffemodel
2. 网络描述文件在caffe/models/bvlc_reference_caffenet下的deploy.prototxt
3.测试图片 cat.png 在caffe/examples/images下
4. 类别名称文件 synset_words.txt 自行下载
5.均值文件 ilsvrc_2012_mean.npy 自行下载
python-caffe
的接口(怎么配置请参考别 的优秀blog)。import caffe
之前一定要把路径添加进来,要不然会找不到caffe
这个模块;这里提供两种方法,一种是文档里的import sys
caffe_root = '../' # this file should be run from {caffe_root}/examples (otherwise change this line)
sys.path.insert(0, caffe_root + 'python')
一种是我用的(参见底部代码)
第一步的完整代码
import numpy as np
import matplotlib.pyplot as plt
# 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
deploy.prototxt
和 *.caffemodel
文件,而加载网络是通过路径名,所以先设置好这两个文件的路径名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)
然后设置cpu or gpu
,可自行挑选其中一个
# 用cpu
caffe.set_mode_cpu()
# 用gpu
caffe.set_device(0)
caffe.set_mode_gpu()
由于输入到网络的图片是经过处理的,所以要把读取进来的图片处理成与网络训练时所见过的图片是一致的,这里的一致包括 shape,颜色通道顺序,取值范围([0,1] or [0,255]?)
,以及是否减均值(channel-wise or pixel-wise ?
) , 这些都需要一致。
首先把网络用到的均值文件加载进来(这个文件在caffe/imagenet/ilsvrc_2012_mean.npy)
,
mu = np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy')
mu = mu.mean(1).mean(1) # 这里即是计算出 channel-wise所用到的均值
print 'mean-subtracted values:', zip('BGR', mu) # print出来BGR三个channel的均值是多少
接下来设置(create)预处理操作
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1)) [h,w,c] → [c,h,w]
transformer.set_mean('data', mu) # 减均值 channel-wise
transformer.set_raw_scale('data', 255) [0,1] → [0,255]
transformer.set_channel_swap('data', (2,1,0)) RGB--> BGR
caffe.io.load_image
读取,读取进来的是[0,1],RGB,(h,w,3)
cv.2
读取进来的是 [0,255], BGR, (h,w,3)
[0,255],RGB,(h,w,3)
image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg')
transformed_image = transformer.preprocess('data', image)
plt.imshow(image)
#(ps: 要自己加上 plt.show() 才能显示如下这只可爱的喵星人~~)
net.blobs['data'].data[...] = transformed_image
output = net.forward()
output_prob = output['prob'][0] # 把网络输出层(prob)第一个值取出来 (注意了!这里shape用的是[50,3,227,227],一次一个batch的,batchsize是50,而我们只喂给网络一张图像,所以要取出对应的prob,即 output['prob'][0])output['prob'][0]是一个 1000维的向量
print 'predicted class is:', output_prob.argmax() # 再把这1000维的向量最大值所在的标号取出来,即分类类别
只知道分类类别标号,并不能满足我们最终想知道这张图片到底是什么物体,所以需要一个索引,索引存在synset_words.txt
中,这个文件需要自己下载,(下载方式:caffe/data/ilsvrc12
里有个sh
文件,通过这个文件即可下载)
abels_file = caffe_root + 'data/ilsvrc12/synset_words.txt'
labels = np.loadtxt(labels_file, str, delimiter='\t')
print 'output label:', labels[output_prob.argmax()]
这样就可以print出这张图像是什么物体了,结果没错的话就是:
output label: n02123045 tabby, tabby cat
对于Imagenet,总所周知的是除了top1 还有 top5, 那么如何获取top5呢,我们来看看:
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])
至此classification就结束啦!
4. 第四步:看看中间层都是什么
不能让神经网络太”黑“,只知道 input,output,所以这里提供了一些函数可以对filters,feature maps
进行可视化,还可以对输入的概率值进行绘制直方图
我这里按照功能分1-7个步骤
(1)获取各blob的名字以及shape
for layer_name, blob in net.blobs.iteritems():
print layer_name + '\t' + str(blob.data.shape)
(2)获取params的名字以及shape
for layer_name, param in net.params.iteritems():
print layer_name + '\t' + str(param[0].data.shape), str(param[1].data.shape)
(3)对卷积核进行可视化
首先要定义个函数,要来show出卷积核的,就是把n个卷积核画到一张图片上
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
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 的卷积核长什么样
filters = net.params['conv1'][0].data
vis_square(filters.transpose(0, 2, 3, 1))
transpose
的是因为,网络数据流动的shape
是[batch,channel,h,w]
而plot
函数是绘制[h,w,channel]
的,所以要transpose
一下
(4)看看 经过conv1卷积得到的feature maps 是什么样,这里只看前36个(好plot吧,毕竟36 = 6*6)
feat = net.blobs['conv1'].data[0, :36]
vis_square(feat)
(5)看看 pool5后得到的 feature maps 是什么样
feat = net.blobs['pool5'].data[0]
vis_square(feat)
(6)看了feature 和 filters还不过瘾,那来看看中间层的activation的分布情况吧
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)
第一个图可以看到fc6层(4096个神经元)各个神经元输出值是多少,整体分布在(0,60)之间
第二个图可以看出4096个输出值是怎么样的一个分布,可以发现在0-20之间占了绝大部分
(7)来看看网络的输出prob的分布
feat = net.blobs['prob'].data[0]
plt.figure(figsize=(15, 3))
plt.plot(feat.flat)
可以看到 281个神经元的输出是最大的,不过也有写干扰项,整体来说这样的输出还是很满意的(即没有太多别的类别的干扰)
以上是根据官方教程来写的,
我自己的demo并不是完全一样,但是思路是一样的,大家可以参考:
注意! 路径需要更改,
import sys
sys.path.append('/home/***/caffe/python')
sys.path.append('/home/***/caffe/python/caffe')
import caffe
import numpy as np
import argparse
import glob
import matplotlib.pyplot as plt
# import cv2
def obtain_img_path(dir_path, codec='png'):
# dir_path is the directory of image
# imgs_path is a list , element of imgs_path is single image path
imgs_path = [img_path for img_path in glob.glob(dir_path + '*' + codec)]
return imgs_path
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--deploy_folder', default='./', help='path to the deploy FOLDER. [DEFAULT=...]')
parser.add_argument('--deploy_file', default='deploy.prototxt', help='path to the deploy NAME. [DEFAULT=deploy.prototxt]')
parser.add_argument('--model_state_folder', default='./', help='stored model FOLDER. [DEFAULT=../../Result/Section6/]')
parser.add_argument('--caffemodel_file', default='bvlc_reference_caffenet.caffemodel', help='path to the .caffemodel NAME')
args = parser.parse_args()
caffe.set_mode_gpu()
caffe.set_device(0)
DEPLOY_FULL_PATH = args.deploy_folder + args.deploy_file
CAFFEMODEL_FULL_PATH = args.model_state_folder + args.caffemodel_file
# load caffemodel
# requirements: deploy.prototxt and ***.caffemodel
net = caffe.Net(DEPLOY_FULL_PATH,CAFFEMODEL_FULL_PATH,caffe.TEST)
# load test images_path
img_dir = '../../../caffe/examples/images/cat.jpg'
# imgs_dir = '../../Data/tt/' # the last '/' can not be less'
# imgs_path = obtain_img_path(imgs_dir, codec='png')
labels_filename = './synset_words.txt' # 类别名称文件,将数字标签转换回类别名称
# image preprocess
# load ImageNet mean
mu = np.load('./ilsvrc_2012_mean.npy')
mu = mu.mean(1).mean(1) # BGR ,channel-wise not pixel-wise
print '111111111111111111111`~~~~~~~~~~~`mu :',type(mu)
print 'mean-subtracted values:', zip('BGR', mu)
# process image
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) # (10,3,227,227), input one image, it will copy 10 copies
transformer.set_transpose('data', (2,0,1)) # (w,h,c)--> (c,w,h)
transformer.set_mean('data', mu) # channel-wise
transformer.set_raw_scale('data', 255) # [0,1] --> [0,255]
transformer.set_channel_swap('data', (2,1,0)) # RGB --> BGR
# load image
img = img_dir
im=caffe.io.load_image(img) # im is RGB with 0~1 float
# print 'raw im ~~~~ :' , im.shape
net.blobs['data'].data[...]=transformer.preprocess('data',im)
# print 'processed im ~~~: ', net.blobs['data'].data
# 执行测试
out = net.forward()
labels = np.loadtxt(labels_filename, str, delimiter='\t') # 读取类别名称文件
prob= net.blobs['prob'].data[0].flatten() # 取出最后一层(Softmax)属于某个类别的概率值,并打印
# print prob
order=prob.argsort()[-1] # [-2] is second largest
print 'the class is:',labels[order] # find labels name
print 'the class is : ' + str(order)
# sort top five predictions from softmax output
top_inds = prob.argsort()[::-1][:5]
# the shape of net.blobs
for layer_name, blob in net.blobs.iteritems():
print layer_name + '\t' + str(blob.data.shape)
# the shape of net.params
for layer_name, param in net.params.iteritems():
print layer_name + '\t' + str(param[0].data.shape) , str(param[1].data.shape)
# vis function
def vis_square(data):
"""输入一个形如:(n, height, width) or (n, height, width, 3)的数组,并对每一个形如(height,width)的特征进行可视化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)) # 在相邻的滤波器之间加入空白
+ ((0, 0),) * (data.ndim - 3)) # 不扩展最后一维
data = np.pad(data, padding, mode='constant', constant_values=1) # 扩展一个像素(白色)
# 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')
plt.show()
# show filters
# filters = net.params['conv1'][0].data
# vis_square(filters.transpose(0, 2, 3, 1)) batchsize,c,w,h --> bs,w,h,c
# show fearture maps it require input
# feat = net.blobs['conv1'].data[0, :9]
# vis_square(feat)
# show the distribution
feat = net.blobs['prob'].data[0]
plt.ylim(feat.min(),feat.max()) # 设置坐标轴的最大最小区间
plt.subplot(2, 1, 1)
plt.plot(feat.flat)
plt.subplot(2, 1, 2)
_ = plt.hist(feat.flat[feat.flat > 0], bins=100)
plt.show()