YOLOv1 详解
YOLOv1: You Only Look Once
论文: You Only Look Once: Unified, Real-Time Object Detection (CVPR 2016)
作者: Joseph Redmon, Santosh Divvala, Ross Girshick, Ali Farhadi
一、核心思想
1.1 什么样的任务是”回归问题”?
一句话:输出连续数值的是回归,输出离散标签的是分类。
1 | ┌─────────────────────────────────────────────────────────────┐ |
数学本质上:
- 分类:模型学的是决策边界,把输入空间切分成不同类别区域
- 回归:模型学的是连续映射函数 $f: \text{image} \to \mathbb{R}^n$,直接从像素映射到实数
1.2 为什么目标检测天然不像是回归问题?
传统上,目标检测不用回归解决,是因为有个致命障碍:
1 | 回归要求: 输入 → 固定维度的输出 |
这就是为什么 R-CNN 系列走「分类路线」:
- 先想办法找出候选区(可能有物体的地方)
- 对每个候选区分别做分类(这个区域是猫还是背景?)
- 对每个候选区微调位置(回归修正框的坐标)
本质上是把检测拆成了「分类 + 小回归」,绕开了输出维度不固定的难题。
1.3 YOLO 怎么把检测变成回归的?—— 固定网格的妙想 🔑
YOLO 的核心洞察:把图像切成固定网格,每个网格固定输出一组数值。
1 | 一张图切成 S×S 网格 → 每个网格固定预测 B 个框 + C 个类别 |
| 传统思路 | YOLO 的思路 |
|---|---|
| “图里有几个物体?先找出来,再一个个分类” | “不管几个物体,我把图切成 49 格,每格固定输出一组预测” |
| 输出维度不固定 → 不能直接回归 | 输出维度固定 → 可以端到端回归 |
| 需要 region proposal 步骤 | 不需要,整张图一次性出结果 |
| 多个模型/步骤串行 | 单个 CNN 端到端 |
💡 关键的思维转换:不是”找出物体再分类”,而是”每个位置都预测,相信总有一个位置能对上”。
1.4 YOLO 回归的具体内容
YOLO 从一个 CNN 里直接回归出两类连续值:
| 回归内容 | 具体数值 | 类型 |
|---|---|---|
| BBox 坐标 | $(x, y, w, h)$ | 连续实数(位置和大小) |
| 置信度 | $\text{confidence} \in [0,1]$ | 连续实数(有物体的确信程度) |
| 类别概率 | $P(\text{class}_i) \in [0,1]$ | 连续实数(各类别的概率值) |
所有这些值都是直接从像素回归出来的实数,中间没有”这个区域是不是物体”的二分类判断步骤。
对比:Faster R-CNN 的 RPN 阶段先做二分类(有物体/无物体),再做回归(修框)。YOLO 把这全部压缩进一次回归。
1.5 深入理解:凭什么说它是”回归”?—— 人定规则 ≠ 不是回归 🔥
很多人读到这会困惑:“网格分配、IoU 选框、confidence 公式……不都是人设计的规则吗?模型只是照规则输出,这也能叫回归?”
这是一个关键误区:把”人怎么设计 label”和”模型在解什么数学问题”混为一谈了。
1 | ┌────────────────────────────────────────────────────────────┐ |
用房价预测来类比——最纯粹的回归问题:
1 | 房价预测: |
YOLO 完全一样:
1 | YOLO: |
判断”是不是回归”只看三样东西,跟 label 规则无关:
1 | ┌──────────────────────────────────────────────────────────────┐ |
YOLO 三条全中回归:输出实数、L2 损失、学连续映射。
那 R-CNN 不也输出坐标吗?凭什么它不是”纯回归”?
1 | R-CNN 的做法: |
一句话总结这个误区:
“人设计了网格分配的规则”说明的是训练数据的 label 是怎么构造的——这和房价预测中”人给房子标价格”是同一件事。但 YOLO 之所以是回归,是因为模型在做的事情是从像素直接映射到连续实数,全程没有离散分类决策——这和房价预测中”从特征映射到价格”是同一类数学问题。
1.6 一句话总结
1 | Two-Stage (R-CNN系列): |
YOLO vs Faster R-CNN 关键差异:
- YOLO 没有显式求解 region proposal,整个网络统一训练
- YOLO 同时预测位置(回归)和类别(分类),而非分两步
- Faster R-CNN 需反复训练 RPN 和 Fast RCNN,YOLO 端到端一步搞定
二、网络架构
2.1 整体结构
1 | 输入: 448×448×3 |
2.2 设计灵感与关键组件
| 组件 | 配置 | 作用 |
|---|---|---|
| 卷积层 | 24 层 | 1×1降维 + 3×3特征提取,替代 Inception Module |
| 激活函数 | Leaky ReLU (0.1) | $f(x)=\max(0.1x, x)$,防止神经元在负区间彻底”死亡” |
| 池化层 | 4 层 Max Pooling (2×2, s=2) | 逐步下采样:448→224→112→56→28→14→7 |
| Dropout | 第1个FC层后,rate=0.5 | 防止过拟合 |
| 数据增强 | 随机缩放、平移、曝光+饱和度调整 | 提升泛化能力 |
2.3 下采样过程
1 | 448×448 → Pool1 → 224×224 → Pool2 → 112×112 |
⚠️ 实际实现中,第一个全连接层被替换为局部连接层,与论文有所不同。
⚠️ 重要限制: 由于使用了全连接层,YOLOv1 只支持与训练图像相同分辨率的输入图片。
三、图像预处理
3.1 输入处理流程
1 | 原始图像 (任意尺寸) |
四、预测阶段(前向推断)详解
4.1 完整推理流程
1 | Step 1: Resize image → 448×448 |
4.2 网格划分策略
将 448×448 图像划分为 7×7 = 49 个网格。
核心规则: 如果某个 object 的中心点落在某个网格内,则该网格负责预测该 object。
1 | ┌───────────────┐ |
⭐ 通俗理解:这句话到底在说什么?
很多人在这一步卡住——“凭什么中心点在哪格就归哪格管?网络怎么知道的?”
答案:网络不知道。是你训练时教的。
1 | 不要把这句话理解成"网络的行为", |
为什么是”中心点”?——解决”一个物体跨多个格子,谁说了算”的问题。
1 | 猫的身体可能横跨 3 个格子: |
打个比方——分田到户:
1 | 一个村(图片) → 分成 49 块地(网格) → 每块地一个农民(cell) |
训练时的实际过程:
1 | 你拿来一张训练图: |
不要理解错:
| ❌ 不要理解成 | ✅ 应该理解成 |
|---|---|
| 网络主动判断”这个物体归格(3,2)管” | 训练时人告诉网络: 格(3,2), 这道题你的正确答案是这个 |
| 网格是模型自己学会的”结构” | 网格是人给训练数据贴标签的”分配方案” |
| “凭什么?” —— 网络有魔法 | “凭什么?” —— 没凭什么, 人定的规矩, 为了方便教学 |
4.3 每个网格的预测内容
每个网格预测 B=2 个 Bounding Box + C=20 个类别概率(PASCAL VOC)。
BBox 预测 (每个 BBox 5 个值):
| 值 | 含义 | 归一化方式 |
|---|---|---|
| x, y | BBox 中心点坐标 | 相对于当前网格左上角,归一化到 [0, 1] |
| w, h | BBox 宽和高 | 相对于整张图片的宽高,归一化到 [0, 1] |
| confidence | 置信度 | 见下方公式 |
4.4 坐标解码公式
编码(网络输出):
- $x, y$: 相对于所在网格左上角偏移,归一化到 [0,1]
- $w, h$: 相对于整张图宽高,归一化到 [0,1]
解码(从网格坐标还原实际坐标):
设网格位置为 $(c_x, c_y)$,网格宽度为 $g_w$,网格高度为 $g_h$:
$$\text{实际中心X} = (c_x + x) \times g_w$$
$$\text{实际中心Y} = (c_y + y) \times g_h$$
$$\text{实际宽度W} = w \times \text{image_width}$$
$$\text{实际高度H} = h \times \text{image_height}$$
例如:图片 448×448,网格(2,3)位于第2列第3行,网格宽=448/7=64。若预测 x=0.5, 则实际中心X = (3+0.5)×64 = 224。
4.5 置信度 Confidence 详解
置信度由两个因素组成:
$$\text{confidence} = \text{Pr(Object)} \times \text{IoU}_{pred}^{truth}$$
| 因子 | 含义 | 取值 |
|---|---|---|
| Pr(Object) | 该 BBox 包含目标的可能性 | 有目标 → 1,无目标 → 0 |
| IoU(truth, pred) | 预测框与真实框的交并比 | [0, 1] |
训练时:confidence 的 label 按 $\text{Pr(Object)} \times \text{IoU}$ 计算——有物体时 Pr=1,label=IoU;无物体时 Pr=0,label=0。且 IoU 是动态变化的(每轮预测框都在变)。
推理时:网络直接输出一个 confidence 值。不需要也不可能分别算出 Pr(Object) 和 IoU(因为没有 GT 框!)。网络在训练中学会了直接输出这个乘积值——它隐含地包含了”有没有物体”和”框得准不准”两重信息。
💡 类比:训练时你教网络”conf = 有物体×IoU”这个公式,测试时网络直接吐出答案,你不需要拆开看中间步骤。
4.6 Class-Specific Confidence Score
这是最终用于 NMS 筛选的分数。
每个网格只预测一组类别概率(不管预测几个 BBox),即 $P(Class_i|Object)$。
$$\text{Score} = P(Class_i|Object) \times \text{confidence}$$
$$= P(Class_i|Object) \times \text{Pr(Object)} \times \text{IoU}_{pred}^{truth}$$
1 | 具体计算: |
每个网格产生: 2×20 = 40 个分数(2个BBox × 20类)
整张图产生: 49×2×20 = 1960 个分数
五、IoU 计算方法
5.1 IoU 定义
IoU (Intersection over Union) = 交并比,衡量两个矩形框的重叠程度。
$$\text{IoU} = \frac{\text{Area of Intersection}}{\text{Area of Union}} = \frac{A \cap B}{A \cup B}$$
5.2 几何图解
1 | ┌──────────┐ |
5.3 计算步骤
1 | 输入: BoxA(x1,y1,x2,y2), BoxB(x1,y1,x2,y2) |
5.4 IoU 在 YOLO 中的两个用途
| 用途 | 何时 | 说明 |
|---|---|---|
| 训练时选 BBox | 反向传播 | 找出与 GT 框 IoU 最大的预测框,该框负责位置损失 |
| NMS 去重 | 推理后处理 | 对同一类别的重叠框,IoU 高则移除得分低的 |
六、NMS(非极大值抑制)处理流程
NMS 的目标:从大量重叠的预测框中,每个物体只保留一个最好的框。
6.1 逐类别 NMS 六步法
1 | 输入: 所有 BBox (每框含:类别、得分、坐标) |
6.2 NMS 效果示意
1 | 处理前 (3个重叠框都预测 "dog"): |
七、训练阶段(反向传播)详解
7.1 训练参数设定
| 参数 | 值 | 含义 |
|---|---|---|
| S | 7 | 网格 7×7 |
| B | 2 | 每网格预测 2 个 BBox |
| C | 20 | PASCAL VOC 20 类 |
| 输入尺寸 | 448×448 | 固定分辨率 |
| 输出张量 | 7×7×30 | $S \times S \times (5B + C)$ |
7.2 输出张量结构
1 | 7×7×30 张量的 30 个通道拆解: |
7.3 检测框分配机制 —— 最难理解的核心 🔥🔥🔥
这是 YOLOv1 最绕、也最关键的概念。一句话总结:
物体中心落在哪个网格 → 该网格”负责”这个物体 → 该网格的两个 BBox 中 IoU 最大的那个”代表”这个网格去预测。
⭐ 前置理解:从”训练”和”测试”两个阶段分别看
很多人第一次读 YOLO 都会问:“凭什么物体的中心落在这个 cell,它就负责预测?”
答案是:这不是网络自己决定的,而是训练阶段你通过标签”教”它的。
1 | 训练阶段(你教网络): |
💡 这和教小孩认东西一样:你指着苹果说”这是苹果”(训练),小孩以后看到苹果就知道说”苹果”(测试)。”中心落在哪个 cell”只是你给训练样本贴标签的规则,不是网络自己悟出来的魔法。
第一步:网格分配 —— 谁”拥有”这个物体?
1 | 规则: 只看物体的几何中心点落在哪个网格里。 |
关键推论:
- ✅ 一个网格可以”拥有”多个物体吗? → 不可以(v1 的限制:每格只预测一组类别)
- ✅ 一个物体可以被多个网格”拥有”吗? → 不可以(中心点只落在一个格子里)
- ✅ 物体横跨多个网格怎么办? → 只看中心点,中心在哪格就归哪格
第二步:BBox 分配 —— 谁来”干活”?
1 | 网格(3,2) 有两个 BBox: |
这时:
- Box2 的 (x,y,w,h) 参与位置损失,努力学习去拟合 GT 框
- Box1 的 (x,y,w,h) 不参与位置损失,但 confidence 被监督为 0(”你预测得不好”)
- Box2 的 confidence 被监督为 当前 IoU=0.7(”你的 confidence 应该反映你的 IoU”)
第三步:完整分配链
1 | 图片 (448×448) |
具体例子:一张图的完整分配
1 | 假设一张训练图片中有 3 个物体: 狗(中心在格(3,2))、车(中心在格(5,4))、人(中心在格(1,6)) |
为什么要这样设计?
| 问题 | 如果不这样设计会怎样? | YOLO 的设计 |
|---|---|---|
| 两个 BBox 都参与位置损失? | 两个框争抢同一个物体,互相干扰 | 只让 IoU 最大的那个学习,另一个”退让” |
| 所有网格都预测类别? | 背景网格被迫输出假的物体类别 | 无物体的网格只监督 confidence→0 |
| 类别直接共享? | 一个网格两个框预测不同类,NMS 后可能留两个同类框 | 共享类别 + 选最佳框 = 每格最多输出 1 个物体 |
两个灵魂追问 🔥
理解了上面的分配机制后,自然会产生两个问题。
追问一:如果两个 BBox 各有独立的类别预测,同个 cell 能预测两个物体吗?
1 | 假设每个 BBox 独立预测类别(不共享): |
这也是为什么 YOLOv2 要引入 Anchor:Anchor 的形状天然决定了”谁更适合预测哪个物体”——细长的 Anchor 适合人,宽扁的 Anchor 适合车。有了这个分配依据,每个 Anchor 才能独立预测不同的类别。
追问二:既然一个 cell 只能预测一个物体,为什么要设计 2 个 BBox?
1 | 答案: "找两个人并行干活,在线选做得更好的那个。" |
这个设计的核心缺点
1 | 问题场景: 两个物体的中心落在同一个网格 |
7.4 训练时的 Ground Truth 构造
基于以上分配机制,构造 7×7×30 的 target 张量:
1 | 对于坐标为 (i, j) 的网格: |
7.5 训练时的 Confidence 计算细节
这是一个关键细节:训练时 confidence 的 ground truth 是动态变化的!
| 情况 | Confidence GT 值 | 说明 |
|---|---|---|
| 有物体的网格 + IoU 最大的 BBox | $\hat{C}i = \text{IoU}{pred}^{truth}$ | 训练时不断用最新预测框与 GT 计算 IoU! |
| 有物体的网格 + 另一个 BBox | $\hat{C}_i = 0$ | 不参与位置损失,confidence也不学习 |
| 无物体的网格 + 两个 BBox | $\hat{C}_i = 0$ | 两个框的 confidence 都应趋于 0 |
⚠️ 注意:有物体的网格中,$\hat{C}_i$ = 当时的 IoU 值。每轮训练预测的 BBox 位置都在变,所以 IoU 也在变,Confidence 的 ground truth 是动态的!
7.6 哪些 BBox 参与哪种 Loss?
这是 YOLOv1 最容易搞混的地方,总结如下:
1 | 对于一个有物体的网格 (i, j): |
7.7 损失函数完整解析
$$\mathcal{L} = \underbrace{\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{1}_{ij}^{obj}\left[(x_i-\hat{x}_i)^2+(y_i-\hat{y}i)^2\right]}{\text{① 有obj网格中最佳BBox的中心坐标损失}}$$
$$+ \underbrace{\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{1}_{ij}^{obj}\left[(\sqrt{w_i}-\sqrt{\hat{w}_i})^2+(\sqrt{h_i}-\sqrt{\hat{h}i})^2\right]}{\text{② 有obj网格中最佳BBox的宽高损失,对w,h取平方根}}$$
$$+ \underbrace{\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{1}_{ij}^{obj}(C_i-\hat{C}i)^2}{\text{③ 有obj网格中所有BBox的置信度损失,}\hat{C}i=\text{IoU}{pred}^{truth}}$$
$$+ \underbrace{\lambda_{noobj}\sum_{i=0}^{S^2}\sum_{j=0}^{B}\mathbb{1}_{ij}^{noobj}(C_i-\hat{C}i)^2}{\text{④ 无obj网格中所有BBox的置信度损失,}\hat{C}_i=0}$$
$$+ \underbrace{\sum_{i=0}^{S^2}\mathbb{1}{i}^{obj}\sum{c \in classes}(p_i(c)-\hat{p}i(c))^2}{\text{⑤ 有obj网格的类别概率损失}}$$
⚠️ 一个容易被忽略的细节:注意第⑤项——类别损失用的也是均方误差(L2 Loss),而不是分类任务中常见的交叉熵。
这意味着 YOLOv1 在数学上彻底把目标检测变成了回归问题——位置是回归、置信度是回归、连类别预测都是回归(输出连续概率值,用 L2 监督)。
这在当时是不寻常的(分类用交叉熵是常识),也是 YOLOv1 损失函数比较”粗糙”的原因之一。后续版本(v3)就把分类改回了交叉熵。
7.8 损失函数权重设计原理
| 参数 | 值 | 原因 |
|---|---|---|
| λ_coord = 5 | 定位权重 | 定位比分类更重要,增加梯度信号 |
| λ_noobj = 0.5 | 无目标惩罚权重 | 49个网格中大多数没有物体,不惩罚太狠避免训练发散 |
| w,h 取平方根 | 非线性映射 | 小框 1像素偏差比大框 1像素偏差影响更大,平方根缩小差异 |
为什么 w,h 要用平方根?
1 | 大框: w=100, δ=5 → 误差贡献 ∝ 5² = 25 |
平方根让小框的定位偏差被更大惩罚,符合人类直觉:在小物体上偏一点比在大物体上偏一点更不能接受。
7.9 训练流程伪代码
1 | # 简化版 YOLOv1 训练循环 |
八、优缺点深度分析
优点 ✅
- 极快: 45 FPS (标准) / 150 FPS (极速版),真正的实时检测
- 背景误检率低: 全局推理看整张图,不像 R-CNN 只看候选区域 → 背景误检率仅 4.75% (Fast R-CNN 13.6%)
- 迁移能力强: 泛化到艺术品检测等非自然图像领域效果远超 DPM 和 R-CNN
- Pipeline 简单: 统一回归,端到端训练
局限 ❌
- 输入尺寸固定: FC 层要求固定分辨率(448×448),不能处理任意尺寸
- 每格最多检测 1 个物体: 每个网格只预测一组类别,密集小物体(鸟群、人群)效果差
- 小物体定位差: 平方根缓解但不根治,小物体定位精度不如大物体
- 泛化弱: 对不常见角度/长宽比的目标表现不佳
- 损失函数粗糙: 分类也用 SSE,不如交叉熵合理(v3 改进)
九、性能表现
| 指标 | YOLOv1 | Fast R-CNN | Faster R-CNN |
|---|---|---|---|
| 速度 | 45 FPS / 150 FPS | ~0.5 FPS | 7 FPS |
| mAP (VOC 2007) | 63.4% | 70.0% | 73.2% |
| mAP (VOC 2012) | 57.9% | — | 70.4% |
| 背景误检率 | 4.75% | 13.6% | — |
十、训练配置 (yolo-master From Scratch)
📦 参考: [[30.resources/yolo-master/docs/Pytorch_YoLo_From_Scratch/v1/YOLOv1.ipynb|YOLOv1 Notebook]]
| 配置项 | 值 | 说明 |
|---|---|---|
| 数据集 | PASCAL VOC 2007+2012 | train/val 2007 + train/val 2012 |
| 测试集 | VOC 2007 test | — |
| Batch Size | 64 | ~14GB 显存 |
| Epochs | 156 | — |
| 单 Epoch 耗时 | ~140s | — |
| 总训练时间 | ~6h | — |
| 优化器 | SGD + Momentum | 动量 0.9 |
| 学习率调度 | 分段衰减 | 前75 epoch: 1e-2, 后30: 1e-3, 最后: 1e-4 |
| 权重衰减 | 5e-4 | L2 正则化 |
| mAP 最终 | 79.4% (VOC) | 对齐官方 darknet 实现 |
Notebook 结构:
1 | 数据准备: VOC下载 → 格式转换 → DataLoader |
十一、相关链接
- 📝 [[YOLO 系列总览]] — 版本对比总览
- 📝 [[YOLO 核心概念]] — Grid/Anchor/IoU/NMS 详解
- 📝 [[YOLOv2 详解]] — 锚框机制 + BN 改进
- 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/YOLO学习笔记1——YOLOv1详解]] — 知乎原文 (rtfff)
- 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/YOLO系列算法精讲:从yolov1至yolov8的进阶之路(2万字超全整理)-CSDN博客]] — CSDN 原文 (AI菌)
- 📦 [[30.resources/yolo-master/docs/Pytorch_YoLo_From_Scratch/v1/README|v1 From Scratch 教程]]