本文介绍RPN是如何筛选正负样本。对应图中的红色部分。
假设输出特征图尺寸为13×19,在特征图的每个位置上会放置15个Anchor,则总共有13×19×15=3705个Anchor。但是一张图像上的Ground Truth是有限的,不能要求每一个Anchor都试图去回归Ground Truth,因此必须要有取舍。RPN从所有的Anchor中挑选出256个样本,其中正样本最多为128个,不够的用负样本补齐,并通过这256个样本进行训练。
首先计算图像中所有Ground Truth与Anchor的IoU值,将IoU>0.7的Anchor设为正样本,IoU<0.3的样本设为负样本,IoU位于两者之间的后续不进行处理。
def __call__(self, match_quality_matrix):
# 计算anchors与每个gtboxes匹配的iou最大值,并记录索引,
if match_quality_matrix.numel() == 0:
# empty targets or proposals not supported during training
if match_quality_matrix.shape[0] == 0:
raise ValueError(
"No ground-truth boxes available for one of the images "
"during training")
else:
raise ValueError(
"No proposal boxes available for one of the images "
"during training")
# match_quality_matrix is M (gt) x N (predicted)
# Max over gt elements (dim 0) to find best gt candidate for each prediction
# M x N 的每一列代表一个anchors与所有gt的匹配iou值
# matched_vals代表每列的最大值,即每个anchors与所有gt匹配的最大iou值
# matches对应最大值所在的索引
matched_vals, matches = match_quality_matrix.max(dim=0) #matched_vals为匹配到的iou值, matches为匹配到的gt, 如果二者IoU都为0, 选择第二个gt。
if self.allow_low_quality_matches: #是否允许将iou最大的anchor作为正样本
all_matches = matches.clone()
else:
all_matches = None
# Assign candidate matches with low quality to negative (unassigned) values
# 计算iou小于low_threshold的索引
below_low_threshold = matched_vals < self.low_threshold
# 计算iou在low_threshold与high_threshold之间的索引值
between_thresholds = (matched_vals >= self.low_threshold) & (
matched_vals < self.high_threshold
)
# iou小于low_threshold的matches索引置为-1
matches[below_low_threshold] = self.BELOW_LOW_THRESHOLD # -1
# iou在[low_threshold, high_threshold]之间的matches索引置为-2
matches[between_thresholds] = self.BETWEEN_THRESHOLDS # -2
if self.allow_low_quality_matches:
assert all_matches is not None
self.set_low_quality_matches_(matches, all_matches, match_quality_matrix)
# 将与gt间iou最大的anchor作为正样本,就算没有满足iou_thresold
return matches
虽然Anchor数量很多,但是与GT间IoU值大于0.7的却很少,甚至没有。但是没有正样本又没法进行训练,所以也将IoU值最大的Anchor设为正样本,无论该IoU值是否超过阈值。
接下来从正样本中随机挑选128个样本,不够的使用负样本补齐,样本总数为256个,进行训练。