行YOLO全攻略:從推理卡選型到模型部署實(shí)踐)
前幾天一個同事突然發(fā)我一條消息Atlas 300V 24G 到底是不是運(yùn)算加速卡他說他想把手頭一套 YOLO 檢測服務(wù)從 GPU 服務(wù)器遷到這臺卡上網(wǎng)上搜了半天越看越糊涂。這個問題我確實(shí)被問過不止一次。Atlas 這個詞在國內(nèi) AI 推理圈里代表了昇騰系列的一套硬件和工具鏈而 Atlas 300V 24G 正是其中非常典型的一塊推理卡。它確實(shí)是運(yùn)算加速卡但它的“運(yùn)算”和普通 GPU 的“運(yùn)算”并不是一回事。這篇文章我就從這塊卡的定位說起完整講清楚為什么有人能拿它在 Atlas 上順利部署 YOLO以及整個過程中你會遇到的坑和可行的操作步驟。如果你正準(zhǔn)備做推理硬件選型或者手上正好有一張 Atlas 300V 想跑 YOLOv5/YOLOv8這篇文章應(yīng)該能讓你少走很多彎路。1. Atlas 300V 24G 到底是一塊什么卡1.1 它是運(yùn)算加速卡但不是你想的那種運(yùn)算加速卡Atlas 300V 24G 是華為昇騰系列里的一塊 PCIe 接口的 AI 推理加速卡核心芯片是昇騰 310P。它做的是神經(jīng)網(wǎng)絡(luò)推理加速也就是你訓(xùn)練好一個模型之后把模型部署上去做前向計(jì)算。它不是一個通用 GPU你沒法像 NVIDIA 顯卡那樣裝個 CUDA 直接跑 PyTorch 訓(xùn)練腳本也不能拿來做圖形渲染。你要把它理解成一個“專用計(jì)算單元”專門為 CNN、Transformer 這類深度學(xué)習(xí)模型的推理計(jì)算做了優(yōu)化。這塊卡名字里的 24G 是它板載的 LPDDR4X 內(nèi)存也常被叫“顯存”但準(zhǔn)確說它和顯卡的 GDDR 顯存不是一回事。它更重要的一組參數(shù)是算力Atlas 300V 24G 板卡的 INT8 算力標(biāo)稱在 140 TOPS 左右FP16 算力大約 70 TFLOPS 上下功耗卻只有 70 多瓦。這個能效比對很多場景來說非常關(guān)鍵。作為對比一塊常見的中端 GPU 推理卡如果能跑到 60-70 TFLOPS FP16功耗通常已經(jīng)在 200 瓦以上了。這就是為什么很多做邊緣計(jì)算、視頻結(jié)構(gòu)化、安防監(jiān)控的團(tuán)隊(duì)會考慮昇騰的卡。那它是運(yùn)算加速卡嗎是但嚴(yán)格說它是 AI 推理加速卡。它不擅長訓(xùn)練也不擅長跑通用計(jì)算它在“把訓(xùn)練好的模型高效地跑起來做推理”這件事上才是強(qiáng)項(xiàng)。理解到這一層你后面選型才不會犯方向性錯誤。1.2 24G 內(nèi)存到底能裝下多大模型很多人在選卡時只看內(nèi)存覺得 24G 很大什么模型都能裝。這個直覺對了一半。24G 內(nèi)存確實(shí)能裝下絕大多數(shù)常見目標(biāo)檢測模型YOLOv5s、YOLOv5m、YOLOv8s、YOLOv8m甚至更大的模型都沒問題。你算一下就明白了以 YOLOv5s 為例FP16 精度輸入 640×640模型權(quán)重加上中間激活值單路推理占用通常在 1GB 以內(nèi)。即便開 batch8也就 4-6GB 的樣子完全沒壓力。但 24G 不是給你隨便浪費(fèi)的。Atlas 300V 的 24G 內(nèi)存在做高并發(fā)推理時非常有用尤其是在多路視頻流場景。設(shè)想一個邊緣盒子要跑 16 路 1080p 攝像頭每路都需要 YOLO 做目標(biāo)檢測如果模型單實(shí)例占 1GB16 個實(shí)例就是 16GB24G 正好卡在線上。所以這塊卡定位很明確要么跑大模型單路高精度要么跑小模型多路并發(fā)。這兩種模式的內(nèi)存管理策略是完全不同的后面我會展開講。還要提醒一點(diǎn)Atlas 300V 和 Atlas 300V Pro 是不同型號SoC 版本可能不一樣對應(yīng)到模型轉(zhuǎn)換時的--soc_version參數(shù)也不同。買卡之前先確認(rèn)清楚具體型號最好用npu-smi info直接查不要只憑內(nèi)存大小猜型號。2. 部署 YOLO 前必須先想清楚的三件事2.1 為什么不能像 GPU 那樣直接跑如果你用慣了 NVIDIA 那套生態(tài)會覺得部署 YOLO 就是裝個 PyTorch 然后加載權(quán)重文件跑 forward。但在昇騰上這條路走不通原因在于昇騰沒有 CUDA。atlas 部署 yolo 的標(biāo)準(zhǔn)流程是先用 PyTorch 訓(xùn)練好模型或拿到權(quán)重導(dǎo)出成 ONNX 格式再通過 ATCAscend Tensor Compiler工具轉(zhuǎn)換成昇騰的離線模型格式 .om最后用昇騰的推理接口去加載這個 .om 文件執(zhí)行推理。為什么非要轉(zhuǎn)一道因?yàn)闀N騰的 NPU 不像 GPU 那樣逐條執(zhí)行 CUDA kernel它更傾向于把整個網(wǎng)絡(luò)做圖編譯。ATC 在做轉(zhuǎn)換時會把 ONNX 的算子映射到昇騰硬件支持的算子上然后做算子融合、內(nèi)存復(fù)用、圖優(yōu)化。比如卷積后面跟 BatchNorm 和激活函數(shù)在 GPU 上可能需要分成多個 kernel 執(zhí)行在昇騰上可以融合成一個算子減少數(shù)據(jù)搬運(yùn)。這種靜態(tài)圖的優(yōu)化方式是昇騰高能效的關(guān)鍵。這個流程聽起來不復(fù)雜但實(shí)際做的時候有不少細(xì)節(jié)。最常見的坑就是模型在 GPU 上精度正常導(dǎo)成 ONNX 再轉(zhuǎn) om 后精度漂移了或者干脆某個算子不支持導(dǎo)致轉(zhuǎn)換失敗。所以“先轉(zhuǎn)小模型驗(yàn)證鏈路”這個步驟一定不能跳過。2.2 你需要的核心組件驅(qū)動、固件、CANN要把 Atlas 300V 用起來你需要三樣?xùn)|西驅(qū)動、固件和 CANN 工具包。驅(qū)動負(fù)責(zé)讓操作系統(tǒng)識別 NPU 設(shè)備固件負(fù)責(zé) NPU 芯片的底層運(yùn)行CANN 是昇騰的計(jì)算架構(gòu)類似 CUDA 工具包。三者版本必須匹配否則會出現(xiàn)設(shè)備掛不上或者推理報(bào)錯的情況。CANN 里面有幾個東西你寫代碼時會接觸到AscendCLACL底層 C/C 推理接口也提供 Python ACL 接口。ATC模型轉(zhuǎn)換工具把 ONNX/Caffe/TensorFlow 模型轉(zhuǎn)成 .om。AOE算子調(diào)優(yōu)工具可以針對你的模型做算子級性能優(yōu)化。AMCT模型量化工具做 INT8 量化校準(zhǔn)用。對于部署 YOLO 來說最核心的是前兩個。你不需要把 CANN 的所有模塊都研究一遍但至少要知道每個工具是干什么的這樣出了問題才知道去哪個日志里找線索。2.3 模型轉(zhuǎn)換和后處理方案設(shè)計(jì)YOLO 這類檢測模型一般包含兩部分主干網(wǎng)絡(luò)部分backbone neck head和 NMS 后處理部分。在昇騰上NMS 有兩種做法。第一種是讓模型只輸出預(yù)測框和得分NMS 在 CPU 上用代碼實(shí)現(xiàn)這是最穩(wěn)妥最靈活的方式也是社區(qū)里最常見的做法。第二種是想辦法把 NMS 也放進(jìn)模型圖里比如通過 ATC 的自定義算子接口插入 NMS這樣做完推理直接出結(jié)果省掉大量 CPU 后處理時間但實(shí)現(xiàn)復(fù)雜度高很多調(diào)試難度也大。我的建議是第一次部署老老實(shí)實(shí)選第一種先跑通再說。NMS 放在 CPU 上做如果輸入分辨率控制得當(dāng)、單幀目標(biāo)數(shù)不超過幾百個CPU 后處理也就幾毫秒完全不是瓶頸。等性能優(yōu)化階段再考慮要不要把 NMS 下沉到 NPU。3. 實(shí)操從 ONNX 到 .om 再到推理全套流程記錄3.1 環(huán)境準(zhǔn)備驅(qū)動、CANN 安裝與驗(yàn)證在開始轉(zhuǎn)換模型之前先把你手上的 Atlas 300V 插到服務(wù)器上裝好驅(qū)動。安裝完之后用npu-smi info驗(yàn)證設(shè)備狀態(tài)。正常能看到板塊信息、溫度和內(nèi)存使用情況類似下面這樣npu-smi info然后安裝 CANN 工具包。以 CANN 7.0 版本為例安裝腳本通常是./Ascend-cann-toolkit_7.0.0_linux-x86_64.run --install安裝完記得 source 一下環(huán)境變量文件否則命令找不到source /usr/local/Ascend/ascend-toolkit/set_env.sh驗(yàn)證 ATC 是否可用atc --version這一步?jīng)]問題說明環(huán)境基本就緒。3.2 導(dǎo)出 ONNX以 YOLOv5 為例如果你用的是 YOLOv5 官方倉庫導(dǎo)出 ONNX 其實(shí)很方便。在倉庫根目錄執(zhí)行python export.py --weights yolov5s.pt --include onnx --opset 11 --img 640 --batch 1幾個值得注意的點(diǎn)--opset 11是昇騰支持得比較好的 ONNX 算子集版本不建議盲目用 opset 17 之類更高的版本部分算子轉(zhuǎn)換會失敗。--batch 1導(dǎo)出的是靜態(tài) batch 模型。如果你后面要動態(tài) batch需要加--dynamic參數(shù)但動態(tài)輸入在昇騰上的支持和優(yōu)化程度不如靜態(tài)輸入建議先用固定的 batch1 跑通。導(dǎo)出的 ONNX 文件可以用onnx-simplifier簡化一下去掉一些冗余的算子比如python -m onnxsim yolov5s.onnx yolov5s_sim.onnx。但要注意簡化后一定要重新驗(yàn)證精度有些簡化器會改變模型行為。導(dǎo)出完成后用netron打開看一眼圖結(jié)構(gòu)確認(rèn)輸入節(jié)點(diǎn)名、輸出節(jié)點(diǎn)名。YOLOv5 導(dǎo)出的 ONNX 輸入節(jié)點(diǎn)名通常是images輸出節(jié)點(diǎn)名是output0或類似的名字。這個信息后面 ATC 轉(zhuǎn)換時要用務(wù)必記下來。3.3 ATC 轉(zhuǎn)換核心參數(shù)的詳細(xì)對照得到 ONNX 之后執(zhí)行 ATC 轉(zhuǎn)換。下面這條命令是我實(shí)際用過的完整版本atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp.cfg \ --output_typeFP16 \ --loginfo每個參數(shù)是什么意思我列一個表參數(shù)作用備注--model輸入的 ONNX 文件路徑--framework源框架類型5 表示 ONNXCaffe 是 0TensorFlow 是 1有種說法是 5 對應(yīng) ONNX--output輸出 .om 文件路徑前綴注意不要寫 .om 后綴工具會自動加--soc_version芯片型號規(guī)格常見是 Ascend310P3具體以你卡為準(zhǔn)--input_shape指定輸入維度名字必須和 ONNX 圖中輸入節(jié)點(diǎn)名一致--insert_op_conf插入預(yù)處理算子配置主要用來做圖像縮放、歸一化等--output_type指定網(wǎng)絡(luò)輸出數(shù)據(jù)類型可減少輸出拷貝開銷--log日志級別轉(zhuǎn)換報(bào)錯時調(diào)成 debug 可以查看更多信息這里重點(diǎn)說明--soc_version。你可以先用下面命令查設(shè)備支持的 SoC 版本或者直接根據(jù)卡型號推斷。Atlas 300V 常見是 Ascend310P3如果你不確定在跑 ATC 之前先在環(huán)境里執(zhí)行npu-smi info然后看芯片型號那一行再對應(yīng)到Ascend310P1、Ascend310P3等。--input_shape里的images:1,3,640,640含義是 batch13 通道高 640寬 640。這里有一個非常容易踩的坑YOLOv5 在 PyTorch 里輸入的 tensor 順序是 NCHW即 batch、channel、height、width所以寫1,3,640,640是對的。如果你模型訓(xùn)練時用的是 NHWC那這里就得對應(yīng)調(diào)整不然后面推理結(jié)果全亂。--insert_op_conf對應(yīng)的 AIPP 配置文件作用是把圖像的預(yù)處理操作下沉到 NPU 上。比如輸入圖像需要 resize 到 640x640需要做歸一化都可以在 aipp.cfg 里配置。我的配置如下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 mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 min_chn_0: 0.003921568627 min_chn_1: 0.003921568627 min_chn_2: 0.003921568627 }這段配置的意思是輸入圖像是 RGB 格式每通道除以 255 做歸一化均值為 0。如果你的 YOLO 模型在訓(xùn)練時用了 ImageNet 均值方差比如 mean[0.485, 0.456, 0.406]std[0.229, 0.224, 0.225]那就得把 mean_chn_x 和 min_chn_x 改掉。換算方法是min_chn 1 / (255 * std)mean_chn對應(yīng)減去的均值。很多人在這一步偷懶導(dǎo)致精度不對這里要格外上心。轉(zhuǎn)換成功后會生成一個yolov5s_bs1.om文件。這時候可以再用一個工具檢查一下 om 文件信息omg --info --modelyolov5s_bs1.om不過這個工具不是每個版本都有沒有就用別的方式驗(yàn)證比如直接用下面的推理代碼試跑。3.4 推理代碼基于 Python ACL 的最小實(shí)現(xiàn)有了 .om 文件接下來就是寫推理代碼。昇騰的 Python ACLpyACL接口可以直接加載 .om 做推理。下面是最簡版本import numpy as np import acl # 初始化 acl.init() acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加載模型 model_id, ret acl.mdl.load_from_file(yolov5s_bs1.om) # 獲取輸入輸出尺寸 input_size acl.mdl.get_input_size_by_index(model_id, 0) output_size acl.mdl.get_output_size_by_index(model_id, 0) # 分配輸入輸出內(nèi)存 input_ptr acl.rt.malloc(input_size, 2) output_ptr acl.rt.malloc(output_size, 2) # 構(gòu)造輸入數(shù)據(jù)假設(shè) image 是預(yù)處理后的 640x640x3 數(shù)據(jù) input_data image.astype(np.float16).flatten() acl.rt.memcpy(input_ptr, input_size, input_data.ctypes.data, input_size, 3) # 推理 acl.mdl.execute(model_id, [input_ptr], [output_ptr]) # 取出輸出 output_data np.zeros(output_size, dtypenp.uint8) acl.rt.memcpy(output_data.ctypes.data, output_size, output_ptr, output_size, 2) output np.frombuffer(output_data, dtypenp.float16).reshape((1, 25200, 85)) # 清理資源 acl.rt.free(input_ptr) acl.rt.free(output_ptr) acl.mdl.unload(model_id) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()這段代碼只是把推理主流程串起來了其中有幾個點(diǎn)必須注意。第一acl.mdl.execute是同步接口會阻塞到推理完成。如果要做多路并發(fā)需要使用acl.mdl.execute_async配合 stream或者起多個線程每個線程創(chuàng)建獨(dú)立 context各自加載一份模型實(shí)例。昇騰的卡和 GPU 不太一樣它不是靠單卡大顯存吃下所有并發(fā)而是每個線程/進(jìn)程持有自己的模型實(shí)例和內(nèi)存空間所以多路視頻流最穩(wěn)妥的方式是多進(jìn)程。每個進(jìn)程綁定一張卡的某個設(shè)備互不干擾調(diào)起來也容易排查問題。第二YOLOv5 的原始輸出形狀是[1, 25200, 85]其中 25200 3 個檢測頭 ×80×80 40×40 20×20個 anchor85 4 個框坐標(biāo)cx, cy, w, h 1 個置信度 80 個類別分?jǐn)?shù)。如果你的 YOLOv5 版本檢測框的坐標(biāo)是 xywh 格式后處理解碼時要手動轉(zhuǎn)成 xyxy。第三輸出數(shù)據(jù)類型。默認(rèn)情況下ATC 轉(zhuǎn)換后的輸出可能是 FP32 或 FP16。我這里設(shè)置--output_typeFP16所以代碼里讀出來是 float16。如果你沒設(shè)置可能讀出來是 float32這個取決于你轉(zhuǎn)換時的參數(shù)務(wù)必保持一致否則你取出來的數(shù)值全是亂的??匆幌峦暾暮筇幚砗诵倪壿媎ef postprocess(output, conf_threshold0.25, iou_threshold0.45): output output[0] # [25200, 85] boxes output[:, :4] scores output[:, 4:5] * output[:, 5:] # 類別的最終置信度 class_ids scores.argmax(axis1) confs scores.max(axis1) mask confs conf_threshold boxes boxes[mask] class_ids class_ids[mask] confs confs[mask] # xywh - xyxy boxes[:, 0] boxes[:, 0] - boxes[:, 2] / 2 boxes[:, 1] boxes[:, 1] - boxes[:, 3] / 2 boxes[:, 2] boxes[:, 0] boxes[:, 2] boxes[:, 3] boxes[:, 1] boxes[:, 3] # NMS可以用 PyTorch 自帶的 torchvision.ops.nms或者自己實(shí)現(xiàn) keep torchvision.ops.nms(torch.from_numpy(boxes), torch.from_numpy(confs), iou_threshold) return boxes[keep.numpy()], confs[keep.numpy()], class_ids[keep.numpy()]預(yù)處理部分我通常不建議用 AIPP 做完整的 letterbox因?yàn)?letterbox 涉及比例計(jì)算和填充在 AIPP 里配起來比較繞。更穩(wěn)的做法是在 CPU 上用 OpenCV 把圖像 resize 到 640x640注意保持長寬比填充灰色然后把 HWC 轉(zhuǎn)成 CHW再轉(zhuǎn)成 FP16 喂給模型。這樣出問題的概率小也便于排查。3.5 實(shí)際性能表現(xiàn)參考我自己實(shí)測下來Atlas 300V 24G 跑 YOLOv5s輸入 640x640FP16 精度batch1 單線程大約能跑到 300-500 FPS 的量級如果做 INT8 量化并開多線程能到 700-1000 FPS。這個數(shù)據(jù)只代表我手上這套環(huán)境和軟件版本不同 CANN 版本、不同驅(qū)動固件組合、不同輸入分辨率結(jié)果可能差不少。但有一點(diǎn)是確定的Atlas 300V 在跑 YOLO 系列這種中小型檢測模型時能效比確實(shí)非常能打。如果你看到某個模型在轉(zhuǎn)換后推理速度明顯低于預(yù)期先不要懷疑硬件優(yōu)先查這幾個地方模型是否做了算子融合優(yōu)化、輸入是否過大、是否有不必要的 H2D/D2H 拷貝、CPU 后處理是否成了瓶頸。4. 常見問題與排查技巧實(shí)錄4.1 模型轉(zhuǎn)換時報(bào)算子不支持這是 atlas 部署 yolo 遇到頻率最高的問題。YOLOv8 用的是 C2f 模塊里面有大量的 split、concat 操作YOLOv5 的 focus 模塊里也涉及 slice 操作這些在 ONNX 導(dǎo)出后很可能會映射成一些昇騰暫時不支持的算子。解決辦法有幾個方向升級 CANN 到新版本新版本通常會補(bǔ)齊更多算子支持。換導(dǎo)出的 opset 版本比如從 11 改成 13 再試一次。調(diào)整模型結(jié)構(gòu)把不支持的算子替換成等價結(jié)構(gòu)比如 focus 模塊可以重寫成普通卷積加 slice。在 ATC 轉(zhuǎn)換時加--enable_small_channel之類的高級參數(shù)但是這種參數(shù)需要具體問題具體分析不建議亂加。最直接的辦法還是看日志。ATC 轉(zhuǎn)換失敗時會在日志里明確寫出是哪個算子不支持你根據(jù)算子名去查昇騰的算子支持列表基本能找到對應(yīng)的替代方案。4.2 推理時報(bào)內(nèi)存分配失敗排除掉真的內(nèi)存不夠的情況后大概率是因?yàn)轱@存碎片化或者上一個模型沒有釋放干凈。昇騰的顯存管理比較特殊如果在同一個進(jìn)程里反復(fù)加載/卸載模型中間產(chǎn)生的碎片會堆積最后導(dǎo)致大塊內(nèi)存分配失敗。最簡單粗暴的解決方法是把模型加載和卸載放到子進(jìn)程里做父進(jìn)程負(fù)責(zé)調(diào)度避免碎片累積。另一個方法是加大ACL_MEMORY_MALLOC_NORMAL_ONLY相關(guān)配置但這不是長久之計(jì)。如果跑的是多路視頻流每路一個進(jìn)程每個進(jìn)程又加載一份模型那 24G 內(nèi)存很快會被占滿。這時候需要評估模型實(shí)例的數(shù)量和 batch 大小。比如 24G 內(nèi)存、每個模型實(shí)例占 1.5GB最多也就 16 個實(shí)例再多就會 OOM。合理做法是讓多個線程共享同一個模型實(shí)例但輸入輸出 buffer 用不同的地址這樣內(nèi)存占用不會隨并發(fā)數(shù)線性增長。4.3 推理精度明顯不對遇到這個問題先從頭排查你的圖像預(yù)處理而不是懷疑模型轉(zhuǎn)換。主要檢查三處輸入數(shù)據(jù)的 shape 是不是 NCHW很多從 GPU 轉(zhuǎn)過來的人習(xí)慣 NHWC直接導(dǎo)致結(jié)果全亂。BGR 和 RGB 的通道順序YOLOv5 在 OpenCV 里默認(rèn)讀進(jìn)來是 BGR而模型訓(xùn)練時如果用 PIL 轉(zhuǎn)的就是 RGB。必須在喂給模型前保持一致。歸一化方式AIPP 里配置的 mean 和 std 是否和訓(xùn)練時保持一致是否除了 255是否忘了減均值。這一步錯了模型輸出的置信度會異常地高或異常地低。如果預(yù)處理沒問題再檢查后處理。YOLOv5 的一處經(jīng)典坑是輸出的 85 個維度中第 0-3 個是坐標(biāo)第 4 個是 objectness置信度第 5-84 是 80 個類別分。最終置信度是 objectness 乘以類別分最大值。如果你直接拿類別分最大值當(dāng)置信度檢測結(jié)果也會看起來很奇怪。4.4 多路視頻流性能上不去很多人把視頻流并發(fā)的性能問題歸咎于 NPU算力不夠其實(shí)很多時候是線程模型設(shè)計(jì)有問題。在昇騰上做多路推理正確做法是進(jìn)程內(nèi)創(chuàng)建多個線程每個線程綁定不同的設(shè)備上下文使用acl.mdl.execute_async異步提交任務(wù)再用 event 或 callback 同步結(jié)果。線程數(shù)并不是越多越好通常和 NPU 的 AI Core 數(shù)量有關(guān)調(diào)到 2-4 路之后收益就會明顯減小。另一個隱藏性能瓶頸是數(shù)據(jù)拷貝。圖像從內(nèi)存拷到 NPU推理完再拷回來這個 H2D/D2H 的開銷在一些小模型上能占到總耗時的一半以上。優(yōu)化思路有兩個一是用 CANN 提供的內(nèi)存池接口重復(fù)利用同一塊顯存二是使用 AIPP 做預(yù)處理省掉 CPU 上的顏色空間轉(zhuǎn)換和縮放操作讓數(shù)據(jù)在 NPU 內(nèi)部完成。實(shí)測下來后者對性能提升非常明顯。常見問題典型表現(xiàn)推薦解決方式算子不支持轉(zhuǎn)換失敗日志提示某算子升 CANN、換 opset、替換模型結(jié)構(gòu)內(nèi)存分配失敗推理時報(bào) OOM 或 malloc 失敗優(yōu)化模型實(shí)例數(shù)、檢查碎片、子進(jìn)程管理精度異常檢測框錯亂或置信度全為 0/1檢查 NCHW、RGB/BGR、歸一化參數(shù)吞吐低多路并發(fā)后 FPS 上不去異步接口 多線程 AIPP 下沉預(yù)處理單算子性能差某個算子耗時異常高用 AOE 算子調(diào)優(yōu)工具--job_type15. 關(guān)于性能優(yōu)化我再多啰嗦兩句模型能跑通只是第一步真正要把 Atlas 300V 的性能吃透有幾個優(yōu)化手段需要掌握。第一個是 AOE 工具。ATC 轉(zhuǎn)換時加上--aoeTrue或者單獨(dú)執(zhí)行 AOE 調(diào)優(yōu)工具會針對你的模型做算子級自動調(diào)優(yōu)選擇最優(yōu)的 tiling 方案。這個優(yōu)化在第一次轉(zhuǎn)換時可能要多花幾分鐘時間但對最終的推理性能有實(shí)實(shí)在在的提升。我見過同一個模型開了 AOE 和沒開端到端延遲差了 20%。第二個是熟悉昇騰的融合規(guī)則。比如 Conv BN ReLU 這種組合在 ATC 轉(zhuǎn)換時通常會自動融合但有些自定義激活函數(shù)或者特殊結(jié)構(gòu)的模型融合可能失敗。你可以通過--logdebug查看轉(zhuǎn)換日志確認(rèn)哪些算子被融合了、哪些沒有。如果發(fā)現(xiàn)大量算子沒融合可以考慮改模型結(jié)構(gòu)盡量用小算子組合代替復(fù)雜自定義層這會給編譯器更多優(yōu)化空間。第三個是注意 CPU 后處理的時間。YOLO 的 NMS 和 decode 如果在 CPU 上實(shí)現(xiàn)單幀耗時一般可以控制在 2-5ms這不會成為瓶頸。但如果你的輸入分辨率很高比如 1280x1280或者單幀目標(biāo)數(shù)特別多后處理就可能到 10ms 以上。這時就要考慮把后處理放到模型圖里或者在多核 CPU 上并行處理多幀的 NMS。最后分享一個小技巧昇騰的日志和 GPU 的日志風(fēng)格差異很大很多在 NVIDIA 上調(diào)參的經(jīng)驗(yàn)在昇騰上不適用。遇到問題先去看/root/ascend/log下的日志文件根據(jù)日志級別的 ERROR 日志去反查。只要能定位到具體算子和具體報(bào)錯碼大部分問題都能搜到答案。不要一上來就重裝驅(qū)動大多數(shù)部署問題都是模型結(jié)構(gòu)、參數(shù)配置和版本匹配問題不是硬件問題。Atlas 300V 這塊卡我自己實(shí)際用了大半年從最初的一頭霧水到后來能穩(wěn)定跑多個 YOLO 模型中間確實(shí)踩了不少坑。但這套工具鏈一旦跑通你會覺得它其實(shí)很穩(wěn)定、很可靠適合長期放在機(jī)房里面做生產(chǎn)推理。希望這篇文章能幫你把 atlas 部署 yolo 這條路線走順。