小喵的嘮叨話:這次的博客,真心累傷了小喵的心。但考慮到知識需要鞏固和分享,小喵決定這次把剩下的內容都寫完。
小喵的博客: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~~
轉載請注明出處~