您的位置:首页 > Web前端

CAFFE源码学习笔记之七-layer_factory以及layer基类

2017-04-01 16:34 537 查看
一、前言

caffe的几大基本模块中之一——Blob已经梳理完毕,现在该轮到Layer了。这一章先梳理layer基类和工厂类,为data_layer做一下铺垫。

Layer定义了layer的初始化,前向传播,后向传播。前向传播可以计算loss,后向传播计算梯度信息。

layer类是caffe中最为庞大的一类,所以作者使用工厂模式,这样当我们使用caffe中已经有的层或者自己定义的层时只需要注册就行了。

在运行期间,可以通过将LayerParameter protobuf参数传入函数就可以调用该层:

LayerRegistry<Dtype>::CreateLayer(param);


注册层的方式有两种:

其一、如果新建层是由其自身的构造函数创建的,注册如下:

REGISTER_LAYER_CLASS(layername);


其二、如果新建层由其他ctreator函数创建,形如:

template <typename Dtype>
Layer<Dtype*> GetlayernameLayer(const LayerParameter& param) {
// your implementation
}


那么可以通过如下方式注册:

REGISTER_LAYER_CREATOR(layername, GetlayernameLayer)


注意:每层只能注册一次。

二、源码分析

1、layer类

(1)成员变量

/**  protobuf 中存储的层参数*/
LayerParameter layer_param_;
/** 状态: TRAIN or TEST */
Phase phase_;
/** 学习参数 */
vector<shared_ptr<Blob<Dtype> > > blobs_;
/** 是否进行后向传播的标志,数据层等都不进行后向传播 */
vector<bool> param_propagate_down_;

/** loss权重,一般只有最后一层权重不为0,其余层都不计算loss */
vector<Dtype> loss_;


(2)层的构造

构造函数完成层参数的传入,包括该层用于训练还是测试,层中存储权值的blob_。

注意构造函数不能是虚函数。

explicit Layer(const LayerParameter& param)
: layer_param_(param) {
phase_ = param.phase();
if (layer_param_.blobs_size() > 0) {
blobs_.resize(layer_param_.blobs_size());//通常blob_[0]是w,blob_[1]是b
for (int i = 0; i < layer_param_.blobs_size(); ++i) {
blobs_[i].reset(new Blob<Dtype>());
blobs_[i]->FromProto(layer_param_.blobs(i));
}
}
}


SetUp函数是自定义层的“构造函数”,主要做:

第一、检查输入和输出的数量;

第二、真正的setup layer;

第三、根据输入和内部结构重构输出的维度;

第四、设置loss权重;

void SetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CheckBlobCounts(bottom, top);
LayerSetUp(bottom, top);
Reshape(bottom, top);
SetLossWeights(top);
}


下面是每个函数的详解:

virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {}//bottom是输入数据,也就是上一层的输出;
//top是输出,但是这个输出没有经过reshape;
//该函数主要完成的是读入层参数完成层的搭建。


virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) = 0;
//根据输入数据的维度修改输出和中间buffer的维度信息


检查输入输出的个数,有些层需要多个输入或者输出

virtual void CheckBlobCounts(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top)


设置每层的loss权重

inline void SetLossWeights(const vector<Blob<Dtype>*>& top) {
const int num_loss_weights = layer_param_.loss_weight_size();
if (num_loss_weights) {
CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be "
"unspecified or specified once per top blob.";
for (int top_id = 0; top_id < top.size(); ++top_id) {
const Dtype loss_weight = layer_param_.loss_weight(top_id);
if (loss_weight == Dtype(0)) { continue; }//如果权重为0,直接跳过
this->set_loss(top_id, loss_weight);
const int count = top[top_id]->count();
Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
caffe_set(count, loss_weight, loss_multiplier);//将权重信息存在diff_blob中。
}
}
}


(3)前向传播

在前向传播之前需要保证reshape函数已经将top修正到合适的维度。

前向传播可以计算loss,具体的前向计算在具体的层中不一样,所以后面单独讲。

(4)后向传播

后向传播不是每次一层都需要的。

inline bool param_propagate_down(const int param_id) {
return (param_propagate_down_.size() > param_id) ?
param_propagate_down_[param_id] : false;
}
/**
* @brief Sets whether the layer should compute gradients w.r.t. a
*        parameter at a particular index given by param_id.
*/
inline void set_param_propagate_down(const int param_id, const bool value) {
if (param_propagate_down_.size() <= param_id) {
param_propagate_down_.resize(param_id + 1, true);
}
param_propagate_down_[param_id] = value;
}


二、layer_factory

1、创建层注册表:

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_;
}


2、注册creator:

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;
}


3、根据参数调用层:

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);//regisry[type]是函数指针


该类不能被实例化,怎么做?将构造函数置于private域中。

在c++11中,可以直接跟“=delete”

private:
LayerRegistry() {}


最后,作者将注册写成了宏:

#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>)    \

#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)

}  // namespace caffe
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: