義分割模型復(fù)現(xiàn)與訓(xùn)練全流程指南)
1. 項(xiàng)目概述與核心思路拆解1.1 為什么PIDNet值得跑一遍語(yǔ)義分割這幾年模型迭代速度極快從FCN到DeepLab系列再到HRNet大家基本都在做同一件事如何在保持高分辨率細(xì)節(jié)的同時(shí)把上下文語(yǔ)義信息也抓到位。PIDNetArXiv 2022這個(gè)模型有意思的地方在于它把經(jīng)典控制理論里的PID控制器思想直接搬進(jìn)了網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)用比例Proportional、積分Integral、微分Derivative三種互補(bǔ)的特性來(lái)指導(dǎo)特征提取流程在速度和精度之間找到了一個(gè)非常實(shí)用的平衡點(diǎn)。我第一次讀這篇論文的時(shí)候最直接的感觸是它的設(shè)計(jì)邏輯很“工程化”。深度學(xué)習(xí)模型越做越深但很多改進(jìn)都是在堆模塊、堆通道實(shí)際落地時(shí)參數(shù)量和推理延遲都很難控制。PIDNet反而是從控制論中汲取靈感把特征提取過(guò)程分成三個(gè)并行分支P分支負(fù)責(zé)捕捉大尺度的上下文關(guān)系I分支負(fù)責(zé)保留高分辨率細(xì)節(jié)信息D分支則在兩者之間做動(dòng)態(tài)調(diào)節(jié)和特征融合。這種三分支結(jié)構(gòu)既能拿到高質(zhì)量的分割結(jié)果又不會(huì)像Transformer那樣吃顯存吃到懷疑人生。對(duì)于想復(fù)現(xiàn)論文、做實(shí)驗(yàn)對(duì)比、或者在這個(gè)模型基礎(chǔ)上做改進(jìn)創(chuàng)新的同學(xué)來(lái)說(shuō)PIDNet是一個(gè)非常合適的基座模型。訓(xùn)練流程相對(duì)透明結(jié)構(gòu)不算特別復(fù)雜改進(jìn)空間又很大。你在它的語(yǔ)義分割框架上動(dòng)刀不管是調(diào)整分支間的融合策略還是替換骨干網(wǎng)絡(luò)都能很清晰地觀察到效果差異。1.2 復(fù)現(xiàn)方案的整體選型考慮我自己實(shí)際跑過(guò)PIDNet的完整訓(xùn)練流程踩了不少坑也總結(jié)了一些經(jīng)驗(yàn)。先說(shuō)結(jié)論P(yáng)IDNet的復(fù)現(xiàn)難度屬于中等偏上比直接調(diào)用一個(gè)封裝好的模型要麻煩但比從零手寫(xiě)一個(gè)訓(xùn)練管線要輕松很多。如果你已經(jīng)熟悉語(yǔ)義分割的訓(xùn)練流程那核心工作量其實(shí)就是環(huán)境配置、數(shù)據(jù)組織、參數(shù)調(diào)整這三件事如果你是第一次跑分割模型那可能需要先在骨架網(wǎng)絡(luò)上多花點(diǎn)時(shí)間理解一下。從技術(shù)選型角度來(lái)看PIDNet官方是基于PyTorch框架實(shí)現(xiàn)的GPU訓(xùn)練是必要的純CPU環(huán)境基本跑不動(dòng)。數(shù)據(jù)準(zhǔn)備方面Cityscapes是論文的主戰(zhàn)場(chǎng)但如果你手頭沒(méi)有Cityscapes數(shù)據(jù)集也可以用VOC或自己標(biāo)注的數(shù)據(jù)來(lái)做遷移訓(xùn)練。我在實(shí)際操作中推薦先用Cityscapes的一個(gè)小子集跑通流程確認(rèn)代碼沒(méi)問(wèn)題后再上全量數(shù)據(jù)這樣能節(jié)省很多排查問(wèn)題的時(shí)間。2. 環(huán)境配置全過(guò)程2.1 硬件與驅(qū)動(dòng)認(rèn)知先弄清楚你的底牌配置環(huán)境之前第一件事不是著急裝包而是先確認(rèn)自己的機(jī)器條件。PIDNet訓(xùn)練時(shí)常用的輸入分辨率是1024x1024Cityscapes標(biāo)準(zhǔn)設(shè)置這個(gè)分辨率下一張RTX 2080 Ti級(jí)別的顯卡11GB顯存只能塞下比較小的batch size通常設(shè)到2到4就頂天了。如果你想用8的batch size訓(xùn)練顯存至少要16GB以上。這里建議你先跑一下nvidia-smi確認(rèn)驅(qū)動(dòng)版本和顯存大小以及CUDA的可用版本。這里要特別說(shuō)明一下CUDA版本的選擇邏輯。PyTorch的預(yù)編譯包是和CUDA版本綁定的比如PyTorch 1.10對(duì)應(yīng)CUDA 11.3PyTorch 1.12對(duì)應(yīng)CUDA 11.6PyTorch 2.x對(duì)應(yīng)CUDA 11.8或12.1。你需要先確定PyTorch版本再反向確定CUDA版本。PIDNet官方代碼是2022年發(fā)布的用的PyTorch版本不算新如果你機(jī)器上已經(jīng)裝了新版PyTorch比如2.x很可能遇到一些API兼容性問(wèn)題后面我會(huì)詳細(xì)講怎么處理。注意不建議在Windows環(huán)境下跑訓(xùn)練除非你非常熟悉MSVC編譯鏈。PIDNet中有一部分算子需要編譯C擴(kuò)展Windows上的坑比較多Linux環(huán)境是最順暢的。如果你是Windows用戶(hù)建議裝一個(gè)WSL2或直接用一個(gè)Linux服務(wù)器。2.2 CUDA與PyTorch安裝要點(diǎn)我推薦的環(huán)境組合是這樣的Ubuntu 20.04或22.04CUDA 11.3對(duì)應(yīng)NVIDIA驅(qū)動(dòng)版本465PyTorch 1.10.1或1.12.0Python 3.8或3.9GCC 7.5以上用于編譯擴(kuò)展算子具體的安裝步驟比較常規(guī)我就快速帶過(guò)重點(diǎn)是Pytorch安裝時(shí)一定要用官方源或可信鏡像源避免包版本不匹配。以PyTorch 1.10.1為例# 創(chuàng)建虛擬環(huán)境 conda create -n pidnet python3.8 conda activate pidnet # 安裝PyTorch注意cuda版本參數(shù)要和你的CUDA環(huán)境對(duì)應(yīng) pip install torch1.10.1cu113 torchvision0.11.2cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html裝完以后先用一段簡(jiǎn)單代碼驗(yàn)證CUDA是否可用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果后面兩步輸出正常說(shuō)明PyTorch已經(jīng)正確識(shí)別到了GPU。這里有個(gè)容易忽視的細(xì)節(jié)即使PyTorch能識(shí)別GPU也不代表訓(xùn)練性能一定沒(méi)問(wèn)題你最好再用torch.cuda.get_device_capability(0)查一下算力PIDNet在算力8.0以上的卡上表現(xiàn)會(huì)更好一些。2.3 依賴(lài)與編譯最容易被卡住的一步PIDNet代碼中涉及到一些自定義的卷積算子大概率需要本地編譯。我遇到過(guò)好幾次環(huán)境測(cè)試能通過(guò)但真正訓(xùn)練時(shí)報(bào)錯(cuò)找不到某個(gè)編譯好的so文件。所以你在clone代碼之后一定要先看代碼庫(kù)里有沒(méi)有setup.py或類(lèi)似文件有的話先編譯安裝。# 安裝基礎(chǔ)依賴(lài) pip install opencv-python pillow numpy tqdm tensorboard scipy # 如果代碼庫(kù)中有編譯腳本 cd PIDNet python setup.py develop編譯過(guò)程如果報(bào)錯(cuò)多數(shù)是GCC版本太高或者缺少依賴(lài)庫(kù)。比較常用的解決方案是安裝低版本GCC或者在編譯時(shí)加上環(huán)境變量export CXX/usr/bin/g export CC/usr/bin/gcc提示如果你用的是PyTorch 2.x編譯時(shí)很可能會(huì)遇到ATEN/CUDA API不兼容的報(bào)錯(cuò)因?yàn)?.x改了部分算子接口。穩(wěn)妥的辦法是直接創(chuàng)建一個(gè)Python 3.8 PyTorch 1.12的新環(huán)境不要在原環(huán)境里硬解依賴(lài)。我在這上面耗過(guò)一晚上的時(shí)間走彎路沒(méi)有意義。3. PIDNet核心細(xì)節(jié)解析3.1 網(wǎng)絡(luò)結(jié)構(gòu)關(guān)鍵設(shè)計(jì)PIDNet的架構(gòu)設(shè)計(jì)是理解整個(gè)項(xiàng)目核心的關(guān)鍵。我用比較通俗的方式拆解一下傳統(tǒng)的語(yǔ)義分割網(wǎng)絡(luò)一般有兩種思路一種是用編碼器把特征越采越深再用解碼器恢復(fù)分辨率比如UNet、DeepLab系列問(wèn)題在于恢復(fù)過(guò)程中會(huì)丟失很多邊緣細(xì)節(jié)另一種是用高分辨率特征做全程等分辨率比如HRNet效果好但計(jì)算量很大。PIDNet走的是第三種路線用輕量級(jí)的骨干網(wǎng)絡(luò)提取基礎(chǔ)特征然后分三條分支并行處理。具體來(lái)說(shuō)P分支Proportional負(fù)責(zé)接收大感受野的上下文信息適合識(shí)別“這是一個(gè)道路區(qū)域”I分支Integral負(fù)責(zé)保持高頻細(xì)節(jié)適合識(shí)別“道路和草坪之間的精確邊界”D分支Differential則是動(dòng)態(tài)融合兩者的信息實(shí)現(xiàn)細(xì)節(jié)與語(yǔ)義的自適應(yīng)平衡。這三者的組合在理論上有點(diǎn)像控制系統(tǒng)中的PID反饋回路網(wǎng)絡(luò)在學(xué)習(xí)中能自動(dòng)調(diào)節(jié)“比例項(xiàng)”“積分項(xiàng)”“微分項(xiàng)”的強(qiáng)度這也是它名字的由來(lái)。在具體實(shí)現(xiàn)層面PIDNet通過(guò)Bag和Head兩個(gè)模塊來(lái)管理三條分支的信息交換。Bag模塊在訓(xùn)練過(guò)程中累積整合不同分支的特征Head模塊則在最后進(jìn)行上采樣和類(lèi)別預(yù)測(cè)。這種設(shè)計(jì)的好處是不同分支可以在不同尺度上工作而融合機(jī)制保證它們不會(huì)信息脫節(jié)。3.2 損失函數(shù)與輔助監(jiān)督很多同學(xué)訓(xùn)練語(yǔ)義分割模型時(shí)只關(guān)注最終的交叉熵?fù)p失但對(duì)PIDNet這種三分支架構(gòu)來(lái)說(shuō)分支級(jí)別的輔助監(jiān)督是保證性能的重要一環(huán)。如果只用最終輸出loss回傳梯度P分支和I分支學(xué)到的特征可能不夠分化最后退化成同一個(gè)模型失去三路并行互補(bǔ)的意義。我在實(shí)際訓(xùn)練中采用了主損失和輔助損失疊加的方式。具體做法是從D分支的融合特征輸出主分割結(jié)果計(jì)算主損失同時(shí)把P分支和I分支的中間輸出也接上分割頭計(jì)算輔助損失最終總損失是主損失加兩個(gè)輔助損失的加權(quán)和。論文中一般用0.5或0.25作為輔助損失的權(quán)重系數(shù)我實(shí)測(cè)下來(lái)0.5的效果會(huì)更穩(wěn)定一些尤其是在訓(xùn)練的前期階段輔助監(jiān)督能明顯加速分支分化。這里有一個(gè)容易被忽略的細(xì)節(jié)輔助損失只在訓(xùn)練時(shí)啟用推理時(shí)只保留D分支的輸出結(jié)果。因?yàn)橛?xùn)練時(shí)需要回傳梯度來(lái)指導(dǎo)分支學(xué)習(xí)但推理時(shí)分支的中間結(jié)果沒(méi)有獨(dú)立應(yīng)用場(chǎng)景。這一點(diǎn)在代碼層面的實(shí)現(xiàn)方式直接決定了訓(xùn)練和驗(yàn)證時(shí)的模型結(jié)構(gòu)差異所以要記得做切換。4. 數(shù)據(jù)準(zhǔn)備與訓(xùn)練配置4.1 數(shù)據(jù)集準(zhǔn)備細(xì)節(jié)PIDNet最常用的數(shù)據(jù)集是Cityscapes但Cityscapes的標(biāo)注文件組織方式有一定特殊之處。它的目錄結(jié)構(gòu)通常是cityscapes/ ├── leftImg8bit/ │ ├── train/ │ └── val/ └── gtFine/ ├── train/ └── val/而PIDNet代碼一般要求的是pix_annotation格式也就是每個(gè)圖片對(duì)應(yīng)一個(gè)和原圖同尺寸的標(biāo)注PNG。Cityscapes官方給的gtFine是polygon格式JSON需要先轉(zhuǎn)換。我不是第一次在這上面栽跟頭。建議參考Cityscapes官方腳本或網(wǎng)上開(kāi)源的轉(zhuǎn)換工具將gtCoarse和gtFine轉(zhuǎn)換成trainId標(biāo)注而不是直接用原始ID。這里特別強(qiáng)調(diào)一下trainId和原始ID的區(qū)別關(guān)系到最終的評(píng)估指標(biāo)轉(zhuǎn)換錯(cuò)了的話mIoU的結(jié)果會(huì)非常奇怪。如果你想用VOC數(shù)據(jù)集替代那要提前確認(rèn)類(lèi)別標(biāo)號(hào)、忽略索引等約定是否和PIDNet的默認(rèn)配置一致。PIDNet通常默認(rèn)19個(gè)類(lèi)別加一個(gè)背景如果你用VOC的20類(lèi)或者21類(lèi)需要同步修改模型末端的分類(lèi)頭數(shù)量和損失函數(shù)的類(lèi)別權(quán)重否則會(huì)直接報(bào)維度不匹配的錯(cuò)。4.2 訓(xùn)練參數(shù)配置在正式開(kāi)訓(xùn)之前有相當(dāng)多的參數(shù)需要根據(jù)你的硬件條件調(diào)整。我梳理一下影響較大的幾個(gè)輸入分辨率Cityscapes標(biāo)準(zhǔn)是1024x1024。如果你的顯存不夠可以降到768x768或640x640但注意評(píng)估時(shí)最好保持訓(xùn)練分辨率否則性能差異不好比較。Batch Size顯存有限的情況下建議2-4并用梯度累積來(lái)模擬較大的batch size。梯度累積步數(shù)的計(jì)算方式是期望batch size除以單次GPU batch size比如想模擬batch size等于8單卡只能放下2那就設(shè)置累積步數(shù)為4。學(xué)習(xí)率策略建議使用多項(xiàng)式學(xué)習(xí)率衰減poly公式是base_lr * (1 - iter/total_iter)^power其中power通常設(shè)為0.9。初始學(xué)習(xí)率在batch size為8時(shí)推薦0.01左右如果你的batch size較小需要相應(yīng)降低學(xué)習(xí)率大致按線性縮放規(guī)律。Epoch數(shù)與迭代數(shù)Cityscapes全量數(shù)據(jù)訓(xùn)練的話通常跑240個(gè)epoch也就是大概12萬(wàn)步迭代。如果你是先用子集跑通流程建議設(shè)置一個(gè)較小的迭代數(shù)比如2000步確認(rèn)loss曲線在正常下降后再跑完整訓(xùn)練。我建議把訓(xùn)練過(guò)程中的日志保存下來(lái)包括每個(gè)iteration的loss值、當(dāng)前的mIoU如果做了驗(yàn)證。這不僅是排查問(wèn)題的依據(jù)也是后續(xù)對(duì)比改進(jìn)效果的關(guān)鍵數(shù)據(jù)。5. 代跑訓(xùn)練實(shí)操記錄5.1 訓(xùn)練啟動(dòng)與監(jiān)控我以Cityscapes數(shù)據(jù)集為例整理了一份實(shí)際的訓(xùn)練啟動(dòng)命令。假設(shè)你已經(jīng)準(zhǔn)備好了數(shù)據(jù)且配置文件位于configs/pidnet/pidnet_s_cityscapes.yaml# 單卡訓(xùn)練 python train.py --config configs/pidnet/pidnet_s_cityscapes.yaml # 多卡分布式訓(xùn)練 python -m torch.distributed.launch --nproc_per_node4 train.py \ --config configs/pidnet/pidnet_s_cityscapes.yaml啟動(dòng)之后不要急著離開(kāi)終端先觀察前30個(gè)iteration的輸出。正常情況下會(huì)看到loss逐漸從初始值回落如果loss直接變成NaN或者穩(wěn)在一個(gè)非常高的數(shù)值不動(dòng)說(shuō)明某個(gè)環(huán)節(jié)出了問(wèn)題。最可能的原因是學(xué)習(xí)率設(shè)置過(guò)大或者數(shù)據(jù)加載過(guò)程中出現(xiàn)了異常的標(biāo)注值。訓(xùn)練過(guò)程中的實(shí)時(shí)監(jiān)控也很關(guān)鍵。我一般會(huì)開(kāi)兩個(gè)窗口一個(gè)跑訓(xùn)練另一個(gè)用tensorboard或者直接看一眼日志文件。PIDNet代碼中通常提供了tensorboard記錄支持你可以在啟動(dòng)命令里加上日志路徑參數(shù)然后實(shí)時(shí)查看loss曲線和驗(yàn)證集上的mIoU變化曲線tensorboard --logdir/path/to/pidnet/output/tensorboard5.2 驗(yàn)證評(píng)估與可視化訓(xùn)練到一半或者結(jié)束后你肯定想知道當(dāng)前分割效果好到什么程度了。PIDNet代碼中一般會(huì)提供驗(yàn)證腳本它和訓(xùn)練腳本是分開(kāi)的。驗(yàn)證時(shí)的幾個(gè)注意事項(xiàng)如下驗(yàn)證集的圖片尺寸很多人在驗(yàn)證時(shí)直接用原始尺寸輸入但要注意如果你的模型在1024x1024分辨率下訓(xùn)練直接輸入2048x1024的原始圖會(huì)導(dǎo)致BatchNorm的表現(xiàn)方差較大特別是當(dāng)驗(yàn)證集的batch size設(shè)為1時(shí)。我一般會(huì)先做一次resize將短邊縮放到1024再送入模型。多尺度評(píng)估如果想得到更高精度的評(píng)估結(jié)果可以嘗試多尺度推理加翻轉(zhuǎn)集成。比如在0.5、0.75、1.0、1.25、1.5五種尺度下分別推理再把結(jié)果融合。這是個(gè)提升mIoU的小技巧一般能漲0.5到1個(gè)點(diǎn)??梢暬Y(jié)果除了數(shù)值指標(biāo)務(wù)必輸出幾張預(yù)測(cè)圖看看肉眼效果。mIoU有時(shí)候漲了但邊緣部分可能變得模糊不清只有通過(guò)可視化才能發(fā)現(xiàn)這些問(wèn)題。驗(yàn)證腳本跑完之后會(huì)輸出具體的類(lèi)別IoU和mIoU。我要求自己不僅僅記錄最終的mIoU數(shù)值還要保存一份分類(lèi)別的IoU表格。原因很簡(jiǎn)單改進(jìn)模型時(shí)你需要注意低類(lèi)別IoU才能針對(duì)性設(shè)計(jì)優(yōu)化方案。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 訓(xùn)練階段常見(jiàn)的工程類(lèi)問(wèn)題我整理了幾個(gè)高頻問(wèn)題你如果遇到了可以直接對(duì)照排查。**問(wèn)題一編譯擴(kuò)展算子失敗。**報(bào)錯(cuò)信息會(huì)包含類(lèi)似error: identifier AT_CHECK is undefined的信息。原因是PyTorch版本和代碼的API版本不匹配。AT_CHECK是舊版PyTorch的宏新版換成了TORCH_CHECK。解決辦法是改代碼里的宏定義或者直接換到和代碼適配的PyTorch版本。**問(wèn)題二訓(xùn)練時(shí)顯存OOM。**這種情況非常常見(jiàn)尤其是你堅(jiān)持1024分辨率加batch size 4時(shí)。我的建議順序是先降batch size到2再考慮降分辨率到768最后才是換模型尺寸。PIDNet有S、M、L三個(gè)版本S版本大約是堆半層參數(shù)量顯存壓力最小。如果S版本還在OOM那說(shuō)明顯卡確實(shí)不太夠考慮梯度累積或混合精度訓(xùn)練吧。**問(wèn)題三Loss變成NaN。**可能原因比較多常見(jiàn)的有學(xué)習(xí)率過(guò)高、數(shù)據(jù)中存在異常標(biāo)注、混合精度訓(xùn)練的梯度縮放參數(shù)設(shè)置不當(dāng)。我的排查路徑是先把學(xué)習(xí)率降到當(dāng)前值的十分之一跑100步試試如果還不行關(guān)閉混合精度再檢查數(shù)據(jù)加載部分的標(biāo)注歸一化是否出了問(wèn)題。**問(wèn)題四mIoU很低比如個(gè)位數(shù)。**這個(gè)一般都和標(biāo)簽不匹配有關(guān)。Cityscapes的標(biāo)簽有原始ID和trainId的區(qū)別很多類(lèi)別在評(píng)估時(shí)是要合并的。如果你把原始ID當(dāng)成類(lèi)別標(biāo)簽輸入到模型所有類(lèi)是混亂的模型自然學(xué)不會(huì)。另外忽略索引通常設(shè)為255有沒(méi)有正確設(shè)置也很關(guān)鍵該忽略的區(qū)域如果不忽略會(huì)嚴(yán)重拉高loss干擾訓(xùn)練。6.2 訓(xùn)練效果不好時(shí)的排查思路訓(xùn)練流程沒(méi)問(wèn)題但效果不佳這個(gè)問(wèn)題比報(bào)錯(cuò)更讓人頭疼。我的建議是分維度排查。**先看訓(xùn)練集上的表現(xiàn)。**如果訓(xùn)練集上的mIoU都不高說(shuō)明模型欠擬合重點(diǎn)調(diào)整學(xué)習(xí)率、訓(xùn)練時(shí)長(zhǎng)、數(shù)據(jù)增強(qiáng)策略。如果訓(xùn)練集上mIoU很高但驗(yàn)證集很低那就是過(guò)擬合考慮加數(shù)據(jù)增強(qiáng)、加大正則化、增加訓(xùn)練數(shù)據(jù)。**再看loss曲線的下降趨勢(shì)。**理想的loss曲線應(yīng)該是一個(gè)平滑下降的曲線如果loss曲線反復(fù)震蕩很可能是學(xué)習(xí)率太大或者batch size太小。如果loss下降得特別慢基礎(chǔ)學(xué)習(xí)率可能太低了注意poly學(xué)習(xí)率策略在訓(xùn)練中后期從宏觀上看下降速度會(huì)變慢這是正常的。**最后檢查數(shù)據(jù)加載是否正常。**我在訓(xùn)練時(shí)會(huì)把第一個(gè)batch的輸入圖像和標(biāo)注保存下來(lái)人工看一眼有沒(méi)有錯(cuò)位、錯(cuò)標(biāo)注、顏色反轉(zhuǎn)這些問(wèn)題。數(shù)據(jù)管線出問(wèn)題的概率不低于模型問(wèn)題而且排查起來(lái)比模型問(wèn)題更費(fèi)時(shí)間。我的習(xí)慣是第一時(shí)間就把可視化腳本寫(xiě)出來(lái)后續(xù)每次換數(shù)據(jù)集都能直接復(fù)用。7. 改進(jìn)創(chuàng)新方向建議7.1 基于細(xì)節(jié)捕捉的小尺度優(yōu)化思路PIDNet的I分支是保持高分辨率細(xì)節(jié)的關(guān)鍵但這個(gè)分支在原始設(shè)計(jì)里比較簡(jiǎn)單主要由基本的連續(xù)卷積堆疊構(gòu)成。我的建議之一是給I分支增加一個(gè)輕量級(jí)的注意力模塊比如通道注意力結(jié)合空間注意力的組合讓小分支自適應(yīng)地增強(qiáng)重要的細(xì)節(jié)區(qū)域。我自己試過(guò)一種方法在I分支的末端疊加一個(gè)特征選擇模塊讓模型自己學(xué)習(xí)哪些細(xì)粒度特征需要傳遞到D分支。具體實(shí)現(xiàn)時(shí)可以用一個(gè)1x1卷積生成一個(gè)門(mén)控權(quán)重再對(duì)原始特征做逐元素乘法。增加的計(jì)算量很小但確實(shí)能在邊界清晰度上看到改善。這個(gè)方向尤其適合對(duì)道路邊緣、車(chē)道線、細(xì)小物體分割效果有要求的場(chǎng)景比如自動(dòng)駕駛的車(chē)道分割和可行駛區(qū)域檢測(cè)。7.2 面向?qū)崟r(shí)應(yīng)用的高效架構(gòu)調(diào)整方向PIDNet本身已經(jīng)是在追求精度和速度的平衡但你落地時(shí)往往會(huì)面對(duì)更嚴(yán)格的算力限制這時(shí)可以考慮以下改進(jìn)方向?qū)⑻卣魈崛〔糠痔鎿Q成更輕量的骨干網(wǎng)絡(luò)。經(jīng)典的PIDNet默認(rèn)用輕量卷積網(wǎng)絡(luò)做編碼器你可以嘗試替換成MobileNetV3或EfficientNet等輕量化模型。替換時(shí)要注意保持特征的通道數(shù)與后續(xù)Bag模塊的輸入要求對(duì)齊。我測(cè)試過(guò)一個(gè)方案計(jì)算量能下降約三成mIoU只掉零點(diǎn)幾個(gè)點(diǎn)在邊緣設(shè)備上效果很理想。對(duì)三個(gè)分支做動(dòng)態(tài)裁剪??梢园凑漳P驮诓煌瑢蛹?jí)的特征重要度動(dòng)態(tài)決定跳過(guò)某些冗余分支的計(jì)算。這個(gè)思路實(shí)現(xiàn)起來(lái)比較復(fù)雜但如果你對(duì)算子級(jí)別的性能優(yōu)化有經(jīng)驗(yàn)收益會(huì)很可觀。把注意力模塊加在Bag融合部分。原本的Bag模塊主要是簡(jiǎn)單的特征疊加或拼接操作你可以嘗試把特征拼接后接入一個(gè)輕量級(jí)的注意力模塊重新加權(quán)不同分支的特征而不是簡(jiǎn)單直接求和。這個(gè)小改動(dòng)在多個(gè)數(shù)據(jù)集上有可復(fù)現(xiàn)的提升。7.3 訓(xùn)練策略與數(shù)據(jù)優(yōu)化方向模型結(jié)構(gòu)只是事情的一半訓(xùn)練策略的優(yōu)化空間同樣值得重視。基于我在多輪實(shí)驗(yàn)中的經(jīng)驗(yàn)以下幾個(gè)手段比較有效。**一是使用更強(qiáng)的數(shù)據(jù)增強(qiáng)策略。**除了基礎(chǔ)的隨機(jī)翻轉(zhuǎn)、縮放和裁剪可以嘗試顏色抖動(dòng)、高斯模糊、隨機(jī)遮擋等更強(qiáng)力的增強(qiáng)方式這對(duì)提升模型泛化能力非常有幫助。但要注意增強(qiáng)強(qiáng)度不是越大越好過(guò)強(qiáng)的增強(qiáng)會(huì)讓模型在驗(yàn)證集上產(chǎn)生偏差我通常采用溫和增強(qiáng)短暫預(yù)熱的方式。**二是加入知識(shí)蒸餾。**用一個(gè)更大更強(qiáng)的Teacher模型比如PIDNet-L來(lái)指導(dǎo)Student模型比如PIDNet-S訓(xùn)練。具體做法是讓Student模型的輸出分布盡量接近Teacher模型的輸出分布同時(shí)保持和真實(shí)標(biāo)簽的交叉熵。我的經(jīng)驗(yàn)是蒸餾出的Student模型要比直接訓(xùn)練同一Student模型高出2%-3%的mIoU同時(shí)推理速度完全不變。**三是引入半監(jiān)督訓(xùn)練思路。**如果你的預(yù)算不夠采集大量昂貴標(biāo)注數(shù)據(jù)可以利用無(wú)標(biāo)簽數(shù)據(jù)做一致性正則化對(duì)不同擾動(dòng)下的預(yù)測(cè)結(jié)果保持一致性。這類(lèi)方法在語(yǔ)義分割方向上已經(jīng)比較成熟對(duì)PIDNet這種三分支結(jié)構(gòu)的模型同樣適用。我建議先從簡(jiǎn)單的弱增強(qiáng)和強(qiáng)增強(qiáng)一致性學(xué)習(xí)入手再逐步添加更多的輔助約束。這些改進(jìn)思路都不是特別復(fù)雜的技術(shù)體操而是基于對(duì)PIDNet結(jié)構(gòu)特點(diǎn)的針對(duì)性?xún)?yōu)化。你在做改進(jìn)實(shí)驗(yàn)時(shí)我的建議是不要同時(shí)疊加太多改動(dòng)否則一旦效果變差很難定位是哪個(gè)改動(dòng)導(dǎo)致的回退。逐步做單點(diǎn)更改、單點(diǎn)驗(yàn)證是最穩(wěn)妥也最高效的推進(jìn)方式。寫(xiě)在后面PIDNet在我做過(guò)的語(yǔ)義分割項(xiàng)目里屬于那種“越改越有感覺(jué)”的模型。它的三分支結(jié)構(gòu)把細(xì)節(jié)和語(yǔ)義分開(kāi)處理這一設(shè)計(jì)在背后給了后人很大的調(diào)整空間。從環(huán)境配置到訓(xùn)練調(diào)優(yōu)再到結(jié)構(gòu)改動(dòng)每一步都需要細(xì)心排查但每一步的回報(bào)也都直接反映在數(shù)據(jù)上。最后分享一個(gè)小習(xí)慣我每次做模型改進(jìn)實(shí)驗(yàn)都會(huì)把改動(dòng)點(diǎn)、實(shí)驗(yàn)配置、結(jié)果指標(biāo)記錄在表格里哪怕是一個(gè)很小的參數(shù)調(diào)整也不放過(guò)。等整個(gè)實(shí)驗(yàn)周期結(jié)束后往回看這份記錄的價(jià)值往往比單純的技術(shù)方案還要大。它能讓你的改進(jìn)路徑清晰可見(jiàn)也能避免重復(fù)試錯(cuò)。如果你正準(zhǔn)備拿PIDNet做實(shí)驗(yàn)希望這篇文章能幫你少走一些彎路。