戰(zhàn)指南)
最近不少朋友私信問我Atlas 300V 24G是不是運(yùn)算加速卡能不能拿來跑YOLO這問題其實(shí)問得很實(shí)在因?yàn)楹芏嘧霭卜?、智慧交通、工業(yè)質(zhì)檢的團(tuán)隊(duì)手里項(xiàng)目用到目標(biāo)檢測又聽說華為Atlas平臺功耗低、成本劃算但真到部署階段就迷茫了。先說結(jié)論Atlas 300V 24G是昇騰系列里專門做AI推理的加速卡不是訓(xùn)練卡它確實(shí)非常適合用來批量部署YOLO這類目標(biāo)檢測模型尤其在視頻流分析、邊緣服務(wù)器場景里性價(jià)比很高。這篇文章我就把自己在Atlas 300V 24G上部署YOLOv5的完整過程和踩坑記錄整理出來從硬件選型、軟件棧梳理、模型轉(zhuǎn)換到最終跑通推理一次說明白給正準(zhǔn)備入手的團(tuán)隊(duì)和個(gè)人一個(gè)可以直接參考的實(shí)操樣本。1. Atlas 300V 24G 到底是什么卡1.1 先把運(yùn)算加速卡這個(gè)概念理清楚很多第一次接觸Atlas的朋友有個(gè)誤解以為運(yùn)算加速卡就是顯卡。其實(shí)加速卡分兩大類一類是訓(xùn)練卡像NVIDIA的A100、H100、RTX系列主要跑訓(xùn)練腳本需要高精度浮點(diǎn)運(yùn)算和大量顯存另一類是推理卡專門干已經(jīng)訓(xùn)練好的模型進(jìn)行前向推斷的活對單張圖的處理速度、批量吞吐、功耗控制要求更高。Atlas 300V 24G就是后者它是一塊AI推理加速卡搭載昇騰310P芯片板載24GB顯存支持FP16、INT8等低精度推理還帶視頻編解碼模塊所以特別適合做視頻流分析。從架構(gòu)上看昇騰NPU不是GPU那種通用CUDA核心設(shè)計(jì)它內(nèi)部有專門的AI Core對卷積、矩陣乘這類算子做了硬件級優(yōu)化。這意味著同一個(gè)YOLO模型在GPU上可能依賴驅(qū)動和CUDA庫在Atlas上則要換成華為的CANN軟件棧。但換來的是同樣的算力需求下功耗和采購成本都更友好而且不需要走主板上額外的電源線服務(wù)器選型壓力小很多。1.2 Atlas 300V 24G和普通顯卡到底差在哪我在實(shí)際部署中做過對比用一張RTX 3060 12G和Atlas 300V 24G跑同一個(gè)YOLOv5s模型batch size都是1。GPU的延遲大概是5毫秒左右Atlas大概在8到10毫秒單卡吞吐視覺上稍有差距但Atlas有24G顯存可以開很大的batch而且整卡功耗只有幾十瓦機(jī)房不用擴(kuò)容散熱和電力。要是把模型量化成INT8Atlas推理速度還能再提一截這是GPU默認(rèn)精度模式下很難直接比的優(yōu)勢。這里有一個(gè)關(guān)鍵認(rèn)知訓(xùn)練時(shí)你可以用GPU當(dāng)主力但生產(chǎn)環(huán)境如果長期跑固定模型、追求穩(wěn)定低功耗用推理卡是合理方案。Atlas 300V的定位就是給服務(wù)器插上這種推理加速能力相當(dāng)于給一臺普通服務(wù)器補(bǔ)上密集計(jì)算模塊。至于24G是運(yùn)算加速卡嗎這種疑問本質(zhì)上是不清楚推理卡和訓(xùn)練卡的分工看完這張差異表應(yīng)該就明白了項(xiàng)目GPU如RTX 3060/4090Atlas 300V 24GNPU設(shè)計(jì)目標(biāo)訓(xùn)練 通用計(jì)算推理 視頻分析軟件生態(tài)CUDA / cuDNN / TensorRTCANN / ACL / MindX常用精度FP32 / TF32 / FP16FP16 / INT8顯存12G~24G不等24G空閑功耗高部分卡待機(jī)就幾十瓦明顯更低模型格式ONNX / TensorRT engineONNX / OM訓(xùn)練支持非常成熟基本不建議1.3 為什么YOLO這類模型天生適合跑NPUYOLOv5、YOLOv8這些模型結(jié)構(gòu)很規(guī)整主干網(wǎng)絡(luò)幾乎全是卷積、BN、激活函數(shù)、殘差連接檢測頭也就是幾組卷積輸出和reshape操作沒有太多動態(tài)分支和復(fù)雜控制流。這種結(jié)構(gòu)特別適合NPU編譯成固定靜態(tài)圖然后做算子融合和內(nèi)存復(fù)用所以部署起來比其他帶有大量自定義OP的模型要順。另外YOLO的后處理雖然包含NMS這種循環(huán)邏輯但在Atlas上只要把模型輸出拿回主機(jī)端做NMS或者把NMS算子放進(jìn)OM圖里都可行實(shí)踐中我更推薦把前處理、模型推理、后處理拆成三段來設(shè)計(jì)這樣問題好排查。后面我會具體講這套三段式設(shè)計(jì)怎么落地。2. 部署前先想清楚整體架構(gòu)2.1 Atlas平臺的軟件棧到底是怎么分層的一根筋A(yù)tlas平臺不像GPU那樣把CUDA裝上就能直接用它有自己的軟件棧從上到下大概是這么幾層Driver / Firmware把昇騰NPU設(shè)備掛載到系統(tǒng)里對應(yīng)npu-smi能看到設(shè)備列表。CANNCompute Architecture for Neural Networks昇騰計(jì)算架構(gòu)包含算子實(shí)現(xiàn)、圖編譯工具鏈、運(yùn)行時(shí)庫。ACLAscend Computing Language提供給應(yīng)用層的API類似CUDA Runtime可以加載模型、傳輸數(shù)據(jù)、啟動推理。上層應(yīng)用用C/C、Python調(diào)用ACL接口完成業(yè)務(wù)邏輯。生產(chǎn)部署時(shí)建議按順序安裝先裝驅(qū)動和固件再裝CANN Toolkit最后安裝CANN補(bǔ)充包或者M(jìn)indX SDK。如果只做單卡推理裝一個(gè)Toolkit就夠。裝完后要手動source環(huán)境變量不然后面python里import acl會失敗。2.2 PyTorch模型為什么不能直接塞進(jìn)NPU剛開始用Atlas的同事最容易問我的YOLOv5是PyTorch訓(xùn)練出來的.pt文件能不能直接丟上去答案是不能。PyTorch模型依賴Python執(zhí)行圖和自動梯度NPU需要的是固定靜態(tài)圖所以中間必須經(jīng)過一個(gè)轉(zhuǎn)換先把PyTorch模型導(dǎo)出成ONNX再用CANN的ATC工具把ONNX轉(zhuǎn)成昇騰專用的OM模型。ONNX就好比模型交換語言各家框架都能導(dǎo)ATC再針對昇騰NPU做算子映射和圖優(yōu)化。這里有個(gè)經(jīng)驗(yàn)轉(zhuǎn)換時(shí)盡量用保守的opset版本YOLOv5官方導(dǎo)出命令默認(rèn)opset大概是17但Atlas對某些高版本算子支持可能滯后我推薦指定opset11兼容性最好。轉(zhuǎn)換命令我們后面寫。2.3 推理流程的三段式設(shè)計(jì)模型跑起來之后真正到單幀圖像的處理流程可以分解成三個(gè)部分預(yù)處理讀圖、按YOLO訓(xùn)練時(shí)的letterbox方式縮放填充把BGR通道換成RGB并歸一化到0到1再轉(zhuǎn)成NCHW張量。模型推理把處理好的數(shù)據(jù)搬到設(shè)備顯存調(diào)用ACL執(zhí)行OM模型拿到輸出張量。后處理對輸出做解碼提取每個(gè)框的坐標(biāo)、類別、置信度再做NMS去除重疊框。很多部署翻車都栽在預(yù)處理和后處理和訓(xùn)練時(shí)不一致上。比如訓(xùn)練時(shí)用的是0到1歸一化推理時(shí)忘了歸一化或者letterbox的分辨率填了960但導(dǎo)出模型時(shí)是640模型直接就出了問題。所以我在項(xiàng)目里會把預(yù)處理函數(shù)單獨(dú)封裝統(tǒng)一輸入輸出規(guī)范保證在GPU上驗(yàn)證過的邏輯在Atlas上原樣復(fù)現(xiàn)只把推理部分替換成ACL調(diào)用。3. 實(shí)戰(zhàn)部署從零跑通YOLOv53.1 環(huán)境準(zhǔn)備與驅(qū)動固件安裝我用的服務(wù)器是x86架構(gòu)系統(tǒng)Ubuntu 20.04Atlas 300V 24G插在PCIe插槽系統(tǒng)能正常識別到PCIe設(shè)備后開始安裝驅(qū)動。這里有個(gè)很關(guān)鍵的細(xì)節(jié)安裝順序必須是先裝驅(qū)動再裝固件且兩者版本要和CANN匹配不能隨意升級。安裝驅(qū)動一般用華為提供的.run文件# 賦予執(zhí)行權(quán)限 chmod x Ascend-hdk-*-npu-driver_*.run # 安裝驅(qū)動--install 是靜默安裝 ./Ascend-hdk-*-npu-driver_*.run --install # 安裝固件 chmod x Ascend-hdk-*-npu-firmware_*.run ./Ascend-hdk-*-npu-firmware_*.run --install裝完重啟后執(zhí)行npu-smi info如果能看到設(shè)備編號、芯片型號和顯存說明驅(qū)動正常。這里有個(gè)坑如果只裝驅(qū)動不裝固件有時(shí)候npu-smi也能看到設(shè)備但后面調(diào)用ACL時(shí)會報(bào)固件版本不匹配我之前就卡在這個(gè)問題上排查了很久。接著安裝CANN Toolkit# 解壓后進(jìn)入目錄 ./Ascend-cann-toolkit_*.run --install # 安裝完成后務(wù)必設(shè)置環(huán)境變量 source /usr/local/Ascend/ascend-toolkit/set_env.sh為了以后方便可以把source命令寫進(jìn)~/.bashrc。同時(shí)確認(rèn)python環(huán)境里有acl模塊能import成功python3 -c import acl不報(bào)錯(cuò)環(huán)境才算真正就緒。3.2 導(dǎo)出ONNX模型我以YOLOv5s為例先在已經(jīng)訓(xùn)練好的PyTorch環(huán)境里導(dǎo)出ONNX。直接使用YOLOv5倉庫自帶的export腳本python export.py --weights yolov5s.pt --include onnx --opset 11 --simplify導(dǎo)出完成后會生成yolov5s.onnx。一定要用--simplify剪掉一些冗余的算子結(jié)構(gòu)比如Identity、空Gather這種多余節(jié)點(diǎn)在ATC轉(zhuǎn)換時(shí)容易出幺蛾子。為了驗(yàn)證導(dǎo)出沒問題可以在本地用onnxruntime跑一張圖確認(rèn)輸出結(jié)果的shape是[1, 25200, 85]。25200是YOLOv5s在640x640輸入下的anchor數(shù)量85是xywh、objectness和80個(gè)類別概率加起來。這里再提醒一句導(dǎo)出時(shí)batch size固定為1。雖然ATC也支持帶batch維度轉(zhuǎn)換但固定batch更容易優(yōu)化也更方便后面做性能測試。如果有多batch需求可以單獨(dú)導(dǎo)出一份batch4的模型。3.3 ATC模型轉(zhuǎn)換的關(guān)鍵參數(shù)拿到ONNX后下一步就是用ATC工具轉(zhuǎn)成OM模型。命令如下atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --precision_modeallow_mix_precision \ --loginfo逐個(gè)講下參數(shù)--framework5固定寫法表示輸入是ONNX模型。--soc_versionAscend310P3這里很關(guān)鍵必須和你板卡芯片型號對上。Atlas 300V 24G采用的昇騰310P通常填A(yù)scend310P3但如果你的卡是Pro版或者具體型號不一樣要用npu-smi info確認(rèn)芯片全名防止填錯(cuò)。--input_shape指定輸入張量名字和維度這里images是YOLOv5導(dǎo)出時(shí)的輸入節(jié)點(diǎn)名。名字不對轉(zhuǎn)換直接報(bào)錯(cuò)。如果忘記了可以用onnx.graph.input打印或者直接用netron打開onnx文件看一眼。--precision_modeallow_mix_precision允許混合精度默認(rèn)可能把部分算子轉(zhuǎn)成FP16推理更快精度損失一般可控。--loginfo轉(zhuǎn)換過程中打印詳細(xì)日志方便定位問題。轉(zhuǎn)換成功后會生成yolov5s_bs1.om大小通常比ONNX小一些。轉(zhuǎn)換時(shí)如果報(bào)錯(cuò)說某些算子不支持下面的常見問題章節(jié)會專門講怎么處理。3.4 用pyACL寫一個(gè)最簡單的推理腳本Atlas推理應(yīng)用最常用的是pyACL這是華為提供的Python版ACL接口可以直接在Python里控制設(shè)備、模型加載和推理。我的推理腳本核心邏輯是這樣的import acl import numpy as np import cv2 # 初始化 acl.init() ret acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加載模型 model_path byolov5s_bs1.om model_id, ret acl.mdl.load_from_file_with_mem(model_path) # 獲取模型輸入輸出信息 input_size acl.mdl.get_input_size_by_index(model_id, 0) output_size acl.mdl.get_output_size_by_index(model_id, 0) # 準(zhǔn)備輸入輸出內(nèi)存 input_data acl.util.np_to_ptr(np.zeros((1,3,640,640), dtypenp.float32)) output_data acl.util.np_to_ptr(np.zeros((1,25200,85), dtypenp.float32)) # 推理 ret acl.mdl.execute(model_id, input_data, input_size, output_data, output_size) # 取回?cái)?shù)據(jù) output_np acl.util.ptr_to_np(output_data, (1,25200,85), dtypenp.float32) print(output_np.shape) # 釋放資源 acl.mdl.unload(model_id) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()但實(shí)際項(xiàng)目里不能這么粗糙輸入數(shù)據(jù)必須經(jīng)過正確預(yù)處理輸出也必須做后處理才能得到框。我在項(xiàng)目里另外封裝了一個(gè)preprocess函數(shù)讀入圖像路徑先做letterbox再按640x640 resize最后歸一化轉(zhuǎn)float32代碼不長但容易出錯(cuò)def preprocess(img_path): img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w img.shape[:2] scale min(640 / w, 640 / h) nw, nh int(w * scale), int(h * scale) img_resized cv2.resize(img, (nw, nh), interpolationcv2.INTER_LINEAR) canvas np.zeros((640, 640, 3), dtypenp.float32) canvas[:nh, :nw] img_resized / 255.0 tensor canvas.transpose(2, 0, 1)[None, ...].astype(np.float32) return tensor, scale, nh, nw后處理則負(fù)責(zé)把25200個(gè)候選框解析出來。因?yàn)镺M模型輸出默認(rèn)是FP32的NCHW需要按YOLOv5的格式reshape成[1, 25200, 85]然后按confidence過濾再做一次NMS??梢钥紤]用opencv的dnn.NMSBoxes也可以用簡單實(shí)現(xiàn)只要保證框坐標(biāo)從640的letterbox坐標(biāo)映射回原圖坐標(biāo)時(shí)把縮放和填充補(bǔ)回來這一步細(xì)節(jié)很多。3.5 性能數(shù)據(jù)怎么看模型跑通后我習(xí)慣先統(tǒng)計(jì)單幀耗時(shí)和吞吐量。用time.time()包住推理調(diào)用部分連續(xù)跑200幀取平均值對比batch1和batch4的差別。實(shí)測下來Atlas 300V 24G在batch1時(shí)YOLOv5s單幀推理約8毫秒加上預(yù)處理后處理大約12毫秒batch4時(shí)吞吐明顯提升單幀均攤時(shí)間能壓到6毫秒左右這說明推理卡吃batch千萬不要浪費(fèi)24G顯存只跑單張圖。如果要做得更規(guī)范建議開啟異步推理接口比如acl.mdl.execute_async把數(shù)據(jù)搬運(yùn)和計(jì)算重疊起來但那樣對工程化要求更高后續(xù)可以單獨(dú)開源一個(gè)完整異步版本。4. 部署踩坑記錄與排查技巧4.1 npu-smi找不到設(shè)備或驅(qū)動狀態(tài)異常最常見的是驅(qū)動裝完重啟后npu-smi info直接提示No device。我排查過好幾個(gè)環(huán)境原因基本都是驅(qū)動和固件安裝順序不對或者固件沒裝。另一個(gè)容易被忽略的坑是BIOS里沒開啟PCIe的Above 4G Decoding這會導(dǎo)致昇騰卡的BAR空間無法映射設(shè)備就消失了。如果遇到這個(gè)問題進(jìn)BIOS搜Above 4G打開再重啟。如果npu-smi info能看到設(shè)備但狀態(tài)是Health Status異??梢杂胐mesg看內(nèi)核日志確認(rèn)是不是固件版本和CANN不匹配。匹配問題沒有捷徑查一下官方版本配套表最好按照安裝包里自帶的readme來配。4.2 ATC轉(zhuǎn)換報(bào)錯(cuò)算子不支持YOLOv5算是轉(zhuǎn)換很順利的模型但如果你換了YOLOv8或者帶自定義模塊的變體ATC很可能會報(bào)類似Unsupported op: XXX的錯(cuò)誤。我的一般處理步驟是先看ONNX里是哪個(gè)節(jié)點(diǎn)不識別用netron打開搜索節(jié)點(diǎn)名。如果只是某個(gè)激活函數(shù)或reshape變體嘗試在ONNX里做圖優(yōu)化比如打開--simplify把無用節(jié)點(diǎn)刪掉。如果確實(shí)有昇騰不支持的算子考慮改模型結(jié)構(gòu)比如把一些pixel shuffle操作拆成reshapeconv或者修改導(dǎo)出代碼里對應(yīng)的部分。最后實(shí)在繞不過去可以降低opset版本重新導(dǎo)出有很大概率能兼容。要注意ATC個(gè)別版本對Split、Slice這類算子在不同維度的支持有差異所以導(dǎo)出時(shí)最好設(shè)置輸入name統(tǒng)一減少動態(tài)shape。4.3 pyACL初始化失敗和內(nèi)存報(bào)錯(cuò)acl.init返回非0最常見原因是環(huán)境變量沒source或者CANN的so庫依賴沒找到??梢栽谀_本開頭加import sys sys.path.insert(0, /usr/local/Ascend/ascend-toolkit/latest/pyACL/python/site-packages)實(shí)在不行就換成C的ACL接口但沒必要多數(shù)情況是環(huán)境問題。內(nèi)存報(bào)錯(cuò)一般來自acl.util.np_to_ptr傳入的數(shù)據(jù)類型錯(cuò)誤。ACL對內(nèi)存對齊要求很嚴(yán)格輸入數(shù)據(jù)必須是連續(xù)內(nèi)存最好用np.ascontiguousarray包一層。如果直接傳普通numpy array推理時(shí)會有偶發(fā)崩潰。4.4 推理精度不對全是錯(cuò)框這類問題十有八九是預(yù)處理和后處理出問題。我遇到過全黑圖、坐標(biāo)偏移、置信度全是負(fù)值的情況后來發(fā)現(xiàn)是letterbox填充沒把填充值加到0到255的對應(yīng)歸一化區(qū)間。YOLO訓(xùn)練時(shí)letterbox填充是gray114如果做歸一化填充值應(yīng)該是114 / 255 0.447很多人直接填了0導(dǎo)致邊框周圍噪音嚴(yán)重。另外OM模型的輸出順序可能和PyTorch導(dǎo)出的原始輸出一樣但有些atc配置會改變輸出name的排序后處理之前先打印output_np.shape和幾個(gè)抽樣坐標(biāo)跟本地PyTorch推理結(jié)果對齊一下能省很多調(diào)試時(shí)間。4.5 性能上不去的排查清單如果你發(fā)現(xiàn)模型跑起來只有幾個(gè)FPS先不要怪硬件按這個(gè)順序查是不是每次推理都在主機(jī)和設(shè)備之間拷貝大塊數(shù)據(jù)可以把輸入和輸出內(nèi)存提前申請好只更新數(shù)據(jù)不要每次創(chuàng)建和釋放。是不是batch size只有1盡量用batch4或8試一下。是不是沒有開異步推理多路視頻流場景用同步接口會卡住整體流程。是不是沒有做INT8量化FP16能跑但I(xiàn)NT8能進(jìn)一步壓時(shí)長代價(jià)是精度可能掉1到2個(gè)點(diǎn)。4.6 獨(dú)家避坑動態(tài)shape、連續(xù)推理與資源釋放我最后再分享幾個(gè)只有實(shí)際部署踩過才注意到的細(xì)節(jié)第一ATC轉(zhuǎn)換時(shí)能固定shape就固定shape。動態(tài)shape雖然靈活但NPU側(cè)會做動態(tài)內(nèi)存規(guī)劃推理性能掉一截不說內(nèi)存也不是按最大shape預(yù)留的一旦輸入分辨率超過預(yù)期就會報(bào)錯(cuò)。第二連續(xù)推理時(shí)千萬記得釋放模型和上下文。我有一次寫了個(gè)循環(huán)做視頻流推理跑了一個(gè)晚上后內(nèi)存大漲后來發(fā)現(xiàn)是把a(bǔ)cl.mdl.execute的返回值忽略后每幀申請的設(shè)備內(nèi)存沒釋放。最好每次推理都復(fù)用同一塊輸入輸出內(nèi)存避免顯存泄漏。第三多卡場景要注意設(shè)備隔離。如果服務(wù)器插了多張Atlas 300V每個(gè)進(jìn)程通過acl.rt.set_device(device_id)綁定一張卡綁定后不要跨設(shè)備訪問否則容易出現(xiàn)無效設(shè)備句柄的報(bào)錯(cuò)。個(gè)人體會是Atlas 300V 24G沒有想象中那么玄乎本質(zhì)就是一塊推理加速卡把模型轉(zhuǎn)換和環(huán)境配好之后YOLO部署的難點(diǎn)已經(jīng)從算子層轉(zhuǎn)移到工程化層了反而是預(yù)處理、內(nèi)存復(fù)用、批處理這些基本功決定最終效果。如果你正卡在某個(gè)安裝或轉(zhuǎn)換環(huán)節(jié)照著上面步驟走一遍大概率能跑通。