YOLO11n 内部结构修改指南

本文由GPT-5.3-Codex·Medium撰写。

1. 先理解 YOLO11n 是如何被加载的

在这个仓库里,YOLO11n 不是单独一个 yolo11n.yaml 文件,而是通过 yolo11.yaml 里的 scales.n 缩放规则得到。

核心流程:

  1. 传入 model=yolo11n.yaml 或 yolo11n.pt
  2. 解析时会统一映射到 ultralytics/cfg/models/11/yolo11.yaml
  3. 从文件名中提取 scale=n
  4. 使用 parse_model() 按 YAML 的 backbone/head 构建网络

涉及关键代码:

  • ultralytics/nn/tasks.py
    • yaml_model_load()
    • guess_model_scale()
    • parse_model()
    • DetectionModel.init()

2. 改结构时通常要改哪些文件

A. 只改网络拓扑(不新增新算子)

只需要改:

  • ultralytics/cfg/models/11/yolo11.yaml

建议不要直接覆盖官方 yolo11.yaml,改为新文件,例如:

  • ultralytics/cfg/models/11/yolo11n_my.yaml

前提:你用到的模块(如 Conv、C2f、C3k2、SPPF、Detect)已经在 parse_model 中支持。

B. 新增自定义模块(新增算子/Block)

通常至少需要改 3 类文件:

  1. 模块实现文件
  • ultralytics/nn/modules/block.py
  • 或 ultralytics/nn/modules/conv.py
  • 或 ultralytics/nn/modules/head.py
  1. 模块导出文件
  • ultralytics/nn/modules/init.py
  1. 模型解析与注册
  • ultralytics/nn/tasks.py
    • 顶部 import 列表要导入新模块
    • parse_model() 里按模块类型处理参数
    • 若你的模块是 [c1, c2, …] 这种常规结构块,要加入 base_modules
    • 若你的模块支持 repeats,要加入 repeat_modules

C. 如果改了检测头输出定义

还要同步检查:

  • ultralytics/nn/modules/head.py
  • ultralytics/utils/loss.py
  • 推理后处理相关逻辑(如解码、维度约定)

否则可能出现训练正常但推理/导出异常,或 loss 维度不匹配。

3. 示例 1:只改 YAML(推荐先从这里开始)

目标:把骨干某层从 C3k2 改成 C2f,快速验证结构变更流程。

3.1 新建配置文件

复制:

  • ultralytics/cfg/models/11/yolo11.yaml
    到:
  • ultralytics/cfg/models/11/yolo11n_my.yaml

然后把其中一层改掉(示例):

1
2
3
4
5
# 原来
- [-1, 2, C3k2, [256, False, 0.25]]

# 修改后
- [-1, 2, C2f, [256, True]]

说明:

  • C2f 已被 parse_model 支持,通常不需要改 Python 源码。
  • 这是最稳妥的“改结构”方式,适合先做 baseline。

3.2 训练验证

1
yolo detect train model=ultralytics/cfg/models/11/yolo11n_my.yaml data=coco8.yaml imgsz=640 epochs=1

4. 示例 2:新增自定义模块 MyBlock

假设你要新增一个模块 MyBlock,并在 YAML 里使用它。

4.1 在模块文件中实现

在 ultralytics/nn/modules/block.py 增加:

1
2
3
4
5
6
7
8
class MyBlock(nn.Module):
def __init__(self, c1, c2, k=3):
super().__init__()
self.cv1 = Conv(c1, c2, k, 1)
self.cv2 = Conv(c2, c2, 3, 1)

def forward(self, x):
return self.cv2(self.cv1(x))

4.2 在导出文件注册

在 ultralytics/nn/modules/init.py:

  1. 从 block import MyBlock
  2. 把 MyBlock 加入 all

4.3 在 tasks.py 注册给解析器

在 ultralytics/nn/tasks.py:

  1. 顶部 from ultralytics.nn.modules import (…) 中加入 MyBlock
  2. 在 parse_model() 的 base_modules 集合加入 MyBlock
  3. 如果 MyBlock 支持 repeats 参数 n,再加入 repeat_modules

4.4 在 YAML 中使用

例如在 yolo11n_my.yaml 中:

1
- [-1, 1, MyBlock, [256, 3]]

5. 高风险注意事项(非常重要)

  1. 文件命名与 scale 自动识别
  • scale 是从文件名里识别的(n/s/m/l/x)。
  • 如果命名不规范,可能拿不到期望的缩放系数。
  • 推荐命名包含 n/s/m/l/x,例如 yolo11n_my.yaml。
  1. 通道数和拼接索引必须匹配
  • Concat 的输入层索引、输出通道必须和后续层对齐。
  • 一旦改动 backbone/head 任意层,后续引用索引都要复核。
  1. repeats 和参数顺序
  • parse_model 会对部分模块自动插入 repeats 到参数中。
  • 如果你的模块参数顺序设计不一致,会在构建时报错或 silent shape 错误。
  1. Detect 头改动要联动 loss 与后处理
  • 改检测头输出格式,不仅要改 head,还要改 loss 与 decode 逻辑。
  1. 预训练权重加载不是全量匹配
  • 改结构后常见现象是仅部分权重能迁移,这是正常的。
  • 需要关注日志里的 transferred items,确认是否符合预期。
  1. 先做最小可运行验证
  • 建议先用 coco8 做 1 个 epoch 烟雾测试,再跑正式训练。

6. 推荐修改流程(实践版)

  1. 先只改 YAML,确保能 train + val + predict。
  2. 再引入自定义模块,逐步加到 parse_model。
  3. 每改一步都做一次最小训练/推理验证。
  4. 最后再考虑导出(ONNX/TensorRT)兼容性测试。

7. 快速自检清单

  • 是否新建了独立 YAML(避免污染官方配置)
  • parse_model 是否能识别你的模块名
  • base_modules/repeat_modules 是否注册正确
  • Concat/Detect 的输入索引和通道是否一致
  • 训练、推理、导出至少各做过一次最小验证

PS:封面图来源:AiRomance