機檢測實戰(zhàn):YOLO格式轉(zhuǎn)換與YOLOv8訓(xùn)練全流程)
簡介這份資源是面向無人機俯視視角的農(nóng)場場景目標檢測數(shù)據(jù)集適合從事農(nóng)業(yè)智能化、無人機巡檢及目標檢測算法實踐的開發(fā)者與研究者使用可解決農(nóng)場中車輛、農(nóng)機與行人識別任務(wù)缺乏高質(zhì)量標注數(shù)據(jù)的問題。數(shù)據(jù)集包含1000余張圖像壓縮包共2000個文件其中497張jpg為無人機航拍原圖1502個txt為對應(yīng)的YOLO格式標注文件另有1個yaml配置文件整體約522.36MB。目錄已按train、val、test劃分完畢data.yaml中定義了car、people、tractor、van四類目標yolov5、yolov7、yolov8等主流框架可直接加載訓(xùn)練省去格式轉(zhuǎn)換與劃分工作。目前已有451人學(xué)習(xí)下載配套博文提供了數(shù)據(jù)集說明與檢測結(jié)果參考便于讀者快速驗證模型效果并復(fù)現(xiàn)實驗流程適合作為農(nóng)業(yè)場景目標檢測的入門與進階訓(xùn)練素材。1. 農(nóng)場里的農(nóng)機檢測VisDrone 數(shù)據(jù)集為什么值得你花一個周末跑通去年幫一個做智慧農(nóng)業(yè)的朋友看模型他拿 COCO 預(yù)訓(xùn)練的 YOLO 直接去數(shù)拖拉機結(jié)果把收割機認成卡車、把旋耕機認成摩托mAP 掉到 0.2 出頭。問題不在模型在數(shù)據(jù)——通用數(shù)據(jù)集里農(nóng)業(yè)機械的樣本少得可憐類別定義也對不上。VisDrone 原本是無人機視角的視覺目標檢測數(shù)據(jù)集里面包含大量俯拍場景下的車輛、行人、農(nóng)機等目標其中農(nóng)業(yè)機械這一類在農(nóng)田、果園、牧場場景下有相當(dāng)數(shù)量的標注。這個標題指向的就是把 VisDrone 里跟農(nóng)業(yè)機械相關(guān)的子集抽出來整理成 YOLO 格式直接用于訓(xùn)練自己的農(nóng)機檢測模型。適合誰做農(nóng)業(yè)無人機巡檢、農(nóng)機自動駕駛、果園管理機器人的一線開發(fā)者以及想找一個真實場景數(shù)據(jù)集練手 YOLO 全流程的工程師。你不需要從零標注幾千張圖但需要理解這個數(shù)據(jù)集的格式、類別映射和轉(zhuǎn)換邏輯否則拿到手也是一堆看不懂的 txt。2. VisDrone 原始標注拆解與 YOLO 格式對齊2.1 VisDrone 的標注文件長什么樣VisDrone 數(shù)據(jù)集常見發(fā)布形式是每張圖片對應(yīng)一個 txt 標注文件文件名與圖片同名。標注內(nèi)容按行組織每行 8 個字段用逗號分隔。字段順序依次是bbox_left、bbox_top、bbox_width、bbox_height、score、object_category、truncation、occlusion。其中 score 表示標注置信度1 表示有效object_category 是類別編號truncation 和 occlusion 分別表示截斷程度和遮擋程度取值 0 或 1。類別編號在 VisDrone 官方定義中通常有 10 類左右包括 ignored regions、pedestrian、people、bicycle、car、van、truck、tricycle、awning-tricycle、bus、motor、others。農(nóng)業(yè)機械并沒有單獨一個類而是分散在 truck、van、others 等類別里。這意味著你不能直接拿 VisDrone 的類別定義去訓(xùn)練農(nóng)機檢測必須做一步類別篩選和重映射。我一般會先寫一個統(tǒng)計腳本看看每個類別在標注里出現(xiàn)多少次再決定哪些類別歸入農(nóng)機。比如 truck 里可能包含拖拉機、收割機van 里可能包含農(nóng)用運輸車others 里可能包含旋耕機、播種機等。這一步不做后面訓(xùn)練出來的模型類別就是亂的。2.2 從 VisDrone 格式轉(zhuǎn) YOLO 格式的完整腳本YOLO 格式要求每張圖片對應(yīng)一個 txt每行 5 個字段class_id、x_center、y_center、width、height。所有坐標都要歸一化到 0 到 1 之間。轉(zhuǎn)換的核心邏輯是讀取 VisDrone 標注行過濾掉不需要的類別把 bbox 的左上角坐標和寬高轉(zhuǎn)成中心點坐標和寬高再除以圖片寬高做歸一化。下面是我常用的轉(zhuǎn)換腳本依賴 Pillow 和 tqdm直接放在數(shù)據(jù)集根目錄跑就行。import os from PIL import Image from tqdm import tqdm # VisDrone 類別編號到名稱的映射按你實際拿到的數(shù)據(jù)集版本調(diào)整 VISDRONE_CATEGORIES { 0: ignored, 1: pedestrian, 2: people, 3: bicycle, 4: car, 5: van, 6: truck, 7: tricycle, 8: awning-tricycle, 9: bus, 10: motor, 11: others } # 你希望保留并映射到 YOLO 的類別這里只保留農(nóng)機相關(guān) # 鍵是 VisDrone 類別編號值是 YOLO 類別編號 CATEGORY_MAP { 5: 0, # van - agricultural_machine 6: 0, # truck - agricultural_machine 11: 0, # others - agricultural_machine } # YOLO 類別名稱順序與 CATEGORY_MAP 的值對應(yīng) YOLO_NAMES [agricultural_machine] def convert_annotation(anno_path, img_path, output_path): # 讀取圖片尺寸用于歸一化 with Image.open(img_path) as im: img_w, img_h im.size lines_out [] with open(anno_path, r) as f: for line in f: parts line.strip().split(,) if len(parts) 8: continue # VisDrone 字段順序left, top, width, height, score, category, truncation, occlusion left, top, w, h map(float, parts[:4]) score int(parts[4]) category int(parts[5]) # 跳過無效標注和不需要的類別 if score 0 or category not in CATEGORY_MAP: continue # 跳過寬高為 0 的異???if w 0 or h 0: continue # 轉(zhuǎn)成中心點坐標并歸一化 x_center (left w / 2.0) / img_w y_center (top h / 2.0) / img_h norm_w w / img_w norm_h h / img_h # 裁剪到 0-1 范圍防止越界 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) norm_w min(max(norm_w, 0.0), 1.0) norm_h min(max(norm_h, 0.0), 1.0) class_id CATEGORY_MAP[category] lines_out.append(f{class_id} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}) # 即使沒有目標也要生成空文件YOLO 訓(xùn)練時要求圖片和標簽一一對應(yīng) with open(output_path, w) as f: f.write(\n.join(lines_out)) def main(): # 假設(shè)目錄結(jié)構(gòu)dataset/images/ 和 dataset/annotations/ img_dir dataset/images anno_dir dataset/annotations label_dir dataset/labels os.makedirs(label_dir, exist_okTrue) img_files [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] for img_file in tqdm(img_files, descConverting): base os.path.splitext(img_file)[0] img_path os.path.join(img_dir, img_file) anno_path os.path.join(anno_dir, base .txt) out_path os.path.join(label_dir, base .txt) if not os.path.exists(anno_path): # 沒有標注文件的圖片生成空標簽 open(out_path, w).close() continue convert_annotation(anno_path, img_path, out_path) # 生成 classes.txt方便后續(xù)訓(xùn)練和可視化 with open(os.path.join(label_dir, classes.txt), w) as f: f.write(\n.join(YOLO_NAMES)) if __name__ __main__: main()邏輯說明腳本先讀取圖片真實寬高因為 VisDrone 的 bbox 是絕對像素坐標必須除以圖片尺寸才能歸一化。CATEGORY_MAP 是核心你把哪些 VisDrone 類別歸入農(nóng)機就改這個字典。score 為 0 的行直接跳過那是無效標注。寬高為 0 的框也要跳過否則歸一化后會出現(xiàn) NaN。最后生成空標簽文件是必須的YOLO 訓(xùn)練時如果某張圖沒有對應(yīng)標簽文件會被當(dāng)成負樣本處理但空文件表示這張圖確實沒有目標兩者語義不同。參數(shù)說明VISDRONE_CATEGORIES 只用于打印和調(diào)試不影響轉(zhuǎn)換結(jié)果。CATEGORY_MAP 的鍵是 VisDrone 原始類別編號值是你自定義的 YOLO 類別編號從 0 開始連續(xù)編號。YOLO_NAMES 的順序必須與 CATEGORY_MAP 的值一一對應(yīng)。歸一化后的坐標保留 6 位小數(shù)足夠YOLO 官方實現(xiàn)也是這個精度。2.3 轉(zhuǎn)換后必須做的三項校驗轉(zhuǎn)換腳本跑完不代表數(shù)據(jù)就能用。我每次都會做三項校驗少一項都可能在后頭翻車。第一項檢查標簽文件數(shù)量是否與圖片數(shù)量一致。如果 labels 目錄下的 txt 數(shù)量少于 images 目錄下的圖片數(shù)量說明有圖片沒生成標簽訓(xùn)練時會報錯。用一條 shell 命令就能查echo images: $(ls dataset/images | wc -l) echo labels: $(ls dataset/labels/*.txt | wc -l)第二項隨機抽 10 張圖做可視化把 YOLO 格式的框畫回原圖看位置和類別對不對。我一般用 Python 的 PIL 畫框不依賴 matplotlib速度快。from PIL import Image, ImageDraw import os, random img_dir dataset/images label_dir dataset/labels names open(os.path.join(label_dir, classes.txt)).read().strip().split(\n) for img_file in random.sample(os.listdir(img_dir), 10): base os.path.splitext(img_file)[0] img Image.open(os.path.join(img_dir, img_file)).convert(RGB) draw ImageDraw.Draw(img) w, h img.size label_path os.path.join(label_dir, base .txt) if os.path.exists(label_path): for line in open(label_path): parts line.strip().split() if len(parts) ! 5: continue cid, xc, yc, nw, nh int(parts[0]), *map(float, parts[1:]) x1 (xc - nw / 2) * w y1 (yc - nh / 2) * h x2 (xc nw / 2) * w y2 (yc nh / 2) * h draw.rectangle([x1, y1, x2, y2], outlinered, width2) draw.text((x1, y1), names[cid], fillred) img.save(fcheck_{base}.jpg)第三項統(tǒng)計每個類別的框數(shù)量確認沒有類別為空。如果 agricultural_machine 的框數(shù)為 0說明 CATEGORY_MAP 配錯了或者這個子集里根本沒有農(nóng)機標注。這時候要么換子集要么重新審視類別映射。3. 用 YOLOv8 在 VisDrone 農(nóng)機子集上跑通訓(xùn)練3.1 環(huán)境配置與數(shù)據(jù)目錄組織YOLOv8 的環(huán)境配置現(xiàn)在比兩年前省事多了但 Anaconda 環(huán)境下還是有幾個版本坑。我一般用 Python 3.9 到 3.10PyTorch 選 2.0 以上CUDA 版本跟顯卡驅(qū)動匹配就行。安裝命令就一行pip install ultralyticsUltralytics 包會自動拉取匹配的 PyTorch 版本但如果你之前裝過舊版 torch建議先卸載再裝否則容易出現(xiàn) CUDA 版本沖突。裝完后跑yolo checks看一眼環(huán)境確認 CUDA 可用。數(shù)據(jù)目錄按 YOLO 官方推薦的結(jié)構(gòu)組織dataset/ images/ train/ val/ labels/ train/ val/ data.yamltrain 和 val 的劃分比例我一般用 8:2如果圖片總數(shù)少于 500 張用 7:3驗證集大一點更能反映真實泛化能力。劃分腳本很簡單用 sklearn 的 train_test_split 或者自己寫隨機抽樣都行。注意圖片和標簽要同步移動不能只移圖片。data.yaml 的內(nèi)容如下path: /absolute/path/to/dataset train: images/train val: images/val nc: 1 names: [agricultural_machine]path 必須寫絕對路徑Y(jié)OLOv8 對相對路徑的處理有時候會出玄學(xué)問題。nc 是類別數(shù)names 的順序必須與轉(zhuǎn)換腳本里的 YOLO_NAMES 一致。3.2 訓(xùn)練命令與關(guān)鍵參數(shù)怎么設(shè)訓(xùn)練命令本身不復(fù)雜但參數(shù)設(shè)不對結(jié)果差很多。我常用的起手命令yolo detect train \ datadataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ projectruns/visdrone_agri \ nameexp1model 選 yolov8s.pt 還是 yolov8n.pt取決于你的部署端算力。如果最終要跑在 RK3588 或者 Atlas 這類邊緣設(shè)備上建議從 yolov8n.pt 開始精度不夠再往上換。imgsz 設(shè) 640 是通用起點VisDrone 圖片分辨率普遍偏高如果農(nóng)機目標在圖中占比很小可以嘗試 1280但顯存占用會翻倍。batch 根據(jù)顯存調(diào)16 是 8GB 顯存的保守值。lr0 初始學(xué)習(xí)率 0.01 是 YOLOv8 的默認值如果 loss 震蕩厲害降到 0.005。patience 設(shè) 20 表示 20 個 epoch 沒有提升就早停省時間。訓(xùn)練過程中重點看三個指標box_loss、cls_loss、mAP50。box_loss 下降說明框位置在收斂cls_loss 下降說明類別判斷在改善。如果 box_loss 一直不降檢查標簽格式是不是有越界坐標。如果 cls_loss 降不下去可能是類別映射有問題或者某些類別樣本太少。3.3 訓(xùn)練完怎么驗證模型真的學(xué)到了農(nóng)機訓(xùn)練結(jié)束后runs 目錄下會生成權(quán)重文件和驗證結(jié)果。不要只看 mAP 數(shù)字一定要做可視化推理。用訓(xùn)練好的 best.pt 跑一批驗證集圖片yolo detect predict \ modelruns/visdrone_agri/exp1/weights/best.pt \ sourcedataset/images/val \ conf0.25 \ saveTrue \ projectruns/visdrone_agri/predictconf 設(shè) 0.25 是起點如果發(fā)現(xiàn)漏檢多降到 0.15 試試如果誤檢多升到 0.4。VisDrone 的俯拍視角下農(nóng)機目標有時候會被樹冠或建筑遮擋模型容易漏檢。這時候可以看預(yù)測結(jié)果里哪些圖漏了單獨拿出來分析是標注問題還是模型能力問題。我還會做一件事把驗證集里所有包含農(nóng)機的圖片單獨抽出來算一個子集的 mAP。因為整體 mAP 可能被大量簡單樣本拉高掩蓋了農(nóng)機類別的真實表現(xiàn)。如果農(nóng)機子集 mAP 明顯低于整體說明模型對農(nóng)機特征學(xué)習(xí)不夠需要增加農(nóng)機樣本或做數(shù)據(jù)增強。4. 農(nóng)機檢測訓(xùn)練中的避坑與排查記錄4.1 標簽越界導(dǎo)致訓(xùn)練直接報錯現(xiàn)象訓(xùn)練啟動后幾秒鐘就報錯提示坐標超出 0 到 1 范圍。原因VisDrone 原始標注里有些框的左上角坐標加寬高超過了圖片邊界轉(zhuǎn)換時沒有做裁剪。解決在轉(zhuǎn)換腳本里加一步裁剪把 x_center、y_center、width、height 都限制在 0 到 1 之間。更穩(wěn)妥的做法是先算 x1、y1、x2、y2裁剪到圖片范圍內(nèi)再反算中心點和寬高。我上面給的腳本已經(jīng)做了簡單裁剪但如果原始框完全在圖片外裁剪后寬高會變成 0需要跳過。4.2 類別映射錯位導(dǎo)致模型學(xué)出四不像現(xiàn)象訓(xùn)練 loss 正常下降但推理時把所有農(nóng)機都標成同一個類或者把背景標成農(nóng)機。原因CATEGORY_MAP 的鍵值對寫反了或者 YOLO_NAMES 的順序與 CATEGORY_MAP 的值不對應(yīng)。解決轉(zhuǎn)換完先跑一遍統(tǒng)計腳本打印每個 YOLO 類別對應(yīng)的框數(shù)量確認沒有類別為空、沒有類別數(shù)量異常偏多。另外classes.txt 里的名稱順序必須與 data.yaml 里的 names 完全一致大小寫和空格都要對齊。4.3 驗證集 mAP 虛高但實際部署誤檢嚴重現(xiàn)象驗證集 mAP50 到 0.85但拿真實農(nóng)田視頻跑誤檢率很高把石頭、草垛都認成農(nóng)機。原因VisDrone 的驗證集和訓(xùn)練集來自同一分布圖片風(fēng)格、光照、角度都相似模型過擬合了。解決從真實場景里抽 50 到 100 張圖手工標注后作為獨立測試集不參與訓(xùn)練。如果測試集 mAP 掉到 0.5 以下說明泛化能力不夠需要加數(shù)據(jù)增強比如隨機裁剪、色彩抖動、馬賽克增強。YOLOv8 默認開啟了馬賽克增強但你可以調(diào) mosaic 參數(shù)控制強度。4.4 小目標漏檢嚴重現(xiàn)象大農(nóng)機檢測沒問題但遠處的小農(nóng)機基本漏檢。原因VisDrone 俯拍視角下遠處農(nóng)機可能只有幾十個像素YOLO 的下采樣倍率導(dǎo)致小目標特征丟失。解決把 imgsz 從 640 提到 1280或者用 YOLOv8 的 P2 檢測頭。P2 檢測頭會增加計算量但小目標召回率提升明顯。另一個辦法是切片推理把大圖切成小塊分別檢測再合并適合離線批量處理場景。4.5 訓(xùn)練到一半 loss 突然變成 NaN現(xiàn)象前幾十個 epoch 正常突然 loss 變成 NaN訓(xùn)練中斷。原因?qū)W習(xí)率過高導(dǎo)致梯度爆炸或者某批數(shù)據(jù)里有異常標簽。解決先把 lr0 降到 0.001 重跑如果還出 NaN檢查標簽文件里有沒有空行、有沒有非數(shù)字字符。VisDrone 轉(zhuǎn)換后的標簽文件如果某行只有 4 個字段YOLO 解析時會出錯。用腳本掃一遍所有標簽文件確保每行要么為空要么正好 5 個字段。5. 把農(nóng)機檢測模型推到邊緣設(shè)備前的最后幾步訓(xùn)練出一個能跑的模型只是開始真正落地到農(nóng)場場景還有幾件事必須做。第一件是模型導(dǎo)出。YOLOv8 支持導(dǎo)出 ONNX、TensorRT、OpenVINO 等格式如果你要部署到 RK3588一般走 ONNX 再轉(zhuǎn) RKNN。導(dǎo)出命令yolo export modelruns/visdrone_agri/exp1/weights/best.pt formatonnx opset12 simplifyTrueopset 選 12 是兼容性比較好的版本simplify 會做一層圖優(yōu)化減小模型體積。導(dǎo)出后先用 onnxruntime 跑一遍推理確認輸出與 PyTorch 一致再往邊緣設(shè)備上轉(zhuǎn)。第二件是置信度門限的調(diào)整。YOLO 默認 conf 是 0.25但在農(nóng)田場景下光照變化大、背景雜亂我一般會把部署時的 conf 提到 0.4 到 0.5犧牲一點召回率換準確率。如果業(yè)務(wù)允許漏檢但不能誤檢就設(shè)高如果寧可誤檢也不能漏就設(shè)低。這個門限沒有標準答案拿一批真實場景圖跑一遍畫 precision-recall 曲線選 F1 最高的點。第三件是輸入尺寸的權(quán)衡。邊緣設(shè)備算力有限1280 的輸入可能跑不到實時。我一般會做一次 benchmark測 640、960、1280 三檔的推理耗時和 mAP選一個滿足幀率要求且精度可接受的尺寸。RK3588 上跑 YOLOv8n 加 640 輸入一般能做到 30 FPS 以上夠用了。最后說一個我自己的習(xí)慣每次訓(xùn)練完把 data.yaml、轉(zhuǎn)換腳本、訓(xùn)練命令、驗證結(jié)果截圖全部歸檔到一個文件夾命名帶上日期和數(shù)據(jù)集版本。農(nóng)機檢測這個方向數(shù)據(jù)集會不斷更新模型也會迭代沒有歸檔三個月后你根本記不清當(dāng)時是怎么跑的。這個習(xí)慣幫我省過很多次后悔藥。希望幫到你。本文還有配套的精品資源點擊獲取