1.安装
mac下安装caffe可以参考之前的一篇wiki(在mac下安装caffe),当然如果遇到其他问题请自行google。
对于各种linux系统,网上的教程已经非常多了。
2.caffe代码与架构层次简单介绍
caffe源码是Cpp语言的,基于一些外部的库,包括BLAS(矩阵计算),CUDA(GPU驱动),gflags,glog,boost,protobuf,hdf5,leveldb,lmdb等。
只要各个以来都安装完毕,编译的时候修改下caffe自带的Makefile.config(路径和编译选项的修改),即可编译整个工程。
caffe代码文件夹主要包括:
build 所有编译好的文件存放位置
data 数据文件夹
docs 教程和说明文件夹(建议好好阅读,部分内容讲的非常详细)
include 包含文件夹,头文件
examples 各种demo的文件夹,相关应用可以参考或者直接使用对应的demo和配置
mnist 手写汉字识别 cifar10 场景识别 imagenet 图片分类 cpp_classification 分类的cpp接口文件 feature_extraction提特征的demo文件夹
matlab matlab对应的接口
python python对应的接口
models model文件的路径,一些训练好的model可以参考caffe官网model zoo:http://caffe.berkeleyvision.org/model_zoo.html
tools 一些工具
src 所有源代码存放位置
docs/tutorial 中的文件非常值得阅读,关于caffe 的架构和基本使用讲的很透彻,入门必读
3.caffe的一些应用
(1)网络训练参数调节,可以参考mnist或者cifar10中的demo。
调参根据经验,或者参考docs/tutorial/solver.md
需要调节的参数(solver.prototxt)主要包括:
base_lr:初始学习率,这个是非常重要的一个参数;momentum:一般设置为0.9,如果base_lr特别低的话也可以设置为0.99或0.999等 weight_decay:默认0.005,可以适当调整,类似于正则化项;
lr_policy:学习率变化策略,常见的有fixed(固定), inv,step等,详细的说明见 http://stackoverflow.com/questions/30033096/what-is-lr-policy-in-caffe
或者参考源代码src/caffe/solver.cpp中的GetLerningRate函数
常见的调参方式包括:examples/mnist中的各种方式 fixed,inv,step等,或者cifar10中的quick两步调参的方法。
主要是控制初始的学学习率,并对应的调节学习率的策略,batch size需要适当控制大小
其他参数:
test_iter:进行多少轮测试 display:迭代多少次显示一次 max_iter:最大迭代次数
snapshot:多少轮之后存一次结果 snapshot_prefix:结果文件前缀 solver_mode:使用CPU还是GPU
(2)根据已知的网络进行fine-tune
fine-tune的好处是可以利用别人预先调节好的网络,适当微调网络结构和参数就可以用于自己的应用。
注意事项:0.fine-tune的网络和之前网络结构应该基本一致,特别是输入,不然对应层参数个数不同,会报错
1.训练的时候加入 --weight 指定从哪个模型导入对应层的参数(根据名字)。
2.由于caffe是根据对应层的名字来寻找是否需要填充对应的参数,所以对于你需要导入参数的层,名字和原来网络保持不变,对于你需要自己调节的层,名字需要修改。
3.如果你修改了网络的结构( 比如由1000类分类改为28),则对应层的名字需要改掉,不然会报错。
4.对于你要调节的层,学习率倍数lr_mult需要变大(10,20),其他层尽可能小;如果其他层不需要调节,可以置为0.
(3)使用cpp做分类
利用已经训练好的网络做分类也是一个非常常见的应用。
代码在examples/cpp_classification/classification.cpp 可执行文件在build/examples/cpp_classification/classification.bin
classification.cpp文件通过读入配置,权重,mean文件,图片,进行一次前向操作得到输出层的概率,然后给出分类结果。
基本函数是Predict和Classify。根据特定需求,加入了几个函数,并且改写了main函数可以通过文件列表读入图像,然后特征写入文件。
examples/cpp_classification/extract_feature.cpp:自己改写之后的classification.cpp包括分类和批量提特征功能
Normalize:做L2-norm归一化的辅助函数
FeatureAndPredict:同时提取特征(引用传递feature变量)和返回分类结果的函数,根据传入的string 提取对应层特征。
FeatureByLayerName:根据传入的string提取特定层特征的函数。
main:将所有参数在main中指定,同时调用提特征函数对读取文件中的图片进行特征提取并写入二进制文件中。
(4)特征提取
利用已经有的网络提取特定层的特征,可以使用examples/feature_extraction或者用上面(3)改写之后的extract_feature.cpp函数。
4.做OCR过程中遇到的一些坑和解决方案
(1)最开始调节网络总会发现各种莫名其妙的报错,例如lock,state等等;主要原因可能有一下几点:
文件或者文件格式生成错误,配置文件格式错误:solver文件或者网络文件
(2)基本熟悉caffe之后就可以在特定网络的基础上进行调节了,此时常见的问题就是不收敛或者正确率不理想到情况。
一般解决思路:
检查lmdb或者leveldb是否生成错误,配置文件是否书写错误
观察loss变化情况,适当调整学习率和策略;
思考训练数据量,类别数,网络结构层数之间的关系,考虑分辨率数据量和网络的能力之间是否相互匹配;如果不合适可以考虑更换网络或者增加数据量。
(3)如果网络收敛,正确率也比较高的情况下,依然可能遇到问题
比如分类中正确率是对的,但是每次输出都概率不是0就是1,遇到这种问题多半是因为softmax之前一层的数据问题,解决方案:
1)更换网络,增加数据量,或者在现有比较好的网络基础上做微调
2)自己获得softmax之前的数据做softmax 变换:xi=exp(xi-max)/sum(exp(xi-max)),减最大值是为了防止数值溢出,具体代码见examples/cpp_classification/classification.cpp的Predict函数。
classification.cpp函数运行时间特别长,单张图像要几秒级别。
这个原因是分类函数每次都会导入配置,时间很长,只要自己手动导入一次配置,然后再进行分类操作即可解决。代码见examples/cpp_classification/extract_feature.cpp
如果一切正常比较大的网络运行时间可能在0.x秒级别,小一点的会在0.0x秒或者0.00x秒级别,但是很多时候比如滑窗这种潜在分类目标太多,运行速度依旧不能满足正常的需求
可以参考examples /extract _feature中的方法进行改进,批量读取图片做为一个batch,进行一次前向操作,代码待完成
(4)解决了0,1概率问题之后,直接用于滑窗,发现正确率依然非常低
造成这个问题的主要原因是分类器给出的置信概率往往不太靠谱,尽管分类正确率比较高。
解决方案是使用分类器给出分类,然后使用prototype原型的方法给出置信概率,经过这样改进之后,CNN+svm+prototype的验证码识别率可以超过传统方法。