caffe源码学习

来源:互联网 发布:python popen pipe 编辑:程序博客网 时间:2024/06/05 05:41

caffe源码学习

学习调试时间: 2017-06-11~2017-06-12
博客编写时间: 2017-06-13

概述

前段时间对机器学习的学习过程中,主要是基于caffe的框架进行学习的。在下载完caffe-master后调试了在LeNet网络上运行mnist的例子。虽然代码调试成功,但是感觉简直是一头雾水。坐在实验室想着:当自己搭建网络,调试每一层的数据,我该怎么做。当拿到Net的python类的时候,我可以对网络做些什么。拿到了一个对象实例,又该怎么知道里面有哪些成员。于是便兴冲冲地去caffe-master/include下进行查看,然而c++中定义的成员名和python中的完成不同。打击万分,caffe的官网上也只有c++的API,用python调用时不知所措。因此,此博客主要介绍了caffe中python语言调用c++底层代码的功能。然后仿照caffe的封装过程,来封装自己的c++类来供python调用。在调试之前在boost的官网将boost库的使用熟悉了一遍,有些函数的使用还不是很清晰,因此建议学习的同学对boost没接触过的。可以看看boost的官网的python使用。

  1. 系统环境为Ubuntu14.0.4
  2. c++编译器为g++4.8
  3. python编译器为python2.7

caffe工程有关封装的文件

在caffe-master/python/caffe文件下:
1. _caffe.cpp:此文件主要是解决如何通过boost库来将c++类或者函数封装为python可以调用的类或者函数.
2. _caffe.so:该文件是_caffe.cpp文件经过编译链接后的文件.在python工程中import caffe(同级文件夹路径下的init.py文件中查看)实际就导入了该文件,python调用c++的底层时,该文件就是程序的入口.
3. pycaffe.py:当用户需要扩展caffe的c++一些类的功能时,改底层的源码的话可能会使得许多地方需要修改.此文件就是扩展c++的功能.通过python中的关键字@property的特点来扩展c++.

学习内容

此次学习主要通过源码来自己学会对c++来进行封装,以供python来进行调用.学习的结果是封装类似与Net类的blobs_和 blob_names_两个成员变量.然后在python中将其封装为OrderedDict.其效果如图:
这里写图片描述

其中最后一行显示了封装后的效果[blobs_names_,blobs_].

接下来将把caffe中相对应的源码拿出来进行模仿,这里只粘贴出模仿过程中使用到的caffe源码.过程如下:

  1. 定义Net类
  2. 利用boost库导出c++接口为python使用
  3. 用python语言扩展c++代码

Net类的定义

Net类的封装在caffe-sdk/caffe-master/include/caffe/net.hpp文件中.这里,我们主要引用了类中的blobs_names_以及blobs_成员变量.

namespace bp = boost::python;namespace caffe {    template <typename Dtype>    class Net {       public:       inline const vector<string>& blob_names() const { return blob_names_; }       inline const vector<shared_ptr<Blob<Dtype> > >& blobs() const {            return blobs_;       }      protected:        vector<shared_ptr<Blob<Dtype> > > blobs_;        vector<string> blob_names_;    }}

利用boost库将c++导出为python

然后就是c++的类如何封装为python可以调用的接口.使用boost库进行封装.源代码在_caffe.cpp文件中.代码如下:

#define BP_REGISTER_SHARED_PTR_TO_PYTHON(PTR) do { \    const boost::python::type_info info = \    boost::python::type_id<shared_ptr<PTR > >(); \    const boost::python::converter::registration* reg = \    boost::python::converter::registry::query(info); \      if (reg == NULL) { \    bp::register_ptr_to_python<shared_ptr<PTR > >(); \      } else if ((*reg).m_to_python == NULL) { \    bp::register_ptr_to_python<shared_ptr<PTR > >(); \      } \    } while (0)shared_ptr<Net<Dtype> > Net_Init_Load(string param_file, string pretrained_param_file, int phase) {      LOG(WARNING) << "DEPRECATION WARNING - deprecated use of Python interface";      LOG(WARNING) << "Use this instead (with the named \"weights\""    << " parameter):";      LOG(WARNING) << "Net('" << param_file << "', " << phase    << ", weights='" << pretrained_param_file << "')";      CheckFile(param_file);      CheckFile(pretrained_param_file);      shared_ptr<Net<Dtype> > net(new Net<Dtype>(param_file,      static_cast<Phase>(phase)));      net->CopyTrainedLayersFrom(pretrained_param_file);      return net;}

以下代码表明
1. 将Net类导出.在python中的类名叫做Net,没有初始化函数
2. 定义python中类Net的初始化函数,在c++中为Net_Init_Load函数,注意c++中Net_Init_Load不是c++类Net的成员函数
3. 将c++中函数Net::blobs的返回值导出,在python中类Net中以属性名为_blobs存在,即python中调用_blobs成员相当于在c++中调用Net::blobs()函数

/*注意:此处将c++中的Net<Dtype>类和Net<Dtype>类的共享指针类(shared_ptr<Net<Dtype> >在c++中也是一个类,boost中的类,并不是指针)在python中都定义为了类Net,即在python中模糊了指针的概念*/bp::class_<Net<Dtype>, shared_ptr<Net<Dtype> >, boost::noncopyable >("Net",bp::no_init).def("__init__",bp::make_constructor(&Net_Init_Load))      .add_property("_blobs",bp::make_function(&Net<Dtype>::blobs,bp::return_internal_reference<>())).add_property("_blob_names",bp::make_function(&Net<Dtype>::blob_names,bp::return_value_policy<bp::copy_const_reference>())) BP_REGISTER_SHARED_PTR_TO_PYTHON(Net<Dtype>);

然后就是将c++函数中使用到的类进行注册,各种std::vector类型

bp::class_<vector<shared_ptr<Blob<Dtype> > > ("BlobVec").def(bp::vector_indexing_suite<vector<shared_ptr<Blob<Dtype> > >, true>())    .def("add_blob",bp::raw_function(&BlobVec_add_blob));bp::class_<vector<Blob<Dtype>*> >("RawBlobVec")    .def(bp::vector_indexing_suite<vector<Blob<Dtype>*>, true>());bp::class_<vector<string> >("StringVec")    .def(bp::vector_indexing_suite<vector<string> >());bp::class_<vector<int> >("IntVec")    .def(bp::vector_indexing_suite<vector<int> >());bp::class_<vector<Dtype> >("DtypeVec")    .def(bp::vector_indexing_suite<vector<Dtype> >());bp::class_<vector<shared_ptr<Net<Dtype> > > >("NetVec")    .def(bp::vector_indexing_suite<vector<shared_ptr<Net<Dtype> > >, true>());

至此c++的接口已经通过boost库完成了封装.现在,再来看看pycaffe.py文件对类的扩展.

对Net类用python进行扩展

from collections import OrderedDict@propertydef _Net_blobs(self):    if not hasattr(self, '_blobs_dict'):            self._blobs_dict = OrderedDict(zip(self._blob_names, self._blobs))    return self._blobs_dictNet.blobs = _Net_blobs

注意:最后一行代码中的Net是python中的类,该文件中给python中的Net类添加了一个blobs变量,通过该变量来获取_blobs_dict.
这里,总结一下c++中Net类的成员变量到python中类的变量的映射过程:

  1. 在net.hpp中定义c++中的成员变量:Net::blobs_ ,Net::blob_names_
  2. 在_caffe.cpp中导出为python变量:Net._blobs, Net._blob_names
  3. 在pycaffe.py中通过@property添加blobs:Net.blobs = _Net_blobs

注意:通过property导出的python变量,在调用时实际上是调用了相应的函数.

仿造源代码封装自己的c++类

以下代码相当于完成了对c++类的定义和对类的导出.即是net.hpp和_caffe.cpp的功能.文件命名为net.cpp

#include<Python.h>#include<boost/python.hpp>#include<boost/shared_ptr.hpp>#include <boost/python/raw_function.hpp>#include <boost/make_shared.hpp>#include<boost/python/suite/indexing/vector_indexing_suite.hpp>#include<iostream>#include<vector>using namespace boost;using namespace boost::python;#define BP_REGISTER_SHARED_PTR_TO_PYTHON(PTR) do { \      const boost::python::type_info info = \      boost::python::type_id<shared_ptr<PTR > >(); \        const boost::python::converter::registration* reg = \            boost::python::converter::registry::query(info); \              if (reg == NULL) { \                  boost::python::register_ptr_to_python<shared_ptr<PTR > >(); \                    } else if ((*reg).m_to_python == NULL) { \                        boost::python::register_ptr_to_python<shared_ptr<PTR > >(); \                          } \                          } while (0)boost::shared_ptr<int> blob_pointer(new int(10));const std::string& name = "hello";class Net{    public:    Net(int a)    {           blobs_.push_back(blob_pointer);        blob_names_.push_back(name);        std::cout<<"Net()"<<std::endl;    }    inline const std::vector<boost::shared_ptr<int> >& blobs() const {        std::cout<<"blobs()"<<std::endl;        return blobs_;    }    inline const std::vector<std::string>& blob_names() const {        std::cout<<"blob_names()"<<std::endl;        return blob_names_;    }     protected:        std::vector<boost::shared_ptr<int> > blobs_;        std::vector<std::string> blob_names_;};shared_ptr<Net> Init(){        std::cout<<"Init()"<<std::endl;        shared_ptr<Net> net(new Net(10));        std::cout<<net<<std::endl;        return net;}BOOST_PYTHON_MODULE(net){    class_<Net,boost::shared_ptr<Net>,boost::noncopyable >("Net",no_init)            .def("__init__",make_constructor(&Init,default_call_policies()))                        .add_property("_blobs",make_function(&Net::blobs,return_internal_reference<>()))            .add_property("_blob_names",make_function(&Net::blob_names,return_value_policy<copy_const_reference>()));    BP_REGISTER_SHARED_PTR_TO_PYTHON(Net);    class_<std::vector<std::string> >("StringVec")            .def(boost::python::vector_indexing_suite<std::vector<std::string> >());    class_<boost::shared_ptr<int> >("IntShare",no_init);    class_<std::vector<boost::shared_ptr<int> > >("IntVec")            .def(boost::python::vector_indexing_suite<std::vector<boost::shared_ptr<int> > >());}

接下来将完成对pycaffe.py文件的模仿

#!/usr/bin/env pythonfrom collections import OrderedDict@propertydef _Net_blobs(self):    if not hasattr(self, '_blobs_dict'):                self._blobs_dict = OrderedDict(zip(self._blob_names, self._blobs))    return self._blobs_dictNet.blobs = _Net_blobs

然后对封装结果的测试如下:

import netne = net.Net() #调用了python类的初始化函数,实际调用的是c++中的Init()函数blo = ne.blobs #此句实际调用了python函数_Net_blobsprint blo

注意net.cpp文件的编译指令

  • 在与net.cpp同级的文件夹路径下,以下是我的makefile文件的代码
PYTHON_INCLUDE = /usr/include/python2.7FILE_NAME = net$(FILE_NAME).so : $(FILE_NAME).o    g++ -shared -Wl,-soname,$(FILE_NAME).so -o $(FILE_NAME).so $(FILE_NAME).o -lpython2.7 -lboost_python$(FILE_NAME).o : $(FILE_NAME).cpp    g++ -c -fPIC $(FILE_NAME).cpp -o $(FILE_NAME).o -I $(PYTHON_INCLUDE)