1. 环境搭建与模型转换基础
任务目标
本案例完成YOLOv5目标检测模型从PyTorch到ONNX的转换基础环境搭建,最终验证导出模型输出张量形状是否符合预期。核心交付物为:可运行的PyTorch环境、YOLOv5模型定义、ONNX导出脚本及形状验证逻辑。
环境依赖
- Python 3.8+,PyTorch 1.12+(CPU或CUDA版本均可)
- 安装依赖:
pip install torch onnx onnxruntime - 无需完整YOLOv5仓库,本案例使用最小模型定义完成导出验证
最小模型定义与导出
以下代码定义了一个与YOLOv5检测头输出结构一致的简化模型(输入640×640,输出3个尺度的预测张量),并完成ONNX导出与形状验证。该模型保留了YOLOv5输出张量的核心维度特征:[batch, 3*(5+num_classes), grid_h, grid_w]。
import torch
import torch.nn as nn
import onnx
import onnxruntime as ort
class ConvBlock(nn.Module):
def __init__(self, c_in, c_out, k=3, s=1):
super().__init__()
self.conv = nn.Conv2d(c_in, c_out, k, s, k//2, bias=False)
self.bn = nn.BatchNorm2d(c_out)
self.act = nn.SiLU()
def forward(self, x):
return self.act(self.bn(self.conv(x)))
class YOLOv5Mini(nn.Module):
"""最小YOLOv5结构:骨干+检测头,输出3个尺度特征图"""
def __init__(self, nc=80):
super().__init__()
self.backbone = nn.Sequential(
ConvBlock(3, 32, s=2), # 320×320
ConvBlock(32, 64, s=2), # 160×160
ConvBlock(64, 128, s=2), # 80×80
ConvBlock(128, 256, s=2),# 40×40
ConvBlock(256, 512, s=2) # 20×20
)
# 检测头:3个尺度,每个anchor预测5+nc个值
self.detect = nn.ModuleList([
nn.Conv2d(512, 3*(5+nc), 1), # P3: 80×80
nn.Conv2d(256, 3*(5+nc), 1), # P4: 40×40
nn.Conv2d(128, 3*(5+nc), 1) # P5: 20×20
])
# 上采样层用于特征融合(简化版)
self.upsample = nn.Upsample(scale_factor=2, mode='nearest')
def forward(self, x):
# 骨干网络逐层提取特征
c1 = self.backbone[0](x) # 320
c2 = self.backbone[1](c1) # 160
c3 = self.backbone[2](c2) # 80
c4 = self.backbone[3](c3) # 40
c5 = self.backbone[4](c4) # 20
# 检测头输出(简化:直接使用各层特征)
out = []
out.append(self.detect[0](c3)) # 80×80
out.append(self.detect[1](c4)) # 40×40
out.append(self.detect[2](c5)) # 20×20
return out
# 初始化模型
model = YOLOv5Mini(nc=80)
model.eval()
# 构造输入
dummy_input = torch.randn(1, 3, 640, 640)
# 导出ONNX
torch.onnx.export(
model,
dummy_input,
"yolov5_mini.onnx",
opset_version=12,
input_names=["images"],
output_names=["out_80", "out_40", "out_20"],
dynamic_axes={"images": {0: "batch"}}
)
# 验证ONNX模型完整性
onnx_model = onnx.load("yolov5_mini.onnx")
onnx.checker.check_model(onnx_model)
print("ONNX模型结构检查通过")
# 使用onnxruntime验证输出形状
ort_session = ort.InferenceSession("yolov5_mini.onnx")
ort_inputs = {ort_session.get_inputs()[0].name: dummy_input.numpy()}
ort_outputs = ort_session.run(None, ort_inputs)
# 预期输出形状验证
expected_shapes = [(1, 255, 80, 80), (1, 255, 40, 40), (1, 255, 20, 20)]
for i, (out, exp_shape) in enumerate(zip(ort_outputs, expected_shapes)):
assert out.shape == exp_shape, f"输出{i}形状{out.shape}不匹配预期{exp_shape}"
print(f"输出{i}形状验证通过: {out.shape}")
运行与验证
- 将上述代码保存为
export_yolov5.py,在项目目录下执行python export_yolov5.py - 程序依次完成:模型定义→ONNX导出→结构检查→推理形状验证
- 通过条件:控制台输出以下内容即判定成功:
ONNX模型结构检查通过输出0形状验证通过: (1, 255, 80, 80)输出1形状验证通过: (1, 255, 40, 40)输出2形状验证通过: (1, 255, 20, 20)
关键说明
- 输出通道数255 = 3×(5+80),对应3个anchor、5个框属性(x,y,w,h,conf)和80个类别
- 本案例使用简化模型验证导出链路,实际YOLOv5s模型结构更复杂,但导出流程与验证方法完全一致
- 若需导出完整YOLOv5s,可替换模型定义部分,其余导出与验证代码无需修改