深度学习之YOLO目标检测(3):yolov4&yolov5
一、YOLOv4
1.主干网络
从v3的darknet53到CSPdarknet53,v3层数深计算量大,反向传播慢。主要改进点就是把特征分为两部分,一部分卷积一部分直接连接,最后在融合。
2.激活函数
leakrelu改进为mish,mish更平滑更稳定
3.颈部结构
FPN上加了PANet。v3只有FPN,从上往下传递特征(有深到浅),v4又PAN和PANet,从上往下+从下往上双向融合,有利于小物体和密集物体的检测。
4.加入SPP模块
也就是空间金字塔池化层,v3中没有这个结构,v4在主干网络后加入SPP,用四种不同尺度池化,把局部特征+全局特征一次性融合。
5.损失函数
GIoU → CIoU,检测框的更加准确。考虑到了重叠面积+中心点距离+宽高比。
损失改进过程:
1)IOU损失
本来是IOU损失,但出现下面情况所以如何进一步对比这三种情况?

2)GIOU损失

但是如果我们的预选框在真实框内,那么上面损失又无法正常起到作用
3)DIOU

4)CIOU

V4也不需要考虑输入图片的大小,甚至不需要是32的倍数,加入金字塔池化层就能对图片进行缩放
二、YOLOv5
从 YOLOv3 之后,YOLO 系列的整体检测框架并未发生根本性改变,后续版本(YOLOv4、v5、v6、v7、v8 等)主要是在网络结构、特征融合方式、损失函数、训练策略等方面进行优化,从而提升模型的检测精度与速度。在上一篇文章中我们已经对 YOLOv3 的基本使用方法进行了介绍,而后续版本虽然在细节上有所改进,但核心思想、训练流程与推理逻辑基本一致,遇到的差异问题也可以借助大模型快速理解与解决。
现在我们需要学习的就是如何获取框架
github网站,这个是一个巨大的开放性开源网站
这里就以下载yolov5架构为例
我们在网站上搜索github,来到主页在红框搜索框中搜索yolo

会发现又yolov5,yolov7等,这里我们要下载yolov5所以点进第一个

不同版本的yolov5对一些库的版本要求不一样


关于库的版本要求我们可以在这些文件中找到一个requirements.txt文件,里面就是对当前版本库的要求

当我们把文件下载下来之后,可以直接运行requirements.txt文件,输入pip install +requirements.txt文件路径,就能一步把这些第三方库下载下来。
和v3差不多,也有不同,我们不需要修改classes,会自己识别,但依旧要模仿写coco文件,
模型训练过程中会产生新的东西,runs文件夹使我们每一次运行保存下来的信息,及时运行没有成功也会存下来,如果运行成功并训练好了,就只需要点开runs文件夹里面最后一个exp文件夹。里面会有weights文件夹,点开一个是best.pt和last.pt,就是我们训练过程中产生的最好模型和最后一个模型权重,可以在detect.py运行的时候使用。
同时还有result.png是我们训练过程中的数据曲线图。

train.py只需要修改一下参数

detect.py文件只需要传入两个参数source参数为0是打开摄像头进行检测,或者写图片路径,视频路径,对视频和图片进行目标检测。

使用的时候会遇到一些问题,这里我会把我遇到问题的解决方法展示,修改yolov5中的一些代码:
首先在utils文件夹打开utils.py文件
ctrl+f键搜索函数def build_targets,把这个函数的代码注释改为下面这个
def build_targets(p, targets, model):
# Build targets for compute_loss(), input targets(image,class,x,y,w,h)
# 1. 优先获取Detect层,兼容多卡训练
det = model.module.model[-1] if type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) \
else model.model[-1] # Detect() module
na, nt = det.na, targets.shape[0] # number of anchors, targets
tcls, tbox, indices, anch = [], [], [], []
# 2. 统一设备(核心修复:所有张量移到模型输出p的设备)
device = p[0].device
targets = targets.to(device) # 确保targets在模型设备上
gain = torch.ones(6, device=device) # normalized to gridspace gain
off = torch.tensor([[1, 0], [0, 1], [-1, 0], [0, -1]], device=device).float() # overlap offsets
at = torch.arange(na, device=device).view(na, 1).repeat(1, nt) # anchor tensor(指定设备)
g = 0.5 # offset
style = 'rect4'
for i in range(det.nl):
anchors = det.anchors[i].to(device) # 锚框移到目标设备
gain[2:] = torch.tensor(p[i].shape)[[3, 2, 3, 2]].to(device) # xyxy gain(指定设备)
# Match targets to anchors
a, t, offsets = [], targets * gain, 0
if nt:
# 修复:计算wh比例时确保张量设备一致
r = t[None, :, 4:6] / anchors[:, None] # wh ratio
# 修复:model.hyp可能不存在,先兼容(如果hyp在det里,改为det.hyp)
anchor_t = model.hyp['anchor_t'] if hasattr(model, 'hyp') else 4.0 # 默认阈值
j = torch.max(r, 1. / r).max(2)[0] < anchor_t # compare
# 核心修复:索引张量j移到目标设备,避免跨设备索引
j = j.to(device)
# 修复:at和t的设备统一,避免之前的RuntimeError
a = at[j].to(device)
t = t.repeat(na, 1, 1)[j].to(device) # filter
# overlaps
gxy = t[:, 2:4] # grid xy
z = torch.zeros_like(gxy, device=device) # 指定设备
if style == 'rect2':
j, k = ((gxy % 1. < g) & (gxy > 1.)).T
# 修复:索引张量j/k移到设备
j, k = j.to(device), k.to(device)
a = torch.cat((a, a[j], a[k]), 0)
t = torch.cat((t, t[j], t[k]), 0)
offsets = torch.cat((z, z[j] + off[0], z[k] + off[1]), 0) * g
elif style == 'rect4':
j, k = ((gxy % 1. < g) & (gxy > 1.)).T
l, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T
# 修复:索引张量j/k/l/m移到设备
j, k, l, m = j.to(device), k.to(device), l.to(device), m.to(device)
a = torch.cat((a, a[j], a[k], a[l], a[m]), 0)
t = torch.cat((t, t[j], t[k], t[l], t[m]), 0)
offsets = torch.cat((z, z[j] + off[0], z[k] + off[1],
z[l] + off[2], z[m] + off[3]), 0) * g
# Define
b, c = t[:, :2].long().T # image, class
gxy = t[:, 2:4] # grid xy
gwh = t[:, 4:6] # grid wh
gij = (gxy - offsets).long()
gi, gj = gij.T # grid xy indices
# 修复:限制索引范围,防止越界(新增)
gi = torch.clamp(gi, 0, p[i].shape[3] - 1)
gj = torch.clamp(gj, 0, p[i].shape[2] - 1)
# Append
indices.append((b, a, gj, gi)) # image, anchor, grid indices
tbox.append(torch.cat((gxy - gij, gwh), 1)) # box
anch.append(anchors[a]) # anchors(已移到设备)
tcls.append(c) # class
return tcls, tbox, indices, anch
在找到def output_to_target函数,全部注释改为下面这个
def output_to_target(output, width, height):
# Convert model output to target format [batch_id, class_id, x, y, w, h, conf]
targets = []
# 先将output移到CPU,避免CUDA张量转NumPy报错
output = [o.detach().cpu() for o in output]
for i, o in enumerate(output):
for *box, conf, cls in o.numpy():
# 核心修复:给1维box增加维度,变为2维 (1,4),适配xyxy2xywh
box = np.array(box).reshape(1, 4) # 从(4,) → (1,4)
box = xyxy2xywh(box) # xyxy to xywh(现在能正确处理)
box = box[0] # 还原为1维,方便后续计算 (4,)
# 归一化坐标
box[[0, 2]] /= width # normalize x, w
box[[1, 3]] /= height # normalize y, h
targets.append([i, cls, *box, conf])
# 最终转为NumPy数组
return np.array(targets)
在以下文件中用ctrl+f找到所有torch.load
![]()

一般情况下都在后面加上红框内容,一定不要漏掉



在yolo.py文件里添加with语句,不要忘记缩进

utils.py和datasets.py文件中查找np.int,把所有np.int改为int。


当然可能还会有其他问题,可以借助大模型解决。
(注意,这里的github网站进入会有些问题,这是因为github网站是境外网站,需要加速器或者翻墙,这个问题需要自行解决,用户注册可以用qq邮箱注册,如果一直注册不成功也是因为上面原因)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)