行全流程)
做FPGA端的深度學(xué)習(xí)部署Vitis-AI基本是繞不開(kāi)的一條技術(shù)路線。這篇文章把我最近在ZCU104上跑通YOLOv5的完整過(guò)程記錄下來(lái)從Pytorch訓(xùn)練好的模型權(quán)重開(kāi)始到量化校準(zhǔn)再到編譯成DPU可執(zhí)行的xmodel每一步都展開(kāi)講清楚。代碼我已經(jīng)整理開(kāi)源想直接照著做的朋友可以到我的GitHub倉(cāng)庫(kù)里找倉(cāng)庫(kù)里的腳本和文章里用到的完全一致。內(nèi)容適合剛接觸Vitis-AI、準(zhǔn)備把目標(biāo)檢測(cè)模型部署到Zynq UltraScale平臺(tái)的開(kāi)發(fā)者參考也適合已經(jīng)在Xilinx生態(tài)里摸爬滾打、想快速評(píng)估YOLOv5在DPU上效果的同行。這個(gè)系列我打算寫(xiě)三篇第一篇就是現(xiàn)在這篇重點(diǎn)解決模型怎么從Pytorch變成DPU能跑的xmodel這一核心問(wèn)題。很多朋友卡在這個(gè)環(huán)節(jié)要么是Docker環(huán)境起不來(lái)要么是量化時(shí)算子不兼容報(bào)錯(cuò)要么是編譯完發(fā)現(xiàn)精度掉得沒(méi)法看。這些坑我基本都踩了一遍文章里會(huì)逐個(gè)說(shuō)明原因和解決辦法。1. 整體方案設(shè)計(jì)與選型思路1.1 為什么選ZCU104 Vitis-AI而不是 Jetson 或嵌入式NPU做邊緣端目標(biāo)檢測(cè)硬件方案其實(shí)很多。NVIDIA Jetson系列生態(tài)成熟跑YOLOv5幾乎零門(mén)檻TensorRT加速效果也出色但功耗和整板成本都比較高而且在嚴(yán)苛的工業(yè)環(huán)境下散熱和可靠性不一定滿足要求。嵌入式NPU比如瑞芯微RK3588、算能BM1684性價(jià)比很高但模型適配受限于各類(lèi)工具鏈想做自定義圖像采集、協(xié)議解析、電機(jī)控制這類(lèi)邏輯時(shí)NPU的通用性就不夠。ZCU104的優(yōu)勢(shì)在于它是Zynq UltraScale MPSoCPS側(cè)有四個(gè)ARM A53核PL側(cè)有可編程邏輯DPU相當(dāng)于在PL里例化出來(lái)的專(zhuān)用CNN加速I(mǎi)P。這意味著卷積推理可以卸載到DPU上而圖像采集、幀處理、NMS后處理、外部接口通信這些完全可以在PS側(cè)自己控制。如果你需要的是一個(gè)既能跑AI、又能靈活控制外設(shè)的邊緣計(jì)算平臺(tái)ZCU104就是比Jetson更適合的選擇。Vitis-AI是AMD/Xilinx官方提供的AI推理加速工具鏈它解決的問(wèn)題很直接你不用自己寫(xiě)RTL去實(shí)現(xiàn)卷積也不用費(fèi)勁學(xué)HLS去折騰矩陣運(yùn)算只需要把訓(xùn)練好的模型丟給工具鏈量化、編譯之后就能在DPU上跑起來(lái)。整個(gè)流程從Pytorch模型到最終在板卡上運(yùn)行鏈路非常清晰而且官方鏡像把所有依賴都封裝好了很大程度上降低了FPGA開(kāi)發(fā)的門(mén)檻。當(dāng)然它的學(xué)習(xí)曲線并沒(méi)有完全消失——量化原理、算子支持范圍、DPU架構(gòu)選擇這些知識(shí)如果不懂遇到問(wèn)題還是會(huì)一頭霧水。1.2 端到端流程拆解訓(xùn)練、量化、編譯、部署整個(gè)技術(shù)路徑可以分成四個(gè)階段訓(xùn)練階段在Pytorch框架下完成YOLOv5模型的訓(xùn)練得到浮點(diǎn)權(quán)重文件這一步有官方Y(jié)OLOv5倉(cāng)庫(kù)可以用也可以在自己數(shù)據(jù)集上微調(diào)。量化階段用Vitis-AI提供的vai_q_pytorch工具以少量真實(shí)圖片作為校準(zhǔn)集統(tǒng)計(jì)網(wǎng)絡(luò)各層激活值的動(dòng)態(tài)范圍把FP32參數(shù)映射到INT8。編譯階段用vai_c_xir工具把量化后的模型編譯成DPU指令流的xmodel文件這一階段會(huì)指定具體的DPU架構(gòu)例如ZCU104上的DPUCZDX8G。部署階段在ZCU104上運(yùn)行Vitis AI Runtime加載xmodel執(zhí)行推理PS側(cè)CPU負(fù)責(zé)圖像預(yù)處理和結(jié)果后處理。文章后面所有內(nèi)容都圍繞這條主線展開(kāi)。我在實(shí)際開(kāi)發(fā)中比較建議先跑通一個(gè)最簡(jiǎn)單的分類(lèi)模型再上YOLOv5這種結(jié)構(gòu)復(fù)雜的檢測(cè)模型這樣可以把工具鏈不熟和模型適配問(wèn)題分開(kāi)排查。當(dāng)然如果你已經(jīng)有一定基礎(chǔ)直接按本文流程操作也沒(méi)有問(wèn)題。2. 環(huán)境準(zhǔn)備與必備概念2.1 主機(jī)端環(huán)境搭建Ubuntu Docker Vitis-AI鏡像Vitis-AI官方推薦在Docker容器里完成量化和編譯原因很簡(jiǎn)單不同版本的工具鏈依賴差異很大Docker鏡像把CUDA、Pytorch、nndct等一堆依賴固定好避免污染主機(jī)環(huán)境也方便版本切換。我自己的習(xí)慣是單獨(dú)劃分一個(gè)工作目錄把模型文件、數(shù)據(jù)集、輸出產(chǎn)物都掛載進(jìn)容器這樣即使容器重建也不會(huì)丟數(shù)據(jù)。主機(jī)環(huán)境建議Ubuntu 18.04或20.04先安裝Docker然后拉取官方鏡像。Vitis-AI 1.4是比較經(jīng)典的穩(wěn)定版本新項(xiàng)目也可以考慮2.5或3.0版本但不同版本的命令和算子支持會(huì)有差異本文以1.4版本為例說(shuō)明# 拉取鏡像根據(jù)實(shí)際網(wǎng)絡(luò)環(huán)境選擇合適的源 docker pull xilinx/vitis-ai:1.4.130 # 啟動(dòng)容器掛載工作目錄 docker run -it \ -v /home/user/vitis_ai_workspace:/workspace \ xilinx/vitis-ai:1.4.130 \ bash進(jìn)入容器后激活Pytorch環(huán)境conda activate vitis-ai-pytorch這一步很關(guān)鍵Vitis-AI容器里同時(shí)內(nèi)置了TensorFlow2、TensorFlow1、Pytorch、Caffe等多個(gè)環(huán)境如果不激活pytorch環(huán)境就執(zhí)行vai_q_pytorch命令會(huì)直接提示找不到命令。我用的是1.4版本自帶的python3.7 pytorch 1.8組合實(shí)際測(cè)試下來(lái)和官方Y(jié)OLOv5 v5.0版本的代碼兼容性比較好。2.2 核心概念速通量化、編譯、DPU、xmodel在動(dòng)手之前一定要把幾個(gè)基本概念搞明白否則后面連錯(cuò)誤提示都看不懂。量化就是把浮點(diǎn)模型轉(zhuǎn)成定點(diǎn)模型。FP32精度下每個(gè)參數(shù)占4字節(jié)而DPU的DSP單元做INT8定點(diǎn)乘加運(yùn)算更高效FP32轉(zhuǎn)INT8之后模型體積縮小到原來(lái)的四分之一推理速度大幅提升。量化的數(shù)學(xué)本質(zhì)是找一個(gè)浮點(diǎn)實(shí)數(shù)r和整數(shù)q之間的映射關(guān)系q round(r / scale) zero_point其中scale和zero_point是根據(jù)每一層參數(shù)的取值范圍算出來(lái)的。Vitis-AI在量化時(shí)會(huì)在校準(zhǔn)階段統(tǒng)計(jì)激活值的分布從而確定合適的scale和zero_point。編譯不是普通程序員理解的源碼變機(jī)器碼在Vitis-AI里編譯是把已經(jīng)量化的模型進(jìn)一步轉(zhuǎn)換成DPU的指令流同時(shí)做算子融合和內(nèi)存調(diào)度優(yōu)化。DPU認(rèn)識(shí)的不是PyTorch的算子圖而是一套專(zhuān)用的AI指令最終產(chǎn)物是xmodel文件。DPU是Xilinx提供的可配置CNN加速I(mǎi)P核在ZCU104上用的典型配置是DPUCZDX8G。你可以在Vivado里例化DPU核設(shè)置不同的架構(gòu)參數(shù)比如能放幾個(gè)卷積核、支持多大的輸入圖像然后綜合出FPGA比特流。軟件側(cè)的模型編譯必須和硬件側(cè)的DPU架構(gòu)匹配否則編譯出來(lái)的xmodel在板子上根本加載不了。2.3 YOLOv5模型準(zhǔn)備與針對(duì)性預(yù)處理YOLOv5本身是Pytorch框架下非常成熟的目標(biāo)檢測(cè)工程模型主體由CSPDarknet骨干網(wǎng)絡(luò)、PANet特征融合層和YOLO檢測(cè)頭組成。準(zhǔn)備模型時(shí)我建議直接用官方v5.0分支訓(xùn)練自己的權(quán)重或者先用官方預(yù)訓(xùn)練權(quán)重yolov5s.pt走通整個(gè)流程后續(xù)再換自定義數(shù)據(jù)。量化編譯階段用到的模型文件是PyTorch的.pt/.pth權(quán)重不是ONNX這一點(diǎn)要特別注意。有一個(gè)針對(duì)Vitis-AI的關(guān)鍵修改必須在量化前完成YOLOv5默認(rèn)使用SiLU激活函數(shù)也就是Swish但DPUCZDX8G對(duì)SiLU的支持很有限量化和編譯時(shí)大概率會(huì)報(bào)Unsupported Op錯(cuò)誤。我的處理方式是把模型結(jié)構(gòu)中的SiLU全部替換成ReLU雖然理論上會(huì)讓模型表達(dá)能力略降一些但實(shí)際測(cè)試下來(lái)精度損失很小而且能順利在DPU上部署。如果你是自己訓(xùn)練模型建議在訓(xùn)練階段就直接把激活函數(shù)換掉比如在模型定義里把SiLU改成ReLU再訓(xùn)練這樣從源頭避免麻煩。輸入圖像的預(yù)處理也必須和訓(xùn)練時(shí)保持一致。YOLOv5在預(yù)處理時(shí)會(huì)對(duì)圖像做letterbox等比縮放后填充灰邊然后歸一化到0到1之間量化校準(zhǔn)階段給模型喂的數(shù)據(jù)必須完全按這個(gè)流程來(lái)否則量化統(tǒng)計(jì)出的激活值范圍是不準(zhǔn)確的最終部署精度會(huì)受到牽連。3. 量化流程詳解從FP32到INT83.1 為什么必須做量化以及它帶來(lái)的收益ZCU104上的DPU本質(zhì)上就是一個(gè)定點(diǎn)運(yùn)算引擎INT8是它的核心工作精度。如果不做量化直接把FP32模型扔給工具鏈編譯編譯根本不會(huì)通過(guò)即便強(qiáng)行部署也會(huì)因?yàn)橛?jì)算資源開(kāi)銷(xiāo)巨大而性能慘不忍睹。量化帶來(lái)的收益體現(xiàn)在三個(gè)層面一是模型體積壓縮一個(gè)70MB左右的FP32模型量化后約17.5MB對(duì)片上存儲(chǔ)有限的嵌入式設(shè)備來(lái)說(shuō)這個(gè)差別很關(guān)鍵二是推理速度提升INT8的定點(diǎn)乘加在DSP上可以流水線執(zhí)行實(shí)測(cè)在ZCU104上YOLOv5s的推理延遲比FP32模擬運(yùn)行快好幾倍三是功耗降低FPGA上INT8運(yùn)算的單位能耗遠(yuǎn)低于浮點(diǎn)運(yùn)算。當(dāng)然量化不是免費(fèi)的它一定帶來(lái)精度損失。所以整個(gè)量化流程里最重要的事情就是控制精度損失這是Vitis-AI使用中最核心的經(jīng)驗(yàn)之一。量化誤差主要來(lái)源于參數(shù)和激活值在轉(zhuǎn)INT8時(shí)的舍入誤差以及極端分布數(shù)值被截?cái)唷itis-AI的解決方案是在校準(zhǔn)階段用真實(shí)數(shù)據(jù)來(lái)統(tǒng)計(jì)每一層的動(dòng)態(tài)范圍從而確定合適的量化參數(shù)讓舍入誤差盡量小。3.2 校準(zhǔn)數(shù)據(jù)集的選取直接決定量化精度校準(zhǔn)集是量化時(shí)用來(lái)統(tǒng)計(jì)激活值分布的小批量真實(shí)圖片。很多人在這一步不重視隨手拿幾張圖就校準(zhǔn)了結(jié)果量化后模型幾乎不可用。根據(jù)我的測(cè)試和官方建議校準(zhǔn)集一般取200到500張左右比較合適圖片要覆蓋真實(shí)場(chǎng)景中的典型情況不同光照、不同目標(biāo)尺寸、不同背景復(fù)雜度的樣本都盡量包含。如果太少統(tǒng)計(jì)出的數(shù)值范圍有偏差如果太多校準(zhǔn)時(shí)間長(zhǎng)而且收益會(huì)遞減。我自己在項(xiàng)目里通常先從驗(yàn)證集中隨機(jī)抽樣300張圖片再手動(dòng)檢查一遍圖片多樣性確保不會(huì)集中在某幾個(gè)場(chǎng)景。同時(shí)要注意校準(zhǔn)圖片的預(yù)處理必須和訓(xùn)練完全一致。比如YOLOv5訓(xùn)練時(shí)用了letterbox和歸一化那校準(zhǔn)階段也必須是同樣的操作不能讓模型看到與訓(xùn)練時(shí)差異很大的數(shù)據(jù)分布。校準(zhǔn)集的載體路徑我放在一個(gè)文本文件里每行一張圖片的路徑。后面量化命令會(huì)讀取這個(gè)文件。組batch時(shí)建議batch_size設(shè)為1避免多張圖統(tǒng)計(jì)時(shí)個(gè)別異常值對(duì)量化參數(shù)造成干擾。3.3 量化執(zhí)行使用vai_q_pytorch一行命令完成Vitis-AI針對(duì)Pytorch模型的量化工具是vai_q_pytorch它封裝了nndct的量化器。在激活好vitis-ai-pytorch環(huán)境后基本用法如下vai_q_pytorch quantize \ --model ./float/yolov5s.pth \ --input_size 1,3,640,640 \ --batch_size 1 \ --dataset ./data/calib_list.txt \ --data_preprocess ./data/calib_dataloader.py \ --save_dir ./quant_res拆開(kāi)說(shuō)幾個(gè)重要參數(shù)。--model指定的是浮點(diǎn)權(quán)重文件路徑--input_size是模型輸入尺寸YOLOv5默認(rèn)640x640按batch,channel,height,width的順序?qū)?-dataset是剛才說(shuō)的校準(zhǔn)圖片路徑列表文件--data_preprocess是一個(gè)Python腳本路徑里面實(shí)現(xiàn)和訓(xùn)練階段一致的數(shù)據(jù)預(yù)處理邏輯--save_dir是輸出目錄量化產(chǎn)物會(huì)寫(xiě)到這個(gè)目錄下。如果你希望更精細(xì)地控制量化過(guò)程也可以直接使用Vitis-AI提供的Pytorch API來(lái)做from pytorch_nndct.apis import torch_quantizer # 校準(zhǔn)階段 quantizer torch_quantizer(calib, float_model, input_tensor, device) quant_model quantizer.quant_model # 在若干batch數(shù)據(jù)上執(zhí)行forward讓工具統(tǒng)計(jì)各層激活范圍 run_calibration(quant_model) # 測(cè)試階段 quantizer torch_quantizer(test, float_model, input_tensor, device) quant_model quantizer.quant_model evaluate(quant_model) # 導(dǎo)出xmodel quantizer torch_quantizer(deploy, float_model, input_tensor, device) quant_model quantizer.quant_model quantizer.export_xmodel()API方式的好處是可以在量化前后分別加載模型做精度評(píng)估這在排查量化導(dǎo)致精度嚴(yán)重下降的問(wèn)題時(shí)很有用。命令行方式則更適合標(biāo)準(zhǔn)化流程我個(gè)人的項(xiàng)目里兩種方式都保留排查問(wèn)題用API方式正式流程用命令行方式。等命令跑完quant_res目錄下會(huì)生成quantized_model.xmodel等文件。這里要特別提醒有些版本的vai_q_pytorch在量化模式下不會(huì)生成xmodel需要在deploy模式下才能導(dǎo)出不過(guò)最新版命令行工具一般會(huì)自動(dòng)完成這一步。3.4 量化后模型精度評(píng)估如何科學(xué)地確認(rèn)精度損失量化完成不等于任務(wù)結(jié)束必須對(duì)量化后的模型做一輪精度評(píng)估和目標(biāo)檢測(cè)的mAP指標(biāo)一起對(duì)比。我的流程是在驗(yàn)證集上分別跑浮點(diǎn)模型和量化模型算出兩套mAP如果差值在可接受范圍內(nèi)比如mAP0.5下降不超過(guò)2%就繼續(xù)編譯部署如果精度掉得厲害就需要返工。不同任務(wù)對(duì)精度損失的容忍度不同目標(biāo)檢測(cè)一般比圖像分類(lèi)更敏感。我自己的經(jīng)驗(yàn)是如果mAP0.5下降超過(guò)5%說(shuō)明校準(zhǔn)集可能沒(méi)有覆蓋足夠多的場(chǎng)景或者模型里某些層對(duì)量化特別敏感。你可以使用Vitis-AI提供的量化分析工具或日志查看每一層的量化敏感度優(yōu)先保留對(duì)精度影響大的層為更高精度其他層用INT8。當(dāng)然更簡(jiǎn)單的做法是先嘗試把校準(zhǔn)集從200張?jiān)黾拥?00張?jiān)僦匦铝炕槐楹芏鄷r(shí)候問(wèn)題就解決了。4. 模型編譯與部署產(chǎn)物生成4.1 編譯命令詳解指定ZCU104架構(gòu)量化出來(lái)的模型還要通過(guò)編譯變成DPU實(shí)際執(zhí)行的文件。ZCU104開(kāi)發(fā)板上我們需要指定DPUCZDX8G架構(gòu)。在Vitis-AI容器里編譯命令如下vai_c_xir \ --xmodel ./quant_res/quantized_model.xmodel \ --arch /opt/vitis_ai/compiler/arch/dpuv2/ZCU104/ZCU104.json \ --net_name yolov5s_zcu104 \ --output_dir ./compiled_model--xmodel指定量化模型的路徑--arch指定DPU架構(gòu)描述文件這個(gè)JSON文件告訴編譯器ZCU104上DPU支持哪些運(yùn)算、內(nèi)存帶寬、指令長(zhǎng)度等參數(shù)。如果你用的是其他板卡比如ZCU102或KV260就要換對(duì)應(yīng)路徑下的JSON文件千萬(wàn)不能混用。--net_name就是生成的xmodel網(wǎng)絡(luò)名稱(chēng)--output_dir決定編譯產(chǎn)物保存路徑。編譯完成后目錄里會(huì)生成類(lèi)似yolov5s_zcu104.xmodel的文件。這個(gè)文件就是最終要拷到ZCU104上加載的模型文件。編譯階段的核心工作是對(duì)計(jì)算圖做算子融合和內(nèi)存規(guī)劃比如把卷積后面的批歸一化和ReLU融合到卷積運(yùn)算內(nèi)部這在DPU上是硬件級(jí)別的優(yōu)化能顯著減少指令數(shù)和內(nèi)存訪問(wèn)次數(shù)。4.2 DPU硬件配置與架構(gòu)匹配問(wèn)題編譯時(shí)指定的架構(gòu)必須與ZCU104里實(shí)際例化的DPU核保持一致。如果你在Vivado里創(chuàng)建DPU核時(shí)選擇了DPUCZDX8G_ISA1_B4096那么arch路徑下對(duì)應(yīng)的JSON也要匹配這個(gè)指令集架構(gòu)ISA。如果軟件編譯的ISA和硬件DPU的ISA不匹配板卡運(yùn)行時(shí)加載xmodel會(huì)直接報(bào)錯(cuò)這在Vitis-AI里是非常典型的部署錯(cuò)誤。常見(jiàn)的ZCU104配置有B4096和B512等這些參數(shù)描述了DPU中并發(fā)計(jì)算單元的數(shù)量可以理解為DPU的算力檔位。B4096計(jì)算能力強(qiáng)占用FPGA資源多最高時(shí)鐘頻率可能上不去B512資源占用小頻率更穩(wěn)但算力相對(duì)低。對(duì)于YOLOv5s來(lái)說(shuō)B4096是比較均衡的選擇INT8下的理論算力能跑到2TOPS左右實(shí)際幀率視輸入尺寸而定。DPU頻率的設(shè)置也影響性能。我在Vivado配置DPU核時(shí)選擇300MHz或者更高的頻率但如果PL側(cè)的時(shí)序收斂不好就降到270MHz否則系統(tǒng)跑起來(lái)可能有偶發(fā)錯(cuò)誤排查起來(lái)非常痛苦。Vivado綜合后會(huì)用報(bào)時(shí)序問(wèn)題的路徑這時(shí)候通過(guò)降低DPU頻率來(lái)收時(shí)序是最快的方法。4.3 輸出產(chǎn)物說(shuō)明xmodel、指示文件與部署準(zhǔn)備編譯輸出的xmodel文件是二進(jìn)制格式包含DPU指令流和模型權(quán)重?cái)?shù)據(jù)。除此之外編譯日志和元信息文件也值得關(guān)注。日志里的total instruction count和total memory footprint可以用來(lái)粗略估算推理延遲和資源占用。我在項(xiàng)目里會(huì)用一張表對(duì)比不同輸入尺寸下的編譯結(jié)果配置項(xiàng)建議值備注輸入尺寸640x640精度優(yōu)先速度會(huì)慢一些輸入尺寸320x320速度優(yōu)先精度會(huì)下降DPU架構(gòu)B4096平衡資源占用和算力編譯架構(gòu)文件ZCU104.json必須匹配硬件DPU配置量化方式INT8對(duì)稱(chēng)默認(rèn)配置大多數(shù)場(chǎng)景足夠部署到ZCU104之前還需要把對(duì)應(yīng)版本的Vitis AI RuntimeVART放到板卡上。官方的Petalinux鏡像里已經(jīng)預(yù)編譯好了runtime也可以自己編譯。runtime版本必須和編譯時(shí)的Vitis-AI版本嚴(yán)格對(duì)齊否則會(huì)出現(xiàn)版本不匹配的報(bào)錯(cuò)。這一步比較容易忽略很多人在板子上跑不起來(lái)xmodel檢查后發(fā)現(xiàn)是runtime版本和主機(jī)編譯工具鏈版本對(duì)不上。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 算子不支持SiLU激活函數(shù)引發(fā)的不兼容這是我在量化時(shí)遇到的第一個(gè)報(bào)錯(cuò)Unsupported Ops: aten::silu。原因在前面說(shuō)過(guò)ZCU104上的DPUCZDX8G指令集不直接支持SiLU激活。解決辦法是把YOLOv5模型里所有SiLU替換成ReLU。需要注意替換的位置不止一處YOLOv5的CSPDarknet骨干里有多處SiLU如果不小心漏掉一個(gè)量化還是會(huì)報(bào)錯(cuò)。建議在修改后用腳本掃描一遍模型定義確保沒(méi)有遺漏。5.2 量化后精度下降嚴(yán)重從校準(zhǔn)集和預(yù)處理兩個(gè)方向排查量化后mAP掉點(diǎn)通常有以下幾類(lèi)原因校準(zhǔn)集規(guī)模太小校準(zhǔn)圖片預(yù)處理與訓(xùn)練不一致模型里有某些層對(duì)量化非常敏感輸入圖像尺寸和訓(xùn)練時(shí)不匹配。我的排查順序是先擴(kuò)充校準(zhǔn)集數(shù)量再檢查預(yù)處理代碼然后考慮對(duì)敏感層做更高精度的保護(hù)最后才考慮改模型結(jié)構(gòu)。大多數(shù)情況到第二步就能解決我遇到過(guò)一次因?yàn)閐ataloader里忘了做letterbox而是直接resize導(dǎo)致校準(zhǔn)數(shù)據(jù)分布偏移量化后精度慘不忍睹排查了半天才定位到預(yù)處理的問(wèn)題。5.3 編譯錯(cuò)誤架構(gòu)文件版本與容器版本不匹配如果你在編譯時(shí)發(fā)現(xiàn)報(bào)錯(cuò)提示JSON架構(gòu)文件里沒(méi)有對(duì)應(yīng)算子定義通常是Vitis-AI容器版本和arch文件版本不匹配。解決方法是重新拉取和arch文件配套的容器版本或者直接在容器里使用默認(rèn)路徑下的arch文件而不是到處拷貝網(wǎng)上找來(lái)的JSON。這個(gè)坑在多人協(xié)作或者在網(wǎng)上搜索教程時(shí)很容易踩因?yàn)橥粋€(gè)ZCU104的JSON在不同版本里的字段有細(xì)微差異。5.4 部署時(shí)加載xmodel失敗版本對(duì)齊和ISA匹配是重點(diǎn)板卡端最常見(jiàn)的問(wèn)題是加載xmodel失敗。首先是runtime版本必須和主機(jī)端Vitis-AI版本一致其次就是前面反復(fù)強(qiáng)調(diào)的DPU ISA匹配。如果平臺(tái)報(bào)錯(cuò)提示xmodel is not compatible with the DPU基本可以確定是編譯時(shí)的arch配置和板上DPU硬件配置不一致。另外檢查一下ZCU104鏡像里是否已經(jīng)正確加載了DPU驅(qū)動(dòng)裸機(jī)部署和Petalinux部署在這個(gè)環(huán)節(jié)略有不同。5.5 性能不達(dá)標(biāo)不只是DPU頻率的問(wèn)題如果部署后幀率不理想不只是調(diào)高DPU頻率就能解決。模型輸入尺寸、DPU配置、PS側(cè)預(yù)處理代碼的寫(xiě)法、是否合理使用多線程都會(huì)影響整體性能。我建議在硬件允許的前提下把YOLOv5的輸入從640x640降到416x416測(cè)試一下通常延遲能下降接近一半對(duì)很多檢測(cè)場(chǎng)景來(lái)說(shuō)精度依然夠用。另外預(yù)處理如果全部放在PS側(cè)串行執(zhí)行會(huì)成為瓶頸建議使用NEON優(yōu)化或者直接改成多線程并行。排查以上問(wèn)題時(shí)我習(xí)慣先把問(wèn)題拆成主機(jī)端和板卡端兩段來(lái)定位。量化、編譯階段的報(bào)錯(cuò)留在Docker里反復(fù)驗(yàn)證部署階段的報(bào)錯(cuò)再到板子上調(diào)試不要混在一起排查否則思路很容易亂。再說(shuō)一個(gè)非常容易踩的坑Vitis-AI官方文檔里推薦的Docker鏡像非常大如果網(wǎng)速不好拉取時(shí)間很長(zhǎng)。我一般會(huì)在同一個(gè)鏡像基礎(chǔ)上維護(hù)一個(gè)自己的tag把數(shù)據(jù)集、腳本、編譯產(chǎn)物都放在掛載目錄里這樣每次啟動(dòng)容器不需要重新下載鏡像也不要頻繁提交新鏡像既省時(shí)間又避免容器膨脹。如果你按著這個(gè)流程走通了你會(huì)發(fā)現(xiàn)Vitis-AI本身并沒(méi)有想象中那么神秘核心就是量化、編譯、部署三步。真正花時(shí)間的地方全在細(xì)節(jié)上算子兼容性、校準(zhǔn)集質(zhì)量、預(yù)處理一致性、版本對(duì)齊。這一篇讓你在主機(jī)端得到了可部署的xmodel。下一步就是在ZCU104上加載它跑起推理了這部分涉及VART API的調(diào)用、YOLOv5檢測(cè)頭的輸出解析、NMS后處理的實(shí)現(xiàn)內(nèi)容比量化編譯還要細(xì)碎一些我放到系列第二篇里詳細(xì)講。我個(gè)人在實(shí)際項(xiàng)目里的體會(huì)是先把模型側(cè)這一關(guān)理順了runtime的工程量其實(shí)不大真正花時(shí)間的往往是在調(diào)試輸入圖像顯示不對(duì)、坐標(biāo)偏移這類(lèi)不起眼的問(wèn)題上。