YOLO 模型從 PyTorch 轉換為 ONNX 并優化
在深度學習部署中,ONNX(Open Neural Network Exchange) 已成為跨框架與跨平臺的標準格式。我們經常需要將 YOLOv8 在 PyTorch 中訓練好的模型轉換為 ONNX,并進行優化,以便在 CPU、GPU 或邊緣設備 上高效推理。
本文將詳細介紹:
- 如何從 PyTorch 導出 YOLOv8 為 ONNX
- 如何用 onnxruntime 優化模型(包含新舊版本對比)
- 每個操作的意義
- 優化選項與參數說明
1. 為什么要優化 ONNX 模型?
導出的原始 ONNX 文件里,可能包含很多“冗余算子”或“不適合推理加速的計算方式”。
比如:
- 多余的
Transpose
、Identity
節點。 - 可合并的
Conv → BatchNorm → Relu
。 - 未展開的常量。
優化的目的就是:減少節點數、合并算子、提升推理速度,同時不改變模型的計算邏輯。
2. onnxruntime 的優化機制
在 onnxruntime
里,優化是在 InferenceSession 初始化 時完成的,控制參數就是:
options = ort.SessionOptions()
options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
2.1 優化級別(GraphOptimizationLevel)
onnxruntime 提供 4 種優化級別:
選項 | 作用 | 典型場景 |
---|---|---|
ORT_DISABLE_ALL | 完全不做優化 | 調試/測試 |
ORT_ENABLE_BASIC | 基礎優化:常量折疊、消除無用節點 | 保守優化,結果最安全 |
ORT_ENABLE_EXTENDED | 擴展優化:算子融合(Conv+BN+Relu),節點重排序 | 性能更好,常用 |
ORT_ENABLE_ALL | 啟用所有可用優化,包括 layout 優化、內存重用 | 生產環境推薦 |
默認是 ORT_ENABLE_ALL
,你可以按需求調節。
2.2 其他常用參數(SessionOptions)
options.intra_op_num_threads = 4 # 單個算子內部使用的線程數
options.inter_op_num_threads = 2 # 不同算子之間的并行線程數
options.execution_mode = ort.ExecutionMode.ORT_PARALLEL # 啟用并行執行
options.enable_mem_pattern = True # 內存復用模式
options.enable_cpu_mem_arena = True # CPU 內存分配優化
📌 意義:
- 線程數控制可以充分利用 CPU 多核。
- 內存優化選項可以減少內存占用,適合邊緣設備。
3. 優化的保存方式
舊版本(≤1.16):
- 優化只存在于內存,不會保存到
.onnx
文件。 - 即使你
onnx.save()
,保存的還是原始模型。
新版本(≥1.17):
-
可以用:
options.optimized_model_filepath = "yolov8n_optimized.onnx"
這樣在 Session 初始化時,會把優化后的圖寫入文件。
👉 這就是新版和舊版最大的區別。
4. 環境準備
安裝必要依賴:
pip install ultralytics onnx onnxruntime
如果需要高級優化(算子融合、量化),還可以安裝:
pip install onnxruntime-tools
確認版本(非常關鍵,因為 API 有變化):
pip show onnxruntime
- 舊版 onnxruntime:≤ 1.16.x
- 新版 onnxruntime:≥ 1.17.0
5. 從 PyTorch 導出 YOLOv8 為 ONNX
from ultralytics import YOLO
from pathlib import PathMODELS_DIR = Path("models")# 加載 YOLOv8 模型
model = YOLO(str(MODELS_DIR / "yolov8n.pt"))# 導出為 ONNX 格式
model.export(format="onnx")
- 輸入:PyTorch 的
.pt
模型 - 輸出:
yolov8n.onnx
- 意義:ONNX 模型是跨平臺的,可以在不同推理引擎(onnxruntime、TensorRT、OpenVINO)中運行。
👉 導出后,你可以用 Netron 查看模型結構,檢查輸入/輸出 shape 是否正確。
5.1 什么是 Netron?
Netron 是一個 神經網絡模型可視化工具,用來查看 .onnx
、.pt
、.h5
、.tflite
等模型的網絡結構。
- 你可以 直觀查看每一層的算子(Conv、Relu、FC 等)。
- 支持 模型輸入輸出 shape、權重信息。
- 非常適合在導出 ONNX 后,檢查網絡結構是否符合預期。
5.2 怎么使用 Netron?
-
安裝(桌面應用 / 瀏覽器版):
- 桌面版下載地址:https://github.com/lutzroeder/netron
- 瀏覽器版直接打開:https://netron.app
-
打開模型:
- 在桌面應用里,直接拖拽
yolov8n.onnx
文件進去。 - 在網頁端,點擊
Open Model
上傳模型。
- 在桌面應用里,直接拖拽
-
查看結構:
- 你會看到類似一張「流程圖」,每一層算子(Conv、BN、Relu、Concat 等)都展示出來。
- 可以點選層節點,查看 輸入/輸出 shape 和 權重參數。
5.3 為什么要用 Netron?
- 確認 ONNX 導出是否成功(有時候導出會缺少算子或層)。
- 檢查 輸入輸出 shape 是否正確(例如 YOLO 輸入是否是
1x3x640x640
)。 - 驗證 模型優化前后層結構是否保持一致。
它就像模型的「顯微鏡」,幫我們把黑盒的 .onnx
文件拆開看清楚。
6. 優化 ONNX 模型(onnxruntime)
6.1 新舊版本對比
對比項 | 舊版 onnxruntime (≤1.16.x) | 新版 onnxruntime (≥1.17.0) |
---|---|---|
導入方式 | from onnxruntime import GraphOptimizationLevel, SessionOptions | import onnxruntime as ort |
設置優化級別 | options.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL | options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL |
創建 Session | session = ort.InferenceSession("model.onnx", options) | session = ort.InferenceSession("model.onnx", sess_options=options) |
保存優化模型 | ? 不支持,優化僅存在內存 | ? options.optimized_model_filepath = "optimized.onnx" |
是否推薦 | ?? 已過時 | ? 官方推薦 |
6.2 新版寫法(推薦,onnxruntime ≥ 1.17)
import onnxruntime as ort
from pathlib import PathMODELS_DIR = Path("models")
onnx_path = str(MODELS_DIR / "yolov8n.onnx")
optimized_path = str(MODELS_DIR / "yolov8n_optimized.onnx")# 設置優化選項
options = ort.SessionOptions()
options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
options.optimized_model_filepath = optimized_path# 創建 Session(會觸發優化并保存)
session = ort.InferenceSession(onnx_path, sess_options=options)print(f"優化后的模型已保存到: {optimized_path}")
📌 意義:
- 優化發生在 Session 初始化 階段。
- 優化圖會被寫入文件,避免每次加載時重復優化。
- 部署時直接加載優化好的
.onnx
,加快推理啟動速度。
6.3 舊版寫法(已過時,onnxruntime ≤ 1.16.x)
from onnxruntime import GraphOptimizationLevel, SessionOptions
import onnxruntime as ortoptions = SessionOptions()
options.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALLsession = ort.InferenceSession("yolov8n.onnx", options)
?? 問題:
- 優化僅存在內存,不會保存到
.onnx
文件。 - 部署時需要每次重新優化,影響啟動性能。
7. 高級優化(可選,onnxruntime-tools)
如果需要進一步優化(算子融合、Transformer 專用優化、量化),可以用:
from onnxruntime_tools import optimizer
from pathlib import PathMODELS_DIR = Path("models")
onnx_path = str(MODELS_DIR / "yolov8n.onnx")
optimized_path = str(MODELS_DIR / "yolov8n_tools_optimized.onnx")# YOLO 沒有專用類型,"transformer" 或 "bert" 都能工作
optimized_model = optimizer.optimize_model(onnx_path,model_type="bert",opt_level=99
)optimized_model.save_model_to_file(optimized_path)
print(f"進一步優化后的模型已保存到: {optimized_path}")
8. InferenceSession 和 onnxruntime-tools 對比
方法 | 優點 | 缺點 |
---|---|---|
onnxruntime-tools | 支持更多優化策略(圖融合、量化),可控性強 | 需要額外安裝 |
options.optimized_model_filepath | 依賴少,直接在 onnxruntime 里完成 | 只做 onnxruntime 的圖級優化,不包含高級策略 |
9. 驗證優化后的模型
優化后需要驗證模型是否能正確推理:
import numpy as np# 加載優化模型
session = ort.InferenceSession(str(MODELS_DIR / "yolov8n_optimized.onnx"))# 構造隨機輸入 (1, 3, 640, 640)
input_data = np.random.randn(1, 3, 640, 640).astype(np.float32)# 獲取輸入/輸出名稱
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name# 運行推理
outputs = session.run([output_name], {input_name: input_data})print(outputs)
📌 意義:
- 確保優化后的模型依然輸出正確結果。
- 可以進一步對比 PyTorch 原始輸出和 ONNX 輸出,確保差異在數值誤差范圍內(1e-4 ~ 1e-6)。
10. 總結
- 轉換:使用 Ultralytics 將 YOLOv8
.pt
導出為.onnx
。 - 優化:用 onnxruntime 的
SessionOptions
,推薦設置graph_optimization_level = ORT_ENABLE_ALL
,并保存為新文件。 - 版本差異:onnxruntime ≥ 1.17 才能保存優化模型;舊版僅內存優化。
- 參數調整:可以通過線程數和內存選項進一步提升性能。
- 驗證:運行推理,確認優化模型與原始模型一致。
- 高級優化:onnxruntime-tools 可做更激進的算子融合與量化。