知智
首发于知智
人脸识别论文再回顾之一:Center Loss

人脸识别论文再回顾之一:Center Loss

写在前面:
对外自称搞人脸识别有一段时间了,但是回头仔细想一想还有很多东西都一知半解。
本系列为重读最近几年人脸识别领域的经典文章,争取把一些似懂非懂的问题给搞清楚。
故文章内容可能不会很全面,主要把握一些技术细节和具体实现。

[ECCV 2016] A Discriminative Feature Learning Approach for Deep Face Recognition

ECCV 2016的这篇文章主要是提出了一个新的Loss:Center Loss,用以辅助Softmax Loss进行人脸的训练,主要目的是利用softmax loss来分开不同类别,利用center loss来压缩同一类别,最终获取discriminative features. 本文主要对center loss的一些细节做分析介绍,若对论文实验感兴趣,请自行看作者原文。

[center loss]

center loss意思即为:为每一个类别提供一个类别中心,最小化min-batch中每个样本与对应类别中心的距离,这样就可以达到缩小类内距离的目的。

C_{yi} 就是每个这个batch中每个样本对应的类别中心,它和特征x的维度一样,对相关变量求导如下:

值得注意的是,在进行类别中心的求导时,只用当前batch中某一类别的图片来获得该类别中心的更新量。即每一个类别中心的变化只用属于这个类别的图片特征来计算。

训练是softmax loss结合center loss进行联合训练的,loss的权重为lambda.


[center 的计算与更新]

论文中说:

In each iteration, the centers are computed by averaging the features of the corresponding classes

但是实际在实现时候却是:

使用"xavier"进行初始化,然后用 \Delta c_{j} 进行更新与计算,而且\Delta c_{j}并不是上面说的那样进行求均值,而是通过求梯度得到的,详见上面的公式,总而言之,center就像一个参数一样,先随机初始化,然后再每个迭代后在当前类别中更新一次。

这点可以看center loss的源码:

layer {
  name: "center_loss"
  type: "CenterLoss"
  bottom: "fc5"
  bottom: "label"
  top: "center_loss"
  param {
    lr_mult: 1
    decay_mult: 2 
  }
  center_loss_param {
    num_output: 10572
    center_filler {
      type: "xavier"
    }
  }
  loss_weight: 0.008
}


if (this->blobs_.size() > 0) {
    LOG(INFO) << "Skipping parameter initialization";
  } else {
    //每个类别都有自己的一个类别中心,每个类别的特征是K_维
    //故center_shape为[N_, K_]
    this->blobs_.resize(1);
    // Intialize the weight
    vector<int> center_shape(2);
    center_shape[0] = N_;
    center_shape[1] = K_;
    this->blobs_[0].reset(new Blob<Dtype>(center_shape));
    // fill the weights
    // 初始化center_shape的值
    shared_ptr<Filler<Dtype> > center_filler(GetFiller<Dtype>(
        this->layer_param_.center_loss_param().center_filler()));
    center_filler->Fill(this->blobs_[0].get());

  }  // parameter initialization

[center 的“梯度”具体是怎么得到的]

对一个mini-batch进行遍历,时间复杂度为O(NM)其中N为类别数,M为样本数。

caffe源码如下:

// Gradient with respect to centers
  // 计算center反向传播的更新量,
  if (this->param_propagate_down_[0]) {
    const Dtype* label = bottom[1]->cpu_data();
    Dtype* center_diff = this->blobs_[0]->mutable_cpu_diff();//存center更新值的地方
    //variation_sum_data数据维度和center一样,用于更新center时候的计算,不出现的类别都为0
    Dtype* variation_sum_data = variation_sum_.mutable_cpu_data();
    const Dtype* distance_data = distance_.cpu_data();

    // \sum_{y_i==j}
    // 计算一个batch中出现的样本的类别的更新量
    caffe_set(N_ * K_, (Dtype)0., variation_sum_.mutable_cpu_data());
    for (int n = 0; n < N_; n++) {    // N个类别,每个都要试一遍,时间复杂度O(N_*M_)
      int count = 0;                  //当前类别出现的个数
      for (int m = 0; m < M_; m++) {  //m个样本
        const int label_value = static_cast<int>(label[m]); //当前样本的类别id
        if (label_value == n) {
          count++;
          //variation_sum_data初始值为0, 0-distance_data,即为公式中的相减操作
          caffe_sub(K_, variation_sum_data + n * K_, distance_data + m * K_, variation_sum_data + n * K_);
        }
      }
	  //center_diff = (Dtype)1./(count + (Dtype)1.) * variation_sum_data
	  //K_为个数,特征的维度,即计算的偏移量是一个向量
      caffe_axpy(K_, (Dtype)1./(count + (Dtype)1.), variation_sum_data + n * K_, center_diff + n * K_);
    }
  }


[疑点]

论文中说引入两个超参数,其中 \lambda 为loss的权重,这个比较简单。

另外还有一个超参数: \alpha 用来调节center的更新,但是没有看到这个超参数在哪里使用了啊。。。

求大佬指点! 哈哈哈哈

--------更新线---------

我一定是个智障。。。 center loss层已经把center搞成层参数了, \alpha 就是该层的学习率啊!!上面图中对w参数的更新是在另外一个softmax loss层中进行的,用的是那个层的学习率,不要弄混淆了。。。

编辑于 2018-06-19

文章被以下专栏收录