CosineFace实现

介绍

        CosFace: Large Margin Cosine Loss for Deep Face Recognition 是腾讯出的一篇很有名的人脸识别算法,在loss上的创新,用分类的方法实现高精度人脸识别。在github上搜索会有很多实现,有一些做的也很不错。对我来说,我觉得都不太合适,一是我要caffe版本,二是大家实现的方式都不太一样,用起来感觉不太放心,三是,我融入了一些和其他实现不一样的想法。

Normalize layer

        类似cosine face的方法,都以normalize feature 作为基础条件进行设计,normalize前向公式是很简单的,主要是反向求导比较复杂,数学公式可以到这里看:https://blog.csdn.net/Iriving_shu/article/details/78300192,其中可以考虑把y置换成x,就可以对应这个实现:https://github.com/weiliu89/caffe/tree/ssd,其他的实现,如caffe-windows都写的很复杂,要搞清楚对不对还是要花些时间的。当然用这个实现还是要去修改的,主要是把scale的学习给关掉,让它为常数1。

LMCL

        数学公式请看论文,这里只讲解代码,地址见:https://github.com/huneng/cosine_loss


template <typename Dtype>
void LMCLLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top) {
    Dtype scale;
    // norm weight
    if(this->phase_ == TRAIN){
        Dtype *weight = this->blobs_[0]->mutable_cpu_data();

        scale = Dtype(0.0f);
        for(int i = 0; i < N_; i ++){
            Dtype dot;
            dot = caffe_cpu_dot(K_, weight, weight);
            dot = sqrt(dot + 1e-10);
            caffe_scal(K_, Dtype(1.0) / dot, weight);
            weight += K_;

            scale += dot;
        }

        scale /= N_;

        caffe_scal(N_ * K_, scale, this->blobs_[0]->mutable_cpu_data());
    }

    // y = W * x
    caffe_cpu_gemm(CblasNoTrans, CblasTrans, M_, N_, K_,
            Dtype(1.0), bottom[0]->cpu_data(), this->blobs_[0]->cpu_data(),
            Dtype(0.0), top[0]->mutable_cpu_data());

    //*
    // margin
    if(this->phase_ == TRAIN && margin_ > Dtype(1e-5f)){
        Dtype *y = top[0]->mutable_cpu_data();
        const Dtype *ptrLabel = bottom[1]->cpu_data();

        for(int i = 0; i < M_; i ++){
            int label = int(ptrLabel[i]);
            CHECK(label < N_);
            y[label] = ((y[label] / scale) - margin_) * scale;
            y += N_;
        }
    }
    // */
}



template <typename Dtype>
void LMCLLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down,
        const vector<Blob<Dtype>*>& bottom) {
    if (this->param_propagate_down_[0]) {
        const Dtype* dy = top[0]->cpu_diff();
        const Dtype* x = bottom[0]->cpu_data();
        Dtype *dw = this->blobs_[0]->mutable_cpu_diff();

        caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
                N_, K_, M_,
                (Dtype)1., dy, x, (Dtype)0., dw);
    }

    if (propagate_down[0]) {
        const Dtype* dy = top[0]->cpu_diff();
        const Dtype* w = this->blobs_[0]->cpu_data();
        Dtype *dx = bottom[0]->mutable_cpu_diff();

        caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans,
                M_, K_, N_,
                (Dtype)1., dy, w, (Dtype)0., dx);
    }
}

实现需注意几个细节

1 理论上权重是要单位化,但是实际使用的时候并不需要这一层,所以仅设计训练的时候单位化权重;

2很多实现都说收敛很慢,我发现是因为权重单位化了以后,梯度的量纲(取值范围)发生了阶跃,因此统计了每个权重的模,求平均,作为一个缩放系数乘以权重矩阵,根据数学原理,并不影响后续的loss,并且能够快速收敛;

3关于margin,也就是算法的核心,需要在训练的时候根据输入的label进行叠加,因此要有条件判断;

4根据算法原理backward过程和inner product同方法。

结果

       我用vggface作为训练集,采用了一个普通pc机5ms的分类网络,在测试集上最佳阈值下,精度和召回率在95%以上,20ms网络99%以上。

       对于识别这块,我并不是专家,做这个技术的目的是为了其他方向,目前有效的解决了我的问题。因为我觉得我的实现比较简单,效果也挺好,特此记录一下。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值