部署YOLO:從環(huán)境配置到推理跑通)
最近后臺好幾個朋友都在問同一個詞atlas。有的是搜“atlas部署yolo”進來的問昇騰的推理卡怎么把YOLOv5跑起來有的更直接——“atlas 300v 24g 是運算加速卡嗎”一看就是采購清單里出現(xiàn)這型號想確認自己到底買了塊什么東西。這其實暴露了一個現(xiàn)狀越來越多做AI應(yīng)用的人開始接觸華為昇騰Atlas系列但這套生態(tài)對習(xí)慣了CUDA的人來說第一次上手確實有點繞。驅(qū)動裝好只是第一步后面還有CANN工具鏈、模型轉(zhuǎn)換、推理框架選型、算力評估一堆事。這篇文章我把從零接觸Atlas 300V 24G到把YOLO模型真正部署跑通的完整過程寫出來包括這塊卡到底怎么定位、為什么大家都拿它做目標檢測、CANN環(huán)境怎么配、模型怎么轉(zhuǎn)、推理代碼怎么寫、常見坑怎么排。適合剛拿到卡的朋友照著操作也適合準備采購還在猶豫的朋友用來判斷這張卡是不是你要的那盤菜。1. Atlas到底是個什么產(chǎn)品線300V 24G又是什么在動手部署之前先把Atlas這個概念理清楚。很多人第一次聽到“Atlas”以為是一個軟件框架其實它是昇騰AI處理器的硬件產(chǎn)品線名稱覆蓋從訓(xùn)練卡、推理卡到邊緣小站、服務(wù)器整機的一整套系列。我們常說的“Atlas 300V”“Atlas 300I”“Atlas 800”都屬于這條產(chǎn)品線的不同型號對應(yīng)不同的算力規(guī)格和使用場景。搞清楚自己手里的卡屬于哪類后續(xù)所有軟件選型才能對上號。1.1 昇騰Atlas家族里300V 24G是什么定位Atlas 300V是一個面向邊緣推理場景的加速卡系列主打視頻分析、目標檢測、圖像分類這一類負載典型形態(tài)是一張標準半高半長PCIe插卡插在服務(wù)器或者工控機的PCIe槽位上就能用。這一系列里有個很常見的細分型號就是24G版本。搜索熱詞里大家反復(fù)確認“atlas 300v 24g 是運算加速卡嗎”答案是肯定的它是一張標準的AI推理加速卡不是顯卡不能直接接顯示器它的任務(wù)是替代CPU去做神經(jīng)網(wǎng)絡(luò)的計算加速尤其擅長跑卷積神經(jīng)網(wǎng)絡(luò)的推理。這塊卡上集成了昇騰AI處理器的多個計算核心配合板載大容量內(nèi)存專門用來加載模型權(quán)重、存放中間特征圖從而把YOLO、ResNet這類模型跑出可用的幀率。相比GPU它的優(yōu)勢是功耗低、體積小、國產(chǎn)化軟硬件棧完整在安防、工業(yè)質(zhì)檢、智慧交通這些需要大規(guī)模邊緣部署的場景里性價比表現(xiàn)很突出。1.2 “24G”到底是顯存還是內(nèi)存很多人把這個24G直接理解成“24G顯存”不能說錯但不嚴謹。GPU上的GDDR顯存是為圖像渲染設(shè)計的高帶寬專用存儲而Atlas 300V板載的24G是LPDDR4X內(nèi)存角色上確實和顯存類似——推理時模型權(quán)重和中間特征都要放在這里容量越大能加載的模型越復(fù)雜。但它和GPU顯存不是一個東西軟件棧上也不走CUDA那套顯存管理接口。實際使用中這24G怎么理解更實在拿YOLO系列來說YOLOv5s的FP16模型轉(zhuǎn)換后排布在卡上大概占1到2GYOLOv8m這種中等規(guī)模模型也就幾個G24G容量意味著在不考慮算力瓶頸的前提下跑絕大多數(shù)落地級目標檢測模型都綽綽有余。如果你手里的業(yè)務(wù)模型更大比如一些多輸入、高分辨率的大模型這個容量也能兜得住。1.3 為什么大家盯上這張卡刨開參數(shù)大家選這張卡的真實原因我看下來就三條。第一是成本可控。一張Atlas 300V 24G的采購價格相比同等算力的GPU設(shè)備要低不少在批量部署的場景里差價會被放大得非常明顯。第二是功耗友好整卡典型功耗控制在百瓦以內(nèi)一臺2U服務(wù)器插滿四張卡供電和散熱的壓力都不大機房改造的成本很低。第三是國產(chǎn)化軟硬件棧CANN工具鏈完全自研從芯片到框架到算子庫都是自己的在信創(chuàng)和國產(chǎn)化替代的項目里幾乎成了默認選擇。但代價是生態(tài)遷移成本。以前在CUDA環(huán)境下訓(xùn)練好的模型沒辦法直接扔到這張卡上跑中間要做模型轉(zhuǎn)換推理代碼也要基于昇騰的ACL或者MindX SDK重寫。這篇文章后面要講的核心就是把這個遷移過程走通。2. 部署YOLO前先把昇騰軟件棧理順Atlas的部署難點不在硬件安裝而在軟件棧的理解。很多教程上來就叫你裝CANN裝完還是一頭霧水不知道下一步干嘛。我拆開講一下這套軟件棧到底分幾層每一層干什么裝完怎么確認沒裝錯。昇騰的軟件體系大致是三層底層是驅(qū)動和固件負責(zé)讓操作系統(tǒng)能識別這張卡管理設(shè)備節(jié)點和內(nèi)存中間是CANN工具包包含算子庫、模型轉(zhuǎn)換工具ATC、運行時ACL以及上層用的應(yīng)用開發(fā)接口再往上才是推理框架比如華為的MindX SDK或者你直接用ACL手寫推理邏輯。2.1 必備三件套驅(qū)動、固件、CANN很多新手在第一步就卡住因為不知道要裝三個東西以為裝一個就完事了。實際必須安裝的是固件firmware燒錄到設(shè)備上的底層程序管芯片的初始化、電源、溫度這些硬件行為。驅(qū)動driver讓Linux系統(tǒng)能識別這張PCIe卡安裝后會出現(xiàn)/dev/davinci0這類設(shè)備節(jié)點。CANN Toolkit昇騰的計算平臺軟件包提供算子、推理運行時、模型轉(zhuǎn)換工具。三者還有版本配套關(guān)系。下載時建議直接去昇騰社區(qū)官網(wǎng)找到和你的卡型號匹配的版本組合。最容易踩的坑是版本不配套比如固件和驅(qū)動版本跨度大導(dǎo)致設(shè)備起不來或者CANN版本和驅(qū)動不兼容跑推理時直接報錯。安裝順序基本是先固件、再驅(qū)動、最后CANN。固件驅(qū)動安裝包一般是一個.run文件用root權(quán)限執(zhí)行按提示走就行。CANN Toolkit也是.run文件但安裝路徑建議固定放在/usr/local/Ascend下因為后面很多環(huán)境變量默認指向這里。2.2 快速檢查環(huán)境是否就緒裝完這三件套別急著寫代碼先執(zhí)行一個命令確認設(shè)備正常npu-smi info這個命令類似GPU世界里的nvidia-smi能看到卡的溫度、功耗、芯片使用率、內(nèi)存占用還能確認驅(qū)動和固件版本是否匹配。如果執(zhí)行報錯優(yōu)先排查/dev/davinci0是否存在、當前用戶有沒有權(quán)限、驅(qū)動是否加載成功。如果npu-smi info能正常打印出卡的信息說明硬件層面已經(jīng)OK。接著驗證CANN是否裝好可以隨便寫一句Python導(dǎo)入測試CANN一般會帶AscendCL的Python接口python3 -c import acl; print(acl ok)這里如果報找不到模塊基本就是環(huán)境變量沒配對CANN安裝目錄下的set_env.sh腳本就是干這個的記得在測試前 source 一下。2.3 一個容易卡住的坑NPU設(shè)備權(quán)限我這里單獨拿出來講因為踩的人實在太多了。裝好驅(qū)動后普通用戶執(zhí)行npu-smi info大概率會報權(quán)限錯誤因為/dev/davinci*設(shè)備節(jié)點的默認權(quán)限只允許root訪問。解決辦法有兩個要么把當前用戶加到HwHiAiUser用戶組CANN安裝時默認創(chuàng)建的用戶組要么用root跑所有命令。我個人建議后者只在調(diào)試時用日常工作還是配好用戶組不然以后跑服務(wù)長期用root會有安全風(fēng)險。sudo usermod -a -G HwHiAiUser $USER執(zhí)行完退出重新登錄。然后再跑npu-smi info能看到芯片使用率開始慢慢走動說明環(huán)境和設(shè)備已經(jīng)打通可以進入模型部署階段了。3. 手把手把YOLO模型搬到Atlas上環(huán)境搞定之后就到了重點環(huán)節(jié)怎么把訓(xùn)練好的YOLO模型部署到Atlas 300V 24G上。之前用GPU開發(fā)的朋友要注意PyTorch訓(xùn)練出來的.pt權(quán)重文件Atlas是不能直接加載的。昇騰推理的官方模型格式是.om需要經(jīng)過一套離線轉(zhuǎn)換流程。這中間還要解決算子兼容、輸入尺寸固定、后處理實現(xiàn)這幾個問題。好在昇騰提供了ATCAscend Tensor Compiler工具把模型轉(zhuǎn)換這件事做成了半自動流程我們要做的就是準備好中間格式模型、寫好轉(zhuǎn)換參數(shù)、處理掉不支持的算子。3.1 權(quán)重準備從PyTorch到ONNXATC支持直接轉(zhuǎn)換多種框架的模型但實際部署里最穩(wěn)的路線是PyTorch權(quán)重先導(dǎo)出為ONNX再用ATC轉(zhuǎn)成OM。原因很簡單ONNX是中間表示框架差異早就抹平了算子兼容性最好排查問題也最直接。以YOLOv5為例倉庫里官方提供了導(dǎo)出腳本一行命令就能出ONNXpython export.py --weights yolov5s.pt --include onnx --img-size 640 640導(dǎo)出的時候有幾個細節(jié)要注意。一是輸入尺寸ATC轉(zhuǎn)換時要固定輸入shape如果訓(xùn)練時用的不是640導(dǎo)出時就要統(tǒng)一二是opset版本建議設(shè)置在11到13之間太新或太舊都可能觸發(fā)ATC不支持的算子三是導(dǎo)出后的ONNX要自己驗證一下用OnnxRuntime跑一張測試圖確認輸出結(jié)果和PyTorch一致再去轉(zhuǎn)OM。這個驗證步驟很多人跳過后面出問題回頭查的時候會非常痛苦。3.2 ATC離線轉(zhuǎn)換生成OM模型有了ONNX文件接下來用ATC把它轉(zhuǎn)成OM。我實際用的命令大致長這樣source /usr/local/Ascend/ascend-toolkit/set_env.sh atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_formatNCHW \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg這里解釋幾個關(guān)鍵參數(shù)。--framework5表示輸入模型是ONNX格式這是ATC框架類型的固定編號。--soc_version要填卡上AI處理器對應(yīng)的版本號具體是哪個要查卡的規(guī)格或者用npu-smi info配合工具確認填錯會直接轉(zhuǎn)換失敗。跑YOLO這種檢測模型還需要--insert_op_conf指向一個AIPP配置文件作用是把圖片預(yù)處理縮放、歸一化、通道變換從CPU挪到NPU上做大幅縮短單張圖片的預(yù)處理時間。轉(zhuǎn)換過程如果順利會輸出一個.om文件。如果中途報算子不支持的錯誤一般有兩種情況一是ONNX里帶了動態(tài)shape操作需要把輸入的shape固定死二是某幾個算子在圖優(yōu)化階段沒法嵌合這時候最省事的辦法是回模型導(dǎo)出環(huán)節(jié)調(diào)整opset版本或者把耗時操作挪到模型外面。這里提醒一句YOLOv5輸出的不是最終檢測框而是三個尺寸的預(yù)測特征圖所以后處理anchor解碼、NMS需要另外實現(xiàn)。ATC本身可以往OM模型里插入后處理算子但我實測下來這種方案靈活性很差一旦要改閾值、改IOU策略就得重新轉(zhuǎn)模型非常費事。更推薦的做法是讓OM只輸出特征圖在外部用NumPy或者OpenCV做后處理。3.3 推理代碼怎么寫得順手模型轉(zhuǎn)換完成接下來編寫推理代碼。昇騰官方提供兩條路線一是直接用ACLAscendCLPython API寫起來比較接近PyTorch的推理腳本二是用MindX SDK把解碼、縮放、推理、后處理串成pipeline適合視頻流的批量分析。我個人建議剛上手時用ACL Python接口邏輯透明方便定位問題。核心流程就是四步初始化設(shè)備、加載模型、準備輸入輸出、執(zhí)行推理。做一個最小實現(xiàn)簡化后的代碼邏輯如下import acl import numpy as np # 初始化 ret acl.init() ret acl.rt.set_device(0) # 加載模型 model_path byolov5s_bs1.om model_id acl.mdl.load_from_file(model_path) # 準備輸入輸出 input_size 1 * 3 * 640 * 640 output_size ... # 從模型描述信息里取 input_data np.random.rand(input_size).astype(np.float32) input_buffer acl.rt.malloc(input_size * 4, 2) # 這里需要把數(shù)據(jù)拷貝到設(shè)備內(nèi)存并創(chuàng)建數(shù)據(jù)描述對象 # 執(zhí)行推理 ret acl.mdl.execute(model_id, input_buffer, output_buffer) # 釋放資源 acl.mdl.unload(model_id) acl.rt.reset_device(0) acl.finalize()上面是骨架真正寫的時候還需要通過acl.mdl.get_desc查模型的輸入輸出維度因為OM模型的一個特點就是輸入輸出在轉(zhuǎn)換時就固定了代碼必須嚴格按照這個shape來配緩沖區(qū)。很多人第一次跑通耗在維度不匹配上調(diào)試方法也很簡單先打印模型描述把輸入輸出的維度、數(shù)據(jù)類型都打出來再對照著改代碼。輸入圖片的預(yù)處理有兩個選擇如果你在AIPP配置里開了圖像處理那只需要把原始圖片數(shù)據(jù)拷貝進內(nèi)存不用在CPU側(cè)做歸一化如果沒開AIPP那就要在CPU側(cè)完成resize、減均值、乘系數(shù)、HWC轉(zhuǎn)CHW處理成模型要求的輸入形態(tài)對齊后直接進卡。兩種方式都行但AIPP方式省CPU處理視頻流時優(yōu)勢很明顯。后處理部分自己寫YOLO的解碼和NMS邏輯有點工作量但思路完全和GPU版本一致。先按anchor把三個特征圖解碼出候選框坐標和置信度再做閾值過濾、類別篩選、NMS去重。整個過程用NumPy實現(xiàn)在24G這張卡上跑后處理耗時占比不大不會成為瓶頸。3.4 AIPP歸一化到底要不要開AIPP是很多新手會忽略、但實際影響特別大的配置。YOLOv5訓(xùn)練時輸入的歸一化方式是像素值除以255然后做RGB通道的減均值標準化。如果不做任何配置這些操作全部得在CPU側(cè)寫代碼完成每幀圖片都要做一遍算力不高的邊緣設(shè)備上會白白消耗不少CPU時間。AIPP配置文件的思路是把這些預(yù)處理挪到NPU上在圖像數(shù)據(jù)進入模型之前由硬件完成色域轉(zhuǎn)換、縮放、歸一化。配置文件的簡單示例aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: false 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.003921568859368562 var_reci_chn_1: 0.003921568859368562 var_reci_chn_2: 0.003921568859368562 }注意var_reci_chn填的是方差的倒數(shù)YOLO場景里就是 1/255 也就是 0.003921569減均值設(shè)成0。開了AIPP以后CPU側(cè)只需要做圖片解碼和縮放不需要再寫歸一化的代碼了推理管線會干凈很多。但這塊有個坑如果開了AIPPCPU側(cè)就不能再對圖像做歸一化否則就等于做了兩遍預(yù)處理輸出置信度會異常很多看起來“模型跑通了但結(jié)果全錯”的問題就是這么來的。4. 這塊卡實際跑下來性能評估與故障排查模型能跑通了下一個繞不開的問題是這張卡到底能扛多大并發(fā)實際部署中排查問題從哪入手我把自己的實測結(jié)果和踩坑記錄整理一下。4.1 這張卡能跑幾路視頻流先給結(jié)論拿YOLOv5s模型、640x640輸入、單卡模式來測單路視頻流跑到25到30幀每秒是沒什么壓力的。如果業(yè)務(wù)是檢測多路攝像頭目標是每路實時分析那要結(jié)合兩個維度評估單路延遲能不能接受以及卡的算力余量有多少。從算力分配上看300V 24G面向的就是邊緣視頻分析場景內(nèi)部多核心并行多路視頻流可以同時占滿整卡算力。實際壓測下來1080p視頻流如果限制每路10幀左右分析頻率跑4到6路是比較穩(wěn)的區(qū)間。超過這個量要么降低輸入分辨率要么拉大抽幀間隔要么走多卡方案。一個容易被忽略的瓶頸是CPU而不是NPU。如果后處理全用Python實現(xiàn)CPU占用會隨著路數(shù)增加直線上升最后可能卡在CPU上。解決辦法是控制每一幀在CPU側(cè)的處理耗時能向量化的操作盡向量化能扔給OpenCV的不要自己寫循環(huán)。再激進一點把NMS邏輯改成C擴展或者換用MindX SDK的流程編排讓后處理也在卡上做一部分。4.2 常見問題速查表部署期間最常遇到的問題我整理成一個速查表基本覆蓋了新手會碰到的80%場景?,F(xiàn)象可能原因處理方式執(zhí)行npu-smi info報權(quán)限錯誤用戶不在 HwHiAiUser 組用usermod -a -G HwHiAiUser $USER加入組并重新登錄執(zhí)行npu-smi info報驅(qū)動版本不匹配固件和驅(qū)動版本不一致重新刷配套版本的固件和驅(qū)動Python導(dǎo)入acl模塊失敗沒source環(huán)境變量或CANN路徑不對檢查/usr/local/Ascend/ascend-toolkit/set_env.sh是否被正確sourceATC轉(zhuǎn)換報算子不支持ONNX里有動態(tài)shape或opset版本過新固定輸入shape調(diào)低opset到11~13執(zhí)行推理時模型加載失敗OM模型Soc版本和實際芯片不匹配用npu-smi info配合查詢實際芯片版本重轉(zhuǎn)模型推理能跑但輸出結(jié)果全是garbageCPU預(yù)處理和AIPP重復(fù)做了歸一化檢查AIPP開關(guān)和CPU側(cè)代碼二選一視頻流多了以后延遲突然升高CPU后處理成為瓶頸改用向量化操作限制抽幀率考慮MindX SDK流水線排查順序我通常是從底往上先確認硬件設(shè)備正常再確認驅(qū)動固件版本然后驗證CANN運行環(huán)境最后才懷疑模型轉(zhuǎn)換和代碼邏輯。按照這個順序走大多數(shù)問題能在十分鐘內(nèi)定位。4.3 幾個真金白銀的避坑經(jīng)驗最后分享幾條實操中得來的經(jīng)驗屬于文檔里不會細講、但實際影響很大的點。第一點是盡量用FP16而不是FP32做推理。模型轉(zhuǎn)換時ATC提供了半精度轉(zhuǎn)換選項YOLO這類模型在FP16下推理精度損失非常小但速度有明顯收益。如果是自己用PyTorch轉(zhuǎn)ONNX可以在導(dǎo)出時把權(quán)重轉(zhuǎn)成半精度也可以靠ATC在轉(zhuǎn)換時統(tǒng)一處理。實測下來FP16對檢測框精度的影響基本控制在一個像素以內(nèi)完全可以接受。第二點是輸入尺寸別一味求大。640x640是YOLOv5的默認訓(xùn)練尺寸但如果你檢測的目標比較大或者攝像頭機位離目標比較近試試416甚至320輸入推理速度能提升一截精度損失往往沒你想的那么夸張。在邊緣設(shè)備上這個平衡非常值得做。第三點是批處理大小要考慮實際業(yè)務(wù)形態(tài)。ATC轉(zhuǎn)換時--input_shape里的bs參數(shù)決定了一次推理處理幾張圖。實時視頻流場景建議bs1減少單次等待時間批量檢測場景可以設(shè)bs4或者8吞吐量能跑得更滿。別一上來就設(shè)個大batch邊緣設(shè)備的實時性要求通常比吞吐要求更敏感。第四點也是我踩過最狠的一腳不要在CANN版本很舊的環(huán)境里硬套新版文檔的命令。昇騰的接口演進非??觳煌姹纠顰TC參數(shù)名、Python接口調(diào)用方式都有差異。遇到報錯先看版本號再去對應(yīng)版本的文檔里查不要拿著新命令在舊環(huán)境里死磕。關(guān)于后續(xù)的擴展方向Atlas 300V 24G這套環(huán)境跑通YOLO之后再往深了走還有兩個方向比較有價值。一個是把推理代碼從單張圖片擴展到視頻流用多線程加隊列的方式讓解碼、預(yù)處理、推理、后處理四段流程并行起來吞吐量能再上一個臺階。另一個是基于MindX SDK搭一套完整的檢測服務(wù)把結(jié)果輸出成標準協(xié)議接口直接對接業(yè)務(wù)系統(tǒng)。我個人在實際部署中的體會是昇騰這套生態(tài)現(xiàn)在真正卡人的地方已經(jīng)不在硬件性能而在軟件棧的學(xué)習(xí)曲線。但只要沉下心把一個模型從轉(zhuǎn)換到跑通走完整一遍后續(xù)遷其他模型的成本會直線下降。如果你也正在調(diào)這塊卡的部署照著上面的順序踩一遍應(yīng)該能少走不少彎路。