SimpleHumanPose代码及原理分析(三)-- loss损失函数
在上一篇blog中,已经把SimpleHumanPose的数据前处理部分给搞定了,具体可参见:SimpleHumanPose代码及原理分析(二)-- data与label前处理
简单的进行总结一下:
首先,默认输入的图片大小为256*256的,然后backbone为resnet。图片首先从256的shape一直进行下卷积到16,然后从16通过上卷积(这个上卷积才是SimpleHumanPose的核心)到64。就比如说,输入的shape为(16,3,256,256)(16表示的是batch_size,3表示的是图片的维度,256表示的是resize之后图片的长宽大小,这里由于是用caffe框架的,所以形式为bcwh,而不是通常tf中的bwhc形式),通过网络结构(网络结构可参见:Resnet网络实现)最终得到的feature map的shape为(16,points,64,64),这里第二个维度就是你要预测关键点的个数,该数要与生成的heatmap label的channels保持一致,不然后续进行损失函数计算的时候会报错。
代码:
#include <vector>
#include "caffe/layers/keypoint_L2_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template<typename Dtype>
void KeypointL2LossLayer<Dtype>::Reshape(
const vector<Blob<Dtype> *> &bottom, const vector<Blob<Dtype> *> &top) {
LossLayer<Dtype>::Reshape(bottom, top);
//对输入的两个向量:模型生成的heatmap与我们自主制作的label的heatmap 进行维度比较判断
CHECK_EQ(bottom[0]->num(),bottom[1]->num())<<"Inputs must have the same num.";
CHECK_EQ(bottom[0]->channels(),bottom[1]->channels())<<"Inputs must have the same channels.";
CHECK_EQ(bottom[0]->height(),bottom[1]->height())<<"Inputs must have the same height.";
CHECK_EQ(bottom[0]->width(),bottom[1]->width())<<"Inputs must have the same width.";
diff_.ReshapeLike(*bottom[0]);
}
//改写前向处理
template<typename Dtype>
void KeypointL2LossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype> *> &bottom,
const vector<Blob<Dtype> *> &top) {
int count = bottom[0]->count(); //表示第一路输入进来的heatmap的总维度乘积:B * C * H * W
int n = bottom[0]->num(); //表示的是batchsize
int c = bottom[0]->channels(); //表示的是c
int dim = bottom[0]->count(2); //表示的是h
caffe_sub(
count,
bottom[0]->cpu_data(),
bottom[1]->cpu_data(),
diff_.mutable_cpu_data());//两个(b,c,h,w)的向量进行逐位相减,生成的diff_的shape也是(b,c,h,w)
Dtype* diff_data=diff_.mutable_cpu_data();//将相减之后所得到的结果diff存放到cpu中
const Dtype* label_data=bottom[1]->cpu_data(); //将label生成的heatmap存放到cpu中
Dtype loss=0; //定义每个channels总损失变量
for(int j=0;j<c;++j) //遍历每个channels维度
{
Dtype joint_loss=0; //定义单个关键点的loss变量
for(int i=0;i<n;++i) //遍历每个batchsize
{
if(label_data[(i*c+j)*dim] < 0)
{
for(int k=0;k<dim;++k)
{
diff_data[(i*c+j)*dim+k]=0;
}
}
else
{
for(int k=0;k<dim;++k)
{
joint_loss+=diff_data[(i*c+j)*dim+k]*diff_data[(i*c+j)*dim+k]; //均方差计算
}
}
}
loss+=joint_loss/n/dim; //每个channels的平均损失
}
//改写反向传播函数
template<typename Dtype>
void KeypointL2LossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype> *> &top,
const vector<bool> &propagate_down,
const vector<Blob<Dtype> *> &bottom) {
int n = bottom[0]->num();
int c = bottom[0]->channels();
int dim = bottom[0]->count(2);
Dtype* diff_data=diff_.mutable_cpu_data();
for (int id = 0; id < 2; ++id)
{
if (propagate_down[id])
{
const Dtype sign = (id == 0) ? 1 : -1;
const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[id]->count();
Dtype *bottom_diff=bottom[id]->mutable_cpu_data();
for(int j=0;j<c;++j)
{
for(int i=0;i<n;++i)
{
for(int k=0;k<dim;++k)
{
bottom_diff[(i*c+j)*dim+k]=diff_data[(i*c+j)*dim+k]*alpha;
}
}
}
}
}
#ifdef CPU_ONLY
STUB_GPU(KeypointL2LossLayer);
#endif
INSTANTIATE_CLASS(KeypointL2LossLayer);
REGISTER_LAYER_CLASS(KeypointL2Loss);
} // namespace caffe