YOLO (You Only Look Once)

1. 前言:什么是 YOLO?

在 YOLO 出现之前,目标检测主要依靠 R-CNN 等两阶段(Two-stage)算法。两阶段算法需要先找候选框,再进行分类,速度极慢。

2015年,Joseph Redmon 提出了 YOLO。它将目标检测看作一个单纯的回归问题(Regression)

  • You Only Look Once:模型只需看一眼图像,就能直接输出图中所有物体的类别和位置。
  • 实时性:这种“一阶段(One-stage)”的设计让它可以在 GPU 上跑出超过 45 FPS(甚至更高)的速度,真正实现了实时监控。

2. 网络概述:核心设计思想

YOLO 的核心逻辑是将图像划分为 S×SS \times S 的网格(Grid):

  1. 网格负责制:如果一个物体的中心落在某个网格内,该网格就负责检测这个物体。
  2. 预测框 (Bounding Box):每个网格预测 BB 个边界框及其置信度(Confidence)。
  3. 预测结果:模型最终输出一个张量,包含:
    • 位置信息(x,y,w,h)(x, y, w, h)
    • 置信度:该框内存在物体的概率及其准确度。
    • 类别概率CC 个类别的概率分布。

YOLO 的演进路线:

  • YOLOv1-v3:奠定了基础架构,引入了多尺度预测(FPN)和 Anchor Box。
  • YOLOv5:由 Ultralytics 推出,极其易用,成为工业界事实上的标准。
  • YOLOv8/v10/v11:引入了 Anchor-Free 设计和极致的轻量化结构。

3. 详细网络结构:PyTorch 实现

为了让你快速上手,我们实现一个符合 YOLO 风格的检测骨架(Backbone + Head)。YOLO 通常由 Backbone(提取特征)、Neck(特征融合)和 Head(输出预测)三部分组成。

3.1 核心组件:CBM 模块

YOLO 中最基础的单元是:Conv + BatchNorm + SiLU (或 LeakyReLU)

import torch
import torch.nn as nn

def autopad(k, p=None):  # 自动计算 padding
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]
    return p

class Conv(nn.Module):
    # 基础卷积单元: Conv + BN + SiLU
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

3.2 预测头:Detect Head

这是 YOLO 的“大脑”,负责将特征图转化为坐标和类别。

class Detect(nn.Module):
    def __init__(self, nc=80, ch=()):  # nc: 类别数, ch: 输入通道数
        super().__init__()
        self.nc = nc  
        self.nl = len(ch)  # 检测层的层数 (通常是3层,对应大中小物体)
        
        # 每一个检测层都有一个 Conv 输出 (nc + 5) * anchors_num
        # 5 代表: x, y, w, h, confidence
        self.m = nn.ModuleList(nn.Conv2d(x, (nc + 5), 1) for x in ch)

    def forward(self, x):
        res = []
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # 通过 1x1 卷积得到预测
            # 后续还需要进行 Reshape 和坐标转换逻辑
            res.append(x[i])
        return res

4. 损失函数 (Loss Function)

YOLO 的 Loss 比较复杂,通常由三部分加权组成: Loss=λcoordLossbox+λobjLossobj+λclassLossclsLoss = \lambda_{coord} Loss_{box} + \lambda_{obj} Loss_{obj} + \lambda_{class} Loss_{cls}

  1. 定位损失 (LossboxLoss_{box}):早期使用 MSE,现代 YOLO 使用 IoU Loss (如 CIoU, DIoU),因为 IoU 能更好地衡量两个框的重合度。
  2. 置信度损失 (LossobjLoss_{obj}):判断框内是否有物体,通常使用 BCE Loss(二元交叉熵)。
  3. 分类损失 (LossclsLoss_{cls}):判断物体是什么,同样使用 BCE Loss。

5. 推理逻辑:NMS (非极大值抑制)

YOLO 预测时,一个物体可能会被多个网格同时检测到,产生多个重叠的框。我们需要 NMS 来进行筛选:

  1. 按置信度从高到低排序。
  2. 保留置信度最高的框。
  3. 计算其余框与该框的 IoU,如果 IoU 超过阈值(如 0.5),则认为是重复检测,直接剔除。

6. 总结与建议

为什么选择 YOLO?

  • 部署友好:可以轻松转成 TensorRT, ONNX, NCNN,跑在手机或嵌入式设备(如 Jetson Nano)上。
  • 生态丰富:尤其是 YOLOv5 和 YOLOv8,官方提供了完整的训练、验证、导出工具链。

在你的 daomanpy.com 实战建议: 如果你是初学者,不要从头写 YOLO 的所有底层代码。建议先学会调用 ultralytics 库进行 迁移学习 (Transfer Learning)

from ultralytics import YOLO

# 加载一个预训练模型 (例如 YOLOv8 Nano 版)
model = YOLO('yolov8n.pt')

# 训练你自己的数据集 (只需准备好 data.yaml)
model.train(data='my_data.yaml', epochs=100, imgsz=640)

# 推理并保存结果
results = model('bus.jpg')
results[0].save()