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属性作为区分。