论文:

DropBlock: A regularization method for convolutional networks 

Github:

https://github.com/miguelvr/dropblock

https://github.com/DHZS/tf-dropblock

 

论文主要提出了一种针对卷积层的正则化方法DropBlock,最终在ImageNet分类任务上,使用Resnet-50结构,将精度提升1.6%个点,在COCO检测任务上,精度提升1.6%个点。

(a)原始输入图像

(b)绿色部分表示激活的特征单元,b图表示了随机dropout激活单元,但是这样dropout后,网络还会从drouout掉的激活单元附近学习到同样的信息

(c)绿色部分表示激活的特征单元,c图表示本文的DropBlock,通过dropout掉一部分相邻的整片的区域(比如头和脚),网络就会去注重学习狗的别的部位的特征,来实现正确分类,从而表现出更好的泛化。

 

DropBlock 模块:

DropBlock 模块主要有2个参数,block_size,γ。

block_size:表示dropout的方块的大小(长,宽),当block_size=1,DropBlock 退化为传统的dropout,正常可以取3,5,7

γ:表示drop过程中的概率,也就是伯努利函数的概率

首先,保证DropBlock drop的元素个数和传统dropout drop的元素个数相等。

那么,传统的dropout drop的元素个数为drop概率乘以一共的元素个数,即(1-keep_prob)*(feat_size*feat_size)

DropBlock drop的元素个数也是drop概率乘以一共的元素个数,

这里在实现中,为了保证drop的block不会超出原始图像,需要先设置一个drop的有效区域,如下图(a)中绿色的区域,也就是原始的图减去block,即(feat_size-block_size+1)。

这里的概率γ为伯努利函数的概率,而这个概率只表示了随机drop的中间点的概率,如下图(a)中红色的×,实际需要的是要drop掉X周围的block_size大小的区域,也就是一个X对应一个block_size大小的区域(假设不相互重叠)。

所以,DropBlock 的概率即γ*(block_size*block_size),有效的区域面积为(feat_size-block_size+1)*(feat_size-block_size+1),最终得到drop掉的元素数目为γ*(block_size*block_size)*(feat_size-block_size+1)*(feat_size-block_size+1)

最后,dropout==DropBlock ,即让(1-keep_prob)*(feat_size*feat_size)=γ*(block_size*block_size)*(feat_size-block_size+1)*(feat_size-block_size+1)

这样就会得到上面最终的伯努利概率γ。

在实验过程中,使用固定的keep_prob值的效果不如使用线性下降的keep_prob值。

实验中keep_prob值从1.0线性下降为0.75。

 

整体流程:

(1)输入特征层A,block_size,γ,模式(训练,测试)

(2,3,4)如果是测试,直接返回特征层A

(5)根据输入的概率γ,使用伯努利函数对生成的随机数mask矩阵进行drop,最终该步骤得到的mask只有0,1值。如上图中(a)。

伯努利概率分布为0-1分布,或者两点分布,公式如下

(6)对上一步得到的mask进行max pooling操作(stride=1,kernel_size=block_size),得到最终需要使用的mask。如上图中(b)。

(7)输入矩阵和Mask矩阵相乘,得到输出矩阵

(8)将上一步的输出矩阵进行归一化操作,保证做DropBlock 之前和做DropBlock 之后,该层都具体相同的均值,方差。

 

哪一层该使用DropBlock:

Keep_prob值的选择(a图):

Drouout:0.7取得最优值

SpatialDropout:0.9取得最优值

DropBlock:0.9取得最优值

 

使用线性下降的方式(scheduling)来使用Keep_prob,可以获得更高的验证准确性。(b图)

Block_size=7时,获得最高验证集准确性

对Keep_prob值进行线性下降方式(scheduling)可以获得更好效果

Resnet-50的第3,4个block加Drouout比只在第4个block加Drouout更有效

在Resnet-50的卷积层和skip connection都使用Drouout效果更好

 

实验分析:

训练时使用block_size=7,keep_prob=0.9,测试时使用block_size=7,keep_prob=1.0,可以获得更好的效果。这点和传统的dropout一样。

第一行为原始的图片,第二行为未使用DropBlock,第三行为block_size=1,第四行为block_size=7。可以看出

(1)使用DropBlock比不使用DropBlock具有更大的热量图

(2)DropBlock的block_size越大,热量图的激活部分越多

 

实验结果:

ImageNet 图像分类:

COCO检测任务:

PASCAL VOC 分割任务:

自己的复现:

import tensorflow as tf
 
 
def compute_gamma(keep_prob,feat_size_h,feat_size_w,block_size_h,block_size_w):
    feat_size_h=tf.to_float(feat_size_h)
    feat_size_w=tf.to_float(feat_size_w)
    gamma=(1-keep_prob)*(feat_size_h*feat_size_w)/(block_size_h*block_size_w)/((feat_size_h-block_size_h+1)*(feat_size_w-block_size_w+1))
    return gamma
 
def compute_keep_prob(now_step=0,start_value=1.0,stop_value=0.75,nr_steps=100000,trainable=False):
    prob_values = tf.linspace(start=start_value, stop=stop_value, num=nr_steps)
    prob=tf.where(tf.less(now_step,nr_steps),prob_values[now_step],prob_values[-1])
    keep_prob=tf.where(tf.equal(trainable,False),start_value,prob)
    return keep_prob
 
def bernoulli(shape,gamma=0):
    mask=tf.cast(tf.random_uniform(shape, minval=0, maxval=1, dtype=tf.float32)<gamma,tf.float32)
    return mask
 
def compute_block_mask(shape,padding,gamma,block_size_shape):
    mask=bernoulli(shape,gamma)
    mask = tf.pad(mask, padding,"CONSTANT")
    mask = tf.nn.max_pool(mask, [1,block_size_shape[0],block_size_shape[1], 1], [1, 1, 1, 1], 'SAME')
    mask = 1 - mask
    return mask
def DropBlock(inputs,keep_prob,block_size_shape,feat_size_shape):
    #keep_prob:keep ratio,float32
    #block_size_shape:[height,width]
    #feat_size_shape:[batch,height,width,channel]
    gamma=compute_gamma(keep_prob,feat_size_shape[1],feat_size_shape[2],block_size_shape[0],block_size_shape[1])
    shape=[feat_size_shape[0],feat_size_shape[1]-block_size_shape[0]+1,feat_size_shape[2]-block_size_shape[1]+1,feat_size_shape[3]]
    bottom = (block_size_shape[0]-1) // 2
    right = (block_size_shape[1]-1) // 2
 
    top = (block_size_shape[0]-1) // 2
    left = (block_size_shape[1]-1) // 2
 
    padding = [[0, 0], [top, bottom], [left, right], [0, 0]]
    mask=compute_block_mask(shape,padding,gamma,block_size_shape)
 
    normalize_mask=mask*tf.to_float(tf.size(mask)) / tf.reduce_sum(mask)
    
    outputs=inputs*normalize_mask
 
    return outputs

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐