Regularization
1. Label Smoothing
对于一个分类问题,假设一共有 K K K 类,分别用 y 1 , y 2 , . . . , y i y_1,y_2,...,y_i y1,y2,...,yi表示,label smoothing可以用以下公式表示:
{
y
i
′
=
(
1
−
ϵ
)
⋅
y
i
+
ϵ
u
(
K
)
u
(
K
)
=
1
K
\left\{ \begin{aligned} y_i' &=(1-\epsilon)\cdot y_i+\epsilon u(K)\\ u(K)&=\frac{1}{K} \end{aligned} \right.
⎩⎨⎧yi′u(K)=(1−ϵ)⋅yi+ϵu(K)=K1
where
ϵ
=
0.1
(
d
e
f
a
u
l
t
)
\epsilon=0.1(default)
ϵ=0.1(default) and
y
′
y'
y′ represents the smoothened labels.
要实现,首先要看一下YOLOv3里面关于label是怎么处理的。训练custom数据的时候,按照readme文件
生成了一个有标签的txt文件,data/custom/labels/。The dataloader expects that the annotation file corresponding to the image data/custom/images/train.jpg has the path data/custom/labels/train.txt. 看看哪里用到了labels文件夹。
。。。没研究明白lebel到底是怎么传的,决定先实现这个算法,放进utils.py里面。
函数名:label_smoothing();
input:smoothing之前的labels,总类别数K;
output:做完label smoothing的labels,记为soft_labels 返回。
def label_smoothing(labels, K, epsilon=0.1):
"""
labels: list of int, original labels;
K: int, total # of classes
epsilon: float, default = 0.1
"""
u_k = 1/K
soft_labels = [round(((1-epsilon)*label + epsilon * u_k) ,4) for label in labels] # 默认保留了4位小数
return soft_labels
2. DropBlock
由dropout演化而来,不再去掉feature map中单独的一个点,而是去掉周围的一片。
两个参数:
b
l
o
c
k
_
s
i
z
e
block\_size
block_size is the size of the block to be dropped,
γ
\gamma
γ controls how many activation units to drop。
函数名:drop_block();
input:feature map A,
b
l
o
c
k
_
s
i
z
e
block\_size
block_size,
γ
\gamma
γ;
output:做完dropblock之后的feature map,A_dropblock。
算法如下:
import torch
import numpy as np
def drop_block(A, block_size=5, mode=None, keep_prob=0.8):
"""`
A: tensor, output activations of a layer , size: (N,C,H,W), 4-D tensor
blocksize: int
gamma: float
mode: str,
"""
if mode == 'Inference' or keep_prob == 1: #keep all
return A
# compute gamma 中点drop率
drop_rate = 1 - keep_prob
gamma = drop_rate / np.power(block_size,2)
# create a padding layer
pad = torch.nn.ZeroPad2d(block_size//2) #假设block_size 5
A_padded = pad(A) #pad过后的图像 假设A:(1 x 3 x 416 x 416), 那么A_padded: (1 x 3 x 420 x 420)
# generate a mask which yields to Bernoulli(gamma) distribution
# mask尺寸和pad过后的图像保持一致,利用小于号判断找到小于gamma的M_{i,j},用float()将True\False转化为0\1
M = (torch.rand(A_padded.shape) < gamma).float() # M: (1 x 3 x 420 x 420)
M_block = torch.ones_like(M) # 最后drop掉block的mask 暂时全是1
# crop patches with block_size around mask_i,j < gamma
for n in range(M.shape[0]): # 1
for c in range(M.shape[1]): # 3
for w in range(block_size//2, M.shape[2]-(block_size//2)): # 2:418
for h in range(block_size//2, M.shape[3]-(block_size//2)):
if M[n,c,w,h] == 1: # ==1 表示 小于gamma为True
# 准备dropblock
# 把M_block中对应的位置改掉,最后直接乘原图的pad就可以返回值了
for i in range(block_size//2):
for j in range(block_size//2):
M_block[n,c,w+i,h+j]=0
M_block[n,c,w+i,h-j]=0
M_block[n,c,w-i,h+j]=0
M_block[n,c,w-i,h-j]=0
# apply the mask, element-wise multiplication
# remove the padded part
mask = M_block[:,:,(block_size//2):(M.shape[2]-(block_size//2)),(block_size//2):(M.shape[2]-(block_size//2))]
A = A * mask
# normalize the features
A = A * mask.numel() / mask.sum()
return A
一堆for应该有什么更好的写法。最后弄了个输入测试了一下
发现最后算下来的keep_prob会比设定的值要高一些,比如这个算下来是0.06,也就是0元素的占比,那么kp应该就是0.94这样,但是当时设定里给的是default=0.8。想了一下可能是因为这个比值在包含了pad部分的图像时去计算,是接近的。但是去掉pad的一圈0之后再算,可能就上升了。
3. 接口设置
这两种都是train的时候用到的tricks,可以考虑在train.py中加上额外的args来调用。
parser.add_argument("--label_smoothing", type=int, default=False, help="whether use label smoothing or not")
parser.add_argument("--drop_block", type=int, default=False, help="whether use DropBlock or not")
后面要用的时候就使用opt.label_smoothing 以及 opt.drop_block进行判断就行。
if opt.label_smoothing == True:
soft_labels = label_smoothing(labels,K=2)
if opt.drop_block == True:
A = drop_block(A)