戰(zhàn):從零部署YOLO推理全流程)
拿到Atlas 300V 24G這塊卡的時(shí)候我第一反應(yīng)其實(shí)是有點(diǎn)懵的。群里有人問(wèn)這是不是運(yùn)算加速卡還有人問(wèn)能不能拿來(lái)跑YOLO但官方手冊(cè)寫(xiě)得云里霧里社區(qū)里的帖子又零散得很。我花了差不多兩周時(shí)間從刷固件、配CANN到把YOLOv5的ONNX模型轉(zhuǎn)成OM格式再寫(xiě)代碼把推理跑通中間踩的坑比過(guò)去一年加起來(lái)都多。這篇文章就是想把Atlas到底怎么用起來(lái)這件事完整捋一遍特別圍繞YOLO部署這條主線給正準(zhǔn)備入坑的人一條能直接照著走的路。1. Atlas 300V 24G這塊卡先把它說(shuō)透1.1 它到底是什么性質(zhì)的硬件先回答熱搜里那個(gè)問(wèn)題Atlas 300V 24G是不是運(yùn)算加速卡是但不是GPU那種通用加速卡。它本質(zhì)上是華為昇騰系列的AI推理加速卡核心芯片是昇騰310P系列主打的是INT8精度下的神經(jīng)網(wǎng)絡(luò)推理加速。你可以把訓(xùn)練理解為出題推理理解為做題——這塊卡就是專門(mén)用來(lái)大規(guī)??焖僮鲱}的不是用來(lái)出題的。這也就意味著如果你拿它去跑訓(xùn)練會(huì)非常難受。雖然它理論上有FP16能力但驅(qū)動(dòng)、軟件棧、算子優(yōu)化全都是朝著推理場(chǎng)景優(yōu)化的。真正適合它的活兒是視頻流里跑目標(biāo)檢測(cè)YOLO系列是典型場(chǎng)景圖像分類、特征提取這類CV推理轉(zhuǎn)碼加推理一體化的視頻分析應(yīng)用多路并發(fā)的在線推理服務(wù)我個(gè)人是拿它來(lái)做邊緣側(cè)視頻結(jié)構(gòu)化服務(wù)的一路視頻流接入實(shí)時(shí)跑YOLOv5檢測(cè)行人車輛再輸出結(jié)構(gòu)化結(jié)果。這個(gè)場(chǎng)景用這塊卡很合適因?yàn)樗牡?、體積也不夸張能塞進(jìn)2U服務(wù)器里做高密度推理。1.2 24G顯存版與常見(jiàn)型號(hào)的參數(shù)對(duì)照Atlas 300V這個(gè)家族不算復(fù)雜但從型號(hào)看很容易眼暈。我做了個(gè)表把我接觸過(guò)的幾款放在一起對(duì)比方便你判斷自己手里的卡到底是哪個(gè)檔次型號(hào)顯存標(biāo)稱INT8算力功耗典型定位Atlas 300V24GB LPDDR4X約140 TOPS72W左右標(biāo)準(zhǔn)推理卡24G大顯存是賣(mài)點(diǎn)Atlas 300V Pro24GB LPDDR4X約140 TOPS72W左右增強(qiáng)版接口和編解碼能力略強(qiáng)Atlas 300I Pro24GB約140 TOPS72W左右主打視頻圖像分析Atlas 300I Duo48GB雙芯約280 TOPS150W左右兩張300I Pro合體這里要?jiǎng)潅€(gè)重點(diǎn)24G這個(gè)顯存看似不大但對(duì)于推理場(chǎng)景真的夠用。YOLOv5s的INT8模型轉(zhuǎn)成OM格式后大小只有十幾MB就算YOLOv8l這種大模型INT8量化完也就幾十MB。24G顯存意味著你可以同時(shí)塞進(jìn)幾十上百路視頻流的模型副本或者跑一個(gè)很大的batch這是這塊卡性價(jià)比的核心來(lái)源。算力方面140 TOPS是INT8稀疏算力實(shí)際拿到的有效算力要看模型結(jié)構(gòu)、算子融合情況、是否開(kāi)啟多batch等等。別被宣傳數(shù)字沖昏頭腦后面我會(huì)放實(shí)測(cè)數(shù)據(jù)給你參考。1.3 什么場(chǎng)景該選它什么場(chǎng)景別碰它先潑一盆冷水如果你的需求是我要用PyTorch隨便訓(xùn)練一個(gè)模型不要買(mǎi)Atlas直接買(mǎi)NVIDIA顯卡省心。昇騰的訓(xùn)練棧雖然現(xiàn)在也能用了但生態(tài)成熟度跟CUDA比還是有差距。反過(guò)來(lái)如果你是以下情況Atlas 300V就非常香手頭有訓(xùn)練好的ONNX模型YOLO、ResNet、BERT等要做推理部署對(duì)功耗有硬性要求機(jī)房或邊緣機(jī)柜電費(fèi)敏感需要高密度部署一臺(tái)機(jī)器插多張卡產(chǎn)品面向信創(chuàng)或國(guó)產(chǎn)化場(chǎng)景硬件選型有合規(guī)要求Atlas 300V 24G還有一個(gè)優(yōu)點(diǎn)是好買(mǎi)。相比Atlas 800訓(xùn)練服務(wù)器那種動(dòng)輒幾十萬(wàn)的大家伙單張推理卡的價(jià)格親民得多個(gè)人開(kāi)發(fā)者搞一張研究研究也算能承受。我這張就是公司采購(gòu)的測(cè)試卡渠道走的是正規(guī)代理到手還帶了全套線材和轉(zhuǎn)接板。2. 部署YOLO前的環(huán)境底子驅(qū)動(dòng)、固件、CANN的版本搭配2.1 拿到卡片后第一步刷固件和裝驅(qū)動(dòng)的順序這塊卡不是你插上就能用的。我先把正確的順序給你再講為什么必須按這個(gè)順序來(lái)安裝物理卡確認(rèn)被系統(tǒng)識(shí)別lspci能看到設(shè)備安裝NPU驅(qū)動(dòng)Driver安裝固件Firmware安裝CANN工具包配置環(huán)境變量并驗(yàn)證順序錯(cuò)一步后面全白搭。我最開(kāi)始就是先裝了CANN再裝驅(qū)動(dòng)結(jié)果ascend-dmi工具跑不起來(lái)報(bào)了一堆設(shè)備節(jié)點(diǎn)錯(cuò)誤查了半天才反應(yīng)過(guò)來(lái)是順序問(wèn)題。驅(qū)動(dòng)和固件的獲取路徑是華為昇騰社區(qū)的軟件包下載頁(yè)面。需要注意下載的時(shí)候選對(duì)硬件型號(hào)和操作系統(tǒng)版本這兩者的匹配矩陣官方有一個(gè)兼容性列表務(wù)必對(duì)照著查。我用的環(huán)境是Ubuntu 20.04 x86_64 內(nèi)核5.4驅(qū)動(dòng)選的對(duì)應(yīng)版本固件選的配套版本。安裝驅(qū)動(dòng)比較簡(jiǎn)單chmod x Ascend-hdk-*.run ./Ascend-hdk-*.run --full --install-for-all裝完驅(qū)動(dòng)后用npu-smi info命令驗(yàn)證一下能看到卡的溫度、顯存占用、算力狀態(tài)說(shuō)明驅(qū)動(dòng)層OK了。2.2 CANN工具包到底裝哪個(gè)版本CANN是昇騰的計(jì)算架構(gòu)全稱是Compute Architecture for Neural Networks類似CUDA在NVIDIA生態(tài)里的位置。模型轉(zhuǎn)換工具ATC、AI推理框架、算子庫(kù)全都在CANN里。CANN的版本迭代比較快社區(qū)版基本上半年左右就有一個(gè)大版本。我的建議是別追最新版選穩(wěn)定版。新版本往往伴隨著算子行為變化老舊模型可能出現(xiàn)意想不到的兼容問(wèn)題。我目前用的是CANN 7.0版本系列配合Atlas 300V系列穩(wěn)定性不錯(cuò)。CANN安裝也走.run包c(diǎn)hmod x Ascend-cann-toolkit_*.run ./Ascend-cann-toolkit_*.run --install裝完之后最重要的一件事是source環(huán)境變量source /usr/local/Ascend/ascend-toolkit/set_env.sh這個(gè)環(huán)境變量不source后面ATC工具找不到、python導(dǎo)入acl報(bào)錯(cuò)全都會(huì)冒出來(lái)。我習(xí)慣把它寫(xiě)進(jìn)~/.bashrc省得每次開(kāi)終端都手動(dòng)敲一遍。2.3 環(huán)境變量配置里最容易翻車的地方環(huán)境變量這塊有兩個(gè)點(diǎn)特別容易被忽略我單獨(dú)拎出來(lái)說(shuō)。第一個(gè)是PYTHONPATH。CANN的python接口叫pyACL它在toolkit里的python目錄下。如果你用的是虛擬環(huán)境conda venv之類一定要把CANN的python路徑加進(jìn)去否則import acl會(huì)直接報(bào)ModuleNotFoundError。第二個(gè)是芯片型號(hào)的標(biāo)識(shí)。ATC轉(zhuǎn)換模型時(shí)需要一個(gè)--soc_version參數(shù)它決定了生成OM格式時(shí)針對(duì)哪個(gè)芯片做算子優(yōu)化。Atlas 300V 24G對(duì)應(yīng)的是Ascend310P3或Ascend310P4視具體Pro版本而定。這個(gè)參數(shù)填錯(cuò)了比如填成Ascend310轉(zhuǎn)換能成功但跑起來(lái)性能會(huì)差很多因?yàn)樗阕記](méi)有針對(duì)310P系列優(yōu)化。驗(yàn)證環(huán)境是否就緒可以用一條命令ascend-dmi -i -t能看到設(shè)備健康狀態(tài)、算力溫度曲線就說(shuō)明驅(qū)動(dòng)、固件、CANN三層都通了。到這一步環(huán)境準(zhǔn)備工作才算真正收尾。3. YOLO模型落地的關(guān)鍵一步ONNX到OM的ATC轉(zhuǎn)換3.1 前置工作導(dǎo)出帶動(dòng)態(tài)軸的ONNX從訓(xùn)練框架到昇騰推理中間隔著模型格式的鴻溝。PyTorch的.pt文件不能直接被昇騰加載需要先導(dǎo)出成ONNX再用ATC工具轉(zhuǎn)成昇騰的OM格式。導(dǎo)出ONNX這一步看起來(lái)簡(jiǎn)單但有個(gè)關(guān)鍵坑YOLO模型里的NMS非極大值抑制操作導(dǎo)成ONNX時(shí)要格外小心。常規(guī)做法是導(dǎo)出時(shí)把檢測(cè)頭拆開(kāi)讓輸出保留為原始特征圖把NMS留給后處理在CPU上做。這背后的原因有兩個(gè)一是NMS操作本身有循環(huán)和動(dòng)態(tài)shapeONNX導(dǎo)出時(shí)可能會(huì)出warning甚至error尤其老版本的torch.onnx.export對(duì)這類動(dòng)態(tài)控制流支持不好。二是即使導(dǎo)出成功ATC轉(zhuǎn)換時(shí)NMS算子也可能不支持。昇騰的算子庫(kù)雖然覆蓋了常見(jiàn)算子但NMS這類邏輯復(fù)雜的算子覆蓋情況不穩(wěn)定跨版本差異很大。我導(dǎo)出的做法是在YOLO的模型類里加一個(gè)export模式forward里只保留backboneneckhead去掉NMS然后用固定shape導(dǎo)出比如640x640輸入。雖然昇騰支持動(dòng)態(tài)shape但固定shape在ATC轉(zhuǎn)換時(shí)能做更多圖優(yōu)化推理延遲更低。import torch def export_onnx(model, pathyolov5s.onnx): model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, path, opset_version11, input_names[images], output_names[output], dynamic_axesNone # 固定shape后續(xù)ATC轉(zhuǎn)換最穩(wěn)妥 )3.2 ATC轉(zhuǎn)換命令參數(shù)逐個(gè)拆解ATC工具是昇騰模型轉(zhuǎn)換的重頭戲命令行參數(shù)看著多但核心就是那么幾個(gè)。我先給一個(gè)實(shí)際可用的轉(zhuǎn)換命令atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_om \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP32 \ --input_formatNCHW各參數(shù)的含義我拆開(kāi)講--model輸入的ONNX模型路徑--framework55代表ONNX這是ATC約定的編碼0是Caffe1是MindSpore別記錯(cuò)--output輸出OM文件的路徑前綴--input_shape輸入張量的shape跟ONNX導(dǎo)出時(shí)的輸入對(duì)齊--soc_version指定目標(biāo)芯片型號(hào)這個(gè)前面說(shuō)了必須填對(duì)--insert_op_conf插入AIPP預(yù)處理配置這個(gè)很有用后面單獨(dú)說(shuō)--output_type輸出精度通常FP32就夠了轉(zhuǎn)換成功的標(biāo)志是終端打印出ATC run success并生成了.om文件。如果中途報(bào)錯(cuò)最常見(jiàn)的就是算子不支持Operator XX not supported解法有兩種一是升級(jí)CANN版本到更新算子庫(kù)二是修改模型結(jié)構(gòu)避開(kāi)該算子。YOLO系列模型基本沒(méi)遇到過(guò)算子不支持的場(chǎng)景除非你加了很奇怪的模塊。3.3 關(guān)于AIPP圖像預(yù)處理到底該放模型里還是放卡上AIPP是昇騰的AI預(yù)處理模塊可以在硬件層面完成圖像縮放、減均值、除方差、顏色通道轉(zhuǎn)換等操作。它的核心價(jià)值是把預(yù)處理從CPU卸載到硬件上推理pipeline更短。我問(wèn)一個(gè)直擊靈魂的問(wèn)題YOLO部署時(shí)圖像resize到640x640這個(gè)操作放哪里做最合適放CPU上用OpenCV做簡(jiǎn)單但是占用CPU周期放模型里做加一個(gè)resize層浪費(fèi)算力且效果不好放AIPP做硬件完成CPU零開(kāi)銷顯然AIPP是最優(yōu)解。AIPP的配置文件長(zhǎng)這樣aipp_op { aipp_mode: static input_format: YUV420SP_U8 src_image_size_w: 1920 src_image_size_h: 1080 crop { crop_size_w: 640 crop_size_h: 640 } resize { src_image_size_w: 1920 src_image_size_h: 1080 } csc_switch: true rbuv_swap_switch: false mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 }這里input_format要根據(jù)你的輸入源來(lái)定。我的輸入是視頻解碼后的YUV420SP數(shù)據(jù)所以在AIPP里做YUV到RGB的轉(zhuǎn)換。如果你輸入的是jpg解碼后的RGB圖像直接把input_format改成RGB888_U8就行。需要注意用了AIPP之后喂給模型的數(shù)據(jù)不再需要你在代碼里做預(yù)處理。如果你代碼里既用了AIPP又手動(dòng)做了resize出來(lái)的推理結(jié)果會(huì)完全錯(cuò)亂這個(gè)我踩過(guò)慘痛教訓(xùn)。3.4 動(dòng)態(tài)batch和動(dòng)態(tài)分辨率什么時(shí)候用什么時(shí)候別用ATC支持把模型轉(zhuǎn)成動(dòng)態(tài)shape版本也就是轉(zhuǎn)換時(shí)輸入shape寫(xiě)-1運(yùn)行時(shí)再指定實(shí)際shape。聽(tīng)起來(lái)很靈活但代價(jià)是性能——?jiǎng)討B(tài)shape下算子融合和圖優(yōu)化的空間變小推理延遲普遍比固定shape高20%-30%。我的經(jīng)驗(yàn)是能用固定shape就堅(jiān)決用固定。YOLO推理服務(wù)面對(duì)的視頻流分辨率一般是固定的1920x1080輸入resize到640x640這個(gè)鏈路完全確定動(dòng)態(tài)shape毫無(wú)必要。只有當(dāng)你無(wú)法預(yù)知輸入分辨率時(shí)才考慮動(dòng)態(tài)shape方案。如果確實(shí)需要?jiǎng)討B(tài)ATC命令里這樣寫(xiě)atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_dynamic \ --input_shapeimages:-1,3,-1,-1 \ --dynamic_dims640;960;1280 \ --soc_versionAscend310P3注意dynamic_dims那里分號(hào)分隔的是可選的分辨率檔位。運(yùn)行時(shí)模型會(huì)按最近匹配的原則選一個(gè)檔位執(zhí)行做不到真正的任意尺寸推理。4. 推理代碼怎么寫(xiě)pyACL方式跑通YOLOv5/v84.1 初始化、資源申請(qǐng)和模型加載轉(zhuǎn)到代碼環(huán)節(jié)。pyACL是昇騰的Python推理接口整體流程可以歸納為初始化-申請(qǐng)?jiān)O(shè)備-加載模型-準(zhǔn)備輸入輸出-執(zhí)行推理-后處理。先看初始化這段import acl # 初始化 ret acl.init() assert ret 0, facl.init failed: {ret} # 申請(qǐng)?jiān)O(shè)備0是設(shè)備ID ret acl.rt.set_device(0) assert ret 0, fset_device failed: {ret} # 創(chuàng)建上下文 context, ret acl.rt.create_context(0) assert ret 0, fcreate_context failed: {ret}這三個(gè)步驟是固定的代碼結(jié)構(gòu)基本不會(huì)變。經(jīng)驗(yàn)是初始化失敗大概率是環(huán)境變量沒(méi)source或者CANN版本和驅(qū)動(dòng)版本不匹配排查優(yōu)先級(jí)最高。模型加載用的是acl.mdl.load_from_file# 加載OM模型 model_id, ret acl.mdl.load_from_file(yolov5s_om.om) assert ret 0, fload model failed: {ret} # 獲取模型描述信息 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id)模型描述里包含了輸入輸出的具體shape、數(shù)據(jù)類型后面分配內(nèi)存要用到。建議在加載后打印一下desc里的維度信息確認(rèn)和ATC轉(zhuǎn)換時(shí)的規(guī)劃一致。4.2 輸入輸出的數(shù)據(jù)搬運(yùn)與內(nèi)存管理這可能是整個(gè)pyACL流程里最容易出錯(cuò)的部分。昇騰的設(shè)備內(nèi)存和主機(jī)內(nèi)存是分開(kāi)的推理數(shù)據(jù)必須先搬到設(shè)備側(cè)模型輸出也要從設(shè)備側(cè)搬回來(lái)。輸入側(cè)的標(biāo)準(zhǔn)流程是創(chuàng)建設(shè)備側(cè)內(nèi)存acl.rt.malloc獲取模型輸入buffer地址acl.mdl.get_input_data_ptr把圖像數(shù)據(jù)拷貝到設(shè)備側(cè)執(zhí)行模型推理核心代碼如下# 創(chuàng)建設(shè)備側(cè)內(nèi)存 dev_ptr, ret acl.rt.malloc(640*640*3, 2) # 這里的2是內(nèi)存對(duì)齊單位固定寫(xiě)2 # 獲取模型輸入緩存地址 input_ptr acl.mdl.get_input_data_ptr(model_desc, 0) # 把數(shù)據(jù)拷貝到輸入buffer ret acl.rt.memcpy(input_ptr, 640*640*3, dev_ptr, 640*640*3, acl.rt.MEMCPY_DEVICE_TO_DEVICE)這里有個(gè)細(xì)節(jié)很多人會(huì)忽略get_input_data_ptr拿到的是模型內(nèi)部的固定buffer你不需要自己重新malloc直接把數(shù)據(jù)memcpy到這個(gè)ptr上就行。如果你自己malloc了一片新內(nèi)存再傳給推理接口反而可能因?yàn)閮?nèi)存對(duì)齊告警導(dǎo)致推理失敗。輸出的處理邏輯類似關(guān)鍵是要知道輸出有幾個(gè)tensor。YOLO模型如果去掉NMS后導(dǎo)出輸出通常是一個(gè)大tensorshape是[1, 25200, 85]這種格式Y(jié)OLOv5在640x640輸入下。其中25200就是三種特征層預(yù)測(cè)框數(shù)量80x8040x4020x208400乘以3個(gè)anchor2520085是4個(gè)框坐標(biāo)1個(gè)置信度80個(gè)類別概率。這個(gè)結(jié)構(gòu)在后處理parse時(shí)需要記住。4.3 后處理對(duì)齊坐標(biāo)換算、置信度過(guò)濾、NMS拿到模型輸出不是終點(diǎn)還得把它變成真正有用的檢測(cè)框。C代碼里大家喜歡用結(jié)構(gòu)化體存儲(chǔ)Python里簡(jiǎn)單numpy數(shù)組直接處理。import numpy as np # 假設(shè)output是模型輸出的numpy數(shù)組shape為(25200, 85) boxes output[..., :4] # cx, cy, w, h scores output[..., 4] # 置信度 class_probs output[..., 5:] # 類別概率 # 過(guò)濾低置信度框 mask scores 0.5 boxes boxes[mask] scores scores[mask] class_probs class_probs[mask] # 計(jì)算最終類別和得分 class_ids class_probs.argmax(axis1) final_scores scores * class_probs.max(axis1)NMS我用的是torchvision.ops.nms或者cv2.dnn.NMSBoxes都可以。如果追求性能建議用C實(shí)現(xiàn)后處理或者把NMS放到推理卡的CPU側(cè)做——但這屬于后期優(yōu)化初版先用Python跑通邏輯最重要。有個(gè)很隱蔽的坑模型的輸出格式取決于導(dǎo)出時(shí)怎么寫(xiě)的。如果導(dǎo)出時(shí)把檢測(cè)頭拆開(kāi)分別輸出三個(gè)特征層各自輸出那后處理邏輯就完全是另一套需要分別對(duì)三個(gè)輸出做解碼再合并。所以寫(xiě)后處理之前先仔細(xì)看一眼ONNX模型的結(jié)構(gòu)或者直接打印輸出tensor的shape確認(rèn)。4.4 實(shí)測(cè)性能數(shù)據(jù)與參數(shù)調(diào)優(yōu)記錄寫(xiě)到這里上一組我的實(shí)測(cè)數(shù)據(jù)供你參考。測(cè)試環(huán)境硬件Atlas 300V 24G模型YOLOv5s輸入640x640INT8量化后OM約15MB輸入視頻流抽幀1920x1080 - AIPP resize到640x640精度FP32輸出項(xiàng)目數(shù)據(jù)單幀模型推理延遲約8-12ms端到端單路延遲含解碼前后處理約25-30ms單芯片并發(fā)路數(shù)1080p視頻12-16路連續(xù)運(yùn)行72小時(shí)穩(wěn)定性無(wú)異常顯存占用穩(wěn)定整卡功耗約50-70W這個(gè)數(shù)據(jù)對(duì)于一臺(tái)2U服務(wù)器來(lái)說(shuō)性價(jià)比非常能打。如果換成YOLOv8s推理延遲差不多在10-15ms路數(shù)會(huì)略降。如果上YOLOv8n或者YOLOv5n這種輕量模型單幀延遲甚至可以壓到5ms以內(nèi)。一個(gè)提升吞吐量的小技巧是開(kāi)batch。比如同一個(gè)模型同時(shí)喂batch4的數(shù)據(jù)雖然單batch延遲會(huì)從8ms升到15ms左右但吞吐量翻了約2.5倍。這個(gè)trade-off對(duì)視頻流多路場(chǎng)景很劃算畢竟視頻流天然就是多路并發(fā)的。5. 多路并發(fā)與生產(chǎn)化配置建議5.1 異步推理與流水線設(shè)計(jì)初版代碼只要能跑通單幀推理距離能上生產(chǎn)還有一大步。視頻分析場(chǎng)景中最影響體驗(yàn)的就是并發(fā)能力。昇騰提供了異步推理接口核心思想是提交推理-立刻返回-稍后取結(jié)果這樣在等推理完成的同時(shí)CPU可以繼續(xù)做下一幀的預(yù)處理。官方推薦的標(biāo)準(zhǔn)流水線是線程A負(fù)責(zé)拉流和解碼把解碼后的幀放入隊(duì)列線程B負(fù)責(zé)AIPP預(yù)處理和模型推理用異步接口發(fā)請(qǐng)求線程C負(fù)責(zé)后處理和結(jié)果上報(bào)這個(gè)方案跑起來(lái)后單路的吞吐優(yōu)化空間就打開(kāi)了。我實(shí)際跑下來(lái)同樣的模型異步方案比同步方案端到端延遲低了大概40%。異步接口的關(guān)鍵是acl.mdl.execute_asyncret acl.mdl.execute_async(model_id, input_ptr, output_ptr, batch_num, stream)其中stream需要先創(chuàng)建stream, ret acl.rt.create_stream()注意異步推理的所有內(nèi)存操作必須在同一個(gè)stream上下文中同步時(shí)用acl.rt.synchronize_stream。5.2 顯存放不下模型副本時(shí)怎么辦24G顯存看起來(lái)大但如果你同時(shí)加載多個(gè)不同模型比如一個(gè)YOLO做檢測(cè)一個(gè)ResNet做分類再一個(gè)OCR模型做文字識(shí)別疊加起來(lái)還是挺可觀的。我遇到過(guò)一種情況模型加載成功了但推理時(shí)頻繁報(bào)device memory not enough排查了半天是顯存碎片化問(wèn)題。解決思路有三個(gè)業(yè)務(wù)上錯(cuò)峰加載不同模型不同時(shí)間段使用加載新模型前先卸載舊模型復(fù)用一個(gè)context不同模型共享同一個(gè)device和context減少上下文切換開(kāi)銷減小AIPP緩存如果AIPP設(shè)置的圖像尺寸過(guò)大顯存會(huì)被預(yù)留給大圖處理可以動(dòng)態(tài)調(diào)整其中復(fù)用context是最推薦的也是我實(shí)際用到的方案。代碼上就是加載多個(gè)模型到同一個(gè)context推理時(shí)按model_id區(qū)分調(diào)用。5.3 運(yùn)維側(cè)的幾個(gè)監(jiān)控姿勢(shì)生產(chǎn)環(huán)境里除了讓功能跑通還要讓問(wèn)題能被及時(shí)發(fā)現(xiàn)。昇騰提供了npu-smi命令行工具建議寫(xiě)進(jìn)監(jiān)控體系里。# 查看所有卡的狀態(tài) npu-smi info # 查看指定卡的詳細(xì)信息和算力使用 npu-smi info -t board -i 0 npu-smi info -t usages -i 0重點(diǎn)關(guān)心的指標(biāo)有三個(gè)溫度超過(guò)85度就要檢查風(fēng)道和散熱顯存占用持續(xù)超過(guò)80%要評(píng)估是否擴(kuò)容或優(yōu)化模型算力使用率如果長(zhǎng)期跑不滿檢查是不是CPU側(cè)的前后處理成了瓶頸我還習(xí)慣寫(xiě)一個(gè)定時(shí)腳本每5分鐘把npu-smi的到日志里出問(wèn)題時(shí)可以回溯。5.4 最后的提醒多卡并聯(lián)和分布式推理如果你有更高的算力需求Atlas 300V是支持多卡并聯(lián)的。一臺(tái)機(jī)器插4張卡通過(guò)昇騰的集合通信庫(kù)做數(shù)據(jù)分發(fā)。但我要說(shuō)句實(shí)話多卡并聯(lián)的復(fù)雜度上升不是線性的至少是平方級(jí)。驅(qū)動(dòng)配置、PCIe帶寬、任務(wù)調(diào)度都要重新考慮。一個(gè)更接地氣的建議是先單卡跑通把卡的能力吃透再?zèng)Q定要不要上多卡。很多客戶場(chǎng)景單卡24G就已經(jīng)富余了你與其糾結(jié)多卡并行不如把單卡的batch調(diào)度和異步流水線打磨到極致這帶來(lái)的收益更大也不折騰人。我個(gè)人最近在做的擴(kuò)展是用Atlas 300V同時(shí)跑YOLOv8加一個(gè)輕量姿態(tài)估計(jì)模型一個(gè)做檢測(cè)一個(gè)做關(guān)鍵點(diǎn)利用24G大顯存把兩個(gè)模型同時(shí)駐留效果意外地好。這種一卡多模型的玩法反而是很多用戶沒(méi)充分挖掘的方向。