本文参考了
在caffe重大更新之后,每一个层都有一个与之对应的cpp和hpp文件。所以每自定义一个新的层就要分别给出对应的cpp和hpp文件,并且放到对应的文件夹里,再进行编译,才能够使用。
本文在原来博主的基础上,介绍centerloss,为自己以后能够自己添加一个层铺垫。
一、首先创建一个HPP头文件(layer.hpp)
不同类型的层所引用的头文件也不相同,hpp文件一般放置在“/include/caffe/layers”目录里。新添加的hpp文件也应该放置在该目录内。
注意:(1)两个注释内的部分需要特别注意。(2)命名的格式应该严格一致,注意大小写。
#ifndef CAFFE_CENTER_LOSS_LAYER_HPP_
#define CAFFE_CENTER_LOSS_LAYER_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/layers/loss_layer.hpp"
namespace caffe{
template <typename Dtype>
class CenterLossLayer : public LossLayer<Dtype>{
public:
explicit CenterLossLayer(const LayerParameter& param)
:LossLayer<Dtype>(param){}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual inline const char* type() const {return "CenterLoss";}
virtual inline int ExactNumBottomBlobs() const {return 2;}
virtual inline int ExactNumTopBlobs() const {return -1;}
protocted:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down const vector<Blob<Dtype>*>& bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down const vector<Blob<Dtype>*>& bottom);
int M_;
int K_;
int N_;
Bolb<Dtype> distance_;
Blob<Dtype> variation_sum_;
};
} //namespace caffe
#endif //CAFFE_CENTER_LOSS_LAYER_HPP_
二、创建对应的cpp文件
cpp文件一般放置在“src/caffe/layers”目录下
#include <vector>
#include "caffe/filler.hpp"
#include "caffe/layers/center_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe{
template <typename Dtype>
void CenterLossLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top){
const int num_output = this->layer_param_.center_loss_param().num_output();
N_ = num_output;
const int axis = bottom[0]->CanonicalAxisIndex(
this->layer_param_.center_lossLparam().axis());
//Dimensions starting from "axis" are "flattened" into a single
//length K_ vector. for example, if bottom[0]'s shape is (N,C,H,W),
//and axis==1, N inner products with dimension CHW are performed.
K_ =bottom[0]->count(axis);
//Check if we need to set up the weights
if (this->blobs_.size()>0){
LOG(INFO)<<"Skipping parameter initialization";
}else{
this->blobs_.resize(1);
//Intialize the weight
vector<int> center_shape(2);
center_shape[0] = N_;
center_shape[1] = K_;
this->blobs_[0].reset(ner Blob<Dtype>(center_shape));
//fill the weights
shared ptr<>filler<Dtype> > center_filler(GetFiller<Dtype>(
this->layer_param_.center_loss_param().center_filler()));
center_filler->Fill(this->blobs_[0].get());
} //parameter initialization
this->param_propagate_down_.resize(this->blobs_.size(),true);
}
}
template <typename Dtype>
void CenterLossLater<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top){
CHECK_EQ(bottom[1]->channels(),1);
CHECK_EQ(bottom[1]->height(),1);
CHECK_EQ(bottom[1]->width(),1);
M_ = bottom[0]->num();
//the shape will be the bottom shape with the flattened axes dropped
//and replaced by a single axis with dimension num_output (N_).
LossLayer<Dtype>::Reshape( bottom,top);
distance_.ReshapeLike(*bottom[0]);
variation_sum_.ReshapeLike(*this->blobs_[0]);
}
template <typename Dtype>
void CenterLossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
const Dtype* label = bottom[1]->cpu_data();
const Dtype* center = this->blobs_[0]->cpu_data();
Dtype* distance_data = distance_.mutable_cpu_data();
// the i-th distance_data
for (int i = 0; i < M_; i++) {
const int label_value = static_cast<int>(label[i]);
// D(i,:) = X(i,:) - C(y(i),:)
caffe_sub(K_, bottom_data + i * K_, center + label_value * K_, distance_data + i * K_);
}
Dtype dot = caffe_cpu_dot(M_ * K_, distance_.cpu_data(), distance_.cpu_data());
Dtype loss = dot / M_ / Dtype(2);
top[0]->mutable_cpu_data()[0] = loss;
}
template <typename Dtype>
void CenterLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom) {
// Gradient with respect to centers
if (this->param_propagate_down_[0]) {
const Dtype* label = bottom[1]->cpu_data();
Dtype* center_diff = this->blobs_[0]->mutable_cpu_diff();
Dtype* variation_sum_data = variation_sum_.mutable_cpu_data();
const Dtype* distance_data = distance_.cpu_data();
// \sum_{y_i==j}
caffe_set(N_ * K_, (Dtype)0., variation_sum_.mutable_cpu_data());
for (int n = 0; n < N_; n++) {
int count = 0;
for (int m = 0; m < M_; m++) {
const int label_value = static_cast<int>(label[m]);
if (label_value == n) {
count++;
caffe_sub(K_, variation_sum_data + n * K_, distance_data + m * K_, variation_sum_data + n * K_);
}
}
caffe_axpy(K_, (Dtype)1./(count + (Dtype)1.), variation_sum_data + n * K_, center_diff + n * K_);
}
}
// Gradient with respect to bottom data
if (propagate_down[0]) {
caffe_copy(M_ * K_, distance_.cpu_data(), bottom[0]->mutable_cpu_diff());
caffe_scal(M_ * K_, top[0]->cpu_diff()[0] / M_, bottom[0]->mutable_cpu_diff());
}
if (propagate_down[1]) {
LOG(FATAL) << this->type()
<< " Layer cannot backpropagate to label inputs.";
}
}
#ifdef CPU_ONLY
STUB_GPU(CenterLossLayer);
#endif
INSTANTIATE_CLASS(CenterLossLayer);
REGISTER_LAYER_CLASS(CenterLoss);
} // namespace caffe
三、修改“src/caffe/proto/caffe.proto”
为新写的层首先增加一个id,注意不要和前面id重复,一般在next available layer-specific 后面的id,增加完之后记得在后面也改一下。位置不重要,只要记得是在LayerParameter里面就行。然后在后面为新定义的层写一个parameter函数。
// NOTE
// Update the next available ID when you add a new LayerParameter field.
//
// LayerParameter next available layer-specific ID: 148 (last added: center_loss_param)
message LayerParameter {
optional string name = 1; // the layer name
optional string type = 2; // the layer type
repeated string bottom = 3; // the name of each bottom blob
repeated string top = 4; // the name of each top blob
// The train / test phase for computation.
optional Phase phase = 10;
// The amount of weight to assign each top blob in the objective.
// Each layer assigns a default value, usually of either 0 or 1,
// to each top blob.
repeated float loss_weight = 5;
// Specifies training parameters (multipliers on global learning constants,
// and the name and other settings used for weight sharing).
repeated ParamSpec param = 6;
// The blobs containing the numeric parameters of the layer.
repeated BlobProto blobs = 7;
// Specifies whether to backpropagate to each bottom. If unspecified,
// Caffe will automatically infer whether each input needs backpropagation
// to compute parameter gradients. If set to true for some inputs,
// backpropagation to those inputs is forced; if set false for some inputs,
// backpropagation to those inputs is skipped.
//
// The size must be either 0 or equal to the number of bottoms.
repeated bool propagate_down = 11;
// Rules controlling whether and when a layer is included in the network,
// based on the current NetState. You may specify a non-zero number of rules
// to include OR exclude, but not both. If no include or exclude rules are
// specified, the layer is always included. If the current NetState meets
// ANY (i.e., one or more) of the specified rules, the layer is
// included/excluded.
repeated NetStateRule include = 8;
repeated NetStateRule exclude = 9;
// Parameters for data pre-processing.
optional TransformationParameter transform_param = 100;
// Parameters shared by loss layers.
optional LossParameter loss_param = 101;
// Layer type-specific parameters.
//
// Note: certain layers may have more than one computational engine
// for their implementation. These layers include an Engine type and
// engine parameter for selecting the implementation.
// The default for the engine is set by the ENGINE switch at compile-time.
optional AccuracyParameter accuracy_param = 102;
optional ArgMaxParameter argmax_param = 103;
optional BatchNormParameter batch_norm_param = 139;
optional BiasParameter bias_param = 141;
optional CenterLossParameter center_loss_param = 147;
optional ConcatParameter concat_param = 104;
optional ContrastiveLossParameter contrastive_loss_param = 105;
optional ConvolutionParameter convolution_param = 106;
optional CropParameter crop_param = 144;
optional DataParameter data_param = 107;
optional DropoutParameter dropout_param = 108;
optional DummyDataParameter dummy_data_param = 109;
optional EltwiseParameter eltwise_param = 110;
optional ELUParameter elu_param = 140;
optional EmbedParameter embed_param = 137;
optional ExpParameter exp_param = 111;
optional FlattenParameter flatten_param = 135;
optional HDF5DataParameter hdf5_data_param = 112;
optional HDF5OutputParameter hdf5_output_param = 113;
optional HingeLossParameter hinge_loss_param = 114;
optional ImageDataParameter image_data_param = 115;
optional InfogainLossParameter infogain_loss_param = 116;
optional InnerProductParameter inner_product_param = 117;
optional InputParameter input_param = 143;
optional LogParameter log_param = 134;
optional LRNParameter lrn_param = 118;
optional MemoryDataParameter memory_data_param = 119;
optional MVNParameter mvn_param = 120;
optional ParameterParameter parameter_param = 145;
optional PoolingParameter pooling_param = 121;
optional PowerParameter power_param = 122;
optional PReLUParameter prelu_param = 131;
optional PythonParameter python_param = 130;
optional RecurrentParameter recurrent_param = 146;
optional ReductionParameter reduction_param = 136;
optional ReLUParameter relu_param = 123;
optional ReshapeParameter reshape_param = 133;
optional ScaleParameter scale_param = 142;
optional SigmoidParameter sigmoid_param = 124;
optional SoftmaxParameter softmax_param = 125;
optional SPPParameter spp_param = 132;
optional SliceParameter slice_param = 126;
optional TanHParameter tanh_param = 127;
optional ThresholdParameter threshold_param = 128;
optional TileParameter tile_param = 138;
optional WindowDataParameter window_data_param = 129;
}
在定义完了id之后,写对应的parameter函数。
message CenterLossParameter {
optional uint32 num_output = 1; // The number of outputs for the layer
optional FillerParameter center_filler = 2; // The filler for the centers
// The first axis to be lumped into a single inner product computation;
// all preceding axes are retained in the output.
// May be negative to index from the end (e.g., -1 for the last axis).
optional int32 axis = 3 [default = 1];
}
上述的1,2,3依次递增。如果还需要定义新的参数,可以接在后面添加。
四、重新编译caffe
centerloss层的参数应该遵循hpp和cpp文件定义的一样。