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层的输出直方图;
这功能未必是我们想要的,所以大家可以自行挑选,最后我会附上我的代码
函数总结
先总结一些最关心的接口函数,即可能用到的python接口函数,也是我们最关心的python里用什么函数对blob,params进行 ”读“,”写“
1. 加载model的函数,net = caffe.Net(model_def, model_weights, caffe.TEST) ,model_def是deploy.prototxt的路径,model_weights是*.caffemodel的路径,caffe.TEST是说明网络是用来test的,不会执行dropout
2. 执行测试out = net.forward() ,
3. 获取 batchsize中第一张图像的输出 put_prob = output[‘prob’][0]
4. 最大值所在的标号 output_prob.argmax()
5. 获取 网络weights 参数net.params[‘conv1’][0].data
6. 获取网络blob里的数据net.blobs[‘conv1’].data[0, :9]
7. 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 自行下载
(百度云盘打包5个文件:链接: https://pan.baidu.com/s/1o8dqifS 密码: rjbb)
第一步、Setup
第一步肯定是要安装好 python,安装好numpy,matplotlib 包。配置好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
第二步、Load net and set up input preprocessing
第二步,加载网络以及对图片进行预处理
加载网络需要有 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
第三步,输入图像进行分类
有了网络net,有个对输入图像的预处理操作的设置,接来下就可以把一张图像 put到net中,net给出分类结果
首先读取图像,读取图像这里需要注意几点,采用不同函数读取进来的图像数据,其形式可能不一样,这里给出一些总结
**caffe.io.load_image读取,读取进来的是[0,1],RGB,(h,w,3)
cv.2 读取进来的是 [0,255], BGR, (h,w,3)
matlab 读取进来的是 [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吧
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文件,通过这个文件即可下载)
labels_file = caffe_root + 'data/ilsvrc12/synset_words.txt'
labels = np.loadtxt(labels_file, str, delimiter=' ')
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就结束啦!
第四步,看看中间层都是什么
不能让神经网络太”黑“,只知道input,output,所以这里提供了一些函数可以对filters,feature maps进行可视化,还可以对输入的概率值进行绘制直方图
我这里按照功能分1-7个步骤
1.获取各blob的名字以及shape
for layer_name, blob in net.blobs.iteritems():
print layer_name + ' ' + str(blob.data.shape)
2.获取params的名字以及shape
for layer_name, param in net.params.iteritems():
print layer_name + ' ' + str(param[0].data.shape), str(param[1].data.shape)
- 对卷积核进行可视化
首先要定义个函数,要来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并不是完全一样,但是思路是一样的,大家可以参考:
注意! 路径需要更改,
# -*- coding: utf-8 -*-
'''
@author: TensorSense
'''
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=' ') # 读取类别名称文件
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 + ' ' + str(blob.data.shape)
# the shape of net.params
for layer_name, param in net.params.iteritems():
print layer_name + ' ' + 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()