系列说明:本系列共四篇,本篇为第二篇,聚焦 YOLOv3 的多尺度检测架构,以及如何使用 LabelMe 工具完成自定义数据集的标注、格式转换与模型训练全流程。


一、YOLOv3 核心改进概览

论文:《YOLOv3: An Incremental Improvement》(2018)
作者:Joseph Redmon & Ali Farhadi

YOLOv3 相比 YOLOv2 没有颠覆性变化,但通过一系列精心设计的改进,在精度上大幅提升,尤其是对小目标的检测能力显著增强。

核心改进点:

改进点YOLOv2YOLOv3
主干网络Darknet-19Darknet-53(引入残差结构)
检测尺度单尺度(13×13)三尺度(13×13、26×26、52×52)
分类器Softmax(互斥)独立 Logistic(支持多标签)
先验框数量5 个9 个(分 3 组,每组 3 个)

二、Darknet-53:强大的特征提取骨干

2.1 架构设计

YOLOv3 使用 Darknet-53 作为骨干网络,其命名来自网络共有 53 个卷积层。相比 Darknet-19,它引入了 ResNet 中的残差连接(Residual Block):

输入 x
   │
   ├─ Conv(1×1) → Conv(3×3) → 特征输出
   │
   └─ shortcut(直连)
         ↓
      输出 = 特征输出 + x(残差相加)

残差连接的好处:

  • 有效缓解梯度消失问题;
  • 支持构建更深的网络;
  • 特征可以在层间自由流动。

在这里插入图片描述

图1:Darknet-53 由卷积模块(Conv Block)和残差块(Residual Block)交替构成

2.2 特征图尺寸变化

以 416×416 输入为例,经过 Darknet-53 后:

416×416 → 208×208 → 104×104 → 52×52 → 26×26 → 13×13

YOLOv3 从最后三个尺度(13×13、26×26、52×52)分别引出特征图用于检测。


三、多尺度检测:FPN 思想的应用

3.1 为什么需要多尺度检测?

  • 大目标:在下采样后的小分辨率特征图(13×13)上检测,感受野大;
  • 中等目标:在中等分辨率特征图(26×26)上检测;
  • 小目标:在高分辨率特征图(52×52)上检测,空间分辨率高,保留了更多细节。

这正是**特征金字塔网络(FPN)**的核心思路。

3.2 多尺度特征融合

YOLOv3 采用自顶向下的特征融合方式:

Darknet-53特征提取
        │
   13×13特征图 ──────────────────────────── 检测头①(大目标)
        │
  上采样 ×2 = 26×26 + concat(26×26浅层特征)
        │
   26×26特征图 ──────────────────────────── 检测头②(中目标)
        │
  上采样 ×2 = 52×52 + concat(52×52浅层特征)
        │
   52×52特征图 ──────────────────────────── 检测头③(小目标)

在这里插入图片描述

图2:YOLOv3 三尺度检测结构,融合不同层的特征信息

3.3 先验框分配策略

YOLOv3 通过 K-Means 聚类在 COCO 数据集上得到 9 种先验框,按尺寸分为三组分配给三个检测尺度:

检测尺度先验框尺寸适合检测目标
13×13(感受野最大)(116×90), (156×198), (373×326)大目标
26×26(中等感受野)(30×61), (62×45), (59×119)中等目标
52×52(感受野最小)(10×13), (16×30), (33×23)小目标

规律:大感受野对应大先验框,小感受野对应小先验框,感受野与先验框尺寸需要匹配。


四、YOLOv3 输出格式解析

每个检测头的输出张量形状为:

N×N×[3×(5+C)]N \times N \times [3 \times (5 + C)]N×N×[3×(5+C)]

其中:

  • N×NN \times NN×N:特征图分辨率;
  • 333:每个格子对应 3 个先验框;
  • 555:[x,y,w,h,objectness][x, y, w, h, \text{objectness}][x,y,w,h,objectness];
  • CCC:类别数(COCO 为 80,VOC 为 20)。

以 COCO 数据集为例:

  • 13×13×255(255 = 3×85 = 3×(5+80))
  • 26×26×255
  • 52×52×255

五、分类器改进:Logistic 替代 Softmax

5.1 Softmax 的局限性

Softmax 将所有类别的概率归一化,假设类别互斥(即一个目标只能属于一个类别)。但现实场景中存在类别重叠:

示例:一只猫,既是"猫"也是"动物",这两个标签同时成立。

Softmax 无法处理这类多标签(Multi-label)情况。

5.2 独立 Logistic 分类器

YOLOv3 为每个类别独立使用 Sigmoid(Logistic)函数:

P(classi)=σ(zi)=11+e−ziP(\text{class}_i) = \sigma(z_i) = \frac{1}{1 + e^{-z_i}}P(classi​)=σ(zi​)=1+e−zi​1​

每个类别的预测相互独立,通过设置阈值(如 0.5)判断目标是否属于该类别,天然支持多标签分类。

对比示例:

图像Softmax 输出Logistic 输出
猫猫:0.9, 狗:0.08, 动物:0.02猫:0.8, 狗:0.3, 动物:0.7
预测结果只预测"猫"预测"猫"和"动物"(均>0.5)

六、LabelMe 数据标注工具实战

在自定义数据集训练 YOLOv3 之前,需要对数据进行标注。本节介绍使用 LabelMe 完成完整的标注与训练流程。

6.1 常用数据标注工具对比

工具特点支持标注类型
LabelImg轻量快速,专注目标检测2D 矩形框
LabelMe功能全面,MIT 开源多边形、矩形、圆、线、点
CVAT企业级,支持视频多边形、骨架、分割等
RectLabelmacOS 专用,支持 YOLO 导出矩形框
VOTT微软出品,支持半自动标注矩形框

6.2 安装 LabelMe

# 方法一:pip 安装(推荐)
pip install labelme
pip install pyqt5
pip install pillow

# 方法二:从源码安装
git clone https://github.com/wkentaro/labelme.git
cd labelme
pip install -e .

安装完成后,在命令行输入以下命令启动:

labelme

在这里插入图片描述

图3:LabelMe 标注工具界面,支持多种标注类型

6.3 使用 LabelMe 进行标注

基本操作流程:

  1. 打开文件夹:File → Open Dir,选择待标注图像所在文件夹;
  2. 新建多边形/矩形:点击 Edit → Create Polygons 或 Create Rectangle;
  3. 绘制标注框:在图像上拖拽绘制框,松开鼠标后输入类别标签;
  4. 保存标注:Ctrl+S 保存,默认生成与图像同名的 .json 文件。

LabelMe JSON 格式示例:

{
  "version": "5.0.1",
  "flags": {},
  "shapes": [
    {
      "label": "cat",
      "points": [[100, 150], [300, 150], [300, 400], [100, 400]],
      "group_id": null,
      "shape_type": "rectangle",
      "flags": {}
    }
  ],
  "imagePath": "cat.jpg",
  "imageHeight": 600,
  "imageWidth": 800
}

在这里插入图片描述

图4:使用 LabelMe 对图像中的目标进行矩形框标注

6.4 JSON 格式转换为 YOLO 格式

LabelMe 默认输出 JSON 格式,需转换为 YOLO 训练所需的 .txt 格式。

YOLO 格式规范(每行一个目标):

<class_id> <x_center> <y_center> <width> <height>

所有坐标值均为相对图像尺寸的归一化值(0~1)。
在这里插入图片描述

转换脚本(位于 utils/ 文件夹):

import json
import os
from pathlib import Path

def labelme_json_to_yolo(json_path, label_map, output_dir):
    """
    将 LabelMe JSON 标注转换为 YOLO txt 格式
    
    Args:
        json_path: LabelMe 生成的 JSON 文件路径
        label_map: 类别名到 ID 的映射字典,如 {'cat': 0, 'dog': 1}
        output_dir: YOLO txt 文件输出目录
    """
    with open(json_path, 'r', encoding='utf-8') as f:
        data = json.load(f)
    
    img_w = data['imageWidth']
    img_h = data['imageHeight']
    
    yolo_lines = []
    for shape in data['shapes']:
        label = shape['label']
        if label not in label_map:
            continue
        
        class_id = label_map[label]
        points = shape['points']
        
        # 获取矩形框的最小外接矩形
        xs = [p[0] for p in points]
        ys = [p[1] for p in points]
        x_min, x_max = min(xs), max(xs)
        y_min, y_max = min(ys), max(ys)
        
        # 转换为 YOLO 格式(归一化)
        x_center = (x_min + x_max) / 2 / img_w
        y_center = (y_min + y_max) / 2 / img_h
        width = (x_max - x_min) / img_w
        height = (y_max - y_min) / img_h
        
        yolo_lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}")
    
    # 写入输出文件
    output_path = Path(output_dir) / (Path(json_path).stem + '.txt')
    with open(output_path, 'w') as f:
        f.write('\n'.join(yolo_lines))
    
    print(f"Converted: {json_path} → {output_path}")


# 使用示例
label_map = {'cat': 0, 'dog': 1, 'person': 2}
json_folder = 'data/raw_labels/'    # LabelMe 生成的 JSON 文件夹
output_dir = 'data/custom/labels/'  # YOLO 格式输出目录

os.makedirs(output_dir, exist_ok=True)
for json_file in Path(json_folder).glob('*.json'):
    labelme_json_to_yolo(str(json_file), label_map, output_dir)

在这里插入图片描述

图5:JSON 标签格式转换为 YOLO 所需的 txt 格式

6.5 配置模型文件

在这里插入图片描述
可以根据训练模型中对应的config和data去查看改成类似的形式。
第一步:生成自定义模型配置文件

使用官方提供的脚本生成适配自定义类别数的配置文件(需安装 Git):

# 在项目目录下右键 → Git Bash Here
# 2 代表自定义数据集的类别数(根据实际情况修改)
bash create_custom_model.sh 2

⚠️ 注意:每次运行前先删除旧的 yolov3-custom.cfg 文件,否则内容会追加到末尾导致配置错误。

第二步:修改类别名称文件

编辑 data/custom/classes.names,每行一个类别名称,顺序需与标注时一致:

cat
dog
person

第三步:配置数据路径文件

编辑 data/custom/custom.data:

classes = 2
train = data/custom/train.txt
valid = data/custom/val.txt
names = data/custom/classes.names
backup = checkpoints/

train.txt 和 val.txt 中写入对应图像的绝对路径,每行一条:

/path/to/dataset/images/img001.jpg
/path/to/dataset/images/img002.jpg
...

在这里插入图片描述

图6:数据配置文件结构与路径设置

6.6 训练与测试

训练命令:

# train.py 关键参数
python train.py \
    --model_def config/yolov3-custom.cfg \       # 模型配置文件
    --data_config config/custom.data \            # 数据配置文件
    --pretrained_weights weights/darknet53.conv.74  # 预训练权重(迁移学习)

测试/推理命令:

python detect.py \
    --image_folder data/samples/ \               # 测试图像文件夹
    --model_def config/yolov3-custom.cfg \       # 模型配置
    --checkpoint_model checkpoints/yolov3_ckpt_100.pth \  # 训练好的权重
    --class_path data/custom/classes.names       # 类别名称文件

6.7 完整工作流程图

原始图像
    ↓
LabelMe 标注(生成 JSON)
    ↓
json_to_yolo 格式转换(生成 txt)
    ↓
配置 classes.names / custom.data / train.txt / val.txt
    ↓
bash create_custom_model.sh <类别数>(生成 yolov3-custom.cfg)
    ↓
python train.py --model_def ... --data_config ... --pretrained_weights ...
    ↓
python detect.py --image_folder ... --checkpoint_model ...
    ↓
检测结果输出

七、YOLOv3 性能分析

7.1 与其他算法的对比(COCO 数据集)

算法mAP@50FPS特点
YOLOv248.140速度快,精度一般
YOLOv357.920精度大幅提升,速度适中
SSD51248.919精度与 YOLOv3 相近
RetinaNet61.15精度高,速度慢
Faster R-CNN55.77精度高,速度慢

7.2 YOLOv3 的局限性

  • 对密集小目标检测仍有不足;
  • 训练速度较慢(相比 v2 网络更深);
  • 没有充分利用注意力机制等现代技巧。

这些问题将在 YOLOv4 中进一步解决。


八、小结

本文详细介绍了:

  1. YOLOv3 的三大核心改进:Darknet-53 骨干、三尺度检测头、独立 Logistic 分类器;
  2. 9 种先验框的分配策略:大感受野对应大框,小感受野对应小框;
  3. 完整的自定义数据集训练流程:从 LabelMe 标注到 YOLO 格式转换再到模型训练。

掌握这套流程后,可以快速将 YOLOv3 应用到任何自定义目标检测任务中。下一篇将介绍 YOLOv4,它在 YOLOv3 基础上引入了更多先进技术,进一步突破了速度与精度的平衡极限。


参考资源

  1. Redmon, J., & Farhadi, A. “Yolov3: An incremental improvement.” arXiv 2018.
  2. LabelMe 官方仓库:https://github.com/wkentaro/labelme
  3. COCO 数据集介绍:https://blog.csdn.net/qq_44554428/article/details/122597358
    分类器;
  1. 9 种先验框的分配策略:大感受野对应大框,小感受野对应小框;
  2. 完整的自定义数据集训练流程:从 LabelMe 标注到 YOLO 格式转换再到模型训练。

更多推荐