YOLO

目录

深度学习目标检测

一、核心思想:从手工特征到学习特征

传统方法 vs 深度学习方法

对比维度 传统方法(HOG+SVM等) 深度学习方法
特征提取 手工设计特征(梯度、纹理等) 网络自动学习特征
适应性 特征固定,通用性有限 特征自适应,任务特定
检测流程 滑动窗口+分类器 端到端学习
性能 精度有限,速度慢 精度高,速度快(优化后)

关键转变:从“人设计特征,机器分类”到“机器自己学习特征并分类”

二、深度学习目标检测的两种基本范式

2.1 两阶段检测器(Two-Stage)

代表算法:R-CNN、Fast R-CNN、Faster R-CNN

核心流程: 1. 第一阶段:生成候选区域 - 找出图像中可能有目标的区域 - 方法:Selective Search、RPN(Region Proposal Network) - 输出:几百到几千个候选框

  1. 第二阶段:分类和定位
  2. 对每个候选框:
    • 裁剪并调整到固定大小
    • 通过CNN提取特征
    • 分类(是什么目标)
    • 回归(调整边界框位置)
# 两阶段检测器的简化实现思路
class TwoStageDetector:
    def __init__(self):
        # 第一阶段:候选区域生成
        self.region_proposal = RegionProposalNetwork()

        # 第二阶段:检测网络
        self.detection_network = DetectionCNN()

    def forward(self, image):
        # 第一阶段:生成候选区域
        proposals = self.region_proposal(image)  # 输出候选框

        # 第二阶段:对每个候选框检测
        detections = []
        for proposal in proposals:
            # 裁剪候选区域
            crop = crop_image(image, proposal)

            # 通过CNN进行分类和回归
            class_score, bbox_refine = self.detection_network(crop)
            detections.append({
                'bbox': refine_bbox(proposal, bbox_refine),
                'class': class_score,
                'score': confidence_score
            })

        return detections

优点:精度高,候选框质量好
缺点:速度慢,不适合实时应用

2.2 单阶段检测器(One-Stage)

代表算法:YOLO、SSD、RetinaNet

核心流程: 1. 直接回归:将目标检测视为回归问题 2. 密集预测:在特征图的每个位置直接预测边界框和类别 3. 端到端:输入图像直接输出检测结果

# 单阶段检测器的简化实现思路
class OneStageDetector:
    def __init__(self, grid_size=13, num_classes=20):
        self.grid_size = grid_size  # 网格划分的一维个数,总个数为grid_size*grid_size
        self.num_classes = num_classes

        # 特征提取骨干网络
        self.backbone = CNNBackbone()

        # 检测头:直接预测边界框和类别
        self.detection_head = nn.Conv2d(
            in_channels=512,
            out_channels=grid_size*grid_size*(5+num_classes),
            kernel_size=1
        )

    def forward(self, image):
        # 提取特征
        features = self.backbone(image)  # [batch, channels, H, W]

        # 检测头直接预测
        predictions = self.detection_head(features)  # [batch, S*S*(5+C), H, W]

        # 解码预测结果
        # predictions包含了:边界框(x,y,w,h)、置信度、类别概率
        return decode_predictions(predictions)

优点:速度快,适合实时检测
缺点:精度通常低于两阶段方法

注:5+num_classes中:5 是边界框本身必须带 5 个参数num_classes 是它是某一类与否的打分,两者缺一不可,所以相加。

下面把 5 拆给你看(一句话一个数字):

  1. x 框中心在网格内的横向偏移
  2. y 框中心在网格内的纵向偏移
  3. w 框的宽度(相对于整图)
  4. h 框的高度(相对于整图)
  5. conf 框里有物体的置信度

三、YOLO:单阶段检测的代表

3.1 YOLO的核心创新

一句话理解YOLO:将图像划分成网格,每个网格负责检测中心点落在该网格内的目标。

# YOLO网格划分的直观理解
import numpy as np

# 假设416×416的图像
image_height = 416
image_width = 416
grid_cells = 13  # 划分为13×13网格

# 计算每个网格的像素范围
cell_height = image_height / grid_cells  # 32像素
cell_width = image_width / grid_cells    # 32像素

# 示例:判断目标(100, 150)属于哪个网格
target_x, target_y = 100, 150
grid_x = int(target_x // cell_width)    # 第3列
grid_y = int(target_y // cell_height)   # 第4行

print(f"目标坐标({target_x}, {target_y})属于网格({grid_x}, {grid_y})")
print(f"该网格负责检测中心在[{grid_x*32}:{(grid_x+1)*32}, {grid_y*32}:{(grid_y+1)*32}]区域的目标")

3.2 YOLO的预测内容

每个网格预测: 1. 边界框(Bounding Box):预测B个框(YOLO v1是2个,v2/v3是3个) - (x, y):框中心相对于网格的偏移(0-1之间) - (w, h):框的宽高相对于整个图像的比例 - confidence:框的置信度 = P(有目标) × IoU(预测框, 真实框)

  1. 类别概率:C个类别的条件概率

输出维度:S × S × (B×5 + C) - S:网格数(如13) - B:每个网格预测的框数(如3) - 5:每个框的5个参数(x, y, w, h, confidence) - C:类别数(如COCO的80类)

四、关键概念详解

4.1 Anchor Box(锚框)

为什么要用Anchor Box?

  • 在 v1 里一个网格只能输出 1 组框 → 遇多目标/多尺度就冲突;v2 起让网格一次输出 3 组 anchor 框(不同尺寸),每组框独立跑回归,于是同一个网格可以“各管各”地检测多个目标。

  • v1:每网格 2 个框,但共享类别概率 → 仍只能当 1 个目标。

  • v2+:每网格 3 个 anchor 框,每个 anchor 独立带 (x,y,w,h,conf,类别),     因此同一网格可以同时预测 3 个不同尺度/形状的目标。

  • 问题:一个网格只能预测一个目标(v1的问题)

  • 解决:一个网格预测多个不同尺寸的框,每个框专门检测特定尺寸的目标

训练过程

  1. 前向预测 把一张训练图片喂进网络,得到 13×13×(3×(5+80)) 的张量 → 即每个网格 3 个 anchor 的预测框、置信度、类别概率。
  2. 正负样本分配 • 用 IoU 把每个真实框(gt_box) 与 所有 anchor(含位置、尺寸)做匹配。 • IoU>阈值 的 anchor 标记为 正样本(负责回归这个框); • IoU<阈值 的标记为 负样本(背景); • 其余忽略。
  3. 反向传播 把三类损失一起反向传播: • 回归损失:只针对正样本,学 Δx, Δy, Δw, Δh; • 置信度损失:所有样本都算,让正样本 confidence≈1,负样本 ≈0(负样本的监督信号); • 分类损失:仅正样本,让类别概率逼近真实类别。

重复 1-3 步,直到损失收敛,即模型学会用不同 anchor 检测不同尺度目标。

# Anchor Box的简单示例
class AnchorBox:
    def __init__(self):
        # 预定义的框尺寸(宽高)
        # 这些尺寸是通过对训练集聚类得到的
        self.anchors = [
            [116, 90],   # 适合大目标
            [156, 198],  # 适合中目标
            [373, 326]   # 适合小目标
        ]

    def match_anchors(self, gt_box):
        """将真实框匹配到最相似的锚框"""
        # 计算真实框与每个锚框的IoU
        ious = []
        for anchor in self.anchors:
            iou = calculate_iou(gt_box, anchor)
            ious.append(iou)

        # 选择IoU最大的锚框
        best_anchor_idx = np.argmax(ious)
        return best_anchor_idx, ious[best_anchor_idx]

4.2 IoU(交并比)

IoU = 交集面积 / 并集面积

def calculate_iou(box1, box2):
    """
    计算两个边界框的IoU
    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_area = max(0, x2 - x1) * max(0, y2 - y1)

    # 计算各自面积
    box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
    box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])

    # 计算并集面积
    union_area = box1_area + box2_area - inter_area

    return inter_area / union_area