戰(zhàn):從ONNX到OM跑通YOLOv5全流程)
這兩年只要聊到國(guó)產(chǎn)AI推理卡繞不開一個(gè)詞Atlas。不管是社區(qū)里還是技術(shù)群里隔三差五就能看到有人在問“atlas部署yolo怎么搞”“atlas 300v 24g 是運(yùn)算加速卡嗎”。說實(shí)話我第一次拿到Atlas 300V的時(shí)候也愣了半天——這卡長(zhǎng)得跟普通顯卡完全不一樣無風(fēng)扇、被動(dòng)散熱、單槽位插上服務(wù)器開機(jī)之后系統(tǒng)里看不到任何傳統(tǒng)GPU的設(shè)備節(jié)點(diǎn)連驅(qū)動(dòng)裝法都跟NVIDIA不是一套思路。但正是這種“不一樣”讓Atlas在實(shí)際落地項(xiàng)目里的價(jià)值被低估了。它是一塊實(shí)打?qū)嵉腁I推理加速卡專門為神經(jīng)網(wǎng)絡(luò)的前向推理設(shè)計(jì)能跑YOLO系列、能接CANN工具鏈、能在邊緣和數(shù)據(jù)中心做高并發(fā)推理。這篇內(nèi)容我不打算寫成官方文檔的復(fù)述就從一個(gè)實(shí)際做過遷移、踩過坑、把YOLOv5跑上Atlas 300V的人的角度把“這卡到底算什么”“怎么把YOLO跑起來”“一路會(huì)遇到哪些坑”講清楚。不管你是剛開始接觸昇騰生態(tài)的新手還是準(zhǔn)備把手頭GPU推理服務(wù)遷移到國(guó)產(chǎn)卡上的老手這篇都值得看完。1. 先搞清楚Atlas到底是什么它不是一個(gè)產(chǎn)品而是一整套推理方案很多人第一次接觸Atlas被一堆型號(hào)繞暈了Atlas 200、Atlas 300I、Atlas 300V、Atlas 800、Atlas 900……名字像長(zhǎng)得也像但定位完全不同。1.1 從層級(jí)上理解Atlas我習(xí)慣把Atlas拆成三層來看芯片層昇騰系列AI處理器比如310、310P、910等。這是算力的源頭。板卡/模組層把芯片封裝成推理卡、訓(xùn)練卡或者開發(fā)者套件比如Atlas 300I Pro、Atlas 300V、Atlas 200開發(fā)者套件。用戶直接接觸的就是這一層。服務(wù)器/集群層把多張卡裝進(jìn)一臺(tái)設(shè)備里比如Atlas 800推理服務(wù)器、Atlas 900訓(xùn)練集群。這一層解決的是機(jī)架級(jí)部署問題。熱詞里提到的Atlas 300V就屬于板卡層產(chǎn)品。它是一塊半高半長(zhǎng)的標(biāo)準(zhǔn)PCIe推理卡插到x86服務(wù)器或者華為的Taishan服務(wù)器上通過PCIe接口跟CPU通信跟GPU的物理形態(tài)很像但里子完全不同。1.2 300V在家族里的位置華為昇騰的推理卡產(chǎn)品線大體上分兩個(gè)方向一個(gè)偏數(shù)據(jù)中心高并發(fā)一個(gè)偏邊緣低功耗。Atlas 300I系列和300V系列都屬于數(shù)據(jù)中心和邊緣通用的PCIe推理卡采用昇騰310P芯片主打的是INT8算力。以Atlas 300V常見的24GB版本來說它的參數(shù)大致是這樣基于昇騰310P系列芯片INT8算力在140 TOPS這個(gè)量級(jí)FP16算力大概70 TFLOPS左右顯存24GB單卡功耗控制在幾十瓦級(jí)別。這組數(shù)據(jù)放在推理卡里是什么水平對(duì)比NVIDIA的T4——T4的INT8算力大約130 TOPS顯存16GB功耗70W。你會(huì)看到Atlas 300V的規(guī)格跟T4基本處于同一競(jìng)爭(zhēng)檔次甚至顯存還更大一些。所以回到那個(gè)熱詞問題“atlas 300v 24g 是運(yùn)算加速卡嗎”答案是肯定的。它是一塊用于AI推理場(chǎng)景的運(yùn)算加速卡能承擔(dān)目標(biāo)檢測(cè)、圖像分類、語義分割、OCR、推薦系統(tǒng)等推理任務(wù)。但它不是訓(xùn)練卡不適合用來從頭訓(xùn)練大模型也不是通用GPU不能當(dāng)顯卡輸出畫面更不能直接跑CUDA代碼。1.3 為什么這兩年Atlas突然變得熱鬧一個(gè)很現(xiàn)實(shí)的原因越來越多AI應(yīng)用要落地到國(guó)產(chǎn)硬件平臺(tái)而Atlas是當(dāng)前生態(tài)成熟度最高的國(guó)產(chǎn)AI推理方案之一。另一個(gè)原因是它在性價(jià)比上確實(shí)有亮點(diǎn)——24GB大顯存、百TOPS級(jí)別算力、低功耗做視頻結(jié)構(gòu)化分析、工業(yè)質(zhì)檢、智慧交通這類場(chǎng)景很合適。YOLO這種輕量級(jí)檢測(cè)網(wǎng)絡(luò)更是它的主場(chǎng)所以“atlas部署yolo”能成為熱詞一點(diǎn)都不奇怪。2. Atlas 300V的身份辨析它算加速卡但別用GPU的慣性思維去用如果你是從CUDA生態(tài)轉(zhuǎn)過來的最容易犯的錯(cuò)誤就是拿老思路去套Atlas。這一節(jié)我把關(guān)鍵差異講透能幫你少走一大半彎路。2.1 沒有CUDA但有CANNNVIDIA的做法是CUDA統(tǒng)一編程模型加上cuDNN、TensorRT這些庫。昇騰這邊對(duì)應(yīng)的是CANNCompute Architecture for Neural Networks——昇騰芯片的計(jì)算架構(gòu)平臺(tái)。CANN里包含了幾層?xùn)|西AscendCL統(tǒng)一的推理編程接口類似CUDA Runtime。模型加載、數(shù)據(jù)搬運(yùn)、推理執(zhí)行都通過它完成。ATC工具模型轉(zhuǎn)換器負(fù)責(zé)把ONNX、TensorFlow、Caffe等格式的模型轉(zhuǎn)換成昇騰芯片能跑的OM格式。MindSpore華為自家的深度學(xué)習(xí)框架對(duì)昇騰有原生支持。MindX SDK更高層的開發(fā)套件封裝了推理流水線、圖像預(yù)處理、后處理等模塊適合快速搭應(yīng)用。實(shí)際開發(fā)中最常用到的組合是“ONNX ATC AscendCL”。先把自己訓(xùn)練的模型導(dǎo)出成ONNX用ATC轉(zhuǎn)成OM再寫AscendCL代碼加載模型做推理。2.2 在編程思路上跟GPU的關(guān)鍵區(qū)別我在實(shí)際體驗(yàn)中總結(jié)出幾個(gè)最關(guān)鍵的區(qū)別模型格式不同。GPU生態(tài)里PyTorch/TensorRT直接加載模型權(quán)重文件或者engine文件就能跑。Atlas不行必須經(jīng)過ATC轉(zhuǎn)換成OM格式而且轉(zhuǎn)換時(shí)就要確定輸入shape至少靜態(tài)shape必須確定不像TensorRT那樣可以在運(yùn)行時(shí)靈活處理動(dòng)態(tài)shape。算子支持范圍不同。昇騰芯片對(duì)CNN類算子覆蓋很全但對(duì)一些冷門算子可能不支持。模型轉(zhuǎn)換時(shí)如果遇到不支持的算子就得改寫網(wǎng)絡(luò)結(jié)構(gòu)調(diào)整算子或者用CPU算子兜底性能會(huì)掉。顯存管理更依賴開發(fā)者手動(dòng)操心。用AscendCL時(shí)Host端和Device端的內(nèi)存搬運(yùn)、buffer生命周期管理都要手動(dòng)做比PyTorch的tensor自動(dòng)管理要原始一些寫起來更像CUDA的裸接口編程。2.3 一張表看懂300V和常見GPU加速卡的區(qū)別對(duì)比維度NVIDIA T4NVIDIA A10Atlas 300V 24G定位通用推理/輕訓(xùn)練通用推理/訓(xùn)練專用推理編程模型CUDACUDACANN/AscendCLINT8算力約130 TOPS約250 TOPS稀疏約140 TOPS顯存16GB GDDR624GB GDDR624GB典型功耗70W150W幾十瓦量級(jí)模型格式TensorRT engine等TensorRT engine等OM由ATC轉(zhuǎn)換生態(tài)成熟度極高極高持續(xù)完善中這張表不是要分高下而是想說Atlas 300V在推理場(chǎng)景下的紙面能力不弱但它的開發(fā)范式跟GPU有本質(zhì)差異。上手成本不在硬件安裝而在軟件棧切換。3. 實(shí)操在Atlas 300V上把YOLO模型完整跑起來下面進(jìn)入正題。我以YOLOv5作為例子因?yàn)樗木W(wǎng)絡(luò)結(jié)構(gòu)規(guī)整、導(dǎo)出ONNX最簡(jiǎn)單、后處理邏輯也清晰最適合用來上手昇騰推理流程。YOLOv8/v7流程大同小異區(qū)別主要在后處理和輸出節(jié)點(diǎn)名上。3.1 環(huán)境準(zhǔn)備驅(qū)動(dòng)、固件、CANN一個(gè)都不能少拿到Atlas 300V后第一步不是寫代碼而是裝環(huán)境。系統(tǒng)建議Ubuntu 20.04/22.04 x86_64或者openEuler內(nèi)核版本在CANN的兼容列表里就行。需要安裝的東西按順序來NPU驅(qū)動(dòng)Driver讓系統(tǒng)能識(shí)別到設(shè)備安裝后用npu-smi info能看到卡的信息。固件Firmware跟驅(qū)動(dòng)配套一般用官方配套的版本就行。CANN工具包包含ATC、AscendCL、算子庫等核心組件。安裝后需要source環(huán)境變量腳本。安裝完成后務(wù)必先跑一下npu-smi info確認(rèn)卡被正確識(shí)別。輸出里能看到芯片型號(hào)、算力狀態(tài)、溫度、功耗這些信息。如果這里就報(bào)錯(cuò)后面的流程都白搭。我在實(shí)際部署中遇到過一個(gè)典型問題服務(wù)器上同時(shí)插了NVIDIA GPU和Atlas卡系統(tǒng)里nvidia-smi正常但npu-smi一開始識(shí)別不到。排查下來發(fā)現(xiàn)是驅(qū)動(dòng)安裝時(shí)沒有正確加載內(nèi)核模塊npu-smi info報(bào)“no device”。重裝驅(qū)動(dòng)并加載模塊后解決。這個(gè)問題看似低級(jí)但很多首次部署的人都會(huì)卡在環(huán)境一步。環(huán)境變量也必須配好source /usr/local/Ascend/ascend-toolkit/set_env.sh3.2 從PyTorch導(dǎo)出ONNX模型訓(xùn)練好的權(quán)重是.pt文件我們需要先導(dǎo)出ONNX。YOLOv5自帶export腳本python export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 1這里有兩個(gè)注意事項(xiàng)--opset建議設(shè)為11到13之間太高的opset在ATC轉(zhuǎn)換時(shí)可能遇到算子不支持的問題。--batch-size先用1把靜態(tài)shape跑通后再考慮多batch優(yōu)化。導(dǎo)出后可以看一眼ONNX的輸入輸出節(jié)點(diǎn)名。YOLOv5的輸入節(jié)點(diǎn)一般叫images輸出是output融合后的1x25200x85 tensor。這個(gè)信息后面ATC配置和寫推理代碼時(shí)都要用。3.3 ATC模型轉(zhuǎn)換把ONNX變成OM這是整個(gè)流程的核心也是坑最多的一步。ATC命令的基本形態(tài)如下atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_formatNCHW \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --input_shapeimages:1,3,640,640參數(shù)含義逐一說清楚--framework55表示ONNX這是ATC里ONNX的固定編號(hào)。--soc_version目標(biāo)芯片型號(hào)Asend310P系列填A(yù)scend310P3。具體填什么要以npu-smi info里識(shí)別的型號(hào)為準(zhǔn)填錯(cuò)了會(huì)直接報(bào)錯(cuò)。--input_shape固定輸入shape格式是“節(jié)點(diǎn)名:維度”。這里用靜態(tài)shape最省事。--insert_op_conf指定AIPP配置文件用于把圖像預(yù)處理操作縮放、減均值、除方差、色域轉(zhuǎn)換融合到模型里減少Host端預(yù)處理開銷。AIPP配置文件aipp.cfg內(nèi)容如下aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_h: 640 src_image_size_w: 640 csc_switch: true rbuv_swap_switch: false 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 }這段配置的意思是輸入圖像是RGB888格式的U8數(shù)據(jù)尺寸是640x640做一次RGB到BGR的通道順序調(diào)整csc_switch: true在部分版本里同時(shí)處理色域轉(zhuǎn)換然后每個(gè)通道像素值除以255var_reci_chn是1/255的十進(jìn)制表示。這樣把歸一化操作也放進(jìn)模型里Host端只需要把原始圖像resize再轉(zhuǎn)成RGB數(shù)據(jù)拷貝過去就行。轉(zhuǎn)換成功后會(huì)生成yolov5s_bs1.om文件。用atc轉(zhuǎn)換時(shí)如果報(bào)算子不支持先檢查opset版本再檢查網(wǎng)絡(luò)里有沒有特殊算子比如一些新版本YOLO里的SiLU激活在舊版CANN里可能不識(shí)別需要升級(jí)CANN或者改寫網(wǎng)絡(luò)。3.4 寫AscendCL推理代碼OM模型有了接下來就是寫推理程序。我用C舉例因?yàn)樯a(chǎn)環(huán)境里C的推理性能最好而且AscendCL的C接口資料也最全。核心流程分五步初始化、加載模型、準(zhǔn)備輸入輸出內(nèi)存、執(zhí)行推理、解析結(jié)果。#include acl/acl.h #include fstream #include iostream #include cstring int main() { // 1. 初始化 aclInit(nullptr); int32_t deviceId 0; aclrtSetDevice(deviceId); aclrtContext context; aclrtCreateContext(context, deviceId); aclrtStream stream; aclrtCreateStream(stream); // 2. 加載模型 uint32_t modelId; const char* omPath ./yolov5s_bs1.om; aclmdlLoadFromFile(omPath, modelId); // 3. 準(zhǔn)備輸入輸出 size_t inputSize aclmdlGetInputSizeByIndex(modelId, 0); size_t outputSize aclmdlGetOutputSizeByIndex(modelId, 0); std::cout input size: inputSize , output size: outputSize std::endl; void* inputBuf nullptr; void* outputBuf nullptr; aclrtMalloc(inputBuf, inputSize, ACL_MEM_MALLOC_NORMAL_ONLY); aclrtMalloc(outputBuf, outputSize, ACL_MEM_MALLOC_NORMAL_ONLY); // 把預(yù)處理后的圖像數(shù)據(jù)填充到inputBuf這里假設(shè)imageData是 // 640x640x3的RGB U8數(shù)據(jù)大小等于inputSize // ... aclrtMemcpy(inputBuf, inputSize, imageData, inputSize, ACL_MEMCPY_HOST_TO_DEVICE); // 4. 構(gòu)建dataset并執(zhí)行推理 aclmdlDataset* inputDataSet aclmdlCreateDataset(); aclDataBuffer* inputDataBuffer aclmdlCreateDataBuffer(inputBuf, inputSize); aclmdlAddDatasetBuffer(inputDataSet, inputDataBuffer); aclmdlDataset* outputDataSet aclmdlCreateDataset(); aclDataBuffer* outputDataBuffer aclmdlCreateDataBuffer(outputBuf, outputSize); aclmdlAddDatasetBuffer(outputDataSet, outputDataBuffer); aclmdlExecute(modelId, inputDataSet, outputDataSet); // 5. 把輸出拷回Host端 // 輸出Shape一般是[1, 25200, 85]float類型 float* outputHost new float[outputSize / sizeof(float)]; aclrtMemcpy(outputHost, outputSize, outputBuf, outputSize, ACL_MEMCPY_DEVICE_TO_HOST); // 后處理解析25200個(gè)候選框過濾低置信度做NMS // ... // 清理資源 aclrtFree(inputBuf); aclrtFree(outputBuf); aclmdlUnload(modelId); aclrtDestroyStream(stream); aclrtDestroyContext(context); aclFinalize(); return 0; }這段代碼看著簡(jiǎn)單但每一步都有細(xì)節(jié)。比如aclrtMemcpy的第四個(gè)參數(shù)是拷貝方向ACL_MEMCPY_HOST_TO_DEVICE和ACL_MEMCPY_DEVICE_TO_HOST別寫反了寫反了會(huì)拷貝出亂碼數(shù)據(jù)后處理結(jié)果完全不對(duì)。3.5 編譯鏈接編譯時(shí)需要鏈接昇騰的庫g -o yolov5_infer main.cpp \ -I$HOME/Ascend/ascend-toolkit/latest/include \ -L$HOME/Ascend/ascend-toolkit/latest/lib64 \ -lascendcl \ -Wl,-rpath$HOME/Ascend/ascend-toolkit/latest/lib64運(yùn)行前確保環(huán)境變量已source然后./yolov5_infer就能看到推理結(jié)果。我實(shí)際跑通的第一個(gè)版本輸出解析后成功畫出檢測(cè)框的那一刻說實(shí)話挺有成就感的。但從寫代碼到這一步中間踩了不止一個(gè)坑。下一節(jié)我把最有代表性的幾個(gè)坑完整復(fù)盤一下。4. 復(fù)盤從“ONNX在GPU上正常”到“OM在Atlas上跑飛”我踩過的坑這一節(jié)說幾個(gè)真實(shí)的排查過程比直接給結(jié)論更有價(jià)值。每個(gè)坑背后都對(duì)應(yīng)一條排查鏈路。4.1 ATC轉(zhuǎn)換報(bào)錯(cuò)的排查鏈路現(xiàn)象運(yùn)行ATC命令沒幾分鐘就報(bào)錯(cuò)退出錯(cuò)誤日志指向某個(gè)算子不支持。完整排查過程先看報(bào)錯(cuò)信息里提到的算子名稱。我遇到的是新版YOLOv8里用到的某個(gè)自定義模塊在CANN算子清單里找不到對(duì)應(yīng)實(shí)現(xiàn)。定位到算子后用Python把該算子替換成等效的標(biāo)準(zhǔn)算子組合比如把自定義注意力模塊拆成Mul/Add/Softmax組合。重新導(dǎo)出ONNX再跑ATC這次轉(zhuǎn)換通過。這個(gè)方法的本質(zhì)是把模型中的非標(biāo)準(zhǔn)算子替換成昇騰原生算子能表達(dá)的組合。不需要重訓(xùn)改改網(wǎng)絡(luò)腳本重新導(dǎo)出即可。還有一個(gè)常見坑是輸入shape不匹配。如果ONNX里輸入是動(dòng)態(tài)shape但ATC命令里--input_shape沒寫或者寫錯(cuò)了節(jié)點(diǎn)名會(huì)報(bào)類似“input shape not specified”的錯(cuò)誤。解決方法是先用Netron打開ONNX文件確認(rèn)輸入節(jié)點(diǎn)名和維度再對(duì)應(yīng)填寫。4.2 推理結(jié)果全是背景框AIPP配置的鍋現(xiàn)象模型轉(zhuǎn)換成功、推理也成功但輸出的檢測(cè)框置信度全是0.01以下等于模型什么都沒檢出來。排查鏈路先用同一張測(cè)試圖在GPU上跑原始PyTorch模型確認(rèn)模型本身沒問題能正常檢出目標(biāo)。確認(rèn)輸入數(shù)據(jù)格式是否正確——這一步最常見的問題是用OpenCV讀圖后通道順序是BGR但我喂給模型的是RGB。如果AIPP里沒做通道轉(zhuǎn)換模型看到的顏色錯(cuò)了檢測(cè)結(jié)果自然一塌糊涂。檢查AIPP里的歸一化參數(shù)。YOLOv5導(dǎo)出ONNX時(shí)模型本身不包含歸一化處理輸入是0-255的U8像素值。如果AIPP里的var_reci_chn設(shè)成0或1.0之外的值相當(dāng)于對(duì)輸入做了額外的縮放特征分布完全不對(duì)。最終我把AIPP配置改成input_format: RGB888_U8csc_switch: truevar_reci_chn: 0.003921569即1/255同時(shí)不要在Host端再額外做歸一化。因?yàn)闅w一化已經(jīng)融進(jìn)模型了Host端只需要把resize后的RGB圖像原樣拷貝過去。理清“哪些預(yù)處理在Host做哪些在AIPP做”這個(gè)分工問題就解決了。4.3 輸出tensor維度對(duì)不上后處理崩潰現(xiàn)象C跑起來沒報(bào)錯(cuò)但輸出數(shù)據(jù)解析出來完全不是預(yù)想的1x25200x85。排查鏈路用ATC轉(zhuǎn)換時(shí)加--output_typeFP32確保輸出的數(shù)據(jù)類型是FP32而不是FP16。很多情況下默認(rèn)輸出是FP16如果不做類型轉(zhuǎn)換Host端用float解析會(huì)得到亂碼。打印實(shí)際輸出size跟預(yù)期對(duì)比。如果輸出size是輸入shape相關(guān)的1x25200x85x4字節(jié)說明shape對(duì)得上如果對(duì)不上回看ATC命令里是否漏了輸出節(jié)點(diǎn)配置。檢查輸出節(jié)點(diǎn)個(gè)數(shù)。YOLOv5的ONNX如果沒融合輸出可能是三個(gè)分支80x80、40x40、20x20每個(gè)分支shape不同如果融合了就是一個(gè)1x25200x85。ATC轉(zhuǎn)換后OM的輸出個(gè)數(shù)跟ONNX導(dǎo)出時(shí)的結(jié)構(gòu)一致。這里就要根據(jù)實(shí)際情況去寫解析邏輯。老實(shí)說我第一次跑通后處理是直接在Python里驗(yàn)證的用同一個(gè)OM模型通過acl的Python接口跑一遍把輸出dump到npy文件再用Python做NMS確認(rèn)檢測(cè)結(jié)果正確后再用C重寫。這種“兩步走”策略對(duì)排查后處理問題非常高效推薦新手也這么做。4.4 多卡場(chǎng)景下設(shè)備ID搞錯(cuò)Atlas 300V通常插在多卡服務(wù)器上設(shè)備編號(hào)從0開始。如果代碼里寫死device_id0但實(shí)際卡在別的PCIe槽位上可能兩張卡來回插拔過導(dǎo)致編號(hào)亂了。排查方法是npu-smi info查看實(shí)際設(shè)備列表和編號(hào)然后在代碼里用aclrtSetDevice(device_id)改成對(duì)應(yīng)編號(hào)。另外多進(jìn)程推理時(shí)每個(gè)進(jìn)程綁定不同的device_id避免相互搶占顯存。5. 性能觀察與調(diào)優(yōu)讓Atlas的算力真正吃滿模型跑通只是第一步生產(chǎn)環(huán)境里還要考慮吞吐和時(shí)延。這一節(jié)分享幾個(gè)我在實(shí)踐中驗(yàn)證有效的優(yōu)化方向。5.1 先用npu-smi觀察卡到底忙不忙很多人的慣性思維是“代碼不報(bào)錯(cuò)就是跑滿了”實(shí)際上完全不是。用npu-smi info能看到AI Core利用率、內(nèi)存占用、溫度、功耗這幾個(gè)關(guān)鍵指標(biāo)。我在一次壓測(cè)中發(fā)現(xiàn)單路推理時(shí)AI Core利用率只有30%左右溫度很低功耗也上不去——這說明模型推理大部分時(shí)間在等待數(shù)據(jù)搬運(yùn)算力沒吃滿。5.2 提高吞吐的三種有效手段第一種是加大batch。ATC轉(zhuǎn)換時(shí)把--input_shape設(shè)為images:4,3,640,640一次性喂4張圖做推理。數(shù)據(jù)搬運(yùn)的固定開銷被攤薄到多張圖上吞吐提升很明顯。我實(shí)測(cè)bs1到bs4吞吐能提升2倍以上。第二種是數(shù)據(jù)流水線化。把“圖像解碼采集”“預(yù)處理resize/通道轉(zhuǎn)換”“模型推理”“后處理NMS”這四個(gè)階段拆開用多線程或異步隊(duì)列串聯(lián)讓圖像采集和模型推理并行執(zhí)行。最簡(jiǎn)單的實(shí)現(xiàn)是開兩個(gè)線程一個(gè)線程做預(yù)處理并往隊(duì)列里放數(shù)據(jù)另一個(gè)線程做推理并處理輸出。這個(gè)改動(dòng)通常能讓整卡利用率再提升30%以上。第三種是用DVPP硬件預(yù)處理。昇騰平臺(tái)自帶DVPP硬件圖像編解碼模塊可以把resize、裁剪、格式轉(zhuǎn)換這些操作從CPU搬到硬件上執(zhí)行。我用它處理1080P視頻幀的resizeCPU占用率明顯下降整條推理流水線的時(shí)延更穩(wěn)定了。DVPP的API和直接memcpy不同要引入acl_dvpp的庫代碼會(huì)復(fù)雜一點(diǎn)但收益很實(shí)在。5.3 動(dòng)態(tài)分辨率的進(jìn)階玩法YOLO這類檢測(cè)模型輸入分辨率直接影響檢測(cè)精度。一個(gè)實(shí)用的優(yōu)化手段是大圖上先用小分辨率比如320x320跑一遍粗檢找出目標(biāo)集中區(qū)域再對(duì)區(qū)域用高分辨率比如1280x1280精檢。Atlas 300V固定shape推理時(shí)效率很高多跑一次小圖的開銷很小但這個(gè)策略能顯著提升小目標(biāo)召回率。這個(gè)方案在Atlas上實(shí)現(xiàn)比GPU上更舒服因?yàn)锳TC支持多模型同時(shí)加載到內(nèi)存里兩個(gè)模型來回切換推理的開銷很低很適合這種兩階段檢測(cè)思路。6. 一些個(gè)人體會(huì)和資料獲取建議Atlas這套生態(tài)跟CUDA生態(tài)最大的區(qū)別在于CUDA的資料鋪天蓋地遇到問題一搜就有答案昇騰的資料相對(duì)分散很多細(xì)節(jié)藏在官方文檔的角落和社區(qū)帖子里。我自己踩坑后總結(jié)了幾條找資料的經(jīng)驗(yàn)優(yōu)先看Ascend官方文檔里的“模型遷移”章節(jié)里面有專門針對(duì)PyTorch模型轉(zhuǎn)ONNX再轉(zhuǎn)OM的詳細(xì)說明。遇到算子不支持的報(bào)錯(cuò)不要急著改網(wǎng)絡(luò)先去昇騰社區(qū)搜算子名。有時(shí)候是CANN版本太舊升級(jí)到新版后算子里就覆蓋了。善用Netron工具查看ONNX結(jié)構(gòu)。所有ATC參數(shù)、輸出節(jié)點(diǎn)個(gè)數(shù)、輸入維度的問題都能在Netron里找到答案。另外CANN版本更新很快新版本對(duì)算子的支持和性能都有明顯提升。如果你用的版本太老建議優(yōu)先升級(jí)CANN到支持你硬件型號(hào)的最新穩(wěn)定版再考慮改網(wǎng)絡(luò)。很多時(shí)候版本一升原本報(bào)錯(cuò)的算子就自動(dòng)支持了。從拿到Atlas 300V的硬件到跑通YOLO、再到把吞吐調(diào)到接近卡的上限我的整體感受是昇騰推理卡的硬件規(guī)格和性價(jià)比確實(shí)能打但軟件棧的學(xué)習(xí)成本不能忽視。它不像插上NVIDIA GPU那樣開箱即用需要你愿意花一兩天時(shí)間讀文檔、試配置、排查報(bào)錯(cuò)。不過一旦把整個(gè)流程跑通后續(xù)再遷移其他模型就很快了——無非是導(dǎo)ONNX、調(diào)ATC、寫AscendCL三板斧。最后再說一個(gè)小技巧開發(fā)階段可以先用Python寫AscendCL的調(diào)用代碼做快速驗(yàn)證跑通后再用C重寫推理部分。Python接口和C接口的參數(shù)基本一一對(duì)應(yīng)但C省去了GIL鎖的干擾多線程并發(fā)推理的穩(wěn)定性更好。先用Python驗(yàn)證算法正確性再用C追求性能這個(gè)策略能讓你少走很多彎路。