윈도우 caffe 비주얼스튜디오로 새로운 레이어 추가 정의하기
caffe에서 기본 제공하는 레이어 외에 필요한 레이어를 직접 생성해서 사용할 일이 생겼습니다.
정보공유 및 개인 보관용으로 포스팅을 올립니다.
윈도용 caffe을 컴파일한 후 build 폴더의 솔루션을 비주얼 스튜디오로 열어보면 아래와 같이 45개의 프로젝트가 보입니다.
tools/caffe.bin, caffe, caffeproto의 세 가지 프로젝트를 눈여겨 봅시다.
caffe 프로젝트에선 caffe.lib을 컴파일하고 caffeproto 프로젝트에서는 caffeproto.lib를 컴파일하며, tools/caffe.bin 프로젝트에서는 caffe.exe를 컴파일 합니다.
caffe.bin의 프로젝트 속성을 보면 caffe와 caffeproto를 참조하는 것을 볼 수 있습니다. caffe.bin을 빌드하면 두 프로젝트도 함께 컴파일 합니다.
새로운 레이어를 추가하기 위해 caffe 프로젝트를 변경해줍니다.
1. 소스코드 작성
ln loss layer라는 새로운 레이어를 추가하기 위해 ln_loss_layer.hpp, ln_loss_layer.cpp, ln_loss_layer.cu 세 파일을 생성합니다.
ln_loss_layer.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #ifndef CAFFE_LNLOSS_LAYER_HPP_ #define CAFFE_LNLOSS_LAYER_HPP_ #include <vector> #include "caffe/blob.hpp" #include "caffe/common.hpp" #include "caffe/layer.hpp" #include "caffe/layers/loss_layer.hpp" #include "caffe/proto/caffe.pb.h" namespace caffe { template <typename Dtype> class LnLossLayer : public LossLayer<Dtype> { public: explicit LnLossLayer(const LayerParameter& param) : LossLayer<Dtype>(param), diff_() {} 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 "LnLoss"; } protected: void ln_loss_cpu(const int n, const Dtype* in, Dtype* out); //void ln_loss_gpu(const int n, const Dtype* in, Dtype* out); 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); Blob<Dtype> diff_; Blob<Dtype> lns_; Dtype weight_; }; } // namespace caffe #endif // CAFFE_LNLOSS_LAYER_HPP_ | cs |
ln_loss_layer.cpp
cpp파일에는 기본 설정과 cpu버전의 forward, backward를 정의해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | #include "caffe/layers/ln_loss_layer.hpp" namespace caffe { template <typename Dtype> void LnLossLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const LnLossParameter& ln_loss_param = this->layer_param_.ln_loss_param(); weight_ = ln_loss_param.weight(); } template <typename Dtype> void LnLossLayer<Dtype>::Reshape( const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { LossLayer<Dtype>::Reshape(bottom, top); CHECK_EQ(bottom[0]->count(1), bottom[1]->count(1)) << "Inputs must have the same dimension."; diff_.ReshapeLike(*bottom[0]); lns_.ReshapeLike(*bottom[0]); // shape lns_ } template <typename Dtype> void LnLossLayer<Dtype>::ln_loss_cpu(const int n, const Dtype* in, Dtype* out){ // ln loss // f(x) = w * ln(|x|+1) for (int i = 1; i < n; ++i){ out[i] = weight_*log(abs(in[i]) + 1); } } template <typename Dtype> void LnLossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { // forward int count = bottom[0]->count(); caffe_sub( // difference count, bottom[0]->cpu_data(), bottom[1]->cpu_data(), diff_.mutable_cpu_data()); ln_loss_cpu(count, diff_.cpu_data(), lns_.mutable_cpu_data()); // ln loss Dtype loss = caffe_cpu_asum(count, lns_.cpu_data()); top[0]->mutable_cpu_data()[0] = loss / bottom[0]->num(); } template <typename Dtype> void LnLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { // backward for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { const Dtype sign = (i == 0) ? 1 : -1; const Dtype alpha = sign / top[0]->cpu_diff()[0] / bottom[i]->num(); // gradient: 1/x caffe_cpu_axpby( bottom[i]->count(), // count alpha, // alpha diff_.cpu_data(), // a Dtype(0), // beta bottom[i]->mutable_cpu_diff()); // b } } } #ifdef CPU_ONLY STUB_GPU(EuclideanLossLayer); #endif INSTANTIATE_CLASS(LnLossLayer); REGISTER_LAYER_CLASS(LnLoss); } // namespace caffe | cs |
ln_loss_layer.cu
cu 에는 cuda 코드를 작성합니다. gpu 버전의 forward, backward를 정의해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <vector> #include "caffe/layers/ln_loss_layer.hpp" #include "caffe/util/math_functions.hpp" namespace caffe { template <typename Dtype> __global__ void ln_loss_gpu_global(const int n, const Dtype* in, Dtype* out, Dtype weight){ // f(x) = w*ln(|x|+1) CUDA_KERNEL_LOOP(index, n){ out[index] = weight*log(abs(in[index]) + 1); } } template <typename Dtype> void LnLossLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { int count = bottom[0]->count(); caffe_gpu_sub( count, bottom[0]->gpu_data(), bottom[1]->gpu_data(), diff_.mutable_gpu_data()); ln_loss_gpu_global<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(count, diff_.gpu_data(), lns_.mutable_gpu_data(), weight_); CUDA_POST_KERNEL_CHECK; Dtype loss; caffe_gpu_asum(count, lns_.gpu_data(), &loss); top[0]->mutable_cpu_data()[0] = loss / bottom[0]->num(); } template <typename Dtype> void LnLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { for (int i = 0; i < 2; ++i) { if (propagate_down[i]) { const Dtype sign = (i == 0) ? 1 : -1; const Dtype alpha = sign / top[0]->cpu_diff()[0] / bottom[i]->num(); // 1/x caffe_gpu_axpby( bottom[i]->count(), // count alpha, // alpha diff_.gpu_data(), // a Dtype(0), // beta bottom[i]->mutable_gpu_diff()); // b } } } INSTANTIATE_LAYER_GPU_FUNCS(LnLossLayer); } // namespace caffe | cs |
2. Proto 파일 수정
새로 정의하는 loss layer에서 파라메터를 사용하기 위하여 caffe/Source/Proto/caffe.proto 파일을 수정합니다.
...
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;
optional LnLossParameter ln_loss_param = 147;
위와 같이 이미 정의되어있는 마지막 번호의 다음 번호를 파라메터에 부여하여 추가합니다. 147번의 LnLossParameter를 추가했습니다.
또, 아래와 같이 caffe.proto의 마지막에 파라메터 정보를 추가시켜줍니다.
message LnLossParameter {
// log shape loss
// parameter is weight (curvature of loss function)
optional float weight = 1 [default = 1.0];
}
이제 새로 작성한 cu 파일의 컴파일 정보를 수정합니다. Custom Build Tool로 설정해줘야 컴파일이 이루어 집니다.
다른 레이어를 참고하여 세부정보도 넣어줍니다.
F:\caffe2\caffe\build\src\caffe\CMakeFiles\generate.stamp.depend 파일을 열어서 obj 정보를 추가시켜줍니다.
F:/caffe2/caffe/build/src/caffe/CMakeFiles/cuda_compile.dir/layers/cuda_compile_generated_ln_loss_layer.cu.obj.depend
C:/ProgramData/Anaconda2/Library/share/cmake-3.6/Modules/FindCUDA/run_nvcc.cmake
F:/caffe2/caffe/build/src/caffe/CMakeFiles/cuda_compile.dir/layers/cuda_compile_generated_ln_loss_layer.cu.obj.cmake.pre-gen
위의 항목을 적절한 위치에 넣었습니다.
마지막으로
caffe\build\src\caffe\CMakeFiles\cuda_compile.dir\layers\ 폴더에 생성되는 파일 중 새로 추가한 레이어에서 빠진 항목이 있으면 다른 레이어를 참고해서 만들어줍니다.
위와 같이 모든 세팅을 끝낸 후에 tools/caffe.bin을 컴파일하면 레이어가 추가된 caffe.exe가 빌드됩니다.
-------------------
오래전에 썼던 글인데 이제야 공개합니다. caffe는 워낙 오래된 플랫폼이라 이제는 다른 플랫폼을 사용하는 것을 추천합니다.