小喵的唠叨话:在写完上一次的博客之后,已经过去了2个月的时间,小喵在此期间,做了大量的实验工作,最终在使用的DeepID2的方法之后,取得了很不错的结果。这次呢,主要讲述一个比较新的论文中的方法,L-Softmax,据说单model在LFW上能达到98.71%的等错误率。更重要的是,小喵觉得这个方法和DeepID2并不冲突,如果二者可以互补,或许单model达到99%+将不是梦想。

再次推销一下~

小喵的博客网址是: http://www.miaoerduo.com

博客原文:  http://www.miaoerduo.com/deep-learning/基于caffe的large-ma…ftmax-loss的实现(上).html

和上一篇博客一样,小喵对读者做了如下的假定:

  1. 了解Deep Learning的基本知识。
  2. 仔细阅读过L-Softmax的论文,了解其中的数学推导。
  3. 使用Caffe作为训练框架。
  4. 即使不满足上述3条,也能持之以恒的学习。

L-Softmax的论文:Large-Margin Softmax Loss for Convolutional Neutral Networks

Google一下,第一条应该就是论文的地址,鉴于大家时间有限,小喵把原文地址也贴出来了,但不保证长期有效。http://jmlr.org/proceedings/papers/v48/liud16.pdf 这里我们也将整个系列分几部分来讲。

一、margin与lambda

margin和lambda这两个参数是我们这篇博客的重点。也是整篇论文的重点。对于分类的任务,每个样本都会有N的输出的分数(N的类别),如果在训练中,人为的使正确类别的得分变小,也就是说加大了区分正确类别的难度,那么网络就会学习出更有区分能力的特征,并且加大类间的距离。作者选用的加大难度的方式就是改变最后一个FC层中的weight和特征之间的角度值,角度增大的倍数就是margin,从而使特定类别的得分变小。而第二个参数lambda是为了避免网络不收敛而设定的,我们之后会讲到。

为了实现这个效果,我们需要设计一个新的层,large_margin_inner_product_layer。这个层和一般的inner_product_layer很相似,但是多了特定类别削弱的功能。 考虑到这个层是有参数的,我们需要在caffe.proto(caffe_home/src/caffe/proto/caffe.proto)中做一些修改。这里的定义是按照protobuf的语法写的,简单的修改只要照着其他的参数来改写就好。 首先定义我们的这个层的参数。

 message LargeMarginInnerProductParameter {
optional uint32 num_output = ; // The number of outputs for the layer
optional bool bias_term = [default = true]; // whether to have bias terms
optional FillerParameter weight_filler = ; // The filler for the weight
optional FillerParameter bias_filler = ; // The filler for the bias // The first axis to be lumped into a single inner product computation;
// all preceding axes are retained in the output.
// May be negative to index from the end (e.g., -1 for the last axis).
optional int32 axis = [default = ];
// Specify whether to transpose the weight matrix or not.
// If transpose == true, any operations will be performed on the transpose
// of the weight matrix. The weight matrix itself is not going to be transposed
// but rather the transfer flag of operations will be toggled accordingly.
optional bool transpose = [default = false];
optional uint32 margin = [default = ];
optional float lambda = [default = ];
}

参数的定义和InnerProductParameter非常相似,只是多了两个参数margin和lambda。 之后在LayerParameter添加一个可选参数(照着InnerProductParameter写就好)。

optional LargeMarginInnerProductParameter large_margin_inner_product_param = 147;

这时,喵粉可能很在意这个147是怎么回事。其实呢,在protobuf中,每个结构中的变量都需要一个id,只要保证不重复即可。我们在LayerParameter的最开始可以看到这么一行注释:

说明下一个有效的id是147。这里我们新加的参数就果断占用了这个id。

修改之后,建议把注释改一下(不要人为的挖坑): LayerParameter next available layer-specific ID: 148 (last added: large_margin_inner_product_param)

避免之后再新加层的时候出问题。

工作完毕,我们就可以在train_val.prototxt中用这种方式使用这个新层了(具体的使用,后面再说):

 layer {
name: "fc2"
type: "LargeMarginInnerProduct"
bottom: "fc1"
bottom: "label"
top: "fc2"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
large_margin_inner_product_param {
num_output:
margin:
lambda:
weight_filler {
type: "xavier"
}
}
}

二,运筹帷幄之成员变量

我们刚刚在caffe.proto中,添加了新参数的定义。而事实上,我们还没有这个层的具体实现。这部分,主要介绍我们需要的临时变量。 首先,我们要理清整个计算的流程。

先看前馈。

第一步,需要求出W和x的夹角的余弦值:

\[\cos(\theta_j)=\frac{W_j^Tx_i}{\|W_j\|\|x_i\|}\]

第二步,计算m倍角度的余弦值:

\[\cos(m\theta_i)=\sum_n(-1)^n{C_m^{2n}\cos^{m-2n}(\theta_i)\cdot(1-\cos(\theta_i)^2)^n}, (2n\leq m)\]

第三步,计算前馈:

\[f_{y_{i}}=(-1)^k\cdot\|W_{y_{i}}\|\|x_{i}\|\cos(m\theta_i)-2k\cdot\|W_{y_i}\|\|x_i\|\]

k是根据$\cos(\theta)$的取值决定的。

后馈比前馈要复杂一些,不过使用的变量也是一样的。 因此我们可以编写自己的头文件了。

 #ifndef CAFFE_LARGE_MARGIN_INNER_PRODUCT_LAYER_HPP_
#define CAFFE_LARGE_MARGIN_INNER_PRODUCT_LAYER_HPP_ #include <vector> #include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h" namespace caffe { template <typename Dtype>
class LargeMarginInnerProductLayer : public Layer<Dtype> {
public:
explicit LargeMarginInnerProductLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top); virtual inline const char* type() const { return "LargeMarginInnerProduct"; }
// edited by miao
// LM_FC层有两个bottom
virtual inline int ExactNumBottomBlobs() const { return ; }
// end edited
virtual inline int ExactNumTopBlobs() const { return ; } protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom); int M_;
int K_;
int N_;
bool bias_term_;
Blob<Dtype> bias_multiplier_;
bool transpose_; ///< if true, assume transposed weights // added by miao // 一些常数
Blob<Dtype> cos_theta_bound_; // 区间边界的cos值
Blob<int> k_; // 当前角度theta所在的区间的位置
Blob<int> C_M_N_; // 组合数
unsigned int margin; // margin
float lambda; // lambda Blob<Dtype> wx_; // wjT * xi
Blob<Dtype> abs_w_; // ||wj||
Blob<Dtype> abs_x_; // ||xi||
Blob<Dtype> cos_t_; // cos(theta)
Blob<Dtype> cos_mt_; // cos(margin * theta) Blob<Dtype> dydw_; // 输出对w的导数
Blob<Dtype> dydx_; // 输出对x的导数
// end added
}; } // namespace caffe #endif // CAFFE_LARGE_MARGIN_INNER_PRODUCT_LAYER_HPP_

这里主要是复制了inner_product_layer.hpp,然后做了一点修改。具体是增加了几个成员变量,同时改了ExactNumBottomBlobs的返回值,因为我们的这个层磁带bottom需要两个,前一层的feature和样本的label。

三、内存和常量的初始化

这部分,主要给我们的各个成员变量分配内存,同时给几个常量进行初始化。这里也是照着inner_product_layer.cpp来写的,在setup的时候,增加了一些用于初始化的代码,并删除了forward_cpu和backwark_cpu的具体实现。

修改之后的代码如下:

 #include <vector>
#include <cmath> #include "caffe/filler.hpp"
#include "caffe/layers/large_margin_inner_product_layer.hpp"
#include "caffe/util/math_functions.hpp" #define PI 3.14159265 namespace caffe { int factorial(int n) {
if ( == n) return ;
int f = ;
while (n) {
f *= n;
-- n;
}
return f;
} template <typename Dtype>
void LargeMarginInnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) { const int axis = bottom[]->CanonicalAxisIndex(
this->layer_param_.large_margin_inner_product_param().axis());
// added by miao
std::vector<int> wx_shape();
wx_shape[] = bottom[]->shape();
this->wx_.Reshape(wx_shape);
this->abs_w_.Reshape(wx_shape);
this->abs_x_.Reshape(wx_shape);
this->k_.Reshape(wx_shape);
this->cos_t_.Reshape(wx_shape);
this->cos_mt_.Reshape(wx_shape); std::vector<int> cos_theta_bound_shape();
this->margin = static_cast<unsigned int>(this->layer_param_.large_margin_inner_product_param().margin());
cos_theta_bound_shape[] = this->margin + ;
this->cos_theta_bound_.Reshape(cos_theta_bound_shape);
for (int k = ; k <= this->margin; ++ k) {
this->cos_theta_bound_.mutable_cpu_data()[k] = std::cos(PI * k / this->margin);
}
this->C_M_N_.Reshape(cos_theta_bound_shape);
for (int n = ; n <= this->margin; ++ n) {
this->C_M_N_.mutable_cpu_data()[n] = factorial(this->margin) / factorial(this->margin - n) / factorial(n);
} // d size
std::vector<int> d_shape();
d_shape[] = bottom[]->shape();
d_shape[] = bottom[]->count(axis);
this->dydw_.Reshape(d_shape);
this->dydx_.Reshape(d_shape); this->lambda = this->layer_param_.large_margin_inner_product_param().lambda();
// end added transpose_ = false; // 坚决不转置! const int num_output = this->layer_param_.large_margin_inner_product_param().num_output();
bias_term_ = this->layer_param_.large_marin_inner_product_param().bias_term();
N_ = num_output; // Dimensions starting from "axis" are "flattened" into a single
// length K_ vector. For example, if bottom[0]'s shape is (N, C, H, W),
// and axis == 1, N inner products with dimension CHW are performed.
K_ = bottom[]->count(axis);
// Check if we need to set up the weights
if (this->blobs_.size() > ) {
LOG(INFO) << "Skipping parameter initialization";
} else {
if (bias_term_) {
this->blobs_.resize();
} else {
this->blobs_.resize();
}
// Initialize the weights
vector<int> weight_shape();
if (transpose_) {
weight_shape[] = K_;
weight_shape[] = N_;
} else {
weight_shape[] = N_;
weight_shape[] = K_;
}
this->blobs_[].reset(new Blob<Dtype>(weight_shape));
// fill the weights
shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>(
this->layer_param_.large_margin_inner_product_param().weight_filler()));
weight_filler->Fill(this->blobs_[].get());
// If necessary, intiialize and fill the bias term
if (bias_term_) {
vector<int> bias_shape(, N_);
this->blobs_[].reset(new Blob<Dtype>(bias_shape));
shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>(
this->layer_param_.inner_product_param().bias_filler()));
bias_filler->Fill(this->blobs_[].get());
} } // parameter initialization
this->param_propagate_down_.resize(this->blobs_.size(), true);
} template <typename Dtype>
void LargeMarginInnerProductLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
// Figure out the dimensions
const int axis = bottom[]->CanonicalAxisIndex(
this->layer_param_.large_margin_inner_product_param().axis());
const int new_K = bottom[]->count(axis);
CHECK_EQ(K_, new_K)
<< "Input size incompatible with large margin inner product parameters.";
// The first "axis" dimensions are independent inner products; the total
// number of these is M_, the product over these dimensions.
M_ = bottom[]->count(, axis);
// The top shape will be the bottom shape with the flattened axes dropped,
// and replaced by a single axis with dimension num_output (N_).
vector<int> top_shape = bottom[]->shape();
top_shape.resize(axis + );
top_shape[axis] = N_;
top[]->Reshape(top_shape);
} template <typename Dtype>
void LargeMarginInnerProductLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
// not implement
} template <typename Dtype>
void LargeMarginInnerProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom) {
// not implement
} #ifdef CPU_ONLY
STUB_GPU(LargeMarginInnerProductLayer);
#endif INSTANTIATE_CLASS(LargeMarginInnerProductLayer);
REGISTER_LAYER_CLASS(LargeMarginInnerProduct); } // namespace caffe

至此,large_margin_inner_product_layer的准备工作就做完了。下一篇博客,我们来详细的讨论前馈的具体实现。

如果您觉得本文对您有帮助,那请小喵喝杯茶吧~~O(∩_∩)O~~ 小喵为了写公式,还专门学习了$\LaTeX$。

转载请注明出处~

基于Caffe的Large Margin Softmax Loss的实现(上)的更多相关文章

  1. 基于Caffe的Large Margin Softmax Loss的实现(中)

    小喵的唠叨话:前一篇博客,我们做完了L-Softmax的准备工作.而这一章,我们开始进行前馈的研究. 小喵博客: http://miaoerduo.com 博客原文:  http://www.miao ...

  2. Large Margin Softmax Loss for Speaker Verification

    [INTERSPEECH 2019接收] 链接:https://arxiv.org/pdf/1904.03479.pdf 这篇文章在会议的speaker session中.本文主要讨论了说话人验证中的 ...

  3. cosface: large margin cosine loss for deep face recognition

    目录 概 主要内容 Wang H, Wang Y, Zhou Z, et al. CosFace: Large Margin Cosine Loss for Deep Face Recognition ...

  4. Large-Margin Softmax Loss for Convolutional Neural Networks

    paper url: https://arxiv.org/pdf/1612.02295 year:2017 Introduction 交叉熵损失与softmax一起使用可以说是CNN中最常用的监督组件 ...

  5. caffe中softmax loss源码阅读

    (1) softmax loss <1> softmax loss的函数形式为:     (1) zi为softmax的输入,f(zi)为softmax的输出. <2> sof ...

  6. 基于Caffe的DeepID2实现(下)

    小喵的唠叨话:这次的博客,真心累伤了小喵的心.但考虑到知识需要巩固和分享,小喵决定这次把剩下的内容都写完. 小喵的博客:http://www.miaoerduo.com 博客原文: http://ww ...

  7. 基于Caffe的DeepID2实现(中)

    小喵的唠叨话:我们在上一篇博客里面,介绍了Caffe的Data层的编写.有了Data层,下一步则是如何去使用生成好的训练数据.也就是这一篇的内容. 小喵的博客:http://www.miaoerduo ...

  8. 基于Caffe的DeepID2实现(上)

    小喵的唠叨话:小喵最近在做人脸识别的工作,打算将汤晓鸥前辈的DeepID,DeepID2等算法进行实验和复现.DeepID的方法最简单,而DeepID2的实现却略微复杂,并且互联网上也没有比较好的资源 ...

  9. 卷积神经网络系列之softmax,softmax loss和cross entropy的讲解

    我们知道卷积神经网络(CNN)在图像领域的应用已经非常广泛了,一般一个CNN网络主要包含卷积层,池化层(pooling),全连接层,损失层等.虽然现在已经开源了很多深度学习框架(比如MxNet,Caf ...

随机推荐

  1. vmware里面的名词 vSphere、vCenter Server、ESXI、vSphere Client

    vmware里面的名词 vSphere.vCenter Server.ESXI.vSphere Client vSphere.vCenter Server.ESXI.vSphere Client VS ...

  2. 在docker中运行ASP.NET Core Web API应用程序(附AWS Windows Server 2016 widt Container实战案例)

    环境准备 1.亚马逊EC2 Windows Server 2016 with Container 2.Visual Studio 2015 Enterprise(Profresianal要装Updat ...

  3. 【.net 深呼吸】跨应用程序域执行程序集

    应用程序域,你在网上可以查到它的定义,凡是概念性的东西,大伙儿只需要会搜索就行,内容看了就罢,不用去记忆,更不用去背,“名词解释”是大学考试里面最无聊最没水平的题型. 简单地说,应用程序域让你可以在一 ...

  4. IE8/9 JQuery.Ajax 上传文件无效

    IE8/9 JQuery.Ajax 上传文件有两个限制: 使用 JQuery.Ajax 无法上传文件(因为无法使用 FormData,FormData 是 HTML5 的一个特性,IE8/9 不支持) ...

  5. CSharpGL(33)使用uniform块来优化对uniform变量的读写

    CSharpGL(33)使用uniform块来优化对uniform变量的读写 +BIT祝威+悄悄在此留下版了个权的信息说: Uniform块 如果shader程序变得比较复杂,那么其中用到的unifo ...

  6. javascript 笔记!

    1.通过javascript向文档中输出文本 document是javascript的内置对象,代表浏览器的文档部分 document.write("Hello Javascript&quo ...

  7. 说说BPM数据表和日志表中几个状态字段的详细解释

    有个客户说需要根据这些字段的值作为判断条件做一些定制化需求,所以需要知道这些字段的名词解释,以及里面存储的值具体代表什么意思 我只好为你们整理奉上这些了! Open Work Sheet  0 Sav ...

  8. 【SAP业务模式】之ICS(二):基础数据

    讲完业务,计划在前台做一下ICS的基本操作,不过在操作之前,得先建立好基本的基础数据. 1.首先创建接单公司LEON,对应工厂是ADA: 2.创建生产公司MXPL,对应工厂是PL01: 3.创建接单公 ...

  9. nginx启动报错:/usr/local/nginx/sbin/nginx: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No such file or directory

    查看依赖库:

  10. [转] 从知名外企到创业公司做CTO是一种怎样的体验?

    这是我近期接受51CTO记者李玲玲采访的一篇文章,分享给大家. 作者:李玲玲来源:51cto.com|2016-12-30 15:47 http://cio.51cto.com/art/201612/ ...