:從環(huán)境配置到推理調(diào)優(yōu))
最近后臺和私信里被問得最多的一件事就是Atlas 300V Pro 24G這塊卡到底怎么樣網(wǎng)上炒得火熱有人說是運算加速卡有人說是智商稅還有人問能不能拿來跑YOLO。說實話這塊卡我前后折騰了小一個月踩了不少坑才把YOLOv8完整部署上去。今天不整虛的直接把整個流程、參數(shù)、遇到的坑全部攤開講講清楚。先說結(jié)論Atlas 300V Pro 24G確實是一塊推理加速卡設(shè)計目標(biāo)就是做AI模型推理不是拿來跑訓(xùn)練的。它最典型的落地場景就是YOLO系列模型在邊緣服務(wù)器或私有化環(huán)境里的部署這也是我這次折騰的核心內(nèi)容。如果你手里正好有這塊卡或者正準(zhǔn)備采購這篇內(nèi)容應(yīng)該能幫你省下不少時間。1. Atlas 300V Pro 24G到底是什么和普通顯卡差在哪1.1 一張圖看懂這塊卡的定位很多人第一次拿到Atlas 300V Pro會下意識拿它跟NVIDIA的RTX 4090、A10這種顯卡比這是個誤區(qū)。Atlas 300V Pro屬于華為昇騰推理卡產(chǎn)品線芯片用的是昇騰310P整卡顯存24GB但它的定位是“推理加速卡”不是“通用計算卡”。它和普通顯卡的核心差別在于指令集和架構(gòu)不同昇騰310P用的是達芬奇架構(gòu)里面的AI Core是專門為矩陣運算設(shè)計的對卷積、池化、全連接這類算子做了深度定制但通用并行計算能力比NVIDIA的CUDA生態(tài)弱很多。軟件棧不同NVIDIA走CUDA/cuDNN/TensorRT這條線昇騰走的是CANNCompute Architecture for Neural Networks這條線算子庫叫AscendCL模型轉(zhuǎn)換工具叫ATC。精度策略不同昇騰卡對INT8量化支持得非常激進很多模型在INT8下的吞吐量能翻好幾倍FP16和FP32的表現(xiàn)反而不是它的強項。所以你要拿它跑渲染、跑Matlab、跑CUDA寫的科學(xué)計算那基本沒法用。但如果你要跑YOLOv8、YOLOv5、ResNet50這些CV模型的批量推理尤其是在國產(chǎn)化環(huán)境里它反而是個很穩(wěn)的選擇。1.2 24GB顯存意味著什么Atlas 300V Pro 24G的24GB是LPDDR4X帶寬雖然比不上GDDR6或者HBM但勝在容量大。對大模型推理來說容量比帶寬有時候更關(guān)鍵。我實測下來24GB顯存大概能干這些事跑YOLOv8s模型batch size開到16以上沒有任何壓力。同時加載兩三個不同版本的檢測模型進行多模型并行推理每個模型單獨分配一個上下文。跑一些輕量化的分割模型比如SegFormer-B0、PP-LiteSeg也是綽綽有余。甚至可以跑一些參數(shù)量在10億以內(nèi)的Transformer模型比如BERT-base、RoBERTa-base的推理。當(dāng)然如果你要跑Llama-7B這種百億參數(shù)的大語言模型24GB還是會吃緊量化到INT8勉強能塞進去但推理速度就不太樂觀了。1.3 什么場景真正適合選它從我自己的項目經(jīng)驗看這塊卡最適合以下幾個場景第一個是邊緣AI服務(wù)器。比如園區(qū)安防、工業(yè)質(zhì)檢、智慧交通這類場景一臺2U邊緣服務(wù)器插上兩到四張Atlas 300V Pro就可以把幾十路攝像頭的視頻流全接進來實時做目標(biāo)檢測和結(jié)構(gòu)化分析。功耗低不用改機房供電比插8張RTX 4090省心太多。第二個是國產(chǎn)化替代需求。很多政企項目明確要求核心組件必須用國產(chǎn)芯片昇騰是目前生態(tài)最成熟的國產(chǎn)AI芯片之一CANN工具鏈和MindSpore框架這幾年迭代很快已經(jīng)在大量項目中落地。第三個是私有化模型服務(wù)。比如企業(yè)內(nèi)部的OCR識別服務(wù)、缺陷檢測平臺、商品識別系統(tǒng)輸入是固定格式的圖片或者視頻幀輸出是檢測框和類別。這種固定場景用推理卡非常劃算因為推理卡的采購成本和功耗都比同級別的GPU低不少。2. 部署YOLO前的環(huán)境準(zhǔn)備沒你想的那么輕松2.1 硬件環(huán)境和系統(tǒng)要求先說硬件Atlas 300V Pro是標(biāo)準(zhǔn)的PCIe全高全長短卡單槽位被動散熱所以服務(wù)器必須有風(fēng)道或者機箱風(fēng)扇對著吹。我一開始插在一臺塔式工作站里風(fēng)道不對卡很快就跑到85度以上后來換到機架式服務(wù)器里才穩(wěn)定在60度左右。系統(tǒng)方面官方支持Ubuntu 18.04/20.04、CentOS 7.6/8.2等但我的建議是直接用Ubuntu 20.04 x86_64兼容性最好。ARM版本不建議自己折騰除非你用的是華為自己的泰山服務(wù)器否則在第三方ARM主板上裝CANN會有一堆坑。安裝前確認(rèn)三件事lspci能看到卡lspci | grep -i ascendBIOS里打開Above 4G Decoding和Resizable BAR否則DMA可能出問題內(nèi)核版本在4.18以上推薦5.42.2 驅(qū)動、固件、CANN的版本匹配這是最容易踩坑的地方。昇騰的軟件棧分三部分NPU驅(qū)動、NPU固件、CANN工具包。三者之間不是隨便亂搭配的版本必須嚴(yán)格對應(yīng)。我這次用的版本組合是驅(qū)動Ascend-hdk-310p-npu-driver_23.0.rc3_linux-x86_64.run固件Ascend-hdk-310p-npu-firmware_23.0.rc3_linux-x86_64.runCANNAscend-cann-toolkit_7.0.0_linux-x86_64.run推理引擎Ascend-cann-nnrt_7.0.0_linux-x86_64.run安裝順序不能亂先驅(qū)動再固件最后CANN。驅(qū)動和固件的下載地址在昇騰社區(qū)需要注冊企業(yè)賬號個人開發(fā)者也能注冊。CANN在昇騰社區(qū)直接下載。安裝驅(qū)動和固件chmod x Ascend-hdk-310p-npu-driver_23.0.rc3_linux-x86_64.run ./Ascend-hdk-310p-npu-driver_23.0.rc3_linux-x86_64.run --full chmod x Ascend-hdk-310p-npu-firmware_23.0.rc3_linux-x86_64.run ./Ascend-hdk-310p-npu-firmware_23.0.rc3_linux-x86_64.run --full裝完后重啟執(zhí)行npu-smi info如果能顯示芯片信息說明驅(qū)動和固件OK。安裝CANNchmod x Ascend-cann-toolkit_7.0.0_linux-x86_64.run ./Ascend-cann-toolkit_7.0.0_linux-x86_64.run --install --install-for-all source /usr/local/Ascend/ascend-toolkit/set_env.sh注意set_env.sh每次開新終端都要source一次或者直接寫進.bashrc。2.3 版本不匹配的癥狀和處理方式版本不匹配的典型癥狀就是安裝時報錯、npu-smi能看到卡但初始化失敗、ATC轉(zhuǎn)換模型時直接報CANN Internal Error。我遇到過一次比較詭異的問題驅(qū)動和固件都是23.0.rc3但CANN是6.3.rc2結(jié)果ATC轉(zhuǎn)換時老是在Graph is not ready這個位置卡住。后來查了昇騰社區(qū)的官方兼容性表才發(fā)現(xiàn)CANN 6.3.rc2只支持到22.0.rc1的驅(qū)動跨版本太大驅(qū)動和CANN之間的通信協(xié)議對不上。重新刷成7.0.0之后問題就消失了。注意昇騰的版本兼容性表是動態(tài)更新的安裝前一定要去昇騰社區(qū)查一下當(dāng)前的“驅(qū)動固件與CANN版本配套表”不要憑經(jīng)驗亂配。2.4 換源和服務(wù)配置的小坑昇騰的pip源是獨立的不在PyPI上。裝Python依賴的時候需要指定昇騰源pip3 install --upgrade pip pip3 install attrs numpy decorator sympy cffi pyyaml pathlib2 psutil protobuf scipy requests absl-py pip3 install --upgrade --user astroid另外CANN自帶的Python接口在/usr/local/Ascend/ascend-toolkit/latest/pyACL/python/site-packages/acl下如果Python import acl失敗多半是PYTHONPATH沒設(shè)置。建議把以下內(nèi)容加到.bashrcexport ASCEND_HOME/usr/local/Ascend/ascend-toolkit/latest export LD_LIBRARY_PATH$ASCEND_HOME/lib64:$ASCEND_HOME/lib64/plugin/opskernel:$ASCEND_HOME/lib64/plugin/nnengine:$LD_LIBRARY_PATH export PYTHONPATH$ASCEND_HOME/pyACL/python/site-packages:$PYTHONPATH3. YOLOv8模型轉(zhuǎn)換全流程ATC是核心3.1 導(dǎo)出ONNX模型雖然昇騰也支持直接加載MindSpore和Caffe的模型但現(xiàn)階段最順的路徑還是走PyTorch → ONNX → OM昇騰離線模型。我用的是YOLOv8s預(yù)訓(xùn)練權(quán)重在COCO上跑過。先從ultralytics導(dǎo)出ONNXyolo export modelyolov8s.pt formatonnx opset12 imgsz640這里有兩個關(guān)鍵點opset版本必須用12或13不要用更高的版本ATC對高版本ONNX算子支持不全。imgsz建議固定成640如果你是訓(xùn)練時用608或672這種非標(biāo)準(zhǔn)尺寸導(dǎo)出時直接改為對應(yīng)尺寸即可但推理輸入要和它一致。導(dǎo)出后用onnxsim簡化一下反正不費事python3 -m onnxsim yolov8s.onnx yolov8s_sim.onnx簡化的作用是去掉一些冗余的Identity節(jié)點和Shape節(jié)點ATC轉(zhuǎn)換時能少報幾個Unsupported Operator。3.2 ATC轉(zhuǎn)換OM模型參數(shù)詳解ATC轉(zhuǎn)換是整條鏈路里最需要耐心的環(huán)節(jié)。命令我貼出來后面逐個參數(shù)說source /usr/local/Ascend/ascend-toolkit/set_env.sh atc --modelyolov8s_sim.onnx \ --framework5 \ --outputyolov8s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --precision_modeforce_fp16 \ --output_typeFP32 \ --optypelist_for_implmodeSigmoid \ --op_select_implmodehigh_performance每個參數(shù)說明--framework55代表ONNX。--output輸出文件名不寫后綴自動生成.om。--input_shapeONNX模型的輸入節(jié)點名是images維度是(batch, channel, height, width)。如果你導(dǎo)出的ONNX輸入節(jié)點叫別的名字用netron查看。--soc_version必須填A(yù)scend310P3或者Ascend310P4具體看你的卡是哪個芯片版本用npu-smi info能看到。填錯了會報錯提示當(dāng)前的SoC版本不匹配。--insert_op_confAIPP配置文件路徑這個后面單獨說。--precision_modeforce_fp16強制用FP16計算。昇騰的FP16性能遠(yuǎn)好于FP32YOLOv8的精度損失可以忽略。--output_typeFP32輸出層用FP32避免輸出結(jié)果精度被壓縮導(dǎo)致框坐標(biāo)偏移。--optypelist_for_implmode和--op_select_implmode讓Sigmoid算子走高性能實現(xiàn)對YOLOv8的置信度分支速度有優(yōu)化。如果轉(zhuǎn)換成功會生成yolov8s_bs1.om文件。轉(zhuǎn)換過程大概需要幾分鐘期間CPU占用會比較高注意不要同時開太多任務(wù)。3.3 AIPP配置圖像預(yù)處理交給NPUAIPPAI Preprocessing是CANN里一個非常強大的功能能把圖像縮放、歸一化、通道變換這些操作統(tǒng)統(tǒng)塞進模型里。這樣的話在推理時你直接往NPU塞原始BGR圖像就行不需要在CPU上做復(fù)雜的預(yù)處理省掉一部分CPU開銷。我的aipp.cfgaipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false load_start_pos_h: 0 load_start_pos_w: 0 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 }要點是input_format必須和你的輸入數(shù)據(jù)一致。YOLOv8的預(yù)處理是把圖片縮放到640x640BGR順序像素值歸一化到0-1所以這里寫RGB888_U8其實不太對應(yīng)該用BGR888_U8。這個我踩過坑后面問題列表里再展開。var_reci_chn是歸一化系數(shù)0.003921569就是1/255YOLOv8的歸一化方式就是直接除以255。注意很多顯卡在推理時要RESIZE如果模型是640輸入你ffeeding時應(yīng)該已經(jīng)resize好再傳給NPU。如果想讓NPU自己完成resize需要在AIPP里配置resize參數(shù)但這樣會多一步操作效率上反而不如自己先在CPU上做letterbox再傳進去。3.4 動態(tài)batch和多batch的處理我自己的場景里經(jīng)常要同時處理十幾路視頻流每路視頻的畫面數(shù)量不一樣所以需要支持動態(tài)batch。ATC轉(zhuǎn)換時用dynamic_batch_size參數(shù)atc --modelyolov8s_sim.onnx \ --framework5 \ --outputyolov8s_dynbs \ --input_shapeimages:-1,3,640,640 \ --dynamic_batch_size1,2,4,8,16 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --precision_modeforce_fp16 \ --output_typeFP32注意動態(tài)batch的模型在推理時需要在設(shè)備端動態(tài)設(shè)置batch大小代碼里要做適配稍微麻煩點。如果你的業(yè)務(wù)場景里batch大小基本固定比如總是處理固定4路或8路視頻直接用固定batch的模型就好性能和穩(wěn)定性都更好。4. 用Python實現(xiàn)YOLOv8推理可以先跑起來再優(yōu)化4.1 用pyACL實現(xiàn)推理的完整代碼昇騰的底層推理接口是AscendCLACL可以用C調(diào)也可以用Python調(diào)。我先用Python跑通全流程驗證精度沒問題之后再用C做正式的服務(wù)化部署。首先初始化環(huán)境和設(shè)備import acl import numpy as np import cv2 import time # 初始化ACL ret acl.init() assert ret 0 # 設(shè)置推理設(shè)備0表示第一張卡 ret acl.rt.set_device(0) assert ret 0 # 創(chuàng)建上下文 context, ret acl.rt.create_context(0) assert ret 0 # 加載OM模型 model_path b./yolov8s_bs1.om model_id, ret acl.mdl.load_from_file(model_path) assert ret 0然后準(zhǔn)備輸入輸出。因為模型輸入已經(jīng)包含了AIPP預(yù)處理所以這里只需要把圖像resize到640x640然后轉(zhuǎn)成BGR格式就行def preprocess(image): # image是BGR格式的numpy數(shù)組 img cv2.resize(image, (640, 640)) img img.astype(np.float32) # 注意這里不再做歸一化AIPP已經(jīng)處理了 img img[:, :, ::-1] # BGR轉(zhuǎn)RGB因為AIPP配置的是RGB888_U8 img np.transpose(img, (2, 0, 1)) # HWC轉(zhuǎn)CHW img np.expand_dims(img, axis0) # 增加batch維度 return np.ascontiguousarray(img, dtypenp.uint8)這里有個非常重要的點如果AIPP配了歸一化那你輸入的數(shù)據(jù)類型要用uint8不要再除以255。我一開始沒注意把uint8的圖像直接除255轉(zhuǎn)成float32再送進去結(jié)果檢測結(jié)果全亂了。接著把輸入數(shù)據(jù)拷貝到設(shè)備端# 獲取模型輸入輸出尺寸信息 input_desc acl.mdl.get_input_desc(model_id) output_desc acl.mdl.get_output_desc(model_id) input_size acl.mdl.get_input_size_by_index(model_id, 0) output_size acl.mdl.get_output_size_by_index(model_id, 0) # 創(chuàng)建設(shè)備端輸入輸出buffer input_data np.zeros((input_size,), dtypenp.uint8) output_data np.zeros((output_size,), dtypenp.float32) input_buffer acl.rt.malloc(input_size, 2) output_buffer acl.rt.malloc(output_size, 2) # 把圖像數(shù)據(jù)拷貝到設(shè)備端 input_data preprocess(image) acl.rt.memcpy(input_buffer, input_size, input_data.tobytes(), input_data.nbytes, 1)執(zhí)行推理# 執(zhí)行推理 dim acl.mdl.get_input_dim_index(model_id, 0) acl.mdl.execute_async(model_id, [input_buffer], [output_buffer], None, 0) acl.rt.synchronize(context)推理完成后把輸出拷回主機端然后做后處理。YOLOv8的輸出是一個1x84x8400的tensor84表示4個框坐標(biāo)加80個類別置信度8400是不同特征層的anchor總數(shù)。4.2 后處理坐標(biāo)還原和NMS后處理這塊每個模型的格式大同小異但細(xì)節(jié)上會有差別。YOLOv8的輸出格式和YOLOv5不一樣v8沒有objectness分支所以解碼邏輯要調(diào)整。def postprocess(output_data, orig_shape, conf_thres0.5, iou_thres0.45): predictions output_data.reshape(1, 84, 8400) predictions np.transpose(predictions, (0, 2, 1)) # (1, 8400, 84) boxes predictions[0, :, :4] class_scores predictions[0, :, 4:] class_ids np.argmax(class_scores, axis1) confs np.max(class_scores, axis1) # 置信度過濾 mask confs conf_thres boxes boxes[mask] class_ids class_ids[mask] confs confs[mask] if len(boxes) 0: return [] # xywh轉(zhuǎn)xyxy x1 boxes[:, 0] - boxes[:, 2] / 2 y1 boxes[:, 1] - boxes[:, 3] / 2 x2 boxes[:, 0] boxes[:, 2] / 2 y2 boxes[:, 1] boxes[:, 3] / 2 # 坐標(biāo)縮放回原圖尺寸 scale_w orig_shape[1] / 640 scale_h orig_shape[0] / 640 x1 * scale_w x2 * scale_w y1 * scale_h y2 * scale_h boxes np.stack([x1, y1, x2, y2], axis1) # 按類別做NMS final_boxes [] for cls in np.unique(class_ids): idx class_ids cls cls_boxes boxes[idx] cls_confs confs[idx] keep nms(cls_boxes, cls_confs, iou_thres) for i in keep: final_boxes.append([*cls_boxes[i], cls_confs[i], cls]) return final_boxesNMS可以自己寫一個簡單實現(xiàn)也可以用opencv的cv2.dnn.NMSBoxes但注意cv2.dnn在有些版本里API不兼容建議直接裝一個nms工具庫pip3 install nms或者手寫一個Python實現(xiàn)邏輯也不復(fù)雜二三十行搞定。4.3 實測性能參考我實測下來在單張Atlas 300V Pro 24G上YOLOv8s模型640x640輸入FP16推理batch1的情況下純推理耗時大約12到14毫秒加上圖像解碼和前后處理整體在18到22毫秒左右。如果打開多batchbatch4的時候整體吞吐量能到每秒150幀左右。這個性能是什么水平呢做一個對比RTX 4090跑YOLOv8s的純推理大概是5到7毫秒但4090的功耗是450WAtlas 300V Pro的功耗大約75W。如果按每瓦特的推理幀數(shù)來算Atlas 300V Pro在推理任務(wù)上的能效比反而更高。不過要提醒的是我這里的數(shù)據(jù)是基于CANN 7.0和特定固件版本測的昇騰的軟件迭代對性能影響很大同一個模型在CANN 5.1和CANN 7.0下能差出30%的推理速度。所以如果你入手了這塊卡第一步先把CANN升到最新穩(wěn)定版再測性能。5. 部署過程中的常見問題踩坑實錄5.1 ATC轉(zhuǎn)換時報錯Unsupported Operator這是出現(xiàn)頻率最高的問題。YOLOv8導(dǎo)出ONNX后里面通常會有一些昇騰算子庫暫時不支持的算子最常見的是Einsum、GridSample、Multinomial這幾個。解決辦法有幾種第一種升級CANN版本新版CANN會不斷補齊算子。我之前在CANN 6.3上轉(zhuǎn)換一直報Einsum算子不支持升到7.0之后就自動解決了。第二種如果升級版本還是不支持就得手工改ONNX圖把這些算子的計算邏輯拆成幾個基礎(chǔ)算子的組合。比如Einsum可以拆成MatMul加ReduceSumGridSample可以拆成Resize加Concat。這個工作量有點大但也不是不能做。第三種換模型結(jié)構(gòu)。比如YOLOv8的多尺度檢測頭輸出層有一堆使用split和concat的操作如果老版本ATC處理不好可以試試直接在導(dǎo)出ONNX時把后處理邏輯比如decode部分剔除掉只保留backbone和head的特征輸出后處理全部放在業(yè)務(wù)代碼里做。我自己實際用下來最省心的方式是第三種。模型只輸出三個尺度的原始特征圖分別對應(yīng)80x80、40x40、20x20然后自己在Python代碼里做decode和NMS。犧牲一點點推理效率但換來了極大的靈活性后續(xù)想調(diào)整NMS參數(shù)、置信度閾值都不用重新轉(zhuǎn)模型。5.2 推理結(jié)果全為0或者框位置不對這個問題的原因往往不是模型沒轉(zhuǎn)對而是圖像預(yù)處理和模型訓(xùn)練時的預(yù)處理不一致。YOLOv8訓(xùn)練時用的預(yù)處理是image cv2.resize(image, (640, 640)) image image[:, :, ::-1] # BGR轉(zhuǎn)RGB image image.astype(np.float32) / 255.0 # 歸一化但如果AIPP配置里已經(jīng)做了歸一化你再在Python代碼里除255那就是雙重歸一化模型輸出幾乎肯定不對。還有一種情況是AIPP里寫錯了通道順序。我前面提到過如果輸入圖像是BGR順序但AIPP里配的input_format是RGB888_U8那相當(dāng)于把R和B通道互換了檢測出來的物體類別會概率低下定位框也是飄的。最簡單的排查方法是用一張純紅色圖片做測試。如果AIPP配的是RGB888_U8那么喂進去的BGR圖像會把紅色通道讀取成藍(lán)色輸出類別很可能從red變成blue。反過來就是通道反了。5.3 顯存占用虛高多模型加載失敗Atlas 300V Pro的24GB雖然不小但不代表可以無限加載模型。CANN的顯存管理和CUDA是兩套邏輯它默認(rèn)會為每個上下文預(yù)留一部分工作內(nèi)存如果你的代碼里頻繁創(chuàng)建上下文卻不釋放顯存就會慢慢被吃光。我遇到過一個情況同一個進程里反復(fù)加載和釋放模型第三次加載時直接報ACL_ERROR_RT_MEMORY_ALLOCATION。排查半天發(fā)現(xiàn)是釋放模型時只調(diào)了acl.mdl.unload_from_model但沒有調(diào)用acl.rt.destroy_context釋放上下文。正確做法是每次加載模型前檢查上下文數(shù)量用完立刻釋放acl.mdl.unload_from_model(model_id) acl.rt.destroy_context(context)另外CANN環(huán)境變量里也可以設(shè)置顯存池的大小和復(fù)用策略export ASCEND_GLOBAL_EVENT_ENABLE0 export ASCEND_GLOBAL_LOG_LEVEL3如果覺得自己管理顯存太麻煩可以考慮用MindX SDK的mxVision它提供了基于pipeline的流式推理框架顯存和Buffer管理由框架自動完成上手門檻低很多。但要注意mxVision的靈活度不如直接用pyACL如果你有一些非常規(guī)的預(yù)處理邏輯比如自定義的letterbox方式在mxVision的plugin里實現(xiàn)會麻煩一些。5.4 多卡并行時CPU占用過高插了兩張Atlas 300V Pro之后我一開始是每個進程綁定一張卡各自做推理。結(jié)果發(fā)現(xiàn)CPU占用非常高甚至出現(xiàn)CPU先到瓶頸的情況。后來排查發(fā)現(xiàn)問題出在圖像解碼和預(yù)處理上還是放在了CPU做。CANN雖然支持AIPP在NPU上做預(yù)處理但我之前把它關(guān)掉了。把AIPP重新打開并把decode也放到NPU上利用DVPP硬件解碼模塊CPU占用馬上從80%降到30%以內(nèi)。DVPP的用法是在CANN里創(chuàng)建VP和JPEGD等模塊但代碼寫起來比較復(fù)雜。如果要快速落地建議直接用MindX SDK它自帶了解碼、縮放、歸一化等常用插件可以直接串成一條pipeline。5.5 一個容易被忽略的硬坑PCIe帶寬很多人買了Atlas 300V Pro之后只看顯存和算力沒注意它是PCIe 3.0 x8的接口。如果服務(wù)器主板的PCIe槽位分配不合理插槽實際工作在x4模式下推理數(shù)據(jù)的傳輸時間會明顯變長。檢查辦法lspci -vvv | grep -A 20 Huawei看LnkCap和LnkSta如果是8GT/s x8那就正常。如果是x4或者x2去BIOS里找PCIe槽位帶寬設(shè)置或者換一個槽位。這個坑在實際部署中很容易被忽略但因為它的癥狀非常隱蔽只是整體耗時變高不會直接報錯。6. 模型量化與進一步調(diào)優(yōu)6.1 要不要做INT8量化前面提到昇騰的INT8性能很好我建議如果你對精度沒那么敏感或者有足夠的數(shù)據(jù)做校準(zhǔn)可以嘗試量化。CANN提供AMCTAscend Model Compression Toolkit做量化流程是# 準(zhǔn)備校準(zhǔn)數(shù)據(jù)集通常是幾百張代表真實場景的圖片 amct_onnx --modelyolov8s_sim.onnx \ --input_shapeimages:1,3,640,640 \ --data_dircalibration_images/ \ --output_path./quant_model量化后的模型和原模型一樣用ATC轉(zhuǎn)換但精度會有一點損失。我在我們自己的工業(yè)質(zhì)檢數(shù)據(jù)集上測試YOLOv8s量化后mAP從0.782掉到0.751損失約3%推理性能提升約1.8倍這個性價比還是很高的。6.2 batch size的選擇如果你的業(yè)務(wù)不是必須逐幀處理比如項目里是流式視頻分析強烈建議把batch size提到4或8。我實測batch1的FP16推理是13毫秒/張batch4時平均單張降到7毫秒batch8時單張降到5.5毫秒。當(dāng)batch超過8之后提升就不明顯了反而顯存占用上漲明顯所以8左右是一個甜點值。6.3 多線程推理優(yōu)化昇騰的310P芯片是多核架構(gòu)的如果單線程跑推理AI Core的利用率可能只有40%到60%。用多線程并發(fā)推理同一張卡上跑多個推理任務(wù)能明顯提高卡的整體吞吐。CANN里可以用acl.mdl.execute_async_in_thread_async實現(xiàn)多線程異步推理或者直接用MindX SDK自帶的多個推理引擎實例來并發(fā)。我最后把視頻流分析的服務(wù)改成了8線程并發(fā)推理整體吞吐提升了大約1.6倍卡的利用率從55%漲到了85%。7. 這套部署方案能不能直接用到生產(chǎn)從我個人項目經(jīng)驗來看Atlas 300V Pro 24G部署YOLOv8完全可以在生產(chǎn)環(huán)境里用但前提是你得接受它的思維方式和GPU不一樣。它不是把CUDA生態(tài)里的東西直接搬過來就能跑而是要走一套新的工具鏈從ONNX導(dǎo)出到ATC轉(zhuǎn)換再到ACL推理每一步都有一些無法跳過的細(xì)節(jié)。這些東西官方文檔都寫了但散落在不同地方?jīng)]有一個完整實例把路走通這也正是我寫這篇文章的原因。最后再分享一個小經(jīng)驗如果你在部署過程中卡在某一步不要自己硬扛。昇騰社區(qū)的論壇和官方技術(shù)支持其實是有人回應(yīng)的搜索時不要把關(guān)鍵詞限定在中文社區(qū)很多問題在英文版的技術(shù)討論帖里早就有答案。另外CANN每個版本的Release Notes一定要看很多時候你以為是自己的代碼問題實際上是CANN新版本改了默認(rèn)行為這類問題在Release Notes里都會注明。我目前正在把這套部署方案擴展到Y(jié)OLOv8-seg和YOLOv8-pose上原理類似只是輸出頭的解碼部分要額外處理。等到跑通了再回來更新。