从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 共同点

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

5.2 不同点

方面ResNetYOLOv11
任务图像分类目标检测
输出一个类别标签多个边界框+类别
结构单一骨干网络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)应用场景
YOLO11n2.6M39.51.5移动端、实时检测
YOLO11s9.4M47.02.5边缘设备
YOLO11m20.1M51.54.5服务器端
YOLO11l25.3M53.46.0高精度需求
YOLO11x56.9M54.710.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 关键对比

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

10.3 学习路线

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

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

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

10.4 一句话总结

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


附录:关键术语表

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

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


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

更多推荐