Lazy Coder

윈도우 caffe 비주얼스튜디오로 새로운 레이어 추가 정의하기

mutefreeze 2019. 7. 23. 14:01

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

}


위처럼 proto 파일을 수정하면 프로젝트를 빌드할 때

caffe\build\include\caffe\proto 경로의 caffe.pb.h, caffe.pb.cc, caffe_pb2.py 파일을 자동으로 수정하여 파라메터를 사용할 수 있게 됩니다

3. 컴파일

이제 새로 작성한 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는 워낙 오래된 플랫폼이라 이제는 다른 플랫폼을 사용하는 것을 추천합니다.