1. 程式人生 > >[caffe筆記008]:使用matlab除錯caffe中新加的層

[caffe筆記008]:使用matlab除錯caffe中新加的層

寫在前面: caffe的底層是c++實現的,所以當自己新增層時自然可以用除錯c++的方法進行除錯。一方面除錯c++需要偵錯程式,另一方面c++除錯的時候觀測矩陣或者向量的資料不是很方便。由於caffe有比較好的matlab介面,採用matlab進行輔助除錯也就成為了一個很好的選擇。

1. 利用MATLAB進行除錯流程

我們這邊需要除錯自己實現的SoftProposal層。編譯通過而且編譯了matlab的介面。

Step1:構建單層測試網路

第一步便是編寫一個測試網路’test.prototxt’,具體如下:

name: "TEST"
input: "data"
input_dim
: 1 input_dim: 512 input_dim: 40 input_dim: 30 force_backward: true layer { name: "testlayer" type: "SoftProposal" bottom: "data" top: "testlayer" }

Step2:觀測單層測試輸出

接下來,開啟Matlab,

編寫.m檔案如下:

addpath $CAFFE_ROOT/matlab
model = 'test.prototxt';
caffe.set_mode_cpu();
% 測試gpu程式碼時請用GPU模式
% caffe.set_mode_gpu();
% caffe.set_device(gpu_id); net = caffe.Net(model, 'test'); % 生成data同緯度的資料,並填入'data'層的blobs net.blobs('data').set_data(randn(net.blobs('data').shape)); % 前向過程 net.forward(); % 檢查生成的"res"是否是期望的結果 res = net.blobs('testlayer').get_data(); % 後向過程 % diff為自己設定的梯度值,保證維度一致 net.blobs('testlayer').set_diff(diff); net.
backward(); % 檢查生成的"data_diff"是否是期望的結果 data_diff = net.blobs('data').get_diff();

在matlab中可以分別觀測層輸出和層反向傳播的值,以判斷是否實現正確。

2. 一些除錯技巧

2.1. 利用LOG(INFO)觀測單個數值

在caffe的底層c++程式碼中可以用LOG(INFO)在linux的shell視窗中輸出一些簡單的變數。

觀測新新增的層的一些常量引數是否配置成功

例如我們新建的SoftProposal層,該層有引數maxiteration,如果設定了這個引數就採用設定值,否則採用預設值20。

  if(soft_proposal_param.has_maxiteration()) 
    maxIteration_ = soft_proposal_param.maxiteration();
  else
    maxIteration_ = 20;
  LOG(INFO) << "maxIteration: " << maxIteration_;
觀測一些中間結果

例如迭代優化時觀測每次迭代終止條件的判定值。

 for (j = 0; j < maxIteration_; j++) {
     // proposalBuffer_data = transferMatrix_data * proposal_data - proposalBuffer_data;
     caffe_cpu_gemv<Dtype>(CblasNoTrans, N_, N_, 1.,
     transferMatrix_data, proposal_data, -1., proposalBuffer_data);

    float normDiff = sqrt(proposalBuffer_.sumsq_data());

    LOG(INFO) << "normDiff(" << j << "): " << normDiff;

    if (normDiff < tolerance_) break;
    // proposalBuffer_data = proposal_data + proposalBuffer_data;
     UpdateProposalKernel(N_, proposal_data, proposalBuffer_data, proposalBuffer_data, -1.0f);  
end

2.2. 利用Blob的存取觀測矩陣或者向量的值

儲存Blob到檔案(底層c++程式碼中)。

儲存Blob到檔案可以採用如下程式碼儲存到指定檔案中。

#include "caffe/util/io.hpp" 

... ...

BlobProto sp;
proposal_.ToProto(&sp, false);// false只儲存blob.data,ture則同時儲存blob.data和blob.diff
WriteProtoToBinaryFile(sp, "proposal.blob"); //儲存到proposal.blob檔案中
從檔案中讀取Blob資料(matlab中)

matlab中讀取blob檔案可以通過read_mean函式來實現。

proposal = caffe.io.read_mean('proposal.blob'); //從proposal.blob檔案中讀入資料到proposal

讀入之後就可以在matlab中方便的查看了。

2.3. 除錯一些子函式

子函式的除錯,其實是利用了第一部分除錯層的方式,只不過把和需要觀測的子函式的輸出值放入到當前層的topblob中。

例如有子函式InitDistanceMetricKernel,作用是初始化Blob<Dtype> distanceMetric_這個成員變數。

// for page rank
template <typename Dtype>  
void SoftProposalLayer<Dtype>::InitDistanceMetricKernel(){
  Dtype* distanceMetric_data = distanceMetric_.mutable_cpu_data();  

  long nthreads = N_ * N_;
  for(long n=0;n<nthreads;n++)
  {
    const long q = n % width_; 
    const long p = (n / width_) % height_;
    const long j = (n / N_) % width_;
    const long i = n / N_ / width_;
    const long u = i * width_ + j;
    const long v = p * width_ + q;

    if (u >= v) {
        *(distanceMetric_data + n) = expf(((i - p) * (i - p) + (j - q) * (j - q)) / (-2 * factor_ * factor_));
        *(distanceMetric_data + v*N_ + u) = *(distanceMetric_data + n);
    }
  }   
}

我們需要觀測初始化是否成功,那麼在Forward_cpu

template <typename Dtype>   
void SoftProposalLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  Dtype* top_data = top[0]->mutable_cpu_data();

  InitDistanceMetricKernel();  
  const Dtype* distanceMetric_data = distanceMetric_.cpu_data();

  /*
  編寫一節程式,將distanceMetric_data賦值到top_data當中
  */
}

當再次編譯後執行第一部分的測試網路,就可以獲得distanceMetric_的資料。