CornerNet讲解和tensorflow复现

CornerNet讲解和tensorflow复现

Corner Net简介

cornernet是2018ECCV的oral,是检测领域内的一个新的分支,打破了传统的由faster-rcnn和yolo两大派系的壁垒,另辟蹊径。论文
我已经用tensorflow复现了CornerNet ,记得star哦,复现不易。
先看效果图
不再检测x,y,w,h,只需要检测两个点就可以了
下面是cornernet的结构
corner的结构图,其中corner pooling是本文的一个创新点,也是这篇论文能中oral的一个重大原因

Hourglass

cornernet的backbone是使用hourglass架构,这种架构的优点就是考虑到了多尺度问题,下面就是hourglass的结构图:Stacked Hourglass Networks for Human Pose Estimation
hourglass的网络结构
上面就是hourglass的整体的网络结构。就是几个沙漏进行级联。
其中每个沙漏如下:
每个hourglass的内部结构详细的请看原文,上面提供了连接,这里只是顺带提一下,因为这篇博客的主角不是hourglass。

Corner Pooling

corner pooling的top-left的运算过程这个运算过程大致为:计算top-left时,1.计算top:对feature map从下往上看,最后一行不变,倒数第二行的数值跟最后一行比较,如果大于最后一行,就保留当前数值,否则就更新成最后一行的数值,以此类推。2.计算left:对feature map从右往左看,最后一列保存不变,倒数第二列的数值跟最后一列比较,如果大于最后一列,就保留当前数值,否则就更新成最后一列的数值,以此类推。然后两者相加,就是top-left的数值。
作用:作者在论文里面说,由于预测的那两个点,并不是在‘’内容‘’上面,而是在‘’内容‘’旁边,例如下图,橘色的点并不是打在人物身上,而是打在了人物旁边,但是这个地方所提供的信息并没有作用,而真正有作用的是人物,所以需要通过corner pool的方式将人物上的信息,转移到旁边来,以便模型在预测点的时候能更准确。
预测的点
corner pooling的反向传播:你没看错,这部分作者是自己写了反向传播部分(用c语言),因为是新的方法,所以常见的框架都没有提供这种操作的反向传播。我在这部分用tensorflow实现了前向和反向传播的部分,详情请看代码

def TopPool(inputs):
    #forward
    def forward(inputs):
        out=tf.expand_dims(tf.reduce_max(inputs,1),1)
        i=tf.constant(1)
        batch,h,w,c=inputs.get_shape().as_list()
        def cond(i,out):
            return i < h
        def body(i,out):
            d=tf.expand_dims(tf.reduce_max(inputs[:,i:,:,:],1),1)
            out=tf.concat((out,d),1)
            i = i + 1
            return i,out
        _,out = tf.while_loop(cond, body, [i,out],shape_invariants= [i.get_shape(), tf.TensorShape([batch,None,w,c])])
        return out
    #backward
    def backward(inputs,dy):
        zeros=tf.expand_dims(tf.zeros_like(inputs[:,-1,:,:]),1)
        ones=tf.expand_dims(tf.ones_like(inputs[:,-1,:,:]),1)
        mask=tf.expand_dims(tf.ones_like(inputs[:,-1,:,:]),1)
        batch,h,w,c=inputs.get_shape().as_list()
        i=tf.constant(h-1)

        def cond(i,mask):
            return i > 0
        def body(i,mask):
            max_value=tf.expand_dims(tf.reduce_max(inputs[:,i:,:,:],1),1)
            temp_mask=tf.where(tf.greater(tf.expand_dims(inputs[:,i-1,:,:],1),max_value),ones,zeros)
            mask=tf.concat((temp_mask,mask),1)
            i = i - 1
            return i,mask
        _,mask = tf.while_loop(cond, body, [i,mask],shape_invariants= [i.get_shape(), tf.TensorShape([batch,None,w,c])])
        return mask*dy
    @tf.custom_gradient
    def new_grad(x):
        def grad(dy):
            return backward(x,dy)
        return forward(x), grad
    return new_grad(inputs)

Grouping Corners

既然我们能利用hourglass产生heatmap的图,也就是corner的点,那怎么知道哪两个点是一组的呢,看看下面这张图,一共预测了4个点,那如何确定哪两个点是一组(哪两个是绿色的,哪两个是橘色的)
图一这个地方作者借鉴了这篇文章的一个做法,就是ssociative embedding,这种方法最先用在human pose里面,用来做多人的关键点检测的ssociative embedding: End-to-end learning forjoint detection and groupin
图二
工作原理看上两个图,在预测的heatmaps的地方,已经产生了所有的4个点,现在对4个点进行分组,在train的时候,已知label了,也就是已经知道了每对corner的位置,所以在Embeddings这个预测模块的相应位置,提取出值,然后对这些值求方差和均值,同一对的corner优化这个方差尽可能小,对于不同对的corner则使刚刚求出来的均值做差取绝对值,尽可能优化使绝对值最大。通过pull和push的操作最终训练好,模型的分组操作。
可以看看下面的公式来理解:
图三
对于测试的部分看代码更好理解,说起来还不太好说,所以我就直接把代码放上,看看代码

def decode(self,heat_tl,heat_br,tag_tl,tag_br,offset_tl,offset_br,k=100,ae_threshold=0.5,num_dets=1000):
        batch=tf.shape(heat_br)[0]
        heat_tl=tf.nn.sigmoid(heat_tl)
        heat_br=tf.nn.sigmoid(heat_br)
        #nms
        heat_tl=nms(heat_tl)
        heat_br=nms(heat_br)
        value_tl,position_tl,class_tl,y_tl,x_tl=top_k(heat_tl,k)
        value_br,position_br,class_br,y_br,x_br=top_k(heat_br,k)

        #expand to square
        x_tl=tf.cast(expand_copy(x_tl,k,False),tf.float32)
        y_tl=tf.cast(expand_copy(y_tl,k,False),tf.float32)
        x_br=tf.cast(expand_copy(x_br,k,True),tf.float32)
        y_br=tf.cast(expand_copy(y_br,k,True),tf.float32)


        offset_tl=map_to_vector(offset_tl,position_tl)
        offset_br=map_to_vector(offset_br,position_br)
        # offset_tl=tf.reshape(offset_tl,(tf.shape(offset_tl)[0],k,1,2))
        # offset_br=tf.reshape(offset_br,(tf.shape(offset_br)[0],1,k,2))
        offset_tl=tf.reshape(offset_tl,(offset_tl.get_shape().as_list()[0],k,1,2))
        offset_br=tf.reshape(offset_br,(offset_br.get_shape().as_list()[0],1,k,2))


        x_tl=x_tl+offset_tl[:,:,:,0]
        y_tl=y_tl+offset_tl[:,:,:,1]
        x_br=x_br+offset_br[:,:,:,0]
        y_br=y_br+offset_br[:,:,:,1]

        offset_tl=tf.reshape(offset_tl,(batch,k,1,2))
        offset_br=tf.reshape(offset_br,(batch,1,k,2))

        #all k boxes
        bboxes=tf.stack((x_tl,y_tl,x_br,y_br),axis=-1)

        tag_tl=map_to_vector(tag_tl,position_tl)
        tag_tl=tf.reshape(tag_tl,(batch,k,1))
        tag_br=map_to_vector(tag_br,position_br)
        tag_br=tf.reshape(tag_br,(batch,1,k))
        dists=tf.abs(tag_tl-tag_br)

        value_tl=expand_copy(value_tl,k,False)
        value_br=expand_copy(value_br,k,True)
        scores=(value_tl+value_br)/2
        invalid=-tf.ones_like(scores)

        #======debug=====
        debug_scores=tf.reshape(scores,(batch,-1))
        debug_scores,debug_indexs=tf.nn.top_k(debug_scores,10)
        debug_bboxes=tf.reshape(bboxes,(batch,-1,4))
        debug_bboxes=map_to_vector(debug_bboxes,debug_indexs,transpose=False)
        #======debug=====

        class_tl=tf.cast(expand_copy(class_tl,k,False),tf.float32)#[batch,k,k]
        class_br=tf.cast(expand_copy(class_br,k,True),tf.float32)

        mask_scores=tf.where(tf.cast(tf.equal(class_tl,class_br),tf.int32)>0,scores,invalid)
        mask_scores=tf.where(dists<ae_threshold,mask_scores,invalid)
        mask_scores=tf.where(x_tl<x_br,mask_scores,invalid)
        mask_scores=tf.where(y_tl<y_br,mask_scores,invalid)

        mask_scores=tf.reshape(mask_scores,(batch,-1))
        scores,indexs=tf.nn.top_k(mask_scores,num_dets)
        scores=tf.expand_dims(scores,-1)

        bboxes=tf.reshape(bboxes,(batch,-1,4))
        bboxes=map_to_vector(bboxes,indexs,transpose=False)

        class_=tf.reshape(class_br,(batch,-1,1))
        class_=map_to_vector(class_,indexs,transpose=False)

        value_tl=tf.reshape(value_tl,(batch,-1,1))
        value_tl=map_to_vector(value_tl,indexs,transpose=False)

        value_br=tf.reshape(value_br,(batch,-1,1))
        value_br=map_to_vector(value_br,indexs,transpose=False)

        detection=tf.concat([bboxes,scores,value_tl,value_br,class_],-1)
        return detection,debug_bboxes

Loss

本文loss分为了三个loss:1.对heatmap求focal loss 2.对分组求pull和push loss 3.由label缩小产生的位移偏差loss
1.focal loss:focal loss最初是由何凯明提出来的,用于解决’‘难易训练样本之间的权衡问题’’,有的样本很容易学,于是权重就比较低,有些样本比较难学,于是权重就比较高。另外作者除了使用focal loss外,还用了heatmap预测时常用的高斯点,这一点也体现到loss里面去了,所以跟原始的focal loss有些不同。改动的主要目的就是由于在标签点附近的点,其实偏离也不是很大,所以他的loss权重也应该小一点。
原始的focal loss
y^'为预测概率
本文的focal loss
p~cij为预测的概率
2.分组的pull和push loss
在这里插入图片描述
3.偏移loss
在这里插入图片描述

由于时间关系,没有写太详细,等忙完这段时间,我把内容补充的更详细一点,把细节讲清楚,如果想自己探索细节,我已经提供了tensorflow的代码记得star哦,创作不易

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值