一、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邮箱注册,如果一直注册不成功也是因为上面原因)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐