境搭建與訓練全流程實戰(zhàn)指南)
1. 為什么選擇PaddleDetection而不是其他檢測框架如果你正在讀這篇文章大概率是準備入坑目標檢測或者手頭有個實際項目需要快速跑通一個檢測模型。我當初選PaddleDetection的原因很直接開箱即用的模型庫足夠全中文文檔友好而且從訓練到部署的鏈路是打通的。這一點對剛接觸檢測任務的人來說太重要了因為目標檢測不像圖像分類從數(shù)據(jù)標注、anchor設計、數(shù)據(jù)增強到后處理NMS每一步都有坑如果框架本身再不省心很容易在環(huán)境階段就勸退。PaddleDetection是飛槳生態(tài)下的目標檢測開發(fā)套件覆蓋了PP-YOLOE、YOLOv3、Faster R-CNN、SOLOv2、Swin Transformer等主流檢測和實例分割模型同時提供了從數(shù)據(jù)準備、模型訓練、評估到導出部署的完整工具鏈。它解決的核心問題是讓開發(fā)者不用從零手寫網絡結構和訓練循環(huán)通過配置文件就能切換模型和數(shù)據(jù)集。適合的人群包括計算機視覺方向的學生、需要快速驗證檢測方案的算法工程師、以及想把檢測模型落地到實際業(yè)務中的開發(fā)者。但我要先潑一盆冷水環(huán)境搭建這一步官方文檔給的是理想路徑實際執(zhí)行時會遇到各種版本沖突、依賴缺失、CUDA不匹配的問題。我前后在三臺不同配置的機器上裝過PaddleDetection每次都會碰到不一樣的情況。所以這篇文章不會只給你一條命令讓你復制粘貼而是把每一步背后的邏輯講清楚讓你遇到報錯時知道該往哪個方向排查。提示本文基于PaddleDetection 2.x版本系列撰寫不同小版本之間API和目錄結構可能有差異建議先確認你下載的版本號。2. 環(huán)境搭建前的硬件與軟件版本決策2.1 先搞清楚你的顯卡和CUDA版本這一步很多人會跳過直接pip install paddlepaddle就完事了結果跑訓練時發(fā)現(xiàn)用的是CPU速度慢到懷疑人生。PaddleDetection的訓練幾乎必須依賴GPU除非你只是跑一個推理demo看看效果。你需要先確認三件事顯卡型號和顯存大小顯存直接決定了你能用多大的batch size和輸入分辨率。6GB顯存跑YOLOv3的416輸入基本是極限8GB以上會舒服很多。CUDA驅動版本在終端執(zhí)行nvidia-smi右上角會顯示當前驅動支持的CUDA版本。注意這是驅動支持的最高版本不是你實際安裝的CUDA版本。cuDNN版本PaddlePaddle的GPU版本對cuDNN有最低版本要求一般CUDA 10.2對應cuDNN 7.6以上CUDA 11.x對應cuDNN 8.x以上。我踩過的一個坑是機器上裝了CUDA 11.6但PaddlePaddle當時只支持到CUDA 11.2結果裝完GPU版paddle后paddle.utils.run_check()一直報找不到動態(tài)庫。后來降級CUDA才解決。所以不要盲目追求最新CUDA版本要看飛槳官方文檔里明確支持的版本列表。2.2 Python環(huán)境隔離別在base環(huán)境里亂裝我強烈建議用conda創(chuàng)建一個獨立環(huán)境不要直接在系統(tǒng)Python或base環(huán)境里裝。原因很簡單PaddleDetection依賴的opencv、numpy、pycocotools等庫版本要求比較嚴格和你系統(tǒng)里已有的其他項目很容易沖突。conda create -n paddledet python3.8 -y conda activate paddledet為什么選Python 3.8而不是3.9或3.10因為PaddleDetection 2.x在Python 3.8上的兼容性驗證最充分很多預編譯的依賴包也是針對3.8構建的。我試過Python 3.10pycocotools編譯時直接報錯折騰了半天還是退回3.8。2.3 PaddlePaddle安裝CPU版還是GPU版這是第一個關鍵決策點。如果你只是想在本地跑通推理流程、看看模型效果CPU版就夠了安裝簡單不會有CUDA兼容問題。但如果你要訓練模型必須裝GPU版。GPU版安裝命令需要根據(jù)你的CUDA版本選擇# CUDA 10.2 python -m pip install paddlepaddle-gpu2.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple # CUDA 11.2 python -m pip install paddlepaddle-gpu2.4.2.post112 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html安裝完成后一定要驗證import paddle paddle.utils.run_check()如果輸出PaddlePaddle is installed successfully!并且顯示了GPU信息說明安裝正確。如果只顯示CPU信息那說明裝的是CPU版或者GPU版沒有正確識別到顯卡。注意run_check()如果報libcudnn.so找不到大概率是cuDNN路徑沒有加到LD_LIBRARY_PATH里??梢栽趡/.bashrc中追加export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH然后source一下。3. PaddleDetection源碼獲取與依賴安裝的完整鏈路3.1 下載源碼的正確姿勢PaddleDetection的代碼托管在GitHub上國內直接clone可能會很慢。我的做法是先用gitee鏡像或者直接下載release壓縮包。git clone https://github.com/PaddlePaddle/PaddleDetection.git cd PaddleDetection git checkout release/2.6 # 切換到穩(wěn)定分支為什么要checkout到release分支而不是直接用develop因為develop分支的代碼可能包含未測試的新特性依賴文件也可能和文檔不一致。release分支是經過驗證的穩(wěn)定版本適合實際項目使用。下載完成后先看一眼目錄結構PaddleDetection/ ├── configs/ # 模型配置文件 ├── ppdet/ # 核心代碼 ├── tools/ # 訓練、評估、導出腳本 ├── deploy/ # 部署相關 ├── dataset/ # 數(shù)據(jù)集存放目錄需自己創(chuàng)建 └── requirements.txt # 依賴列表3.2 安裝依賴時最容易卡住的三個地方直接執(zhí)行pip install -r requirements.txt看起來很簡單但實際執(zhí)行時大概率會卡在以下三個地方第一個是pycocotools。這個包在Windows上編譯需要Visual C Build Tools在Linux上需要cython和numpy先裝好。如果報錯可以先單獨裝pip install cython numpy pip install pycocotools第二個是opencv-python。PaddleDetection要求的是opencv-python但如果你系統(tǒng)里已經裝了opencv-contrib-python或者opencv-python-headless會產生沖突。建議先卸載再裝pip uninstall opencv-python opencv-contrib-python opencv-python-headless -y pip install opencv-python4.5.5.64第三個是PaddleDetection自身的安裝。在PaddleDetection根目錄下執(zhí)行pip install -e .這個-e參數(shù)是可編輯安裝的意思它會把當前目錄加入到Python的包搜索路徑中這樣你修改ppdet目錄下的代碼后不需要重新安裝就能生效。如果你不做這一步直接跑tools/train.py會報ModuleNotFoundError: No module named ppdet。3.3 驗證安裝是否成功安裝完成后用官方提供的一個小測試來驗證python tools/infer.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml \ -o weightshttps://paddledet.bj.bcebos.com/models/yolov3_mobilenet_v1_270e_coco.pdparams \ --infer_imgdemo/000000014439.jpg如果一切正常會在output目錄下生成一張帶檢測框的圖片。這一步能跑通說明環(huán)境基本沒問題了。我遇到過的情況是命令執(zhí)行后卡在下載權重那里不動。這是因為權重文件托管在遠程服務器上網絡不穩(wěn)定時會超時。解決辦法是手動下載權重文件放到本地然后用-o weights本地路徑指定。4. 跑通第一個訓練任務從配置文件到模型收斂4.1 配置文件的結構與繼承機制PaddleDetection的配置文件用的是YAML格式而且支持繼承。什么意思呢比如yolov3_mobilenet_v1_270e_coco.yml這個文件里可能只有幾行_BASE_: [ ../datasets/coco_detection.yml, ../runtime.yml, _base_/optimizer_270e.yml, _base_/yolov3_mobilenet_v1.yml, _base_/yolov3_reader.yml, ]它通過_BASE_字段繼承了數(shù)據(jù)集配置、優(yōu)化器配置、模型結構配置和數(shù)據(jù)讀取配置。這種設計的好處是你只需要修改差異部分不用把整個配置復制一遍。但壞處是新手容易看暈不知道某個參數(shù)到底在哪個文件里定義的。我的建議是先用python tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml --eval跑一遍然后在訓練日志里找到實際生效的完整配置把它打印出來對照著看。4.2 數(shù)據(jù)集準備COCO格式還是VOC格式PaddleDetection支持COCO和VOC兩種數(shù)據(jù)格式。COCO格式更通用但標注文件是一個大的jsonVOC格式是每張圖對應一個xml更直觀。如果你用自己的數(shù)據(jù)我建議轉成COCO格式因為PaddleDetection里大部分預訓練模型都是基于COCO訓練的用COCO格式可以直接復用數(shù)據(jù)增強策略和評估指標。數(shù)據(jù)集目錄結構應該是這樣的dataset/coco/ ├── annotations/ │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ │ ├── 000000000001.jpg │ └── ... └── val2017/ ├── 000000000002.jpg └── ...然后在配置文件中修改dataset_dir指向這個目錄。4.3 啟動訓練與關鍵參數(shù)解讀python tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml \ --eval \ -o use_gputrue \ -o TrainReader.batch_size4 \ -o LearningRate.base_lr0.001這里幾個參數(shù)值得展開說batch_size顯存不夠時第一個要調的就是它。但注意batch_size減小后學習率也要相應減小否則訓練容易震蕩。經驗公式是new_lr base_lr * new_batch_size / base_batch_size。base_lrYOLOv3在COCO上的默認學習率是0.001對應batch_size8。如果你改成batch_size4學習率建議改成0.0005。--eval每訓練一個epoch后在驗證集上評估一次。這會增加訓練時間但能讓你實時看到mAP的變化判斷模型是否在正常收斂。訓練過程中重點關注loss的變化。正常情況下前幾個epoch loss會快速下降然后趨于平緩。如果loss一直不降或者變成nan大概率是學習率太大或者數(shù)據(jù)有問題。提示訓練日志默認保存在output目錄下可以用visualdl --logdir output啟動VisualDL可視化查看loss曲線和mAP曲線。5. 環(huán)境搭建中那些文檔不會告訴你的坑5.1 顯存明明夠卻報OOM這個坑我遇到過好幾次。明明nvidia-smi顯示顯存還剩很多但訓練時就是報Out of Memory。原因通常是PaddlePaddle默認會申請全部顯存而不是按需分配。解決辦法是在訓練命令中加上-o use_gputrue -o GPUDeviceId0或者在代碼中設置import paddle paddle.set_device(gpu:0)另外如果你同時跑了多個訓練任務每個任務都會嘗試占用顯存這時候需要指定不同的GPUDeviceId。5.2 數(shù)據(jù)增強導致的詭異報錯PaddleDetection默認開啟了Mosaic、RandomExpand等數(shù)據(jù)增強。這些增強在COCO數(shù)據(jù)集上沒問題但如果你自己的數(shù)據(jù)集圖片尺寸特別小或者標注框特別密集可能會觸發(fā)一些邊界條件的bug。我遇到過一次所有圖片的標注框都被Mosaic拼接到了一起導致小目標完全丟失訓練出來的模型對小目標檢測效果極差。后來在配置文件中把Mosaic的概率調低問題才解決。TrainReader: sample_transforms: - Decode: {} - Mosaic: prob: 0.5 # 從默認的1.0降到0.55.3 評估指標不漲反降的排查思路訓練過程中如果發(fā)現(xiàn)mAP不升反降按以下順序排查檢查學習率是否過大loss曲線如果劇烈震蕩說明學習率太大。檢查數(shù)據(jù)標注是否有問題用tools/infer.py在訓練集上跑幾張圖看看模型預測的框和真實標注是否對得上。檢查是否過擬合如果訓練loss持續(xù)下降但驗證mAP下降說明過擬合了需要增加數(shù)據(jù)增強或減小模型復雜度。檢查類別不平衡如果某些類別的樣本特別少模型會偏向預測多的類別??梢杂?-classwise參數(shù)查看每個類別的AP。5.4 導出推理模型時的版本兼容問題訓練完成后用tools/export_model.py導出推理模型python tools/export_model.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml \ -o weightsoutput/yolov3_mobilenet_v1_270e_coco/best_model.pdparams \ --output_dirinference_model導出的模型包含model.pdmodel和model.pdiparams兩個文件。注意導出時的PaddlePaddle版本必須和推理時的版本一致否則會報模型格式不兼容。我試過用2.4訓練、2.3推理直接報錯。所以生產環(huán)境中要鎖定版本。6. 從跑通到用好幾個提升效率的實操建議6.1 用AMP混合精度訓練省顯存如果你的顯卡支持Tensor CoreRTX系列、V100等開啟AMP混合精度訓練可以節(jié)省約30%的顯存同時訓練速度提升20%左右。在配置文件中加一行use_amp: true或者在命令行中python tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml -o use_amptrue但要注意AMP在某些模型上可能會導致訓練不穩(wěn)定如果發(fā)現(xiàn)loss異常先關掉AMP試試。6.2 預訓練權重的正確加載方式PaddleDetection加載預訓練權重有兩種方式pretrain_weights和weights。前者是在模型初始化時加載骨干網絡的權重后者是加載整個模型的權重繼續(xù)訓練。# 加載骨干網絡預訓練權重 pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/MobileNetV1_pretrained.pdparams # 斷點續(xù)訓 weights: output/yolov3_mobilenet_v1_270e_coco/model_final斷點續(xù)訓時優(yōu)化器狀態(tài)和學習率調度器狀態(tài)也會被恢復所以不用擔心學習率重置的問題。6.3 多卡訓練的啟動方式如果你有多張顯卡可以用tools/train_multi_GPU.py啟動多卡訓練python -m paddle.distributed.launch --gpus 0,1,2,3 tools/train.py \ -c configs/yolov3/yolov3_mobilenet_v1_270e_coco.yml多卡訓練時總batch_size 單卡batch_size × 顯卡數(shù)學習率也要相應放大。但注意學習率不是線性放大的一般按平方根縮放比較穩(wěn)妥。6.4 日志分析與訓練監(jiān)控訓練過程中我習慣用tail -f output/yolov3_mobilenet_v1_270e_coco/train.log實時看日志。重點關注幾個指標指標正常范圍異常表現(xiàn)可能原因loss持續(xù)下降后趨于平穩(wěn)震蕩或nan學習率過大mAP逐步上升不升或下降數(shù)據(jù)問題或過擬合學習率按調度器變化不變調度器配置錯誤顯存占用穩(wěn)定持續(xù)增長內存泄漏如果發(fā)現(xiàn)顯存持續(xù)增長大概率是數(shù)據(jù)加載器里有變量沒有釋放可以嘗試減小num_workers或者檢查自定義的數(shù)據(jù)增強代碼。7. 一些個人體會PaddleDetection的環(huán)境搭建本身不難難的是遇到報錯時知道去哪里找答案。我的經驗是先看終端報錯的最后幾行那里通常有具體的錯誤類型和文件路徑然后去PaddleDetection的GitHub Issues里搜關鍵詞大部分坑別人已經踩過了最后才是看官方文檔因為文檔往往只覆蓋理想情況。另外不要在一個環(huán)境里同時裝多個深度學習框架。我試過在同一個conda環(huán)境里裝PyTorch和PaddlePaddle結果兩者的CUDA庫版本沖突折騰了一整天才找到原因。現(xiàn)在我的做法是每個框架一個獨立環(huán)境互不干擾。最后說一個細節(jié)PaddleDetection的配置文件里有很多_BASE_繼承修改參數(shù)時一定要確認你改的是最終生效的那個文件。我見過有人改了_base_/yolov3_reader.yml里的batch_size但實際訓練時用的是yolov3_mobilenet_v1_270e_coco.yml里覆蓋的值導致改了沒生效。排查方法是在訓練日志開頭找到打印出來的完整配置以那個為準。