據(jù)集詳解:YOLO標(biāo)簽格式與訓(xùn)練全流程)
簡介這份咖啡葉片檢測數(shù)據(jù)集專為YOLO系列目標(biāo)檢測算法設(shè)計(jì)適用于YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10以及YOLO11等常見版本面向需要真實(shí)農(nóng)業(yè)場景圖像來訓(xùn)練葉片檢測模型的開發(fā)者和研究人員。整個(gè)壓縮包共有兩千個(gè)文件其中包含九百九十九張JPG格式原圖、一千個(gè)TXT格式標(biāo)注文件以及一個(gè)YAML配置文件整體大小約為二十四點(diǎn)五三兆字節(jié)數(shù)據(jù)已經(jīng)按照訓(xùn)練、驗(yàn)證和測試劃分完畢下載后可以直接用于模型訓(xùn)練和效果驗(yàn)證。每個(gè)標(biāo)簽都采用標(biāo)準(zhǔn)的YOLO格式即「類別、中心點(diǎn)橫坐標(biāo)、中心點(diǎn)縱坐標(biāo)、目標(biāo)框?qū)挾?、目?biāo)框高度」所有坐標(biāo)均被歸一化到零到一之間加載方便同時(shí)也可以根據(jù)需求轉(zhuǎn)換成VOC格式便于切換到其他目標(biāo)檢測框架。目前已有五十七人學(xué)習(xí)下載對于剛接觸YOLO或者需要快速獲得帶標(biāo)注農(nóng)業(yè)數(shù)據(jù)集的學(xué)習(xí)者來說能夠省去自行采集和人工標(biāo)注的大量時(shí)間可以直接專注于算法調(diào)參與精度對比。1. 咖啡葉片檢測為什么離不開 YOLO 算法這份帶標(biāo)簽數(shù)據(jù)集先解決什么問題用 yolo 算法做咖啡葉片檢測本質(zhì)上要解決一個(gè)問題給模型一張葉片照片讓它把所有葉片或病斑區(qū)域用矩形框標(biāo)出來。咖啡葉片檢測數(shù)據(jù)集-1000張圖像帶標(biāo)簽-葉子testing-detection-zsmzd.zip 這份 zip 包恰好提供了訓(xùn)練一個(gè)最小可用模型所需的全部原料圖像、標(biāo)簽和明確的檢測目標(biāo)。你不需要自己去網(wǎng)上爬圖、手工畫框解壓之后就能跑通一整套目標(biāo)檢測流程。對誰有用如果你是剛接觸目標(biāo)檢測的開發(fā)者這份 1000 張圖的小數(shù)據(jù)集是第一個(gè)能讓你完整走一遍 YOLO 流程的練習(xí)場如果你做植物病理研究或農(nóng)場巡檢它能驗(yàn)證“用框定位病斑”這條路是否可行再?zèng)Q定要不要投入大規(guī)模標(biāo)注如果你在評估邊緣設(shè)備的機(jī)器視覺方案它也能快速幫你判斷 YOLO 算法在當(dāng)前硬件上的表現(xiàn)邊界。這套流程不只能用在咖啡葉片上軸承缺陷檢測、鳥類目標(biāo)檢測等單類目標(biāo)檢測場景基本是同一套做法換數(shù)據(jù)集就能遷移。2. 解剖這份 1000 張帶標(biāo)簽數(shù)據(jù)集目錄結(jié)構(gòu)、標(biāo)簽格式與訓(xùn)練前檢查2.1 先解壓看目錄這類 zip 包的常見命脈拿到類似“葉子testing-detection-zsmzd.zip”的包第一步永遠(yuǎn)是先解壓看目錄結(jié)構(gòu)不要直接拿去訓(xùn)練。這是我反復(fù)強(qiáng)調(diào)的習(xí)慣數(shù)據(jù)集目錄一旦理解錯(cuò)后面所有路徑、腳本、訓(xùn)練命令都會(huì)跟著錯(cuò)。我一般這樣操作unzip yolo算法-咖啡葉片檢測數(shù)據(jù)集-1000張圖像帶標(biāo)簽-葉子testing-detection-zsmzd.zip -d coffee_leaf cd coffee_leaf tree -L 2參數(shù)說明unzip -d指定解壓目標(biāo)目錄避免在下載目錄里攤開幾百個(gè)文件tree -L 2只顯示兩層目錄足夠看清頂層結(jié)構(gòu)。如果系統(tǒng)沒有 tree用ls -R也能代替。常見的目錄結(jié)構(gòu)有兩種。一種是頂層直接放images/和labels/兩個(gè)目錄下各有 train、val、test 子目錄另一種是頂層按 train/val 分每個(gè)子目錄內(nèi)再放 images 和 labels。這份數(shù)據(jù)集名字里有“testing-detection”很可能在壓縮包里還夾帶一個(gè)說明文件或類別映射文件。解壓后第一時(shí)間打開classes.txt或label_map.txt看一眼確認(rèn)類別編號從 0 開始還是從 1 開始——這地方出錯(cuò)不會(huì)報(bào)錯(cuò)只會(huì)讓你訓(xùn)練出來的模型所有框都偏一個(gè)類別屬于比較隱蔽的坑。另外要注意圖片和標(biāo)簽文件的命名一致性。圖片叫IMG_0001.jpg、標(biāo)簽叫0001.txt的錯(cuò)位情況并不少見。如果文件名對不上YOLO 訓(xùn)練時(shí)會(huì)直接跳過這些圖數(shù)據(jù)量本來就只有 1000 張?jiān)偕賻资畯垖Y(jié)果影響不小。2.2 YOLO 標(biāo)簽格式解讀每行是一個(gè)歸一化坐標(biāo)這類數(shù)據(jù)集的標(biāo)簽通常是.txt純文本和 YOLO 系列算法的要求一致。每個(gè) txt 文件對應(yīng)一張 jpg 圖片每一行描述一個(gè)目標(biāo)框格式固定為五個(gè)數(shù)字0 0.5234375 0.3819444 0.04375 0.0708333 0 0.61875 0.4333333 0.03125 0.0527778這五個(gè)數(shù)字的含義如下表字段含義取值范圍class_id類別編號從 0 開始0 ~ nc-1x_center目標(biāo)框中心點(diǎn) x 坐標(biāo)已除以圖像寬0 ~ 1y_center目標(biāo)框中心點(diǎn) y 坐標(biāo)已除以圖像高0 ~ 1width目標(biāo)框?qū)挾纫殉詧D像寬0 ~ 1height目標(biāo)框高度已除以圖像高0 ~ 1所有數(shù)值都在 0 到 1 之間這是 YOLO 標(biāo)簽區(qū)別于 VOC 格式 xml 的關(guān)鍵特征。如果這份數(shù)據(jù)集的任務(wù)是檢測葉片本身那么類別編號只有 0如果同時(shí)標(biāo)注了“健康葉片”“銹病葉片”“蟲眼葉片”等多個(gè)類別txt 里就會(huì)混著多個(gè) class_id。拿到數(shù)據(jù)后先弄清楚標(biāo)注粒度后面訓(xùn)練才知道自己的模型到底在學(xué)什么。和 COCO 那種 json 標(biāo)注不同YOLO 的 txt 標(biāo)簽是純文本、一個(gè)文件對一張圖不需要額外解析庫。如果某個(gè)數(shù)據(jù)集給的是 VOC 的 xml 標(biāo)注需要先用腳本轉(zhuǎn)成 txt但帶“testing-detection”這類標(biāo)識的包通常直接給 YOLO 格式省了轉(zhuǎn)換這一步。萬一你手上的包是 xml轉(zhuǎn)換時(shí)注意兩點(diǎn)一是類別要從 0 重新編號二是標(biāo)注框的坐標(biāo)要除以圖片寬高做歸一化缺一不可。2.3 訓(xùn)練前用腳本檢查標(biāo)簽統(tǒng)計(jì)類別、框尺寸和缺漏拿到標(biāo)簽后不要急著訓(xùn)練先寫個(gè)腳本做一次“體檢”。我通常會(huì)統(tǒng)計(jì)總框數(shù)、類別分布、異常標(biāo)注和缺圖情況import os from collections import Counter label_dir labels/train image_dir images/train total_boxes 0 class_counter Counter() issues [] for f in os.listdir(label_dir): if not f.endswith(.txt): continue img_file f.replace(.txt, .jpg) if not os.path.exists(os.path.join(image_dir, img_file)): issues.append(f缺圖: {f}) with open(os.path.join(label_dir, f)) as fh: for line in fh: parts line.strip().split() if len(parts) ! 5: issues.append(f列數(shù)不對: {f} - {line}) continue cls int(parts[0]) x, y, w, h map(float, parts[1:]) if w 1 or h 1: issues.append(f坐標(biāo)未歸一化: {f} - {line}) if x 0 or y 0 or x 1 or y 1: issues.append(f中心點(diǎn)越界: {f} - {line}) class_counter[cls] 1 total_boxes 1 print(總框數(shù):, total_boxes) print(類別分布:, class_counter) print(問題記錄:) for item in issues: print( , item)這段腳本做四件事檢查標(biāo)簽和圖片是否一一對應(yīng)檢查每行是否正好 5 列檢查框?qū)捀呤欠裨?0 到 1 之間檢查中心點(diǎn)坐標(biāo)是否越界。跑完一遍數(shù)據(jù)集里比較明顯的錯(cuò)誤都能暴露出來。對缺圖的文件寧可刪掉對應(yīng)標(biāo)簽也不要硬湊文件名否則訓(xùn)練讀取時(shí)會(huì)觸發(fā)邊界異常。除了“缺圖”還有一種更隱蔽的情況是“缺標(biāo)簽”圖片存在但沒有任何 txt 文件對應(yīng)。ultralytics 訓(xùn)練時(shí)會(huì)靜默跳過這類圖片不會(huì)報(bào)錯(cuò)所以需要單獨(dú)對比兩個(gè)目錄的文件名集合把缺標(biāo)簽的圖找出來。數(shù)據(jù)只有 1000 張多找回幾十張有效圖比調(diào)任何訓(xùn)練參數(shù)都實(shí)在。3. 把數(shù)據(jù)集跑進(jìn) YOLOv8環(huán)境準(zhǔn)備、目錄重排與最小訓(xùn)練命令3.1 環(huán)境搭建和依賴版本CUDA、PyTorch、ultralytics 的組合跑 YOLO 算法目前最省心的方式是使用 ultralytics 這個(gè) Python 包它把 YOLOv8 的訓(xùn)練、驗(yàn)證、推理、導(dǎo)出都封裝成了命令行工具。環(huán)境搭建我建議用 conda 隔離避免把系統(tǒng) Python 搞亂conda create -n yolo python3.10 conda activate yolo pip install ultralytics8.2.0 torch2.2.0 torchvision0.17.0這里選 torch 2.2.0 是相對穩(wěn)定的組合和 ultralytics 8.2.0 配合沒有已知的算子兼容問題。如果你裝的是 torch 2.0 以下新版 ultralytics 在導(dǎo)出模型時(shí)可能會(huì)報(bào)某些算子在 CUDA 上找不到實(shí)現(xiàn)反過來 torch 版本太新也可能遇到 CUDA 運(yùn)行時(shí)版本不匹配的警告。如果機(jī)器沒有 NVIDIA 顯卡CPU 也能跑只是訓(xùn)練一個(gè) epoch 的時(shí)間會(huì)長 5 到 10 倍建議先用 yolov8n 最小模型驗(yàn)證流程。裝完先跑一條最簡單的推理命令確認(rèn)環(huán)境通yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg這條命令會(huì)自動(dòng)下載 yolov8n 預(yù)訓(xùn)練權(quán)重然后對示例圖片做一次推理。如果這里能正常輸出結(jié)果圖說明 PyTorch、ultralytics、CUDA 的鏈路是通的。如果報(bào)錯(cuò)多半是 CUDA 版本和 PyTorch 不匹配可以用python -c import torch; print(torch.cuda.is_available())先確認(rèn) GPU 是否真的被識別。3.2 按 YOLO 規(guī)范重排目錄train、val、test 怎么分無論原始 zip 的結(jié)構(gòu)是什么樣的ultralytics 最終需要的是“圖像路徑 標(biāo)簽路徑 數(shù)據(jù)集 YAML”。最穩(wěn)妥的方式是把數(shù)據(jù)統(tǒng)一重排成下面的結(jié)構(gòu)datasets/coffee_leaf/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── coffee_leaf.yaml用下面的腳本把原始圖片和標(biāo)簽按 8:2 劃分到訓(xùn)練集和驗(yàn)證集import os import shutil import random random.seed(42) image_dir images label_dir labels train_ratio 0.8 all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(all_images) split_idx int(len(all_images) * train_ratio) os.makedirs(datasets/coffee_leaf/images/train, exist_okTrue) os.makedirs(datasets/coffee_leaf/images/val, exist_okTrue) os.makedirs(datasets/coffee_leaf/labels/train, exist_okTrue) os.makedirs(datasets/coffee_leaf/labels/val, exist_okTrue) for f in all_images[:split_idx]: shutil.copy(os.path.join(image_dir, f), datasets/coffee_leaf/images/train/) shutil.copy(os.path.join(label_dir, f.replace(.jpg, .txt)), datasets/coffee_leaf/labels/train/) for f in all_images[split_idx:]: shutil.copy(os.path.join(image_dir, f), datasets/coffee_leaf/images/val/) shutil.copy(os.path.join(label_dir, f.replace(.jpg, .txt)), datasets/coffee_leaf/labels/val/) print(訓(xùn)練集:, split_idx, 驗(yàn)證集:, len(all_images) - split_idx)說明這里用shutil.copy而不是os.rename是給自己留后悔藥——即使后面發(fā)現(xiàn)劃分有問題原始文件還在可以重新劃分。隨機(jī)種子固定為 42保證每次跑出來的劃分結(jié)果一致方便復(fù)現(xiàn)實(shí)驗(yàn)。如果你的原始目錄已經(jīng)是 train/val 分開的這段腳本要改成遍歷兩個(gè)子目錄后分別復(fù)制不要照搬。有一點(diǎn)需要特別注意如果你是按文件名隨機(jī)劃分同一株咖啡樹的連續(xù)照片很可能同時(shí)落在訓(xùn)練集和驗(yàn)證集里導(dǎo)致驗(yàn)證分?jǐn)?shù)虛高。正確的做法是先把文件名按拍攝對象的前綴分組再按組劃分。這個(gè)細(xì)節(jié)我在第 5 章專門展開講這里先埋個(gè)伏筆。3.3 最小訓(xùn)練命令從零開始跑通第一個(gè) epoch目錄重排好之后在datasets/coffee_leaf/下新建coffee_leaf.yamlpath: /絕對路徑/datasets/coffee_leaf # 改成你自己的實(shí)際路徑 train: images/train val: images/val nc: 1 names: 0: coffee_leaf然后執(zhí)行訓(xùn)練命令yolo train datacoffee_leaf.yaml modelyolov8n.pt epochs100 imgsz640 batch16第一次訓(xùn)練的目標(biāo)是驗(yàn)證數(shù)據(jù)鏈路是通的不是追求精度。yolov8n.pt是最小的預(yù)訓(xùn)練權(quán)重下載大約 6 MB失敗會(huì)提示網(wǎng)絡(luò)問題。訓(xùn)練時(shí)終端會(huì)打印每個(gè) epoch 的 box_loss、cls_loss、dfl_loss結(jié)束時(shí)在runs/detect/train/下生成best.pt和last.pt。如果這一步報(bào)錯(cuò)常見的幾種情況yaml 里的 path 寫成了相對路徑但當(dāng)前工作目錄不在數(shù)據(jù)集根目錄圖片和標(biāo)簽沒有放在對應(yīng)的 train/val 子目錄里某個(gè)標(biāo)簽文件為空。逐個(gè)排查就行。訓(xùn)練完看一眼runs/detect/train/results.png里面有 loss 曲線和 mAP 曲線能直觀判斷訓(xùn)練是否正常收斂。4. 訓(xùn)練過程中的參數(shù)調(diào)整與效果驗(yàn)證從 mAP 到葉片邊界4.1 關(guān)鍵訓(xùn)練參數(shù)imgsz、batch、epochs、patience 怎么設(shè)跑通最小命令之后可以針對咖啡葉片這個(gè)任務(wù)開始調(diào)參數(shù)。先看imgsz默認(rèn) 640 對大多數(shù)目標(biāo)都適用。咖啡葉片圖像如果是近景拍攝葉片占畫面比例很大640 夠用如果圖像里葉片很小、密集或者要檢測的是葉片上更小的病斑可以提到 960 或 1280但訓(xùn)練時(shí)間會(huì)顯著增加。我一般先用 640 跑通再看驗(yàn)證集里小目標(biāo)的表現(xiàn)決定要不要升。batch大小取決于顯存。16G 顯存跑 yolov8n 用 batch16 沒問題但如果換 yolov8m 或 imgsz1280顯存不夠就會(huì)報(bào) CUDA out of memory。epochs不要一開始設(shè)太多先設(shè) 100 配合patience20做早停即驗(yàn)證集 mAP 連續(xù) 20 個(gè) epoch 沒有提升就自動(dòng)停止省時(shí)間也避免過擬合。我常用的一組起步參數(shù)是yolo train datacoffee_leaf.yaml modelyolov8n.pt \ epochs200 patience30 imgsz640 batch16 \ lr00.005 lrf0.01 optimizerAdamW參數(shù)說明lr0是初始學(xué)習(xí)率YOLO 默認(rèn) 0.01但 1000 張小數(shù)據(jù)集建議降到 0.005 或 0.001否則前幾個(gè) epoch 的 loss 波動(dòng)很大甚至發(fā)散。lrf是最終學(xué)習(xí)率與初始學(xué)習(xí)率的比值0.01 表示訓(xùn)練結(jié)束時(shí)學(xué)習(xí)率降到初始值的 1%。optimizerAdamW對小數(shù)據(jù)集比默認(rèn)的 SGD 更穩(wěn)收斂曲線更平滑缺點(diǎn)是每輪訓(xùn)練時(shí)間略長。如果數(shù)據(jù)量充足、追求更高精度把modelyolov8n.pt換成yolov8s.pt或yolov8m.pt。模型越大backbone 提取的特征越豐富但訓(xùn)練時(shí)間和顯存開銷成倍增加。對咖啡葉片這種紋理特征明顯、目標(biāo)不算太小的任務(wù)yolov8n 到 yolov8s 之間的提升最明顯再往上收益就邊際遞減了。4.2 驗(yàn)證指標(biāo)怎么看mAP50、mAP50-95、precision、recall訓(xùn)練結(jié)束或早停后runs 目錄下會(huì)生成混淆矩陣、PR 曲線和各類指標(biāo)曲線。對葉片檢測任務(wù)我按下面的表格判斷模型好壞指標(biāo)含義葉片場景參考值mAP50IoU 閾值為 0.5 時(shí)的平均精度評估“框大概在不在目標(biāo)附近”0.85 以上算能落地mAP50-95IoU 從 0.5 到 0.95 的平均精度評估框邊界的貼合度0.6 以上說明邊界預(yù)測較準(zhǔn)precision預(yù)測框中真正包含目標(biāo)的比例越高誤檢越少建議 0.85 以上recall真實(shí)目標(biāo)中被檢出的比例越低漏檢越多建議 0.85 以上如果 mAP50 高但 mAP50-95 明顯低說明模型雖然找到了葉片的大致位置但框的邊緣不夠準(zhǔn)。這在葉片互相重疊、病斑和健康組織邊界模糊時(shí)很常見。解決思路要么提升 imgsz要么換更大模型要么檢查標(biāo)簽框本身是否畫得隨意。precision 和 recall 的取舍要看實(shí)際應(yīng)用場景。在病蟲害巡檢場景里我寧可接受低一點(diǎn) precision也要保住 recall。原因是漏檢一片帶銹病的葉子比多框一片健康葉子代價(jià)大得多——前者會(huì)導(dǎo)致病害蔓延后者頂多是人工篩一下。反過來如果做的是自動(dòng)采摘機(jī)器人precision 低會(huì)導(dǎo)致機(jī)械臂抓錯(cuò)目標(biāo)這時(shí)候就要把置信度閾值調(diào)高、優(yōu)先保證 precision。4.3 過擬合與欠擬合的干預(yù)手段早停、數(shù)據(jù)增強(qiáng)、凍結(jié)權(quán)重1000 張圖的數(shù)據(jù)集在深度學(xué)習(xí)里算小的過擬合是大概率事件?,F(xiàn)象是訓(xùn)練集 loss 持續(xù)下降驗(yàn)證集 mAP 停滯甚至倒退。除了早停還有幾個(gè)手段比較好用。數(shù)據(jù)增強(qiáng)是成本最低的解法。ultralytics 默認(rèn)已開啟 HSV 色域變換、隨機(jī)翻轉(zhuǎn)、平移、縮放但這些對葉片這種紋理敏感的目標(biāo)還不夠??梢栽?yaml 里追加mosaic: 1.0 mixup: 0.2 copy_paste: 0.2mosaic 把四張圖拼成一張訓(xùn)練mixup 按比例混合兩張圖copy_paste 把某個(gè)目標(biāo)復(fù)制到另一張圖上。這三個(gè)增強(qiáng)對防止過擬合有顯著效果但要留意mosaic 的拼接會(huì)讓單個(gè)目標(biāo)變小如果數(shù)據(jù)集里本來就有不少小目標(biāo)建議把 mosaic 降到 0.5mixup 超過 0.3 會(huì)讓葉片紋理變得模糊導(dǎo)致驗(yàn)證集上病斑檢測變差。這些都算 yolo 損失函數(shù)之外的“軟技巧”不在訓(xùn)練日志里直接體現(xiàn)但效果很實(shí)在。凍結(jié)權(quán)重是另一個(gè)思路。如果數(shù)據(jù)集和 ImageNet 里的自然圖像差異不算太極端可以先凍結(jié) backbone只訓(xùn)練 headyolo train datacoffee_leaf.yaml modelyolov8n.pt freeze10freeze10 表示凍結(jié)前 10 層保留預(yù)訓(xùn)練模型提取的基礎(chǔ)特征防止小數(shù)據(jù)集把 backbone 帶偏。如果訓(xùn)練到一半發(fā)現(xiàn) loss 下降很慢可以取消凍結(jié)用已經(jīng)收斂的 head 作為初始化繼續(xù)全量微調(diào)。這個(gè)“先凍結(jié)、后解凍”的操作比從頭訓(xùn)練更穩(wěn)也是我在小數(shù)據(jù)集上習(xí)慣用的保底方案。5. 咖啡葉片檢測的常見坑與排查我在跑這類數(shù)據(jù)集時(shí)踩過的雷5.1 標(biāo)簽坐標(biāo)歸一化出錯(cuò)葉片框全偏到左上角現(xiàn)象訓(xùn)練時(shí) loss 能正常下降但推理時(shí)所有框都擠在圖像左上角或者框的尺寸明顯不對。 原因標(biāo)簽文件里的坐標(biāo)沒有除以圖像寬高做歸一化而是像素坐標(biāo)直接進(jìn)了模型或者除以了一個(gè)固定值比如 1000導(dǎo)致中心點(diǎn)和寬高數(shù)值全部偏小。YOLO 要求標(biāo)簽坐標(biāo)必須歸一化到 0 到 1 之間否則模型學(xué)到的框位置就是錯(cuò)的。 解決用 2.3 的腳本遍歷所有標(biāo)簽找出 w 或 h 大于 1 的文件。如果確認(rèn)沒有歸一化批量修正from PIL import Image def normalize_labels(img_path, label_path, out_path): with Image.open(img_path) as im: w_real, h_real im.size with open(label_path) as f: lines f.readlines() with open(out_path, w) as f: for line in lines: parts line.strip().split() cls, x_pix, y_pix, w_pix, h_pix parts x_norm float(x_pix) / w_real y_norm float(y_pix) / h_real w_norm float(w_pix) / w_real h_norm float(h_pix) / h_real f.write(f{cls} {x_norm:.6f} {y_norm:.6f} {w_norm:.6f} {h_norm:.6f}\n)這個(gè)腳本假設(shè)原始標(biāo)簽存的是像素坐標(biāo)而且中心點(diǎn)坐標(biāo)和寬高都是像素值。修正后再跑一遍體檢腳本確認(rèn)所有數(shù)值都落在 0 到 1 區(qū)間內(nèi)。5.2 類別不平衡健康葉片多、病斑葉片太少怎么辦現(xiàn)象訓(xùn)練結(jié)束后模型在健康葉片上 recall 很高但病斑類別的 recall 幾乎為 0輸出的框全部落在健康葉子上。 原因如果這份數(shù)據(jù)集按葉片狀態(tài)標(biāo)注很可能健康葉片占了 80% 以上病斑樣本只占少數(shù)。模型在類別不平衡下學(xué)會(huì)了“全都預(yù)測為健康葉片”因?yàn)檫@樣整體 loss 最低但它根本沒有學(xué)到病斑的特征。 解決先看類別分布。確認(rèn)不平衡后最直接的做法是對少數(shù)類別做過采樣——把病斑樣本復(fù)制幾份和多數(shù)類數(shù)量接近再訓(xùn)練。另一種做法是給少數(shù)類更高的 loss 權(quán)重ultralytics 里設(shè)置類別權(quán)重的方法不是特別直觀我一般優(yōu)先做數(shù)據(jù)層過采樣。如果過采樣之后訓(xùn)練時(shí)病斑的 loss 還是被淹沒再考慮換用帶 focal loss 的配置。5.3 光照與背景干擾大棚環(huán)境下漏檢和誤檢現(xiàn)象在實(shí)驗(yàn)室或統(tǒng)一背景下拍攝的數(shù)據(jù)集上 mAP 很高一到真實(shí)大棚或野外模型就把泥土、雜草、反光水珠當(dāng)成了葉片或者把陰影里的葉片漏掉。 原因訓(xùn)練集里的圖像大多是固定角度、固定光照測試環(huán)境的光照和背景差異大。這是目標(biāo)檢測遷移失敗的典型問題也常見于軸承缺陷檢測這類工業(yè)場景——訓(xùn)練時(shí)是在恒定的流水線打光下拍的部署到現(xiàn)場光照一變效果就崩。 解決訓(xùn)練時(shí)把數(shù)據(jù)增強(qiáng)里的曝光度、對比度、模糊強(qiáng)度加大模擬不同光照條件。ultralytics 的 hsv_h、hsv_s、hsv_v 三個(gè)增強(qiáng)參數(shù)控制色相、飽和度、亮度你可以在 yaml 里把 hsv_v 從默認(rèn)的 0.4 提到 0.6。但這和圖像超分辨率重建是兩碼事——超分是放大圖像細(xì)節(jié)你要的是顏色和亮度的抖動(dòng)讓模型學(xué)會(huì)忽略光照差異。如果增強(qiáng)之后還是不行就在部署現(xiàn)場重新采一批圖像做半自動(dòng)標(biāo)注后加入訓(xùn)練集。5.4 數(shù)據(jù)集泄露同一株咖啡樹的圖像同時(shí)出現(xiàn)在訓(xùn)練集和驗(yàn)證集現(xiàn)象訓(xùn)練時(shí) mAP 沖到 0.95驗(yàn)證時(shí) mAP 也很漂亮但把模型拿到新環(huán)境、新植株上一測成績立即明顯下滑。 原因拍照時(shí)通常會(huì)對同一株樹連續(xù)拍多張這些圖像背景、角度、葉片狀態(tài)高度相似。如果按文件名隨機(jī)切分訓(xùn)練集和驗(yàn)證集同一株的相似圖像會(huì)同時(shí)出現(xiàn)在兩邊驗(yàn)證集過樂觀。這個(gè)現(xiàn)象在遙感圖像標(biāo)注和作物病害數(shù)據(jù)里尤其普遍因?yàn)椴杉瘑T習(xí)慣對著一塊區(qū)域連拍。 解決正確做法是按拍攝對象分組劃分。把文件名前綴相同的圖像視為一個(gè)組按組打亂后劃分 train/val而不是按單張圖劃分。比如文件名是tree_01_001.jpg、tree_01_002.jpg就把tree_01開頭的所有圖放進(jìn)同一個(gè)集合。代碼上先提取前綴、對前綴集合做 split再展開到圖像列表。這一步雖然麻煩但出來的驗(yàn)證指標(biāo)才真正有參考價(jià)值。5.5 目標(biāo)框太小導(dǎo)致縮樣后信息丟失現(xiàn)象病斑區(qū)域在整張圖中只占很小面積imgsz640 訓(xùn)練能收斂但推理時(shí)小病斑一直漏檢導(dǎo)致召回率上不去。 原因病斑可能只有 20×20 像素縮放到 640×640 后在特征圖上只占幾個(gè)像素點(diǎn)backbone 下采樣之后信息基本丟失。yolov8n 的頸部網(wǎng)絡(luò)對微小目標(biāo)本來就偏弱。 解決把 imgsz 提高到 960 或 1280讓病斑在縮放后保留更多像素。YOLO 訓(xùn)練時(shí)本身會(huì)做多尺度隨機(jī)縮放但如果輸入分辨率不夠多尺度也救不回來。這是小目標(biāo)檢測的通用痛點(diǎn)換用專門的小目標(biāo)改進(jìn)版本或提高輸入分辨率是兩條最直接的路。6. 用訓(xùn)練好的權(quán)重做推理與部署驗(yàn)證模型在真實(shí)葉片圖像上的最后一步訓(xùn)練結(jié)束后真正讓人踏實(shí)的是把 best.pt 用在一張沒見過的圖像上。先整一個(gè)帶標(biāo)注的測試目錄跑一次推理看效果yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_images/ \ conf0.25 iou0.45conf 是置信度閾值iou 是 NMS 的 IoU 閾值。放在咖啡葉片場景里如果漏檢嚴(yán)重就把 conf 降到 0.15如果誤檢太多就提到 0.4。我會(huì)先用 0.2 跑一遍看結(jié)果圖里多檢和漏檢的比例再?zèng)Q定往哪個(gè)方向調(diào)。確認(rèn)效果可接受后順手導(dǎo)出 ONNX 格式為后續(xù)邊緣部署做準(zhǔn)備yolo export modelbest.pt formatonnx imgsz640導(dǎo)出后在 Python 里可以用 onnxruntime 推理讀取輸出層的[1, 84, 8400]張量解析出每個(gè)候選框的類別、置信度和坐標(biāo)。到這里整個(gè)流程才算閉環(huán)——從一份 zip 數(shù)據(jù)集到能跑在本地的一顆模型再到能轉(zhuǎn)移到其他運(yùn)行時(shí)環(huán)境的部署文件。我用這套流程處理過好幾類農(nóng)業(yè)檢測數(shù)據(jù)從咖啡葉片到其他作物葉片過程大同小異。最深的感受是數(shù)據(jù)集的質(zhì)量決定訓(xùn)練結(jié)果的上限YOLO 算法只是把這份上限兌現(xiàn)出來。拿到任何一份標(biāo)注數(shù)據(jù)先花半小時(shí)做標(biāo)簽體檢、按場景分組劃分訓(xùn)練集比多調(diào)十次學(xué)習(xí)率都值。這純屬血淚經(jīng)驗(yàn)希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取