戰(zhàn):從環(huán)境配置到性能調(diào)優(yōu))
先交代一下背景。去年我們做視覺(jué)檢測(cè)項(xiàng)目服務(wù)器端目標(biāo)檢測(cè)模型定的是YOLO但硬件那邊一直定不下來(lái)預(yù)算和供貨渠道都卡得很緊。后來(lái)項(xiàng)目組拿到一塊華為昇騰Atlas 300V 24G群里第一個(gè)問(wèn)題刷屏式跳出來(lái)這玩意到底是啥是運(yùn)算加速卡嗎能拿來(lái)部署YOLO嗎說(shuō)實(shí)話當(dāng)時(shí)能一句話講清楚的人真不多。這周剛好接手的新項(xiàng)目又要重復(fù)搭一遍這套環(huán)境我干脆把從接卡、裝驅(qū)動(dòng)、轉(zhuǎn)模型、寫(xiě)推理到壓性能的完整過(guò)程寫(xiě)下來(lái)給準(zhǔn)備在Atlas系列上做目標(biāo)檢測(cè)的朋友趟條路。1. Atlas 300V 24G一塊常被誤解的AI運(yùn)算加速卡1.1 先搞清楚它是什么、不是什么很多人第一次聽(tīng)到“Atlas 300V 24G”的第一反應(yīng)是這又是個(gè)什么顯卡是不是跟游戲顯卡似的插上就能用這里得先把概念掰清楚。Atlas 300V是昇騰系列里的一塊AI推理加速卡核心是自研的昇騰NPU不是GPU也不是普通的數(shù)據(jù)處理卡。它不帶顯示輸出接口你不能把它當(dāng)顯卡插上點(diǎn)亮屏幕它是一塊純算力卡專門(mén)用來(lái)跑神經(jīng)網(wǎng)絡(luò)推理任務(wù)。24G指的是板載內(nèi)存容量用來(lái)放模型權(quán)重和中間特征圖跟顯存的概念類似但物理上是NPU側(cè)的內(nèi)存。我們手上這塊卡的基本參數(shù)以Atlas 300V Pro 24G為例內(nèi)存24GB典型功耗70W左右不需要外接供電PCIe插槽供電就夠形態(tài)是半高半長(zhǎng)、單槽位普通服務(wù)器機(jī)箱可以直接塞進(jìn)去算力規(guī)模INT8下官標(biāo)大概在一百多TOPS這個(gè)量級(jí)完全夠跑輕量級(jí)檢測(cè)模型。不用被TOPS這個(gè)單位嚇到你可以把它粗略理解成“每秒能做多少次整數(shù)運(yùn)算”YOLOv5s這種規(guī)模的模型跑起來(lái)綽綽有余。這里要多說(shuō)一句Atlas 300V的定位是“推理側(cè)”加速卡。它確實(shí)也能參與訓(xùn)練但如果你拿它來(lái)跟訓(xùn)練卡比會(huì)比較吃力。推理任務(wù)的特征是模型已經(jīng)訓(xùn)練好了要的是低延遲、高吞吐地把推理跑起來(lái)而這塊卡就是為這種場(chǎng)景設(shè)計(jì)的。理解了這一點(diǎn)你就知道為什么部署YOLO這種檢測(cè)模型它其實(shí)非常對(duì)口。1.2 為什么團(tuán)隊(duì)最后選了它在我們這個(gè)項(xiàng)目里選Atlas 300V不是拍腦袋決定的。預(yù)算是一個(gè)原因但更核心的是整機(jī)環(huán)境的兼容性。當(dāng)時(shí)服務(wù)器是國(guó)產(chǎn)化平臺(tái)系統(tǒng)用的是麒麟相關(guān)版本在這種條件下有些海外加速卡在驅(qū)動(dòng)層面兼容性很折騰內(nèi)核版本對(duì)不上、驅(qū)動(dòng)編譯失敗都是家常便飯。Atlas整套工具鏈在國(guó)產(chǎn)OS上支持比較完善有官方適配好的驅(qū)動(dòng)和固件包裝完基本不會(huì)遇到太離譜的排錯(cuò)問(wèn)題。另外就是功耗和形態(tài)。一臺(tái)2U機(jī)箱里要塞多張卡做視頻分析24G內(nèi)存和70W功耗意味著可以很從容地做多卡堆疊。對(duì)比我們之前用的方案光配電和散熱就省了很多事。當(dāng)然它的生態(tài)沒(méi)有CUDA那么成熟這意味著需要額外學(xué)習(xí)一套工具鏈。這個(gè)代價(jià)不能忽略但一旦環(huán)境搭好后面跑起來(lái)反而很穩(wěn)定。所以我的態(tài)度是如果你手頭項(xiàng)目對(duì)國(guó)產(chǎn)化有要求或者預(yù)算有限但又有大量視頻流要做檢測(cè)Atlas 300V 24G完全可以作為YOLO的部署平臺(tái)前提是你要愿意花半天時(shí)間啃一啃CANN的工具鏈說(shuō)明。2. 部署YOLO必備的CANN工具鏈2.1 CANN、AscendCL、MindX到底是個(gè)啥關(guān)系開(kāi)始動(dòng)手前你得先把這套名字搞明白不然看文檔會(huì)頭暈。CANN是昇騰的軟件??偡Q全稱Compute Architecture for Neural Networks你可以把它理解為昇騰的“CUDA”。它包括了算子庫(kù)、運(yùn)行時(shí)、編譯器、設(shè)備管理這些。AscendCL是CANN里給開(kāi)發(fā)者用的編程接口類似CUDA Runtime API你在代碼里加載模型、管理內(nèi)存、下發(fā)推理都是走它。MindX SDK是在CANN之上封裝的一套更上層工具提供pipeline式的視頻流處理能力適合快速搭建服務(wù)但靈活性不如直接用AscendCL。還有一個(gè)容易混淆的東西是MindSpore那是華為的深度學(xué)習(xí)訓(xùn)練框架跟PyTorch對(duì)位不是你部署推理模型必須用的。一個(gè)最簡(jiǎn)單的閉環(huán)是PyTorch訓(xùn)練YOLO或直接用現(xiàn)成權(quán)重→ 導(dǎo)出ONNX → 用CANN里的ATC工具轉(zhuǎn)成OM模型 → 再用AscendCL在代碼里加載OM模型做推理。不需要MindSpore也不一定需要MindX SDK但CANN是必需的。實(shí)操中很多朋友會(huì)問(wèn)我直接用torch_npu把PyTorch模型搬到NPU上行不行答案是也行但如果做的是服務(wù)化部署繞不開(kāi)的還是OM模型和AscendCL這條路徑。因?yàn)镺M模型經(jīng)過(guò)編譯器深度優(yōu)化后靜態(tài)圖執(zhí)行效率遠(yuǎn)高于動(dòng)態(tài)圖走解釋執(zhí)行。項(xiàng)目上線追求的是穩(wěn)定和性能不是開(kāi)發(fā)時(shí)一時(shí)爽。2.2 版本匹配這件事能省很多事第一次裝CANN時(shí)我吃了大虧。當(dāng)時(shí)沒(méi)仔細(xì)看版本兼容矩陣直接把最新版toolkit裝上結(jié)果驅(qū)動(dòng)版本不對(duì)npu-smi能看到卡但一跑模型就報(bào)“aclrtSetDevice failed”。排查半天最后發(fā)現(xiàn)是固件、驅(qū)動(dòng)、CANN三個(gè)版本之間沒(méi)有嚴(yán)格對(duì)應(yīng)。所以安裝順序和版本檢查一定要重視。建議的安裝順序是先裝固件和驅(qū)動(dòng)用npu-smi info命令確認(rèn)卡被識(shí)別再裝CANN toolkit社區(qū)版或商業(yè)版根據(jù)系統(tǒng)Python版本裝對(duì)應(yīng)的AscendCL Python接口有些版本在toolkit里自帶最后用官方環(huán)境檢查腳本跑一遍。裝完之后把環(huán)境變量固定下來(lái)建議寫(xiě)到環(huán)境配置里source /usr/local/Ascend/ascend-toolkit/set_env.sh然后驗(yàn)證npu-smi info如果能正常顯示卡型號(hào)和對(duì)應(yīng)顯存說(shuō)明驅(qū)動(dòng)這部分OK了。這里把版本匹配的關(guān)鍵檢查點(diǎn)整理成一個(gè)表方便對(duì)照檢查項(xiàng)常見(jiàn)坑建議固件、驅(qū)動(dòng)、CANN版本三者版本不匹配設(shè)備不可用按官方兼容矩陣一一對(duì)應(yīng)操作系統(tǒng)內(nèi)核版本內(nèi)核太新或太舊驅(qū)動(dòng)安裝失敗優(yōu)先選文檔列出的內(nèi)核版本Python環(huán)境默認(rèn)Python不是目標(biāo)版本用conda單獨(dú)建環(huán)境AscendCL接口接口版本和toolkit不一致確認(rèn)是配套的whl包版本匹配這個(gè)坑看起來(lái)簡(jiǎn)單但確實(shí)是我見(jiàn)過(guò)最多人在第一步就卡住的地方。尤其是新同學(xué)裝環(huán)境時(shí)恨不得全裝最新版結(jié)果踩坑踩到懷疑人生。我的習(xí)慣是先看硬件型號(hào)再搜對(duì)應(yīng)支持列表最后下載指定版本一步都不跳。2.3 硬件狀態(tài)檢查別讓卡在“半死不活”的狀態(tài)下開(kāi)工裝完驅(qū)動(dòng)之后強(qiáng)烈建議做一個(gè)基礎(chǔ)健康檢查。npu-smi info是最常用的命令能看到芯片溫度、當(dāng)前功耗、顯存占用、運(yùn)行狀態(tài)。我見(jiàn)過(guò)有人拿了一塊從別的機(jī)房拆下來(lái)的Atlas卡驅(qū)動(dòng)顯示正常但推理時(shí)速度奇慢最后看npu-smi才發(fā)現(xiàn)芯片溫度已經(jīng)飆到90多度風(fēng)扇轉(zhuǎn)速異常。這種問(wèn)題不提前看后面所有性能實(shí)驗(yàn)數(shù)據(jù)都是廢的。另外如果服務(wù)器有重啟計(jì)劃建議把NPU相關(guān)服務(wù)和驅(qū)動(dòng)加載做成開(kāi)機(jī)自啟。默認(rèn)安裝包不一定幫你想好這一點(diǎn)斷電重啟之后卡不被識(shí)別的情況太常見(jiàn)了。排查的時(shí)候先別慌大概率是驅(qū)動(dòng)沒(méi)自啟手動(dòng)加載一下就好。3. 從PyTorch到NPUYOLO模型完整轉(zhuǎn)換流程3.1 導(dǎo)出一份不會(huì)被卡脖子的小白ONNX模型第一步是把PyTorch權(quán)重轉(zhuǎn)成ONNX。這一步很多教程都沒(méi)講透實(shí)際上非常容易出問(wèn)題。YOLOv5或者YOLOv8官方倉(cāng)庫(kù)里都會(huì)給導(dǎo)出腳本但直接用默認(rèn)參數(shù)導(dǎo)出后往往會(huì)在ATC轉(zhuǎn)換時(shí)報(bào)一系列算子不支持的錯(cuò)。我這邊推薦的做法是先處理掉模型里的動(dòng)態(tài)分支然后用onnx-simplifier做一次簡(jiǎn)化。下面是一個(gè)最小導(dǎo)出腳本以YOLOv5為例import torch model torch.load(yolov5s.pt, map_locationcpu)[model].float() model.eval() dummy torch.zeros(1, 3, 640, 640) torch.onnx.export( model, dummy, yolov5s.onnx, opset_version11, input_names[images], output_names[output], dynamic_axes{images: {0: batch}, output: {0: batch}} )導(dǎo)出后跑一下python -m onnxsim yolov5s.onnx yolov5s_sim.onnx簡(jiǎn)化之后ATC基本就能認(rèn)全算子。這個(gè)步驟不要省它解決了我遇到的各類“Unsupported Op”問(wèn)題。還有一個(gè)小技巧導(dǎo)出時(shí)opset_version盡量用11太高版本的ONNX里有些算子老版本的ATC工具鏈還不認(rèn)識(shí)太低的話部分新結(jié)構(gòu)又會(huì)導(dǎo)出失敗。YOLO系列在opset 11下兼容性最穩(wěn)。3.2 用ATC把ONNX轉(zhuǎn)成OM拿到簡(jiǎn)化后的onnx進(jìn)入轉(zhuǎn)換環(huán)節(jié)。ATC是CANN自帶的離線模型轉(zhuǎn)換工具它做的事可以理解成把通用的ONNX圖編譯成昇騰NPU上真正高效執(zhí)行的指令序列。先記住一個(gè)關(guān)鍵參數(shù)soc_version它指的是你目標(biāo)芯片的型號(hào)。不同卡型號(hào)對(duì)應(yīng)的值不一樣我用的是Ascend310P系列對(duì)應(yīng)的版本號(hào)。怎么確認(rèn)最靠譜的方式是安裝好驅(qū)動(dòng)后執(zhí)行npu-smi info看卡名然后去CANN文檔查對(duì)應(yīng)soc_version不要靠猜。一個(gè)最基本的轉(zhuǎn)換命令長(zhǎng)這樣atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_om \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --output_typeFP32這里有一個(gè)容易忽略的重點(diǎn)input_shape里batch size寫(xiě)成1如果你希望模型支持動(dòng)態(tài)batch可以用--dynamic_batch_size1,2,4,8來(lái)指定可選檔位但動(dòng)態(tài)shape會(huì)犧牲一部分編譯優(yōu)化。多數(shù)推理場(chǎng)景下把batch固定下來(lái)是更聰明的選擇。另外一個(gè)參數(shù)是--output_type默認(rèn)FP32做INT8量化推理時(shí)后面會(huì)說(shuō)怎么處理。AIPP配置文件是另一個(gè)精華它可以把圖像縮放、減均值、除以標(biāo)準(zhǔn)差這些操作全部下沉到NPU里。配置長(zhǎng)這樣aipp_op { aipp_mode: static input_format: RGB888_U8 mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }YOLO訓(xùn)練時(shí)通常不設(shè)置復(fù)雜的mean和std只做除以255的歸一化所以這里mean填0var_reci填1/255。如果訓(xùn)練代碼里用了ImageNet的mean和std這里就要同步改。AIPP的位置非常關(guān)鍵它決定了數(shù)據(jù)在進(jìn)入NPU之前誰(shuí)來(lái)做預(yù)處理這也是后面性能優(yōu)化的核心之一。轉(zhuǎn)換成功后會(huì)生成yolov5s_om.om至此模型這部分完成。如果這一步報(bào)錯(cuò)九成是soc_version寫(xiě)錯(cuò)、ONNX沒(méi)簡(jiǎn)化、或CANN版本與芯片不匹配排查思路非常直接按順序查就好。3.3 寫(xiě)一個(gè)能跑起來(lái)的最小推理程序模型轉(zhuǎn)好了下一步是寫(xiě)推理代碼。如果你用MindX SDK可以走pipeline配置但我更推薦用AscendCL因?yàn)榱鞒谈该鞒鰡?wèn)題好排查。AscendCL的思路跟CUDA有點(diǎn)像初始化設(shè)備加載模型準(zhǔn)備輸入輸出內(nèi)存執(zhí)行推理再取回結(jié)果。下面是一個(gè)最小閉環(huán)的Python版本示意import acl import numpy as np def init_npu(): acl.init() ret acl.rt.set_device(0) context acl.rt.create_context(0) return ret def load_model(om_path): ret, model_id acl.mdl.load_from_file(om_path) desc acl.mdl.create_desc() ret acl.mdl.get_desc(desc, model_id) return model_id, desc def infer(model_id, desc, input_np): # 申請(qǐng)輸入輸出內(nèi)存并綁定 # 把input_np拷貝到NPU側(cè) # 調(diào)用acl.mdl.execute異步或同步執(zhí)行 # 把輸出內(nèi)存拷貝回CPU return output_np代碼不完整但流程是明確。實(shí)際項(xiàng)目中你還要處理輸入數(shù)據(jù)從HWC到NCHW的轉(zhuǎn)換、除以255、以及把結(jié)果從模型輸出中解析出來(lái)。YOLOv5的輸出通常是一個(gè)[1, 25200, 85]的張量包含位置、置信度和類別概率后處理要做置信度過(guò)濾和NMS。我強(qiáng)烈建議后處理放在CPU上做因?yàn)镹PU資源很寶貴NMS這種IO密集又依賴循環(huán)的邏輯在加速卡上不一定有性能優(yōu)勢(shì)反而占著算子執(zhí)行時(shí)間。一個(gè)非常容易踩的坑是YOLOv5模型的輸出頭。有些倉(cāng)庫(kù)導(dǎo)出時(shí)會(huì)帶一個(gè)額外的decode模塊有些不帶。如果模型輸出直接被transpose成[1, 25200, 85]說(shuō)明decode模塊融合在模型里如果是三個(gè)特征圖輸出[1, 80, 80, 85]、[1, 40, 40, 85]、[1, 20, 20, 85]那解碼和NMS必須后置。后者更常見(jiàn)也更容易在ONNX轉(zhuǎn)換時(shí)出現(xiàn)問(wèn)題所以建議提前看一眼。4. 性能調(diào)優(yōu)讓YOLO在Atlas上真正跑滿4.1 數(shù)據(jù)流優(yōu)化DVPP硬解碼和AIPP剛部署完的時(shí)候模型推理可能只需要十幾毫秒但整個(gè)端到端流程跑一次鏡頭分析要七十多毫秒瓶頸全在圖像加載和預(yù)處理上。這里有兩個(gè)方向值得做。第一個(gè)是視頻流場(chǎng)景用DVPP做硬解碼。Atlas 300V板載了硬件編解碼模塊可以把H.264/H.265視頻流直接解碼成YUV數(shù)據(jù)然后硬件縮放、格式轉(zhuǎn)換完全不占NPU算力。流程上就是ffmpeg拉流后把編碼幀交給DVPP的VPC模塊而不是在CPU上軟縮放。第二個(gè)是AIPP配置我上面提到過(guò)。AIPP在模型轉(zhuǎn)換階段就決定了圖像進(jìn)NPU之前要做什么處理。傳統(tǒng)做法是在后端代碼里用OpenCV和NumPy做一幀要花好幾毫秒把這些寫(xiě)進(jìn)aipp.cfg后NPU在算子計(jì)算前自動(dòng)完成實(shí)測(cè)預(yù)處理時(shí)間幾乎可以忽略。很多人有個(gè)誤區(qū)覺(jué)得AIPP配好就一勞永逸其實(shí)如果你要跑動(dòng)態(tài)分辨率AIPP的配置就要多寫(xiě)幾組。好在我們的場(chǎng)景固定是640x640輸入AIPP是性價(jià)比最高的優(yōu)化沒(méi)有之一。4.2 INT8量化算力翻倍的關(guān)鍵第二個(gè)大頭是精度和性能的取舍。Atlas 300V這卡對(duì)INT8的支持非常友好INT8算力指標(biāo)遠(yuǎn)高于FP16和FP32。如果你的業(yè)務(wù)對(duì)精度要求不是極其苛刻量化幾乎是必做項(xiàng)。CANN自帶AMCT量化工具但更省事的方式是直接用YOLO體系里的INT8導(dǎo)出能力或者在做ONNX轉(zhuǎn)OM時(shí)選擇量化感知的流程。我自己的建議是如果對(duì)CANN工具鏈不熟先做離線量化用幾百?gòu)堄写硇缘膱D片做校準(zhǔn)比強(qiáng)行在推理時(shí)搞動(dòng)態(tài)量化穩(wěn)得多。我自己在YOLOv5s上的實(shí)測(cè)數(shù)據(jù)是FP32下640x640輸入的NPU推理耗時(shí)約12毫秒轉(zhuǎn)成INT8后約4毫秒mAP掉點(diǎn)大概在0.5到1個(gè)點(diǎn)以內(nèi)做目標(biāo)定位完全夠用。如果客戶要求高精度建議保留FP32模型做回退用同一套接口按模型名切換。這里還要提醒一句INT8量化后如果出現(xiàn)同一張圖片多次推理結(jié)果不一致或者某些小目標(biāo)漏檢明顯大概率是量化校驗(yàn)集選得太少幾百?gòu)埡蛶资畯埖牟顒e非常大不要嫌麻煩。4.3 實(shí)測(cè)性能數(shù)據(jù)與調(diào)優(yōu)順序建議這里是我們固定硬件、固定YOLOv5s模型下的平均數(shù)據(jù)做一個(gè)參考配置NPU推理耗時(shí)單幀端到端備注初始版CPU預(yù)處理12ms71ms預(yù)處理和后處理占絕對(duì)大頭加AIPPCPU算NMS12ms23msAIPP省掉了歸一化和尺寸變換加DVPP硬解碼直通12ms15ms視頻流場(chǎng)景下效果顯著INT8量化后4ms8ms主推方案這個(gè)表的重點(diǎn)不是數(shù)字本身而是趨勢(shì)Atlas 300V的瓶頸往往不在NPU算力而在你喂數(shù)據(jù)的效率。很多人誤以為推理卡貴其實(shí)把數(shù)據(jù)通路優(yōu)化好了算力根本用不滿。調(diào)優(yōu)順序我個(gè)人建議是先AIPP再DVPP最后量化。這順序成本遞增收益也遞增但每一步都能看到明顯的性能提升容易建立信心。如果你做的是多路視頻分析還有一個(gè)更上層的優(yōu)化是多batch。單幀推理12毫秒但8幀一起推理可能只要30毫秒平均到每幀不到4毫秒。代價(jià)是延遲變大因?yàn)橐獢€夠一批才送進(jìn)去。所以實(shí)時(shí)單路場(chǎng)景用batch 1多路視頻場(chǎng)景用batch 8或16需要你自己權(quán)衡。4.4 多路并發(fā)的坑別用線程堆算力多路視頻流并發(fā)推給NPU很容易出現(xiàn)一個(gè)誤區(qū)每路視頻一個(gè)線程每個(gè)線程都做完整推理結(jié)果大量時(shí)間花在線程切換和內(nèi)存拷貝上。正確做法是先把多路視頻幀各自的預(yù)處理做完攢成一整批一次性送給NPU或者在AscendCL里用多流機(jī)制把不同路的推理錯(cuò)峰調(diào)度。我們最終采用了后者單卡穩(wěn)定扛住了16路1080p視頻流的實(shí)時(shí)檢測(cè)。這里必須強(qiáng)調(diào)多流機(jī)制不是讓NPU同時(shí)跑多個(gè)模型而是把不同的推理任務(wù)掛到不同的執(zhí)行流上硬件調(diào)度器負(fù)責(zé)利用空閑算力。如果只有一個(gè)模型多流主要降低的是排隊(duì)延遲而不是提升總吞吐。真正提吞吐還是得靠batch。這兩個(gè)概念很多人混在一起容易導(dǎo)致調(diào)試半天沒(méi)效果。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 模型轉(zhuǎn)換失敗報(bào)各種算子不支持這是最高頻的問(wèn)題。處理順序是先看日志里報(bào)的算子名去查CANN版本的支持矩陣大多數(shù)情況用onnx-simplifier處理就沒(méi)了。如果還有問(wèn)題試著把opset版本從13降到11YOLO系列在opset 11下兼容性最好。還不行的就看是不是用了新結(jié)構(gòu)考慮把后處理拆出去只保留主干做轉(zhuǎn)換。像YOLOv8里有些新模塊在舊版本CANN上確實(shí)不支持升級(jí)CANN或者改模型結(jié)構(gòu)二選一。5.2 推理結(jié)果全是空框或者坐標(biāo)偏移這個(gè)問(wèn)題幾乎都指向預(yù)處理沒(méi)有和訓(xùn)練時(shí)保持一致。YOLO訓(xùn)練時(shí)如果是RGB輸入、BGR的順序AIPP配置里input_format和訓(xùn)練時(shí)的通道順序就必須對(duì)齊。另一個(gè)容易踩的坑是mean和std設(shè)置如果訓(xùn)練時(shí)減的mean是0你在AIPP里卻寫(xiě)了其他均值那結(jié)果一定是亂的。建議在轉(zhuǎn)換階段用一個(gè)已知結(jié)果的小視頻驗(yàn)證別等整條鏈路打通了再回頭翻。我們當(dāng)時(shí)就是AIPP里寫(xiě)反了RGB和BGR定位框全偏移了半個(gè)圖排查了半天。5.3 長(zhǎng)時(shí)間運(yùn)行后顯存越用越多Atlas和所有推理卡一樣如果每次推理都申請(qǐng)新內(nèi)存跑一晚上顯存必然爆。AscendCL提供了內(nèi)存池和復(fù)用接口官方推薦的姿勢(shì)是初始化時(shí)把模型的輸入輸出內(nèi)存都分配好推理時(shí)只做數(shù)據(jù)拷貝不要反復(fù)malloc和free。另外檢查你的Python流程里是否每次infer都創(chuàng)建了新的dataset描述對(duì)象如果有改成復(fù)用同一個(gè)desc。這里有一個(gè)自查清單照著檢查基本能解決90%的顯存問(wèn)題模型只load一次不隨請(qǐng)求反復(fù)load輸入輸出內(nèi)存在啟動(dòng)時(shí)統(tǒng)一申請(qǐng)推理循環(huán)里傳引用后處理結(jié)果及時(shí)釋放不用的numpy中間數(shù)組做到這三點(diǎn)長(zhǎng)時(shí)間跑幾十小時(shí)顯存曲線基本是一條直線。5.4 推理延遲不穩(wěn)定偶爾出現(xiàn)大的毛刺如果監(jiān)控發(fā)現(xiàn)p99延遲很高但平均延遲正常多半是系統(tǒng)層面的干擾。常見(jiàn)原因有CPU頻率被其他進(jìn)程搶占、PCIe帶寬被多卡爭(zhēng)用、或者后處理代碼里有偶發(fā)的GC。我們遇到過(guò)最隱蔽的一個(gè)是NMS里用了Python的循環(huán)加列表當(dāng)畫(huà)面中目標(biāo)數(shù)量突然變多時(shí)后處理時(shí)間線性暴漲導(dǎo)致整個(gè)鏈路延遲出現(xiàn)尖刺。解決辦法是把NMS改成向量化實(shí)現(xiàn)或者用之前AIPP后處理拆出去的方式嚴(yán)格控制CPU側(cè)的耗時(shí)?,F(xiàn)象可能原因處理方式ATC轉(zhuǎn)換報(bào)未知算子ONNX較新或未簡(jiǎn)化onnxsim 降低opset推理結(jié)果全是空框AIPP通道順序或mean錯(cuò)誤對(duì)照訓(xùn)練預(yù)處理核對(duì)顯存持續(xù)上漲每次推理重復(fù)申請(qǐng)內(nèi)存復(fù)用輸入輸出buffer多路并發(fā)丟幀每路一個(gè)線程方式不對(duì)多流或batch機(jī)制改造延遲尖刺后處理循環(huán)過(guò)多NMS向量化或移出熱點(diǎn)5.5 一個(gè)補(bǔ)充日志和排錯(cuò)心態(tài)CANN工具鏈報(bào)錯(cuò)信息比CUDA要“硬核”一些經(jīng)常是一大段十六進(jìn)制錯(cuò)誤碼加一個(gè)很模糊的提示。遇到這種情況我的建議是別硬猜直接把日志路徑指出來(lái)用grep過(guò)濾關(guān)鍵行。官方日志默認(rèn)在~/ascend/log里面會(huì)有更詳細(xì)的算子執(zhí)行信息。還有一個(gè)很容易被忽視的點(diǎn)多卡環(huán)境下邏輯設(shè)備ID和物理設(shè)備ID容易混淆。在你設(shè)置acl.rt.set_device的參數(shù)時(shí)一定要確認(rèn)是邏輯ID還是物理ID傳錯(cuò)之后會(huì)出現(xiàn)“有時(shí)能跑有時(shí)報(bào)錯(cuò)”的詭異現(xiàn)象。結(jié)語(yǔ)這條路線值不值得走最后分享一個(gè)我自己踩過(guò)幾次坑才悟出的體會(huì)Atlas 300V 24G不是那種插上就能用的卡它的脾氣在工具鏈而不在硬件。只要熬過(guò)了環(huán)境配置和模型轉(zhuǎn)換這兩個(gè)坎后面推理、優(yōu)化、上線反而比在通用卡上要省心因?yàn)樗總€(gè)環(huán)節(jié)都是確定性的沒(méi)有那么多黑盒。尤其是國(guó)內(nèi)項(xiàng)目的國(guó)產(chǎn)化部署選它做YOLO推理的承載平臺(tái)目前看是一個(gè)靠譜且成本可控的路線。如果你正準(zhǔn)備換平臺(tái)建議先拿一張卡、一臺(tái)普通服務(wù)器把今天寫(xiě)的這套流程完整跑一遍再?zèng)Q定要不要大規(guī)模鋪開(kāi)。按照我的經(jīng)驗(yàn)第一天你會(huì)被ATC的各種報(bào)錯(cuò)整到懷疑人生第二天把AIPP和量化摸透了第三天你已經(jīng)能信心滿滿地跟同事說(shuō)穩(wěn)定性沒(méi)問(wèn)題。這卡的真實(shí)水平比你第一眼看到文檔時(shí)想象的要好只是你得先耐住性子。