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

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
Caffe是一个深度学习框架,它主要用于卷积神经网络。然而,由于LSTM网络也是深度学习的一种形式,因此Caffe也可以用于实现LSTM网络。 在Caffe中,实现LSTM网络的步骤与实现卷积神经网络的步骤类似。主要的区别在于LSTM网络中的神经元类型和连接方式。 以下是一个简单的LSTM网络的示例代码: ```python # 定义LSTM网络结构 net = caffe.NetSpec() net.data = caffe.layers.Input(shape=dict(dim=[1, 1, 10, 1])) # 定义LSTM层 net.lstm1 = caffe.layers.LSTM(num_output=10, weight_filler=dict(type='xavier')) net.lstm2 = caffe.layers.LSTM(num_output=10, weight_filler=dict(type='xavier')) # 定义全连接层 net.fc = caffe.layers.InnerProduct(num_output=1, weight_filler=dict(type='xavier')) # 定义网络结构 net.lstm1_top, net.lstm1_memory = net.lstm1(net.data, None) net.lstm2_top, net.lstm2_memory = net.lstm2(net.lstm1_top, net.lstm1_memory) net.fc_top = net.fc(net.lstm2_top) # 生成网络 with open('lstm.prototxt', 'w') as f: f.write(str(net.to_proto())) ``` 在这个示例中,我们定义了一个包含两个LSTM层和一个全连接层的网络。我们使用`LSTM`层来定义LSTM神经元,并使用`InnerProduct`层定义全连接层。我们还使用`Input`层来定义输入数据的形状。 在定义完网络结构之后,我们可以使用`to_proto()`方法将网络结构以字符串的形式写入文件中。这个文件可以被Caffe加载并用于训练和测试LSTM网络。 需要注意的是,与卷积神经网络不同,LSTM网络需要定义内部记忆状态。在这个示例中,我们使用`net.lstm1_memory`和`net.lstm2_memory`来存储LSTM层的内部状态,以便在下一次前向传递中使用。 此外,还需要注意LSTM网络的训练过程中需要使用BPTT(Back-Propagation Through Time)算法。这个算法是用于处理时间序列数据的反向传播算法。在Caffe中,我们可以使用`LSTMUnitLayer`层来实现BPTT算法。 总之,Caffe可以用于实现LSTM网络,只需要将LSTM层和全连接层添加到网络中,并定义好内部状态和BPTT算法即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值