caffe添加C++层和python层定义

caffe的网络层类型注册机制。
我们知道,caffe中有很多网络层(layer),每个层并单独定义成一个c++类,如卷积层class ConvolutionLayer、class PoolingLayer等。当我们自定义一个层结构,该如何融入caffe架构里去呢?为了实现这个目的,需要首先了解一下caffe层的构建机制。

1. 编写源文件及头文件

首先,编写自己类(Mytest)的头文件、源文件:MytestLayer.hpp, MytestLayer.cpp
在cpp源文件的最后,添加宏定义:
INSTANTIATE_CLASS(MytestLayer);
REGISTER_LAYER_CLASS(Mytest)
这两个宏定义大有来头,分析一下有助于理解caffe的构架

2. 分析宏定义

两个宏定义均可在layer_factory.hpp中查看。

  • INSTANTIATE_CLASS(MytestLayer);
    该宏定义只是在common.hpp中申明了一下MytestLayer 类,没有多少可说的
  • REGISTER_LAYER_CLASS(Mytest)
    该宏定义是重点:

REGISTER_LAYER_CLASS定义如下,Creator_##type##Layer()函数返回层的指针,如:MytestLayer,与MytestLayer 类建立了联系。

    #define REGISTER_LAYER_CLASS(type)                                             \
  template <typename Dtype>                                                    \
  shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \ //函数指针!!!!
  {                                                                            \
    return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \
  }                                                                            \
  REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)

内部调用REGISTER_LAYER_CREATOR(type, creator)宏,定义了两个静态(static)全局变量。

 #define REGISTER_LAYER_CREATOR(type, creator)                                  \
  static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \
  static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \

关注静态变量的类型为:LayerRegisterer,该类只有一个构造函数

template <typename Dtype>
class LayerRegisterer {
 public:
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // LOG(INFO) << "Registering layer type: " << type;
    LayerRegistry<Dtype>::AddCreator(type, creator);/静态成员函数
  }
};

调用LayerRegistry::AddCreator(type, creator); 静态成员函数,LayerRegistry类中的所有成员函数均是静态类型 static

template <typename Dtype>
class LayerRegistry {
 public:
  typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);
  typedef std::map<string, Creator> CreatorRegistry;

  static CreatorRegistry& Registry() {
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;
  }

  // Adds a creator,将类别指针与“type”字符建立联系
  static void AddCreator(const string& type, Creator creator) {
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 0)
        << "Layer type " << type << " already registered.";
    registry[type] = creator; //生成所有layer的类别列表
  }

registry 类型为 std::map<string,Creator> ,建立类别字符与类别指针的关系。key=“Mytest”,Creator=new MytestLayer()
每一个层源文件 *.cpp,均会自动调用此函数,并完成类型注册,生成层列表LayerTypeList

3. 在Net中调用各层生成函数

Net中调用:

 layers_.push_back(LayerRegistry<Dtype>::CreateLayer(layer_param));

在 layer_factory.hpp 中:

  // Get a layer using a LayerParameter.
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type();
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeListString() << ")";
    return registry[type](param);
  }
4 python定义层

打开python 层定义开关: WITH_PYTHON_LAYER := 1
然后便可利用python定义层接口。
查看layer_factory.hpp中python层定义接口:

#ifdef WITH_PYTHON_LAYER
template <typename Dtype>
shared_ptr<Layer<Dtype> > GetPythonLayer(const LayerParameter& param) {
  Py_Initialize();
  try {
    bp::object module = bp::import(param.python_param().module().c_str());///加载自己编写的python文件。是自定义python层与caffe之间的直接接口
    bp::object layer = module.attr(param.python_param().layer().c_str())(param);///获取文件中python的层定义param参数
    return bp::extract<shared_ptr<PythonLayer<Dtype> > >(layer)();////生成pythonLayer指针,查看PythonLayer定义
  } catch (bp::error_already_set) {
    PyErr_Print();
    throw;
  }
}

REGISTER_LAYER_CREATOR(Python, GetPythonLayer);//////////是不是有点眼熟?
#endif

PythonLayer.cpp

namespace caffe {

template <typename Dtype>
class PythonLayer : public Layer<Dtype> {
 public:
  PythonLayer(PyObject* self, const LayerParameter& param) /PythonLayer构造函数
      : Layer<Dtype>(param), self_(bp::handle<>(bp::borrowed(self))) { }

  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    // Disallow PythonLayer in MultiGPU training stage, due to GIL issues
    // Details: https://github.com/BVLC/caffe/issues/2936
    if (this->phase_ == TRAIN && Caffe::solver_count() > 1
        && !ShareInParallel()) {
      LOG(FATAL) << "PythonLayer is not implemented in Multi-GPU training";
    }
    //python定义层的参数/
    self_.attr("param_str") = bp::str(
        this->layer_param_.python_param().param_str());
    phase
    self_.attr("phase") = static_cast<int>(this->phase_);
    //调用setup()创建层///
    self_.attr("setup")(bottom, top);//bottom与top已经初始化
  }
  virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    self_.attr("reshape")(bottom, top);
  }

  virtual inline bool ShareInParallel() const {
    return this->layer_param_.python_param().share_in_parallel();
  }

  virtual inline const char* type() const { return "Python"; }

 protected:
  virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    self_.attr("forward")(bottom, top);
  }
  virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
    self_.attr("backward")(top, propagate_down, bottom);
  }

 private:
  bp::object self_;
};

}  // namespace caffe

成员函数与python的对应关系:
LayerSetUp()—————->setup()
Reshape()—————->reshape()
Forward_cpu()—————->forward()
Backward_cpu()—————->backward()

网络初始化过程中,调用各层的layer::SetUp——>PythonLayer::LayerSetUp(bottom, top);完成最终的网络初始化。

layers_[layer_id]->SetUp(bottom_vecs_[layer_id], top_vecs_[layer_id]);

一个小例子:

layer {
  name: "data"
  type: "Python"  ##pythonLayer类型
  top: "data"
  top: "sem"
  top: "geo"
  python_param {  ### 以下三个属性参数必不可少
    module: "siftflow_layers"
    layer: "SIFTFlowSegDataLayer"
    param_str: "{\'siftflow_dir\': \'../data/sift-flow\', \'seed\': 1337, \'split\': \'trainval\'}"
  }
}
layer {
  name: "conv1_1"
  type: "Convolution"
  bottom: "data"
  top: "conv1_1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 64
    pad: 100
    kernel_size: 3
    stride: 1
  }
}

可以有多个python类型的层,可通过name属性作为区分。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值