AI

计算机视觉

动手深度学习

Posted by LXG on May 7, 2026

动手深度学习

动手深度学习-github

图像增广 Image Augmentation

通过“随机改造”训练图片,让模型学会真正理解目标,而不是死记硬背训练集。

翻转和裁减



import torch
import torchvision
from torch import nn
from d2l import torch as d2l

d2l.set_figsize()
# 打开图像
# 这里我们使用了d2l库中的Image类来打开图像文件。你需要确保在当前目录下有一个名为'img'的文件夹,并且里面有一张名为'cat1.jpg'的图片。
# 打开图像后,我们可以使用d2l.plt.imshow()函数来显示图像,并使用d2l.plt.show()函数来展示图像窗口。
img = d2l.Image.open('./img/cat1.jpg')
# d2l.plt.imshow(img);
# d2l.plt.show()

# 定义一个函数来展示增强后的图像
# 参数img是输入图像,aug是图像增强方法,num_rows和num_cols分别指定展示的行数和列数,scale指定图像的缩放比例。
def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):
    Y = [aug(img) for _ in range(num_rows * num_cols)]
    d2l.show_images(Y, num_rows, num_cols, scale=scale)

# 下面我们将使用不同的图像增强方法来处理图像,并展示增强后的结果。
# 首先,我们使用torchvision.transforms.RandomHorizontalFlip()方法来随机水平翻转图像。
# 这个方法会以一定的概率(默认是0.5)将图像水平翻转。
apply(img, torchvision.transforms.RandomHorizontalFlip())

# 接下来,我们使用torchvision.transforms.RandomVerticalFlip()方法来随机垂直翻转图像。
# 这个方法会以一定的概率(默认是0.5)将图像垂直翻转。
apply(img, torchvision.transforms.RandomVerticalFlip())

# 最后,我们使用torchvision.transforms.RandomResizedCrop()方法来随机裁剪图像并调整大小。
# 这个方法会随机裁剪图像的一部分,并将其调整为指定的大小。
# 在这个例子中,我们将图像裁剪为200x200像素,并且裁剪的区域的面积占原图的比例在0.1到1之间,宽高比在0.5到2之间。
shape_aug = torchvision.transforms.RandomResizedCrop(
    (200, 200), scale=(0.1, 1), ratio=(0.5, 2))
apply(img, shape_aug)

d2l.plt.show()

图像裁减后的显示

image_aug_1

改变颜色


# 最后,我们使用torchvision.transforms.ColorJitter()方法来随机改变图像的亮度、对比度、饱和度和色调。
# 在这个例子中,我们将亮度调整范围设置为0.5,对比度、饱和度和色调的调整范围设置为0。
# 8张图像中,每张图像的亮度都会在原图的基础上随机调整,调整范围为原图亮度的0.5倍到1.5倍之间。
apply(img, torchvision.transforms.ColorJitter(
    brightness=0.5, contrast=0, saturation=0, hue=0))

# 8张图像中,每张图像的色调都会在原图的基础上随机调整,调整范围为原图色调的-0.5到0.5之间。
apply(img, torchvision.transforms.ColorJitter(
    brightness=0, contrast=0, saturation=0, hue=0.5))

# 8张图像中,每张图像的亮度、对比度、饱和度和色调都会在原图的基础上随机调整,调整范围为原图的0.5倍到1.5倍之间。
color_aug = torchvision.transforms.ColorJitter(
    brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
apply(img, color_aug)

结合多种图像增广方法


# 最后,我们将随机水平翻转、随机裁剪和颜色抖动这三种图像增强方法组合在一起,来同时增强图像的形状和颜色。
augs = torchvision.transforms.Compose([
    torchvision.transforms.RandomHorizontalFlip(), color_aug, shape_aug])
apply(img, augs)

使用图像增广训练

数据准备


# 下面我们将使用CIFAR-10数据集来展示图像增强的效果。
# CIFAR-10数据集包含了10个类别的60000张32x32像素的彩色图像,其中50000张用于训练,10000张用于测试。
# 这里我们使用torchvision.datasets.CIFAR10()函数来加载CIFAR-10数据集,并将train参数设置为True来获取训练集。
all_images = torchvision.datasets.CIFAR10(train=True, root="../data",
                                          download=False)
# 加载完成后,我们可以使用d2l.show_images()函数来展示前32张图像,并将它们排列成4行8列的格式。
d2l.show_images([all_images[i][0] for i in range(32)], 4, 8, scale=0.8);

d2l.plt.show()

image_aug_2

训练代码


# 定义一个函数来加载CIFAR-10数据集,并应用指定的图像增强方法。
def load_cifar10(is_train, augs, batch_size):
    dataset = torchvision.datasets.CIFAR10(root="../data", train=is_train,
                                           transform=augs, download=False)
    dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
                    shuffle=is_train, num_workers=d2l.get_dataloader_workers())
    return dataloader

#@save
def train_batch_ch13(net, X, y, loss, trainer, devices):
    """
    用多GPU进行小批量训练

    参数:
        net: 神经网络模型(可能已封装为DataParallel以支持多GPU)
        X: 输入数据(可以是Tensor或Tensor列表,BERT等模型可能为列表)
        y: 标签
        loss: 损失函数
        trainer: 优化器
        devices: 设备列表(如[GPU0, GPU1,...])
    返回:
        train_loss_sum: 当前批次的总损失
        train_acc_sum: 当前批次的总准确率
    """
    # 如果输入是列表(如BERT等模型),将每个输入都搬到主设备(devices[0])
    if isinstance(X, list):
        # 微调BERT等模型时,输入通常为多个Tensor
        X = [x.to(devices[0]) for x in X]
    else:
        # 普通情况,直接将输入搬到主设备
        X = X.to(devices[0])
    # 标签也搬到主设备
    y = y.to(devices[0])
    # 设置模型为训练模式(启用Dropout/BN等)
    net.train()
    # 梯度清零,防止梯度累加
    trainer.zero_grad()
    # 前向传播,获得预测结果
    pred = net(X)
    # 计算损失(返回每个样本的损失)
    l = loss(pred, y)
    # 反向传播,计算梯度(对所有样本损失求和后反传)
    l.sum().backward()
    # 优化器更新参数
    trainer.step()
    # 统计本批次总损失
    train_loss_sum = l.sum()
    # 统计本批次准确率
    train_acc_sum = d2l.accuracy(pred, y)
    return train_loss_sum, train_acc_sum

#@save
def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
               devices=d2l.try_all_gpus()):
    """用多GPU进行模型训练"""
    timer, num_batches = d2l.Timer(), len(train_iter)
    animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],
                            legend=['train loss', 'train acc', 'test acc'])
    # 将模型封装为DataParallel以支持多GPU训练,并将模型搬到主设备
    net = nn.DataParallel(net, device_ids=devices).to(devices[0])
    # 训练多个epoch
    for epoch in range(num_epochs):
        # 4个维度:储存训练损失,训练准确度,实例数,特点数
        metric = d2l.Accumulator(4)
        for i, (features, labels) in enumerate(train_iter):
            timer.start()
            l, acc = train_batch_ch13(
                net, features, labels, loss, trainer, devices)
            metric.add(l, acc, labels.shape[0], labels.numel())
            timer.stop()
            if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
                animator.add(epoch + (i + 1) / num_batches,
                             (metric[0] / metric[2], metric[1] / metric[3],
                              None))
        test_acc = d2l.evaluate_accuracy_gpu(net, test_iter)
        animator.add(epoch + 1, (None, None, test_acc))
    print(f'loss {metric[0] / metric[2]:.3f}, train acc '
          f'{metric[1] / metric[3]:.3f}, test acc {test_acc:.3f}')
    print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec on '
          f'{str(devices)}')

batch_size, devices, net = 256, d2l.try_all_gpus(), d2l.resnet18(10, 3)

# 初始化模型参数
def init_weights(m):
    if type(m) in [nn.Linear, nn.Conv2d]:
        nn.init.xavier_uniform_(m.weight)

net.apply(init_weights)

def train_with_data_aug(train_augs, test_augs, net, lr=0.001):
    train_iter = load_cifar10(True, train_augs, batch_size)
    test_iter = load_cifar10(False, test_augs, batch_size)
    loss = nn.CrossEntropyLoss(reduction="none")
    trainer = torch.optim.Adam(net.parameters(), lr=lr)
    train_ch13(net, train_iter, test_iter, loss, trainer, 10, devices)

train_with_data_aug(train_augs, test_augs, net)

训练过程


原始图像
    ↓
数据增强(augmentation)
    ↓
Dataset
    ↓
DataLoader
    ↓
ResNet18
    ↓
forward
    ↓
loss
    ↓
backward
    ↓
Adam 更新参数
    ↓
test accuracy

神经网络图


输入图像 32×32×3
        ↓
卷积层 Conv
        ↓
残差块 Block1
        ↓
残差块 Block2
        ↓
残差块 Block3
        ↓
残差块 Block4
        ↓
全局平均池化 GAP
        ↓
全连接层 FC
        ↓
10分类输出

微调

finetune

源码


import os
import torch
import torchvision
from torch import nn
from d2l import torch as d2l

# 本案例演示如何使用预训练的ResNet-18模型进行迁移学习(微调),用于二分类(热狗/非热狗)任务。

#@save
d2l.DATA_HUB['hotdog'] = (d2l.DATA_URL + 'hotdog.zip',
                         'fba480ffa8aa7e0febbb511d181409f899b9baa5')

data_dir = d2l.download_extract('hotdog')

# 加载训练集和测试集图片,数据集结构需为ImageFolder格式(train/、test/子文件夹分别存放类别图片)。

train_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'))
test_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'))

# 数据增强与标准化:
# 训练集:随机裁剪为224x224、随机水平翻转、转为Tensor、标准化
# 测试集:缩放到256x256、中心裁剪224x224、转为Tensor、标准化

# 使用RGB通道的均值和标准差,以标准化每个通道
normalize = torchvision.transforms.Normalize(
    [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

# 下面我们将使用不同的图像增强方法来处理图像,并展示增强后的结果。
train_augs = torchvision.transforms.Compose([
    torchvision.transforms.RandomResizedCrop(224),
    torchvision.transforms.RandomHorizontalFlip(),
    torchvision.transforms.ToTensor(),
    normalize])

# 
test_augs = torchvision.transforms.Compose([
    torchvision.transforms.Resize([256, 256]),
    torchvision.transforms.CenterCrop(224),
    torchvision.transforms.ToTensor(),
    normalize])

# 
pretrained_net = torchvision.models.resnet18(pretrained=True)

# 加载预训练的ResNet-18模型,并将最后的全连接层(fc)替换为2分类输出(热狗/非热狗)
finetune_net = torchvision.models.resnet18(pretrained=True)
finetune_net.fc = nn.Linear(finetune_net.fc.in_features, 2)  # 输出类别数为2
nn.init.xavier_uniform_(finetune_net.fc.weight);  # 用Xavier初始化新全连接层参数

# 如果param_group=True,输出层中的模型参数将使用十倍的学习率
def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5,
                      param_group=True):
    # 构建训练集和测试集的DataLoader,应用对应的数据增强
    train_iter = torch.utils.data.DataLoader(
        torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'), transform=train_augs),
        batch_size=batch_size, shuffle=True)
    test_iter = torch.utils.data.DataLoader(
        torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'), transform=test_augs),
        batch_size=batch_size)
    # 自动检测可用GPU设备
    devices = d2l.try_all_gpus()
    # 使用交叉熵损失
    loss = nn.CrossEntropyLoss(reduction="none")
    # 优化器参数分组:
    # 预训练层使用较小学习率,最后新加的全连接层使用10倍学习率,加速收敛
    if param_group:
        params_1x = [param for name, param in net.named_parameters()
                     if name not in ["fc.weight", "fc.bias"]]
        trainer = torch.optim.SGD([
            {'params': params_1x},  # 预训练层参数
            {'params': net.fc.parameters(), 'lr': learning_rate * 10}  # 新增层参数
        ], lr=learning_rate, weight_decay=0.001)
    else:
        # 所有参数同一学习率
        trainer = torch.optim.SGD(net.parameters(), lr=learning_rate, weight_decay=0.001)
    # 调用d2l实现的多GPU训练主循环
    d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
# 调用训练函数,进行微调。learning_rate=5e-5,最后一层学习率为5e-4。
train_fine_tuning(finetune_net, 5e-5)

训练流程


                ┌────────────────────┐
                │   热狗训练图片      │
                └────────────────────┘
                           │
                           ▼
                ┌────────────────────┐
                │   数据增强(DataAug) │
                │ 随机裁剪/翻转/归一化 │
                └────────────────────┘
                           │
                           ▼
                ┌────────────────────┐
                │  预训练ResNet18    │
                │ (ImageNet学好的)   │
                └────────────────────┘
                           │
          ┌────────────────┴────────────────┐
          ▼                                 ▼
┌──────────────────┐             ┌──────────────────┐
│ 前面卷积层        │             │ 最后全连接层(fc) │
│ 保留已有知识      │             │ 换成新的2分类层  │
└──────────────────┘             └──────────────────┘
          │                                 │
          │ 小学习率                        │ 大学习率
          │ 慢慢调整                        │ 快速学习
          ▼                                 ▼
                ┌────────────────────┐
                │   输出分类结果      │
                │ hotdog / nothotdog │
                └────────────────────┘

学习率不同解释


ImageNet预训练模型
        ↓
   已有视觉能力
        ↓
替换最后分类层
        ↓
小学习率保护原知识
大学习率训练新层
        ↓
逐渐适应热狗任务
        ↓
得到专用分类器

学习率本质: 参数每次更新的步长

AI 大模型微调(Fine-tuning)到底是什么?


GPT-4
↓
继续训练
↓
变成:
法律助手
医疗助手
代码助手
金融助手

因为预训练模型太通用, 现实任务往往很专业

场景 需要什么
医疗AI 医学知识
金融AI 金融术语
客服AI 公司业务
编程AI 代码风格
Android助手 Framework知识

为什么会突然出现几十家大模型公司

Llama/Qwen 开源 -> 行业门槛大降 -> 中国AI公司爆发

为什么 DeepSeek 会震动行业?

  • 因为它证明:高性能AI不一定需要OpenAI级资金
  • 过去行业默认:只有几千亿美元资本, 才能做AGI

生态图


                    ┌──────────────────────┐
                    │     算力基础设施      │
                    │ NVIDIA / AMD / 云厂商 │
                    └──────────────────────┘
                               ▲
                               │
                               │ GPU/集群/云服务
                               │
────────────────────────────────────────────────────

                    ┌──────────────────────┐
                    │     基础模型层        │
                    │ Foundation Models     │
                    └──────────────────────┘

        ┌──────────────┬──────────────┬──────────────┐
        ▼              ▼              ▼
   闭源路线         开源路线         中国路线

┌────────────┐  ┌────────────┐  ┌────────────┐
│ OpenAI     │  │ Llama      │  │ Qwen       │
│ Claude     │  │ Mistral    │  │ DeepSeek   │
│ Gemini     │  │ Gemma      │  │ GLM        │
└────────────┘  └────────────┘  └────────────┘

────────────────────────────────────────────────────

                    ┌──────────────────────┐
                    │    后训练/微调层      │
                    │ SFT / RLHF / LoRA     │
                    └──────────────────────┘

        ┌──────────────┬──────────────┬──────────────┐
        ▼              ▼              ▼

   医疗AI          金融AI          工业AI
   法律AI          教育AI          政务AI

────────────────────────────────────────────────────

                    ┌──────────────────────┐
                    │     Agent工作流层     │
                    │ Tool Use / RAG / MCP │
                    └──────────────────────┘

          ┌───────────────────────────┐
          │ AI开始调用真实世界工具     │
          │                           │
          │ 搜索 / SQL / IDE / 浏览器 │
          │ ERP / CRM / API系统       │
          └───────────────────────────┘

────────────────────────────────────────────────────

                    ┌──────────────────────┐
                    │      应用层           │
                    │ AI Native Apps       │
                    └──────────────────────┘

    ┌──────────┬──────────┬──────────┬──────────┐
    ▼          ▼          ▼          ▼

 AI编程      AI客服     AI办公     AI搜索
 AI设计      AI医疗     AI投顾     AI机器人

利润流向图


          AI产业利润金字塔(2026)

                   ▲
                   │
                   │ 利润率最高
                   │
        ┌────────────────────┐
        │    GPU/云厂商       │
        │ NVIDIA / 云计算     │
        └────────────────────┘
                   │
                   │ 所有人都要买算力
                   ▼

        ┌────────────────────┐
        │   基础模型公司      │
        │ OpenAI/Anthropic    │
        └────────────────────┘
                   │
                   │ 高研发投入
                   │ 高推理成本
                   ▼

        ┌────────────────────┐
        │  AI Agent/平台层    │
        │ 自动化工作流         │
        └────────────────────┘
                   │
                   │ 开始出现SaaS利润
                   ▼

        ┌────────────────────┐
        │   行业应用层        │
        │ 医疗/教育/办公AI    │
        └────────────────────┘
                   │
                   │ 最卷
                   ▼

        ┌────────────────────┐
        │ 普通AI套壳应用      │
        │ Chat Wrapper        │
        └────────────────────┘

大模型厂商的护城河


          AI技术壁垒金字塔

                   ▲
                   │
                   │ 最难
                   │
        ┌────────────────────┐
        │ 超大规模预训练      │
        │ 分布式训练/算力     │
        └────────────────────┘

                   ▼

        ┌────────────────────┐
        │ RLHF / 后训练       │
        │ 对齐 / 推理能力      │
        └────────────────────┘

                   ▼

        ┌────────────────────┐
        │ 高质量行业数据       │
        │ 企业私有知识         │
        └────────────────────┘

                   ▼

        ┌────────────────────┐
        │ Agent工作流系统      │
        │ Tool Use / MCP      │
        └────────────────────┘

                   ▼

        ┌────────────────────┐
        │ 工程优化能力         │
        │ CUDA / 推理加速      │
        └────────────────────┘

                   ▼

        ┌────────────────────┐
        │ UI套壳               │
        │ 最容易复制            │
        └────────────────────┘

OpenAI 对比 DeepSeek

项目 OpenAI GPT-4时代 DeepSeek V3/R1时代
模型路线 闭源 Dense 开源 MoE
训练方式 超大规模暴力堆算力 强工程优化
训练成本 ~$1亿+ ~$500万级(公开估算)
GPU规模 数万张A100/H100 几千张H800
激活参数 大量全部激活 只激活部分专家
推理成本 很高 极低
API价格 极低
开源
主要优势 顶级综合能力 超高性价比
最大壁垒 Frontier Research 工程优化+效率

Anthropic 多模态

战略核心不同:他们押的是“可靠性”,不是“能力广度”

Anthropic 的主线是:安全 + 可控 + 可解释 + 推理能力


多模态 = 工程复杂度暴增,但收益不一定线性增长

多模态意味着:

文本 + 图像 + 音频 + 视频 + 时序

会带来:

数据对齐难度上升
训练成本暴涨
评估体系复杂
错误类型更多

Claude 系列在企业端的优势非常明确:

稳定性 > 炫技能力

  • 法律总结错一条条款
  • 金融分析幻觉一个数字
  • 代码生成引入隐性 bug

👉 成本是直接可量化的。

长文本能力 = 企业刚需

  • 可以读完整合同
  • 可以分析整份财报
  • 可以理解整套代码仓库
  • 可以做审计级分析

企业愿意为“可靠性”付溢价

企业级“安全AI基础设施”

目标检测和边界框



import torch
from d2l import torch as d2l

d2l.set_figsize()
img = d2l.plt.imread('../img/catdog.jpg')
# d2l.plt.imshow(img);

# d2l.plt.show()

#@save
def box_corner_to_center(boxes):
    """从(左上,右下)转换到(中间,宽度,高度)"""
    x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
    cx = (x1 + x2) / 2
    cy = (y1 + y2) / 2
    w = x2 - x1
    h = y2 - y1
    boxes = torch.stack((cx, cy, w, h), axis=-1)
    return boxes

#@save
def box_center_to_corner(boxes):
    """从(中间,宽度,高度)转换到(左上,右下)"""
    cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
    x1 = cx - 0.5 * w
    y1 = cy - 0.5 * h
    x2 = cx + 0.5 * w
    y2 = cy + 0.5 * h
    boxes = torch.stack((x1, y1, x2, y2), axis=-1)
    return boxes

# bbox是边界框的英文缩写
dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0]

#@save
def bbox_to_rect(bbox, color):
    # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:
    # ((左上x,左上y),宽,高)
    return d2l.plt.Rectangle(
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=2)

fig = d2l.plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(dog_bbox, 'blue'))
fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red'));

d2l.plt.show()

image_aug_3

锚框

目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边界从而更准确地预测目标的真实边界框(ground-truth bounding box)

不同的模型使用的区域采样方法可能不同。 这里我们介绍其中的一种方法:以每个像素为中心,生成多个缩放比和宽高比(aspect ratio)不同的边界框。 这些边界框被称为锚框(anchor box)

“先在图片上预设大量候选框(锚框),再让神经网络判断哪些框里有目标,并微调框的位置。

image_aug_4

Anchor 的思想

提前在图像上:

  • 放很多不同大小
  • 不同长宽比
  • 不同位置

的候选框。

为什么每个像素都生成锚框?


因为:

目标可能出现在任意位置。

所以:

每个像素中心
    ↓
生成多个候选框

最终:

特征图 H×W×K 个 anchors

数量巨大。

例如:

38×38×4 = 5776

这也是目标检测计算量大的原因之一。

IoU(交并比)

衡量:两个框有多像

iou

IoU 意义
>0.5 匹配较好
>0.7 很好
<0.3 很差

IoU 在哪里用?


1. 训练阶段

决定:

哪个 anchor 负责哪个目标。

例如:

anchor 和 狗 的 IoU 最大

那么:

这个 anchor 负责检测狗

2. 推理阶段

用于:

NMS(非极大值抑制)

删除重复框。

目标检测 = 分类 + 回归

  • 分类: 框里是什么?
  • 回归: 框的位置怎么调?

Anchor 有很多问题:

  • 超参数太多
  • 正负样本不平衡
  • 计算量大
  • 需要调 sizes/ratios

Anchor 思想依然是理解目标检测的基础

流程图


输入图片
    ↓
CNN特征图
    ↓
每个像素生成多个Anchor
    ↓
预测:
    1. 类别
    2. offset
    ↓
根据offset修正框
    ↓
得到大量预测框
    ↓
NMS去重
    ↓
最终检测结果

目标检测训练之前,必须先人工标注(label)目标框和类别


一个完整标注长这样

例如:

图片:street.jpg

目标1:
类别:person
框:[120, 80, 300, 600]

目标2:
类别:car
框:[500, 400, 900, 700]

目标检测的人工数据标注成本远高于分类

训练过程


┌─────────────────────────────────────┐
│         人工标注训练数据             │
│                                     │
│ image.jpg                          │
│   ├─ cat  [100,100,300,300]        │
│   └─ dog  [400,200,700,600]        │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│            输入训练图片              │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         CNN 提取特征图               │
│                                     │
│ ResNet / MobileNet / VGG            │
│                                     │
│ image                               │
│   ↓                                 │
│ Feature Map                         │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│        生成大量 Anchor              │
│                                     │
│ 每个特征点生成多个框:               │
│                                     │
│ ▢  ▭  ▯                             │
│                                     │
│ 总数量可能:                        │
│ 32×32×9 = 9216                      │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│       Anchor 与 GT 计算 IoU         │
│                                     │
│ A1 → 0.1                            │
│ A2 → 0.8                            │
│ A3 → 0.2                            │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│        给 Anchor 打标签              │
│                                     │
│ Positive:                           │
│   class = cat                       │
│   offset = Δx Δy Δw Δh              │
│                                     │
│ Negative:                           │
│   background                        │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         神经网络开始预测             │
│                                     │
│ 对每个 Anchor 输出:                 │
│                                     │
│ 1. 分类概率                         │
│ 2. bbox offset                      │
│                                     │
│ 例如:                              │
│ cat: 0.92                           │
│ dog: 0.01                           │
│ bg : 0.07                           │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│            计算 Loss                │
│                                     │
│ L = 分类损失 + bbox损失             │
│                                     │
│ 分类错了?                          │
│ 框偏了?                            │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│          反向传播 Backprop          │
│                                     │
│ 自动计算梯度                        │
│ 调整卷积核参数                      │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│            更新网络参数              │
│                                     │
│ SGD / Adam                          │
│                                     │
│ 让 Loss 更小                        │
└─────────────────────────────────────┘
                  │
                  │
        ┌─────────┴─────────┐
        │                   │
        │  下一批训练数据    │
        │  (Next Batch)     │
        │                   │
        └─────────┬─────────┘
                  │
                  │
                  ▼
        ╭───────────────────╮
        │                   │
        │   重复几十万次     │
        │                   │
        │ Forward           │
        │   ↓               │
        │ Loss              │
        │   ↓               │
        │ Backprop          │
        │   ↓               │
        │ Update Weights    │
        │                   │
        ╰────────┬──────────╯
                 │
                 │
                 └──────────────────────┐
                                        │
                                        ▼
                          ┌─────────────────────┐
                          │   CNN继续学习特征    │
                          │                     │
                          │ 什么像猫?          │
                          │ 什么像狗?          │
                          │ 框怎么调整?        │
                          └─────────────────────┘
                                        │
                                        ▼
                          ┌─────────────────────┐
                          │     最终训练完成     │
                          │                     │
                          │ 得到目标检测模型     │
                          └─────────────────────┘

推理过程


┌─────────────────────────────────────┐
│             新输入图片               │
│                                     │
│ test.jpg                            │
│                                     │
│ (注意:这里已经没有GT真实框)       │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│          图像预处理 Preprocess       │
│                                     │
│ 1. resize                           │
│ 2. normalize                        │
│ 3. RGB转换                          │
│ 4. tensor化                         │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│          输入神经网络 CNN            │
│                                     │
│ ResNet / MobileNet / CSPDarknet     │
│                                     │
│ 提取高级特征                         │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│          生成 Feature Map            │
│                                     │
│ 例如:                              │
│                                     │
│ 640×640 image                       │
│      ↓                              │
│ 80×80 feature map                   │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│          生成大量 Anchor             │
│                                     │
│ 每个特征点生成多个框:               │
│                                     │
│ ▢  ▭  ▯                             │
│                                     │
│ 小目标框                            │
│ 大目标框                            │
│ 长方形框                            │
│                                     │
│ 总数可能:                          │
│ 80×80×3 = 19200                     │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│      网络对每个 Anchor 做预测        │
│                                     │
│ 输出:                              │
│                                     │
│ 1. 分类概率                         │
│ 2. bbox offset                      │
│ 3. confidence                       │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         分类概率输出示例             │
│                                     │
│ Anchor#15231                        │
│                                     │
│ cat : 0.92                          │
│ dog : 0.03                          │
│ car : 0.01                          │
│ bg  : 0.04                          │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         bbox offset 输出             │
│                                     │
│ Δx = +0.12                          │
│ Δy = -0.08                          │
│ Δw = +0.05                          │
│ Δh = -0.03                          │
│                                     │
│ 意思:                              │
│ 向右移动一点                        │
│ 向上移动一点                        │
│ 宽一点                              │
│ 矮一点                              │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│        使用 offset 修正 Anchor       │
│                                     │
│ Anchor                              │
│ [90,90,290,310]                     │
│                                     │
│       + offset                      │
│                                     │
│ Final Box                           │
│ [100,100,300,300]                   │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         得到大量预测框               │
│                                     │
│ Box1: cat 0.95                      │
│ Box2: cat 0.93                      │
│ Box3: cat 0.91                      │
│ Box4: dog 0.88                      │
│ ...                                 │
│                                     │
│ (大量重复框)                       │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│     置信度过滤 Confidence Filter     │
│                                     │
│ 删除低分框:                         │
│                                     │
│ score < 0.5                         │
│                                     │
│ 例如:                              │
│                                     │
│ cat 0.12  → 删除                    │
│ dog 0.08  → 删除                    │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│        NMS 非极大值抑制              │
│                                     │
│ 目的:                              │
│ 删除重复框                           │
│                                     │
│ Step1:                              │
│ 按score排序                         │
│                                     │
│ 0.95                                │
│ 0.93                                │
│ 0.91                                │
│                                     │
│ Step2:                              │
│ 保留最高分框                         │
│                                     │
│ Step3:                              │
│ 计算IoU                             │
│                                     │
│ IoU太高 → 删除重复框                │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│         最终检测结果输出             │
│                                     │
│ cat  0.95                           │
│ bbox:[100,100,300,300]              │
│                                     │
│ dog  0.88                           │
│ bbox:[400,200,700,600]              │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│           绘制检测框                 │
│                                     │
│ 在图像上画出:                       │
│                                     │
│ ┌──────────┐                        │
│ │   cat    │                        │
│ └──────────┘                        │
│                                     │
│ 显示类别 + 分数                      │
└─────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────┐
│             推理结束                 │
│                                     │
│ 输出最终目标:                       │
│                                     │
│ “图中有猫和狗”                       │
└─────────────────────────────────────┘

YOLO

You Only Look Once


输入图片
    ↓
CNN提取特征
    ↓
直接输出:
    1. 类别
    2. bbox
    3. confidence
    ↓
NMS
    ↓
最终结果

YOLOv5 对比 YOLOv8

项目 YOLOv5 YOLOv8
检测范式 Anchor-based Anchor-free
Head Coupled Decoupled
样本分配 静态IoU 动态TaskAligned
Backbone CSP C2f
Loss CIoU DFL + IoU
后处理 简单 更复杂
工程成熟度 极高
RKNN支持 极成熟 中高
小目标 更强
泛化能力 更强
部署难度
实时性 极强
Transformer思想 更多

Ultralytics YOLO26

Ultralytics YOLO26 是 YOLO 系列实时对象检测器的最新演进,从头开始专为边缘和低功耗设备而设计。它引入了简化的设计,消除了不必要的复杂性,同时集成了有针对性的创新,以实现更快、更轻、更易于访问的部署。

Ultralytics-YOLO26-Benchmark

SAM3

项目 YOLO SAM3.x
核心 Dense Detection Scene Understanding
Backbone CNN为主 Transformer为主
时序Memory 很少 很强
Tracking
Segmentation 可选 核心
视频理解 一般
NPU友好 极强 一般
Attention占比 很高
全局语义 很强

多尺度目标检测


Input Image
    │
    ▼
CNN Backbone
    │
    ├────────► P3 (80×80)
    │             │
    │             └──► 小目标检测
    │
    ├────────► P4 (40×40)
    │             │
    │             └──► 中目标检测
    │
    └────────► P5 (20×20)
                  │
                  └──► 大目标检测Merge All Predictions
    ▼
NMS / End-to-End
    ▼
Final Detection Result

目标检测数据集

标准训练过程


训练 Train
    ↓
训练完整个训练集(1个epoch)
    ↓
验证 Validation
    ↓
记录指标
    ↓
继续下一轮epoch

epoch含义解释


Epoch 1
    │
    ├── Batch1 Train
    ├── Batch2 Train
    ├── Batch3 Train
    └── ...

整个Train结束
    ↓

Validation
    ↓
计算mAP
    ↓
保存best.pt

进入Epoch2

源码



import os
import pandas as pd
import torch
import torchvision
from d2l import torch as d2l

#@save
d2l.DATA_HUB['banana-detection'] = (
    d2l.DATA_URL + 'banana-detection.zip',
    '5de26c8fce5ccdea9f91267273464dc968d20d72')

# 目标检测数据集说明:
# 该数据集用于香蕉目标检测,结构如下:
# - bananas_train/  训练集目录
#   - images/       训练图片
#   - label.csv     每张图片的标注信息
# - bananas_val/    验证集目录
#   - images/       验证图片
#   - label.csv     每张图片的标注信息
#
# label.csv 文件内容:
# img_name,class,xmin,ymin,xmax,ymax
# 其中:
#   img_name:图片文件名
#   class:类别编号(本例中均为0,表示香蕉)
#   xmin, ymin:目标左上角坐标
#   xmax, ymax:目标右下角坐标
#
# 每张图片可能有一个或多个目标(本例均为1个香蕉)。

#@save
def read_data_bananas(is_train=True):
    """
    读取香蕉检测数据集中的图像和标签
    返回:
        images: 图像数据列表,每个元素为Tensor,形状[C,H,W]
        targets: 目标信息Tensor,形状[N,1,5],每行为[class,xmin,ymin,xmax,ymax],坐标已归一化到[0,1]
    """
    data_dir = d2l.download_extract('banana-detection')
    # 选择训练或验证集的标注文件
    csv_fname = os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'label.csv')
    csv_data = pd.read_csv(csv_fname)
    csv_data = csv_data.set_index('img_name')
    images, targets = [], []
    for img_name, target in csv_data.iterrows():
        # 读取图片
        images.append(torchvision.io.read_image(
            os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}')))
        # 读取目标信息:[类别, xmin, ymin, xmax, ymax]
        # 本例类别恒为0,坐标为像素值,需归一化到[0,1](除以256)
        targets.append(list(target))
    # 返回图像列表和归一化后的目标信息
    return images, torch.tensor(targets).unsqueeze(1) / 256

#@save
class BananasDataset(torch.utils.data.Dataset):
    """
    一个用于加载香蕉检测数据集的自定义数据集
    每个样本返回:(图像Tensor, 目标信息Tensor)
    目标信息格式:[类别, xmin, ymin, xmax, ymax],坐标已归一化
    """
    def __init__(self, is_train):
        # 读取所有图片和标签
        self.features, self.labels = read_data_bananas(is_train)
        print('read ' + str(len(self.features)) + (f' training examples' if is_train else f' validation examples'))

    def __getitem__(self, idx):
        # 返回第idx个样本:(图像, 目标信息)
        return (self.features[idx].float(), self.labels[idx])

    def __len__(self):
        # 返回数据集样本数
        return len(self.features)

#@save
def load_data_bananas(batch_size):
    """加载香蕉检测数据集"""
    train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True),
                                             batch_size, shuffle=True)
    val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False),
                                           batch_size)
    return train_iter, val_iter

batch_size, edge_size = 32, 256
train_iter, _ = load_data_bananas(batch_size)
batch = next(iter(train_iter))
batch[0].shape, batch[1].shape

imgs = (batch[0][0:10].permute(0, 2, 3, 1)) / 255
axes = d2l.show_images(imgs, 2, 5, scale=2)
for ax, label in zip(axes, batch[1][0:10]):
    d2l.show_bboxes(ax, [label[0][1:5] * edge_size], colors=['w'])

d2l.plt.show()

代码流程图


banana-detection.zip
        │
        ▼
读取 label.csv
        │
        ▼
读取 image
        │
        ▼
读取 bbox
        │
        ▼
归一化坐标
        │
        ▼
构造 Dataset
        │
        ▼
DataLoader 组成 Batch
        │
        ▼
送入神经网络

label.csv 是什么?


img_name,label,xmin,ymin,xmax,ymax
0.png,0,104,20,143,58
1.png,0,68,175,118,223
2.png,0,163,173,218,239
3.png,0,48,157,84,201
4.png,0,32,34,90,86
5.png,0,69,201,120,245
6.png,0,167,99,212,135
7.png,0,157,67,197,108
8.png,0,131,157,189,219
9.png,0,163,93,217,158
10.png,0,94,15,153,71

字段 含义
img_name 图片名
class 类别
xmin,ymin 左上角
xmax,ymax 右下角