把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的坑再和各位分享吧。

0 0