Quantcast
Viewing all articles
Browse latest Browse all 9596

mxnet_center_loss

相关论文见 《A Discriminative Feature Learning Approach for Deep Face Recognition》 , Yandong Wen, Kaipeng Zhang, Zhifeng Li, and Yu Qiao, Shenzhen

更新作者官方放出的 代码 ,基于caffe

作者的出发点和contrastive loss一样,在配合softmax适用的时候,希望使学习到的特征具有更好的泛化性和辨别能力。通过惩罚每个种类的样本和该种类样本中心的偏移,使得同一种类的样本尽量聚合在一起。相对于triplet和contrastive来说,这个目标其实相对‘清晰’, 所以网络收敛的速度甚至比仅仅用softmax更快,而且不需要像前两者那样构造大量的训练对,细节请见论文。

mxnet构造新operator

尽管我是mxnet的粉丝,但是不得不说现在mxnet的文档还是欠缺了一些,尤其文档列出来的操作通常是最常用的,稍微有点深入的时候就需要看代码和issue。所以这里稍微总结一下自己踩坑的过程,给大家做一个参考。

目前mxnet有两种构造新operator的方式,一是用提供的python接口,另外就是用mshadow写c++的代码了,我们验证算法有效性当然是首选用python了,所以还是要先看完这个 连接 。

简而言之:

list_argument 函数,告知系统需要的 参数 ,operator的weight也在这里声明,返回的list的顺序和 in_data 的顺序一致。 infer_shape 通过 in_shape 参数分别推算 in_data , out_data 和 aux 的shape,系统会自动为weight分配内存,多卡时候会自动根据Context分配到对应的gpu上去,不需要手动干预。 forward 和 backward 就是具体干活的地方,这时候内存都已经准备好了。 1 构造operator和对应metric

代码 center_loss.py

算法本身并不复杂,基本是直白地翻译到python代码,没有什么特别。当然如果需要更好的效率可能要考虑使用mshadow的库实现c++的算法,把for循环做个展开什么的,可以参考mxnet的其它operator,当然得利于mxnet的框架优势,一份代码写好cpu和gpu都可以用啦, 需要注意的可能有:

由于我们使用了softmax和centerloss两个loss,所以需要对centerloss构造一个metric的回调函数,使用 mx.metric.CompositeEvalMetric 组合两个metric,传递给 model.fit 函数的对应参数 eval_metric centerloss这个operator有个稍微特殊一点的地方,它内含的各个类别的中心center,既不是 in_data 也不是 out_data ,又不是普通的weight(它并不是随着grad去更新的,有自己的更新手段),所以最终把这个作为了 auxiliary 。但是对多卡来说,这里其实不是很准确,由于mxnet采用的数据划分,所以在每个卡上都有一套完整的参数。可是centerloss的参数center是aux变量,所以会各自有一套不同的center。暂时没有想到更好的方式来处理这个问题,不过由于特征抽取的网络是同步的,所以每个center差别也不大,最好的训练结果其实没有太大的区别,当然我只在MNIST这个小库上对比了一发,如果你有更好的方法请告诉我 :) 最后,由于使用了aux变量,需要添加 list_auxiliary_states 函数表明需求, 并在 infer_shape 函数中返回正确的大小。每个参数都加上了bias字样,因为mxnet会根据不同的名字选择不同的初始化方式,bias一般都使用零初始化。另外一个比较坑的地方是, diff_bias 的shape是需要和 in_data 一致,但是不要使用 in_shape 来推断。假如每个batch的大小的是100,那么 diff_bias 应该是(100,2)的shape,但是如果我有两个gpu卡,在每个卡在下一次 infer_shape 的时候会得到(50,2)。由于mxnet是先申请一次cpu的内存,然后把参数复制过去,这里会因为shape不一致而报错。所以这里才用了直接计算每张卡的样本数目,当作参数传递进去。 2 数据迭代器

代码 data.py

其实这里的softmax和centerloss用的label一模一样,可是我把一个label输入给softmax和centerloss时,报错说label的数目和output的数目不一致,无奈只能重载一下,输出两个一样的label。另外记得迭代器输出的名字 softmax_label 要和 Variable 的名字对应。(刚开始没注意MNIST默认输出的居然是叫 "softmax_label" ,导致一个莫名其妙的错误)

3 训练

代码 train.py train_model.py

主要结构都是从mxnet的example拿来用的,稍微做了一些修改。

首先需要修改 data.py 中的 mxnet_root 变量为你本地mxnet的根目录,然后就可以训练测试了。为了方便可视化,中间的fc层的hidden_num被我修改到了2,因此MNIST的效果很差,不过毕竟只是为了验证loss的正确性嘛。

cpu训练

python train.py --batch-size=128

gpus训练( 当然MNIST这么小的例子用多卡反而会慢 )

python train.py --batch-size=128

python train.py --gpus=0,1 --batch-size=256

4 可视化结果

代码 vis.py

如果你没改动其它地方,训练完之后直接运行

python vis.py

效果应该是像下图右边那个样子,左边是去掉centerloss之后运行的结果:


Image may be NSFW.
Clik here to view.
mxnet_center_loss

简单验证一下我们训练的结果,由于我是直接embedding到2维,所以和作者论文中显示的可能略有不同,不过可以看出,不同类别都和自己聚的比较靠拢,显然后者的泛化性会更好。并且对比两者的验证准确率,可以发现后者其实收敛的更快(我并没有比较其它的实验比较,所以也许这也不能作为centerloss会提升训练准确性的依据,详细还是参考作者论文较好):


Image may be NSFW.
Clik here to view.
mxnet_center_loss

当然其实对于闭集的分类来说,这样一点小的提升可能不是很明显。可是考虑对深度学习的人脸识别来说,这个loss非常的适用。考虑到人脸通常不是闭集的分类,实际使用的时候是去掉了fc层,使用中间某个layer的输出作为人脸的特征表达,我们使用有限的训练集训练网络时候,我们当然希望有右边那种比较‘紧凑’的嵌入,而softmax的监督信号并不要求一个人的人脸表达非常接近。

引用:potable_water:狐狸大神的说法:

想象一个完美的人脸特征的embedding,所有人的特征都在其中分散开来,每个人自己的特征都聚合在一起。现在拿掉你训练集中没有的人,留出很多空间是不是空荡荡的。

当然实际的训练不会是这个样子,不过至少可以看到同类样本聚合在一起是个必要条件。因此作者在megaface上的实验,使用centerloss和不使用center相比,进步是非常明显的,这点我在自己的人脸实验上也得到了验证。


Viewing all articles
Browse latest Browse all 9596

Trending Articles