实例分割之YOLACT(You Only Look At CoefficienTs)
论文:YOLACT: Real-time Instance Segmentation
Github: https://github.com/dbolya/yolact
论文提出了基于one-stage的目标检+分割的框架YOLACT。类似于YOLO,主打的亮点在于实时性。精度弱于mask-rcnn,但是速度却比mask-rcnn快很多。在MS-COCO上达到了29.8的map和33fps的速度(Titan XP)。
论文贡献:
- 提出了实时(>30fps)的检测+分割框架YOLACT
- 提出了NMS的改进版fast nms,可以比传统的NMS快12ms,并且只有很小的精度损失。
- 有别于传统的思路,直接在分类,回归,并行的加一个分支进行分割的预测。而是将其分为了2个任务。第一步,产生整体的prototype masks,类似于语义分割,但是只区分前景背景,不区分类别,可以获得整体图像的mask,对小目标也有更好的感受野。第二步,预测相等通道数的prototype masks的加权组合的权值,有正负之分。最后,将前2步的预测组合起来,得到不同anchor对应的mask分割结果。
整体结构:
网络基础骨架为ResNet101,采用FPN的思想进行底层和高层的特征融合。输入图片大小为550*550。网络最大的亮点,就在于,增加了下面的prorotype分支进行不同位置和前景背景分割的预测。并且在FPN的预测部分,也增加了分割的一个分支。使用FPN处分割的分支预测的权值和prorotype mask结合,生成每一个anchor对应的mask分割结果。
Prototype mask结构:
Prototype部分通过对P3的feature map进行卷积操作和反卷积操作,生成最终138*138*k的输出特征图。对p3层进行了1次上采样操作。最终输出的特征图大小为原图的1/4大小。输出的通道数为k,这里的k默认取32。K值具有很强的鲁棒性。即使取别的值,也不会对结果有很大的影响。
这里,底层的特征图,可以产生更加鲁棒的mask。高层的特征图可以产生更加高质量的特征,同时对小目标效果更好。所以这里选择了高层特征P3层。
Prototype分支,最终输出经过了RELU处理,保证最终的输出的分割结果都是正值。
Mask Coefficients分支:
Mask相关系数分支,对RetinaNet的多任务分支,进行了融合。最终在传统分类和回归的2个分支的基础上,加了一个额外的分割的分支。
其中,c表示分类的类别数,a表示anchor数目,k表示分割的输出通道数,和Prototype分支输出的通道数一样多。
Mask Assembly:
其中,P表示h*w*k的prototype masks,C表示n*k的mask coefficients,其中n表示经过NMS之后的预测的物体个数。σ表示sigmoid操作,保证输出的最终mask为正值。
损失函数:
分类Lcls:softmax CrossEntrop(c个类别+1个背景类)
回归Lbox:Smooth L1
实例分割Lmask:binary cross entropy
语义分割:Prototype mask分支模块加入二分类的语义分割loss。在训练过程中,直接在P3层的输出,接一个1*1卷积,输出c个通道,进行语义分割的loss传递训练。类似YOLOv3,使用c个sigmoid二分类,而不是c+1个softmax多分类。最终可以获得0.4map的收益提升。
当然也可以不接该分支进行训练。在测试的时候,不需要该分支。
Anchor设置:
aspect ratios :1, 1/2, 2
scales: 24, 48, 96,192,384
Prototype Behavior:
Prototype 1,4分别**左面的边界,5**下面的边界。Prototype 2负责**底部,左面的方向。Prototype3负责**背景,Prototype6负责**前景。
Fast NMS:
1)对每一个类别ci,取top-n个候选目标,并按得分降序排列;
2) 计算一个c×n×n的IOU矩阵,其中每个n×n矩阵表示对该类n个候选框,两两之间的IOU;
3)因为自己与自己的IOU=1,IOU(A,B)=IOU(B,A),所以对上一步得到的IOU矩阵进行一次处理。具体做法是将每一个通道,的对角线元素和下三角部分置为0(公式2);
4)去除与得分高的候选框重叠比例较大的框,具体做法是对上一步得到的矩阵,按列取最大值(公式3),然后对取完最大值的矩阵按阈值t划分,大于阈值t的全部置为0(K>t),得到最终Fast NMS的结果。
layers/functions/detection.py
def fast_nms(self, boxes, masks, scores, iou_threshold=0.5, top_k=200, second_threshold=False):
scores, idx = scores.sort(1, descending=True)
idx = idx[:, :top_k].contiguous()
scores = scores[:, :top_k]
num_classes, num_dets = idx.size()
boxes = boxes[idx.view(-1), :].view(num_classes, num_dets, 4)
masks = masks[idx.view(-1), :].view(num_classes, num_dets, -1)
iou = jaccard(boxes, boxes)
iou.triu_(diagonal=1)
iou_max, _ = iou.max(dim=1)
# Now just filter out the ones higher than the threshold
keep = (iou_max <= iou_threshold)
# We should also only keep detections over the confidence threshold, but at the cost of
# maxing out your detection count for every image, you can just not do that. Because we
# have such a minimal amount of computation per detection (matrix mulitplication only),
# this increase doesn't affect us much (+0.2 mAP for 34 -> 33 fps), so we leave it out.
# However, when you implement this in your method, you should do this second threshold.
if second_threshold:
keep *= (scores > self.conf_thresh)#0.05
#keep *= (scores > 0.3)
# Assign each kept detection to its corresponding class
classes = torch.arange(num_classes, device=boxes.device)[:, None].expand_as(keep)
classes = classes[keep]
boxes = boxes[keep]
masks = masks[keep]
scores = scores[keep]
# Only keep the top cfg.max_num_detections highest scores across all classes
scores, idx = scores.sort(0, descending=True)
idx = idx[:cfg.max_num_detections]
scores = scores[:cfg.max_num_detections]
classes = classes[idx]
boxes = boxes[idx]
masks = masks[idx]
return boxes, masks, classes, scores
这里有个需要注意的地方,就是second_threshold=False,这个参数,来保证对得分很低的框进行滤除。(self.conf_thresh=0.05)对于正常的NMS来说,是应该做这一步的。但是在这里没有直接做这一布,而是在后续的处理中(layers/output_utils.py中postprocess函数),使用更高的阈值0.3进行滤除。
if score_threshold > 0:#score_threshold=0.3
keep = dets['score'] > score_threshold
for k in dets:
if k != 'proto':
dets[k] = dets[k][keep]
if dets['score'].size(0) == 0:
return [torch.Tensor()] * 4
上面的操作放在哪里做的都可以,效果也都一样。
在YOLACT框架中,Fast NMS相比Cython实现的传统NMS快11.8ms,并且精度只下降0.1MAP。
在MASK-RCNN框架中,Fast NMS相比CUDA实现的传统NMS快16.5ms,并且精度只下降0.3MAP。
实验结果:
Mask 质量
Mask 得分:
Fast NMS效果和效率的平衡:
Prototypes中输出通道数k的鲁棒性测试
其他baselines:
讨论:
定位失败,Localization Failure:
YOLACT的Map精度低,主要是由于分类的错误和边框定位不准造成。而分割的结果会受到定位不准的影响。
如上图2个相邻的卡车定位成了一个,导致分割也没有分割开。
泄露,Leakage
由于YOLACT采用整体分割,再组合的方式得到mask,再从中根据框切出mask的方式,所以对框周围的噪声比较敏感。而对于mask-rcnn这种有了框再分割的思想,对于外部噪声就不敏感,就不容易犯这样的错误。
对于一个框内部的相同类别的分割,容易都当成一个。如上图的人左下角的一部分,都会将另一个人的一部分身体当成这个人的。
总结:
- YOLACT,一个实时的检测+分割的one-stage框架。
- 将分割分为Prototype mask和Mask Coefficients两个部分的组合的思想非常好。