1. PyTorch模型转换为ONNX基础
任务目标
将一个简单的PyTorch CNN分类模型转换为ONNX格式,并通过对比转换前后模型在相同输入下的输出,验证转换正确性。
环境依赖
- Python 3.8+
- PyTorch 2.0+(含 torch.onnx)
- ONNX Runtime(用于加载和推理ONNX模型)
安装命令:pip install torch onnxruntime
关键完整代码
import torch
import torch.nn as nn
import onnxruntime as ort
import numpy as np
# 1. 定义简单CNN模型
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(32 * 8 * 8, 128),
nn.ReLU(),
nn.Linear(128, num_classes),
)
def forward(self, x):
return self.classifier(self.features(x))
# 2. 初始化模型并设为推理模式
model = SimpleCNN()
model.eval()
# 3. 构造示例输入(batch=1, 3通道, 32x32)
dummy_input = torch.randn(1, 3, 32, 32)
# 4. 转换为ONNX格式
onnx_path = "simple_cnn.onnx"
torch.onnx.export(
model,
dummy_input,
onnx_path,
input_names=["input"],
output_names=["output"],
dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}}, # 支持动态batch
opset_version=17,
)
# 5. 使用ONNX Runtime加载并推理
ort_session = ort.InferenceSession(onnx_path)
ort_inputs = {ort_session.get_inputs()[0].name: dummy_input.numpy()}
ort_outputs = ort_session.run(None, ort_inputs)[0]
# 6. 对比PyTorch与ONNX输出
with torch.no_grad():
torch_output = model(dummy_input).numpy()
# 7. 验证一致性
max_diff = np.max(np.abs(torch_output - ort_outputs))
print(f"PyTorch输出形状: {torch_output.shape}")
print(f"ONNX输出形状: {ort_outputs.shape}")
print(f"最大误差: {max_diff:.6f}")
# 判定标准:误差小于1e-5即通过
assert max_diff < 1e-5, "转换验证失败:输出差异过大"
print("✓ 转换验证通过:PyTorch与ONNX输出一致")
运行与验证
- 将上述代码保存为
convert_to_onnx.py,直接运行:python convert_to_onnx.py - 程序执行后会在当前目录生成
simple_cnn.onnx文件。 - 控制台输出包含两个输出张量的形状以及最大误差值。
通过条件
- 程序无异常退出,且打印
✓ 转换验证通过。 - 最大误差小于
1e-5,说明ONNX模型与原始PyTorch模型在数值上保持一致。 - 生成的
simple_cnn.onnx文件可被ONNX Runtime正常加载(脚本第5步已隐式验证)。
常见问题
- 动态轴警告:若不需要动态batch,可删除
dynamic_axes参数,模型将固定为batch=1。 - opset版本过低:若环境中ONNX Runtime版本较旧,可将
opset_version降至11或13。 - 误差过大:检查模型是否包含训练/推理模式差异(如Dropout、BatchNorm),确保已调用
model.eval()。