1. 程式人生 > >正則化方法之DropBlock

正則化方法之DropBlock

論文:

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

呼叫:

......
pool=*****
now_step=tf.Variable(0,trainable=False,name="now_step")
keep_prob=compute_keep_prob(now_step=now_step,trainable=True)
dropblock=DropBlock(pool,keep_prob,[3,3],tf.shape(pool))
......

訓練中只需要將每一步step 傳遞(feed)給now_step即可。