:從VOC轉(zhuǎn)YOLO到Y(jié)OLOv8訓(xùn)練實(shí)戰(zhàn))
簡(jiǎn)介這份數(shù)據(jù)集面向光伏運(yùn)維、目標(biāo)檢測(cè)及深度學(xué)習(xí)入門(mén)者用于太陽(yáng)能光伏板表面灰塵Dirt檢測(cè)模型的訓(xùn)練與驗(yàn)證。資源共包含1463張jpg圖片每張均對(duì)應(yīng)Pascal VOC格式的xml標(biāo)注和YOLO格式的txt標(biāo)注總計(jì)標(biāo)注目標(biāo)框6822個(gè)類(lèi)別僅Dirt一項(xiàng)采用labelImg工具以矩形框方式標(biāo)注。壓縮包內(nèi)文件總數(shù)2000主要由1463個(gè)xml、537個(gè)txt及圖片文件組成整體大小75.98MB目錄按圖片編號(hào)組織可直接對(duì)接YOLO等常見(jiàn)訓(xùn)練流程。目前已有537人瀏覽/學(xué)習(xí)數(shù)據(jù)熱度適中。數(shù)據(jù)集標(biāo)注規(guī)范、邊界框定位準(zhǔn)確適合快速開(kāi)展光伏積灰檢測(cè)實(shí)驗(yàn)或作為目標(biāo)檢測(cè)教學(xué)案例作者同時(shí)提示圖片重復(fù)度較高且不對(duì)特定模型權(quán)重精度作保證因此更適合對(duì)樣本多樣性要求不高、追求低成本獲取標(biāo)注數(shù)據(jù)的場(chǎng)景正式項(xiàng)目使用前建議抽樣核查。1. 光伏板積灰檢測(cè)數(shù)據(jù)集1463張單類(lèi)別能撐起什么場(chǎng)景光伏電站最頭疼的運(yùn)維問(wèn)題之一就是積灰。板面落灰之后發(fā)電量直線(xiàn)掉人工巡檢又慢又貴所以很多團(tuán)隊(duì)轉(zhuǎn)向“無(wú)人機(jī)巡檢 目標(biāo)檢測(cè)”的路線(xiàn)。這個(gè)數(shù)據(jù)集包就是為這條路準(zhǔn)備的1463張光伏板圖像只標(biāo)注一個(gè)類(lèi)別——積灰同時(shí)提供VOC和YOLO兩套標(biāo)注格式。如果你正在做光伏積灰檢測(cè)相關(guān)的yolo入門(mén)項(xiàng)目或者手里有電站圖像但不知道怎么組織成能訓(xùn)練的數(shù)據(jù)集這套數(shù)據(jù)可以直接作為起點(diǎn)。它適合兩類(lèi)人一是想驗(yàn)證“積灰檢測(cè)到底能不能用視覺(jué)做”的工程師二是已經(jīng)在跑yolov8訓(xùn)練自己的數(shù)據(jù)集但被數(shù)據(jù)格式卡住的新手。不適合誰(shuí)想做灰塵厚度分級(jí)、想?yún)^(qū)分鳥(niǎo)糞和灰、想判斷清洗優(yōu)先級(jí)的人單類(lèi)別標(biāo)注撐不起這種需求。2. VOC轉(zhuǎn)YOLO格式歸一化坐標(biāo)是所有轉(zhuǎn)換腳本的核心拿到一個(gè)同時(shí)帶VOC和YOLO格式的數(shù)據(jù)集第一件事不是急著訓(xùn)練而是搞清楚兩種格式的坐標(biāo)邏輯。Pascal VOC用XML保存絕對(duì)像素坐標(biāo)YOLO用txt保存相對(duì)坐標(biāo)。兩套格式在數(shù)據(jù)集的目錄結(jié)構(gòu)、標(biāo)注內(nèi)容、類(lèi)別編號(hào)方式上完全不同轉(zhuǎn)換腳本寫(xiě)錯(cuò)一個(gè)分母檢測(cè)框就全偏。2.1 Pascal VOC的XML里到底存了什么VOC格式的目錄通常長(zhǎng)這樣VOCdevkit/ └── VOC2007/ ├── Annotations/ # 每個(gè)jpg對(duì)應(yīng)一個(gè)xml ├── JPEGImages/ # 原圖 └── ImageSets/Main # 訓(xùn)練/驗(yàn)證劃分文件一個(gè)標(biāo)注文件內(nèi)關(guān)鍵節(jié)點(diǎn)有三個(gè)size原圖寬高、object的name類(lèi)別名、bndboxxmin、ymin、xmax、ymax四個(gè)絕對(duì)坐標(biāo)。比如annotation size width1920/width height1080/height /size object namedust/name bndbox xmin300/xmin ymin450/ymin xmax720/xmax ymax610/ymax /bndbox /object /annotation這里的坐標(biāo)是像素值直接基于原圖分辨率不需要任何額外計(jì)算。老工具鏈、標(biāo)注軟件很多都吃這套格式這也是為什么很多數(shù)據(jù)分發(fā)喜歡附帶VOC版本——通用性最好。2.2 YOLO的txt標(biāo)注為什么必須歸一化YOLO格式不關(guān)心原圖是多大每個(gè)目標(biāo)單獨(dú)一行五個(gè)數(shù)class_id x_center y_center width height后四個(gè)全部歸一化到[0,1]。歸一化意味著坐標(biāo)除以原圖寬高模型在訓(xùn)練時(shí)不用處理不同分辨率帶來(lái)的尺度問(wèn)題這是YOLO系算法能批量縮放輸入圖的基礎(chǔ)。注意一個(gè)細(xì)節(jié)YOLO的中心坐標(biāo)和寬高都是相對(duì)值所以轉(zhuǎn)換公式是x_center (xmin xmax) / 2 / image_width y_center (ymin ymax) / 2 / image_height width (xmax - xmin) / image_width height (ymax - ymin) / image_height很多轉(zhuǎn)換翻車(chē)就翻在忘記除以寬高或者只歸一化了中心點(diǎn)而忘記把框?qū)捀咭矚w一化。另外類(lèi)別ID必須從0開(kāi)始單類(lèi)別數(shù)據(jù)集就是0不是1。2.3 用Python把VOC轉(zhuǎn)成YOLO的常用寫(xiě)法如果你拿到手的數(shù)據(jù)只有VOC格式或者想驗(yàn)證數(shù)據(jù)集里自帶YOLO標(biāo)注是否正確可以自己寫(xiě)一個(gè)轉(zhuǎn)換腳本。下面這個(gè)是我常用的版本import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path: Path, out_txt_path: Path, class_names: list): tree ET.parse(xml_path) root tree.getroot() # 讀原圖尺寸這是歸一化的分母讀錯(cuò)全盤(pán)皆輸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 全部轉(zhuǎn)成歸一化坐標(biāo) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 限制在[0,1]區(qū)間防止標(biāo)注越界影響訓(xùn)練 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_w min(max(box_w, 0.0), 1.0) box_h min(max(box_h, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) out_txt_path.write_text(\n.join(lines)) class_names [dust] # 單類(lèi)別ID為0 xml_dir Path(Annotations) txt_dir Path(labels) for xml_file in xml_dir.glob(*.xml): voc_to_yolo(xml_file, txt_dir / f{xml_file.stem}.txt, class_names)邏輯說(shuō)明腳本先解析XML讀原圖寬高再遍歷每個(gè)object取類(lèi)別和邊框最后把絕對(duì)坐標(biāo)轉(zhuǎn)成相對(duì)坐標(biāo)寫(xiě)入txt。class_names.index(name)決定了類(lèi)別ID如果你把類(lèi)別名列表寫(xiě)成[dust, dust2]但數(shù)據(jù)里只有dust一個(gè)類(lèi)訓(xùn)練不會(huì)報(bào)錯(cuò)只是模型會(huì)以為存在兩個(gè)類(lèi)導(dǎo)致類(lèi)別數(shù)不匹配問(wèn)題。參數(shù)說(shuō)明Class_names的順序就是ID順序必須和后續(xù)訓(xùn)練配置文件保持一致min(max(...), 1.0)是為了防止標(biāo)注數(shù)據(jù)越界導(dǎo)致訓(xùn)練時(shí)loss異常。如果你發(fā)現(xiàn)轉(zhuǎn)換后的框整體偏移先查size節(jié)點(diǎn)里寬高和實(shí)際圖片分辨率是否一致。3. 用YOLOv8訓(xùn)練自己的積灰數(shù)據(jù)集從解壓到出結(jié)果的完整命令格式確認(rèn)沒(méi)問(wèn)題之后下一步就是把數(shù)據(jù)集喂給模型?,F(xiàn)階段用ultralytics的YOLOv8訓(xùn)練自己的數(shù)據(jù)集是主流做法社區(qū)資料多、命令收斂、報(bào)錯(cuò)也好搜。整個(gè)流程分三步整理目錄、寫(xiě)data.yaml、跑訓(xùn)練命令。3.1 數(shù)據(jù)集目錄和data.yaml怎么組織先把壓縮包解壓成一個(gè)固定結(jié)構(gòu)的目錄YOLO系列訓(xùn)練器對(duì)目錄有默認(rèn)約定。我一般這樣組織solar_dust/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/每個(gè)jpg在images/train下同名txt必須在labels/train下。圖片和標(biāo)注文件名一一對(duì)應(yīng)缺一個(gè)txt就少一個(gè)訓(xùn)練樣本。如果解壓出來(lái)的數(shù)據(jù)集是VOC的ImageSets劃分你需要先按劃分文件把圖片和XML拷到對(duì)應(yīng)目錄。然后寫(xiě)data.yamlpath: /data/solar_dust # 數(shù)據(jù)集絕對(duì)路徑 train: images/train # 相對(duì)于path val: images/val # 相對(duì)于path names: 0: dust # 類(lèi)別ID從0開(kāi)始參數(shù)說(shuō)明path盡量寫(xiě)絕對(duì)路徑避免相對(duì)路徑在不同工作目錄下解析錯(cuò)誤names的鍵是類(lèi)別ID0對(duì)應(yīng)第2章轉(zhuǎn)換腳本里的class_name順序單類(lèi)別場(chǎng)景names只有一項(xiàng)模型輸出維度會(huì)相應(yīng)收斂推理時(shí)只需要一個(gè)置信度閾值。3.2 訓(xùn)練、驗(yàn)證一條命令跑通目錄和yaml就緒后直接跑yolo detect train datadata.yaml modelyolov8s.pt epochs120 imgsz640 batch16 patience20 projectruns namesolar_dust參數(shù)說(shuō)明modelyolov8s.pt是加載COCO預(yù)訓(xùn)練權(quán)重對(duì)小數(shù)據(jù)集有幫助不建議從頭訓(xùn)練imgsz640是訓(xùn)練輸入尺寸光伏板圖像通常分辨率很高1920以上但訓(xùn)練時(shí)YOLO會(huì)做縮放640是速度和精度折中batch16要看顯存8G顯存可以降到8patience20表示20個(gè)epoch沒(méi)有提升就自動(dòng)停防止過(guò)擬合。如果你用的是ultralytics庫(kù)而不是CLI等價(jià)寫(xiě)法是from ultralytics import YOLO model YOLO(yolov8s.pt) model.train(datadata.yaml, epochs120, imgsz640, batch16, patience20)跑起來(lái)之后終端會(huì)打印每個(gè)epoch的box_loss、cls_loss、dfl_loss和mAP指標(biāo)。第一次跑完你會(huì)在runs/solar_dust下看到權(quán)重文件best.pt和last.pt以及一堆可視化輸出。3.3 訓(xùn)練輸出的幾個(gè)關(guān)鍵文件怎么看訓(xùn)練完成后重點(diǎn)看四個(gè)東西results.png整個(gè)訓(xùn)練過(guò)程的loss曲線(xiàn)和mAP曲線(xiàn)。如果epoch到中期loss就掉不下去可能是學(xué)習(xí)率或數(shù)據(jù)問(wèn)題如果驗(yàn)證曲線(xiàn)在訓(xùn)練曲線(xiàn)還在降時(shí)就開(kāi)始反彈就是過(guò)擬合。confusion_matrix.png單類(lèi)別下主要看背景誤檢情況。如果很多干凈板面被預(yù)測(cè)成積灰說(shuō)明負(fù)樣本不夠。val_batch0_pred.jpg第一張驗(yàn)證集圖像的預(yù)測(cè)可視化。這一步能最直觀(guān)地看到框有沒(méi)有偏移、積灰區(qū)域是不是被漏檢。best.pt和last.ptbest是驗(yàn)證集指標(biāo)最好的權(quán)重部署時(shí)用best。提示單類(lèi)別數(shù)據(jù)集不建議只盯mAP50-95光伏積灰檢測(cè)的現(xiàn)實(shí)需求是“框得準(zhǔn)、別誤報(bào)”mAP50更有參考價(jià)值。4. 單類(lèi)別檢測(cè)調(diào)參mAP和損失函數(shù)不看錯(cuò)方向就是白調(diào)1463張小數(shù)據(jù)集訓(xùn)練本身不難難的是驗(yàn)證集怎么分、loss怎么看、增強(qiáng)怎么開(kāi)。這幾個(gè)坑不處理訓(xùn)練日志再漂亮現(xiàn)場(chǎng)一跑就露餡。4.1 訓(xùn)練/驗(yàn)證劃分按電站分別按文件隨機(jī)分這是小數(shù)據(jù)集最容易犯的錯(cuò)誤。隨機(jī)劃分會(huì)把同一個(gè)電站、同一個(gè)組件在不同角度、光照下的照片同時(shí)分進(jìn)訓(xùn)練集和驗(yàn)證集驗(yàn)證mAP會(huì)虛高到接近1.0看起來(lái)完美部署到新電站直接崩。常見(jiàn)做法是按拍攝來(lái)源劃分。如果文件名有電站前綴比如site01_0001.jpg、site02_0012.jpg先按前綴分組再劃分from pathlib import Path import random img_files sorted(Path(images).glob(*.jpg)) grouped {} for img in img_files: site img.stem.split(_)[0] # 按文件名前綴分組 grouped.setdefault(site, []).append(img) random.seed(42) groups list(grouped.values()) random.shuffle(groups) train_ratio 0.85 train_groups groups[:int(len(groups) * train_ratio)] val_groups groups[int(len(groups) * train_ratio):] # 落盤(pán)到目錄并在labels目錄中同步對(duì)應(yīng)txt如果不能按來(lái)源分至少保證同一張圖片的變體裁剪、翻轉(zhuǎn)、濾鏡版本不跨集合。判斷標(biāo)準(zhǔn)很簡(jiǎn)單驗(yàn)證是衡量“沒(méi)見(jiàn)過(guò)的電站”能不能檢測(cè)而不是“見(jiàn)過(guò)的電站換個(gè)角度”能不能檢測(cè)。4.2 讀yolo訓(xùn)練日志box_loss、cls_loss、dfl_loss分別盯什么單類(lèi)別訓(xùn)練時(shí)三個(gè)loss的變化意義完全不同cls_loss分類(lèi)損失。單類(lèi)別下它很快會(huì)降到一個(gè)極低值因?yàn)槟P椭恍枰獏^(qū)分“前景/背景”這是正常的不用覺(jué)得詭異。box_loss定位損失決定框準(zhǔn)不準(zhǔn)。這是單類(lèi)別數(shù)據(jù)集最需要盯的指標(biāo)。如果box_loss收斂慢或震蕩優(yōu)先懷疑標(biāo)注框質(zhì)量問(wèn)題。dfl_lossDistributed Focal Loss影響邊界框回歸的分布。它和box_loss趨勢(shì)一致如果兩者背離檢查學(xué)習(xí)率是否過(guò)大。另外單類(lèi)別模型的驗(yàn)證集mAP很可能遠(yuǎn)高于多類(lèi)別因?yàn)橹挥幸粋€(gè)類(lèi)混淆矩陣?yán)镏挥姓?lèi)和背景兩個(gè)維度。不要因?yàn)檫@個(gè)數(shù)字高就認(rèn)為模型可以上線(xiàn)真正決定能不能用的指標(biāo)是誤報(bào)率。4.3 小數(shù)據(jù)集數(shù)據(jù)增強(qiáng)怎么開(kāi)到不翻車(chē)YOLOv8默認(rèn)開(kāi)mosaic等增強(qiáng)但對(duì)1463張的單類(lèi)別積灰數(shù)據(jù)增強(qiáng)不是越大越好。光伏板方向固定、積灰區(qū)域紋理也無(wú)明顯旋轉(zhuǎn)不變性過(guò)強(qiáng)的旋轉(zhuǎn)和色彩增強(qiáng)反而讓模型學(xué)到錯(cuò)誤特征。我常用的調(diào)整方向yolo detect train datadata.yaml modelyolov8s.pt \ epochs120 imgsz640 batch16 \ mosaic0.5 hsv_h0.01 hsv_s0.3 hsv_v0.3 degrees5 fliplr0.5參數(shù)說(shuō)明mosaic0.5把默認(rèn)的1.0降下來(lái)小數(shù)據(jù)集mosaic太猛會(huì)過(guò)度混合背景degrees5只允許小角度旋轉(zhuǎn)光伏板要么水平要么小傾角不需要90度旋轉(zhuǎn)增強(qiáng)fliplr0.5水平翻轉(zhuǎn)保留對(duì)積灰檢測(cè)基本無(wú)副作用。如果你發(fā)現(xiàn)驗(yàn)證集mAP波動(dòng)大可以進(jìn)一步把mosaic降到0.2甚至關(guān)掉用mosaic0.0鎖定對(duì)比。4.4 置信度閾值和NMS的Iou閾值怎么定訓(xùn)練完成后推理時(shí)的默認(rèn)閾值是conf0.25, iou0.45但這套默認(rèn)值在單類(lèi)別積灰檢測(cè)里通常偏松。積灰檢測(cè)一旦誤檢運(yùn)維人員就得跑一趟現(xiàn)場(chǎng)誤報(bào)代價(jià)高。我一般會(huì)先用驗(yàn)證集掃一遍yolo predict modelbest.pt sourceval_images conf0.4 iou0.5然后看預(yù)測(cè)結(jié)果里的誤檢數(shù)量。confidence從0.3到0.5逐檔試選擇誤檢數(shù)量和召回率平衡點(diǎn)。太陽(yáng)能板面反光、邊框陰影、雜草遮擋都會(huì)產(chǎn)生高分誤檢閾值設(shè)到0.4左右通常能壓掉大部分但也會(huì)犧牲部分小面積積灰區(qū)域需要結(jié)合現(xiàn)場(chǎng)接受度來(lái)定。5. 積灰檢測(cè)常見(jiàn)問(wèn)題與避坑標(biāo)注、轉(zhuǎn)換、訓(xùn)練三類(lèi)翻車(chē)現(xiàn)場(chǎng)這個(gè)方向我自己跑的時(shí)候踩過(guò)不少坑挑五個(gè)最典型的寫(xiě)出來(lái)。每條都是真實(shí)的現(xiàn)場(chǎng)問(wèn)題按“現(xiàn)象→原因→解決”寫(xiě)方便你遇到時(shí)對(duì)號(hào)入座。5.1 VOC轉(zhuǎn)YOLO后框全偏多半是沒(méi)按原圖寬高做歸一化現(xiàn)象訓(xùn)練時(shí)loss正常下降但可視化預(yù)測(cè)框整體向左上或右下偏移框的尺寸比例明顯不對(duì)。 原因轉(zhuǎn)換腳本沒(méi)有讀size節(jié)點(diǎn)或者size里的寬高和實(shí)際圖片分辨率不一致。還有一種可能是XML里存的是縮放后的標(biāo)注坐標(biāo)但圖片本身沒(méi)縮放。 解決先用PIL讀圖片寬高與XML的size節(jié)點(diǎn)逐一比對(duì)轉(zhuǎn)換時(shí)統(tǒng)一用圖片實(shí)際寬高做分母。我習(xí)慣直接在轉(zhuǎn)換腳本里同時(shí)讀原圖from PIL import Image img_w, img_h Image.open(jpg_path).size用圖片真實(shí)寬高代替XML里的size能避免源標(biāo)注文件本身沒(méi)更新分辨率的問(wèn)題。5.2 訓(xùn)練loss收斂但驗(yàn)證mAP很低先查數(shù)據(jù)泄漏和樣本不平衡現(xiàn)象訓(xùn)練集mAP能到0.95以上驗(yàn)證集mAP只有0.6甚至更低且波動(dòng)大。 原因兩個(gè)可能。第一個(gè)是驗(yàn)證集場(chǎng)景和訓(xùn)練集差異過(guò)大比如訓(xùn)練集全是地面電站平視角度驗(yàn)證集全是無(wú)人機(jī)俯拍第二個(gè)是積灰面積大的極端樣本都集中在訓(xùn)練集驗(yàn)證集只剩小面積積灰。 解決按4.1的分組方式重新劃分確保每個(gè)劃分里都有不同面積積灰的樣本。另外檢查驗(yàn)證集是否存在難樣本比如灰塵顏色和板面底色接近的低對(duì)比度圖這類(lèi)樣本會(huì)嚴(yán)重拉低mAP但恰恰是現(xiàn)場(chǎng)最需要處理的。5.3 預(yù)測(cè)時(shí)把干凈板面誤檢測(cè)成積灰負(fù)樣本和閾值要一起調(diào)現(xiàn)象模型在正常光伏板上框出一大片“積灰”尤其反光強(qiáng)烈的時(shí)候。 原因這是單類(lèi)別檢測(cè)的老毛病——模型只在正樣本上學(xué)習(xí)沒(méi)見(jiàn)過(guò)“干凈的板面”也可以作為背景。如果積灰區(qū)域和板面清洗后的反光區(qū)域紋理相似模型就會(huì)混淆。 解決收集負(fù)樣本。不需要額外標(biāo)注直接拉一批干凈光伏板圖片放進(jìn)訓(xùn)練集不配txt標(biāo)簽?zāi)P途蜁?huì)把它們當(dāng)作背景學(xué)習(xí)。同時(shí)配合提高置信度閾值到0.4以上雙管齊下。負(fù)樣本來(lái)源可以是網(wǎng)上的清洗后電站照片也可以是自己拍的未積灰板面。5.4 類(lèi)別ID不一致導(dǎo)致訓(xùn)練報(bào)錯(cuò)單類(lèi)別也要統(tǒng)一從0開(kāi)始現(xiàn)象訓(xùn)練啟動(dòng)時(shí)報(bào)錯(cuò)提示number of classes mismatch或者模型訓(xùn)練正常但推理時(shí)把所有檢測(cè)框都?xì)w為0類(lèi)而0類(lèi)又是錯(cuò)誤的類(lèi)別。 原因VOC里的類(lèi)別名是字符串dustYOLO是整數(shù)ID。如果你在data.yaml里寫(xiě)成names: {1: dust}或者轉(zhuǎn)換腳本里用class_names.index(name)1把ID變成1就會(huì)出現(xiàn)類(lèi)別數(shù)對(duì)不上。 解決單類(lèi)別的類(lèi)別ID固定是0。檢查轉(zhuǎn)換腳本里是否加了1檢查data.yaml里names是否從0開(kāi)始兩者確認(rèn)一致再啟動(dòng)訓(xùn)練。5.5 文件名對(duì)不上導(dǎo)致圖片被跳過(guò)訓(xùn)練前先跑一遍校驗(yàn)?zāi)_本現(xiàn)象訓(xùn)練日志里顯示圖片數(shù)量比數(shù)據(jù)集少幾十張或者模型對(duì)某幾張圖完全檢測(cè)不到。 原因圖片是jpg標(biāo)簽txt名字里多了空格、后綴大小寫(xiě)不一致——比如IMG_001.JPG對(duì)應(yīng)img_001.txtWindows和Linux文件名大小寫(xiě)敏感差異會(huì)直接漏讀。 解決訓(xùn)練前先做一次強(qiáng)制校驗(yàn)from pathlib import Path for split in [train, val]: img_dir Path(fimages/{split}) lbl_dir Path(flabels/{split}) imgs {p.stem.lower(): p for p in img_dir.glob(*.jpg)} for lbl in lbl_dir.glob(*.txt): if lbl.stem.lower() not in imgs: print(f標(biāo)簽無(wú)對(duì)應(yīng)圖片: {lbl.name})這段腳本把所有文件名統(tǒng)一轉(zhuǎn)小寫(xiě)后比對(duì)能快速揪出大小寫(xiě)不一致、多余空格、后綴差異三類(lèi)問(wèn)題。跑一遍再訓(xùn)練避免靜默丟數(shù)據(jù)。6. 把檢測(cè)框變成運(yùn)維指標(biāo)積灰面積占比的現(xiàn)場(chǎng)后處理模型輸出的是一堆框運(yùn)維要的是一個(gè)可以排期的指標(biāo)。常見(jiàn)做法是用檢測(cè)框面積除以整塊光伏板面積得到“積灰面積占比”再按占比分清洗優(yōu)先級(jí)低于10%不處理10%到30%列入計(jì)劃超過(guò)30%優(yōu)先清洗。6.1 用檢測(cè)框算每塊組件的積灰面積占比思路是先檢測(cè)出組件區(qū)域再在組件內(nèi)聚合所有積灰框計(jì)算覆蓋比例。如果你的數(shù)據(jù)是單塊板面特寫(xiě)組件區(qū)域就是整張圖如果是多塊板面的巡檢圖要先拿到組件框。以下代碼按“整圖是一塊板”的假設(shè)聚合def dust_ratio(dust_boxes, img_w, img_h): panel_area img_w * img_h dust_area 0.0 for x1, y1, x2, y2 in dust_boxes: # 將歸一化坐標(biāo)轉(zhuǎn)回像素假設(shè)輸入已歸一化 px1, py1 int(x1 * img_w), int(y1 * img_h) px2, py2 int(x2 * img_w), int(y2 * img_h) dust_area (px2 - px1) * (py2 - py1) return min(dust_area / panel_area, 1.0)這段的用途是給出一個(gè)可用指標(biāo)但要注意重復(fù)檢測(cè)問(wèn)題。同一塊積灰區(qū)域被模型輸出5個(gè)重疊框時(shí)面積會(huì)被算5次。合理做法是先做NMS或者按中心點(diǎn)聚類(lèi)再把聚類(lèi)的框面積合并。6.2 導(dǎo)出ONNX并在連續(xù)幀上做置信度過(guò)濾驗(yàn)證完效果就要考慮部署。把best.pt導(dǎo)出成ONNX格式y(tǒng)olo export modelbest.pt formatonnx imgsz640導(dǎo)出后的ONNX可以在Jetson、工控機(jī)、樹(shù)莓派上跑CPU或GPU推理。現(xiàn)場(chǎng)巡檢往往是連續(xù)拍攝單幀誤檢不可避免我的習(xí)慣是連續(xù)10幀里至少7幀檢測(cè)到積灰才觸發(fā)告警用時(shí)間維度換掉一半誤報(bào)。再疊加一個(gè)“相鄰幀檢測(cè)框IoU大于0.5才認(rèn)為是同一目標(biāo)”的過(guò)濾基本能壓住偶發(fā)的光照突變誤檢。另一個(gè)低成本交叉驗(yàn)證是灰度直方圖積灰區(qū)域會(huì)讓光伏板表面灰度分布偏離干凈板面的峰值。取檢測(cè)框內(nèi)灰度均值和同組件干凈區(qū)域的灰度均值做對(duì)比兩者差異低于閾值就認(rèn)為是一次誤檢。這個(gè)方法不增加模型復(fù)雜度只作后處理但在低對(duì)比度場(chǎng)景下能救回不少誤報(bào)。我的實(shí)際經(jīng)驗(yàn)是任何檢測(cè)模型到了現(xiàn)場(chǎng)都要按電站重新校準(zhǔn)閾值別指望一次訓(xùn)練吃遍所有電站。如果你調(diào)的模型在A電站很穩(wěn)換到B電站又翻車(chē)先檢查訓(xùn)練集里有沒(méi)有B電站的樣本再懷疑模型本身。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取