• 基于Caffe的DeepID2实现(下)


    小喵的唠叨话:这次的博客,真心累伤了小喵的心。但考虑到知识需要巩固和分享,小喵决定这次把剩下的内容都写完。

    小喵的博客:http://www.miaoerduo.com

    博客原文: http://www.miaoerduo.com/deep-learning/基于caffe的deepid2实现(下).html ‎

    四、数据的重整,简单的划分

    前面的Data层用于生成成对的输入数据,Normalization层,用于将feature归一化,那么之后是不是就可以使用ContrastiveLoss层进行训练了呢?

    且慢,还差一步。

    ContrastiveLoss层要求有3个bottom:feature1、feature2以及表示对位的feature是否为同一个identity的label。

    我们现在得到的feature却是所有的都在一起,data层直接得到的label也和这里要求的label不同。因此务必要对数据进行一次重整。

    一个简单的规则就是按照奇偶,将feature划分成两部分。这样得到的两部分正好就是相同位置为一对。对于label的重整,也可以用类似的方法。小喵这里只对feature进行重整,而label的处理则是通过改ContrastiveLoss层来实现。

    feature的重整本质上就是一个切片的操作,这里命名为id2_slice_layer,实现方法就是按照奇偶把bottom的数据复制到top。后馈的时候,也就是将两部分的feature的diff都直接复制到对应位置的bottom_diff中,具体实现如下:

     1 // created by miao
     2 #ifndef CAFFE_ID2_SLICE_LAYER_HPP_
     3 #define CAFFE_ID2_SLICE_LAYER_HPP_
     4 
     5 #include <vector>
     6 
     7 #include "caffe/blob.hpp"
     8 #include "caffe/layer.hpp"
     9 #include "caffe/proto/caffe.pb.h"
    10 
    11 namespace caffe {
    12 
    13 /**
    14  * @brief Takes a Blob and slices it along either the num or channel dimension,
    15  *        outputting multiple sliced Blob results.
    16  *
    17  * TODO(dox): thorough documentation for Forward, Backward, and proto params.
    18  */
    19 template <typename Dtype>
    20 class Id2SliceLayer : public Layer<Dtype> {
    21  public:
    22   explicit Id2SliceLayer(const LayerParameter& param)
    23       : Layer<Dtype>(param) {}
    24   virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
    25       const vector<Blob<Dtype>*>& top);
    26   virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
    27       const vector<Blob<Dtype>*>& top);
    28 
    29   virtual inline const char* type() const { return "Id2Slice"; }
    30   virtual inline int ExactNumBottomBlobs() const { return 1; }
    31   virtual inline int MinTopBlobs() const { return 1; }
    32 
    33  protected:
    34   virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    35       const vector<Blob<Dtype>*>& top);
    36   virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
    37       const vector<Blob<Dtype>*>& top);
    38   virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
    39       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    40   virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
    41       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    42 };
    43 
    44 }  // namespace caffe
    45 
    46 #endif  // CAFFE_ID2_SLICE_LAYER_HPP_

    头文件,巨简单。。。

    Cpp的代码,也非常简单,要注意id2_slice层的top有两个,每个的形状都是bottom的一半。

     1 // created by miao
     2 #include <algorithm>
     3 #include <vector>
     4 
     5 #include "caffe/layers/id2_slice_layer.hpp"
     6 #include "caffe/util/math_functions.hpp"
     7 
     8 namespace caffe {
     9 
    10 template <typename Dtype>
    11 void Id2SliceLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
    12       const vector<Blob<Dtype>*>& top) {
    13 }
    14 
    15 template <typename Dtype>
    16 void Id2SliceLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
    17       const vector<Blob<Dtype>*>& top) {
    18     vector<int> top_shape = bottom[0]->shape();
    19     top_shape[0] /= 2;
    20     top[0]->Reshape(top_shape);
    21     top[1]->Reshape(top_shape);
    22 }
    23 
    24 template <typename Dtype>
    25 void Id2SliceLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    26       const vector<Blob<Dtype>*>& top) {
    27     const int feature_size = bottom[0]->count(1);
    28     for (int n = 0; n < bottom[0]->num(); ++ n) {
    29         caffe_copy(
    30                 feature_size, 
    31                 bottom[0]->cpu_data() + n * feature_size, 
    32                 top[n & 1]->mutable_cpu_data() + (n / 2) * feature_size
    33                 );
    34     }
    35 }
    36 
    37 template <typename Dtype>
    38 void Id2SliceLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    39       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
    40     const int feature_size = bottom[0]->count(1);
    41     for (int n = 0; n < bottom[0]->num(); ++ n) {
    42         caffe_copy(
    43                 feature_size,
    44                 top[n & 1]->cpu_diff() + (n / 2) * feature_size,
    45                 bottom[0]->mutable_cpu_diff() + n * feature_size
    46                 );
    47     }
    48 }
    49 
    50 #ifdef CPU_ONLY
    51 STUB_GPU(Id2SliceLayer);
    52 #endif
    53 
    54 INSTANTIATE_CLASS(Id2SliceLayer);
    55 REGISTER_LAYER_CLASS(Id2Slice);
    56 
    57 }  // namespace caffe

    GPU上的实现,为了简单起见,也是直接调用了CPU的前馈函数。

     1 // created by miao
     2 #include <vector>
     3 
     4 #include "caffe/layers/id2_slice_layer.hpp"
     5 #include "caffe/util/math_functions.hpp"
     6 
     7 namespace caffe {
     8 template <typename Dtype>
     9 void Id2SliceLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
    10       const vector<Blob<Dtype>*>& top) {
    11     this->Forward_cpu(bottom, top);
    12 }
    13 
    14 template <typename Dtype>
    15 void Id2SliceLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
    16       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
    17     this->Backward_cpu(top, propagate_down, bottom);
    18 }
    19 
    20 INSTANTIATE_LAYER_GPU_FUNCS(Id2SliceLayer);
    21 
    22 }  // namespace caffe

    这样就完成了feature的重整。由于没有用到新的参数,因此也不需要修改caffe.proto。

    亲可以仿照这个方法对label来做类似的操作。鉴于小喵比较懒。。。这里就只是简单的改ContrastiveLoss层的代码了。

    第一步,在ContrastiveLossLayer中新增一个用于记录feature pair是否是同一个identity的成员变量,取代原本的第三个bottom的功能。这样只需要在前馈的时候提前算好,就可以代替之前的第三个bottom来使用,而不需要再修改别的地方的代码。

    为了大家使用的方便,小喵直接把修改之后的头文件粘贴出来(删掉注释)。新增的行,用“added by miao”这个注释标注出来。头文件只加了一行。

     1 #ifndef CAFFE_CONTRASTIVE_LOSS_LAYER_HPP_
     2 #define CAFFE_CONTRASTIVE_LOSS_LAYER_HPP_
     3 
     4 #include <vector>
     5 
     6 #include "caffe/blob.hpp"
     7 #include "caffe/layer.hpp"
     8 #include "caffe/proto/caffe.pb.h"
     9 
    10 #include "caffe/layers/loss_layer.hpp"
    11 
    12 namespace caffe {
    13 template <typename Dtype>
    14 class ContrastiveLossLayer : public LossLayer<Dtype> {
    15  public:
    16   explicit ContrastiveLossLayer(const LayerParameter& param)
    17       : LossLayer<Dtype>(param), diff_() {}
    18   virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
    19       const vector<Blob<Dtype>*>& top);
    20 
    21   virtual inline int ExactNumBottomBlobs() const { return 3; }
    22   virtual inline const char* type() const { return "ContrastiveLoss"; }
    23   virtual inline bool AllowForceBackward(const int bottom_index) const {
    24     return bottom_index != 2;
    25   }
    26  protected:
    27   /// @copydoc ContrastiveLossLayer
    28   virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    29       const vector<Blob<Dtype>*>& top);
    30   virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
    31       const vector<Blob<Dtype>*>& top);
    32   virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
    33       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    34   virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
    35       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    36 
    37   Blob<Dtype> diff_;  // cached for backward pass
    38   Blob<Dtype> dist_sq_;  // cached for backward pass
    39   Blob<Dtype> diff_sq_;  // tmp storage for gpu forward pass
    40   Blob<Dtype> summer_vec_;  // tmp storage for gpu forward pass
    41   Blob<Dtype> is_same_; // added by miao
    42 };
    43 }  // namespace caffe
    44 
    45 #endif  // CAFFE_CONTRASTIVE_LOSS_LAYER_HPP_

    源文件的修改也十分简单,这里只贴出来Cuda的部分。源文件,修改了与原来的bottom3相关的地方。

      1 #include <algorithm>
      2 #include <vector>
      3 #include <iostream>
      4 #include "caffe/layers/contrastive_loss_layer.hpp"
      5 #include "caffe/util/math_functions.hpp"
      6 
      7 namespace caffe {
      8 
      9 template <typename Dtype>
     10 void ContrastiveLossLayer<Dtype>::Forward_gpu(
     11     const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
     12   const int count = bottom[0]->count();
     13   caffe_gpu_sub(
     14       count,
     15       bottom[0]->gpu_data(),  // a
     16       bottom[1]->gpu_data(),  // b
     17       diff_.mutable_gpu_data());  // a_i-b_i
     18   caffe_gpu_powx(
     19       count,
     20       diff_.mutable_gpu_data(),  // a_i-b_i
     21       Dtype(2),
     22       diff_sq_.mutable_gpu_data());  // (a_i-b_i)^2
     23   caffe_gpu_gemv(
     24       CblasNoTrans,
     25       bottom[0]->num(),
     26       bottom[0]->channels(),
     27       Dtype(1.0),
     28       diff_sq_.gpu_data(),  // (a_i-b_i)^2
     29       summer_vec_.gpu_data(),
     30       Dtype(0.0),
     31       dist_sq_.mutable_gpu_data());  // \Sum (a_i-b_i)^2
     32   Dtype margin = this->layer_param_.contrastive_loss_param().margin();
     33   bool legacy_version =
     34       this->layer_param_.contrastive_loss_param().legacy_version();
     35   Dtype loss(0.0);
     36   for (int i = 0; i < bottom[0]->num(); ++i) {
     37     // added by miao
     38     is_same_.mutable_cpu_data()[i] = (bottom[2]->cpu_data()[2 * i] == bottom[2]->cpu_data()[2 * i + 1])? 1:0;
     39     if (is_same_.cpu_data()[i] == 1) {  // similar pairs
     40       loss += dist_sq_.cpu_data()[i];
     41     } else {  // dissimilar pairs
     42       if (legacy_version) {
     43         loss += std::max(margin - dist_sq_.cpu_data()[i], Dtype(0.0));
     44       } else {
     45         Dtype dist = std::max(margin - sqrt(dist_sq_.cpu_data()[i]),
     46                               Dtype(0.0));
     47         loss += dist*dist;
     48       }
     49     }
     50   }
     51   loss = loss / static_cast<Dtype>(bottom[0]->num()) / Dtype(2);
     52   top[0]->mutable_cpu_data()[0] = loss;
     53 }
     54 
     55 template <typename Dtype>
     56 __global__ void CLLBackward(const int count, const int channels,
     57     const Dtype margin, const bool legacy_version, const Dtype alpha,
     58     const Dtype* y, const Dtype* diff, const Dtype* dist_sq,
     59     Dtype *bottom_diff) {
     60   CUDA_KERNEL_LOOP(i, count) {
     61     int n = i / channels;  // the num index, to access y and dist_sq
     62     if (static_cast<int>(y[n])) {  // similar pairs
     63       bottom_diff[i] = alpha * diff[i];
     64     } else {  // dissimilar pairs
     65       Dtype mdist(0.0);
     66       Dtype beta(0.0);
     67       if (legacy_version) {
     68         mdist = (margin - dist_sq[n]);
     69         beta = -alpha;
     70       } else {
     71         Dtype dist = sqrt(dist_sq[n]);
     72         mdist = (margin - dist);
     73         beta = -alpha * mdist / (dist + Dtype(1e-4)) * diff[i];
     74       }
     75       if (mdist > 0.0) {
     76         bottom_diff[i] = beta;
     77       } else {
     78         bottom_diff[i] = 0;
     79       }
     80     }
     81   }
     82 }
     83 
     84 template <typename Dtype>
     85 void ContrastiveLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
     86     const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
     87   for (int i = 0; i < 2; ++i) {
     88     if (propagate_down[i]) {
     89       const int count = bottom[0]->count();
     90       const int channels = bottom[0]->channels();
     91       Dtype margin = this->layer_param_.contrastive_loss_param().margin();
     92       const bool legacy_version =
     93           this->layer_param_.contrastive_loss_param().legacy_version();
     94       const Dtype sign = (i == 0) ? 1 : -1;
     95       const Dtype alpha = sign * top[0]->cpu_diff()[0] /
     96           static_cast<Dtype>(bottom[0]->num());
     97       // NOLINT_NEXT_LINE(whitespace/operators)
     98       CLLBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
     99           count, channels, margin, legacy_version, alpha,
    100           is_same_.gpu_data(),  // pair similarity 0 or 1  added by miao
    101           diff_.gpu_data(),  // the cached eltwise difference between a and b
    102           dist_sq_.gpu_data(),  // the cached square distance between a and b
    103           bottom[i]->mutable_gpu_diff());
    104       CUDA_POST_KERNEL_CHECK;
    105     }
    106   }
    107 }
    108 
    109 INSTANTIATE_LAYER_GPU_FUNCS(ContrastiveLossLayer);
    110 
    111 }  // namespace caffe

    需要注意的时候,前馈和后馈都需要做一点代码上的修改,虽说十分的简单,但也要小心。

    至此,基于Caffe的DeepID2的修改全部完成。

    如果您觉得本文对您有帮助,那请小喵喝杯茶吧~~O(∩_∩)O~~

    %e6%89%93%e8%b5%8f

    转载请注明出处~

  • 相关阅读:
    离场飞越转弯全家福
    画一个PBN大角度飞越转弯保护区
    画一个小角度飞越转弯保护区
    Point Estimation
    Random variable
    心流
    Survey sampling
    Distribution
    数学分析总结
    topological space
  • 原文地址:https://www.cnblogs.com/idiotgroup/p/5676527.html
Copyright © 2020-2023  润新知