4. YOLO 配置文件解析:.cfg 文件结构、网络层参数与锚点框机制

配置文件,说白了就是 YOLO 的「骨架说明书」。我刚开始接触 YOLO 时,总觉得调代码比调 cfg 重要。后来有一次模型怎么都收敛不了,折腾了两天,最后发现是 cfg 里一个卷积层的步长写错了。嗯,从那以后我再也不敢小看这个文件了。

今天我们就把它彻底拆开看看。

4.1 .cfg 文件整体结构

一个标准的 YOLO .cfg 文件,其实就是一个纯文本的层描述列表。每一段以 [xxx] 开头,后面跟着该层的参数键值对。YOLO 的 Darknet 框架会按顺序逐层解析,构建出完整的计算图。

我个人习惯把 cfg 文件分成三大块:

  • 网络头:定义输入尺寸、训练参数等全局信息
  • 特征提取器:一堆卷积、残差、下采样层,负责把图片变成特征图
  • 检测头:YOLO 层 + 上采样 + 路由层,负责输出检测结果

来看一个典型的 cfg 开头:

[net]
batch=64
subdivisions=16
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
learning_rate=0.001
burn_in=1000
max_batches=500200
policy=steps
steps=400000,450000
scales=.1,.1

这里有个坑,我曾经踩过——subdivisions 这个参数。它把 batch 拆成小份,每份喂给 GPU。如果你的显存不够,调大 subdivisions 就行。但注意,它会影响 BN 层的统计效果,调太大模型会变「毛」。

4.2 核心网络层参数详解

卷积层(Convolutional)

卷积层是 YOLO 的主力。每个卷积层都长这样:

[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky

几个关键参数:

参数 含义 我的经验
filters 输出通道数 一般 8 的倍数,方便内存对齐
size 卷积核大小 3x3 最常用,1x1 用来降维
stride 步长 stride=2 时特征图尺寸减半
pad 是否填充 pad=1 且 size=3 时,输出尺寸不变
batch_normalize 是否接 BN 层 YOLOv3 之后几乎必开
小技巧: 如果你要自己改网络,记住一个公式:输出尺寸 = (输入尺寸 + 2×pad - size) / stride + 1。pad=1, size=3, stride=1 时,输出尺寸 = 输入尺寸。这个组合最常用。

上采样层(Upsample)

上采样层的作用很简单——把特征图放大。YOLO 用它来做多尺度融合。

[upsample]
stride=2

就这么两行。stride=2 表示把特征图宽高各放大 2 倍。实现方式默认是最近邻插值,速度快,但会有锯齿。我在项目中试过换成双线性插值,精度提升微乎其微,速度却慢了 10%,所以还是用默认吧。

YOLO 层(检测层)

这是整个 cfg 的核心。它定义了模型「怎么检测」:

[yolo]
mask = 0,1,2
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=80
num=9
jitter=.3
ignore_thresh=.7
truth_thresh=1
random=1

参数解读:

  • classes:检测类别数,COCO 是 80,VOC 是 20
  • num:每个网格预测的锚点框总数,一般是 9
  • mask:当前 YOLO 层使用的锚点框索引,后面细讲
  • anchors:所有锚点框的宽高,共 9 组
  • ignore_thresh:忽略阈值,低于此值的负样本不参与 loss 计算
  • jitter:数据增强时,随机裁剪的抖动幅度
注意: ignore_thresh 这个值很敏感。设太大(如 0.7),很多模糊样本会被忽略,模型学不到边界情况;设太小(如 0.3),负样本太多,模型容易「摆烂」。我一般从 0.5 开始调。

4.3 锚点框(Anchor Boxes)计算

锚点框是什么?说白了就是一组预设的「框模板」。YOLO 不直接预测框的宽高,而是预测相对于这些模板的偏移量。

锚点框怎么来的?用 K-Means 聚类。我在项目中写过这个脚本,核心逻辑是这样的:

# 伪代码:K-Means 聚类锚点框
def kmeans_anchors(boxes, k=9):
    # boxes: 所有标注框的宽高 (N, 2)
    # 距离度量用 IoU,不用欧氏距离
    # 因为大框的欧氏距离天然大,聚类会偏向大框
    anchors = random_choose(boxes, k)
    for _ in range(max_iter):
        # 计算每个 box 到每个 anchor 的 IoU 距离
        distances = 1 - iou(boxes, anchors)
        # 分配每个 box 到最近的 anchor
        labels = argmin(distances, axis=1)
        # 更新 anchor 为同类 box 的中位数
        for i in range(k):
            anchors[i] = median(boxes[labels == i])
    return anchors

聚类出来的锚点框,YOLOv3 用的是 9 组:

索引 宽 高 适用目标
0 10 13 极小目标(如小动物)
1 16 30 小目标
2 33 23 小目标(方形)
3 30 61 中等目标
4 62 45 中等目标(宽)
5 59 119 中等目标(高)
6 116 90 大目标
7 156 198 大目标(高)
8 373 326 极大目标

你想想看,为什么是 9 组?因为 YOLOv3 有 3 个检测尺度,每个尺度分配 3 组锚点框。小尺度特征图(13x13)负责大目标,用大锚点框;大尺度特征图(52x52)负责小目标,用小锚点框。

4.4 Mask 机制

Mask 机制,说白了就是「分家」。每个 YOLO 层只负责 3 组锚点框,mask 告诉它用哪 3 组。

看这个配置:

# 第一个 YOLO 层(大目标,13x13 特征图)
[yolo]
mask = 6,7,8
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326

# 第二个 YOLO 层(中等目标,26x26 特征图)
[yolo]
mask = 3,4,5

# 第三个 YOLO 层(小目标,52x52 特征图)
[yolo]
mask = 0,1,2

为什么这么分?我画个图你就明白了。

YOLOv3 多尺度检测与 Mask 分配 输入 416x416 Darknet-53 13x13 特征图 mask=6,7,8 26x26 特征图 mask=3,4,5 52x52 特征图 mask=0,1,2 关键逻辑: • 大尺度特征图(13x13)感受野大,适合检测大目标 • 中尺度特征图(26x26)感受野适中,适合检测中等目标 • 小尺度特征图(52x52)感受野小,适合检测小目标 • Mask 机制将 9 组锚点框按大小分配到 3 个尺度上 • 每个网格预测 3 个框,三个尺度共 13x13x3 + 26x26x3 + 52x52x3 = 10,647 个预测框

Mask 机制的好处很明显——每个 YOLO 层只关注自己「擅长」的目标尺寸。大特征图上的锚点框大,小特征图上的锚点框小,各司其职。

核心理解: 锚点框不是模型学出来的,而是从数据里聚类出来的。Mask 是分配策略,告诉每个检测层「你管哪几个框」。这两个机制配合,YOLO 才能做到「一个网络检测所有尺度的目标」。

4.5 实战:如何修改 cfg 适配自己的数据集

假设你要用自己的数据集训练 YOLO,需要改 cfg 里的几个地方:

  1. 修改 classes:把 classes=80 改成你的类别数,比如 classes=5
  2. 修改 filters:YOLO 层前面的卷积层,filters 必须等于 (classes + 5) × 3。5 是 x, y, w, h, confidence 这 5 个值。比如 5 类目标,filters = (5+5)×3 = 30
  3. 重新聚类锚点框:用你的数据集跑一遍 K-Means,把新的锚点框写进 cfg
  4. 调整 max_batches:一般设为 类别数 × 2000,但最少不低于 6000

我曾经帮一个做工业质检的朋友调 cfg,他检测的是电路板上的微小焊点。默认的锚点框太大,小焊点根本检测不到。重新聚类后,锚点框集中在 (5,5) 到 (20,20) 之间,mAP 直接从 0.3 飙到 0.85。所以说,锚点框一定要跟你的数据匹配。

常见错误: 改了 classes 忘了改 filters。YOLO 层前面的卷积层 filters 不对,模型直接报错。我见过有人 debug 了一整天,最后发现是 filters 少算了 3。记住公式:filters = (classes + 5) × 3。

好了,cfg 文件的核心内容就这些。你只要掌握了网络层参数、锚点框计算和 Mask 机制,就能看懂任何 YOLO 版本的配置文件。下次遇到模型效果不好,别急着调代码,先打开 cfg 看看——问题可能就藏在这里。