戰(zhàn)指南)
做AI推理這幾年我手里經(jīng)手過不少加速卡GPU、NPU、FPGA都折騰過。前陣子因?yàn)轫?xiàng)目需求認(rèn)真地把華為昇騰的Atlas 300V 24G摸了一遍還順手把YOLOv5/YOLOv8的模型完整部署了上去。之所以想寫這篇東西是因?yàn)槲野l(fā)現(xiàn)在社區(qū)里“atlas部署yolo”和“atlas 300v 24g 是運(yùn)算加速卡嗎”這類問題的搜索量特別高但真正把從硬件認(rèn)識(shí)、驅(qū)動(dòng)環(huán)境、模型轉(zhuǎn)換到推理調(diào)優(yōu)整條鏈路講清楚的文章少之又少很多人卡在第一步就不動(dòng)了。這篇文章我不想講太多PPT上的理論就從一個(gè)實(shí)際跑過項(xiàng)目的人角度把我踩過的坑、驗(yàn)證過的命令、試出來有效的配置全部攤開講給想做昇騰推理的同學(xué)一條能直接照著走的路。1. Atlas 300V 24G到底是個(gè)什么“加速卡”1.1 先回答熱搜它確實(shí)是運(yùn)算加速卡但和你想的可能不一樣先直接回答那個(gè)被問爛了的問題Atlas 300V 24G是運(yùn)算加速卡嗎答案是肯定的它是一張專門為AI推理設(shè)計(jì)的加速卡。但它和你印象里的“顯卡”完全是兩回事——它沒有顯示輸出接口你沒法給它接個(gè)顯示器打游戲它誕生出來的唯一目的就是把神經(jīng)網(wǎng)絡(luò)模型的計(jì)算跑得飛快。這顆卡的核心是昇騰310P系列處理器具體型號(hào)在部分文檔里也叫310P324G指的是板載內(nèi)存容量這個(gè)內(nèi)存不是用來存畫面的而是用來存放模型權(quán)重、中間特征圖和推理數(shù)據(jù)的。換算成大家熟悉的GPU語(yǔ)言它更像是“專門跑AI作業(yè)的協(xié)處理器”。在模型推理場(chǎng)景下它單卡能提供的INT8算力在百級(jí)TOPS左右具體數(shù)值跟頻率和功耗模式有關(guān)功耗卻控制得很低我記得標(biāo)稱大概在72W附近比動(dòng)輒三四百瓦的GPU溫和太多。很多朋友第一次拿到這卡會(huì)犯迷糊因?yàn)樗L(zhǎng)得太像一張顯卡了又帶散熱片又帶擋板。我在這里給你提個(gè)醒千萬別拿它當(dāng)顯卡用也別指望它能幫你做CUDA加速它的軟件棧是華為自己的一套CANN生態(tài)和CUDA不通用。想在上面跑東西所有的模型轉(zhuǎn)換、算子適配、推理調(diào)用都要圍繞昇騰的工具鏈來。1.2 它憑什么能跑YOLO硬件規(guī)格與算力定位部署YOLO這類目標(biāo)檢測(cè)模型本質(zhì)上就是把訓(xùn)練好的權(quán)重文件轉(zhuǎn)換成能在NPU上運(yùn)行的離線模型OM格式然后通過昇騰的推理接口調(diào)用硬件完成前向計(jì)算。Atlas 300V 24G在這個(gè)過程中的定位非常清晰它是一款高能效比的推理卡專攻“已經(jīng)訓(xùn)練好的模型”的加速而不是用來訓(xùn)練的。拿YOLOv5s舉例這個(gè)模型在GPU上用FP16跑一張圖大概也就幾毫秒到十幾毫秒在Atlas 300V上如果配置得當(dāng)單張圖的推理時(shí)間同樣可以做到個(gè)位數(shù)毫秒級(jí)別。你可能覺得這不就是“能跑”嘛沒什么稀奇。但真正讓它有價(jià)值的是批量處理能力和功耗比在視頻流分析場(chǎng)景里一路視頻按25幀算每秒需要處理25張圖一張卡同時(shí)處理8路、16路視頻流時(shí)它能穩(wěn)定壓住幀率而且整卡功耗遠(yuǎn)低于同規(guī)格GPU。這個(gè)優(yōu)勢(shì)在機(jī)房部署和邊緣服務(wù)器里非常值錢。另外要澄清一個(gè)概念A(yù)tlas 300V 24G這個(gè)“24G”并不是越大越好它主要用來容納更大的模型和更大的batch。比如你想一次推理塞進(jìn)去8張甚至16張圖或者跑YOLOv8x這種大模型內(nèi)存需求就會(huì)明顯上漲。實(shí)際項(xiàng)目中我建議先確認(rèn)模型大小和batch策略再?zèng)Q定要不要上24G版本。如果只是跑個(gè)YOLOv5s單batch推理其實(shí)8G甚至更小內(nèi)存的型號(hào)也能勝任沒必要為了“大內(nèi)存”多花錢。但如果你要做多路視頻流并發(fā)推理24G的余量會(huì)讓人從容很多。2. 部署YOLO前的準(zhǔn)備工作把環(huán)境一次配到位2.1 硬件安裝與驅(qū)動(dòng)檢查拿到卡后第一件事Atlas 300V是一張PCIe插槽的卡安裝過程和裝顯卡基本一樣。但有幾個(gè)細(xì)節(jié)我提醒一下第一供電一定要接好。部分型號(hào)的300V除了PCIe插槽供電外還需要外接一個(gè)8pin或者6pin的輔助供電口。有些同學(xué)裝機(jī)時(shí)圖省事不接外電結(jié)果上電后系統(tǒng)死活識(shí)別不到卡查了半天發(fā)現(xiàn)是供電沒插。這問題我見過不止一次強(qiáng)烈建議你裝卡之前先看清楚卡上的供電接口類型。第二驅(qū)動(dòng)和固件版本要匹配。從官網(wǎng)下載對(duì)應(yīng)型號(hào)的CANN工具包時(shí)里面通常會(huì)包含NPU驅(qū)動(dòng)和固件。我踩過的坑是先裝了舊版驅(qū)動(dòng)再裝新版CANN結(jié)果在推理初始化時(shí)報(bào)設(shè)備不支持的錯(cuò)。后來老老實(shí)實(shí)按官方要求把固件、驅(qū)動(dòng)、CANN三者版本對(duì)齊才解決。裝完驅(qū)動(dòng)后可以用npu-smi info命令查看卡的狀態(tài)這個(gè)命令和NVIDIA的nvidia-smi很像能看到卡的溫度、內(nèi)存占用、算力利用率等關(guān)鍵信息。npu-smi info正常狀態(tài)下你能看到類似這樣的輸出里面有具體的芯片型號(hào)“Ascend 310P”和內(nèi)存大小如果這里顯示不出來說明驅(qū)動(dòng)或硬件連接有問題先別急著往下走把環(huán)境整干凈再說。2.2 軟件棧選型CANN、MindSpore Lite還是自定義算子路徑Atlas卡上的軟件棧不像CUDA那樣只有一條路它其實(shí)給了你幾種選擇。我用過之后給你梳理一下CANN AscendCL這是最底層、最可控的方案。AscendCLACL是C語(yǔ)言/Python的推理接口類似CUDA的Runtime API。你直接調(diào)用aclrt_malloc、aclmdlExecute這類接口自己管理內(nèi)存、排隊(duì)、同步。優(yōu)點(diǎn)是靈活性最高性能潛力最大缺點(diǎn)是你得像寫CUDA一樣注意資源的管理。這是我最推薦的方式后面我也會(huì)重點(diǎn)講這條路徑。MindSpore Lite如果你訓(xùn)練模型用的是MindSpore框架轉(zhuǎn)換和部署會(huì)比較順滑。但如果你手里是PyTorch的權(quán)重反而多一層轉(zhuǎn)換步驟沒太大優(yōu)勢(shì)。第三方推理框架比如通過OpenCV的DNN模塊配合CANN后端或者用ONNX Runtime的昇騰EPExecution Provider。這種方式上手最快幾行代碼就能跑起來但對(duì)算子的控制力弱性能上限也有限。我建議生產(chǎn)環(huán)境還是走AscendCL。我在實(shí)際項(xiàng)目中選了CANN AscendCL原因很簡(jiǎn)單部署YOLO這種模型后處理里NMS非極大值抑制和輸出解析占的時(shí)間不少如果全丟給框架的黑盒處理出了問題很難定位。自己用ACL把推理主鏈路管起來后處理在CPU上自己寫出了性能問題我能明確知道瓶頸在NPU還是在后處理排查起來清晰很多。提示第一次接觸昇騰的同學(xué)我建議先把CANN開發(fā)套件里的sample跑通一個(gè)比如官方自帶的resnet50推理樣例。先不管你的YOLO模型這一步是為了驗(yàn)證驅(qū)動(dòng)、固件、CANN三方環(huán)境是好的。樣例能跑通再往上加復(fù)雜度。3. 模型遷移鏈路從PyTorch權(quán)重到OM離線模型3.1 導(dǎo)出ONNX的三個(gè)關(guān)鍵點(diǎn)在Atlas上跑YOLO最繞不開的一步就是模型轉(zhuǎn)換。昇騰的NPU不認(rèn)PyTorch的權(quán)重文件它只認(rèn)OM格式的離線模型。所以整個(gè)遷移鏈路通常是PyTorch權(quán)重 → ONNX → OM。這個(gè)過程中ONNX導(dǎo)出是第一個(gè)大坑。我基于YOLOv5和YOLOv8的實(shí)際經(jīng)驗(yàn)給你總結(jié)三個(gè)關(guān)鍵點(diǎn)第一輸入尺寸一定要固定。導(dǎo)出ONNX時(shí)建議把輸入尺寸定死在模型推理時(shí)實(shí)際使用的尺寸比如640x640。雖然ONNX協(xié)議支持動(dòng)態(tài)尺寸但昇騰的ATC轉(zhuǎn)換對(duì)動(dòng)態(tài)shape支持有限動(dòng)態(tài)尺寸不僅會(huì)拉低性能還容易在轉(zhuǎn)換時(shí)報(bào)錯(cuò)。我在項(xiàng)目里直接固定成1x3x640x640省了無數(shù)麻煩。如果你確實(shí)需要多尺寸推理你可以轉(zhuǎn)換多個(gè)不同尺寸的OM模型運(yùn)行時(shí)根據(jù)輸入圖尺寸動(dòng)態(tài)選擇。第二后處理算子不要一股腦塞進(jìn)模型里。YOLO的檢測(cè)頭輸出通常包括目標(biāo)框坐標(biāo)、置信度、類別概率后續(xù)還要經(jīng)過解碼和NMS。有些同學(xué)圖省事把NMS也寫進(jìn)模型網(wǎng)絡(luò)里想著NPU能一并處理。但昇騰對(duì)NMS這類動(dòng)態(tài)算子的支持很有限強(qiáng)行塞進(jìn)去輕則性能變差重則轉(zhuǎn)換失敗。我建議只保留模型主干和檢測(cè)頭的原始輸出把解碼和NMS放到模型外部的CPU后處理里。第三用torch.onnx.export時(shí)注意算子版本。我遇到過導(dǎo)出的ONNX里某些算子版本過新ATC不認(rèn)的情況。通常是設(shè)置opset_version11或12就夠用了沒必要追求最新版。另外導(dǎo)出后最好用onnxsim等工具對(duì)圖做一次簡(jiǎn)化去掉一些冗余的Transpose、ReshapeATC轉(zhuǎn)換時(shí)成功率會(huì)明顯提高。下面是我導(dǎo)出YOLOv5 ONNX時(shí)常用的一段代碼關(guān)鍵部分import torch from models.experimental import attempt_load model attempt_load(yolov5s.pt, map_locationcpu) model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, yolov5s.onnx, opset_version11, input_names[images], output_names[output], dynamic_axesNone ) print(export done)導(dǎo)出后我會(huì)立刻用onnxruntime跑一遍確認(rèn)輸出結(jié)果和PyTorch原始推理一致再進(jìn)入ATC轉(zhuǎn)換。這一步能提前暴露很多算子兼容性問題。3.2 ATC轉(zhuǎn)換實(shí)戰(zhàn)命令、AIPP配置與常見報(bào)錯(cuò)拿到ONNX模型后下一步就是用ATC工具把它轉(zhuǎn)成OM。ATC工具在CANN安裝目錄下的/usr/local/Ascend/ascend-toolkit/latest/bin/atc第一次用要先確認(rèn)它在你系統(tǒng)的PATH里。我最常用的轉(zhuǎn)換命令長(zhǎng)這樣atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --output_typeFP32這里幾個(gè)參數(shù)我逐個(gè)解釋--framework5表示輸入是ONNX格式這是ATC的固定寫法。--output是輸出OM文件的前綴名。--input_shape需要和你導(dǎo)出ONNX時(shí)完全一致不然會(huì)報(bào)維度不匹配。--soc_version要注意Atlas 300V對(duì)應(yīng)的是Ascend310P3別和Atlas 300I的Ascend310P1弄混了選錯(cuò)會(huì)直接報(bào)錯(cuò)。--insert_op_conf是AIPP配置文件這個(gè)非常關(guān)鍵我單獨(dú)說一下。AIPPAI Preprocessing是昇騰在硬件上做預(yù)處理的功能可以把圖像縮放、減均值、除方差、色域轉(zhuǎn)換這些操作從CPU挪到NPU上完成。很多人在轉(zhuǎn)換時(shí)忽略這一步把預(yù)處理全部留在CPU上用OpenCV做結(jié)果推理速度被CPU預(yù)處理拖慢不少。我建議把所有能下沉的預(yù)處理都配置進(jìn)AIPP。下面是一個(gè)YOLOv5彩色圖輸入的典型AIPP配置aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }這里input_format: RGB888_U8告訴AIPP輸入是RGB三通道8位圖模型訓(xùn)練時(shí)的預(yù)處理是標(biāo)準(zhǔn)化到0~1所以mean全為0var_reci是1/255約等于0.00392。如果你訓(xùn)練時(shí)用的是歸一化均值±標(biāo)準(zhǔn)差還需要按實(shí)際值配置。AIPP這個(gè)設(shè)計(jì)我覺得比GPU上手動(dòng)寫預(yù)處理要優(yōu)雅不少一旦配好推理時(shí)輸入就可以直接丟原始圖像數(shù)據(jù)進(jìn)去NPU自己完成resize和歸一化。轉(zhuǎn)換成功后你會(huì)在輸出目錄里看到y(tǒng)olov5s_bs1.om文件。可以用omg自帶的工具查看模型信息或者直接進(jìn)入下一步用一個(gè)小測(cè)試腳本來驗(yàn)證OM能不能正確推理。常見報(bào)錯(cuò)我先列幾個(gè)E10001: Input shape is inconsistent輸入shape沒對(duì)齊檢查ATC參數(shù)里的--input_shape。E10002: Unsupported op type xxx模型里有ATC不支持的算子。這時(shí)優(yōu)先考慮回源頭修改模型把特殊算子替換成通用算子。E19999: Inner Error這種比較頭疼通常是CANN版本和模型算子兼容性問題可以先查CANN的版本日志再考慮升級(jí)或降級(jí)CANN版本。4. 基于AscendCL的推理部署寫一個(gè)能跑的YOLO推理程序4.1 初始化與資源管理模型轉(zhuǎn)換好了接下來就是寫推理程序。這里我走的是CANN AscendCL路徑Python版本用起來也很方便C語(yǔ)言適合性能極致要求的場(chǎng)景我開發(fā)時(shí)先用Python快速驗(yàn)證線上再優(yōu)化C。這里我介紹Python版本的流程因?yàn)槟闩芡ㄟ壿嫼笤俑某蒀只是API層面的替換。推理程序的第一步是初始化ACL環(huán)境import acl # 初始化 ret acl.init() assert ret 0 # 設(shè)置推理設(shè)備 ret acl.rt.set_device(0) assert ret 0 # 創(chuàng)建上下文 context, ret acl.rt.create_context(0) assert ret 0這里有幾個(gè)容易犯的錯(cuò)誤第一每個(gè)進(jìn)程必須且只能調(diào)用一次acl.init()多次調(diào)用會(huì)報(bào)重復(fù)初始化的錯(cuò)。第二acl.rt.set_device(0)里的0是設(shè)備ID如果你機(jī)器上插了多張Atlas卡要先確認(rèn)你的模型在哪張卡上跑??梢杂胣pu-smi info查看卡的編號(hào)。第三上下文Context一定要?jiǎng)?chuàng)建并且后續(xù)所有推理調(diào)用都必須在同一個(gè)上下文中執(zhí)行。這就像CUDA里的context一樣搞錯(cuò)了會(huì)莫名其妙地報(bào)空指針錯(cuò)誤。初始化完成后加載OM模型model_path byolov5s_bs1.om # 加載模型 model_id, ret acl.mdl.load_from_file(model_path) assert ret 0 # 獲取模型描述 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id)模型加載成功后你就拿到了model_id后續(xù)所有推理執(zhí)行都靠它。同時(shí)我強(qiáng)烈建議你調(diào)用acl.mdl.get_desc獲取模型的輸入輸出信息它會(huì)告訴你模型期望的輸入大小、輸出Tensor數(shù)量、每個(gè)Tensor的shape和數(shù)據(jù)類型。這些信息在后處理階段非常重要。4.2 推理主流程拆解YOLO推理的完整流程是讀圖 → 預(yù)處理 → 拷貝到設(shè)備內(nèi)存 → 推理 → 從設(shè)備內(nèi)存取回輸出 → 后處理。在ACL環(huán)境下每一步都有對(duì)應(yīng)的API。讀圖和預(yù)處理我用OpenCV完成但注意因?yàn)锳IPP已經(jīng)把resize和歸一化下沉到NPU了所以CPU端只需要把圖像數(shù)據(jù)轉(zhuǎn)成RGB排列并resize到640x640不需要再做歸一化。然后申請(qǐng)?jiān)O(shè)備內(nèi)存并拷貝數(shù)據(jù)# 假設(shè)image是resize后的RGB圖像連續(xù)內(nèi)存 image_bytes image.tobytes() # 申請(qǐng)?jiān)O(shè)備內(nèi)存 device_data, ret acl.rt.malloc(640 * 640 * 3, 2) # 2是內(nèi)存對(duì)齊 # 從主機(jī)內(nèi)存拷貝到設(shè)備內(nèi)存 ret acl.rt.memcpy(device_data, 640 * 640 * 3, image_bytes, 640 * 640 * 3, acl.aclrt_memcpy_kind.aclrt_memcpy_kind_host_to_device)注意acl.rt.malloc的第二個(gè)參數(shù)是內(nèi)存對(duì)齊大小通常傳2即64字節(jié)對(duì)齊也可以傳32具體看官方要求。拷貝方式一定要是host_to_device方向反了你會(huì)在推理時(shí)得到一堆亂碼。執(zhí)行推理這一步很關(guān)鍵ACL支持同步和異步兩種方式。同步接口acl.mdl.execute簡(jiǎn)單粗暴調(diào)用完就阻塞直到推理結(jié)束。異步接口acl.mdl.execute_async需要配合stream使用適合高吞吐場(chǎng)景。我開發(fā)階段先用同步接口驗(yàn)證正確性性能調(diào)優(yōu)時(shí)才切異步。# 同步推理 ret acl.mdl.execute(model_id, [device_data], # 輸入設(shè)備內(nèi)存指針列表 [output_size], # 輸出大小列表 [output_data], # 輸出設(shè)備內(nèi)存指針列表 [output_size]) # 輸出大小列表執(zhí)行完成后把輸出數(shù)據(jù)拷貝回主機(jī)內(nèi)存output_data_host, ret acl.rt.malloc_host(output_size) ret acl.rt.memcpy(output_data_host, output_size, output_data, output_size, acl.aclrt_memcpy_kind.aclrt_memcpy_kind_device_to_host)這里要提醒一個(gè)我踩過的坑輸出Tensor在設(shè)備內(nèi)存里是連續(xù)排列的但不是所有模型輸出順序都跟你想的一樣。一定要用前面get_desc拿到的輸出shape信息去解析數(shù)據(jù)別想當(dāng)然地認(rèn)為第一個(gè)輸出就是坐標(biāo)。我遇到過YOLOv8轉(zhuǎn)出來的ONNX輸出順序和YOLOv5不一樣結(jié)果解析錯(cuò)亂畫出來的框五花八門。4.3 輸出解析與后處理輸出數(shù)據(jù)拷回主機(jī)內(nèi)存后就進(jìn)入CPU后處理階段。YOLOv5的原始輸出是[batch, 25200, 85]的Tensor其中25200是三個(gè)尺度80x80、40x40、20x20的anchor總數(shù)85是[cx, cy, w, h, obj_conf, class1_conf, class2_conf, ...]。而YOLOv8的輸出結(jié)構(gòu)稍有不同它用的是解耦頭shape通常是[batch, 84, 8400]你需要先做一次轉(zhuǎn)置才能按檢測(cè)框的方式解析。后處理的任務(wù)包括解碼把模型的原始輸出轉(zhuǎn)成檢測(cè)框坐標(biāo)和置信度。置信度過濾低于閾值的框直接丟棄。NMS對(duì)重疊的框做非極大值抑制。坐標(biāo)映射把640x640坐標(biāo)系映射回原始圖像的坐標(biāo)系。這部分我用純Python實(shí)現(xiàn)雖然效率比不上C但邏輯清晰方便調(diào)參。等確認(rèn)模型輸出正確、NMS閾值合理后再把這部分代碼改成C或者用numpy向量化加速。注意NMS的閾值conf_thres和iou_thres直接影響檢測(cè)效果。我經(jīng)驗(yàn)上建議置信度閾值設(shè)在0.25左右IOU閾值設(shè)在0.45左右這是YOLOv5倉(cāng)庫(kù)的默認(rèn)配置在實(shí)際場(chǎng)景里平衡得比較好。如果誤檢多就調(diào)高置信度閾值如果漏檢多就調(diào)低。5. 性能調(diào)優(yōu)與踩坑實(shí)錄5.1 三個(gè)直接影響吞吐量的配置模型在Atlas上跑通只是第一步真正讓性能飛起來還得靠調(diào)優(yōu)。我實(shí)際調(diào)優(yōu)后發(fā)現(xiàn)下面這三個(gè)配置對(duì)吞吐量的影響最大第一Batch Size。ATC轉(zhuǎn)換時(shí)可以把--input_shape設(shè)成images:8,3,640,640一次推理同時(shí)處理8張圖。Batch越大NPU的利用率越高8路視頻并發(fā)推理時(shí)Batch8通常是性價(jià)比最高的選擇。但要注意Batch太大內(nèi)存會(huì)爆24G內(nèi)存在Batch16時(shí)建議先估算模型大小再?zèng)Q定。第二Stream與異步推理。acl.mdl.execute_async配合多Stream可以把“數(shù)據(jù)拷貝”和“NPU計(jì)算”重疊起來。我的經(jīng)驗(yàn)是先開2~4個(gè)Stream每個(gè)Stream內(nèi)循環(huán)推理即在一個(gè)Stream里前一個(gè)batch還在NPU上算GPU已經(jīng)可以拷貝下一個(gè)batch的數(shù)據(jù)了。這個(gè)流水線設(shè)計(jì)能讓卡一直處在“忙”的狀態(tài)而不是等數(shù)據(jù)拷完了才開始計(jì)算。第三AIPP盡量承接預(yù)處理。我在前面反復(fù)強(qiáng)調(diào)AIPP是因?yàn)閷?shí)測(cè)下來它的收益非常明顯。YOLO推理一張圖CPU預(yù)處理耗時(shí)約2~3毫秒而AIPP把resize和歸一化下沉到NPU后這部分時(shí)間幾乎可以忽略。如果你的部署場(chǎng)景是實(shí)時(shí)視頻流AIPP是必須打開的功能不然你的CPU會(huì)先被預(yù)處理拖垮。第四補(bǔ)充輸出內(nèi)存復(fù)用。不要頻繁申請(qǐng)釋放設(shè)備內(nèi)存。我的做法是在程序啟動(dòng)時(shí)一次性申請(qǐng)好輸入輸出設(shè)備內(nèi)存整個(gè)生命周期里反復(fù)復(fù)用。內(nèi)存分配釋放是很貴的操作尤其在4K視頻流場(chǎng)景下申請(qǐng)釋放頻率一高性能立刻掉下去。5.2 常見報(bào)錯(cuò)和排查思路我在部署過程中遇到不少問題挑幾個(gè)有代表性的整理成表格給后來的人一個(gè)排查方向現(xiàn)象可能原因排查與解決辦法acl.init返回非0驅(qū)動(dòng)未安裝或CANN環(huán)境變量未配置檢查npu-smi info確認(rèn)/usr/local/Ascend路徑在當(dāng)前環(huán)境變量中acl.mdl.load_from_file報(bào)文件不存在OM模型路徑錯(cuò)誤或模型未轉(zhuǎn)換成功確認(rèn)OM路徑用ls檢查文件重新跑ATC轉(zhuǎn)換推理結(jié)果全零或全錯(cuò)輸入數(shù)據(jù)方向拷貝錯(cuò)誤預(yù)處理與AIPP不一致檢查memcpy方向核對(duì)AIPP的input_format是否與輸入圖像一致推理時(shí)報(bào)內(nèi)存不足設(shè)備內(nèi)存申請(qǐng)過大batch過大減小batch用acl.rt.mem_info查看設(shè)備剩余內(nèi)存異步推理結(jié)果不刷新未調(diào)用acl.rt.synchronize_stream在execute_async后調(diào)用acl.rt.synchronize_stream(stream)同步ATC轉(zhuǎn)換報(bào)Unsupported opONNX中的算子超出支持范圍回源頭修改模型優(yōu)化ONNX圖適當(dāng)升級(jí)CANN版本memcpy數(shù)據(jù)錯(cuò)位輸出shape判斷錯(cuò)誤用acl.mdl.get_desc打印所有輸入輸出信息對(duì)比實(shí)際數(shù)據(jù)維度另外排查問題是還有一個(gè)經(jīng)驗(yàn)想分享CANN的日志系統(tǒng)默認(rèn)是關(guān)閉的但出問題時(shí)你真的需要它。在運(yùn)行推理程序前可以先設(shè)置環(huán)境變量ASCEND_GLOBAL_LOG_LEVEL1開啟info級(jí)日志ASCEND_SLOG_PRINT_TO_STDOUT1把日志打到終端。這樣能很直觀地看到ACL初始化、模型加載、每層推理的耗時(shí)和可能的報(bào)錯(cuò)比瞎猜高效得多。調(diào)完后再把日志級(jí)別設(shè)回去因?yàn)槿罩颈旧硪矔?huì)拖慢推理速度。還有一個(gè)小技巧用npu-smi info監(jiān)控卡的溫度和利用率。如果利用率一直上不去而CPU占用很高大概率是預(yù)處理或后處理在拖后腿如果利用率很高但吞吐量上不去可能是batch太小或者stream數(shù)量不夠。性能調(diào)優(yōu)本質(zhì)上就是在找系統(tǒng)的“瓶頸點(diǎn)”找到瓶頸再針對(duì)性優(yōu)化。最后的最后我再分享一點(diǎn)個(gè)人體會(huì)在Atlas上部署YOLO最容易被低估的其實(shí)是模型轉(zhuǎn)換這一步。很多人覺得PyTorch模型能跑就萬事大吉結(jié)果卡在ATC轉(zhuǎn)換報(bào)錯(cuò)上反反復(fù)復(fù)修改模型結(jié)構(gòu)、算子版本反而花掉整個(gè)項(xiàng)目一半的時(shí)間。我的建議是轉(zhuǎn)換之前先小規(guī)模驗(yàn)證ONNX的算子兼容性把能簡(jiǎn)化的結(jié)構(gòu)在導(dǎo)出階段就簡(jiǎn)化掉不要在轉(zhuǎn)換報(bào)錯(cuò)后再回頭改模型那樣效率太低了。Atlas這套工具鏈確實(shí)有它的學(xué)習(xí)門檻但一旦把環(huán)境配順、把鏈路跑通它的穩(wěn)定性、功耗和成本優(yōu)勢(shì)會(huì)非常突出。希望這篇文章能讓你少走一些彎路。