Caffe 的 Python 接口

一、教程简介

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对一张新来的图片进行分类,而且还讲到如何使用pythonfilters可视化,对feature maps 可视化;如何对各层activations进行绘制直方图;以及如何绘制prob层的输出直方图;

二、函数总结

  1. 加载model的函数,net = caffe.Net(model_def, model_weights, caffe.TEST)model_defdeploy.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 在最后面给出,但是和官方教程不一样,但是本质上是一样的。所以说一下注意事项,即需要的文件以及路径的注意事项。

. 网络模型文件在 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 自行下载
  1. 第一步: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
  1. 第二步: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
  1. 第三步:输入图像进行分类
    有了网络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文件,通过这个文件即可下载)

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 代码

以上是根据官方教程来写的,
我自己的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()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值