在 MLIR 編譯器基礎架構中表示和參考降低 ONNX 模型
此專案由 onnx 維護
託管於 GitHub Pages — 主題由 orderedlist 提供
ONNX-MLIR 定義了一個 ONNX 方言來表示 ONNX 指定的操作。ONNX 方言是使用 MLIR 表格產生工具建立的。每個操作的定義都是使用 python 腳本 utils/gen_onnx_mlir.py 從 ONNX 自動轉換而來的。此腳本從 ONNX 套件檢索操作定義,以產生用於方言表格產生的 ONNXOps.td.inc 和用於 ONNX-MLIR 中 ONNX 模型匯入器的 OpBuilderTable.inc。以下章節將描述如何使用 gen_onnx_mlir.py 將操作新增到 ONNX-MLIR 中的 ONNX 方言,以及如何改進操作的定義。
若要為 ONNX 方言產生操作,請將此操作新增到 gen_onnx_mlir.py 中的字典 'version_dict' 中。此目錄的鍵是操作名稱,值是此操作的操作集清單。通常僅支援此操作的最高版本操作集(在 onnx-mlir/third_party/onnx 中)。有關版本的詳細資訊,請參閱 版本章節。使用此項目,腳本將為 ONNX 方言產生操作定義。
Pure
特徵。ResultTypeInferenceOpInterface
,請將其新增到字典 OpsWithResultTypeInference
。此介面推斷結果張量的類型,而不是形狀。HasOnnxSubgraphOpInterface
介面。此屬性是從 ONNX 操作定義推斷出來的。OpsWithHelpers
為操作定義輔助函式。預設情況下,所有操作都具有形狀推斷介面和 Pure
特徵。如果操作具有 ResultTypeInferenceOpInterface
,請使用字典 OpsWithResultTypeInference
。此介面推斷結果張量的類型,而不是形狀。如果操作具有子圖,則它將具有 HasOnnxSubgraphOpInterface
介面。
如果轉換應該跨越傳遞在本地應用於操作,則可以使用標準化介面進行此轉換。若要為操作啟用標準化,請將此操作的名稱新增到 OpsWithCanonicalizer
的清單中,然後該操作的定義中將具有 hasCanonicalizer = 1;
。
操作的預設建構器需要將結果的類型作為參數。但是,可以推斷結果的類型。自訂建構器可能對簡化程式碼很有用。根據推斷的類型,有兩種類型的建構器:未排序類型和廣播類型。若要為操作啟用特殊建構器,您可以分別將其名稱新增到 custom_builder_unranked_ops_list
和 custom_builder_broadcast_ops_list
中。
請注意,使用 returnType
可以避免重寫規則中對特殊建構器的需求。請參閱 MLIR 文件 或 ONNX-MLIR 中的範例。將此類類型推斷程式碼移至 ONNXOpHelper.cpp 並擺脫自訂建構器可能是一個更好的解決方案。
請注意,使用 returnType
可以避免重寫規則中對特殊建構器的需求。將此類類型推斷程式碼移至 ONNXOpHelper.cpp 並擺脫自訂建構器可能是一個更好的解決方案。
操作的操作描述列出了每個輸入/輸出和屬性的允許類型。表格產生將產生預設驗證器來檢查 IR 的允許類型。如果操作有額外的約束,則應定義自訂驗證器以增強錯誤偵測。例如,操作的兩個輸入可能需要相同的元素類型或相同的等級。此類資訊可以在 ONNX 操作定義中找到,但無法使用方言定義來表示。測試這些約束的最佳方法是在驗證器中。若要將自訂驗證器的介面新增到操作,請在 gen_onnx_mlir.py
中找到下方的陣列,並在其中新增您的操作。
OpsWithVerifier = ['AveragePool', 'Conv', 'InstanceNormalization', 'Mod']
然後,您將在 ONNXOps.td.inc 中的操作定義中找到以下程式碼行
let verifier = [{ return ::verify(*this); }];
當新操作宣告為使用自訂驗證器時,您將需要在 src/Dialect/ONNX/ONNXOps.cpp
中新增實作程式碼。最好查看其他操作以取得一般模式,例如,搜尋 static LogicalResult verify(ONNXInstanceNormalizationOp op)。請注意,每次建立此類操作時,都會執行驗證器。因此,您需要確保它可以與張量和 MemRefs 搭配使用,並且可能與未排序的張量搭配使用。因此,請在適當的情況下保護您的每個測試。例如,一旦張量排序,您就可以驗證等級是否在核准的範圍內(如果存在此類約束);在排序之前,請勿執行此測試。
提示
operandAdaptor
物件來取得輸入(必須使用 operandAdaptor
來取得輸入的目前值),並使用 op
物件來取得屬性(可以使用 op
,因為屬性通常是不可變的)。hasShapeAndRank(X)
來測試 X
輸入目前是否已成形和排序。如果不是,則返回成功,因為我們稍後將有機會使用此資訊測試操作。請注意,某些輸入也可能是純量,在這種情況下,它們可能會或可能不會編碼為形狀類型。mlir::cast<ShapedType>(X.getType())
來取得形狀類型,您可以從中取得等級和維度。此時,我們僅檢查執行階段已知的值的維度有效性。未知的維度編碼為負數。僅當您確定它不會斷言時,才使用轉換,也就是說,該類型確實是 ShapedType
。op->emitError(msg)
回報並顯示易於理解的錯誤訊息。special_op_handler
:在 frontend_dialect_transformer.cpp 中建立特殊的匯入函式。目前,特殊處理程式用於具有操作引數的操作
如果操作的定義需要上述描述之外的額外程式碼,您可以將程式碼放入字典 custom_definition_misc
中。鍵是操作名稱,值是程式碼。
special_op_handler
:在 frontend_dialect_transformer.cpp 中建立特殊的匯入函式。目前,特殊處理程式用於具有操作引數的操作
如果操作的定義需要上述描述之外的額外程式碼,您可以將程式碼放入字典 custom_definition_misc
中。鍵是操作名稱,值是程式碼。
為了執行 gen_onnx_mlir.py,必須安裝 ONNX。請參閱 Readme。在您的建置目錄中,執行以下命令。
make OMONNXOpsIncTranslation
此命令將產生兩個檔案 (src/Dialect/ONNX/ONNXOps.td.inc 和 OpBuilderTable.inc),並將其複製到 src 目錄中的正確位置。如果您修改了 gen_onnx_mlir.py,您也需要檢查兩個產生的檔案。它們在 ONNX-MLIR 建置中被視為原始檔案,因此 ONNX-MLIR 的使用者不需要安裝特定版本的 ONNX。請勿直接修改這些檔案。您也可以使用在 utils 目錄中產生的檔案直接執行腳本。 python ../utils/gen_onnx_mlir.py
。
當新增新的 op 版本或變更 ONNX 版本時,我們也希望在我們支援的操作的 ONNX 文件中反映這些變更。雖然最新的 ONNX 規格 始終可用,但我們支援的規格通常會稍微落後一點,而且我們還會根據上一節提到的版本化名稱支援較舊的版本。
有一個方便的命令可以更新 ONNX 和 Krnl 方言,如下所示。
make onnx-mlir-docs
上述命令在通常的 build
目錄中執行,它會將新的方言 md 檔案直接安裝到 docs/Dialects
目錄中。
當新增操作/變更 Krnl 方言時,應該使用相同的命令。
ONNX-MLIR 專案是在 ONNX 版本為 1.7.0 時啟動的,並且不打算向後相容。我們依靠 onnx/converter 將模型轉換為 ONNX-MLIR 支援的版本。隨著 ONNX 版本的發展,ONNX-MLIR 嘗試跟進,但可能會落後於最新版本。
如先前所述,我們嘗試支援最新版本的 ONNX 操作。目前支援的每個操作的版本都記錄在 utils/gen_onnx_mlir.py 中。此機制提供了一些版本穩定性。若要檢查版本中的變更,請使用標誌「–check-version」執行 gen_onnx_mlir.py,並將報告變更。若要移至較新的版本,請手動更新腳本中的版本字典。
若要支援 op 的多個版本,應將選取的版本新增到 utils/gen_onnx_mlir.py 中的版本字典中。例如,對於 ReduceSum,支援兩個版本(opset):11 和 13。version_dic 中的對應項目是 'ReduceSum': [13, 11]
。
在 ONNX 方言中,最高版本的 op 在 op 名稱中沒有版本,而其他版本則帶有名稱,後跟 'V' 和版本號碼。例如,opset 13 的 ReduceSum 將是 ONNXReduceSumOp
,而 opset 11 的 ReduceSum 是 'ONNXReduceSumV11Op'。由於大多數 ONNX op 在升級到更高版本時都相容,因此我們可以保留方言中操作的名稱,只需更新 gen_onnx_mlir.py 中的 version_dict,而無需修改 ONNX-MLIR 中的程式碼。
匯入模型時,會使用不超過下一個可用版本的最高版本。對於 ReduceSum 的範例,如果 opset 為 12,則會選取 ONNXReduceSumV11Op。
要遷移新的 ONNX 版本,首先必須升級 third_part/onnx 以及您的 ONNX 安裝。接著,您可以使用 --check_operation_version
標籤來執行 gen_onnx_mlir.py。所有運算的最高版本將會輸出為新的 version_dict
。如果某個運算的介面沒有改變(根據 ONNX 的變更文件),您可以直接使用新版本。如果介面有變更,您可以將新版本插入版本列表的第一個位置。對於現有的程式碼,所有相關的程式碼都必須修改。舉例來說,當 ReduceSum 從版本 11 移至版本 13 時,ONNXReduceSumOp 會先被取代為 ONNXReduceSumOpV11。然後,版本 13 的程式碼將使用 ONNXReduceSumOp。這樣設計的原因是大多數的 ONNX 變更不會改變介面。我們不希望增加開發人員的負擔,讓他們必須記住使用哪個版本的運算,除非絕對必要。並非總是需要保留舊版本的程式碼,舊版本程式碼可能會被重寫為新的運算。因此,我們只需要有方言定義,而不需要推論或降低的程式碼。