Skip to content

第二章 目标检测算法


课程导航

项目内容
课时2 小时
类型理论 + 代码实战
前置知识CNN 基础、PyTorch 基本使用、第一章 OpenCV

一、学习目标

  1. 理解目标检测的核心评估指标:IoU、mAP、NMS
  2. 掌握 Faster R-CNN 的两阶段检测流程(RPN + ROI Pooling)
  3. 理解 YOLO 系列从 v1 到 v5 的核心演进思想
  4. 能使用 YOLOv8/v9 训练自定义数据集
  5. 了解小目标检测的优化方法(多尺度训练、FPN)

二、核心知识点

2.1 目标检测评估指标

2.1.1 定义

指标全称作用
IoUIntersection over Union预测框与真实框的交并比,衡量定位精度
mAPmean Average Precision综合召回率与精确率的全局指标
NMSNon-Maximum Suppression后处理去冗余框,保留最优检测

2.1.2 IoU 计算

python
import numpy as np

def compute_iou(box1, box2):
    """
    box: [x1, y1, x2, y2] 左上 + 右下坐标
    """
    x1 = max(box1[0], box2[0])
    y1 = max(box1[1], box2[1])
    x2 = min(box1[2], box2[2])
    y2 = min(box1[3], box2[3])

    inter = max(0, x2 - x1) * max(0, y2 - y1)
    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
    union = area1 + area2 - inter

    return inter / union if union > 0 else 0.0

# 示例
gt = [50, 50, 150, 150]
pd = [60, 60, 140, 140]
print(f"IoU = {compute_iou(gt, pd):.4f}")  # 结果 ≈ 0.64

2.1.3 NMS 算法

python
def nms(boxes, scores, iou_threshold=0.5):
    """
    boxes: [[x1,y1,x2,y2], ...]
    scores: [score, ...]
    返回保留的框索引
    """
    indices = np.argsort(scores)[::-1]  # 按得分降序
    keep = []

    while len(indices) > 0:
        i = indices[0]
        keep.append(i)
        rest = indices[1:]

        ious = np.array([compute_iou(boxes[i], boxes[j]) for j in rest])
        indices = rest[ious <= iou_threshold]  # 移除与当前框 IoU 过大的

    return keep

# 测试
boxes = np.array([[10,10,50,50], [12,12,48,48], [100,100,150,150]])
scores = np.array([0.9, 0.8, 0.95])
kept = nms(boxes, scores, 0.5)
print(f"NMS 保留索引: {kept}")

2.1.4 mAP 计算流程

  1. 对每个类别,按置信度降序排列所有检测框
  2. 依次计算 Precision 和 Recall,绘制 PR 曲线
  3. AP = PR 曲线下面积(通常用 11 点插值或积分法)
  4. mAP = 所有类别 AP 的均值
  5. COCO 标准:mAP@0.5(IoU=0.5)、mAP@0.5:0.95(IoU 从 0.5 到 0.95 步长 0.05 取均值)

2.2 Faster R-CNN — 两阶段检测器

2.2.1 定义

Faster R-CNN 是两阶段(Two-Stage)目标检测的代表,通过**区域建议网络(RPN)**替代 Selective Search,实现端到端训练。

输入图像 → 共享 CNN 骨干 → 特征图
                              ├── RPN → 区域建议(Region Proposals)
                              └── ROI Pooling → 分类 + 回归

核心组件

组件作用
BackboneCNN 提取特征(VGG16 / ResNet-50 / ResNet-101)
RPN在特征图上滑动窗口,输出 anchor 的前景/背景得分 + 坐标偏移
ROI Pooling将不同尺寸的 proposal 池化为固定尺寸(如 7×7)送入全连接层
Head全连接层输出类别概率 + 边界框回归偏移

2.2.2 Anchor 机制

  • 每个滑动窗口位置预设 k 个 anchor box(不同比例 + 不同尺度)
  • 通常 k=9(3 种面积 × 3 种长宽比)
  • RPN 输出:2k 个类别得分 + 4k 个坐标偏移

2.2.3 代码骨架(使用 Detectron2 / Torchvision)

python
# ---------- 使用 torchvision 内置 Faster R-CNN ----------
import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn

# 加载预训练模型
model = fasterrcnn_resnet50_fpn(pretrained=True)
model.eval()

# 推理
from PIL import Image
import torchvision.transforms as T

img = Image.open('test.jpg').convert('RGB')
transform = T.Compose([T.ToTensor()])
img_tensor = transform(img).unsqueeze(0)

with torch.no_grad():
    predictions = model(img_tensor)

# predictions[0] 包含 'boxes', 'labels', 'scores'
boxes = predictions[0]['boxes'].numpy()
scores = predictions[0]['scores'].numpy()
labels = predictions[0]['labels'].numpy()

# 过滤低置信度预测
keep = scores > 0.5
filtered_boxes = boxes[keep]
print(f"检测到 {len(filtered_boxes)} 个目标")

2.3 YOLO 系列演进

2.3.1 定义

YOLO(You Only Look Once)将目标检测视为回归问题,单次前向即可输出类别与位置,实现实时检测。

版本年份核心贡献
v12016首个单阶段检测器;将图像分为 S×S 网格,每个网格预测 B 个框
v22017Batch Normalization、Anchor Box、DarkNet-19、多尺度训练
v32018DarkNet-53、FPN 多尺度预测、9 个 anchor、Logistic 分类器
v42020CSPDarkNet-53、Mish 激活、MOSAIC 数据增强、CIoU Loss
v52020Focus 模块、自适应 anchor、GIOU Loss、四种模型尺寸(n/s/m/l/x)

2.3.2 YOLOv1 核心思想

  • 输入 448×448,输出 S×S×(B×5 + C) 张量
  • S=7, B=2, C=类别数(PASCAL VOC 为 20)
  • 每个框预测:[x, y, w, h, confidence]
  • Loss = 坐标误差 + 置信度误差 + 分类误差

2.3.3 YOLOv3 关键改进

python
# YOLOv3 输出解析核心逻辑
def decode_yolo_output(pred, anchors, num_classes):
    """
    pred: (batch, grid_h, grid_w, num_anchors, 5+num_classes)
    anchors: [[w,h], ...]
    """
    batch, h, w, num_anchors, _ = pred.shape
    grid_x, grid_y = np.meshgrid(np.arange(w), np.arange(h))

    # 解析中心坐标偏移
    x = (grid_x + sigmoid(pred[..., 0])) / w
    y = (grid_y + sigmoid(pred[..., 1])) / h
    # 解析宽高(指数缩放)
    w_box = anchors[:, 0] * np.exp(pred[..., 2]) / w
    h_box = anchors[:, 1] * np.exp(pred[..., 3]) / h
    # 置信度 + 分类
    conf = sigmoid(pred[..., 4])
    cls_probs = softmax(pred[..., 5:], axis=-1)

    return x, y, w_box, h_box, conf, cls_probs

YOLOv3 多尺度预测:在 3 个不同尺度的特征图上分别预测(13×13、26×26、52×52),每个尺度 3 个 anchor,共 9 个 anchor。


2.4 YOLOv8 / v9 自定义训练

2.4.1 YOLOv8 简介

Ultralytics YOLOv8 是目前最流行的 YOLO 实现之一,统一了目标检测、分割、姿态估计、分类等任务的训练框架。

2.4.2 数据集准备(COCO 格式)

dataset/
├── images/
│   ├── train/   # 训练图像
│   └── val/     # 验证图像
├── labels/
│   ├── train/   # 每个图像对应一个 .txt
│   └── val/
└── data.yaml    # 数据集配置

data.yaml 示例:

yaml
train: ./dataset/images/train
val: ./dataset/images/val
nc: 3
names: ['defect', 'scratch', 'dent']

标签格式(每行一个目标):

<class_id> <x_center> <y_center> <width> <height>
# 坐标均归一化到 [0, 1]

2.4.3 训练代码(YOLOv8)

python
from ultralytics import YOLO

# ---------- 加载预训练模型 ----------
model = YOLO('yolov8n.pt')  # n/s/m/l/x 可选

# ---------- 训练 ----------
results = model.train(
    data='data.yaml',
    epochs=100,
    imgsz=640,
    batch=16,
    device='cuda',
    lr0=0.01,
    augment=True,          # 自动数据增强
    patience=20,           # Early Stopping
)

# ---------- 验证 ----------
metrics = model.val()
print(f"mAP@0.5: {metrics.box.map50:.4f}")
print(f"mAP@0.5:0.95: {metrics.box.map:.4f}")

# ---------- 推理 ----------
results = model('test.jpg', conf=0.5)
results[0].show()  # 显示检测结果
results[0].save('result.jpg')

# ---------- 导出 ----------
model.export(format='onnx')  # 导出 ONNX

2.4.4 YOLOv9 亮点

  • PGI(Programmable Gradient Information):解决深层网络梯度信息丢失问题
  • GELAN(Generalized Efficient Layer Aggregation Network):轻量高效的特征融合
  • 相比 v8 在同等参数下精度更高
python
from ultralytics import YOLO

# YOLOv9 训练方式与 v8 高度一致
model = YOLO('yolov9c.pt')  # yolov9t / yolov9c / yolov9e
model.train(data='data.yaml', epochs=100, imgsz=640)

2.5 小目标检测优化

2.5.1 定义

小目标(通常 < 32×32 像素)在特征图中信息量极少,是检测中的难点。

2.5.2 优化策略

策略说明
多尺度训练每次迭代随机选择不同输入尺寸(如 320→640),增强尺度鲁棒性
特征金字塔 (FPN)自顶向下传递语义信息,每个尺度独立预测
高分辨率输入保持输入分辨率(如 1280),但增加计算量
数据增强Mosaic、Copy-Paste、Random Crop 增加小目标多样性
专门的小目标检测头在浅层高分辨率特征图上添加额外的检测头
Swin Transformer 骨干利用全局注意力捕获小目标上下文

2.5.3 FPN 核心思想

P7 ──↑ (1/128)
P6 ──↑ (1/64)
P5 ──↑ (1/32)  ← 自顶向下语义融合
P4 ──↑ (1/16)
P3        (1/8) ← 原始底层特征(高分辨率)
python
# FPN 简化示意
class FPN(nn.Module):
    def __init__(self, in_channels_list, out_channels=256):
        super().__init__()
        self.lateral_convs = nn.ModuleList([
            nn.Conv2d(c, out_channels, 1) for c in in_channels_list
        ])
        self.output_convs = nn.ModuleList([
            nn.Conv2d(out_channels, out_channels, 3, padding=1)
            for _ in in_channels_list
        ])

    def forward(self, features):
        # features: [C3, C4, C5] 从骨干网络
        laterals = [conv(f) for f, conv in zip(features, self.lateral_convs)]

        # 自顶向下融合(从最高层开始)
        for i in range(len(laterals)-1, 0, -1):
            laterals[i-1] += F.interpolate(laterals[i],
                                           scale_factor=2,
                                           mode='nearest')

        # 输出卷积平滑
        outputs = [conv(l) for l, conv in zip(laterals, self.output_convs)]
        return outputs  # [P3, P4, P5]

2.5.4 YOLOv8 开启多尺度训练

python
model.train(
    data='data.yaml',
    epochs=100,
    imgsz=640,
    multi_scale=True,      # 开启多尺度训练
    scale=0.5,             # 尺度抖动范围 [1-scale, 1+scale]
    mosaic=1.0,            # Mosaic 增强概率
    copy_paste=0.1,        # Copy-Paste 增强
)

三、小结

知识点掌握程度关键理解
IoU / mAP / NMS必须掌握评价和过滤检测结果的三大工具
Faster R-CNN理解流程RPN 生成 proposal + ROI Pooling 分类回归
YOLO 演进必须掌握单阶段回归 => 多尺度预测 => CSP 结构
YOLOv8/v9 训练必须掌握数据准备 → model.train() → 导出
小目标检测优化理解策略多尺度 + FPN + 数据增强 + 高分辨率

四、课后练习

基础题

  1. 手写 NMS 函数,并用 numpy 实现完整的 mAP 计算流程(给定预测框和真实框列表)。
  2. 使用 torchvision.models.detection.fasterrcnn_resnet50_fpn 对一张包含多个物体的图片进行检测,输出所有置信度 > 0.5 的目标类别和坐标。

进阶题

  1. 收集 100 张自定义数据集(如交通标志),标注为 YOLO 格式,使用 YOLOv8 训练并评估 mAP。
  2. 对比 YOLOv8n / YOLOv8s / YOLOv8m 在相同数据集上的训练速度、模型大小和 mAP 表现。
  3. 使用 YOLOv8-seg 实例分割模型训练一个自定义分割数据集。

思考题

  1. 为什么 YOLOv3 在三个不同尺度的特征图上做预测?这对小目标检测有什么帮助?
  2. 两阶段(Faster R-CNN)和单阶段(YOLO)检测器各自的优缺点是什么?在什么场景下该选谁?

Python 学习资料