把Caffe集成到c++项目的流程

#前言
本来用着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);%获取训练参数
net
im=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
#endif
class 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);
}

//加载训练权重参数 .caffemodel
void 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的坑再和各位分享吧。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值