CenterLoss的caffe实现与深入理解

CenterLoss的caffe实现与深入理解

目的:

打算将centerloss与SSD的multibox_loss融合,或者与faster-rcnn的softmax回归融合。
项目代码将会发布到:https://github.com/BOBrown

思路概述:

paper title:A Discriminative Feature Learning Approach for Deep Face Recognition

Centerloss发表于ECCV2016,研究内容是关于如何学习到更具判别力的特征,如类内聚较小的特征。

作者github : https://github.com/ydwen/caffe-face
文章: 如果arXiv下载慢可以去我的网盘下载, https://pan.baidu.com/s/1up_PWpR85HqVe10yhFzHoQ

这里引用文章中的截图来简要介绍文章思路。

这里写图片描述

这里写图片描述

上述图片显示了一个维度是2的特征向量在特征空间上的表现,第一张图是没有加入center loss的原始神经网络训练得到的特征向量的可视化结果。(之所以选用2维的向量是因为可视化方便)

加入center loss之后随着 λ λ 增大,学习到的同一个类别的特征向量在空间距离就越靠近,因此类间聚就会增大。


数学方法与caffe实现:

1.forward

这里写图片描述

上述公式 m m 是指sample个数,例如batch size=128那么m=128,文章中说mini-batch的学习centerloss的方式更容易使centerloss收敛。 cyi c y i 是第i个样本的center。

这里center的含义是:有多少个输出类别就会有多少个center向量,其中每一个center向量与 xi x i 的维度相同。

当然,公式(2)也可以由向量内积的形式进一步写成为公式(3),其中 xij x j i 表示第i个样本的第j个维度的数值, cyij c j y i 表示第i个样本的center是 yi y i 类别的center,这个center的第j个维度:

LC=12i=1mj=1K(xijcyij)23 L C = 1 2 ∑ i = 1 m ∑ j = 1 K ( x j i − c j y i ) 2 ( 3 )

那么实现中也是按照公式(3)来完成forward的计算:

template <typename Dtype>
void CenterLossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  const Dtype* bottom_data = bottom[0]->cpu_data();
  const Dtype* label = bottom[1]->cpu_data();
  const Dtype* center = this->blobs_[0]->cpu_data();
  Dtype* distance_data = distance_.mutable_cpu_data();//distance_这个blob保存x^i_j - c^{y_i}_j

  // the i-th distance_data
  for (int i = 0; i < M_; i++) {
    const int label_value = static_cast<int>(label[i]);
    // D(i,:) = X(i,:) - C(y(i),:)
    caffe_sub(K_, bottom_data + i * K_, center + label_value * K_, distance_data + i * K_);//相减赋值给distance_data 
  }
  Dtype dot = caffe_cpu_dot(M_ * K_, distance_.cpu_data(), distance_.cpu_data());//在M_ * K_维度进行内积,也就是平方和
  Dtype loss = dot / M_ / Dtype(2); 归一化loss
  top[0]->mutable_cpu_data()[0] = loss;
}

2.Backward

Backward负责计算bottom[0]->mutable_cpu_diff()和this->blob[0]_->mutable_cpu_diff(),后面这个也叫weights_diff,是带参网络层所特有的一个blob。
在center_loss_layer.cpp和.cu代码中,this->blobs_[0]就是指代center,因为center是通过样本学习到的参数。在Backward中需要考虑center这个参数的梯度以及对bottom_data的梯度。

(1)求对bottom data的梯度:
这个比较简单,按照公式(3),对 xij x j i 的偏导如下:

Lxij=1mi=1m[xijcyij]4 ∂ L ∂ x j i = 1 m ∑ i = 1 m [ x j i − c j y i ] ( 4 )

代码,需要注意top[0]->cpu_diff()[0]是惩罚项系数:

if (propagate_down[0]) {
    caffe_copy(M_ * K_, distance_.cpu_data(), bottom[0]->mutable_cpu_diff());
    caffe_scal(M_ * K_, top[0]->cpu_diff()[0] / M_, bottom[0]->mutable_cpu_diff());//此处top diff是loss weights,也就是惩罚项
  }

(2) 求对weight diff的梯度:
公式:
这里写图片描述

解释上述公式: cj c j 表示第j个center向量,center向量的个数与类别数相关,有N个类别,就有N个center向量。
在梯度更新时,并不是更新全部的center向量,而是更新这个mini-batch抓取到的类别index对应的center向量,因此存在 δ(yi=j) δ ( y i = j ) 的判断。分母是归一化梯度处理,本来是除以 i=1mδ(yi=j) ∑ i = 1 m δ ( y i = j ) 就可以了。但是为了防止有的 cj c j 向量没有出现在这次的mini-batch中,这样会导致分母为0,因此采用 1+i=1mδ(yi=j) 1 + ∑ i = 1 m δ ( y i = j ) 的归一形式。

代码部分:

caffe_set(N_ * K_, (Dtype)0., variation_sum_.mutable_cpu_data());//variation_sum_是临时变量
    for (int n = 0; n < N_; n++) {//N表示类别数,每一个c_j向量都需要进行梯度求解
      int count = 0;
      for (int m = 0; m < M_; m++) {
        const int label_value = static_cast<int>(label[m]);
        if (label_value == n) {
          count++;
          caffe_sub(K_, variation_sum_data + n * K_, distance_data + m * K_, variation_sum_data + n * K_);
        }
      }
      caffe_axpy(K_, (Dtype)1./(count + (Dtype)1.), variation_sum_data + n * K_, center_diff + n * K_);
    }
  }

Centerloss的应用

centerloss在人脸识别的效果显著,但是我在训练的时候发现,先训练一个不带centerloss的人脸模型,之后基于这个pretrain model加入centerloss进行微调效果更佳好一点。

centerloss对于output number比较高的任务有较高的表现力。例如一般来说人脸识别的分类的output number=10000+,因此提高类内距是有必要的。但是在output number较低的任务中,例如cifar10/cifar100,表现力可能会下降。

参考文献:

A Discriminative Feature Learning Approach for Deep Face Recognition. ECCV2016

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值