caffe web demo运行+源码分析
2018-04-09 10:10 ZealouSnesS 阅读(895) 评论(0) 编辑 收藏 举报caffe web demo学习
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查看
2、app.py源码分析
这个web demo使用了在ilsvrc2012比赛的数据集上训练得到的模型。
模型已经预先训练好,并给出了模型的网络结构文件deploy.prototxt,训练好的模型文件bvlc_reference_caffenet.caffemodel,训练数据的均值文件ilsvrc_2012_mean.npy(需要它是因为需要对测试数据去均值),标签序号与标签内容的对应文件synset_words.txt,与原始数据集结构有关的imagenet.bet.pickle
其中网络结构文件、训练好的模型文件、均值文件会被输入到caffe.Classifier里用来构造分类器,标签序号与标签内容文件用来在网页上给显示结果。
1)index函数
2)classify_url函数
读取url,urllib.urlopen().read()打开url并读取出二进制字节流(python3中为urllib.request.urlopen().read()),caffe.io.load_image()转换为caffe能接受的格式,输出日志,调用classify_image识别图片,将识别结果和imageurl渲染回html显示给用户
3)classify_upload函数
获取上传的图片,在文件名前插入时间以区分不同文件,将文件保存到服务器,输出日志,调用classify_image识别图片,将识别结果和图片(以base64编码的形式)渲染回html显示给用户
4)embed_image_html函数
为3)服务,读取文件返回base64编码
5)allowed_file函数
检查文件后缀名是否在可接受的类型中(这个函数在app.py中并没有被调用,不知道为什么)
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表示???
初始化指定识别使用cpu还是gpu
初始化分类器:caffe.Classifier,接收上面定义的模型的网络结构文件deploy.prototxt,训练好的模型文件bvlc_reference_caffenet.caffemodel,训练数据的均值文件ilsvrc_2012_mean.npy(需要它是因为需要对测试数据去均值)三个参数
。。。。。。。
在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个特征的值
7)start_tornado函数
为8)服务,启动flask服务器
不太明白,为什么要用tornado的函数启动flask服务器?
8)start_from_terminal函数
为9)服务,使用optparse定义了一些参数debug,port,gpu,用来控制debug模式是否打开,flask运行的端口,gpu模式还是cpu模式,运行flask的端口号,然后调用7)启动flask服务器
9)命令行启动
设置log级别,创建用来存储用户上传的图片的临时文件夹,调用8)