把Caffe集成到c++项目的流程
来源:互联网 发布:淘宝质量问题怎么赔偿 编辑:程序博客网 时间:2024/06/12 00:12
前言
本来用着MatConvNet挺舒服的,奈何集成到c++的项目中实在是痛苦啊,做成DLL没问题,问题是每次运行都要加载MCR,加载一次起码两分钟逼得我在项目里把CNN部分做成mock,测试其他功能的时候禁掉CNN省的加载搞的我烦心。于是我转向了用c++开发的Caffe的怀抱,当然用的是c++的接口和matlab的接口,py的接口没有用过,毕竟从本科开始接触图像处理都是玩的matlab
准备工作
本人运行环境是VS2013,win7 64位,matlab2005rb,小结构网络不用GPU
按着这个兄弟的博文http://blog.csdn.net/jiangjieqazwsx/article/details/53292326来编译就好了,要注意的就是用VS2013,别用10或者12,反正我用12它没办法自动加载依赖库,话说这个caffe的依赖库还真是多。还有最好自己在网上把NugetPackages下下来,省的VS慢慢下了。
这些机器学习框架的基本流程都是:
1.生成训练集,测试集,验证集
2.构建网络
3.开始迭代训练
4.测试
以此为线索简要的介绍一下,不够详细的留言或者私心交流吧
生成训练集
caffe支持的输入格式很多,最常见的是lmdb,leavedb。在你编译好caffe后,在caffe-windows\Build\x64\Release目录下找到convert_imageset.exe程序,用它来生成lmdb。
在caffe-windows\examples\imagenet下有个create_imagenet.sh就是调用这个程序的,你可以根据自己的需求来生成train,val,test,该脚本需要的额外材料是图片的文件命后+标签,自己写个matlab脚本生成一下个文本吧。window下如何运行sh脚本?安装个cygwin吧少年
构建网络
caffe需要三个配置文件,一个训练时的网络结构,一个使用时的网络结构,一个训练信息。在mnist中分别为lenet_train_test.prototxt、deploy.prototxt、lenet.prototxt
之前没有好好看文档,训练好后还想用lenet_train_test去测试,结果肯定是不行啊。一般train_test最后一层是softmax-loss,其上层是fc+label,而deploy最后一层是softmax,本来就是拿来预测的当然上一层也不会有label了。
具体如何去写参考这个http://wenku.baidu.com/link?url=S2mVH6XTS4Y9TOrCGkTPWrqVqy0Dx9waaZdANYt4G30fruX5Pkn5w1CzXHJoCqkZqJuyqoFnN6sLMtu1iNwujlgldjvLcdE7RTzRgBm5jfO
训练
直接给matlab脚本吧,从这个同学那http://blog.csdn.net/zb1165048017/article/details/52653501拿过来的
clear clc close all format long %设置精度,caffe的损失貌似精度在小数点后面好几位 caffe.reset_all%重设网络,否则载入两个网络会卡住 solver=caffe.Solver('lenet_solver.prototxt'); %载入网络 loss=[];%记录相邻两个loss accuracy=[];%记录相邻两个accuracy hold on%画图用的 accuracy_init=0; loss_init=0; for i=1:50 solver.step(100);%每迭代一次就取一次loss和accuracy iter=solver.iter(); loss=solver.net.blobs('loss').get_data();%取训练集的loss accuracy=solver.test_nets.blobs('accuracy').get_data();%取验证集的accuracy %画loss折线图 x=[i-1,i]; y=[loss_init loss]; plot(x,y,'r-') drawnow loss_init=loss; end
测试
这里测试是指自己拿单独的图片去试一试,在训练的时候已经给了val的信息了,准确度可以用这个去看
1.matlab版本
clear clc close all format long %设置精度,caffe的损失貌似精度在小数点后面好几位 caffe.reset_all%重设网络 caffe.set_mode_cpu();%设置CPU模式 model = 'deploy.prototxt';%模型weights = 'snapshot_iter_5000.caffemodel';%参数net=caffe.Net(model,'test');%测试net.copy_from(weights);%获取训练参数netim=caffe.io.load_image('../mnist_img/00001-5.bmp');%读取图片imshow(im)net.blobs('data').set_data(im);%设置输入层net.forward_prefilled();%前向网络pros=net.blobs('prob').get_data();%提取输出层[~, maxlabel] = max(pros);maxlabel-1;
注意啊,这里有个坑,同样一张图你试试用caffe.io.load_image和imread去读,看看有什么差别。我当时随便拿了张训练集的图片去测,发现居然算的不对,这个可不正常,那时我就是用imread去读取图片的。imread就是我们正常读图的方式,而caffe.io.load_image则是按列优先去读的,看上去图片被左右翻转在逆时针90度旋转了。
2.cpp版本
其实你去网上搜大部分人都是给的caffe-windows\examples\cpp_classification下面那个cpp,写的比较通用。我这里给个简化版的
Duco_Classifier.h:
////// 基于CNN的图片识别器/// 1.最大识别100*100的图片/// 2.只识别灰度图#ifndef _DUCO_CLASSIFIER_H_#define _DUCO_CLASSIFIER_H_#include<iostream>#include<vector>#include<caffe/caffe.hpp>using namespace caffe;using namespace std;#ifndef BYTE #define BYTE unsigned char#endifclass Duco_Classifer{public: Duco_Classifer(); ~Duco_Classifer(); //加载模型 deploy.prototxt void LoadingModel(const char* modelFile); //加载训练权重参数 .caffemodel void LoadingTrainedPara(const char* trainedFile); //识别 返回最大标签,从0开始,如果为-1则表示置信度过低 int Recognition(BYTE *pImg, int w, int h);private:private: Net<float> *m_pNet;//CNN网络 float m_imgF32[100 * 100];//识别图像};#endif
Duco_Classifier.cpp:
#include"Duco_Classifier.h"#include"Duco_Classifer_Def.h"//构造Duco_Classifer::Duco_Classifer(){ Caffe::set_mode(Caffe::CPU); m_pNet = NULL; memset(m_imgF32, 0, sizeof(m_imgF32)); }//析构Duco_Classifer::~Duco_Classifer(){ if(m_pNet != NULL) delete[]m_pNet;}//加载模型void Duco_Classifer::LoadingModel(const char* modelFile){ if (m_pNet != NULL)//删除上一个模型 delete []m_pNet; m_pNet = new(std::nothrow)Net<float>(modelFile,TEST);}//加载训练权重参数 .caffemodelvoid Duco_Classifer::LoadingTrainedPara(const char* trainedFile){ m_pNet->CopyTrainedLayersFrom(trainedFile);}//识别 返回最大标签,从0开始,int Duco_Classifer::Recognition(BYTE *pImg, int w, int h){ int i, x, y, maxIndex; float *pCur,max; //1.修改数据类型并归一化到[0,1] for (i = 0; i < w*h; i++){ m_imgF32[i] = pImg[i] * 1.0L / 255; } //2.识别 Blob<float>* input_blobs = m_pNet->input_blobs()[0]; memcpy(input_blobs->mutable_cpu_data(), m_imgF32, sizeof(float)* input_blobs->count());//w*h m_pNet->ForwardPrefilled(); Blob<float>* output_layer = m_pNet->output_blobs()[0]; const float* pBegin = output_layer->cpu_data(); const float* pEnd = pBegin + output_layer->channels(); //3.返回top 1 maxIndex = 0; max = *pBegin; i = 0; for (const float *pCur = pBegin; pCur < pEnd; i++,pCur++){ DUCO_LOG("%.5f ",*pCur); if (*pCur>max){ max = *pCur; maxIndex = i; } } DUCO_LOG("\n"); return maxIndex;}
调用:
//测试接口#include<iostream>#include<vector>#include<caffe/caffe.hpp>#include"bmpFile.h"#include"Duco_Classifier.h"#include"Duco_Classifer_Def.h"using namespace caffe;using namespace std;const char *model_file = "deploy.prototxt";const char *trained_file = "snapshot_iter_5000.caffemodel";FILE *g_fout;int main(){ g_fout = fopen("log.txt", "w+"); int w, h; Duco_Classifer classiferMNIST; classiferMNIST.LoadingModel(model_file); classiferMNIST.LoadingTrainedPara(trained_file); BYTE *pImg = Read8BitBmpFile2Img("0.bmp", &w, &h); int ret = classiferMNIST.Recognition(pImg, w, h); DUCO_LOG("%d\n", ret); return 0;}
我的项目里面图像处理的基本元素都是BYTE,如果你自己项目是opencv的,把元素修改成mat就行,道理都是一样的。此外那个def.h里面定义了一个简单日志的宏
#define DUCO_LOG(...) \ fprintf(g_fout, __VA_ARGS__); fflush(g_fout);
基本流程就是1.设置CPU模式或GPU模式 2.加载模型 3.加载参数 4.向输入层传递数据 5.前向计算 6.取输出层数据Read8BitBmpFile2Img是个读位图的函数,网上自己搜一下吧
代码是很简单的,关键要建立项目有些麻烦。参考以下两篇博客
http://blog.csdn.net/wuzhiyang95_xiamen/article/details/52574668
http://blog.csdn.net/ws_20100/article/details/50499948
第一篇是讲配置的,就是告诉你要加哪里include哪些lib。第二篇是解决你肯定会碰到的问题
因为matlab遇到的那个坑,我开始在cpp里面也把图片类似的旋转了一边,发现结果不对,cpp里面的图片正常读去就行了,不要做旋转操作的。
小结
写的比较简单,主要是汇总近日收集到的有用的博客,以及整个流程。希望跟我有一样需求的同学能节省点时间。以后在项目中再遇到caffe的坑再和各位分享吧。
- 把Caffe集成到c++项目的流程
- 如何把FBReader集成到自己的项目中
- Android(Java):把EHCache集成到android项目里
- [原创]把其他C/C++编译器集成到VC2005中
- 把其他C/C++编译器集成到VC2005中
- 把其他C/C++编译器集成到VC2005中
- 把其他C/C++编译器集成到VC2005中
- 把其他C/C++编译器集成到VC2005中
- 把其他C/C++编译器集成到VC2005中
- 【小C出品】把QQ集成到eclipse中
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把NDK集成到Eclipse
- 把Zeal集成到UltraEdit
- 大数据监控系统相关设计开发
- js获取当前页面所在目录路径
- [bigdata-037]apache hue 用SQL获取数据以及可视化
- iOS 自定义日历的实现
- unity 3d 在限定区域内 实现文字拖动翻页
- 把Caffe集成到c++项目的流程
- READING NOTE: A New Convolutional Network-in-Network Structure
- eclicpse使用入门教程
- dom4j写出项目下的文档到硬盘指定路径下
- 冒泡排序
- C标准库学习之<math.h> ——数学函数
- 报错修改
- git revert的使用
- Java多线程问题总结