[模型优化] 1. 模型转换
·
👋 你好!这里有实用干货与深度分享✨✨ 若有帮助,欢迎:
👍 点赞 | ⭐ 收藏 | 💬 评论 | ➕ 关注 ,解锁更多精彩!
📁 收藏专栏即可第一时间获取最新推送🔔。
📖后续我将持续带来更多优质内容,期待与你一同探索知识,携手前行,共同进步🚀。

模型转换
本文详细介绍深度学习模型转换的相关技术,包括格式转换、计算图优化、算子融合和内存布局优化等方法,帮助你高效地将模型部署到各种硬件平台。
1. 格式转换
1.1 常见模型格式
| 格式 | 开发者 | 适用场景 | 主要特点 | 支持框架 |
|---|---|---|---|---|
| ONNX | 微软/Facebook | 跨平台部署 | 开放标准、广泛兼容 | PyTorch, TensorFlow, MXNet |
| TensorRT | NVIDIA | GPU推理加速 | 高性能、低延迟 | TensorFlow, PyTorch(通过ONNX) |
| TFLite | 移动/嵌入式设备 | 轻量级、低功耗 | TensorFlow | |
| CoreML | Apple | iOS/macOS设备 | 系统集成、低功耗 | TensorFlow, PyTorch(通过ONNX) |
| NCNN | 腾讯 | 移动设备 | 轻量级、无依赖 | PyTorch(通过ONNX) |
1.1.1 ONNX (Open Neural Network Exchange)
- 开放的深度学习模型标准格式
- 支持跨框架模型转换
- 广泛的工具链支持
- 版本兼容性管理(通过opset版本)
1.1.2 TensorRT
- NVIDIA开发的高性能深度学习推理引擎
- 支持模型优化和加速
- 适用于NVIDIA GPU部署
- 支持FP16/INT8量化
- 自动进行算子融合和内存优化
1.1.3 TFLite
- TensorFlow的轻量级解决方案
- 针对移动和嵌入式设备优化
- 支持模型量化和优化
- 提供硬件加速器支持(如EdgeTPU、GPU)
- 支持Android/iOS平台
1.2 转换工具和方法
1.2.1 PyTorch模型转ONNX
# PyTorch模型转ONNX示例
import torch
import torchvision
# 1. 创建模型实例
model = torchvision.models.resnet18(pretrained=True)
model.eval()
# 2. 创建示例输入
dummy_input = torch.randn(1, 3, 224, 224)
# 3. 导出ONNX模型
torch.onnx.export(model, # 要转换的模型
dummy_input, # 模型输入
"resnet18.onnx", # 输出文件名
export_params=True, # 存储训练好的参数权重
opset_version=11, # ONNX算子集版本
do_constant_folding=True, # 是否执行常量折叠优化
input_names=["input"], # 输入节点名
output_names=["output"], # 输出节点名
dynamic_axes={"input":{0:"batch_size"}, # 动态维度
"output":{0:"batch_size"}})
# 4. 验证导出的模型
import onnx
onnx_model = onnx.load("resnet18.onnx")
onnx.checker.check_model(onnx_model) # 验证模型结构
print("ONNX模型验证成功!")
1.2.2 TensorFlow模型转TFLite
# TensorFlow模型转TFLite示例
import tensorflow as tf
# 1. 加载SavedModel或Keras模型
model = tf.keras.applications.MobileNetV2(weights='imagenet', include_top=True)
# 2. 创建TFLite转换器
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 3. 设置转换选项
converter.optimizations = [tf.lite.Optimize.DEFAULT] # 启用默认优化
converter.target_spec.supported_types = [tf.float16] # 启用FP16量化
# 4. 执行转换
tflite_model = converter.convert()
# 5. 保存模型
with open('mobilenet_v2.tflite', 'wb') as f:
f.write(tflite_model)
print("TFLite模型转换成功!")
1.2.3 ONNX模型转TensorRT
# ONNX模型转TensorRT示例
import tensorrt as trt
import numpy as np
# 1. 创建TensorRT logger和builder
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(TRT_LOGGER)
# 2. 创建网络定义
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
# 3. 创建ONNX解析器
parser = trt.OnnxParser(network, TRT_LOGGER)
# 4. 解析ONNX文件
with open("resnet18.onnx", 'rb') as model:
parser.parse(model.read())
# 5. 构建引擎配置
config = builder.create_builder_config()
config.max_workspace_size = 1 << 30 # 1GB
config.set_flag(trt.BuilderFlag.FP16) # 启用FP16精度
# 6. 构建引擎
engine = builder.build_engine(network, config)
# 7. 序列化引擎并保存
with open("resnet18.trt", "wb") as f:
f.write(engine.serialize())
print("TensorRT模型转换成功!")
2. 计算图优化
2.1 常见优化技术
| 优化技术 | 描述 | 优势 | 适用场景 |
|---|---|---|---|
| 常量折叠 | 将常量计算在编译时完成 | 减少运行时计算量 | 有大量常量计算的模型 |
| 算子融合 | 合并可以一起执行的操作 | 减少内存访问和计算开销 | 有连续操作的模型 |
| 冗余节点消除 | 删除不必要的计算节点 | 简化计算图结构 | 复杂模型结构 |
| 内存优化 | 优化张量存储和访问方式 | 减少内存占用和访问延迟 | 内存受限设备 |
| 算子替换 | 用高效实现替换低效算子 | 提高特定硬件上的性能 | 特定硬件部署 |
2.1.1 常量折叠
- 将常量计算在编译时完成
- 减少运行时计算量
- 例如:预计算固定权重的乘法
2.1.2 算子融合
- 合并可以一起执行的操作
- 减少内存访问和计算开销
- 例如:Conv+BN+ReLU融合
2.1.3 冗余节点消除
- 删除不必要的计算节点
- 简化计算图结构
- 例如:移除无效的Reshape操作
2.2 优化示例
# ONNX模型优化示例
import onnx
from onnxoptimizer import optimize
# 1. 加载ONNX模型
model = onnx.load("resnet18.onnx")
# 2. 定义优化Pass
passes = ["eliminate_identity", # 删除恒等变换
"eliminate_nop_transpose", # 删除无效的转置操作
"fuse_consecutive_transposes", # 融合连续的转置操作
"fuse_bn_into_conv"] # 将BN层融合到卷积层
# 3. 执行优化
optimized_model = optimize(model, passes)
# 4. 保存优化后的模型
onnx.save(optimized_model, "resnet18_optimized.onnx")
# 5. 比较优化前后的模型大小
import os
original_size = os.path.getsize("resnet18.onnx") / (1024 * 1024)
optimized_size = os.path.getsize("resnet18_optimized.onnx") / (1024 * 1024)
print(f"原始模型大小: {original_size:.2f} MB")
print(f"优化后模型大小: {optimized_size:.2f} MB")
print(f"减少: {(original_size - optimized_size) / original_size * 100:.2f}%")
3. 算子融合
3.1 常见融合模式
| 融合模式 | 描述 | 性能提升 | 支持平台 |
|---|---|---|---|
| Conv + BN + ReLU | 将批归一化和激活函数融合到卷积中 | 20-30% | 几乎所有平台 |
| Conv + ReLU | 将激活函数融合到卷积中 | 10-15% | 几乎所有平台 |
| MatMul + Add | 将偏置加法融合到矩阵乘法中 | 5-10% | 大多数平台 |
| Conv + Add | 将残差连接融合到卷积中 | 10-20% | 部分平台 |
| Transpose + MatMul | 优化矩阵乘法的内存访问模式 | 5-15% | 部分平台 |
3.2 自定义融合规则
# TensorRT自定义算子融合示例
import tensorrt as trt
import numpy as np
class MyFusionPlugin(trt.IPluginV2):
def __init__(self):
super().__init__()
def enqueue(self, batch_size, inputs, outputs, stream):
# 实现融合后的前向计算逻辑
pass
def get_workspace_size(self, max_batch_size):
# 返回所需工作空间大小
return 0
# 实现其他必要的接口方法
# 注册插件到TensorRT
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
plugin_registry = trt.get_plugin_registry()
# 创建插件创建器
class MyFusionPluginCreator(trt.IPluginCreator):
def create_plugin(self, name, fc):
return MyFusionPlugin()
# 实现其他必要的接口方法
# 使用自定义插件
def add_fusion_plugin(network, inputs):
plugin_layer = network.add_plugin_v2(inputs, MyFusionPlugin())
return plugin_layer.get_output(0)
3.3 融合效果分析
# 融合效果分析示例
import onnx
import onnxruntime as ort
import numpy as np
import time
# 加载原始模型和优化后的模型
original_model = ort.InferenceSession("resnet18.onnx")
fused_model = ort.InferenceSession("resnet18_optimized.onnx")
# 准备输入数据
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
input_name = original_model.get_inputs()[0].name
# 性能测试函数
def benchmark(session, input_data, input_name, num_runs=100):
# 预热
for _ in range(10):
session.run(None, {input_name: input_data})
# 计时
start = time.time()
for _ in range(num_runs):
session.run(None, {input_name: input_data})
end = time.time()
return (end - start) / num_runs * 1000 # 毫秒
# 测试性能
original_time = benchmark(original_model, input_data, input_name)
fused_time = benchmark(fused_model, input_data, input_name)
print(f"原始模型推理时间: {original_time:.2f} ms")
print(f"融合后模型推理时间: {fused_time:.2f} ms")
print(f"性能提升: {(original_time - fused_time) / original_time * 100:.2f}%")
4. 内存布局优化
4.1 数据格式转换
| 数据格式 | 描述 | 适用硬件 | 主要优势 |
|---|---|---|---|
| NCHW | 批次-通道-高度-宽度 | GPU, 大多数框架 | 卷积运算友好 |
| NHWC | 批次-高度-宽度-通道 | CPU, 移动设备 | 内存访问连续 |
| NCHW4 | 通道分组的NCHW | 特定加速器 | 向量化友好 |
| NHWC8 | 通道分组的NHWC | 特定加速器 | SIMD友好 |
4.1.1 数据格式选择
- NCHW vs NHWC
- NCHW: PyTorch默认格式,适合GPU
- NHWC: TensorFlow默认格式,适合CPU
- 选择硬件友好的数据格式
- 根据目标硬件特性选择
- 减少格式转换开销
- 减少格式转换开销
- 尽量在整个推理过程中保持一致的格式
- 必要时使用高效的转换算法
4.2 内存对齐
# PyTorch内存对齐示例
import torch
# 创建对齐的张量
aligned_tensor = torch.zeros(size=(1, 3, 224, 224),
dtype=torch.float32,
memory_format=torch.channels_last) # NHWC格式
# 检查是否连续
print(aligned_tensor.is_contiguous()) # False
print(aligned_tensor.is_contiguous(memory_format=torch.channels_last)) # True
# 转换现有张量的内存格式
standard_tensor = torch.randn(1, 3, 224, 224) # NCHW格式
converted_tensor = standard_tensor.to(memory_format=torch.channels_last) # 转为NHWC
# 使用channels_last格式的模型
model = torchvision.models.resnet18().cuda()
model = model.to(memory_format=torch.channels_last) # 转换模型权重格式
# 测量性能差异
input_nchw = torch.randn(1, 3, 224, 224).cuda()
input_nhwc = input_nchw.to(memory_format=torch.channels_last)
with torch.no_grad():
# 预热
for _ in range(10):
model(input_nhwc)
# 测试NHWC性能
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(100):
model(input_nhwc)
end.record()
torch.cuda.synchronize()
nhwc_time = start.elapsed_time(end) / 100
# 测试NCHW性能
model = model.to(memory_format=torch.contiguous_format) # 转回NCHW
torch.cuda.synchronize()
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(100):
model(input_nchw)
end.record()
torch.cuda.synchronize()
nchw_time = start.elapsed_time(end) / 100
print(f"NCHW格式推理时间: {nchw_time:.2f} ms")
print(f"NHWC格式推理时间: {nhwc_time:.2f} ms")
print(f"性能差异: {(nchw_time - nhwc_time) / nchw_time * 100:.2f}%")
5. 常见问题与解决方案
5.1 模型转换问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 不支持的算子 | 目标格式不支持某些操作 | 使用自定义算子或替换为等效操作 |
| 精度损失 | 数值表示差异 | 调整量化参数或使用更高精度 |
| 动态形状处理 | 静态形状假设 | 指定动态轴或使用固定尺寸 |
| 内存溢出 | 模型过大或中间结果过多 | 减小批次大小或优化内存使用 |
5.2 调试技巧
# ONNX模型可视化和调试
import onnx
import netron
import onnxruntime as ort
import numpy as np
# 加载模型
model_path = "resnet18.onnx"
model = onnx.load(model_path)
# 1. 检查模型结构
onnx.checker.check_model(model)
print("模型结构检查通过")
# 2. 可视化模型(在浏览器中打开)
netron.start(model_path)
# 3. 检查中间输出
def check_intermediate_outputs(model_path, input_data):
# 创建推理会话
session_options = ort.SessionOptions()
session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL
session_options.log_severity_level = 0
session = ort.InferenceSession(model_path, session_options)
# 获取所有节点名称
input_name = session.get_inputs()[0].name
nodes = [node.name for node in session._model_meta.graph_builder._nodes]
# 为每个节点创建输出
output_names = [node for node in nodes if node != input_name]
# 运行推理并获取中间结果
outputs = session.run(output_names, {input_name: input_data})
# 分析结果
results = {}
for name, output in zip(output_names, outputs):
results[name] = {
"shape": output.shape,
"min": float(np.min(output)),
"max": float(np.max(output)),
"mean": float(np.mean(output)),
"std": float(np.std(output)),
"has_nan": bool(np.isnan(output).any()),
"has_inf": bool(np.isinf(output).any())
}
return results
# 示例使用
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
results = check_intermediate_outputs(model_path, input_data)
# 打印可能有问题的节点
for name, info in results.items():
if info["has_nan"] or info["has_inf"] or info["std"] == 0:
print(f"可能有问题的节点: {name}")
print(f" 统计信息: {info}")
6. 最佳实践
6.1 选择合适的模型格式
- 根据部署平台特点选择
- 移动设备: TFLite, NCNN, MNN
- NVIDIA GPU: TensorRT
- 通用平台: ONNX
- 考虑模型大小和性能需求
- 对延迟敏感: TensorRT, OpenVINO
- 对大小敏感: TFLite, NCNN
- 评估工具链支持程度
- 开发工具完善度
- 社区活跃度
- 文档质量
6.2 优化策略组合
- 结合多种优化技术
- 格式转换 + 计算图优化 + 量化
- 先优化结构,再优化精度
- 验证优化效果
- 建立基准测试
- 逐步应用优化并测量
- 权衡精度和性能
- 设定可接受的精度损失范围
- 在精度和性能间找平衡点
6.3 性能评估
- 建立性能基准
- 测量端到端延迟
- 测量吞吐量
- 测量内存使用
- 测试不同优化组合
- 控制变量法测试
- 记录每步优化效果
- 监控资源使用情况
- CPU/GPU利用率
- 内存占用
- 功耗(移动设备)
6.4 调试与验证
- 确保功能正确性
- 与原始模型输出比对
- 使用测试集验证
- 比较优化前后结果
- 计算精度指标(如Top-1准确率)
- 可视化结果差异
- 处理精度损失问题
- 识别问题算子
- 调整优化参数
- 考虑混合精度策略
7. 参考资源
📌 感谢阅读!若文章对你有用,别吝啬互动~
👍 点个赞 | ⭐ 收藏备用 | 💬 留下你的想法 ,关注我,更多干货持续更新!
更多推荐

所有评论(0)