昇騰CANN算子開發(fā)揭秘
開發(fā)者在利用昇騰硬件進(jìn)行神經(jīng)網(wǎng)絡(luò)模型訓(xùn)練或者推理的過程中,可能會遇到以下場景:
訓(xùn)練場景下,將第三方框架(例如TensorFlow、PyTorch等)的網(wǎng)絡(luò)訓(xùn)練腳本遷移到昇騰AI處理器時遇到了不支持的算子。
推理場景下,將第三方框架模型(例如TensorFlow、Caffe、ONNX等)使用ATC工具轉(zhuǎn)換為適配昇騰AI處理器的離線模型時遇到了不支持的算子。
網(wǎng)絡(luò)調(diào)優(yōu)時,發(fā)現(xiàn)某算子性能較低,影響網(wǎng)絡(luò)性能,需要重新開發(fā)一個高性能算子替換性能較低的算子。
推理場景下,應(yīng)用程序中的某些邏輯涉及到數(shù)學(xué)運(yùn)算(例如查找最大值,進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換等),希望通過自定義算子的方式實(shí)現(xiàn)這些邏輯,從而獲得性能提升。
此時我們就需要考慮進(jìn)行自定義算子的開發(fā),本期我們主要帶您了解CANN自定義算子的幾種開發(fā)方式和基本開發(fā)流程,讓您對CANN算子有宏觀的了解。
一、算子基本概念
相信大家對算子的概念并不陌生,這里我們來做簡單回顧。深度學(xué)習(xí)算法由一個個計(jì)算單元組成,我們稱這些計(jì)算單元為算子(Operator,簡稱OP)。
在網(wǎng)絡(luò)模型中,算子對應(yīng)層中的計(jì)算邏輯,例如:卷積層(Convolution Layer)是一個算子;全連接層(Fully-connected Layer, FC layer)中的權(quán)值求和過程,是一個算子。
再例如:tanh、ReLU等,為在網(wǎng)絡(luò)模型中被用做激活函數(shù)的算子。

二、CANN自定義算子開發(fā)方式
學(xué)習(xí)CANN自定義算子開發(fā)方式之前,我們先來了解一下CANN算子的運(yùn)行位置:包括AI Core和AI CPU。
AI Core是昇騰AI處理器的計(jì)算核心,負(fù)責(zé)執(zhí)行矩陣、向量、標(biāo)量計(jì)算密集的算子任務(wù)。
AI CPU負(fù)責(zé)執(zhí)行不適合跑在AI Core上的算子,是AI Core算子的補(bǔ)充,主要承擔(dān)非矩陣類、邏輯比較復(fù)雜的分支密集型計(jì)算。
CANN支持用戶使用多種方式來開發(fā)自定義算子,包括TBE DSL、TBE TIK、AICPU三種開發(fā)方式。其中TBE DSL、TBE TIK算子運(yùn)行在AI Core上,AI CPU算子運(yùn)行在AI CPU上。

1. 基于TBE開發(fā)框架的算子開發(fā)
TBE(Tensor Boost Engine:張量加速引擎)是CANN提供的算子開發(fā)框架,開發(fā)者可以基于此框架使用Python語言開發(fā)自定義算子,通過TBE進(jìn)行算子開發(fā)有TBE DSL、TBE TIK兩種方式。
TBE DSL(Domain-Specific Language ,基于特性域語言)開發(fā)方式
為了方便開發(fā)者進(jìn)行自定義算子開發(fā),CANN預(yù)先提供一些常用運(yùn)算的調(diào)度,封裝成一個個運(yùn)算接口,稱為基于TBE DSL開發(fā)。DSL接口已高度封裝,用戶僅需要使用DSL接口完成計(jì)算過程的表達(dá),后續(xù)的算子調(diào)度、算子優(yōu)化及編譯都可通過已有的接口一鍵式完成,適合初級開發(fā)用戶。
TBE TIK(Tensor Iterator Kernel)開發(fā)方式
TIK(Tensor Iterator Kernel)是一種基于Python語言的動態(tài)編程框架,呈現(xiàn)為一個Python模塊,運(yùn)行于Host CPU上。開發(fā)者可以通過調(diào)用TIK提供的API基于Python語言編寫自定義算子,TIK編譯器會將其編譯為昇騰AI處理器應(yīng)用程序的二進(jìn)制文件。
TIK需要用戶手工控制數(shù)據(jù)搬運(yùn)和計(jì)算流程,入門較高,但開發(fā)方式比較靈活,能夠充分挖掘硬件能力,在性能上有一定的優(yōu)勢。
2.?AI CPU算子開發(fā)方式
AI CPU算子的開發(fā)接口即為原生C++接口,具備C++程序開發(fā)能力的開發(fā)者能夠較容易的開發(fā)出AI CPU算子。AI CPU算子在AI CPU上運(yùn)行。
下面的開發(fā)方式一覽表,對上述幾種開發(fā)方式作對比說明,您可以根據(jù)各種開發(fā)方式的適用場景選擇適合的開發(fā)方式。

三、CANN算子編譯運(yùn)行?
算子構(gòu)成
一個完整的CANN算子包含四部分:算子原型定義、對應(yīng)開源框架的算子適配插件、算子信息庫和算子實(shí)現(xiàn)。這四個組成部分會在算子編譯運(yùn)行的過程中使用。

算子編譯
推理場景下,進(jìn)行模型推理前,我們需要使用ATC模型轉(zhuǎn)換工具將原始網(wǎng)絡(luò)模型轉(zhuǎn)換為適配昇騰AI處理器的離線模型,該過程中會對網(wǎng)絡(luò)中的算子進(jìn)行編譯。
訓(xùn)練場景下,當(dāng)我們跑訓(xùn)練腳本時,CANN內(nèi)部實(shí)現(xiàn)邏輯會先將開源框架網(wǎng)絡(luò)模型下發(fā)給Graph?Engine進(jìn)行圖編譯,該過程中會對網(wǎng)絡(luò)中的算子進(jìn)行編譯。
CANN算子的編譯邏輯架構(gòu)如下:

具體的CANN算子編譯流程如下,在編譯流程中會用到上文提到的算子的四個組成部分。
Graph Engine調(diào)用算子插件,將原始網(wǎng)絡(luò)模型中的算子映射為適配昇騰AI處理器的算子,從而將原始開源框架圖解析為適配昇騰AI處理器的圖。
調(diào)用算子原型庫校驗(yàn)接口進(jìn)行基本參數(shù)的校驗(yàn),校驗(yàn)通過后,會根據(jù)原型庫中的推導(dǎo)函數(shù)推導(dǎo)每個節(jié)點(diǎn)的輸出shape與dtype,進(jìn)行輸出tensor的靜態(tài)內(nèi)存的分配。
Graph Engine根據(jù)圖中數(shù)據(jù)將圖拆分為子圖并下發(fā)給FE。FE在處理過程中根據(jù)算子信息庫中算子信息找到算子實(shí)現(xiàn),將其編譯成算子kernel,最后將優(yōu)化后子圖返回給Graph Engine。
Graph Engine進(jìn)行圖編譯,包含內(nèi)存分配、流資源分配等,并向FE發(fā)送tasking請求,F(xiàn)E返回算子的taskinfo信息給Graph Engine,圖編譯完成后生成適配昇騰AI處理器的模型。
算子運(yùn)行
推理場景下,使用ATC模型轉(zhuǎn)換工具將原始網(wǎng)絡(luò)模型轉(zhuǎn)換為適配昇騰AI處理器的離線模型后,開發(fā)AscendCL應(yīng)用程序,加載轉(zhuǎn)換好的離線模型文件進(jìn)行模型推理,該過程中會進(jìn)行算子的調(diào)用執(zhí)行。
訓(xùn)練場景下,當(dāng)我們跑訓(xùn)練腳本時,內(nèi)部實(shí)現(xiàn)邏輯將開源框架網(wǎng)絡(luò)模型下發(fā)給Graph?Engine進(jìn)行圖編譯后,后續(xù)的訓(xùn)練流程會進(jìn)行算子的調(diào)用執(zhí)行。
CANN算子的運(yùn)行邏輯架構(gòu)如下:

具體流程如下,首先Graph Engine下發(fā)算子執(zhí)行請求給Runtime,然后Runtime會判斷算子的Task類型,若是TBE算子,則將算子執(zhí)行請求下發(fā)到AI Core上執(zhí)行;若是AI CPU算子,則將算子執(zhí)行請求下發(fā)到AI CPU上執(zhí)行。
四、算子開發(fā)流程
本章節(jié)以通過DSL開發(fā)方式開發(fā)一個Add算子為例,帶您快速體驗(yàn)CANN算子開發(fā)的流程。流程圖如下:

算子開發(fā)準(zhǔn)備
環(huán)境準(zhǔn)備:準(zhǔn)備算子開發(fā)及運(yùn)行驗(yàn)證所依賴的開發(fā)環(huán)境與運(yùn)行環(huán)境。工程創(chuàng)建:創(chuàng)建算子開發(fā)工程,有以下幾種實(shí)現(xiàn)方式:
基于MindStudio工具進(jìn)行算子開發(fā),直接使用MindStudio工具創(chuàng)建算子工程,會自動生成算子工程及代碼模板。
基于msopgen工具進(jìn)行開發(fā),會自動生成算子工程及代碼模板。
基于自定義算子樣例工程進(jìn)行開發(fā),開發(fā)者需要自己創(chuàng)建算子相關(guān)實(shí)現(xiàn)文件,或者基于已有樣例進(jìn)行修改。
下面以msopgen工具創(chuàng)建算子開發(fā)工程為例進(jìn)行介紹:
定義AddDSL算子的原型定義json文件,用于生成AddDSL的算子開發(fā)工程。例如,定義的json文件的名字為add_dsl.json,存儲路徑為:$HOME/sample,文件內(nèi)容如下:
使用msopgen工具生成AddDSL算子的開發(fā)工程。
“$HOME/Ascend”為CANN軟件安裝目錄;
“-f tf”參數(shù)代表選擇的原始框架為TensorFlow;
“ai_core-<soc_version>”代表算子在AI Core上運(yùn)行,<soc_version>為昇騰AI處理器的型號。
此命令執(zhí)行完后,會在$HOME/sample/AddDsl目錄下生成算子工程,工程中包含各交付件的模板文件,編譯腳本等,如下所示:
算子開發(fā)過程
實(shí)現(xiàn)AddDSL算子的原型定義。
算子原型定義文件包含算子注冊代碼的頭文件(*.h)以及實(shí)現(xiàn)基本校驗(yàn)、Shape推導(dǎo)的實(shí)現(xiàn)文件(*.cc)。
msopgen工具根據(jù)add_dsl.json文件在“op_proto/add_dsl.h”中生成了算子注冊代碼,開發(fā)者需要檢查自動生成的代碼邏輯是否正確,一般無需修改。
修改“op_proto/add_dsl.cc”文件,實(shí)現(xiàn)算子的輸出描述推導(dǎo)函數(shù)及校驗(yàn)函數(shù)。
- 在IMPLEMT_COMMON_INFERFUNC(AddDSLInferShape)函數(shù)中,填充推導(dǎo)輸出描述的代碼,針對AddDSL算子,輸出Tensor的描述信息與輸入Tensor的描述信息相同,所以直接將任意一個輸入Tensor的描述賦給輸出Tensor即可。
? ? ? ?- 在IMPLEMT_VERIFIER(AddDSL, AddDSLVerify)函數(shù)中,填充算子參數(shù)校驗(yàn)代碼。
實(shí)現(xiàn)AddDSL算子的計(jì)算邏輯。
“tbe/impl/add_dsl.py”文件中已經(jīng)自動生成了算子代碼的框架,開發(fā)者需要在此文件中修改add_dsl_compute函數(shù),實(shí)現(xiàn)此算子的計(jì)算邏輯。
add_dsl_compute函數(shù)的實(shí)現(xiàn)代碼如下:
配置算子信息庫
算子信息庫的路徑為“tbe/op_info_cfg/ai_core/<soc_version>/add_dsl.ini”,包含了算子的類型,輸入輸出的名稱、數(shù)據(jù)類型、數(shù)據(jù)排布格式等信息,msopgen工具已經(jīng)根據(jù)add_dsl.json文件將上述內(nèi)容自動填充,開發(fā)者無需修改。
AddDSL算子的信息庫如下:
實(shí)現(xiàn)算子適配插件
算子適配插件實(shí)現(xiàn)文件的路徑為“framework/tf_plugin/tensorflow_add_dsl_plugin.cc”,針對原始框架為TensorFlow的算子,CANN提供了自動解析映射接口“AutoMappingByOpFn”,如下所示:
以上為工程自動生成的代碼,開發(fā)者僅需要修改.OriginOpType("AddDSL")中的算子類型即可。此處我們僅展示算子開發(fā)流程,不涉及原始模型,我們不做任何修改。
至此,AddDSL算子的所有交付件都已開發(fā)完畢。
算子工程編譯及算子包部署
算子開發(fā)過程完成后,需要編譯自定義算子工程,生成自定義算子安裝包并進(jìn)行自定義算子包的安裝,將自定義算子部署到算子庫。
算子工程編譯
1. 修改build.sh腳本,配置算子編譯所需環(huán)境變量。
將build.sh中環(huán)境變量ASCEND_TENSOR_COMPILER_INCLUDE配置為CANN軟件頭文件所在路徑。
修改前,環(huán)境變量配置的原有代碼行如下:
修改后,新的代碼行如下:
${INSTALL_DIR}請?zhí)鎿Q為CANN軟件安裝后文件存儲路徑。
例如,若安裝的Ascend-cann-toolkit軟件包,則安裝后文件存儲路徑為:$HOME/Ascend/ascend-toolkit/latest。
2. 在算子工程目錄下執(zhí)行如下命令,進(jìn)行算子工程編譯。
編譯成功后,會在當(dāng)前目錄下創(chuàng)建build_out目錄,并在build_out目錄下生成自定義算子安裝包c(diǎn)ustom_opp_<target os>_<target architecture>.run。
自定義算子安裝包部署
以運(yùn)行用戶執(zhí)行如下命令,安裝自定義算子包。
命令執(zhí)行成功后,自定義算子包中的相關(guān)文件部署到CANN算子庫中。
算子運(yùn)行驗(yàn)證
算子包部署完成后,可以進(jìn)行ST測試(System Test)和網(wǎng)絡(luò)測試,對算子進(jìn)行運(yùn)行驗(yàn)證。
ST測試
ST測試的主要功能是:基于算子測試用例定義文件*.json生成單算子的om文件;使用AscendCL接口加載并執(zhí)行單算子om文件,驗(yàn)證算子執(zhí)行結(jié)果的正確性。ST測試會覆蓋算子實(shí)現(xiàn)文件,算子原型定義與算子信息庫,不會對算子適配插件進(jìn)行測試。
網(wǎng)絡(luò)測試
你可以將算子加載到網(wǎng)絡(luò)模型中進(jìn)行整網(wǎng)的推理驗(yàn)證,驗(yàn)證自定義算子在網(wǎng)絡(luò)中運(yùn)行結(jié)果是否正確。網(wǎng)絡(luò)測試會覆蓋算子開發(fā)的所有交付件,包含實(shí)現(xiàn)文件,算子原型定義、算子信息庫以及算子適配插件。
具體的驗(yàn)證過程請參考“昇騰文檔中心[1]”。
以上就是CANN自定義算子開發(fā)的相關(guān)知識點(diǎn),您也可以在“昇騰社區(qū)在線課程[2]”板塊學(xué)習(xí)視頻課程,學(xué)習(xí)過程中的任何疑問,都可以在“昇騰論壇[3]”互動交流!
相關(guān)參考:
[1]昇騰文檔中心:https://www.hiascend.com/zh/document
[2]昇騰社區(qū)在線課程:https://www.hiascend.com/zh/edu/courses
[3]昇騰論壇:https://www.hiascend.com/forum