Mnn Converter Debug [進階] ---- 微架構設計 & 模型定制化轉換概述

1 概述

文章:Mnn Converter Debug
上述文章中,我們介紹了Mnn Converter Debug 工程的構建辦法,構建了該工程,我們可以更便捷地通過調試快速理解 Mnn Converter 的架構 與 流程,并進行簡單的調試修改。

但當我們轉化的模型越來越多,越來越復雜。我們會發(fā)現:
1)不同模型的轉換過程可能遇到相似的問題
2)轉換文件的代碼復雜度變得愈發(fā)復雜
3)復雜的定制化轉換變得異常困難甚至無法進行
4)模型更新時,再次轉換甚至找不到原始的模型轉換代碼

我們需要 對我們實現的 定制化模型轉換進行必要的管理

所以該篇文章,
1)我會以 onnx-->mnn 的轉換需求為例,對 MnnConvertDebug 添加微架構設計。
2)簡單介紹基于該架構的 定制化模型轉換實現辦法
3)簡單介紹定制化模型轉換過程中的常見問題

2 MnnConverterDebug 微架構設計

2.1 基本架構

MNN Converter Debug 基本架構設計

2.2 模塊說明

2.2.1 JsonDump

Mnn模型 基于 FlatBuffers 結構存儲,難以直接閱讀。

有關 Mnn FlatBuffers 的相關介紹可以參考:
文章:FlatBuffers,MNN模型存儲結構基礎 ---- 無法解讀MNN模型文件的秘密
文章:做個游戲吧?玩兒轉Mnn模型存儲結構

所以, Mnn 提供了 JsonDump工具, 可以藉由 Mnn 模型導出 Mnn 基于json描述的網絡結構。我們 將這個工具集入框架,在完成Mnn模型轉換的同時,通過JsonDumpMnn的網絡結構Dump出來,方便對模型進行分析。

2.2.2 Template

約定的 “死”的框架,即可抽離 “活”的定制化
我們將框架相關的模板文件放置在此模塊,避免定制化文件中存在大量的重復代碼,降低維護代價。

2.2.3 Tools

模型轉換的通用工具支持,包括:
1)插入、鏈接 Mnn 操作節(jié)點
2)生成 Mnn 操作節(jié)點
3)轉換 Mnn 操作節(jié)點
4)未實現的 Mnn 節(jié)點轉換器

2.2.4 Converters

這邊將一個 特定模型(一個網絡)的轉換工具 定義為一個轉換器(Converter)。
當然,相似的模型可能復用同一個轉換器。
比如大多數簡單模型可以直接使用Mnn提供的轉換器。(不使用我們定制化的)

2.2.3 ConverterRed

非定制化代碼,即MnnConverterDebug構建時必要的代碼依賴,參考:
文章:Mnn Converter Debug

2.2.6 Main

主流程控制器,主要配置包括:

  1. 轉換器 的選擇
  2. onnx模型文件 路徑配置
  3. 目標mnn模型文件 路徑配置
  4. mnn json dump(不含權重)文件 路徑配置
  5. mnn json dump(含權重信息)文件 路徑配置

2.3 轉換流程

轉換流程

注:圖中的【MnnDebug】【ModelManager】【Python】該篇文章暫不進行具體的說明。

框架執(zhí)行流程:
1)首先,選擇一個 onnx模型 作為輸入;
2)基于模板創(chuàng)建一個 轉換器(比如ConverterY)進行模型調試,轉換出 mnn 模型;
3)對轉換好的 Mnn 模型進行 json dump
4)測試模型效果(比如構建一個MnnDebug工程,或使用其他辦法);
5)Run onnx 或者 更原始的模型(比如pytorch、mxnet等),設置與MnnDebug相同的輸入( 比如全部值設置為1.1),比對輸出,保證一致。

2.4 Demo

工程Demo Git鏈接
構建與集成說明:Mnn Converter Debug

3 使用 & 調試

3.1 新轉換器 的構建 & 配置

1)于 MnnConverterDebug/Custom/Converters 中建立 新的轉換器文件(.hpp/.cpp)
2)基于 MnnConverterDebug /Custom/Template/CYCustomConverter_Template.hpp/.cpp 填寫 新創(chuàng)建的新轉換器
3)在 main.mm 中引入 新創(chuàng)建的轉換器 的 .hpp 文件
4)在 main.mm 中替換轉換函數 onnx2MNNNet(modelPath.modelFile, modelPath.bizCode, netT); 為 新創(chuàng)建的轉換函數。

3.2 調試步驟(新創(chuàng)建的轉換器文件的定制化修改)

我們假設已經完成了3.1接2)的模板代碼拷貝

3.2.1 測試原始轉換

__BREAK_TAG、__MANUAL_TAG 設置一個較大的值(如3000,目前的網絡幾乎不會有這么多操作)。 此時,轉換器沒有任何定制化修改,完全走 Mnn原始的自動轉換邏輯
[轉換順利]:即給予相同的輸入的情況下,轉換的mnn模型 與 轉換前模型的輸出完全一致是,表示模型轉換順利,無須定制化調試修改,流程結束。
[轉換異常]:沒有轉換出mnn模型,或者轉換出的mnn模型輸出異常…… 孩紙!苦逼的時刻到了!做好準備吧!繼續(xù)往下看

3.2.2 查看控制臺輸出

參考控制臺輸出,模板代碼會自動打印所有 onnx模型操作索引、操作名稱、操作類型,如圖:

控制臺輸出

3.3.3 尋找問題操作

參考依據 控制臺輸出索引,修改 __BREAK_TAG 的值,找到轉換開始出現問題的操作。 出現問題指:轉換崩潰、執(zhí)行崩潰、執(zhí)行結果不合期望 等。

3.3.4 分析問題原因

通過 查看onnx網絡結構(推薦工具Netron)、分析onnx轉換前的模型網絡結構(比如pytorch、mxnet等)、又或調試Mnn源碼 等方法 嘗試 確認問題產生的原因

3.3.5 嘗試解決問題

確認問題后進行問題修改,可能需要通過 修改Mnn源碼、添加新的轉換器等、添加新的Mnn
操作、甚至設計重寫設計局部網絡
等方法解決問題。

3.3.6 保證最終輸出

循環(huán)進行 3.3.3 ~ 3.3.5 直到保證Mnn模型最終輸出與原始模型的一致性。
一致性標準參考:每個輸出對應值的小數點后至少三位完全一致。

4 模型轉換常見問題參考

4.1 轉換異常

魔性轉換過程中,可能根本無法正常輸出mnn模型文件

解決參考:
1)檢查輸入、輸出路徑配置
2)參考 [3.2 調試步驟],找到造成 轉換異常 的操作,然后針對修改。
3)分析過程可以適當參考模型的JsonDump結構

常見問題原因:
1)對應操作參數沒有設置好
2)操作鏈接設置錯誤

4.2 模型推理異常

模型轉換順利,并不代表模型可以被正常使用。即在模型的加載、推理過程中出現崩潰。

解決參考:
1)參考 [3.2 調試步驟],找到造成 轉換異常 的操作,然后針對修改。
2)分析過程可以適當參考模型的JsonDump結構

常見問題原因:
1)操作鏈接設置錯誤
2)相關操作輸入輸出不匹配(比如A-B操作相關,A操作輸入時2維,而B操作的輸入期望是4維)

4.3 操作結果不一致

模型推理順利,同樣不等同于轉換成功。
我們必須保證 轉換的mnn模型 在與原始模型 輸入一致的情況下,輸出結果與原始模型完全一致。

思路參考:
1)依次對齊對應操作的輸出形狀(shape)
2)依次對齊對應操作的輸出值(至少要保證三位小數是完全一致的)

目標參考:
網絡輸出層數據完全符合期望。

4.4 操作轉換不支持

有些操作Mnn有實現,但相關的轉換代碼沒有實現,這時候我們需要手動轉換對應的操作。

解決參考:
1)簡單的操作,可以直接使用Demo中的CYOpCreater中定義的操作生成方法生成。
2)也可以手動完成對應操作的生成代碼

4.4 操作不支持

有些Mnn本身沒有實現,而我們使用又比較著急,此時可以先自行添加對應操作的簡易版本。

解決參考:
1)參考Mnn官方文檔,添加新操作
文檔 - Mnn添加操作
2)有些操作可以基于Mnn已存在的操作添加參數配置邏輯來完成我們的目標

4.5 操作運算異常

Mnn的部分偏門操作還是必可避免的存在一些邏輯bug,造成輸出異常。

解決參考:
1)閱讀操作源碼,理清思路,修改bug。
2)對于比較確認的bug的非補丁式修改,建議向mnn提交merge request,惠及更多的朋友哦~

4.6 網絡中間結構復雜

有時候 原始模型(比如pytorch)結構很簡單,但是轉換成 中間模型(比如onnx)后,部分操作變得異常復雜,更糟糕的是,后續(xù)轉換的問題也處在這些復雜的操作中。這時候我們或許可以考慮直接 基于原始模型(比如pytorch)的結構 對目標模型(mnn模型)的部分網絡結構進行重設計代價很大,但針對老板的一些硬性需求,還是值得一做。

舉例:原始模型操作被onnx復雜化:

某onnx模型中間結構

針對 該種場景,建議將轉換設計用繪圖的方式繪制出來,因為在調試過程中會難免會對設計的結構進行修改。當對應的操作比較復雜時,這些修改完全基于代碼去調試 幾乎是一個不可能完成的任務。設計繪圖關鍵要素參考如下:
1)擬定操作類型(如:Reshape)
2)擬定操作名稱(如:C0)
3)擬定操作關鍵參數
4)計算操作輸出形狀(如1,512,1,119,方便測試比對)

網絡結構重設計舉例

結語

定制化模型轉換是一個大坑,其中很多具體的填坑場景也是不盡相同的。
該篇文章提供一種基于onnx-->mnn 的調試框架。一定程度上可以簡化了模型轉換的復雜度。

再次附上Demo的參考鏈接:
工程Demo Git鏈接
(基于公司的保密需要,我不方便將具體模型的轉換相關的框架應用代碼進行分享參考,只能手寫一些簡單模型,提供一些簡單場景的示例,大家見諒咯~)

模型轉換不是一件容易的事情
模型轉換目前還很難成為一個自動化的工作

Mnn的模型定制化轉換更加復雜
基于Mnn本身架構的復雜性(模型的flatbuffers存儲、模型運算中間結果數據排布的多樣性:NCHW,NC4HW4、Mnn緩存機制的處理、Mnn并發(fā)操作代碼的閱讀等) ,Mnn的模型轉換遇到的問題的解決更加復雜。

所以,轉換Mnn模型,信心、耐心 每樣都必不可少。
希望該篇文章能為轉換模型遇到困難無法繼續(xù)進行的同學提供一些思路的參考,也歡迎這些朋友將遇到的問題分享出來,我們一起討論、解決、借鑒。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容