1、运行

安装好caffe后,进入/opt/caffe/examples/web_demo/的caffe web demo项目目录,查看一下app.py文件,这是一个flask编写的网站

查看readme.md了解该项目,查看requirements.txt了解所需的python包

然后运行该项目:python app.py,发现报错

class_labels_file is missing

网上说还需要下载一些data,运行下面两条命令:

确保已经下载了 Reference CaffeNet Model 和 the ImageNet Auxiliary Data:

python opt/caffe/scripts/download_model_binary.py models/bvlc_reference_caffenet

opt/caffe/data/ilsvrc12/get_ilsvrc_aux.sh

然后再运行该项目:python app.py

 

127.0.0.1:5000查看

 caffe web demo运行+源码分析

 

 

2、app.py源码分析

这个web demo使用了在ilsvrc2012比赛的数据集上训练得到的模型。

模型已经预先训练好,并给出了模型的网络结构文件deploy.prototxt,训练好的模型文件bvlc_reference_caffenet.caffemodel,训练数据的均值文件ilsvrc_2012_mean.npy(需要它是因为需要对测试数据去均值),标签序号与标签内容的对应文件synset_words.txt,与原始数据集结构有关的imagenet.bet.pickle

其中网络结构文件、训练好的模型文件、均值文件会被输入到caffe.Classifier里用来构造分类器,标签序号与标签内容文件用来在网页上给显示结果。

 caffe web demo运行+源码分析

 

1)index函数

 caffe web demo运行+源码分析

 

 

2)classify_url函数

读取url,urllib.urlopen().read()打开url并读取出二进制字节流(python3中为urllib.request.urlopen().read()),caffe.io.load_image()转换为caffe能接受的格式,输出日志,调用classify_image识别图片,将识别结果和imageurl渲染回html显示给用户

 caffe web demo运行+源码分析

 

3)classify_upload函数

获取上传的图片,在文件名前插入时间以区分不同文件,将文件保存到服务器,输出日志,调用classify_image识别图片,将识别结果和图片(以base64编码的形式)渲染回html显示给用户

 caffe web demo运行+源码分析

caffe web demo运行+源码分析

 

 

 

 

4)embed_image_html函数

3)服务,读取文件返回base64编码

 caffe web demo运行+源码分析

 

 

5)allowed_file函数

检查文件后缀名是否在可接受的类型中(这个函数在app.py中并没有被调用,不知道为什么)

 caffe web demo运行+源码分析

 

 

6)ImagenetClassifier内部类

定义了5个默认参数:模型的网络结构文件deploy.prototxt,训练好的模型文件bvlc_reference_caffenet.caffemodel,训练数据的均值文件ilsvrc_2012_mean.npy(需要它是因为需要对测试数据去均值),标签序号与标签内容的对应文件synset_words.txt,与原始数据集结构有关的imagenet.bet.pickle

for循环验证了这些文件存不存在,不存在的话报错(安装过程中的报错就是这里抛出的)

定义了2个默认参数,image_dim表示???,raw_scale表示???

 caffe web demo运行+源码分析

 

初始化指定识别使用cpu还是gpu

初始化分类器:caffe.Classifier,接收上面定义的模型的网络结构文件deploy.prototxt,训练好的模型文件bvlc_reference_caffenet.caffemodel,训练数据的均值文件ilsvrc_2012_mean.npy(需要它是因为需要对测试数据去均值)三个参数

。。。。。。。

 caffe web demo运行+源码分析

 

7)中

app.clf = ImagenetClassifier(**ImagenetClassifier.default_args)
app.clf.net.forward()

两句代码执行完之后,我们实际上就创建并训练好我们的分类器了。

然后我们调用net.predict函数,将caffe.io.load_image得到的图片传进去,就可以进行识别,并返回一个numpy.ndarray类型的最后一层(输出层)的结果。

net.predict完之后,识别的中间结果也已经存在app.clf.net中了,只需要调用net.blobs['layer名'].data就可以获取到numpy.ndarray类型的中间结果了。

predict时的oversample表示过取样,默认为true

ndarray.shape可以获取ndarray数组的形状,更多源于numpy.ndarray数组的信息见这篇https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html

ndarray.flatten表示把ndarray多维数组展开成一维ndarray数组,见这篇https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.ndarray.flatten.html

ndarray数组前面加负号表示对每个元素取负值

ndarray.argsort不改变原数组,并返回原数组值从小到大的索引值,示例如下,或见这篇http://blog.csdn.net/maoersong/article/details/21875705

  >>> x = np.array([3, 1, 2])
    >>> np.argsort(x)
    array([1, 2, 0])

 

个人通过加了如下几行代码看了一下这个模型各层结果的格式:(在使用logging.info的过程中注意,logging.info的输出要通过%s来占位,不同于print可直接输出)

# add by zss
# logging.info('classifier.predict.faltten:%s',list(scores))
# logging.info('classifier.predict:%s',list(self.net.predict([image],oversample=True)))
logging.info('########################:%s',list(self.net.blobs['fc8'].data[1]))

如下:

predict函数返回的最后一层(prob层)的结果格式为1*1000的概率数组(值在0-1之间)

‘fc8’层的结果,格式为10*1000的概率数组(值在0-1之间)

分析:

prob层和fc8层的结果对应着1000个标签,含义是该图片是该标签的概率。

fc8层有10个长度1000的数组是因为我们在定义模型(caffe.Classifier())时,对oversample选项采用了默认值true,开启了过取样,相当于将每张图片经过旋转和镜像对称变成了10张相同的图片,所以fc8层有10个结果,然后prob层将这10个结果综合得到了最终结果1*1000的概率数组),关于oversample过取样见这篇http://blog.csdn.net/guoyilin/article/details/42886365

 

‘fc7’层的结果,格式为10*4096的foat数组(值可能大于1),形如下图

分析:

fc7层的结果对应着输入的那一张图片,经过过取样后的10张图片,各自的4096个特征的值

 caffe web demo运行+源码分析

caffe web demo运行+源码分析

 

 

 

7)start_tornado函数

8)服务,启动flask服务器

不太明白,为什么要用tornado的函数启动flask服务器?

 caffe web demo运行+源码分析

 

 

8)start_from_terminal函数

9)服务,使用optparse定义了一些参数debug,port,gpu,用来控制debug模式是否打开,flask运行的端口,gpu模式还是cpu模式,运行flask的端口号,然后调用7)启动flask服务器

 caffe web demo运行+源码分析

 

9)命令行启动

设置log级别,创建用来存储用户上传的图片的临时文件夹,调用8)

 caffe web demo运行+源码分析