参考自http://blog.youkuaiyun.com/qq_30040223/article/details/50300779
写在前面: 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_
的数据。