能電池板YOLO高變焦檢測(cè):24577張數(shù)據(jù)集訓(xùn)練實(shí)戰(zhàn))
簡(jiǎn)介面向太陽(yáng)能光伏板檢測(cè)與YOLO模型訓(xùn)練的實(shí)際需求這份壓縮包針對(duì)高變焦太陽(yáng)能電池板圖像場(chǎng)景提供了帶有標(biāo)簽的光伏板檢測(cè)標(biāo)注數(shù)據(jù)集能幫助開(kāi)發(fā)者和研究人員快速獲得規(guī)范標(biāo)注樣本降低從圖像采集、清洗到標(biāo)注的重復(fù)工作。壓縮包為zip格式共2000個(gè)XML標(biāo)簽文件整體約620.88MBXML文件內(nèi)記錄了光伏板目標(biāo)的位置框與類別信息可配合圖像轉(zhuǎn)換為YOLO訓(xùn)練所需的數(shù)據(jù)結(jié)構(gòu)也便于腳本批量解析與格式整理。目前已有538人學(xué)習(xí)或下載適合處于中高級(jí)階段的深度學(xué)習(xí)使用者也適用于光伏電站巡檢、組件故障篩查等項(xiàng)目場(chǎng)景。數(shù)據(jù)集源于高變焦拍攝環(huán)境標(biāo)簽覆蓋不同視角、光照與背景細(xì)節(jié)豐富有利于強(qiáng)化模型對(duì)裂紋、污漬、遮擋等微小缺陷的識(shí)別能力同時(shí)打包好的目錄便于直接整理、劃分訓(xùn)練集與驗(yàn)證集幫助提升光伏板自動(dòng)檢測(cè)系統(tǒng)的迭代效率與準(zhǔn)確率有效支撐實(shí)際運(yùn)維中的缺陷定位與告警判定。1. 太陽(yáng)能電池板檢測(cè)為什么繞不開(kāi)高變焦先看清這24577張圖的真正價(jià)值光伏板檢測(cè)這幾年在無(wú)人機(jī)巡檢和分布式電站運(yùn)維里需求量很大。拿到這份帶標(biāo)簽的24577張圖像多數(shù)人會(huì)直接跑yolo訓(xùn)練但真正決定模型能不能落地的不是數(shù)量而是「高變焦」這三個(gè)字。高變焦意味著太陽(yáng)能電池板在畫(huà)面里往往很小幾十像素的板子在4K甚至8K的大圖上擠成一片反光、陰影、傾斜視角再疊上去小目標(biāo)檢測(cè)的所有難點(diǎn)基本湊齊了。這篇筆記就圍繞這個(gè)數(shù)據(jù)集講怎么把2萬(wàn)多張圖轉(zhuǎn)成能訓(xùn)練YOLO的格式、參數(shù)怎么調(diào)、坑在哪里以及最后如何用切片推理把高變焦場(chǎng)景的檢出率提上去。適合正在做光伏巡檢的工程師也適合剛?cè)腴Tyolo的人拿這套帶標(biāo)簽數(shù)據(jù)練手。2. 先弄懂?dāng)?shù)據(jù)再談?dòng)?xùn)練把24577張圖的結(jié)構(gòu)、標(biāo)簽和成像質(zhì)量摸一遍2.1 拿到壓縮包先做三件事解壓、看目錄、驗(yàn)標(biāo)簽先解壓。文件名里的「yolo算法-太陽(yáng)能電池板數(shù)據(jù)集-24577張圖像帶標(biāo)簽-太陽(yáng)能光伏板檢測(cè)-高變焦.zip」已經(jīng)點(diǎn)明這是為YOLO準(zhǔn)備的數(shù)據(jù)但壓縮包內(nèi)部的目錄結(jié)構(gòu)不能被名稱代替。我拿到手的第一件事永遠(yuǎn)是先看目錄和文件數(shù)unzip yolo算法-太陽(yáng)能電池板數(shù)據(jù)集-24577張圖像帶標(biāo)簽-太陽(yáng)能光伏板檢測(cè)-高變焦.zip -d solar_panel_dataset cd solar_panel_dataset find . -maxdepth 2 -type d find . -maxdepth 2 -type f | wc -l把壓縮包解壓到 solar_panel_dataset 目錄下用 find 列出兩層以內(nèi)的所有目錄看是否已經(jīng)按 images/ 和 labels/ 分好再用 wc -l 統(tǒng)計(jì)文件總數(shù)和標(biāo)題里的24577對(duì)一下數(shù)量對(duì)不上說(shuō)明可能有空目錄或者分批壓縮。常見(jiàn)做法是已經(jīng)分好了 train/val 子目錄但也常見(jiàn)只按原始拍攝批次分文件夾、標(biāo)簽全堆在一個(gè)目錄里的情形先摸清再動(dòng)手。第二步是驗(yàn)證標(biāo)簽?zāi)懿荒苡?。我遇到過(guò)一萬(wàn)多張圖配的標(biāo)簽文件里有不少0字節(jié)文件訓(xùn)練時(shí)模型等于把這些樣本當(dāng)成了空背景val的mAP被拉低不少。用下面的命令把空標(biāo)簽和損壞圖片都拎出來(lái)看看find labels -name *.txt -size 0 | wc -l python -c from PIL import Image import os bad [] for f in os.listdir(images): try: Image.open(os.path.join(images, f)).load() except Exception as e: bad.append((f, str(e))) print(f損壞圖片數(shù)量: {len(bad)}) for f, e in bad[:20]: print(f, e) 第一條命令統(tǒng)計(jì)空標(biāo)簽文件的數(shù)量第二條遍歷 images 目錄逐張用 Pillow 加載。加載失敗說(shuō)明文件損壞或根本不是圖這類樣本要直接剔除并同步刪除對(duì)應(yīng)的標(biāo)簽文件。這里值得多說(shuō)一句空標(biāo)簽本身并不總是要扔。如果一張圖里確實(shí)沒(méi)有光伏板它是很好的負(fù)樣本但如果空標(biāo)簽是因?yàn)闃?biāo)注時(shí)漏標(biāo)就會(huì)污染訓(xùn)練集。怎么區(qū)分下一個(gè)節(jié)結(jié)合類別統(tǒng)計(jì)一起說(shuō)。2.2 標(biāo)簽分布統(tǒng)計(jì)類別單一不代表沒(méi)坑這批數(shù)據(jù)集的檢測(cè)目標(biāo)大概率只有太陽(yáng)能電池板一類但類別單一不等于可以跳過(guò)標(biāo)簽統(tǒng)計(jì)。YOLO格式的標(biāo)簽每行是「class cx cy w h」先統(tǒng)計(jì)類別數(shù)量、框的數(shù)量和空標(biāo)簽比例import os from collections import Counter label_dir labels counts Counter() sizes [] empty_files 0 for f in os.listdir(label_dir): path os.path.join(label_dir, f) if os.path.getsize(path) 0: empty_files 1 continue with open(path) as fp: for line in fp: parts line.strip().split() if len(parts) 5: cls int(parts[0]) counts[cls] 1 w float(parts[3]) h float(parts[4]) sizes.append((w * 640, h * 640)) # 按640基準(zhǔn)近似像素寬高 print(各類別框數(shù)量:, dict(counts)) print(空標(biāo)簽文件數(shù):, empty_files) print(框總數(shù):, sum(counts.values()))這段腳本掃描 labels 目錄下所有txt按類別統(tǒng)計(jì)框數(shù)量同時(shí)按640的基準(zhǔn)把歸一化寬高換算成近似像素值。換算像素寬高是為了快速判斷小目標(biāo)占比如果一半以上的框?qū)捀叨夹∮?2像素后面訓(xùn)練時(shí) imgsz 必須加大默認(rèn)的640輸入會(huì)對(duì)這些小目標(biāo)很不友好。輸出的大致結(jié)果值得重點(diǎn)看兩個(gè)數(shù)一是空標(biāo)簽占比二是框的尺寸分布。如果空標(biāo)簽只有幾十個(gè)直接剔除問(wèn)題不大如果上千個(gè)我傾向于保留一部分做負(fù)樣本但要在驗(yàn)證集里單獨(dú)統(tǒng)計(jì)它們的表現(xiàn)避免模型學(xué)成「見(jiàn)圖就框」。框尺寸分布還可以用一行排序命令輸出極端值低于5像素的框基本是標(biāo)注噪聲訓(xùn)練前應(yīng)該先洗掉python -c import os bad [] for f in os.listdir(labels): with open(os.path.join(labels, f)) as fp: for line in fp: p line.strip().split() if len(p) 5: continue w, h float(p[3]), float(p[4]) if w 0 or h 0 or w 1 or h 1: bad.append((f, p)) print(異??驍?shù)量:, len(bad)) print(bad[:20]) 這段檢查每個(gè)標(biāo)簽文件的坐標(biāo)是否越界或非正。w或h小于等于0說(shuō)明標(biāo)注時(shí)畫(huà)反了或漏了坐標(biāo)大于1說(shuō)明歸一化出錯(cuò)。歸一化坐標(biāo)越界在格式轉(zhuǎn)換腳本里最容易出現(xiàn)后面第三章會(huì)寫(xiě)一個(gè)更完整的轉(zhuǎn)換方案這里是先做體檢。2.3 從EXIF看高變焦數(shù)據(jù)焦距、分辨率和拍攝距離「高變焦」不能只停留在文件名里圖像本身的元數(shù)據(jù)能把拍攝方式說(shuō)清楚。PIL讀EXIF信息就夠用python -c from PIL import Image import os sample os.listdir(images)[:5] for f in sample: im Image.open(os.path.join(images, f)) exif im.getexif() print(f) print( 分辨率:, im.size) print( 焦距:, exif.get(0x920A) if exif.get(0x920A) else 無(wú)) print( 廠商:, exif.get(0x010F) if exif.get(0x010F) else 無(wú)) 抽幾張圖看分辨率和焦距。如果普遍是4000×3000往上甚至8K的圖那「高變焦」大概率指望遠(yuǎn)端的物理拍攝而不是數(shù)字裁切這也意味著訓(xùn)練推理時(shí)要非常注意長(zhǎng)邊尺寸直接resize到640會(huì)丟掉大量小目標(biāo)。焦距信息還能輔助你判斷同一張圖里板子的尺度差異變焦到最長(zhǎng)焦段拍的板子和廣角端拍的板子在圖中占的像素可能差十幾倍。尺度差異大時(shí)訓(xùn)練集里必須保證每個(gè)尺度的樣本數(shù)量相對(duì)均衡否則模型會(huì)傾向輸出中等大小的框。摸完結(jié)構(gòu)和標(biāo)簽下一步就是把它們整理成YOLO能直接吃的目錄和格式。這一步不難但錯(cuò)一個(gè)斜杠就白跑一次訓(xùn)練。3. 把數(shù)據(jù)集喂給YOLO格式轉(zhuǎn)換、目錄劃分與第一次訓(xùn)練3.1 VOC/COCO標(biāo)注轉(zhuǎn)YOLO格式一個(gè)通用的轉(zhuǎn)換腳本無(wú)論壓縮包里的標(biāo)簽是xml、json還是txt最后都要統(tǒng)一成YOLO的txt格式每行五個(gè)數(shù)class cx cy w h全部歸一化到0到1之間。最常見(jiàn)的情況是原始數(shù)據(jù)用VOC格式xml發(fā)布這里給一個(gè)通用轉(zhuǎn)換腳本按目錄批量處理import xml.etree.ElementTree as ET import os def convert_voc(xml_path, out_txt, class_names): tree ET.parse(xml_path) root tree.getroot() width int(root.findtext(size/width)) height int(root.findtext(size/height)) lines [] for obj in root.findall(object): cls obj.findtext(name) if cls not in class_names: continue box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) cx (xmin xmax) / 2 / width cy (ymin ymax) / 2 / height bw (xmax - xmin) / width bh (ymax - ymin) / height lines.append(f{class_names.index(cls)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w, encodingutf-8) as fp: fp.write(\n.join(lines)) class_names [solar_panel] xml_dir Annotations txt_dir labels os.makedirs(txt_dir, exist_okTrue) for f in os.listdir(xml_dir): if f.endswith(.xml): convert_voc(os.path.join(xml_dir, f), os.path.join(txt_dir, f.replace(.xml, .txt)), class_names)腳本邏輯是按VOC的xml結(jié)構(gòu)解析出圖片寬高和每個(gè)目標(biāo)的邊框轉(zhuǎn)成歸一化中心點(diǎn)坐標(biāo)。注意寬高必須從xml里的size字段讀不能從圖片文件讀——有的數(shù)據(jù)集圖片被二次壓縮過(guò)xml里記的還是原始分辨率用圖片實(shí)際尺寸做歸一化會(huì)讓所有框整體偏移這個(gè)坑在第五章的避坑節(jié)里還會(huì)細(xì)說(shuō)。四舍五入保留6位小數(shù)足夠不需要更高精度。轉(zhuǎn)換完記得抽樣打開(kāi)幾個(gè)txt手工對(duì)照原圖看看框?qū)Σ粚?duì)格式正確但語(yǔ)義錯(cuò)位的情況并不少見(jiàn)。如果原始數(shù)據(jù)是COCO的json邏輯是一樣的只是把xml解析換成json里annotations數(shù)組遍歷。3.2 劃分train/val/test并生成data.yaml轉(zhuǎn)換完后統(tǒng)一目錄。最終目錄結(jié)構(gòu)按YOLO慣例組織solar_panel_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml劃分腳本用固定隨機(jī)種子保證每次劃分結(jié)果一致import os import random import shutil random.seed(42) images [f for f in os.listdir(images) if f.endswith(.jpg) or f.endswith(.png)] random.shuffle(images) n len(images) train images[:int(n * 0.8)] val images[int(n * 0.8):int(n * 0.9)] test images[int(n * 0.9):] for split, files in [(train, train), (val, val), (test, test)]: os.makedirs(fimages/{split}, exist_okTrue) os.makedirs(flabels/{split}, exist_okTrue) for name in files: src_img os.path.join(images, name) dst_img os.path.join(images, split, name) shutil.move(src_img, dst_img) txt_name name.rsplit(., 1)[0] .txt src_txt os.path.join(labels, txt_name) dst_txt os.path.join(labels, split, txt_name) if os.path.exists(src_txt): shutil.move(src_txt, dst_txt)按8:1:1切分兩張圖不會(huì)因?yàn)閿U(kuò)展名不一致而丟失配對(duì)標(biāo)簽。這里要注意劃分必須在圖片和標(biāo)簽都還在平鋪目錄時(shí)做如果已經(jīng)按子目錄分好要先把所有文件匯總再重劃分。24577張圖按8成訓(xùn)練就是將近兩萬(wàn)張訓(xùn)練集足夠大val留10%約2400張也夠看趨勢(shì)。劃分完檢查一下每個(gè)split下images和labels文件數(shù)是否一致差一個(gè)文件后面訓(xùn)練都會(huì)報(bào)FileNotFoundError。然后寫(xiě)data.yaml這是YOLO訓(xùn)練時(shí)的數(shù)據(jù)入口# data.yaml path: /absolute/path/to/solar_panel_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: solar_paneltrain和val填的是相對(duì)于path的路徑。最容易被坑的是path寫(xiě)相對(duì)路徑然后訓(xùn)練命令換個(gè)工作目錄就跑掛。我習(xí)慣寫(xiě)絕對(duì)路徑或至少保證運(yùn)行yolo命令的終端工作目錄和path一致。nc必須和names的數(shù)量對(duì)應(yīng)上只有光伏板一類就填1。如果你檢查標(biāo)簽時(shí)發(fā)現(xiàn)類別數(shù)超過(guò)一個(gè)說(shuō)明原始數(shù)據(jù)里還有別的目標(biāo)names要按實(shí)際類別填不能想當(dāng)然只寫(xiě)solar_panel。3.3 用YOLOv8跑通第一次訓(xùn)練命令與關(guān)鍵參數(shù)目錄和配置都齊了第一次訓(xùn)練先用小模型和相對(duì)小的輸入尺寸跑通流程別一上來(lái)就追求最優(yōu)精度yolo detect train \ data../solar_panel_dataset/data.yaml \ modelyolov8s.pt \ epochs60 \ imgsz1280 \ batch16 \ projectruns \ namepanel_first_try參數(shù)含義model是預(yù)訓(xùn)練權(quán)重yolov8s在精度和速度之間比較均衡第一次跑通用s足夠epochs先設(shè)60確認(rèn)loss在降而不是震蕩再往100加imgsz設(shè)1280而不是默認(rèn)640理由是第二章統(tǒng)計(jì)的框尺寸——高變焦場(chǎng)景里小目標(biāo)占多數(shù)640輸入經(jīng)過(guò)32倍下采樣后小于20像素的目標(biāo)在特征圖上只剩不到1個(gè)像素點(diǎn)幾乎不可分。batch16要結(jié)合顯存調(diào)整后面避坑節(jié)細(xì)說(shuō)。project和name決定訓(xùn)練日志、權(quán)重和曲線圖的輸出目錄每次實(shí)驗(yàn)分開(kāi)命名方便后面對(duì)比。跑完先別急著看mAP打開(kāi) runs/panel_first_try/ 下的 results.png重點(diǎn)看訓(xùn)練集和驗(yàn)證集的box_loss曲線兩者收斂趨勢(shì)是否一致。loss還在下降就說(shuō)明epochs不夠繼續(xù)加val曲線在某個(gè)epoch后開(kāi)始回升而train還在降說(shuō)明過(guò)擬合開(kāi)始早停或加大數(shù)據(jù)增強(qiáng)都行。第一次訓(xùn)練的目標(biāo)不是最優(yōu)精度而是確認(rèn)數(shù)據(jù)鏈路、代碼鏈路都能走通系數(shù)跑起來(lái)正常后再回頭調(diào)。這里還要提一句如果你習(xí)慣用YOLOv5或v11命令結(jié)構(gòu)幾乎一樣v5用python train.py --data ... --img 1280v11用yolo detect train且data.yaml完全兼容。換版本最要注意的是預(yù)訓(xùn)練權(quán)重別下錯(cuò)v8和v11的權(quán)重文件不能互相加載。4. 高變焦場(chǎng)景的檢測(cè)精度瓶頸小目標(biāo)、模糊與尺度變化4.1 為什么高變焦讓YOLO「看不清」anchor和感受野的局限高變焦場(chǎng)景里光伏板在原始大圖上經(jīng)常只有二三十個(gè)像素寬這是目標(biāo)檢測(cè)最頭疼的小目標(biāo)問(wèn)題。YOLO把輸入圖劃分成網(wǎng)格每個(gè)網(wǎng)格負(fù)責(zé)預(yù)測(cè)中心點(diǎn)落在格內(nèi)的目標(biāo)而特征圖經(jīng)過(guò)多次stride下采樣后小目標(biāo)的信息量會(huì)銳減。以YOLOv8為例檢測(cè)頭分別在8倍、16倍、32倍下采樣的特征圖上做預(yù)測(cè)一個(gè)24像素的板子在32倍特征圖上對(duì)應(yīng)0.75個(gè)像素只能靠8倍那層特征圖勉強(qiáng)響應(yīng)。換句話說(shuō)模型不是「不想檢」是能用于判斷的像素太少和反光、陰影疊加在一起后特征就更模糊了。很多人第一反應(yīng)是調(diào)anchor但YOLOv8是anchor-free架構(gòu)沒(méi)有預(yù)設(shè)anchor需要聚類調(diào)anchor這步只對(duì)v5/v7適用。對(duì)v8來(lái)說(shuō)真正有效的杠桿是輸入分辨率、感受野和數(shù)據(jù)增強(qiáng)。另外光伏板本身紋理重復(fù)度高整片板子的邊界在圖像里是平行線簇目標(biāo)與背景的區(qū)分度本來(lái)就低于行人、車輛這類紋理豐富的目標(biāo)模型容易把板間縫隙、屋頂邊緣誤判成板子。這種情況下提升分辨率的作用比換更大的backbone來(lái)得直接后面兩個(gè)小節(jié)按這個(gè)順序展開(kāi)。4.2 用imgsz調(diào)參先試這兩步別急著換模型最直接的手段是提高訓(xùn)練和推理的輸入分辨率。把imgsz從640提到1280小目標(biāo)在特征圖上的有效像素增加一倍AP的提升肉眼可見(jiàn)代價(jià)是訓(xùn)練顯存大約翻四倍推理時(shí)間也成倍增加。一個(gè)務(wù)實(shí)的做法是先用imgsz960看趨勢(shì)確認(rèn)有效再上1280。我在光伏板項(xiàng)目里的經(jīng)驗(yàn)是640到960通常能帶來(lái)3到5個(gè)點(diǎn)的mAP提升960到1280的提升會(huì)變緩但高變焦極端案例的漏檢率下降明顯。imgsz顯存占用趨勢(shì)小目標(biāo)AP推理耗時(shí)適用場(chǎng)景640低基準(zhǔn)低預(yù)覽、快速驗(yàn)證960中中中常規(guī)巡檢1280高好高高變焦、小目標(biāo)為主表格按我自己的經(jīng)驗(yàn)整理不同顯卡和batch下的具體數(shù)值差異很大但趨勢(shì)是穩(wěn)定的。還有一點(diǎn)容易被忽略yolo訓(xùn)練時(shí)默認(rèn)letterbox會(huì)把長(zhǎng)邊縮放并補(bǔ)灰邊保持寬高比不變。高變焦大圖在letterbox后如果原圖寬高比接近1那還好如果是超寬全景圖縮到1280后小目標(biāo)一樣會(huì)丟。遇到這種情況我會(huì)把imgsz保持但配合第六章的切片推理來(lái)解決而不是盲目繼續(xù)加大分辨率。4.3 數(shù)據(jù)增強(qiáng)的取舍mosaic、copy paste與mixup對(duì)光伏板的效果數(shù)據(jù)增強(qiáng)對(duì)高變焦場(chǎng)景是把雙刃劍。Mosaic把四張圖拼成一張等于變相縮小了目標(biāo)尺寸對(duì)提升小目標(biāo)魯棒性有幫助但當(dāng)光伏板密集時(shí)mosaic會(huì)把一塊板子從中間切開(kāi)導(dǎo)致標(biāo)簽框跨拼接邊界模型學(xué)到的是半塊板子特征。YOLOv8默認(rèn)在最后10個(gè)epoch關(guān)閉mosaic原因是此時(shí)模型已經(jīng)在收斂突然喂進(jìn)來(lái)大量被切碎的目標(biāo)會(huì)打斷學(xué)習(xí)。如果你發(fā)現(xiàn)val的box_loss后期震蕩可以檢查是否mosaic影響過(guò)大。Copy paste類增強(qiáng)對(duì)太陽(yáng)能電池板這種重復(fù)紋理的目標(biāo)是友好的。光伏板單體形狀高度相似把一塊板子復(fù)制粘貼到圖像空白區(qū)域相當(dāng)于免費(fèi)提供高質(zhì)量樣本對(duì)稀疏場(chǎng)景下的漏檢改善明顯。Mixup把兩張圖按透明度混合會(huì)讓板子邊緣和反光更糊我在這個(gè)場(chǎng)景下一般把mixup概率調(diào)低或直接關(guān)掉它對(duì)行人這類輪廓清晰的目標(biāo)有效對(duì)邊緣靠平行線定義的光伏板反而有害。增強(qiáng)參數(shù)在ultralytics里通過(guò)訓(xùn)練命令的 mosaic1.0、mixup0.0 這類參數(shù)直接控制不用改代碼先跑個(gè)短epoch對(duì)比再定值。做對(duì)比實(shí)驗(yàn)時(shí)記得固定隨機(jī)種子和除增強(qiáng)外的所有參數(shù)否則你分不清提升是增強(qiáng)帶來(lái)的還是訓(xùn)練噪聲。高變焦數(shù)據(jù)的增強(qiáng)策略沒(méi)有銀彈最可靠的做法是把幾種組合各跑30個(gè)epoch看val曲線再選便宜的方案全量訓(xùn)練。5. 訓(xùn)練實(shí)踐避坑指南損失函數(shù)、顯存和標(biāo)注質(zhì)量的5條踩坑記錄5.1 訓(xùn)練到一半損失函數(shù)變成NaN顯卡和數(shù)據(jù)集誰(shuí)背鍋現(xiàn)象訓(xùn)練跑到第幾百個(gè)iteration控制臺(tái)輸出loss的值突然變成nan或者inf然后整個(gè)batch直接跳掉收斂曲線從此永遠(yuǎn)缺一塊。原因最常見(jiàn)的是學(xué)習(xí)率過(guò)大導(dǎo)致梯度爆炸其次是指標(biāo)數(shù)據(jù)的異常值——某個(gè)標(biāo)注框的寬或高是0或者歸一化坐標(biāo)越界計(jì)算CIoU時(shí)除數(shù)為0。用混合精度訓(xùn)練時(shí)半精度梯度遇到極小值也可能溢出。不能一上來(lái)就懷疑顯卡先用排除法定位。解決先把學(xué)習(xí)率從默認(rèn)的0.01降到0.001加一個(gè)warmup epoch如果還nan再查標(biāo)注數(shù)據(jù)。用第三章的校驗(yàn)?zāi)_本把所有w或h等于0、坐標(biāo)為負(fù)的標(biāo)簽篩出來(lái)單獨(dú)洗掉。順便把混合精度關(guān)掉跑10個(gè)epoch如果正常說(shuō)明你的數(shù)據(jù)里有樣本對(duì)fp16不友好——這種情況多出現(xiàn)在極端長(zhǎng)寬比的框上。三步都試過(guò)還nan才需要考慮驅(qū)動(dòng)和CUDA版本問(wèn)題但概率很低。5.2 驗(yàn)證集mAP很高無(wú)人機(jī)實(shí)拍卻一個(gè)都檢不出現(xiàn)象訓(xùn)練時(shí)val mAP能到0.85看起來(lái)已經(jīng)是可交付水平拿無(wú)人機(jī)或長(zhǎng)焦相機(jī)實(shí)拍的照片一測(cè)漏檢率非常高尤其是遠(yuǎn)處的小板子。原因數(shù)據(jù)分布不一致。這批數(shù)據(jù)集的高變焦樣本可能集中在某個(gè)焦距段或特定光照而你的實(shí)拍照片是在不同高度、不同時(shí)間的強(qiáng)反光下拍的。另一個(gè)常見(jiàn)原因是推理時(shí)的預(yù)處理和訓(xùn)練不一致比如訓(xùn)練用了imgsz1280推理腳本卻傳了640小目標(biāo)直接被下采樣抹掉了。責(zé)任不在數(shù)據(jù)集本身而在于你的驗(yàn)證流程沒(méi)有覆蓋真實(shí)部署條件。解決把實(shí)拍圖加入驗(yàn)證集單獨(dú)統(tǒng)計(jì)一份高變焦子集的AP不要只看全量mAP。mAP是一個(gè)加權(quán)平均會(huì)把表現(xiàn)好的普通場(chǎng)景和表現(xiàn)差的高變焦場(chǎng)景混在一起。實(shí)拍圖上如果只是模糊掉點(diǎn)先用6.1的切片推理跑一遍通常能拉回來(lái)不少如果還不行用這些實(shí)拍圖做增量微調(diào)幾百?gòu)埦蛪虿灰匦掠?xùn)練全部數(shù)據(jù)。5.3 24577張全量訓(xùn)練跑不動(dòng)先分清是顯存不夠還是CPU喂不飽現(xiàn)象訓(xùn)練剛啟動(dòng)就報(bào)CUDA out of memory或者顯卡利用率一直在30%上下波動(dòng)GPU長(zhǎng)時(shí)間排隊(duì)等數(shù)據(jù)。原因報(bào)了OOM的單純是batch和imgsz組合超出了顯存顯卡利用率低則是數(shù)據(jù)加載鏈路有瓶頸。很多人把這兩件事混為一談以為是顯卡不夠好就往下降batch結(jié)果利用率繼續(xù)上不去訓(xùn)得更慢。解決如果是OOM先降batch而不是降imgsz——小目標(biāo)場(chǎng)景里分辨率比batch值錢。batch8配imgsz1280在消費(fèi)級(jí)顯卡上跑yolov8s是常見(jiàn)組合還不行就開(kāi)梯度累積ultralytics里通過(guò)accumulate參數(shù)控制等效batch擴(kuò)大幾倍。如果是利用率低把workers從默認(rèn)2提到8到12并把數(shù)據(jù)放到SSD上高變焦原圖普遍很大從機(jī)械硬盤讀4K圖會(huì)成為瓶頸。判斷方法很簡(jiǎn)單訓(xùn)練時(shí)nvidia-smi看GPU-Util如果很低同時(shí)CPU占用拉滿就是數(shù)據(jù)加載的鍋。5.4 高變焦圖像里的標(biāo)注框錯(cuò)位看起來(lái)沒(méi)毛病訓(xùn)練就是掉點(diǎn)現(xiàn)象抽查標(biāo)簽時(shí)框貼著板子邊緣肉眼看起來(lái)挺準(zhǔn)但訓(xùn)練時(shí)val loss始終偏高個(gè)別batch的置信度混亂。原因高變焦圖里光伏板邊緣是斜的矩形標(biāo)注框很難和傾斜板面完全貼合這是標(biāo)注誤差的物理來(lái)源人人都逃不掉。但更隱蔽的錯(cuò)誤來(lái)自轉(zhuǎn)換腳本有些原始數(shù)據(jù)的xml里width/height寫(xiě)的是圖像原始分辨率圖片本身被預(yù)處理壓縮過(guò)或者標(biāo)注時(shí)用了某款工具導(dǎo)出框坐標(biāo)已經(jīng)做過(guò)一次歸一化又拿去做了一次除以寬高導(dǎo)致坐標(biāo)整體偏移。解決寫(xiě)一個(gè)交叉校驗(yàn)?zāi)_本把標(biāo)簽框反算回像素坐標(biāo)畫(huà)在原圖上批量輸出幾十張對(duì)比圖人工掃一眼。重點(diǎn)看邊緣貼合度和是否存在系統(tǒng)性偏移——比如所有框都往左上偏2個(gè)像素這通常不是標(biāo)注者的手抖而是坐標(biāo)轉(zhuǎn)換時(shí)的坐標(biāo)系問(wèn)題。如果發(fā)現(xiàn)板子是傾斜的而框是正矩形嚴(yán)重不貼合唯一有效的方案是換旋轉(zhuǎn)框標(biāo)注或接受一定誤差不要試圖靠調(diào)loss去彌補(bǔ)標(biāo)注的結(jié)構(gòu)性缺陷。5.5 導(dǎo)出ONNX后精度下降檢測(cè)框偏移和NMS閾值的關(guān)系現(xiàn)象PyTorch里測(cè)試一切正常導(dǎo)出ONNX后用onnxruntime或TensorRT推理框偶爾偏移幾個(gè)像素部分重疊框被漏掉明明是同一塊板子卻輸出兩個(gè)框。原因PyTorch的NMS和ONNX里的NMS算子實(shí)現(xiàn)細(xì)節(jié)有差異輸出shape固定的問(wèn)題在動(dòng)態(tài)輸入時(shí)尤其明顯。另一個(gè)常見(jiàn)原因是導(dǎo)出時(shí)的opset版本選得老某些算子被翻譯成低效實(shí)現(xiàn)還有導(dǎo)出時(shí)把輸入shape動(dòng)態(tài)化推理框架處理letterbox的預(yù)處理又和訓(xùn)練時(shí)不一致導(dǎo)致框坐標(biāo)沒(méi)有被還原到原圖尺寸。解決導(dǎo)出時(shí)固定輸入shape不要用動(dòng)態(tài)維度imgsz1280和訓(xùn)練保持一致opset選12或17都行選新型號(hào)別選太老的。導(dǎo)出后用同一張圖對(duì)比PyTorch和ONNX的輸出專門看小目標(biāo)框偏移量和置信度差異。還有一個(gè)細(xì)節(jié)ONNX推理時(shí)如果后處理NMS的IoU閾值設(shè)得比訓(xùn)練時(shí)高密集排列的光伏板會(huì)保留大量重疊框看起來(lái)像精度下降其實(shí)是閾值沒(méi)對(duì)齊。6. 驗(yàn)證與落地用切片推理和大圖驗(yàn)證把高變焦收益榨干6.1 SAHI切片推理對(duì)大分辨率圖像最直接的有效手段高變焦原圖動(dòng)輒幾千萬(wàn)像素直接整圖推理要么被letterbox縮掉小目標(biāo)要么顯存放不下。行業(yè)里最常見(jiàn)的做法是用切片推理把大圖切成有重疊的多個(gè)小圖分別推理后合并結(jié)果。用sahi庫(kù)可以少寫(xiě)很多膠水代碼命令行大致如下sahi predict --source drone_shot.jpg --model_path best.pt \ --model_type yolov8 --slice_width 640 --slice_height 640 \ --overlap_ratio 0.2 --postprocess_type NMSM \ --postprocess_match_threshold 0.5 --output_dir output/slice_width和slice_height是切片尺寸640和訓(xùn)練分辨率一致overlap_ratio取0.2給切片邊緣的目標(biāo)一個(gè)跨越兩個(gè)切片的冗余防止目標(biāo)正好被切在邊界上。合并后處理用NMSM做跨切片的去重match_threshold控制重疊度多高算同一個(gè)目標(biāo)。切片推理對(duì)高變焦小目標(biāo)的提升非常明顯代價(jià)是推理次數(shù)變多總耗時(shí)上升適合在巡檢后處理階段跑不適合實(shí)時(shí)視頻流。6.2 用混淆矩陣和熱力圖定位系統(tǒng)性漏檢訓(xùn)練結(jié)束后ultralytics會(huì)在驗(yàn)證集上自動(dòng)生成混淆矩陣和各類曲線?;煜仃嚹芸闯瞿P桶压夥邋e(cuò)分成了什么——如果光伏板和背景的混淆項(xiàng)特別高說(shuō)明大量小目標(biāo)被當(dāng)成背景濾掉了重點(diǎn)補(bǔ)小目標(biāo)樣本如果錯(cuò)分成其他類說(shuō)明類別定義在視覺(jué)上有歧義。熱力圖我一般不看抽象的GradCAM而是直接把驗(yàn)證集里置信度低于閾值的預(yù)測(cè)框畫(huà)出來(lái)標(biāo)注上真值框一眼就能看出漏檢集中在圖像哪個(gè)區(qū)域是圖像上半部分的遠(yuǎn)景板子漏得多還是反光區(qū)域漏得多。這個(gè)分布直接決定下一步是調(diào)增強(qiáng)還是補(bǔ)數(shù)據(jù)。6.3 一個(gè)我保留的驗(yàn)證習(xí)慣每次訓(xùn)練完我不只看匯總指標(biāo)我會(huì)固定挑三張圖做人工驗(yàn)收一張普通光照的巡檢圖、一張高變焦極限距離的圖、一張有強(qiáng)反光的圖。三張圖各跑一遍切片推理對(duì)照著看漏檢和誤檢。這個(gè)習(xí)慣幫我擋掉過(guò)好幾次「mAP高但實(shí)際不能用」的翻車交付也從側(cè)面暴露過(guò)訓(xùn)練集里某個(gè)光照條件的樣本偏少。調(diào)imgsz、增強(qiáng)策略這些參數(shù)時(shí)我也是拿這三張圖的檢出數(shù)量作為第一判斷依據(jù)數(shù)值指標(biāo)反而放后面。如果你也在做光伏板檢測(cè)這個(gè)方向從拿到這份數(shù)據(jù)的第一天就保留一組固定的驗(yàn)收?qǐng)D后面每次實(shí)驗(yàn)都在同一標(biāo)準(zhǔn)下對(duì)比比反復(fù)刷新mAP數(shù)字實(shí)在得多。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取