FCOS论文及源码详解(六)
在 FCOS论文及源码详解(二)中提到,FCOS的training部分关键在于调用train_net.py,故解析FCOS源码从该文件开始。
train_net.py→train()开头
→build_detection_model()→GeneralizedRCNN→build_backbone(), build_rpn(), build_roi_heads().
build_backbone()→build_resnet_backbone()→ResNet
FCOS论文及源码详解(二)、 FCOS论文及源码详解(三)已将ResNet分析完毕。
FCOS论文及源码详解(四)得到build_rpn()→build_fcos()→FCOSModule,并分析其head部分, FCOS论文及源码详解(五)分析其box_selector_test部分,本篇分析其loss_evaluator部分。
FCOS代码
build_rpn()
fcos.fcos.FCOSModule
loss_evaluator
loss_evaluator→make_fcos_loss_evaluator()调用自fcos_core.modeling.rpn.fcos.loss
def make_fcos_loss_evaluator(cfg):
loss_evaluator = FCOSLossComputation(cfg)
return loss_evaluator
即loss_evaluator→make_fcos_loss_evaluator()→FCOSLossComputation
在其上方索至
class FCOSLossComputation(object):
"""
This class computes the FCOS losses.
"""
def __init__(self, cfg):
self.cls_loss_func = SigmoidFocalLoss(
cfg.MODEL.FCOS.LOSS_GAMMA, # 2.0
cfg.MODEL.FCOS.LOSS_ALPHA # 0.25
)
SigmoidFocalLoss调用自fcos_core.layers
class SigmoidFocalLoss(nn.Module):
def __init__(self, gamma, alpha):
super(SigmoidFocalLoss, self).__init__()
self.gamma = gamma
self.alpha = alpha
def forward(self, logits, targets):
device = logits.device
if logits.is_cuda:
loss_func = sigmoid_focal_loss_cuda
else:
loss_func = sigmoid_focal_loss_cpu
loss = loss_func(logits, targets, self.gamma, self.alpha)
return loss.sum()
以sigmoid_focal_loss_cpu()为例说明,在其上方索至
def sigmoid_focal_loss_cpu(logits, targets, gamma, alpha):
num_classes = logits.shape[1]
gamma = gamma[0]
alpha = alpha[0]
dtype = targets.dtype
device = targets.device
# arange(): [start, end)
# unsqueeze(): Returns a new tensor with a dimension of size one inserted at the specified position.
# dim (int) – the index at which to insert the singleton dimension
class_range = torch.arange(1, num_classes+1, dtype=dtype, device=device).unsqueeze(0)
t = targets.unsqueeze(1)
p = torch.sigmoid(logits)
term1 = (1 - p) ** gamma * torch.log(p)
term2 = p ** gamma * torch.log(1 - p)
return -(t == class_range).float() * term1 * alpha - ((t != class_range) * (t >= 0)).float() * term2 * (1 - alpha)
该算式即
F
o
c
a
l
L
o
s
s
(
p
t
)
=
−
α
t
(
1
−
p
t
)
γ
l
o
g
(
p
t
)
Focal Loss(p_t)=−α_t(1 − p_t)^γ log(p_t)
FocalLoss(pt)=−αt(1−pt)γlog(pt)
p
t
=
{
p
,
y
=
1
1
−
p
,
o
t
h
e
r
w
i
s
e
p_t= \left\{ \begin{array}{lr} p, y=1 & \\ 1-p, otherwise & \\ \end{array} \right.
pt={p,y=11−p,otherwise
参见FCOS论文及源码详解(一)
继续看类FCOSLossComputation的方法__init__()
self.fpn_strides = cfg.MODEL.FCOS.FPN_STRIDES # [8, 16, 32, 64, 128]
self.center_sampling_radius = cfg.MODEL.FCOS.CENTER_SAMPLING_RADIUS # 0.0 if CENTER_SAMPLING_RADIUS <= 0, it will disable center sampling
self.iou_loss_type = cfg.MODEL.FCOS.IOU_LOSS_TYPE # "iou"
self.norm_reg_targets = cfg.MODEL.FCOS.NORM_REG_TARGETS # False
# we make use of IOU Loss for bounding boxes regression,
# but we found that L1 in log scale can yield a similar performance
self.box_reg_loss_func = IOULoss(self.iou_loss_type)
self.centerness_loss_func = nn.BCEWithLogitsLoss(reduction="sum")
nn.BCEWithLogitsLoss(reduction=“sum”)→sigmiod+BCELoss(Binary Cross Entropy Loss)
L
=
l
1
,
.
.
.
,
l
N
T
,
l
n
=
−
w
n
[
y
n
⋅
l
o
g
σ
(
x
n
)
+
(
1
−
y
n
)
⋅
l
o
g
(
1
−
σ
(
x
n
)
)
]
L={l_1,...,l_N}^T,l_n=-w_n[y_n·logσ(x_n)+(1-y_n)·log(1-σ(x_n))]
L=l1,...,lNT,ln=−wn[yn⋅logσ(xn)+(1−yn)⋅log(1−σ(xn))]
"sum"→
l
(
x
,
y
)
=
s
u
m
(
L
)
l(x,y)=sum(L)
l(x,y)=sum(L)
IOULoss调用自fcos_core.layers
class IOULoss(nn.Module):
def __init__(self, loss_type="iou"):
super(IOULoss, self).__init__()
self.loss_type = loss_type
def forward(self, pred, target, weight=None):
pred_left = pred[:, 0]
# omit pred_top, pred_right, pred_bottom
# omit target_left, ...
target_area = (target_left + target_right) * \
(target_top + target_bottom)
# omit pred_area
w_intersect = torch.min(pred_left, target_left) + torch.min(pred_right, target_right)
# omit g_w_intersect, h_intersect, g_h_intersect
ac_uion = g_w_intersect * g_h_intersect + 1e-7
area_intersect = w_intersect * h_intersect
area_union = target_area + pred_area - area_intersect
# 推断:+1防止除数为0
ious = (area_intersect + 1.0) / (area_union + 1.0)
gious = ious - (ac_uion - area_union) / ac_uion
if self.loss_type == 'iou':
losses = -torch.log(ious)
# omit elif
if weight is not None and weight.sum() > 0:
return (losses * weight).sum()
else:
assert losses.numel() != 0
return losses.sum()
IOULoss可参考下图
参见FCOS论文及源码详解(一)