AppendBottom();
此函数为该层创建bottom blob,由于网络是堆叠而成,即:当前层的输入bottom blob是前一层的输出top blob, 因此此函数并没有真正的创建blob,只是在将前一层的top blob压入到了bottom_vecs_中,因为所有的输入blob都为前一层的输出blob,所以所有的blob都是在AppendTop函数中创建的top blob。
AppendTop();
此函数为该层创建top blob,该函数真正的new的一个blob的对象。并将top blob的指针压入到top_vecs_中
layers_[layer_id]->SetUp();
前面创建了具体的层,并为层创建了输入bottom blob 和输出top blob。该行代码是启动该层,setup()函数的功能是为创建的blob分配数据内存空间,如有必要还需要调整该层的输入bottom blob 和输出top blob的shape。
AppendParam();
对于某些有参数的层,例如:卷积层、全连接层有weight和bias。该函数主要是修改和参数有关的变量,实际的层参数的blob在上面提到的setup()函数中已经创建。如:将层参数blob的指针压入到params_。
net.h
#ifndef CAFFE_NET_HPP_
#define CAFFE_NET_HPP_
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
namespace caffe {
/**
* @brief Connects Layer%s together into a directed acyclic graph (DAG)
* specified by a NetParameter.
*
* TODO(dox): more thorough description.
*/
template <typename Dtype>
class Net {
/// @brief The network name
string name_;
/// @brief The phase: TRAIN or TEST
Phase phase_;
/// @brief Individual layers in the net
vector<shared_ptr<Layer<Dtype> > > layers_;
vector<string> layer_names_;
map<string, int> layer_names_index_;
vector<bool> layer_need_backward_;
/// @brief the blobs storing intermediate results between the layer.
/*blobs_是真正存储的各个层之间输入输出的blobs. 下面如bottom_vecs_和top_vecs_都是存储的
blobs_里面元素的指针。下面的bottom_id_vecs等也是存的是这个blob是在blobs_里面的id, 只是为了后
面索引方便。另外需要注意的是,其实所有的blob都是top blob,其中某些top blob同时为bottom blob.
因为都是先有输出,才能将这个输入当作下一层的输入。*/
vector<shared_ptr<Blob<Dtype> > > blobs_;
vector<string> blob_names_;
map<string, int> blob_names_index_;
vector<bool> blob_need_backward_;
/// bottom_vecs stores the vectors containing the input for each layer.
/// They don't actually host the blobs (blobs_ does), so we simply store
/// pointers.
vector<vector<Blob<Dtype>*> > bottom_vecs_;//每一层的输入blob是个vector,存的是指针
vector<vector<int> > bottom_id_vecs_;
vector<vector<bool> > bottom_need_backward_;
/// top_vecs stores the vectors containing the output for each layer
vector<vector<Blob<Dtype>*> > top_vecs_; //每一层的输出blob是个vector,存的是指针
vector<vector<int> > top_id_vecs_;
/// Vector of weight in the loss (or objective) function of each net blob,
/// indexed by blob_id.
vector<Dtype> blob_loss_weights_;
vector<vector<int> > param_id_vecs_;
vector<int> param_owners_;
vector<string> param_display_names_;
vector<pair<int, int> > param_layer_indices_;
map<string, int> param_names_index_;
/// blob indices for the input and the output of the net
vector<int> net_input_blob_indices_;
vector<int> net_output_blob_indices_;
vector<Blob<Dtype>*> net_input_blobs_;
vector<Blob<Dtype>*> net_output_blobs_;
/// The parameters in the network.
/*就是内部的权重blob和bias blob,从各个layer中query出来放到这个vector中*/
vector<shared_ptr<Blob<Dtype> > > params_;
vector<Blob<Dtype>*> learnable_params_;
/**
* The mapping from params_ -> learnable_params_: we have
* learnable_param_ids_.size() == params_.size(),
* and learnable_params_[learnable_param_ids_[i]] == params_[i].get()
* if and only if params_[i] is an "owner"; otherwise, params_[i] is a sharer
* and learnable_params_[learnable_param_ids_[i]] gives its owner.
*/
vector<int> learnable_param_ids_;
/// the learning rate multipliers for learnable_params_
vector<float> params_lr_;
vector<bool> has_params_lr_;
/// the weight decay multipliers for learnable_params_
vector<float> params_weight_decay_;
vector<bool> has_params_decay_;
/// The bytes of memory used by this net,这个net使用的memory的大小
size_t memory_used_;
/// Whether to compute and display debug info for the net.
bool debug_info_;
// Callbacks
vector<Callback*> before_forward_;
vector<Callback*> after_forward_;
vector<Callback*> before_backward_;
vector<Callback*> after_backward_;
DISABLE_COPY_AND_ASSIGN(Net);
};
} // namespace caffe
#endif // CAFFE_NET_HPP_
net.cpp
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#ifdef USE_HDF5
#include "hdf5.h"
#endif // USE_HDF5
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/net.hpp"
#include "caffe/parallel.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/hdf5.hpp"
#include "caffe/util/insert_splits.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/upgrade_proto.hpp"
namespace caffe {
template <typename Dtype>
Net<Dtype>::Net(const NetParameter& param) {
Init(param);
}
template <typename Dtype>
Net<Dtype>::Net(const string& param_file, Phase phase,
const int level, const vector<string>* stages) {
NetParameter param;
ReadNetParamsFromTextFileOrDie(param_file, ¶m);
// Set phase, stages and level
/*关于state的这三个参数,是定义了一些规则来对网络的某些层的动态添加和删减,可以参考如下链接:
https://blog.youkuaiyun.com/qq_28660035/article/details/80306772*/
param.mutable_state()->set_phase(phase);
if (stages != NULL) {
for (int i = 0; i < stages->size(); i++) {
param.mutable_state()->add_stage((*stages)[i]);
}
}
param.mutable_state()->set_level(level);
Init(param);
}
template <typename Dtype>
void Net<Dtype>::Init(const NetParameter& in_param) {
// Set phase from the state.
phase_ = in_param.state().phase();
// Filter layers based on their include/exclude rules and
// the current NetState.
/*这里就是根据NetParameter里面的state等规格来过滤符合规则的layer来组成所需要的网络*/
NetParameter filtered_param;
FilterNet(in_param, &filtered_param);
LOG_IF(INFO, Caffe::root_solver())
<< "Initializing net from parameters: " << std::endl
<< filtered_param.DebugString();
// Create a copy of filtered_param with splits added where necessary.
NetParameter param;
/*这个函数是为了处理有些输出blob作为多个layer的输入的情况,可以参考:
https://www.cnblogs.com/Relu110/p/12008459.html*/
InsertSplits(filtered_param, ¶m);
// Basically, build all the layers and set up their connections.
/*到这里,就基本形成了所需要的NetParameter param,然后利用param来创建网络*/
name_ = param.name();
/*blob name &blob id的map*/
map<string, int> blob_name_to_idx;
/*所有的top blob name,在Append_top函数里面填充的。*/
set<string> available_blobs;
memory_used_ = 0;
// For each layer, set up its input and output
/*下面六个变量都是vector<vector>, 也就是每个layer有一个vector,因为有些层有多个输出或者输入
所以每一个layer都是一个vector.*/
bottom_vecs_.resize(param.layer_size());
top_vecs_.resize(param.layer_size());
bottom_id_vecs_.resize(param.layer_size());
param_id_vecs_.resize(param.layer_size());
top_id_vecs_.resize(param.layer_size());
bottom_need_backward_.resize(param.layer_size());
for (int layer_id = 0; layer_id < param.layer_size(); ++layer_id) { //这个for循环就是遍历每个layer了
// Inherit phase from net if unset.
if (!param.layer(layer_id).has_phase()) {
//如果layer param中没有指定phase, 则都设置为本net的phase, 也就是test还是train
param.mutable_layer(layer_id)->set_phase(phase_);
}
// Setup layer.
const LayerParameter& layer_param = param.layer(layer_id);
/*这里有个check, 如果layer param中设置了propagate_down, 则propagate_down是个repeated bool ,
他的数量就需要和bottom blob的size相同,也就是每个bottom设置一个propagate down.是为了表示
该bottom是否需要反向传播*/
if (layer_param.propagate_down_size() > 0) {
CHECK_EQ(layer_param.propagate_down_size(),
layer_param.bottom_size())
<< "propagate_down param must be specified "
<< "either 0 or bottom_size times ";
}
/*利用该layer param创建一个layer*/
layers_.push_back(LayerRegistry<Dtype>::CreateLayer(layer_param));
layer_names_.push_back(layer_param.name());
LOG_IF(INFO, Caffe::root_solver())
<< "Creating Layer " << layer_param.name();
bool need_backward = false;
// Figure out this layer's input and output
/*初始化bottom,我们之前说过,blob都是在AppendTop中创建的,AppendBottom里面只是把相应的上一层的
Top blob的指针放到bottom的vector中,那为什么这里先运行AppendBottom,再运行AppendTop呢,
因为第一层都是数据输入层,没有bottom blob, 只有Top blob, 所以这样是可以的。*/
for (int bottom_id = 0; bottom_id < layer_param.bottom_size();
++bottom_id) {
const int blob_id = AppendBottom(param, layer_id, bottom_id,
&available_blobs, &blob_name_to_idx);
// If a blob needs backward, this layer should provide it.
/*从这里可以看出,只要一个bottom需要backward, 则这个layer需要backward*/
need_backward |= blob_need_backward_[blob_id];
}
int num_top = layer_param.top_size();
/*初始化top blob*/
for (int top_id = 0; top_id < num_top; ++top_id) {
AppendTop(param, layer_id, top_id, &available_blobs, &blob_name_to_idx);
// Collect Input layer tops as Net inputs.
if (layer_param.type() == "Input") {
const int blob_id = blobs_.size() - 1;
net_input_blob_indices_.push_back(blob_id);
net_input_blobs_.push_back(blobs_[blob_id].get());
}
}
// If the layer specifies that AutoTopBlobs() -> true and the LayerParameter
// specified fewer than the required number (as specified by
// ExactNumTopBlobs() or MinTopBlobs()), allocate them here.
Layer<Dtype>* layer = layers_[layer_id].get();
/*这里是如果layer param中提供的top blob数量不够,同时允许自己内部创建(AutoTopBlobs)
则内部创建所需要的top blob,注意,这些top blob一般不能作为下一层的输入,所以也没有放到
net_input_blobs_等vector中*/
if (layer->AutoTopBlobs()) {
const int needed_num_top =
std::max(layer->MinTopBlobs(), layer->ExactNumTopBlobs());
for (; num_top < needed_num_top; ++num_top) {
// Add "anonymous" top blobs -- do not modify available_blobs or
// blob_name_to_idx as we don't want these blobs to be usable as input
// to other layers.
AppendTop(param, layer_id, num_top, NULL, NULL);
}
}
// After this layer is connected, set it up.
/*setup layer*/
layers_[layer_id]->SetUp(bottom_vecs_[layer_id], top_vecs_[layer_id]);
LOG_IF(INFO, Caffe::root_solver())
<< "Setting up " << layer_names_[layer_id];
for (int top_id = 0; top_id < top_vecs_[layer_id].size(); ++top_id) {
/*这里是初始化blob_loss_weights_,也就是每个blob创建一个Loss,放到这个vector中,
top_id_vecs_[layer_id][top_id]数值就是该layer_id的top_id的blob的blob_id是多少,因为
blob_id是从0开始排列的,所以最终就是创建max_blob_id + 1个项放到blob_loss_weights_
中,也就是每个blob都有一个对应的Loss_weigth, 在这个vector中的索引也是使用blob_id*/
if (blob_loss_weights_.size() <= top_id_vecs_[layer_id][top_id]) {
blob_loss_weights_.resize(top_id_vecs_[layer_id][top_id] + 1, Dtype(0));
}
/*利用layer的loss函数来初始化这个值。*/
blob_loss_weights_[top_id_vecs_[layer_id][top_id]] = layer->loss(top_id);
//blob的shape是在layer内部reshape出来的。
LOG_IF(INFO, Caffe::root_solver())
<< "Top shape: " << top_vecs_[layer_id][top_id]->shape_string();
/*init的时候loss一般都为0*/
if (layer->loss(top_id)) {
LOG_IF(INFO, Caffe::root_solver())
<< " with loss weight " << layer->loss(top_id);
}
/*所有top blob用到memory就是总的消耗memory*/
memory_used_ += top_vecs_[layer_id][top_id]->count();
}
LOG_IF(INFO, Caffe::root_solver())
<< "Memory required for data: " << memory_used_ * sizeof(Dtype);
/*在layerparam中有一个param参数,是一些training参数*/
const int param_size = layer_param.param_size();
const int num_param_blobs = layers_[layer_id]->blobs().size();
CHECK_LE(param_size, num_param_blobs)
<< "Too many params specified for layer " << layer_param.name();
ParamSpec default_param_spec;
for (int param_id = 0; param_id < num_param_blobs; ++param_id) {
const ParamSpec* param_spec = (param_id < param_size) ?
&layer_param.param(param_id) : &default_param_spec;
/*我们从这里可以看到,只要该layer中有设置学习率,则该layer就需要反向传播*/
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);
}
/*AppendParam是将layer内部的权重blob和偏置blob和配置文件中的学习率等参数匹配起来,
并且将layer内部的这两个blob指针放到一个vector中,这样后面就可以根据学习率等来更新
里面的参数,其实从这里也能看出来,大部分管理的工作其实是net内部做的,layer内部只是
组织了这些数据*/
for (int param_id = 0; param_id < num_param_blobs; ++param_id) {
AppendParam(param, layer_id, param_id);
}
// Finally, set the backward flag
layer_need_backward_.push_back(need_backward);
if (need_backward) {
/*只要该layer需要backward, 则该layer所有的blob都需要backward.*/
for (int top_id = 0; top_id < top_id_vecs_[layer_id].size(); ++top_id) {
blob_need_backward_[top_id_vecs_[layer_id][top_id]] = true;
}
}
}
// Go through the net backwards to determine which blobs contribute to the
// loss. We can skip backward computation for blobs that don't contribute
// to the loss.
// Also checks if all bottom blobs don't need backward computation (possible
// because the skip_propagate_down param) and so we can skip backward
// computation for the entire layer
/*看上面注释可以知道,这里遍历每一层,筛选出对Loss有贡献的层,和需要
计算backward的blob.*/
set<string> blobs_under_loss;
set<string> blobs_skip_backp;
for (int layer_id = layers_.size() - 1; layer_id >= 0; --layer_id) {
bool layer_contributes_loss = false;
bool layer_skip_propagate_down = true;
for (int top_id = 0; top_id < top_vecs_[layer_id].size(); ++top_id) {
const string& blob_name = blob_names_[top_id_vecs_[layer_id][top_id]];
if (layers_[layer_id]->loss(top_id) ||
(blobs_under_loss.find(blob_name) != blobs_under_loss.end())) {
layer_contributes_loss = true;
}
if (blobs_skip_backp.find(blob_name) == blobs_skip_backp.end()) {
layer_skip_propagate_down = false;
}
if (layer_contributes_loss && !layer_skip_propagate_down)
break;
}
// If this layer can skip backward computation, also all his bottom blobs
// don't need backpropagation
if (layer_need_backward_[layer_id] && layer_skip_propagate_down) {
layer_need_backward_[layer_id] = false;
for (int bottom_id = 0; bottom_id < bottom_vecs_[layer_id].size();
++bottom_id) {
bottom_need_backward_[layer_id][bottom_id] = false;
}
}
if (!layer_contributes_loss) { layer_need_backward_[layer_id] = false; }
if (Caffe::root_solver()) {
if (layer_need_backward_[layer_id]) {
LOG(INFO) << layer_names_[layer_id] << " needs backward computation.";
} else {
LOG(INFO) << layer_names_[layer_id]
<< " does not need backward computation.";
}
}
for (int bottom_id = 0; bottom_id < bottom_vecs_[layer_id].size();
++bottom_id) {
if (layer_contributes_loss) {
const string& blob_name =
blob_names_[bottom_id_vecs_[layer_id][bottom_id]];
blobs_under_loss.insert(blob_name);
} else {
bottom_need_backward_[layer_id][bottom_id] = false;
}
if (!bottom_need_backward_[layer_id][bottom_id]) {
const string& blob_name =
blob_names_[bottom_id_vecs_[layer_id][bottom_id]];
blobs_skip_backp.insert(blob_name);
}
}
}
// Handle force_backward if needed.
if (param.force_backward()) {
for (int layer_id = 0; layer_id < layers_.size(); ++layer_id) {
layer_need_backward_[layer_id] = true;
for (int bottom_id = 0;
bottom_id < bottom_need_backward_[layer_id].size(); ++bottom_id) {
bottom_need_backward_[layer_id][bottom_id] =
bottom_need_backward_[layer_id][bottom_id] ||
layers_[layer_id]->AllowForceBackward(bottom_id);
blob_need_backward_[bottom_id_vecs_[layer_id][bottom_id]] =
blob_need_backward_[bottom_id_vecs_[layer_id][bottom_id]] ||
bottom_need_backward_[layer_id][bottom_id];
}
for (int param_id = 0; param_id < layers_[layer_id]->blobs().size();
++param_id) {
layers_[layer_id]->set_param_propagate_down(param_id, true);
}
}
}
// In the end, all remaining blobs are considered output blobs.
for (set<string>::iterator it = available_blobs.begin();
it != available_blobs.end(); ++it) {
LOG_IF(INFO, Caffe::root_solver())
<< "This network produces output " << *it;
net_output_blobs_.push_back(blobs_[blob_name_to_idx[*it]].get());
net_output_blob_indices_.push_back(blob_name_to_idx[*it]);
}
for (size_t blob_id = 0; blob_id < blob_names_.size(); ++blob_id) {
blob_names_index_[blob_names_[blob_id]] = blob_id;
}
for (size_t layer_id = 0; layer_id < layer_names_.size(); ++layer_id) {
layer_names_index_[layer_names_[layer_id]] = layer_id;
}
ShareWeights();
debug_info_ = param.debug_info();
LOG_IF(INFO, Caffe::root_solver()) << "Network initialization done.";
}
/*这个函数是为了根据Layer的phase(test or train)来分配网络。因为一个train网络一般是需要一边
training, 一边test, 所以同时需要两个网络,所以一般在prototext文件中,有些layer会有include字段,
来表明这个layer是test网络还是train网络。 如果不写,则是两个网络共用的layer. 所以这个函数就是根据
这个include字段来分析该layer是哪个网络的,根据各个layer的属性集合在一起组成各个网络的结构。*/
template <typename Dtype>
void Net<Dtype>::FilterNet(const NetParameter& param,
NetParameter* param_filtered) {
NetState net_state(param.state());
param_filtered->CopyFrom(param);
param_filtered->clear_layer();
for (int i = 0; i < param.layer_size(); ++i) {
const LayerParameter& layer_param = param.layer(i);
const string& layer_name = layer_param.name();
CHECK(layer_param.include_size() == 0 || layer_param.exclude_size() == 0)
<< "Specify either include rules or exclude rules; not both.";
// If no include rules are specified, the layer is included by default and
// only excluded if it meets one of the exclude rules.
bool layer_included = (layer_param.include_size() == 0);
for (int j = 0; layer_included && j < layer_param.exclude_size(); ++j) {
if (StateMeetsRule(net_state, layer_param.exclude(j), layer_name)) {
layer_included = false;
}
}
for (int j = 0; !layer_included && j < layer_param.include_size(); ++j) {
if (StateMeetsRule(net_state, layer_param.include(j), layer_name)) {
layer_included = true;
}
}
if (layer_included) {
param_filtered->add_layer()->CopyFrom(layer_param);
}
}
}
template <typename Dtype>
bool Net<Dtype>::StateMeetsRule(const NetState& state,
const NetStateRule& rule, const string& layer_name) {
// Check whether the rule is broken due to phase.
if (rule.has_phase()) {
if (rule.phase() != state.phase()) {
LOG_IF(INFO, Caffe::root_solver())
<< "The NetState phase (" << state.phase()
<< ") differed from the phase (" << rule.phase()
<< ") specified by a rule in layer " << layer_name;
return false;
}
}
// Check whether the rule is broken due to min level.
if (rule.has_min_level()) {
if (state.level() < rule.min_level()) {
LOG_IF(INFO, Caffe::root_solver())
<< "The NetState level (" << state.level()
<< ") is above the min_level (" << rule.min_level()
<< ") specified by a rule in layer " << layer_name;
return false;
}
}
// Check whether the rule is broken due to max level.
if (rule.has_max_level()) {
if (state.level() > rule.max_level()) {
LOG_IF(INFO, Caffe::root_solver())
<< "The NetState level (" << state.level()
<< ") is above the max_level (" << rule.max_level()
<< ") specified by a rule in layer " << layer_name;
return false;
}
}
// Check whether the rule is broken due to stage. The NetState must
// contain ALL of the rule's stages to meet it.
for (int i = 0; i < rule.stage_size(); ++i) {
// Check that the NetState contains the rule's ith stage.
bool has_stage = false;
for (int j = 0; !has_stage && j < state.stage_size(); ++j) {
if (rule.stage(i) == state.stage(j)) { has_stage = true; }
}
if (!has_stage) {
LOG_IF(INFO, Caffe::root_solver())
<< "The NetState did not contain stage '" << rule.stage(i)
<< "' specified by a rule in layer " << layer_name;
return false;
}
}
// Check whether the rule is broken due to not_stage. The NetState must
// contain NONE of the rule's not_stages to meet it.
for (int i = 0; i < rule.not_stage_size(); ++i) {
// Check that the NetState contains the rule's ith not_stage.
bool has_stage = false;
for (int j = 0; !has_stage && j < state.stage_size(); ++j) {
if (rule.not_stage(i) == state.stage(j)) { has_stage = true; }
}
if (has_stage) {
LOG_IF(INFO, Caffe::root_solver())
<< "The NetState contained a not_stage '" << rule.not_stage(i)
<< "' specified by a rule in layer " << layer_name;
return false;
}
}
return true;
}
// Helper for Net::Init: add a new top blob to the net.
/*这里会创建真正的输入输出所用到的blobs,放到blobs_中, 然后将他们的指针和在在blobs_中的
id放到top_vecs_和top_id_vecs_中。这里要注意的时其实所有的blob都是top blob, 某一些top blob同时
为bottom blob. 所以所有的blobs_里面的blob都是在这个函数中创建的。这个函数在net的init函数中调用的,
我们可以看到需要传入layer_id, 所以每个layer都需要调用一次这个函数,将该Layer param中定义的blob创建好*/
template <typename Dtype>
void Net<Dtype>::AppendTop(const NetParameter& param, const int layer_id,
const int top_id, set<string>* available_blobs,
map<string, int>* blob_name_to_idx) {
/*利用传入的layer param创建一个LayerParameter*/
shared_ptr<LayerParameter> layer_param(
new LayerParameter(param.layer(layer_id)));
const string& blob_name = (layer_param->top_size() > top_id) ?
layer_param->top(top_id) : "(automatic)";
// Check if we are doing in-place computation
if (blob_name_to_idx && layer_param->bottom_size() > top_id &&
blob_name == layer_param->bottom(top_id)) {
// In-place computation
LOG_IF(INFO, Caffe::root_solver())
<< layer_param->name() << " -> " << blob_name << " (in-place)";
top_vecs_[layer_id].push_back(blobs_[(*blob_name_to_idx)[blob_name]].get());
top_id_vecs_[layer_id].push_back((*blob_name_to_idx)[blob_name]);
} else if (blob_name_to_idx &&
blob_name_to_idx->find(blob_name) != blob_name_to_idx->end()) {
// If we are not doing in-place computation but have duplicated blobs,
// raise an error.
LOG(FATAL) << "Top blob '" << blob_name
<< "' produced by multiple sources.";
} else {
// Normal output.
if (Caffe::root_solver()) {
LOG(INFO) << layer_param->name() << " -> " << blob_name;
}
/*创建blob*/
shared_ptr<Blob<Dtype> > blob_pointer(new Blob<Dtype>());
const int blob_id = blobs_.size();
blobs_.push_back(blob_pointer);
blob_names_.push_back(blob_name);
blob_need_backward_.push_back(false);
/*这个map里面放的是id和name的对应关系*/
if (blob_name_to_idx) { (*blob_name_to_idx)[blob_name] = blob_id; }
/*下面这两个vector<vector>一个放置的是每一层的blob的blob_id, 另外一个放的是对应的blob的指针*/
top_id_vecs_[layer_id].push_back(blob_id);
top_vecs_[layer_id].push_back(blob_pointer.get());
}
/*将创建的blob的名字写入available_blobs,便于AppendBottom的时候查找这个blob是否存在,
因为所有的bottom blob都是top blob. */
if (available_blobs) { available_blobs->insert(blob_name); }
}
// Helper for Net::Init: add a new bottom blob to the net.
template <typename Dtype>
int Net<Dtype>::AppendBottom(const NetParameter& param, const int layer_id,
const int bottom_id, set<string>* available_blobs,
map<string, int>* blob_name_to_idx) {
const LayerParameter& layer_param = param.layer(layer_id);
const string& blob_name = layer_param.bottom(bottom_id);
/*available_blobs是top blob的集合,所以查看这个blob是否存在,如果不存在,则error.因为
所有的bottom blob都是top blob.*/
if (available_blobs->find(blob_name) == available_blobs->end()) {
LOG(FATAL) << "Unknown bottom blob '" << blob_name << "' (layer '"
<< layer_param.name() << "', bottom index " << bottom_id << ")";
}
const int blob_id = (*blob_name_to_idx)[blob_name];
LOG_IF(INFO, Caffe::root_solver())
<< layer_names_[layer_id] << " <- " << blob_name;
bottom_vecs_[layer_id].push_back(blobs_[blob_id].get());
bottom_id_vecs_[layer_id].push_back(blob_id);
available_blobs->erase(blob_name);
bool need_backward = blob_need_backward_[blob_id];
// Check if the backpropagation on bottom_id should be skipped
if (layer_param.propagate_down_size() > 0) {
need_backward = layer_param.propagate_down(bottom_id);
}
bottom_need_backward_[layer_id].push_back(need_backward);
return blob_id;
}
/*layer param中的param参数是training的参数,比如学习率,一般的Layer中会有两个学习率,一个
是权重,一个是偏置,各对应一个layer内部的blob,这个函数就是将各个layer的param和自己内部的
权重blob和偏置blob对应起来,便于利用param中定义的参数来更新layer内部blob的权重偏置等。*/
template <typename Dtype>
void Net<Dtype>::AppendParam(const NetParameter& param, const int layer_id,
const int param_id) {
const LayerParameter& layer_param = layers_[layer_id]->layer_param();
const int param_size = layer_param.param_size();
string param_name =
(param_size > param_id) ? layer_param.param(param_id).name() : "";
/*如果param内部设置了name,则放进param_display_names,如果没有设置name, 则用param_id来代替,
这个param_id是该layer内部的id,如果该layer内部有两个的话,就是0和1*/
if (param_name.size()) {
param_display_names_.push_back(param_name);
} else {
ostringstream param_display_name;
param_display_name << param_id;
param_display_names_.push_back(param_display_name.str());
}
/*这个net_param_id就是param总的id了,param_id_vecs_把该Layer的param的id都记录在这里,
便于后面使用时查找*/
const int net_param_id = params_.size();
/*这个params_里面盛的就是各个layer内部的权重blob和偏置blob, 通过下面几个vector,
就可以将layer内部的blob和对应的param联系起来。*/
params_.push_back(layers_[layer_id]->blobs()[param_id]);
param_id_vecs_[layer_id].push_back(net_param_id);
param_layer_indices_.push_back(make_pair(layer_id, param_id));
ParamSpec default_param_spec;
const ParamSpec* param_spec = (layer_param.param_size() > param_id) ?
&layer_param.param(param_id) : &default_param_spec;
if (!param_size || !param_name.size() || (param_name.size() &&
param_names_index_.find(param_name) == param_names_index_.end())) {
// This layer "owns" this parameter blob -- it is either anonymous
// (i.e., not given a param_name) or explicitly given a name that we
// haven't already seen.
param_owners_.push_back(-1);
if (param_name.size()) {
param_names_index_[param_name] = net_param_id;
}
const int learnable_param_id = learnable_params_.size();
/*可学习的blobs*/
learnable_params_.push_back(params_[net_param_id].get());
learnable_param_ids_.push_back(learnable_param_id);
has_params_lr_.push_back(param_spec->has_lr_mult());
has_params_decay_.push_back(param_spec->has_decay_mult());
/*学习率*/
params_lr_.push_back(param_spec->lr_mult());
/*正则化的权重的衰减率,防止过拟合的。*/
params_weight_decay_.push_back(param_spec->decay_mult());
} else {
// Named param blob with name we've seen before: share params,和别的layer share param
const int owner_net_param_id = param_names_index_[param_name];
param_owners_.push_back(owner_net_param_id);
const pair<int, int>& owner_index =
param_layer_indices_[owner_net_param_id];
const int owner_layer_id = owner_index.first;
const int owner_param_id = owner_index.second;
LOG_IF(INFO, Caffe::root_solver()) << "Sharing parameters '" << param_name
<< "' owned by "
<< "layer '" << layer_names_[owner_layer_id] << "', param "
<< "index " << owner_param_id;
Blob<Dtype>* this_blob = layers_[layer_id]->blobs()[param_id].get();
Blob<Dtype>* owner_blob =
layers_[owner_layer_id]->blobs()[owner_param_id].get();
const int param_size = layer_param.param_size();
if (param_size > param_id && (layer_param.param(param_id).share_mode() ==
ParamSpec_DimCheckMode_PERMISSIVE)) {
// Permissive dimension checking -- only check counts are the same.
CHECK_EQ(this_blob->count(), owner_blob->count())
<< "Cannot share param '" << param_name << "' owned by layer '"
<< layer_names_[owner_layer_id] << "' with layer '"
<< layer_names_[layer_id] << "'; count mismatch. Owner layer param "
<< "shape is " << owner_blob->shape_string() << "; sharing layer "
<< "shape is " << this_blob->shape_string();
} else {
// Strict dimension checking -- all dims must be the same.
CHECK(this_blob->shape() == owner_blob->shape())
<< "Cannot share param '" << param_name << "' owned by layer '"
<< layer_names_[owner_layer_id] << "' with layer '"
<< layer_names_[layer_id] << "'; shape mismatch. Owner layer param "
<< "shape is " << owner_blob->shape_string() << "; sharing layer "
<< "expects shape " << this_blob->shape_string();
}
const int learnable_param_id = learnable_param_ids_[owner_net_param_id];
learnable_param_ids_.push_back(learnable_param_id);
if (param_spec->has_lr_mult()) {
if (has_params_lr_[learnable_param_id]) {
CHECK_EQ(param_spec->lr_mult(), params_lr_[learnable_param_id])
<< "Shared param '" << param_name << "' has mismatched lr_mult.";
} else {
has_params_lr_[learnable_param_id] = true;
params_lr_[learnable_param_id] = param_spec->lr_mult();
}
}
if (param_spec->has_decay_mult()) {
if (has_params_decay_[learnable_param_id]) {
CHECK_EQ(param_spec->decay_mult(),
params_weight_decay_[learnable_param_id])
<< "Shared param '" << param_name << "' has mismatched decay_mult.";
} else {
has_params_decay_[learnable_param_id] = true;
params_weight_decay_[learnable_param_id] = param_spec->decay_mult();
}
}
}
}
/*从start到end层执行forward运算,返回各个层的loss之和*/
template <typename Dtype>
Dtype Net<Dtype>::ForwardFromTo(int start, int end) {
CHECK_GE(start, 0);
CHECK_LT(end, layers_.size());
Dtype loss = 0;
for (int i = start; i <= end; ++i) {
for (int c = 0; c < before_forward_.size(); ++c) {
before_forward_[c]->run(i);
}
Dtype layer_loss = layers_[i]->Forward(bottom_vecs_[i], top_vecs_[i]);
loss += layer_loss;
if (debug_info_) { ForwardDebugInfo(i); }
for (int c = 0; c < after_forward_.size(); ++c) {
after_forward_[c]->run(i);
}
}
return loss;
}
/*计算从start层到最后一层的forward运算,返回loss之和*/
template <typename Dtype>
Dtype Net<Dtype>::ForwardFrom(int start) {
return ForwardFromTo(start, layers_.size() - 1);
}
template <typename Dtype>
Dtype Net<Dtype>::ForwardTo(int end) {
return ForwardFromTo(0, end);
}
template <typename Dtype>
const vector<Blob<Dtype>*>& Net<Dtype>::Forward(Dtype* loss) {
if (loss != NULL) {
*loss = ForwardFromTo(0, layers_.size() - 1);
} else {
ForwardFromTo(0, layers_.size() - 1);
}
return net_output_blobs_;
}
template <typename Dtype>
const vector<Blob<Dtype>*>& Net<Dtype>::Forward(
const vector<Blob<Dtype>*> & bottom, Dtype* loss) {
LOG_EVERY_N(WARNING, 1000) << "DEPRECATED: Forward(bottom, loss) "
<< "will be removed in a future version. Use Forward(loss).";
// Copy bottom to net bottoms
for (int i = 0; i < bottom.size(); ++i) {
net_input_blobs_[i]->CopyFrom(*bottom[i]);
}
return Forward(loss);
}
template <typename Dtype>
void Net<Dtype>::BackwardFromTo(int start, int end) {
CHECK_GE(end, 0);
CHECK_LT(start, layers_.size());
for (int i = start; i >= end; --i) {
for (int c = 0; c < before_backward_.size(); ++c) {
before_backward_[c]->run(i);
}
if (layer_need_backward_[i]) {
layers_[i]->Backward(
top_vecs_[i], bottom_need_backward_[i], bottom_vecs_[i]);
if (debug_info_) { BackwardDebugInfo(i); }
}
for (int c = 0; c < after_backward_.size(); ++c) {
after_backward_[c]->run(i);
}
}
}
template <typename Dtype>
void Net<Dtype>::ForwardDebugInfo(const int layer_id) {
for (int top_id = 0; top_id < top_vecs_[layer_id].size(); ++top_id) {
const Blob<Dtype>& blob = *top_vecs_[layer_id][top_id];
const string& blob_name = blob_names_[top_id_vecs_[layer_id][top_id]];
const Dtype data_abs_val_mean = blob.asum_data() / blob.count();
LOG_IF(INFO, Caffe::root_solver())
<< " [Forward] "
<< "Layer " << layer_names_[layer_id]
<< ", top blob " << blob_name
<< " data: " << data_abs_val_mean;
}
for (int param_id = 0; param_id < layers_[layer_id]->blobs().size();
++param_id) {
const Blob<Dtype>& blob = *layers_[layer_id]->blobs()[param_id];
const int net_param_id = param_id_vecs_[layer_id][param_id];
const string& blob_name = param_display_names_[net_param_id];
const Dtype data_abs_val_mean = blob.asum_data() / blob.count();
LOG_IF(INFO, Caffe::root_solver())
<< " [Forward] "
<< "Layer " << layer_names_[layer_id]
<< ", param blob " << blob_name
<< " data: " << data_abs_val_mean;
}
}
template <typename Dtype>
void Net<Dtype>::BackwardDebugInfo(const int layer_id) {
const vector<Blob<Dtype>*>& bottom_vec = bottom_vecs_[layer_id];
for (int bottom_id = 0; bottom_id < bottom_vec.size(); ++bottom_id) {
if (!bottom_need_backward_[layer_id][bottom_id]) { continue; }
const Blob<Dtype>& blob = *bottom_vec[bottom_id];
const string& blob_name = blob_names_[bottom_id_vecs_[layer_id][bottom_id]];
const Dtype diff_abs_val_mean = blob.asum_diff() / blob.count();
LOG_IF(INFO, Caffe::root_solver())
<< " [Backward] "
<< "Layer " << layer_names_[layer_id]
<< ", bottom blob " << blob_name
<< " diff: " << diff_abs_val_mean;
}
for (int param_id = 0; param_id < layers_[layer_id]->blobs().size();
++param_id) {
if (!layers_[layer_id]->param_propagate_down(param_id)) { continue; }
const Blob<Dtype>& blob = *layers_[layer_id]->blobs()[param_id];
const Dtype diff_abs_val_mean = blob.asum_diff() / blob.count();
LOG_IF(INFO, Caffe::root_solver())
<< " [Backward] "
<< "Layer " << layer_names_[layer_id]
<< ", param blob " << param_id
<< " diff: " << diff_abs_val_mean;
}
}
template <typename Dtype>
void Net<Dtype>::UpdateDebugInfo(const int param_id) {
const Blob<Dtype>& blob = *params_[param_id];
const int param_owner = param_owners_[param_id];
const string& layer_name = layer_names_[param_layer_indices_[param_id].first];
const string& param_display_name = param_display_names_[param_id];
const Dtype diff_abs_val_mean = blob.asum_diff() / blob.count();
if (param_owner < 0) {
const Dtype data_abs_val_mean = blob.asum_data() / blob.count();
LOG_IF(INFO, Caffe::root_solver())
<< " [Update] Layer " << layer_name
<< ", param " << param_display_name
<< " data: " << data_abs_val_mean
<< "; diff: " << diff_abs_val_mean;
} else {
const string& owner_layer_name =
layer_names_[param_layer_indices_[param_owner].first];
LOG_IF(INFO, Caffe::root_solver())
<< " [Update] Layer " << layer_name
<< ", param blob " << param_display_name
<< " (owned by layer " << owner_layer_name << ", " << "param "
<< param_display_names_[param_owners_[param_id]] << ")"
<< " diff: " << diff_abs_val_mean;
}
}
/*利用本net的各个layer的参数来初始化另外一个net,其实就是把本layer的内部权重和偏置参数copy到
target net中的各个对应layer中,这两个net的结构必须是一致的。*/
template <typename Dtype>
void Net<Dtype>::ShareTrainedLayersWith(const Net* other) {
int num_source_layers = other->layers().size();
for (int i = 0; i < num_source_layers; ++i) {
Layer<Dtype>* source_layer = other->layers()[i].get();
const string& source_layer_name = other->layer_names()[i];
int target_layer_id = 0;
while (target_layer_id != layer_names_.size() &&
layer_names_[target_layer_id] != source_layer_name) {
++target_layer_id;
}
if (target_layer_id == layer_names_.size()) {
LOG(INFO) << "Ignoring source layer " << source_layer_name;
continue;
}
DLOG(INFO) << "Copying source layer " << source_layer_name;
vector<shared_ptr<Blob<Dtype> > >& target_blobs =
layers_[target_layer_id]->blobs();
CHECK_EQ(target_blobs.size(), source_layer->blobs().size())
<< "Incompatible number of blobs for layer " << source_layer_name;
for (int j = 0; j < target_blobs.size(); ++j) {
Blob<Dtype>* source_blob = source_layer->blobs()[j].get();
CHECK(target_blobs[j]->shape() == source_blob->shape())
<< "Cannot share param " << j << " weights from layer '"
<< source_layer_name << "'; shape mismatch. Source param shape is "
<< source_blob->shape_string() << "; target param shape is "
<< target_blobs[j]->shape_string();
target_blobs[j]->ShareData(*source_blob);
}
}
}
template <typename Dtype>
void Net<Dtype>::BackwardFrom(int start) {
BackwardFromTo(start, 0);
}
template <typename Dtype>
void Net<Dtype>::BackwardTo(int end) {
BackwardFromTo(layers_.size() - 1, end);
}
template <typename Dtype>
void Net<Dtype>::Backward() {
BackwardFromTo(layers_.size() - 1, 0);
if (debug_info_) {
Dtype asum_data = 0, asum_diff = 0, sumsq_data = 0, sumsq_diff = 0;
for (int i = 0; i < learnable_params_.size(); ++i) {
asum_data += learnable_params_[i]->asum_data();
asum_diff += learnable_params_[i]->asum_diff();
sumsq_data += learnable_params_[i]->sumsq_data();
sumsq_diff += learnable_params_[i]->sumsq_diff();
}
const Dtype l2norm_data = std::sqrt(sumsq_data);
const Dtype l2norm_diff = std::sqrt(sumsq_diff);
LOG(ERROR) << " [Backward] All net params (data, diff): "
<< "L1 norm = (" << asum_data << ", " << asum_diff << "); "
<< "L2 norm = (" << l2norm_data << ", " << l2norm_diff << ")";
}
}
template <typename Dtype>
void Net<Dtype>::Reshape() {
for (int i = 0; i < layers_.size(); ++i) {
layers_[i]->Reshape(bottom_vecs_[i], top_vecs_[i]);
}
}
/*这个函数就是从proto参数中copy权重和偏置参数到本net中,用于利用训练好的参数来初始化网络*/
template <typename Dtype>
void Net<Dtype>::CopyTrainedLayersFrom(const NetParameter& param) {
int num_source_layers = param.layer_size();
for (int i = 0; i < num_source_layers; ++i) {
const LayerParameter& source_layer = param.layer(i);
const string& source_layer_name = source_layer.name();
int target_layer_id = 0;
while (target_layer_id != layer_names_.size() &&
layer_names_[target_layer_id] != source_layer_name) {
++target_layer_id;
}
if (target_layer_id == layer_names_.size()) {
LOG(INFO) << "Ignoring source layer " << source_layer_name;
continue;
}
DLOG(INFO) << "Copying source layer " << source_layer_name;
vector<shared_ptr<Blob<Dtype> > >& target_blobs =
layers_[target_layer_id]->blobs();
CHECK_EQ(target_blobs.size(), source_layer.blobs_size())
<< "Incompatible number of blobs for layer " << source_layer_name;
for (int j = 0; j < target_blobs.size(); ++j) {
if (!target_blobs[j]->ShapeEquals(source_layer.blobs(j))) {
Blob<Dtype> source_blob;
const bool kReshape = true;
source_blob.FromProto(source_layer.blobs(j), kReshape);
LOG(FATAL) << "Cannot copy param " << j << " weights from layer '"
<< source_layer_name << "'; shape mismatch. Source param shape is "
<< source_blob.shape_string() << "; target param shape is "
<< target_blobs[j]->shape_string() << ". "
<< "To learn this layer's parameters from scratch rather than "
<< "copying from a saved net, rename the layer.";
}
const bool kReshape = false;
target_blobs[j]->FromProto(source_layer.blobs(j), kReshape);
}
}
}
template <typename Dtype>
void Net<Dtype>::CopyTrainedLayersFrom(const string& trained_filename) {
if (H5Fis_hdf5(trained_filename.c_str())) {
CopyTrainedLayersFromHDF5(trained_filename);
} else {
CopyTrainedLayersFromBinaryProto(trained_filename);
}
}
template <typename Dtype>
void Net<Dtype>::CopyTrainedLayersFromBinaryProto(
const string& trained_filename) {
NetParameter param;
ReadNetParamsFromBinaryFileOrDie(trained_filename, ¶m);
CopyTrainedLayersFrom(param);
}
template <typename Dtype>
void Net<Dtype>::CopyTrainedLayersFromHDF5(const string& trained_filename) {
#ifdef USE_HDF5
hid_t file_hid = H5Fopen(trained_filename.c_str(), H5F_ACC_RDONLY,
H5P_DEFAULT);
CHECK_GE(file_hid, 0) << "Couldn't open " << trained_filename;
hid_t data_hid = H5Gopen2(file_hid, "data", H5P_DEFAULT);
CHECK_GE(data_hid, 0) << "Error reading weights from " << trained_filename;
int num_layers = hdf5_get_num_links(data_hid);
for (int i = 0; i < num_layers; ++i) {
string source_layer_name = hdf5_get_name_by_idx(data_hid, i);
if (!layer_names_index_.count(source_layer_name)) {
LOG(INFO) << "Ignoring source layer " << source_layer_name;
continue;
}
int target_layer_id = layer_names_index_[source_layer_name];
DLOG(INFO) << "Copying source layer " << source_layer_name;
vector<shared_ptr<Blob<Dtype> > >& target_blobs =
layers_[target_layer_id]->blobs();
hid_t layer_hid = H5Gopen2(data_hid, source_layer_name.c_str(),
H5P_DEFAULT);
CHECK_GE(layer_hid, 0)
<< "Error reading weights from " << trained_filename;
// Check that source layer doesn't have more params than target layer
int num_source_params = hdf5_get_num_links(layer_hid);
CHECK_LE(num_source_params, target_blobs.size())
<< "Incompatible number of blobs for layer " << source_layer_name;
for (int j = 0; j < target_blobs.size(); ++j) {
ostringstream oss;
oss << j;
string dataset_name = oss.str();
int target_net_param_id = param_id_vecs_[target_layer_id][j];
if (!H5Lexists(layer_hid, dataset_name.c_str(), H5P_DEFAULT)) {
// Target param doesn't exist in source weights...
if (param_owners_[target_net_param_id] != -1) {
// ...but it's weight-shared in target, so that's fine.
continue;
} else {
LOG(FATAL) << "Incompatible number of blobs for layer "
<< source_layer_name;
}
}
hdf5_load_nd_dataset(layer_hid, dataset_name.c_str(), 0, kMaxBlobAxes,
target_blobs[j].get());
}
H5Gclose(layer_hid);
}
H5Gclose(data_hid);
H5Fclose(file_hid);
#else
LOG(FATAL) << "CopyTrainedLayersFromHDF5 requires hdf5;"
<< " compile with USE_HDF5.";
#endif // USE_HDF5
}
/*将本net训练好的权重和偏置参数存储到proto structure中,就可以利用proto将这些参数存储到文件中*/
template <typename Dtype>
void Net<Dtype>::ToProto(NetParameter* param, bool write_diff) const {
param->Clear();
param->set_name(name_);
// Add bottom and top
DLOG(INFO) << "Serializing " << layers_.size() << " layers";
for (int i = 0; i < layers_.size(); ++i) {
LayerParameter* layer_param = param->add_layer();
layers_[i]->ToProto(layer_param, write_diff);
}
}
template <typename Dtype>
void Net<Dtype>::ToHDF5(const string& filename, bool write_diff) const {
// This code is taken from https://github.com/sh1r0/caffe-android-lib
#ifdef USE_HDF5
hid_t file_hid = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT,
H5P_DEFAULT);
CHECK_GE(file_hid, 0)
<< "Couldn't open " << filename << " to save weights.";
hid_t data_hid = H5Gcreate2(file_hid, "data", H5P_DEFAULT, H5P_DEFAULT,
H5P_DEFAULT);
CHECK_GE(data_hid, 0) << "Error saving weights to " << filename << ".";
hid_t diff_hid = -1;
if (write_diff) {
diff_hid = H5Gcreate2(file_hid, "diff", H5P_DEFAULT, H5P_DEFAULT,
H5P_DEFAULT);
CHECK_GE(diff_hid, 0) << "Error saving weights to " << filename << ".";
}
for (int layer_id = 0; layer_id < layers_.size(); ++layer_id) {
const LayerParameter& layer_param = layers_[layer_id]->layer_param();
string layer_name = layer_param.name();
hid_t layer_data_hid = H5Gcreate2(data_hid, layer_name.c_str(),
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK_GE(layer_data_hid, 0)
<< "Error saving weights to " << filename << ".";
hid_t layer_diff_hid = -1;
if (write_diff) {
layer_diff_hid = H5Gcreate2(diff_hid, layer_name.c_str(),
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK_GE(layer_diff_hid, 0)
<< "Error saving weights to " << filename << ".";
}
int num_params = layers_[layer_id]->blobs().size();
for (int param_id = 0; param_id < num_params; ++param_id) {
ostringstream dataset_name;
dataset_name << param_id;
const int net_param_id = param_id_vecs_[layer_id][param_id];
if (param_owners_[net_param_id] == -1) {
// Only save params that own themselves
hdf5_save_nd_dataset<Dtype>(layer_data_hid, dataset_name.str(),
*params_[net_param_id]);
}
if (write_diff) {
// Write diffs regardless of weight-sharing
hdf5_save_nd_dataset<Dtype>(layer_diff_hid, dataset_name.str(),
*params_[net_param_id], true);
}
}
H5Gclose(layer_data_hid);
if (write_diff) {
H5Gclose(layer_diff_hid);
}
}
H5Gclose(data_hid);
if (write_diff) {
H5Gclose(diff_hid);
}
H5Fclose(file_hid);
// This code is taken from https://github.com/sh1r0/caffe-android-lib
#else
LOG(FATAL) << "ToHDF5 requires hdf5; compile with USE_HDF5.";
#endif // USE_HDF5
}
/*对那些可学习的blob执行update操作,其实就是data = data - diff*/
template <typename Dtype>
void Net<Dtype>::Update() {
for (int i = 0; i < learnable_params_.size(); ++i) {
learnable_params_[i]->Update();
}
}
template <typename Dtype>
void Net<Dtype>::ClearParamDiffs() {
for (int i = 0; i < learnable_params_.size(); ++i) {
Blob<Dtype>* blob = learnable_params_[i];
switch (Caffe::mode()) {
case Caffe::CPU:
caffe_set(blob->count(), static_cast<Dtype>(0),
blob->mutable_cpu_diff());
break;
case Caffe::GPU:
#ifndef CPU_ONLY
caffe_gpu_set(blob->count(), static_cast<Dtype>(0),
blob->mutable_gpu_diff());
#else
NO_GPU;
#endif
break;
}
}
}
template <typename Dtype>
void Net<Dtype>::ShareWeights() {
for (int i = 0; i < params_.size(); ++i) {
if (param_owners_[i] < 0) { continue; }
params_[i]->ShareData(*params_[param_owners_[i]]);
params_[i]->ShareDiff(*params_[param_owners_[i]]);
}
}
template <typename Dtype>
bool Net<Dtype>::has_blob(const string& blob_name) const {
return blob_names_index_.find(blob_name) != blob_names_index_.end();
}
template <typename Dtype>
const shared_ptr<Blob<Dtype> > Net<Dtype>::blob_by_name(
const string& blob_name) const {
shared_ptr<Blob<Dtype> > blob_ptr;
if (has_blob(blob_name)) {
blob_ptr = blobs_[blob_names_index_.find(blob_name)->second];
} else {
blob_ptr.reset((Blob<Dtype>*)(NULL));
LOG(WARNING) << "Unknown blob name " << blob_name;
}
return blob_ptr;
}
template <typename Dtype>
bool Net<Dtype>::has_layer(const string& layer_name) const {
return layer_names_index_.find(layer_name) != layer_names_index_.end();
}
template <typename Dtype>
const shared_ptr<Layer<Dtype> > Net<Dtype>::layer_by_name(
const string& layer_name) const {
shared_ptr<Layer<Dtype> > layer_ptr;
if (has_layer(layer_name)) {
layer_ptr = layers_[layer_names_index_.find(layer_name)->second];
} else {
layer_ptr.reset((Layer<Dtype>*)(NULL));
LOG(WARNING) << "Unknown layer name " << layer_name;
}
return layer_ptr;
}
INSTANTIATE_CLASS(Net);
} // namespace caffe