Transformer模型在自然語(yǔ)言領(lǐng)域被提出后,目前已經(jīng)擴(kuò)展到了計(jì)算機(jī)視覺(jué)、語(yǔ)音等諸多領(lǐng)域。然而,雖然Transformer模型在語(yǔ)音識(shí)別領(lǐng)域有著更好的準(zhǔn)確率,但還面臨著一個(gè)問(wèn)題,計(jì)算復(fù)雜度和內(nèi)存儲(chǔ)存開(kāi)銷會(huì)隨著語(yǔ)音時(shí)長(zhǎng)的增加而變大。
技術(shù)普及在于產(chǎn)品價(jià)格親民,而價(jià)格親民在于技術(shù)易落地易實(shí)現(xiàn),離線語(yǔ)音識(shí)別應(yīng)運(yùn)而生,運(yùn)用深度學(xué)習(xí)等技術(shù)且只需在本地進(jìn)行運(yùn)算就可實(shí)現(xiàn)人機(jī)語(yǔ)音交互,而且具備實(shí)時(shí)的響應(yīng)速度、無(wú)需聯(lián)網(wǎng)的特點(diǎn),能更好的應(yīng)用在大小家電、照明、車載、健康儀器、教育設(shè)備等行業(yè)。
本次內(nèi)容由社區(qū)優(yōu)秀開(kāi)發(fā)者、首百第四批新品體驗(yàn)官——宋星辰將帶領(lǐng)大家DIY個(gè)人專屬離線語(yǔ)音識(shí)別,在X3派上玩轉(zhuǎn)一億參數(shù)量的超大Transformer。歡迎感興趣的旭友們點(diǎn)擊注冊(cè)地平線開(kāi)發(fā)者社區(qū)交流討論,相關(guān)文檔詳見(jiàn)地平線開(kāi)發(fā)者社區(qū)。
技術(shù)詳解
Step-1:模型轉(zhuǎn)換的環(huán)境準(zhǔn)備
環(huán)境準(zhǔn)備本身沒(méi)有什么奇技淫巧,這里想重點(diǎn)描述的是:pytorch版本的升級(jí)對(duì)精度瓶頸和速度瓶頸分析所帶來(lái)的跨越式的體驗(yàn)提升。
在地平線開(kāi)發(fā)者社區(qū)官方提供的安裝包中,為了兼容訓(xùn)練算法包海圖(HAT),安裝的 pytorch版本為1.10.0。pytorch版本本身對(duì)模型轉(zhuǎn)換的精度不會(huì)有什么影響,但是不同版本的pytorch所導(dǎo)出的onnx,在節(jié)點(diǎn)(node, 或稱op)命名上有很大的區(qū)別。
就一般情況而言,當(dāng)torch版本為1.10.0時(shí),Node的命名采用了“optype+數(shù)字”的形式,這種形式的缺點(diǎn)是:當(dāng)模型 Layer/SubLayer數(shù)量非常多(比如本文一億參數(shù)量的Transformer,包含的 OP 有上千個(gè)),我們很難一眼定位 Conv_xx 到底是第幾層的第幾個(gè)卷積。
torch 1.10.0版本結(jié)果
通常一個(gè)量化明顯掉點(diǎn)的模型,會(huì)從中間某一個(gè)OP開(kāi)始有鮮明的Cosine Similarity損失,在當(dāng)前的命名格式下,為了找到這個(gè)OP在原始模型中的位置(第x Layer的第y SubLayer),我們需要從頭開(kāi)始一個(gè)一個(gè)數(shù),這無(wú)疑是效率低下的。當(dāng)然,隨著對(duì)模型細(xì)節(jié)的熟悉,定位的速度會(huì)越來(lái)越快,但這不能從根本上解決效率問(wèn)題。
相反,當(dāng)torch版本升級(jí)到1.13.0時(shí)Node的命名采用了“Layer+SubLayer+Attribute+OP”的形式,一眼定位,一眼丁真,大大節(jié)省了開(kāi)發(fā)人員定位精度問(wèn)題(哪層的OP相似度下降嚴(yán)重)or 速度問(wèn)題(哪層的OP跑在CPU)的時(shí)間。
torch 1.13.0版本結(jié)果
Step-2:C++ Demo 的編譯
由于X3派板端內(nèi)存有限,編譯C++ Demo時(shí)筆者采用了交叉編譯的形式,在開(kāi)發(fā)機(jī)上sudo安裝aarch gcc即可。至于使用C++實(shí)現(xiàn)BPU模型的板上推理,實(shí)現(xiàn)推理的邏輯本身是一件很容易的事情,無(wú)論是使用python實(shí)現(xiàn)亦或是C++實(shí)現(xiàn),其流程都是固定的,也即:
關(guān)于這四個(gè)步驟的API調(diào)用范例,官方 C++ 文檔中都給出了比較詳細(xì)的 know-how 示例,但是大多數(shù)都是單模型 + 單輸入的簡(jiǎn)單case,在語(yǔ)音識(shí)別模型中,會(huì)涉及到 多模型(多個(gè)bin串聯(lián))+ 多輸入(一個(gè)bin有多個(gè)輸入)的情況,這里給出本文的針對(duì)性示例:
// BPUAsrModel 類定義 using hobot::easy_dnn::Model; using hobot::easy_dnn::DNNTensor; using hobot::easy_dnn::TaskManager; using hobot::easy_dnn::ModelManager; class BPUAsrModel : public AsrModel { public: BPUAsrModel() = default; ~BPUAsrModel(); BPUAsrModel(const BPUAsrModel& other); void Read(const std::string& model_dir); void PrepareEncoderInput(const std::vector>& chunk_feats); // 其他成員函數(shù)... protected: void ForwardEncoderFunc(const std::vector>& chunk_feats, std::vector>* ctc_prob) override; private: // models std::shared_ptr encoder_model_ = nullptr; std::shared_ptr ctc_model_ = nullptr; // input/output tensors, 使用vector方便應(yīng)對(duì)單模型多輸入的情況 std::vector> encoder_input_, encoder_output_; std::vector> ctc_input_, ctc_output_; // 其他成員變量... };
Step-3:正式開(kāi)始模型轉(zhuǎn)換
(一)一行代碼 改寫Transformer模型
使用工具鏈去轉(zhuǎn)換NLP領(lǐng)域的原生Transformer模型,體驗(yàn)可能會(huì)是非常糟糕的(甚至?xí)谵D(zhuǎn)換過(guò)程中直接報(bào)錯(cuò))。這是因?yàn)镹LP中的Transformer,輸入tensor的維度通常是二維或三維,類型既包含float也包含long 。而XJ3芯片在設(shè)計(jì)時(shí)只著重考慮了視覺(jué)任務(wù),通常都是浮點(diǎn)的四維圖像輸入,工具鏈也只對(duì)這類視覺(jué)模型有比較極致的體驗(yàn)優(yōu)化。
那么,為了轉(zhuǎn)換NLP類的Transformer,我們是否需要重頭訓(xùn)練一個(gè)四維數(shù)據(jù)流的模型呢?答案顯然是否定的,本文通過(guò)等價(jià)替換和抽象封裝,實(shí)現(xiàn)了一行代碼將原生Transformer等價(jià)改寫為BPU友好的Transformer:
# 一鍵完成 3D數(shù)據(jù)流 Transformer 等價(jià)轉(zhuǎn)換 4D數(shù)據(jù)流 Transformer Encoder4D = wenet.bin.export_onnx_bpu.BPUTransformerEncoder(Encoder3D)
這里的BPU TransformerEncoder就像是科幻電影中的“外骨骼機(jī)甲”一樣,其內(nèi)核沒(méi)變(權(quán)重參數(shù)值沒(méi)變),但是功能上實(shí)現(xiàn)了針對(duì)性升級(jí)。具體而言,在 BPUTransformerEncoder 的構(gòu)造過(guò)程中,會(huì)逐OP遍歷原生的 Encoder3D,并對(duì)其中的 BPU 不友好的 OP 實(shí)施等價(jià)改寫。
(二) 一句命令 走完轉(zhuǎn)換全流程
一個(gè)完整pytorch模型到bpu模型的轉(zhuǎn)換流程,一般要經(jīng)過(guò)如下四步:
①pytorch 模型 轉(zhuǎn) onnx 模型;
②構(gòu)造 Calibration 數(shù)據(jù);
③構(gòu)造 config.yaml;
④調(diào)用 hb_mapper 執(zhí)行 onnx 轉(zhuǎn) bpu bin。
在WeNet開(kāi)源的代碼中,我們用人民群眾喜聞樂(lè)見(jiàn)的python把這四個(gè)步驟 “粘” 到了一起,使用如下命令,就可走完全流程。
python3 $WENET_DIR/tools/onnx2horizonbin.py \ --config ./model_subsample8_parameter110M/train.yaml \ --checkpoint ./model_subsample8_parameter110M/final.pt \ --output_dir ./model_subsample8_parameter110M/sample50_chunk8_leftchunk16 \ --chunk_size 8 \ --num_decoding_left_chunks 16 \ --max_samples 50 \ --dict ./model_subsample8_parameter110M/units.txt \ --cali_datalist ./model_subsample8_parameter110M/calibration_data/data.list
其中:
config(描述了模型配置,幾層layer等);
checkpoint(pytorch 浮點(diǎn)模型);
output_dir(.bin 文件輸出目錄);
chunk_size(跟識(shí)別有關(guān)的解碼參數(shù));
num_decoding_left_chunks(跟識(shí)別有關(guān)的解碼參數(shù));
max_samples(使用多少句數(shù)據(jù)制作calibration data);
dict(字典);
cali_datalist(描述了標(biāo)定數(shù)據(jù)的位置)。
綜上,我們對(duì)如下這四個(gè)步驟實(shí)現(xiàn)了完完全全的 python化封裝 和 一體化串聯(lián) ,真正實(shí)現(xiàn)了一句命令(python3 $WENET_DIR/tools/onnx2horizonbin.py ...)走完全部轉(zhuǎn)換流程。
Demo展示
硬件配置:
模型配置:
解碼速度對(duì)比(單核單線程,量化后的模型):
本文轉(zhuǎn)自地平線開(kāi)發(fā)者社區(qū)
原作者:xcsong
-
嵌入式
+關(guān)注
關(guān)注
5052文章
18909瀏覽量
300719 -
語(yǔ)音識(shí)別
+關(guān)注
關(guān)注
38文章
1703瀏覽量
112411 -
人工智能
+關(guān)注
關(guān)注
1789文章
46316瀏覽量
236478 -
Transformer
+關(guān)注
關(guān)注
0文章
136瀏覽量
5961
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論