caffe源码学习

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

@property
def _Net_blobs(self):
    if not hasattr(self, '_blobs_dict'):    
        self._blobs_dict = OrderedDict(zip(self._blob_names, self._blobs))
    return self._blobs_dict

Net.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 python
from collections import OrderedDict

@property
def _Net_blobs(self):
    if not hasattr(self, '_blobs_dict'):
                self._blobs_dict = OrderedDict(zip(self._blob_names, self._blobs))
    return self._blobs_dict

Net.blobs = _Net_blobs

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

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

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

  • 在与net.cpp同级的文件夹路径下,以下是我的makefile文件的代码
PYTHON_INCLUDE = /usr/include/python2.7
FILE_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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值