实现yolo时踩过的坑!
终于把yolo v3框架写好了。支持多模型、多数据集、任意输出层数量、任意anchor数量、模型剪枝还适配k210.不要太好用~
这里记录一下我之前的实现的问题出在哪里。
错误地计算了ignore mask
从yolo v2开始就会计算正确box与预测box直接iou关系,如果iou score大于阈值,那么说明这个预测box是成功预测到了这个对象的,极大的提高了模型的recall。
但是我在开源的yolo v2中使用Boolean mask函数时忽略了一点,比如batch为16,那么输出label的尺寸为\([16,7,10,3,25]\),直接使用Boolean mask会得到正确box尺寸为\([?,4]\)。然后我把这个\([?,4]\)与预测出来的box\([16,7,10,3,4]\)计算iou score。
乍一看还以为没什么毛病,其实这里最大的毛病就是整个batch的正确box都与整个batch的预测box都做了iou score,如果这时候计算最优iou score,很有可能这个最优的预测box不属于这张图片!数据直接出现了混合,这就是根源问题。
在新的写的代码中,我用了map的方式处理每张图片,既提高了效率,又避免了错误。
题外话一句。。我现在都惊讶我之前的yolo v2为啥效果还行,很有可能误打误撞搞了个数据集mix的效果。。。
更新:
通过对比腾讯优图所开源的yolo3的代码,我发现这个ignore mask不但需要每张图像单独计算,还需要单一输出层与全局的目标进行计算,因为我用的是tf.keras,所以没办法在不使用hack的方式下传入整张图像的bbox数组,所以我在label中多加了一维,标记全局的对象位置.
以下代码为我目前的标签制作代码:
- 避免了
inf - 避免了对象重叠(原版yolo也没有考虑到这一点)
- 添加了全局的对象标记.
这些问题消除之后,我的yolo所计算出的loss与腾讯优图所开源的yolo完全一致.终于完美复现出yolo的效果了~
labels = [np.zeros((self.out_hw[i][0], self.out_hw[i][1], len(self.anchors[i]), |
anchor的尺度
前面我有个文章也写了,anchor的作用就是让预测wh与真实wh直接的比例接近与1,那么细细想来,anchor的尺度是对应图片尺度\([224,320]\)还是对应栅格的尺度,还是对应全局的0-1都没有什么关系,只不过anchor的尺度就代表做标签的时候label要转换的尺度。所以为了方便起见,直接把anchor尺度设置为全局的0-1就完事了,还减少运算量。
loss出现NaN
问题原因在于图片标签的width与height出现了0,导致log(0)=-inf的问题.
解决起来很简单,在制作标签的时候限制width与height范围即可.
label中的极端情况的考虑
bbox到达边界值
当bbox的中心点位于边界值最大值时,如下图所示. \[\begin{aligned}
index&=floor(x*w) \\
\because w&=3,x=1 \Rightarrow floor(1*3)=3
\end{aligned}
\]
但使用3进行索引就会报错,所以我们需要限制一下bbox的中心坐标不能大于等于\(1\).
+-------+-------+-------+
| | | |
| | | |
| | | +---------+
+-------+-------+--|----+ |
| | | | | |
| | | | center |
| | | | | |
+-------+-------+--|----+ |
| | | +---------+
| | | |
| | | |
+-------+-------+-------+
当两个目标的label相同时
如下图所示,当两个bbox真的非常靠近时,就会出现他们的label所在的位置都是相同的,就会出现label被覆盖的问题了.目前我将相同label时,后面的label分配给次优的anchor.
+---------------+-------+
| +---------+ | |
| +|--------+| | |
| || | || | |
+-||--------||----------+
| || | || | |
| || | || | |
| || | || | |
+-|+---------+----------+
| +---------+ | |
| | | |
| | | |
+-------+-------+-------+
数据增强
数据增强我使用gluoncv的方式,首先是图像crop与resize,使用的是ssd所提出的带iou约束的crop方式,resize之后结合imgaug库进行数据增强,效果不错。如果可以再进一步,可以使用谷歌提出的autoaugment策略。我这里暂时还没用mixup,gluoncv里面应该是有使用的。
IOULoss
推荐使用ciou loss,我测试之后map提高了4个点,效果相当不错。几个iou loss的实现方式我总结在这里