close

11.3 ONNXRuntime 進階使用

前言

模型部署推理,除了換了一個框架平臺外,最重要的是推理框架平臺支持各種優化方法,使得模型推理效率更高。

onnxruntime中也提供了一些優化方法,例如float16量化、int8量化、計算圖優化等操作。

本小節就介紹在onnxruntime中實現float16量化、int8量化、混合精度量化、計算圖優化、執行緒管理和IO binding

  • float16量化、int8量化、混合精度量化:都是從資料存儲及運算角度出發,提高運行效率
  • 計算圖優化:是從計算邏輯上優化計算過程
  • 執行緒管理:是從設備資源配置及調度的角度,充分利用cpu資源以應對並行、串列的任務
  • IO binding:是從資料在設備之間遷移的問題考慮,將資料提前綁定到gpu上,減少cpugpu之間的通信

float16量化

官方文檔

float16量化是常用的,精度損失較小的量化策略,通常模型的精度是float32,即一個浮點數用32位來表示,它佔用4位元組。

但大多數情況下,用16位元表示浮點數,已經能滿足要求。因此,在推理時可以將模型從float32轉為float16

float16的量化需要用到 onnxconverter-common庫,先提前安裝:pip install onnx onnxconverter-common

然後只需要調用convert_float_to_float16,就可以把onnx模型轉換為float16的模型。

 import onnx

 from onnxconverter_common import float16

 

 model = onnx.load("path/to/model.onnx")

 model_fp16 = float16.convert_float_to_float16(model)

 onnx.save(model_fp16, "path/to/model_fp16.onnx")

Copy

通過配套代碼,進行性能測試,可以看到相比於float32的效率,float16在輸送量上18.8%((1023-861/861)的提升。

<<AI人工智慧 PyTorch自學>> 11.3 ONNX

除了性能效率提升,float16onnx模型只有48.7MB, float3297.4MB,磁片存儲佔用少了50%

int8量化

官方文檔 ; 官方案例

既然能用低比特表示模型,能否再進一步簡化,採用8bit來表示float32呢?當然可以,8bit量化是一種常用於移動端、邊緣端的優化策略。

量化又分動態量化(dynamic quantization)和靜態量化(static quantization),這裡採用動態量化演示,動態量化直接將資料量化到固定的8bit,而靜態量化需要採用校準資料集進行一段時間的推理後,得到量化結果。

int8/uint8動態量化只需要採用兩行代碼,即可將量化後的模型保存到本地。

from onnxruntime.quantization import quantize_dynamic, QuantType

_ = quantize_dynamic(path_model_float32, path_model_int8, weight_type=QuantType.QInt8)

Copy

int8onnx模型只有24.4MB, float3297.4MBfloat1648.7MB,比float32少了75%,比float16少了50%

通過配套代碼,進行性能測試,發現int8的推理速度非常慢,連cpu的比不上,其中的原因可能是硬體的不匹配?

關於int8量化,也需要支援int8計算的顯卡,例如T4A100顯卡。

“Hardware support is required to achieve better performance with quantization on GPUs. You need a device that supports Tensor Core int8 computation, like T4 or A100. Older hardware will not benefit from quantization.”

混合精度

官方文檔

float16精度不夠的時候,可以考慮混合精度,在有必要的地方變為float16,其它地方保持float32,這樣可以實現精度和速度的權衡。

混合精度的實現與float16匯出類似,需要額外注意的是要給模型一些測試資料,用於評估哪些是float16,哪些是float32

from onnxconverter_common import auto_mixed_precision

import onnx

 

model = onnx.load("path/to/model.onnx")

model_fp16 = auto_convert_mixed_precision(model, test_data, rtol=0.01, atol=0.001, keep_io_types=True)

onnx.save(model_fp16, "path/to/model_fp16.onnx")

Copy

計算圖優化

官方文檔

除了對資料的量化,計算圖層面的優化也可以提高推理效率,例如移除不必要的層、網路層的融合:

  • Identity Elimination
  • Slice Elimination
  • Unsqueeze Elimination
  • Dropout Elimination
  • Conv Add Fusion
  • Conv Mul Fusion
  • Conv BatchNorm Fusion
  • Relu Clip Fusion
  • Reshape Fusion

整體上,計算圖優化分為三個levelBasicExtendedLayout Optimizations

代碼實現上比較簡單,是在InferenceSession產生實體的時候,添加sess_options,就可實現計算圖優化。

sess_options需要設置優化的類型,以及優化後模型保存路徑。

優化類型有4level可選:

GraphOptimizationLevel::ORT_DISABLE_ALL -> Disables all optimizations

GraphOptimizationLevel::ORT_ENABLE_BASIC -> Enables basic optimizations

GraphOptimizationLevel::ORT_ENABLE_EXTENDED -> Enables basic and extended optimizations

GraphOptimizationLevel::ORT_ENABLE_ALL -> Enables all available optimizations including layout optimizations

Copy

import onnxruntime as rt

 

sess_options = rt.SessionOptions()

 

# Set graph optimization level

sess_options.graph_optimization_level = rt.GraphOptimizationLevel.ORT_ENABLE_EXTENDED

 

# To enable model serialization after graph optimization set this

sess_options.optimized_model_filepath = "<model_output_path\optimized_model.onnx>"

 

session = rt.InferenceSession("<model_path>", sess_options)

Copy

通過配套代碼,可以觀察輸送量,發現沒有得到任何變化,這可能與設備有關,在這裡暫時不深究,後續生產真用到了,再回來看。

&lt;&lt;AI人工智慧 PyTorch自學&gt;&gt; 11.3 ONNX

&lt;&lt;AI人工智慧 PyTorch自學&gt;&gt; 11.3 ONNX

 

在這裡需要注意的是,由於onnx的顯存不能自動釋放,一次性跑兩個模型的效率評估的話,第二個模型會受到資源問題,導致效率評估不準確,這裡代碼中需要手動切換兩個模型,分兩次跑。上圖中vggfloat32bs=16時,性能突然降低,或許就是因為資源問題。

執行緒管理

onnxruntime提供執行緒管理功能,可以通過調整不同的參數來控制模型的運行方式和性能。該功能的主要特點包括:

  1. 控制執行緒數量:使用sess_options.intra_op_num_threads參數可以控制模型運行時所使用的執行緒數
  2. 循序執行或並存執行:使用sess_options.execution_mode參數可以控制運算子在圖中是循序執行還是並存執行。當模型具有較多的分支時,將該參數設置為ORT_PARALLEL可以提供更好的性能
  3. 控制並存執行的執行緒數量:在sess_options.execution_mode = rt.ExecutionMode.ORT_PARALLEL的情況下,可以使用sess_options.inter_op_num_threads參數來控制在不同節點上並存執行圖時使用的執行緒數。

具體使用參見配套代碼 官方文檔

由於配套代碼中運行的結果並沒有得到提升,這裡猜測與硬體設備有關,因此就不討論執行緒參數設置的問題了。

I/O binding

IO Binding 用於在運行計算圖之前將輸入和輸出張量綁定到設備上,以提高運行效率。

IO Binding 可以避免在運行計算圖時將輸入和輸出資料從 CPU 複製到設備上,從而減少資料複製操作所需的時間。

具體操作可參考:配套代碼io bind

同樣地,配套代碼中的案例沒有得到效率提升,也無法進一步探討了,這裡可以作為參考代碼。

當輸入和輸出張量比較大時,使用 IO Binding 功能可以顯著提高計算圖的執行效率,因此在後續的任務中嘗試使用 IO binding

運行耗時分析工具

onnxruntime還提供了一個運行耗時分析工具,在sess_options中設置sess_options.enable_profiling = True,就可以在目前的目錄輸出一份json檔,根據json檔中詳細記錄了執行時間和性能資料。每個事件條目包括以下資訊:

  • cat:事件的分類,可以是Session(會話)或Node(節點);
  • pid:進程ID
  • tid:執行緒ID
  • dur:事件持續時間,以微秒為單位;
  • ts:事件的時間戳記,以微秒為單位;
  • ph:事件的類型,可以是X(完整事件)或B(事件開始)/E(事件結束);
  • name:事件的名稱;
  • args:事件的參數,包括輸入和輸出張量的類型、形狀和大小,以及執行緒池的名稱、執行緒ID和執行時間。

通過配套代碼,獲得json檔,還可以通過網站:https://www.ui.perfetto.dev/ open trace file打開json進行視覺化觀察。

 

&lt;&lt;AI人工智慧 PyTorch自學&gt;&gt; 11.3 ONNX

小結

本小節介紹了onnxruntime在性能優化上的一些技巧,包括float16量化、int8量化、混合精度量化、計算圖優化、執行緒管理和IO binding

但由於本機設備原因,並沒有看到有多大的性能優化,大家也可以在自己設備上嘗試一下運行效率的變化,按道理這些技巧是能提速的。

這些優化技巧是模型部署過程中常見的加速技巧,在其它框架中也會有實現,這些在TensorRT中會再詳細展開。

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 HCHUNGW 的頭像
    HCHUNGW

    HCHUNGW的部落格

    HCHUNGW 發表在 痞客邦 留言(0) 人氣()