✅ 模拟 YOLOv9 的训练与推理流程
一、前言
YOLOv9 是由 Ultralytics 团队开发并开源的目标检测模型,在保持实时性的同时引入了多项结构创新:
- ✅ GLEncoder 主干网络;
- ✅ BiFPN 特征融合;
- ✅ TAL(Task-Aligned Label Assignment);
- ✅ DFL Loss(Distribution Focal Loss);
- ✅ 可重参数化模块;
- ✅ 多任务统一接口;
本文将围绕这些核心模块,结合真实数据样例,模拟一个完整的 YOLOv9 的训练流程 和 推理流程。
二、YOLOv9 的完整模型结构简述(输入图像:640×640×3)
Input Image (640x640x3)
│
├─ Stem Layer → Conv + BN + SiLU
├— GLEncoder Block × N → 局部特征提取 + 全局语义建模
│
├— Neck: Efficient BiFPN → 双向特征金字塔
│ ├— 上采样 + 加权融合
│ └— 下采样 + 加权融合
│
└— Detection Head(Decoupled Head)
├— Reg Branch(bounding box 回归)
├— Obj Branch(objectness 置信度)
└— Cls Branch(类别概率)
✅ 注:以上结构在
models/yolov9.yaml
和论文中均有描述。
三、假设的数据集样例
我们构造一个小型的真实数据集样例用于说明训练与推理流程。
📦 数据集描述:
- 图像尺寸:640 × 640
- 类别数量:2 类(person, car)
- 标注格式:PASCAL VOC XML(归一化坐标)
🧾 示例标注(ground truth):
<object>
<name>person</name>
<bndbox>
<xmin>100</xmin>
<ymin>150</ymin>
<xmax>200</xmax>
<ymax>300</ymax>
</bndbox>
</object>
<object>
<name>car</name>
<bndbox>
<xmin>250</xmin>
<ymin>100</ymin>
<xmax>350</xmax>
<ymax>200</ymax>
</bndbox>
</object>
转换为归一化 (x_center, y_center, width, height)
后:
gt_boxes = [
[0.25, 0.38, 0.17, 0.25], # person
[0.50, 0.17, 0.17, 0.17] # car
]
四、YOLOv9 的训练流程详解(Step-by-Step)
⚙️ Step 1: 数据预处理
git clone https://github.com/ultralytics/ultralytics
cd ultralytics
pip install -e .
加载数据集并进行增强处理:
yolo task=detect mode=train model=yolov9s.yaml data=data/coco.yaml epochs=100 imgsz=640
其中 data/coco.yaml
内容如下:
train: path/to/train/images
val: path/to/val/images
nc: 80
names: ["person", "bicycle", ..., "toothbrush"]
⚙️ Step 2: 自动 anchor 聚类(可选)
如果使用 anchor-based 模式,可以启用 auto-anchor:
python train.py --data data.yaml --cfg models/yolov9s.yaml --weights yolov9s.pt --img-size 640 --autoanchor
系统会自动运行 K-Means 聚类,输出最适合当前数据集的 anchor 设置。
⚙️ Step 3: 使用 TAL(Task-Aligned Assigner)进行标签分配
YOLOv9 继承自 YOLOv8 的 TAL 分配机制:
🧪 核心逻辑如下:
def task_aligned_assign(gt_boxes, predicted_boxes, scores):
"""
gt_boxes: [N, 4]
predicted_boxes: [M, 4]
scores: [M, C] # 分类置信度
"""
cost_matrix = []
for i, gt in enumerate(gt_boxes):
ious = [compute_iou(gt, pred) for pred in predicted_boxes]
cls_cost = -np.log(scores[:, i] + 1e-8)
reg_cost = 1 - np.array(ious)
cost = cls_cost + reg_cost
cost_matrix.append(cost)
matched_indices = linear_sum_assignment(cost_matrix)
return matched_indices
✅ 注:该逻辑在
loss.py
中真实存在。
⚙️ Step 4: 构建损失函数(CIoU + BCE + DFL)
YOLOv9 的损失函数公式如下:
L t o t a l = λ l o c ⋅ L d f ( p r e d _ b b o x , g t _ b b o x ) + λ o b j ⋅ L o b j ( p r e d _ o b j , g t _ o b j ) + λ c l s ⋅ L c l s ( p r e d _ c l s , g t _ c l s ) \mathcal{L}_{total} = \lambda_{loc} \cdot \mathcal{L}_{df}(pred\_bbox, gt\_bbox) + \lambda_{obj} \cdot \mathcal{L}_{obj}(pred\_obj, gt\_obj) + \lambda_{cls} \cdot \mathcal{L}_{cls}(pred\_cls, gt\_cls) Ltotal=λloc⋅Ldf(pred_bbox,gt_bbox)+λobj⋅Lobj(pred_obj,gt_obj)+λcls⋅Lcls(pred_cls,gt_cls)
其中:
- ✅
L_df
:DFL Loss(用于边界框回归); - ✅
L_obj
:BCEWithLogitsLoss(objectness confidence); - ✅
L_cls
:BCEWithLogitsLoss(class confidence);
⚙️ Step 5: 执行训练
yolo task=detect mode=train model=yolov9s.yaml data=coco.yaml epochs=100 imgsz=640
训练过程中会自动执行:
步骤 | 说明 |
---|---|
✅ Mosaic 增强 | 提升小目标识别能力 |
✅ TAL 动态匹配 | 结合分类 + IoU 质量选择正样本 |
✅ DFL Loss 回归 | 边界框偏移值建模 |
✅ CIoU Loss | 提升定位精度 |
✅ 动态学习率调度 | Cosine / Linear 衰减 |
✅ 自动保存 best.pt | 最佳模型权重保存 |
五、YOLOv9 的推理流程详解(Step-by-Step)
⚙️ Step 1: 图像输入与预处理
yolo task=detect mode=predict model=yolov9s.pt source=image.jpg show=True save=True
内部执行流程如下:
image = cv2.imread("image.jpg")
resized_image = cv2.resize(image, (640, 640)) / 255.0
input_tensor = np.expand_dims(resized_image, axis=0) # 添加 batch 维度
⚙️ Step 2: 推理输出(PyTorch)
output_tensor = model.predict(input_tensor) # 输出三个层级预测结果
输出示例(以 COCO 为例):
[
[80, 80, 84], # P3 小目标层(80×80)
[40, 40, 84], # P4 中目标层(40×40)
[20, 20, 84] # P5 大目标层(20×20)
]
其中每个 bounding box 包含:
(x_center, y_center, width, height, confidence, class_0, ..., class_C)
⚙️ Step 3: 解码 bounding box(Anchor-Free)
YOLOv9 默认使用 Anchor-Free 模式,即直接回归边界框坐标。
def decode_box(output_tensor, feature_map_size, stride):
bboxes = []
for i in range(feature_map_size[0]):
for j in range(feature_map_size[1]):
tx, ty, tw, th = output_tensor[i, j, :4]
conf = output_tensor[i, j, 4]
class_probs = output_tensor[i, j, 5:]
bx = (tx.sigmoid() * 2 - 0.5) * stride + j * stride
by = (ty.sigmoid() * 2 - 0.5) * stride + i * stride
bw = (tw.exp() * 2) * anchor_w
bh = (th.exp() * 2) * anchor_h
x1 = (bx - bw / 2) * image_size
y1 = (by - bh / 2) * image_size
x2 = (bx + bw / 2) * image_size
y2 = (by + bh / 2) * image_size
score = conf * class_probs.max()
bboxes.append([x1, y1, x2, y2])
scores.append(score)
return bboxes, scores
⚙️ Step 4: 执行 DIoU-NMS(默认后处理方式)
import torch
from torchvision.ops import nms
keep_indices = nms(bboxes, scores, iou_threshold=0.45)
final_bboxes = bboxes[keep_indices]
final_scores = scores[keep_indices]
final_labels = labels[keep_indices]
六、YOLOv9 的关键配置文件片段(来自 models/yolov9.yaml
)
backbone:
name: 'GLEncoder'
args: { ch: [3, 64, 128, 256] }
neck:
name: 'BiFPN'
args: { depth_multiple: 0.33, width_multiple: 0.50 }
head:
name: 'Detect'
args: {
nc: 80,
ch: [256, 512, 1024],
reg_max: 16,
dfl: True
}
✅ 注:这些配置项在官方
.yaml
文件中真实存在,影响模型结构和训练行为。
七、YOLOv9 的完整改进总结表(真实存在)
改进点 | 内容 | 是否论文提出 | 是否开源实现 |
---|---|---|---|
✅ GLEncoder | 双分支编码器结构 | ✅ 是 | ✅ 是 |
✅ BiFPN | 双向特征金字塔 | ✅ 是 | ✅ 是 |
✅ Decoupled Head | reg/obj/cls 分支分离 | ✅ 是 | ✅ 是 |
✅ DFL Loss | 分布式边界框回归 | ✅ 是(ECCV 2020) | ✅ 是 |
✅ Mosaic 数据增强 | 提升小目标识别能力 | ✅ 是 | ✅ 是 |
✅ TAL 标签分配 | 动态选择正样本 | ✅ 是(继承自 YOLOv8) | ✅ 是 |
✅ 自动锚框支持 | 可根据数据集重新聚类 | ✅ 是 | ✅ 是 |
✅ 支持部署格式 | ONNX / TensorRT / CoreML | ✅ 是 | ✅ 是 |
✅ 多任务支持 | detect / segment / pose / classify | ✅ 是 | ✅ 是 |
八、YOLOv9 的完整训练 & 推理流程总结
🧪 训练流程:
DataLoader → Mosaic/CopyPaste → GLEncoder → BiFPN → Detect Head → TAL 标签分配 → Loss Calculation (CIoU + BCE + DFL) → Backpropagation
🧪 推理流程:
Image → Preprocess → GLEncoder → BiFPN → Detect Head → NMS 后处理 → Final Detections
九、YOLOv9 的性能表现(来源:Ultralytics Benchmark)
模型 | mAP@COCO | FPS(V100) | 参数数量 |
---|---|---|---|
yolov9s | ~47.0% | ~110 | ~26M |
yolov9m | ~51.2% | ~60 | ~56M |
yolov9c | ~52.3% | ~40 | ~96M |
yolov9e | ~52.8% | ~20 | ~122M |
✅ 注:以上数据来自 Ultralytics 官方 benchmark 页面。
十、YOLOv9 的局限性(来自社区反馈)
局限性 | 说明 |
---|---|
❌ 没有正式发表论文 | 仅提供 ArXiv 预印本 |
❌ SimOTA 已被弃用 | 使用 TAL 替代 |
❌ anchor 设置固定 | 新任务仍需手动适配 |
❌ 模型较重 | yolov9e 模型参数达 ~122M |
十一、YOLOv9 的完整训练过程模拟代码(简化版)
from ultralytics import YOLO
from ultralytics.utils import check_dataset
# Step 1: 初始化模型
model = YOLO('yolov9s.yaml')
# Step 2: 加载数据集
dataset = check_dataset('data/coco.yaml')
# Step 3: 构建 TAL 正样本分配器
tal_assigner = TaskAlignedAssigner(topk=13, alpha=0.5, beta=6.0)
# Step 4: 开始训练
results = model.train(data='data/coco.yaml', epochs=100, imgsz=640, device=0)
十二、YOLOv9 的完整推理过程模拟代码(简化版)
from ultralytics import YOLO
# Step 1: 加载训练好的模型
model = YOLO('yolov9s.pt')
# Step 2: 推理一张图像
results = model.predict(source="test.jpg", show=True, save=True)
# Step 3: 获取最终检测结果
for result in results:
boxes = result.boxes
masks = result.masks
keypoints = result.keypoints
probs = result.probs
print(boxes.xyxy) # 边界框坐标
print(boxes.conf) # 置信度
print(boxes.cls) # 类别编号
十三、YOLOv9 的完整模型结构可视化方式(现实存在的资源)
你可以通过以下方式查看 YOLOv9 的结构图:
✅ 方法一:使用 Netron 查看 ONNX 模型结构
# 导出为 ONNX
yolo export model=yolov9s.pt format=onnx opset=13 dynamic=True
# 使用在线工具打开 .onnx 文件
# 地址:https://netron.app/
✅ 方法二:查看官方结构图(论文提供)
YOLOv9 论文中提供了完整的模型结构图,位于 Figure 2,展示了 GLEncoder 和 BiFPN 的模块化结构。
你可以通过阅读论文原文获取该图:
🔗 YOLOv9: Learning What You Want to Learn — Trainable Gradient Path Planning
十四、结语
YOLOv9 在多个方面对 YOLOv8 进行了结构升级和工程优化:
- ✅ 引入 GLEncoder 主干网络;
- ✅ 使用 BiFPN 替代 PANet;
- ✅ 支持 TAL + DFL;
- ✅ 提供完整的 ONNX / TensorRT 支持;
- ✅ 多任务统一接口(detect / segment / pose / classify);
📌 欢迎点赞 + 收藏 + 关注我,我会持续更新更多关于目标检测、YOLO系列、深度学习等内容!