从ResNet18到ONNX再到.wts:一站式模型转换与权重导出指南
2026/4/6 11:17:56 网站建设 项目流程
1. 从零开始获取ResNet18预训练模型第一次接触模型转换时最让我头疼的就是如何正确获取预训练模型。ResNet18作为经典的图像分类网络在PyTorch中获取它其实非常简单。记得我第一次尝试时因为没搞清楚pretrained参数的作用白白浪费了半天时间。PyTorch官方提供了完整的模型库我们只需要几行代码就能下载ResNet18。这里有个小技巧建议在下载前先检查CUDA是否可用这样可以确保模型直接加载到GPU上。我经常看到新手忘记这一步导致后续转换过程特别慢。import torch import torchvision # 检查GPU可用性 print(可用GPU数量:, torch.cuda.device_count()) # 下载预训练模型 model torchvision.models.resnet18(pretrainedTrue) model model.to(cuda:0) # 转移到GPU model.eval() # 设置为评估模式 # 测试模型运行 test_input torch.ones(1, 3, 224, 224).to(cuda:0) output model(test_input) print(输出形状:, output.shape) # 保存完整模型 torch.save(model, resnet18.pth)保存模型时有两个选择可以只保存权重(.pth)也可以保存整个模型结构。我建议新手保存完整模型这样后续加载时不需要重新定义网络结构。不过要注意这种方式保存的模型文件会比较大在我的测试中完整ResNet18大约有45MB。2. PyTorch到ONNX模型转换详解ONNXOpen Neural Network Exchange格式是模型部署的重要中间件。第一次成功转换模型时那种成就感我现在还记得。但转换过程中有几个坑需要特别注意我在这里分享一下自己的经验。转换的核心是torch.onnx.export函数它有多个重要参数model要转换的PyTorch模型args模型的输入样例f输出的ONNX文件名training模型状态重要import torch def convert_to_onnx(): # 加载之前保存的模型 model torch.load(resnet18.pth) model.eval() # 必须设置为评估模式 # 创建虚拟输入 dummy_input torch.randn(1, 3, 224, 224) device torch.device(cuda if torch.cuda.is_available() else cpu) dummy_input dummy_input.to(device) # 关键转换步骤 torch.onnx.export( model, dummy_input, resnet18.onnx, input_names[input], output_names[output], dynamic_axes{ input: {0: batch_size}, output: {0: batch_size} }, opset_version11 ) if __name__ __main__: convert_to_onnx()转换完成后强烈建议用ONNX Runtime验证一下模型是否正常工作。我遇到过好几次转换成功但推理结果不对的情况都是靠这个检查发现的import onnxruntime as ort import numpy as np # 创建ONNX Runtime会话 ort_session ort.InferenceSession(resnet18.onnx) # 准备输入数据 inputs np.random.randn(1, 3, 224, 224).astype(np.float32) # 运行推理 outputs ort_session.run( None, {input: inputs} ) print(ONNX模型输出:, outputs[0].shape)3. 深入理解.wts权重文件格式.wts格式在TensorRT等推理引擎中很常见它实际上是一个纯文本文件记录了模型的所有权重。第一次看到这个格式时我觉得它既简单又实用特别适合在不同平台间传递模型参数。.wts文件的结构很有规律第一行是权重参数的总数之后每行记录一个权重张量包含参数名参数值的数量十六进制编码的参数值import torch import struct def export_to_wts(): # 加载模型 model torch.load(resnet18.pth) model.eval() # 创建.wts文件 with open(resnet18.wts, w) as f: # 写入参数总数 state_dict model.state_dict() f.write(f{len(state_dict)}\n) # 遍历所有参数 for name, param in state_dict.items(): print(f处理参数: {name}, 形状: {param.shape}) # 展平参数值 param_data param.cpu().detach().numpy().flatten() # 写入参数信息 f.write(f{name} {len(param_data)}) # 以十六进制格式写入每个参数值 for value in param_data: hex_value struct.pack(f, float(value)).hex() f.write(f {hex_value}) f.write(\n) if __name__ __main__: export_to_wts()理解.wts文件的结构后我们可以很容易地实现其他框架的模型加载。比如在C中可以这样读取.wts文件#include fstream #include vector #include string std::mapstd::string, std::vectorfloat load_wts(const std::string file) { std::ifstream infile(file); std::mapstd::string, std::vectorfloat weight_map; int count; infile count; for(int i0; icount; i) { std::string name; int size; infile name size; std::vectorfloat values(size); for(int j0; jsize; j) { std::string hex_val; infile hex_val; // 十六进制转float // ... } weight_map[name] values; } return weight_map; }4. 常见问题与解决方案在实际项目中转换模型时我遇到过各种奇怪的问题。这里总结几个最常见的错误和解决方法希望能帮你少走弯路。问题1ONNX模型输出与PyTorch不一致这是最让人头疼的问题。可能原因有忘记调用model.eval()输入数据没有正确归一化ONNX opset版本不兼容解决方法# 确保模型在评估模式 model.eval() # 检查输入数据范围 print(输入数据范围:, torch.min(input_data), torch.max(input_data)) # 尝试不同的opset版本 torch.onnx.export(..., opset_version10)问题2.wts文件加载失败通常是因为文件编码问题确保是纯文本十六进制解析错误参数数量不匹配调试建议# 检查.wts文件第一行的参数数量 with open(resnet18.wts) as f: first_line f.readline() print(声明的参数数量:, first_line.strip()) # 实际统计参数数量 state_dict model.state_dict() print(实际参数数量:, len(state_dict))问题3TensorRT加载ONNX失败这类问题通常需要检查ONNX模型是否包含不支持的操作输入输出维度是否正确是否使用了动态维度可以使用Netron可视化工具检查ONNX模型结构pip install netron python -m netron resnet18.onnx5. 三种格式的对比与应用场景经过多次项目实践我总结出这三种格式各自的优缺点和适用场景PyTorch .pth格式优点完整保存模型结构和参数加载简单一行代码即可恢复支持继续训练缺点文件体积较大只能在PyTorch中使用适用场景模型训练和微调快速原型开发ONNX格式优点跨框架通用支持多种推理引擎可以进行图优化缺点某些操作可能不支持无法继续训练适用场景生产环境部署多框架协作项目.wts格式优点纯文本可读性好体积相对较小易于跨平台使用缺点只包含权重不包含结构需要额外实现模型结构适用场景嵌入式设备部署自定义推理引擎在实际项目中我通常会保持这三种格式的转换通道畅通。比如在最近的智能摄像头项目中我们使用PyTorch训练模型转换为ONNX进行性能测试最后导出.wts用于嵌入式设备部署。这种工作流既灵活又高效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询