从ResNet到YOLOv11:深度学习如何让计算机"看懂"图像?

从ResNet到YOLOv11:深度学习如何让计算机"看懂"图像?

上一篇我们学习了ResNet,理解了深度学习的基础。这一篇我们来看看ResNet的思想如何应用到实际任务中——以YOLOv11为例,了解目标检测的奥秘。


一、回顾ResNet:我们学到了什么?

1.1 ResNet的核心思想

传统网络:学习完整的映射 H(x)
ResNet:学习残差 F(x) = H(x) - x,输出 F(x) + x

关键创新:跳跃连接(Skip Connection)
作用:解决深层网络的梯度消失问题

1.2 ResNet的结构

输入 x
  │
  ├─────────────────┐
  ↓                 │
Conv → BN → ReLU    │
  ↓                 │
Conv → BN           │
  ↓                 │
  ⊕ ←───────────────┘
  ↓
ReLU
  ↓
输出

1.3 ResNet的影响

ResNet不仅是一个分类网络,它的思想影响了后续几乎所有深度学习模型:

网络 应用 借鉴ResNet的地方
FPN 特征金字塔 多尺度特征融合
U-Net 图像分割 跳跃连接
YOLO 目标检测 Backbone特征提取
PointNet 点云处理 残差思想

今天,我们来看看YOLOv11如何借鉴ResNet的思想。


二、什么是目标检测?(先搞懂任务)

2.1 图像分类 vs 目标检测

图像分类(ResNet做的)

输入:一张图像
输出:一个类别标签

例子:
输入:猫的照片
输出:这是"猫"(概率95%)

目标检测(YOLO做的)

输入:一张图像
输出:多个物体的位置和类别

例子:
输入:街景照片
输出:
- 这里有一辆车(位置:[100, 200, 300, 400],类别:汽车,概率98%)
- 这里有一个人(位置:[500, 100, 600, 500],类别:行人,概率95%)
- 这里有一辆车(位置:[700, 300, 900, 500],类别:汽车,概率92%)

2.2 目标检测的输出格式

每个检测结果包含:
1. 边界框(Bounding Box):[x1, y1, x2, y2]
   - (x1, y1):左上角坐标
   - (x2, y2):右下角坐标

2. 类别(Class):汽车、行人、自行车等

3. 置信度(Confidence):0-100%,表示有多确定

例子:
{
    "bbox": [100, 200, 300, 400],
    "class": "car",
    "confidence": 0.98
}

2.3 目标检测的应用

领域 应用 例子
自动驾驶 车辆行人检测 检测周围的车、人、交通标志
安防监控 异常行为检测 检测入侵、遗留物
医学影像 病灶检测 检测肿瘤、病变
工业质检 缺陷检测 检测产品缺陷
零售分析 客流统计 统计顾客数量

2.4 目标检测的挑战

1. 多尺度问题
   - 远处的车很小(20×20像素)
   - 近处的车很大(200×200像素)
   - 网络需要同时检测大小物体

2. 密集目标
   - 一群人站在一起
   - 物体相互遮挡
   - 需要准确区分每个物体

3. 实时性要求
   - 自动驾驶需要实时检测
   - 每秒处理30帧以上
   - 速度和精度要平衡

三、YOLO系列发展历史(了解背景)

3.1 目标检测的两大流派

两阶段检测器(Two-Stage)

第一阶段:生成候选区域(Region Proposals)
第二阶段:对候选区域分类和回归

代表:R-CNN系列(R-CNN → Fast R-CNN → Faster R-CNN)

优点:精度高
缺点:速度慢

单阶段检测器(One-Stage)

直接在图像上预测边界框和类别
不需要候选区域生成

代表:YOLO系列、SSD

优点:速度快
缺点:早期版本精度较低

3.2 YOLO的发展历程

YOLOv1 (2016)
    │
    ↓ 开创性的单阶段检测
YOLOv2 (2017)
    │
    ↓ 引入Anchor Box
YOLOv3 (2018)
    │
    ↓ 多尺度检测
YOLOv4 (2020)
    │
    ↓ 大量技巧集成
YOLOv5 (2020)
    │
    ↓ 工程化优化,PyTorch实现
YOLOv8 (2023)
    │
    ↓ 解耦头,Anchor-Free
YOLOv11 (2024)
    │
    ↓ 更高效的架构

3.3 为什么叫YOLO?

YOLO = You Only Look Once

传统方法:看很多次
- 滑动窗口:在图像上滑动几千次
- 候选区域:先找可能有物体的地方,再分类

YOLO:只看一次
- 把图像分成网格
- 每个网格直接预测
- 一次前向传播得到所有结果

类比:
传统方法:用放大镜一个区域一个区域扫描
YOLO:用眼睛扫一眼,同时看到所有东西

四、YOLOv11的网络架构(核心内容)

4.1 整体架构:Backbone-Neck-Head

输入图像
    │
    ↓
┌─────────────────────────────────────┐
│           Backbone(骨干网络)        │
│    提取图像特征                      │
│    类似ResNet的作用                  │
└─────────────────────────────────────┘
    │
    ↓
┌─────────────────────────────────────┐
│           Neck(颈部网络)           │
│    融合多尺度特征                    │
│    连接Backbone和Head               │
└─────────────────────────────────────┘
    │
    ↓
┌─────────────────────────────────────┐
│           Head(检测头)             │
│    预测边界框和类别                  │
│    输出最终检测结果                  │
└─────────────────────────────────────┘
    │
    ↓
检测结果:[边界框, 类别, 置信度]

4.2 Backbone:特征提取(借鉴ResNet)

Backbone的作用

输入:原始图像 [H, W, 3]
输出:多尺度特征图

特征图大小:
- P1: H/2 × W/2    (浅层,细节特征)
- P2: H/4 × W/4
- P3: H/8 × W/8    (中层)
- P4: H/16 × W/16
- P5: H/32 × W/32  (深层,语义特征)

YOLOv11的Backbone结构

输入图像 [640×640×3]
    │
    ↓
┌─────────────────┐
│ Conv 3×3, stride=2 │  → [320×320×16]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ Conv 3×3, stride=2 │  → [160×160×32]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ C3k2 Block      │  → [160×160×64]
│ (类似ResNet残差块) │
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ Conv 3×3, stride=2 │  → [80×80×128]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ C3k2 Block ×3   │  → [80×80×128]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ Conv 3×3, stride=2 │  → [40×40×256]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ C3k2 Block ×3   │  → [40×40×256]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ Conv 3×3, stride=2 │  → [20×20×512]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ C3k2 Block      │  → [20×20×512]
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ SPPF            │  → [20×20×512]
│ (空间金字塔池化)   │
└─────────────────┘
    │
    ↓
┌─────────────────┐
│ C2PSA           │  → [20×20×512]
│ (注意力机制)      │
└─────────────────┘

4.3 C3k2 Block:YOLOv11的核心模块

C3k2是什么?

C3 = Cross-Stage Partial (跨阶段部分连接)
k2 = 双kernel大小 (1×1和3×3)

类比ResNet:
- ResNet用残差块
- YOLOv11用C3k2块

两者都用跳跃连接解决梯度消失!

C3k2的结构

输入
  │
  ├──────────────────────────────┐
  ↓                              │
┌─────────────────┐              │
│ Conv 1×1        │  ← 分支1     │
│ (降低通道数)      │              │
└─────────────────┘              │
  │                              │
  ↓                              │
┌─────────────────┐              │
│ Conv 3×3        │              │
│ (特征提取)       │              │
└─────────────────┘              │
  │                              │
  ├──────────────────────────────┤
  │                              │
  ↓                              ↓
┌─────────────────┐              │
│ Conv 1×1        │              │
│ (降低通道数)      │              │
└─────────────────┘              │
  │                              │
  ↓                              │
┌─────────────────┐              │
│ Conv 3×3        │              │
│ (特征提取)       │              │
└─────────────────┘              │
  │                              │
  ↓                              │
Concat(拼接)←───────────────────┘
  │
  ↓
Conv 1×1(融合)
  │
  ↓
输出

为什么叫"跨阶段部分连接"?

输入分成两部分:
- 一部分直接传到输出(跳跃连接)
- 一部分经过卷积处理

最后拼接两部分

好处:
- 减少计算量(只处理一部分)
- 保持梯度流动(跳跃连接)

4.4 SPPF:空间金字塔池化

SPPF的作用

输入:20×20×512的特征图
输出:同样大小,但融合了不同尺度的信息

原理:
用不同大小的池化核提取特征
- 5×5 最大池化
- 9×9 最大池化
- 13×13 最大池化

然后拼接所有特征

代码实现

class SPPF(nn.Module):
    """空间金字塔池化 - 快速版本"""

    def __init__(self, c1, c2, k=5):
        """
        c1: 输入通道数
        c2: 输出通道数
        k: 池化核大小
        """
        super().__init__()
        c_ = c1 // 2  # 隐藏层通道数

        # 1×1卷积降低通道数
        self.cv1 = Conv(c1, c_, 1, 1)

        # 最大池化
        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)

        # 1×1卷积融合
        self.cv2 = Conv(c_ * 4, c2, 1, 1)

    def forward(self, x):
        x = self.cv1(x)           # [B, c_, H, W]

        y1 = self.m(x)            # [B, c_, H, W]
        y2 = self.m(y1)           # [B, c_, H, W]
        y3 = self.m(y2)           # [B, c_, H, W]

        # 拼接所有特征
        return self.cv2(torch.cat([x, y1, y2, y3], 1))

4.5 C2PSA:注意力机制

什么是注意力机制?

人类视觉:自动聚焦重要区域
- 看照片时,眼睛会自动看向人脸、文字等重要区域
- 忽略背景、天空等不重要的区域

注意力机制:让网络也学会"聚焦"
- 自动学习哪些特征重要
- 给重要特征更大的权重

C2PSA的结构

输入
  │
  ├──────────────────────────────┐
  ↓                              │
┌─────────────────┐              │
│ Conv 1×1        │              │
└─────────────────┘              │
  │                              │
  ↓                              │
┌─────────────────┐              │
│ PSA注意力模块     │              │
│ (学习关注哪里)    │              │
└─────────────────┘              │
  │                              │
  ↓                              │
┌─────────────────┐              │
│ Conv 1×1        │              │
└─────────────────┘              │
  │                              │
  ↓                              │
Concat(拼接)←───────────────────┘
  │
  ↓
Conv 1×1(融合)
  │
  ↓
输出

PSA注意力模块

class PSA(nn.Module):
    """位置敏感注意力模块"""

    def __init__(self, c1, c2):
        super().__init__()

        # 通道注意力
        self.channel_attn = nn.Sequential(
            nn.AdaptiveAvgPool2d(1),
            nn.Conv2d(c1, c1 // 16, 1),
            nn.ReLU(),
            nn.Conv2d(c1 // 16, c1, 1),
            nn.Sigmoid()
        )

        # 空间注意力
        self.spatial_attn = nn.Sequential(
            nn.Conv2d(2, 1, 7, padding=3),
            nn.Sigmoid()
        )

    def forward(self, x):
        # 通道注意力
        channel_attn = self.channel_attn(x)
        x = x * channel_attn

        # 空间注意力
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        spatial_attn = self.spatial_attn(torch.cat([avg_out, max_out], dim=1))
        x = x * spatial_attn

        return x

4.6 Neck:特征融合(FPN+PAN)

为什么需要特征融合?

浅层特征(P1, P2):
- 分辨率高,细节丰富
- 适合检测小物体
- 但语义信息少

深层特征(P4, P5):
- 分辨率低,细节少
- 语义信息丰富
- 适合检测大物体

问题:如何同时检测大小物体?
答案:融合多尺度特征!

FPN(特征金字塔)

P5 (20×20) ──────────────────→ 输出5
  │
  ↓ 上采样
  │
P4 (40×40) ──────────────────→ 输出4
  │
  ↓ 上采样
  │
P3 (80×80) ──────────────────→ 输出3
  │
  ↓ 上采样
  │
P2 (160×160) ─────────────────→ 输出2

FPN:从上到下,把深层语义信息传递给浅层

PAN(路径聚合网络)

输出2 ←──────────────────── P2 (160×160)
  │
  ↓ 下采样
  │
输出3 ←──────────────────── P3 (80×80)
  │
  ↓ 下采样
  │
输出4 ←──────────────────── P4 (40×40)
  │
  ↓ 下采样
  │
输出5 ←──────────────────── P5 (20×20)

PAN:从下到上,把浅层位置信息传递给深层

YOLOv11的Neck:FPN + PAN

P5 ─────────────────────────────────────→ 输出5
  │                                     ↑
  ↓ 上采样                              │
  │                                     │
P4 ─────────────────────────────────────→ 输出4
  │          ↑                          ↑
  ↓ 上采样   │ 下采样                   │
  │          │                          │
P3 ─────────────────────────────────────→ 输出3
  │          ↑                          ↑
  ↓ 上采样   │ 下采样                   │
  │          │                          │
P2 ─────────────────────────────────────→ 输出2

双向融合:既传递语义信息,又传递位置信息

4.7 Head:检测头

YOLOv11的解耦头

特征图
  │
  ├─────────────────┐
  ↓                 │
┌─────────────┐     │
│ 分类分支      │     │
│ (预测类别)    │     │
└─────────────┘     │
                    │
  ↓                 │
┌─────────────┐     │
│ 回归分支      │     │
│ (预测边界框)  │     │
└─────────────┘     │
                    │
  ↓                 ↓
  ┌─────────────────┐
  │   检测结果       │
  │ [类别, 边界框]   │
  └─────────────────┘

为什么用解耦头?

耦合头(早期YOLO):
- 一个分支同时预测类别和边界框
- 两个任务互相干扰

解耦头(YOLOv11):
- 分类和回归分开
- 各自独立优化
- 精度更高

五、ResNet与YOLOv11的对比(核心内容)

5.1 共同点

方面 ResNet YOLOv11 共同点
残差连接 都用跳跃连接解决梯度消失
BatchNorm 都用BN加速训练
ReLU激活 都用ReLU引入非线性
深层网络 都可以训练很深的网络
特征提取 都是骨干网络的一部分

5.2 不同点

方面 ResNet YOLOv11
任务 图像分类 目标检测
输出 一个类别标签 多个边界框+类别
结构 单一骨干网络 Backbone+Neck+Head
多尺度 不需要 必须处理多尺度
实时性 不要求 必须实时
应用场景 通用分类 自动驾驶、安防等

5.3 架构对比图

ResNet:
输入图像 → Backbone → 全局池化 → 分类头 → 类别
           [ResNet]

YOLOv11:
输入图像 → Backbone → Neck → Head → 检测结果
           [C3k2]    [FPN   [解耦头]
                      +PAN]

5.4 详细对比:残差块 vs C3k2块

ResNet的残差块

输入 x
  │
  ├─────────────────┐
  ↓                 │
Conv 3×3 → BN → ReLU
  ↓                 │
Conv 3×3 → BN       │
  ↓                 │
  ⊕ ←───────────────┘
  ↓
ReLU
  ↓
输出

特点:
- 两层卷积
- 跳跃连接
- 简单有效

YOLOv11的C3k2块

输入
  │
  ├──────────────────────────────┐
  ↓                              │
Conv 1×1 → Conv 3×3              │
  │                              │
  ├──────────────────────────────┤
  │                              │
  ↓                              │
Conv 1×1 → Conv 3×3              │
  │                              │
  ↓                              │
Concat ←─────────────────────────┘
  │
  ↓
Conv 1×1
  │
  ↓
输出

特点:
- 多分支结构
- 跨阶段连接
- 更高效的特征复用

5.5 共同的设计哲学

1. 层次化特征提取

ResNet:
浅层 → 中层 → 深层
(边缘)  (形状)  (物体)

YOLOv11 Backbone:
浅层 → 中层 → 深层
(细节)  (中层)  (语义)

2. 跳跃连接解决梯度消失

# ResNet
output = conv(x) + x  # 残差连接

# YOLOv11 C3k2
output = concat(branch1, branch2)  # 跨阶段连接

# 本质相同:让梯度有"高速公路"

3. 渐进式特征抽象

第1层:学习简单特征(边缘、纹理)
第2层:学习中等特征(形状、图案)
第3层:学习复杂特征(物体部件)
第4层:学习高级语义(整个物体)

六、代码实现对比

6.1 ResNet的残差块

class ResidualBlock(nn.Module):
    """ResNet的残差块"""

    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()

        # 主路径
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, 1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)

        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, 1, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)

        # 跳跃连接
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        # 主路径
        out = self.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))

        # 跳跃连接
        out += self.shortcut(x)
        out = self.relu(out)

        return out

6.2 YOLOv11的C3k2块

class C3k2(nn.Module):
    """YOLOv11的C3k2块"""

    def __init__(self, c1, c2, n=1, shortcut=True):
        """
        c1: 输入通道数
        c2: 输出通道数
        n: 重复次数
        shortcut: 是否使用跳跃连接
        """
        super().__init__()
        c_ = c2 // 2  # 隐藏层通道数

        # 两个分支
        self.cv1 = Conv(c1, c_, 1, 1)  # 分支1:1×1卷积
        self.cv2 = Conv(c1, c_, 1, 1)  # 分支2:1×1卷积

        # 分支2的后续处理
        self.m = nn.Sequential(
            *[Bottleneck(c_, c_, shortcut) for _ in range(n)]
        )

        # 融合
        self.cv3 = Conv(c2, c2, 1, 1)

    def forward(self, x):
        # 分支1:直接传递
        y1 = self.cv1(x)

        # 分支2:经过Bottleneck
        y2 = self.m(self.cv2(x))

        # 拼接并融合
        return self.cv3(torch.cat([y1, y2], 1))

class Bottleneck(nn.Module):
    """Bottleneck块"""

    def __init__(self, c1, c2, shortcut=True):
        super().__init__()

        self.cv1 = Conv(c1, c2, 1, 1)  # 1×1卷积
        self.cv2 = Conv(c2, c2, 3, 1)  # 3×3卷积

        self.shortcut = shortcut and c1 == c2

    def forward(self, x):
        if self.shortcut:
            return x + self.cv2(self.cv1(x))  # 残差连接!
        else:
            return self.cv2(self.cv1(x))

关键发现:YOLOv11的Bottleneck也用了残差连接!

6.3 YOLOv11的Backbone

class YOLOv11Backbone(nn.Module):
    """YOLOv11的Backbone"""

    def __init__(self):
        super().__init__()

        # 初始卷积层
        self.stem = nn.Sequential(
            Conv(3, 16, 3, 2),    # 640→320
            Conv(16, 32, 3, 2),   # 320→160
            C3k2(32, 64),         # 160×160×64
        )

        # 4个阶段
        self.stage1 = nn.Sequential(
            Conv(64, 128, 3, 2),  # 160→80
            C3k2(128, 128, n=3),  # 80×80×128
        )

        self.stage2 = nn.Sequential(
            Conv(128, 256, 3, 2), # 80→40
            C3k2(256, 256, n=3),  # 40×40×256
        )

        self.stage3 = nn.Sequential(
            Conv(256, 512, 3, 2), # 40→20
            C3k2(512, 512),       # 20×20×512
        )

        # SPPF + 注意力
        self.sppf = SPPF(512, 512)
        self.c2psa = C2PSA(512, 512)

    def forward(self, x):
        # 初始层
        x = self.stem(x)         # [B, 64, 160, 160]

        # 4个阶段
        p2 = self.stage1(x)      # [B, 128, 80, 80]
        p3 = self.stage2(p2)     # [B, 256, 40, 40]
        p4 = self.stage3(p3)     # [B, 512, 20, 20]

        # SPPF + 注意力
        p5 = self.c2psa(self.sppf(p4))  # [B, 512, 20, 20]

        return [p2, p3, p4, p5]  # 返回多尺度特征

6.4 YOLOv11的完整网络

class YOLOv11(nn.Module):
    """完整的YOLOv11网络"""

    def __init__(self, num_classes=80):
        super().__init__()

        # Backbone
        self.backbone = YOLOv11Backbone()

        # Neck (FPN + PAN)
        self.neck = YOLOv11Neck()

        # Head (解耦头)
        self.head = YOLOv11Head(num_classes)

    def forward(self, x):
        # Backbone提取特征
        features = self.backbone(x)  # [p2, p3, p4, p5]

        # Neck融合特征
        fused_features = self.neck(features)  # [f2, f3, f4, f5]

        # Head预测
        predictions = self.head(fused_features)

        return predictions

七、YOLOv11的创新点

7.1 C3k2 vs C2f(YOLOv8)

YOLOv8的C2f:
输入 → Conv 1×1 → 分支1 + 分支2 → Conv 1×1 → 输出

YOLOv11的C3k2:
输入 → Conv 1×1 → 分支1 + 分支2 → Conv 1×1 → 输出
                    ↑
                    使用双kernel(1×1和3×3)

改进:
- 更高效的特征提取
- 参数量减少22%
- 精度保持或提升

7.2 C2PSA注意力机制

YOLOv8:没有注意力
YOLOv11:加入了C2PSA

作用:
- 自动学习重要特征
- 抑制不重要的特征
- 提高检测精度

7.3 Anchor-Free设计

传统YOLO(YOLOv3及以前):
- 预定义Anchor Box(先验框)
- 预测相对于Anchor的偏移

YOLOv11:
- 不需要预定义Anchor
- 直接预测边界框坐标
- 更简单,更灵活

Anchor-Free的优势

# Anchor-Based(传统)
# 需要预定义:
anchors = [[10,13], [16,30], [33,23],  # 小物体
           [30,61], [62,45], [59,119], # 中物体
           [116,90], [156,198], [373,326]] # 大物体

# Anchor-Free(YOLOv11)
# 直接预测:
output = {
    "bbox": [x1, y1, x2, y2],  # 直接预测坐标
    "class": [0.1, 0.9, ...],   # 类别概率
}

八、实战:YOLOv11检测示例

8.1 使用Ultralytics库

from ultralytics import YOLO

# 加载预训练模型
model = YOLO("yolo11n.pt")  # nano版本,最快

# 或者更大的模型
# model = YOLO("yolo11s.pt")  # small
# model = YOLO("yolo11m.pt")  # medium
# model = YOLO("yolo11l.pt")  # large
# model = YOLO("yolo11x.pt")  # extra large

# 推理
results = model("image.jpg")

# 解析结果
for result in results:
    boxes = result.boxes  # 边界框
    for box in boxes:
        # 边界框坐标
        x1, y1, x2, y2 = box.xyxy[0].tolist()

        # 类别和置信度
        cls = int(box.cls[0])
        conf = float(box.conf[0])

        # 类别名称
        class_name = model.names[cls]

        print(f"{class_name}: {conf:.2f} at [{x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}]")

8.2 模型变体对比

模型 参数量 mAP50-95 速度(ms) 应用场景
YOLO11n 2.6M 39.5 1.5 移动端、实时检测
YOLO11s 9.4M 47.0 2.5 边缘设备
YOLO11m 20.1M 51.5 4.5 服务器端
YOLO11l 25.3M 53.4 6.0 高精度需求
YOLO11x 56.9M 54.7 10.0 极致精度

8.3 训练自定义数据集

from ultralytics import YOLO

# 加载模型
model = YOLO("yolo11n.pt")

# 训练
results = model.train(
    data="coco128.yaml",  # 数据集配置
    epochs=100,           # 训练轮数
    imgsz=640,            # 图像大小
    batch=16,             # 批次大小
    device=0,             # GPU编号
)

# 验证
metrics = model.val()
print(f"mAP50-95: {metrics.box.map:.4f}")

8.4 导出和部署

from ultralytics import YOLO

# 加载模型
model = YOLO("yolo11n.pt")

# 导出为ONNX格式
model.export(format="onnx")

# 导出为TensorRT格式(NVIDIA GPU)
model.export(format="engine")

# 导出为CoreML格式(Apple设备)
model.export(format="coreml")

九、YOLOv11在3D占用感知中的应用

9.1 从2D检测到3D占用

YOLOv11:2D目标检测
- 输入:单张图像
- 输出:2D边界框

3D占用感知:
- 输入:多视角图像或点云
- 输出:3D占用网格

联系:
YOLOv11的Backbone可以用于提取2D特征
然后将2D特征提升到3D空间

9.2 结合ResNet和YOLOv11

class OccupancyNetwork(nn.Module):
    """基于YOLOv11的3D占用预测网络"""

    def __init__(self, num_classes=16):
        super().__init__()

        # 使用YOLOv11的Backbone提取2D特征
        self.backbone = YOLOv11Backbone()

        # 2D到3D的提升
        self.lift = nn.Sequential(
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
        )

        # 3D卷积处理
        self.conv3d = nn.Sequential(
            nn.Conv3d(128, 64, 3, padding=1),
            nn.BatchNorm3d(64),
            nn.ReLU(),
        )

        # 占用预测头
        self.occ_head = nn.Sequential(
            nn.Conv3d(64, 32, 3, padding=1),
            nn.BatchNorm3d(32),
            nn.ReLU(),
            nn.Conv3d(32, num_classes, 1),
        )

    def forward(self, images):
        """
        images: [B, N, 3, H, W] - 多视角图像
        返回: [B, X, Y, Z, num_classes] - 3D占用预测
        """
        B, N = images.shape[:2]

        # 提取2D特征
        features = []
        for i in range(N):
            feat = self.backbone(images[:, i])  # [B, C, H, W]
            features.append(feat)

        # 2D到3D提升(简化示意)
        # 实际会用更复杂的几何变换
        features_3d = self.lift_and_splat(features)  # [B, C, X, Y, Z]

        # 3D卷积
        features_3d = self.conv3d(features_3d)

        # 占用预测
        occupancy = self.occ_head(features_3d)  # [B, num_classes, X, Y, Z]

        return occupancy

十、总结:ResNet与YOLOv11的关系

10.1 核心联系

ResNet(2015)
    │
    ↓ 解决深层网络训练问题
    │
    ├──→ 成为各种网络的骨干
    │
    ├──→ YOLOv11的Backbone借鉴了ResNet的思想
    │
    └──→ 残差连接成为标准组件

10.2 关键对比

方面 ResNet YOLOv11
核心创新 残差连接 高效特征融合
解决的问题 梯度消失 多尺度检测
任务 分类 检测
结构 单一骨干 Backbone+Neck+Head
实时性 不要求 必须实时
应用场景 通用 自动驾驶等

10.3 学习路线

第一篇:ResNet
    ↓ 学习深度学习基础
    ↓ 理解残差连接
    ↓ 掌握特征提取

第二篇:YOLOv11(本文)
    ↓ 了解目标检测任务
    ↓ 学习多尺度特征融合
    ↓ 理解实时检测设计

第三篇:PointNet
    ↓ 处理3D点云数据
    ↓ 理解无序数据处理
    ↓ 为3D感知打基础

10.4 一句话总结

ResNet解决了"如何训练深层网络"的问题,YOLOv11解决了"如何实时检测多个物体"的问题。两者共同推动了深度学习在自动驾驶中的应用。


附录:关键术语表

术语 英文 含义
目标检测 Object Detection 定位和识别图像中的物体
边界框 Bounding Box 包围物体的矩形框
锚框 Anchor Box 预定义的参考框
特征金字塔 Feature Pyramid 多尺度特征表示
跨阶段连接 Cross-Stage Partial YOLOv11的特征复用方式
注意力机制 Attention Mechanism 学习关注重要特征
解耦头 Decoupled Head 分类和回归分开的检测头

下期预告:《从PointNet入门点云深度学习——如何处理无序的3D数据?》


作者:[你的公众号名称]
首发:微信公众号
转载请注明出处

posted @ 2026-06-11 20:49  探物AI  阅读(35)  评论(0)    收藏  举报