相信Yolo网络大家都很熟悉,从V1到V2引入了anchor(我理解中文翻译是先验框,也就是通过kmeans聚类算法,得到与数据集中bbox最接近的五个框,网络由V1的直接预测box坐标,变为预测bbox框相对于anchor的偏移量。一个cell有5个anchor)。V3自己也不是很了解,但据自己看的博客和资料来看,V3和V2实际上差不多,只不过anchor变为了9个,然后kmeans聚类的尺度变为/原图尺度。(v2的尺度为/fmap,这是个极易忽略的地方)(当然,也有很多这里没提到的改进,具体的变化,大家可以看看相关paper.)
网上有很多文章和博客介绍Yolo网络,在这里,我就不再次重述了。接下来总结下整个项目(攥写本文时,项目还没做完,所以本文的意义可能是为后面还未做的工作提供个工作思路):
实现对用户上传数据集的自动训练(Yolov2),并部署在AIOT芯片上。
(✔) 0.使用微软VoTT目标检测标定软件进行数据集标定。(看了网上不少目标检测数据集标定,都是用labelimg这款开源软件进行标定,从而生成xml文件。)为什么使用VoTT进行数据集标定?经过我的使用,它有如下好处:1.可直接生成含有图像数据的TFRecord文件(对使用TF框架的我,真的是福音。同时,这样的数据集打包方式,能够大大减少后期用户上传的数据集目录结构混乱的几率)。2.支持对视频的标定。3.使用非常方便,导致标定速度要比用labelimg快很多。
——
(✔)1.数据集增强。在本项目的背景下,不可能要求用户上传很大的数据集(比如几百MB,几GB的量级),所以数据增强对于提升模型的鲁棒性很重要。这里我才用的平移,增加噪声,旋转,剪切操作。
——
(✔)2.基于K-means聚类算法的anchor(先验框)计算。v2引入了anchor后,让网络收敛得更快了。(为什么会这样?根据我的理解加上生动的例子,就比如你想去城市中的某个建筑,在指路的时候,V1这个人给你提供的帮助是,告诉你这个建筑的经纬度坐标。而V2这个人告诉你的是这个建筑相对于你现在位置的偏移量。显然,V2提供的方式,相比于V1提供的方式对于人脑的计算量更小些。[这个例子可能有些不恰当QAQ])
——
(✔)3.loss函数。经过我对Yolo网络的理解,我发现,整个Yolo网络的核心就是它的loss函数,这个loss的计算无疑凝结着yolo网络最精髓的地方。(当然,它也是最需要时间去理解的地方)。相比于滑动窗口实现目标检测,相比于我之前通过opencv获取边缘形态学特征+目标分类而实现的多位手写数字识别(效果:https://www.bilibili.com/video/BV1WE411G7hx)。Yolo的处理速度是真的非常快了。而这个比较快的处理速度,来源于构建yolo的神经网络,这个网络的收敛,又来自于loss计算。所以大家在学习过程中,务必搞清楚loss函数。
——
(✔)4.部署到生产环境中(比如加errorlog,加守护进程等等)
ps:训练过程中还有很多其他操作,这个TODO主要整理比较关键的操作。
在Yolov2的loss计算中,每个cell有5个anchor,那么究竟该由哪个anchor来负责预测目标信息呢?答案是采用与GT(ground truth,也就是fmap尺度的box)IOU最大的anchor进行目标信息的预测。在计算时候,不考虑box的坐标位置。将GT和anchor移到同一原点进行计算,注意,参与计算的数值都是归一化后的。
计算过程图示如下:
代码如下:
简单说一下上述代码,首先获得所有anchor的宽度和高度数据,然后根据上面的计算图示,我们要计算A1和A2的面积,假设A1是长宽都比较小的box,那么a1的w和h的值,必定等于anchor和GT中最小的W和最小的H。再把这两个最小的w,h相乘,就得到了交集面积A1。那么A2的处理也是同理了。根据IOU=交集面积/并集面积。可以得到如下公式:IOU=A1/(A1(GT面积)+A2-A1(交集面积))。
注意,如上的A1并不能约去,因为他们的数学意义是不同的。一个为交集面积,一个为GT面积。在如上的情况下,二者的值是相等的。但是在如下的情况下,二者值并不相等:
Yolov2网络输出为一个fmap,fmap的深度为5*(1+4+classes(分类个数)),如下花了一张图,方便大家理解。(在这里要多说一句,某个深度表示什么,完全取决于loss函数计算,也就是你让某个滤波器取拟合什么数据,它所预测的就是什么数据)
PS:很多人对这个xywhc的含义不太了解,为了防止大家再次踩坑,用中文描述一下吧。xy是bbox中心相对于Cell左上角坐标的偏移量。wh是bbox宽度相对于anchor的偏移量。c是bbox的置信度。
很多刚刚接触yolo的同学可能会问,为啥我网络输出那么多框啊QAQ,真就满屏都是框。例如,下面的图片(当然,置信度不会都像下面这样高。楼主出现如下错误,主要是因为loss函数理解有些问题,导致不能很好的适配maixpy固件里封装好的nms算法。)
出现如上满屏是框的问题,主要是由于没有对网络输出卷积层做NMS。那么好奇的孩子可能会问,为什么不做NMS就会出现这样的问题呢?假设grid_w=10,grid_h=10,那么整个图像就有100个Cell,在yolov2中,每个cell有5个anchor,所以理论上网络会输出500个bbox。所以,假如没有NMS,出现如上情况是正常的。
那么NMS用来干啥的?,NMS,非极大值抑制。从它的名字就可以很清楚的知道它的作用。有两个关键词,第一个是“非极大值”,对于yolo来说,评估bbox是否可靠的的参数,就是它的置信度。所以这里的极大值指得是置信度。第二个关键词“抑制”,抑制是什么意思呢?表面上说,就是阻挡某个东西。
那么把这六个字连起来,NMS的作用就很明显了。对不是极大的置信度进行抑制。
很多人看到这里,可能会说,这不简单嘛?找出最大的置信度的框。能有这样的想法很正常,我当初也是这样想的。不过QAQ,实际上不是这样的。更准确的说法是,设定一个置信度阈值,找出置信度高于这个置信度阈值的框。
现在是不是感觉NMS其实也不是那么难理解,不过看到这里,才刚刚说完NMS算法的一半。另外一半同样也比较重要。仔细推敲上文的NMS方法,我们会发现个BUG,那就是,万一超过置信度阈值的框依旧很多呢?那不也是会导致满屏是框吗???(黑人问号表情包???)
那么我们还需要对这些高于置信度阈值的框做第二步操作,也就是合并操作。设置一个IOU阈值,假如两个框的交并比大于这个IOU阈值。我们就让它两合并。可以预见的是,这样合并着合并着,框就会变得很少了。下面是这个过程的简图:希望促进大家理解
经过对代码的优化,渐渐发现,pc端的nms实际效果很差(loss很小)。这明显是不符合逻辑的,于是开始检查nms的逻辑,又查阅了下资料。发现自己对nms的第二步操作理解有误。正确的第二步操作应该是假设上图的box1和box2的iou(交并比)大于iou阈值(此处为0.4),则认为他们mark的是同一个目标,则留下置信度最高的框,另外一个框去掉。(也就是假如box1的置信度为0.9,box2的置信度为0.8,iou大于0.4,则选择box1,丢掉box2)
首先,NMS是不考虑类别问题(原因:1.假如两个类别没有重叠,那么iou必定为0。2.假如两个类别有一定的重叠,还可以根据IOU阈值进行过滤)的。假如有n个框(在这里为了方便说明,则取A,B,C,D)。假设A框的置信度最高,则把A框添加到nms输出的list中。再将A分别与B,C,D进行IOU计算,假如IOU大于NMS所设置的IOU阈值,则认为他们标注的是用一类目标,则移除B/C/D。若IOU低于NMS所设置的IOU阈值,则再从低于阈值的框中选择置信度最高的框,重复上述过程。
可能大家看到这里还会有个疑问,那置信度阈值干啥的?在我实际看来,置信度阈值可以在一开始过滤掉很多不理想的框,降低后面进行基于IOU阈值过滤框的时间。(因为我是在k210上跑,pc上可能不明显。k210上,假如你将这个置信度阈值调低到0.01,基本就是0.x帧的水平)
NMS非极大值抑制算法就是干了这两件事情,是不是也没那么神秘。接下来要吹一波tensorflow了。tensorflow有个NMS的API,可以供大家直接调用QAQ。(太香啦)
以下为API的文档(地址在:https://tensorflow.google.cn/api_docs/python/tf/image/non_max_suppression?hl=en),有需要的朋友,可以自己去查阅。
由于用了Mobilenet,效果上自然和原论文里的darknet19无法比。loss最低0.5左右,在pc上nms后,效果如上。通过nncase量化后,跑在k210上的效果惨不忍睹。还在继续优化模型中。根据测试,loss收敛到0.01量级能有比较好的效果。
以下为PC端效果视频:
https://www.bilibili.com/video/BV1Za4y1v7Lp
(继续优化,如果各位前辈有好的优化建议,也欢迎私信我)
avg loss = 0.07,K210上跑的效果图(视频见:https://www.bilibili.com/video/BV1B64y1M73y),感觉框很多,并且置信度都很高。继续优化。经过和大佬的交流,发现可能是mobilenet不方便nncase量化(用mobilenet做基网络的同学要注意啦)。
还有个问题就是中间的Cell响应比较好,边缘的cell基本没有响应了。仔细想了下,由于数据增强后,我的四周pad操作,导致四周基本没啥数据。下一步将会下调数据增强的权重(楼主之前训练集全靠数据增强QAQ)
下一波的TODO就是:
1.降低数据增强的权重,增加数据集数量,再次用mb测试效果。
2.若1.的效果仍不乐观,则采用tiny-yolov2中的darknet为基网络。
今天是五一劳动节,祝大家劳动节快乐呀。劳动节,当然是劳动啦。最近把上面的两个TODO都试了下,首先是1.,本来想降低data augment的权重的,但是无意间发现,自己的augment操作中存在些不不利于网络收敛的操作。(比如cut out,直接把一个目标cut了3/4)。然后改了改,发现效果还不错。于是试了下多类别的目标检测,结果类别的prediction又炸了QAQ(心累中…),明天有时间继续研究。
其次是2.我发现用了tiny-yolov2中的基网络,效果还没有mb好。QAQ,果断直接放弃,应该是大佬之前大佬所说的tiny-yolov2效果好,是因为网络被大佬重新优化过。(但是我优化技术不行呀)
效果:https://www.bilibili.com/video/BV1Xt4y127Xz
下一波的TODO:
优化class的loss函数
经过几天的优化QAQ,本项目的主体程序搭建验证完毕。离上线还有一些代码需要写和debug。后天就开始考试了QAQ。争取在五月底左右上线吧。以下为loss曲线,如果大家在训练过程中,loss曲线不长这样,可能是因为:1.数据集标定错误(/加载错误),2.loss函数错误 这样两个错误引起的。(别问我为啥知道,因为这两个坑我都踩过QAQ)
从最后的结果来看QAQ,tiny yolov2效果好一些QAQ。,但是mb的网络小一些(alpha=0.25)