有人一直对Caffe does all the bookkeeping for any DAG of layers to ensure correctness of the forward and backward passes。这句话有疑惑。
我给出解释:
首先给出caffe确定前传和反传的整体流程:
首先根据参数文件的字符串到层的注册表中获取层的Creator函数,然后创建层的实例,然后压入到layers_,然后再把对应的层的名字压入到layer_name。
而反传的决定是:
而反传的决定是:
根据学习率来决定,如果学习率是0则表示该层不需要反传。
这样就形成了一个DAG,用户自己定义。
下面的代码解释了前传的顺序是如何决定的:
// 是否需要从root solver获取共享层
if (share_from_root) {
LOG(INFO) << "Sharing layer " << layer_param.name() << " from root net";
// root solver中获取层,然后压入到layers_容器
layers_.push_back(root_net_->layers_[layer_id]);
layers_[layer_id]->SetShared(true);
} else {
// 否则从层的注册表根据层的参数获取层并压入
layers_.push_back(LayerRegistry<Dtype>::CreateLayer(layer_param));
}
// 将层的名字压入到layer_names_
layer_names_.push_back(layer_param.name());
if (Caffe::root_solver()) {
LOG(INFO) << "Creating Layer " << layer_param.name();
}
bool need_backward = false;
下面的代码解释了是否需要反传是如何决定的:
// 遍历该层的每一个参数
for (int param_id = 0; param_id < num_param_blobs; ++param_id) {
// ParamSpec Specifies training parameters (multipliers on global learning constants,
// and the name and other settings used for weight sharing).
// 当前param_id是否小于param_size,如果小于则用层参数中的param_spec,否则用default_param_spec
const ParamSpec* param_spec = (param_id < param_size) ?
&layer_param.param(param_id) : &default_param_spec;
// 参数是否需要反传
const bool param_need_backward = param_spec->lr_mult() != 0;
// 或运算
need_backward |= param_need_backward;
// 根据最终结果设置该层是否需要反传
layers_[layer_id]->set_param_propagate_down(param_id,
param_need_backward);
}
具体的代码在Net.cpp中的init函数里面