目录 Pytorch机器学习(八)—— YOLOV5中NMS非极大值抑制与DIOU-NMS等改进
在目标检测的预测阶段时,会输出许多候选的anchor box,其中有很多是明显重叠的预测边界框都围绕着同一个目标,这时候我就可以使用NMS来合并同一目标的类似边界框,或者说是保留这些边界框中最好的一个。
如果对IOU等知识不了解的可以看我上篇博客Pytorch机器学习(五)——目标检测中的损失函数(l2,IOU,GIOU,DIOU, CIOU)
我们先看一下NMS的直观理解,左图为两个ground truth的bbox,右图为我自己模拟网络输出的预测框。


而下图则是我使用Pytorch官方提供的NMS实现的非极大值抑制,可以看到经过NMS后预测框保留了效果最好的,去除了冗余的预测框。

下面来讲讲NMS算法的流程,其实也是十分简单的
一.从所有候选框中选取置信度最高的预测边界框B1作为基准,然后将所有与B1的IOU超过预定阈值的其他边界框移除。
(这时所有边界框中B1为置信度最高的边界框且没有和其太过相似的边界框——非极大值置信度的边界框被抑制了)
二.从所有候选框中选取置信度第二高的边界框B2作为一个基准,将所有与B2的IOU超过预定阈值的其他边界框移除。
三.重复上述操作,直到所有预测框都被当做基准——这时候没有一对边界框过于相似
在YOLOV5的源码当中,作者是直接调用了Pytorch官方的NMS的API
在general.py中的non_max_suppression函数中
"""
其中boxes为Nx4的tensor,N为框的数量,4则为x1 y1 x2 y2
socres为N维的tensor,表示每个框的置信度
iou_thres则为上面算法中的IOU阈值
返回值为一个去除了过于相似框后的,根据置信度降序排列的列表,我们就可以根据此列表输出预测框
"""
i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
为了便于后续其他NMS的改进,这里我们也自己写一个NMS算法,这里借鉴了沐神的代码b站链接,大家可以直接在YOLOV5中把上面的torchvision.ops.nms更改为下面的NMS函数
def NMS(boxes, scores, iou_thres, GIoU=False, DIoU=False, CIoU=False):
"""
:param boxes: (Tensor[N, 4])): are expected to be in ``(x1, y1, x2, y2)
:param scores: (Tensor[N]): scores for each one of the boxes
:param iou_thres: discards all overlapping boxes with IoU > iou_threshold
:return:keep (Tensor): int64 tensor with the indices
of the elements that have been kept
by NMS, sorted in decreasing order of scores
"""
# 按conf从大到小排序
B = torch.argsort(scores, dim=-1, descending=True)
keep = []
while B.numel() > 0:
# 取出置信度最高的
index = B[0]
keep.append(index)
if B.numel() == 1: break
# 计算iou,根据需求可选择GIOU,DIOU,CIOU
iou = bbox_iou(boxes[index, :], boxes[B[1:], :], GIoU=GIoU, DIoU=DIoU, CIoU=CIoU)
# 找到符合阈值的下标
inds = torch.nonzero(iou <= iou_thres).reshape(-1)
B = B[inds + 1]
return torch.tensor(keep)
这里的计算IOU的函数——bbox_iou则是直接引用了YOLOV5中的代码,其简洁的集成了对与GIOU,DIOU,CIOU的计算。
def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-9):
# Returns the IoU of box1 to box2. box1 is 4, box2 is nx4
box2 = box2.T
# Get the coordinates of bounding boxes
if x1y1x2y2: # x1, y1, x2, y2 = box1
b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3]
b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3]
else: # transform from xywh to xyxy
b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2
b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2
b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2
b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2
# Intersection area
inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \
(torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0)
# Union Area
w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps
w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps
union = w1 * h1 + w2 * h2 - inter + eps
iou = inter / union
if GIoU or DIoU or CIoU:
cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width
ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height
if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1
c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squared
rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 +
(b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center distance squared
if DIoU:
return iou - rho2 / c2 # DIoU
elif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47
v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2)
with torch.no_grad():
alpha = v / ((1 + eps) - iou + v)
return iou - (rho2 / c2 + v * alpha) # CIoU
else: # GIoU https://arxiv.org/pdf/1902.09630.pdf
c_area = cw * ch + eps # convex area
return iou - (c_area - union) / c_area # GIoU
else:
return iou # IoU
其实DIOU-NMS就是把我上面说的NMS算法中的IOU阈值改为DIOU,将NMS代码中的DIOU设置为True即可。
根据DIOU的论文,如果只是单纯的使用NMS,即是使用IOU作为阈值去筛掉其他预测框时,当两个物体过于接近时,很有可能另外一个物体的预测框就被滤除了。
就像下图中的摩托。使用DIOU-NMS可以一定程度上提升对于靠近的物体的检测。

网上还有一种soft-NMS的算法,其思想就是传统的NMS,如果只通过IOU值就将其他的框直接去掉,有可能会不妥,于是就引入了soft-NMS。
具体流程就是我们把NMS算法中去除其他边界框改成,修改其他边界框的置信度。
以下引一个博主的图

其中的f()函数,现在都是使用的高斯函数
si即为置信度,M为当前最大置信度的边界框,bi为其他边界框

网上对此的效果看法也是褒贬不一,我自己也没有试过,但从直觉来说,我个人觉得效果不会有很大的提升,如果感兴趣的可以自己试一试。
希望我没有误解“ducktyping”的含义,但从我读到的内容来看,这意味着我应该根据对象如何响应方法而不是它是什么类型/类来编写代码。代码如下:defconvert_hash(hash)ifhash.keys.all?{|k|k.is_a?(Integer)}returnhashelsifhash.keys.all?{|k|k.is_a?(Property)}new_hash={}hash.each_pair{|k,v|new_hash[k.id]=v}returnnew_hashelseraise"CustomattributekeysshouldbeID'sorPropertyo
文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk=Var(yt)Cov(yt,yt−k)其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞
是否有chrome开关来抑制“外部协议(protocol)请求”?我正在使用selenium-ruby-watirwebdriver自动化应用程序。我在网上搜索了绕过此窗口和对话的解决方案:http://productforums.google.com/forum/#!topic/chrome/K22hXwRy6zQ概述了我们如何手动执行此操作。但是对于Selenium-Chrome-Ruby,我需要通过可能设置一个chrome开关(chorme开关列表:=http://src.chromium.org/svn/trunk/src/chrome/common/chrome_swit
关于yolov5训练时参数workers和batch-size的理解yolov5训练命令workers和batch-size参数的理解两个参数的调优总结yolov5训练命令python.\train.py--datamy.yaml--workers8--batch-size32--epochs100yolov5的训练很简单,下载好仓库,装好依赖后,只需自定义一下data目录中的yaml文件就可以了。这里我使用自定义的my.yaml文件,里面就是定义数据集位置和训练种类数和名字。workers和batch-size参数的理解一般训练主要需要调整的参数是这两个:workers指数据装载时cpu所使
我正在用Ruby编写DSL来控制我正在处理的Arduino项目;巴尔迪诺。这是一只酒吧猴子,将由软件控制来提供饮料。Arduino通过串行端口接收命令,告诉Arduino要打开什么泵以及打开多长时间。它目前正在读取一个食谱(见下文)并将其打印出来。串行通信的代码以及我在下面提到的其他一些想法仍然需要改进。这是我的第一个DSL,我正在处理之前的示例,所以它的边缘非常粗糙。任何批评、代码改进(是否有任何关于RubyDSL最佳实践或习语的良好引用?)或任何一般性评论。我目前有DSL的粗略草稿,因此饮料配方如下所示(Githublink):desc"Simpleglassofwater"rec
我正在尝试使用ruby改进来应用Rails钩子(Hook)。我想避免猴子补丁。当猴子修补时它会这样工作ActiveRecord::Base.class_evaldoafter_finddo#dosomethingwithmy_methodenddefmy_method#somethingusefulendend我已经能够通过做这样的事情来拥有类方法:moduleActiveRecordRefinementsrefineActiveRecord::Base.singleton_classdodefmy_method#somethingcoolendendend但我无法运行钩子(Hoo
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Railsconsole,howtostopoutputofreturnvalue?考虑IRB中的这个session:>>forain1..5do?>putsa>>end12345=>1..5>>如何抑制输出=>1..5?如果我在Rails控制台session中执行此操作,这一点很重要:forpostinPost.find(:all)doifpost.submit_time>Time.nowputs"Corruptedpost#{post.id}isfromthefuture"endend我不希望所有的帖子最
我正在为一个项目设置一个rakefile,并且我已经定义了一些rakeTestTasks。我运行了一个简单的健全性测试,该测试执行assert_equal(1,2)只是为了检查输出,除了通常的失败输出之外,我还遇到了这样一团糟:rakeaborted!Commandfailedwithstatus(1):[/usr/bin/ruby-w-I"lib:.""/usr/lib/ruby/...]/usr/lib/ruby/1.9.1/rake.rb:993:in`blockinsh'/usr/lib/ruby/1.9.1/rake.rb:1008:in`call'/usr/lib/ruby
您可以使用优化您的类(class)moduleRefinedStringrefineStringdodefto_boolean(text)!!(text=~/^(true|t|yes|y|1)$/i)endendend但是如何细化模块方法呢?这:moduleRefinedMathrefineMathdodefPI22/7endendend引发:TypeError:错误的参数类型模块(预期类) 最佳答案 这段代码可以工作:moduleMathdefself.piputs'originalmethod'endendmoduleRefin
我想拆分一个抑制所有空字段的字符串命令:",1,2,,3,4,,".split(',')结果:["","1","2","","3","4",""]预期:["1","2","3","4"]如何做到这一点?编辑好的。只是总结所有发布的好问题。我想要的是split方法(或其他方法)不生成空字符串。看起来这是不可能的。因此,解决方案是两步过程:像往常一样拆分字符串,然后以某种方式从结果数组中删除空字符串。第二部分正是thisquestion(及其duplicate)所以我会用",1,2,,3,4,,".split(',').delete_if(&:empty?)NikitaRybak和user