據(jù)集格式詳解:自動駕駛目標檢測的數(shù)據(jù)校驗與訓(xùn)練實踐)
簡介面向自動駕駛目標檢測任務(wù)的現(xiàn)成數(shù)據(jù)集包按YOLOv5目錄格式整理可直接投入訓(xùn)練與驗證省去數(shù)據(jù)格式轉(zhuǎn)換與清洗步驟。數(shù)據(jù)內(nèi)容覆蓋卡車、行人、交通信號燈等11個常見道路類別圖像為512×512 RGB圖每張含多目標邊界框標注清晰圖像內(nèi)容完整適合自動駕駛感知、密集目標檢測等場景。資源共2000個文件以txt標簽為主1999個另有1個可視化腳本py標簽文件與圖片一一對應(yīng)訓(xùn)練集21031張、驗證集5266張并分別存放于 datasets-images-train 與 datasets-images-val 分目錄中同時附有11類別對應(yīng)的txt信息文件壓縮包約493MB開箱即用。包內(nèi)另附類別文本文件與可視化腳本運行后即可在隨機圖片上繪制邊界框并保存結(jié)果方便快速核查標注質(zhì)量。目前已有173人學(xué)習(xí)下載適合需要高性價比數(shù)據(jù)支撐的目標檢測練手或項目驗證。1. 拿到一份YOLOV5目錄格式的自動駕駛目標檢測數(shù)據(jù)集先別急著訓(xùn)練做大型自動駕駛道路信息檢測時我拿到一份YOLOV5目錄格式的目標檢測數(shù)據(jù)集11個類別訓(xùn)練集、驗證集都齊了——很多人覺得這就能直接yolo train跑模型了。真跑起來就會意識到數(shù)據(jù)集本身的坑比模型結(jié)構(gòu)多得多標簽索引是不是從 0 開始、坐標有沒有歸一化、訓(xùn)練集和驗證集有沒有按場景切干凈、小目標類別占比是不是低到讓模型直接放棄。這份數(shù)據(jù)集的目錄格式是對的但格式對不等于訓(xùn)練順利。這篇文章按我自己的實踐順序拆解先講清 YOLOV5 目錄格式的每條規(guī)則再講訓(xùn)練集和驗證集怎么劃分、怎么校驗、真正訓(xùn)練時會踩哪些坑最后落到怎么驗證這份數(shù)據(jù)集到底值不值得投入。2. 拆開 YOLOV5 目錄格式目錄結(jié)構(gòu)、標簽編碼與 11 類映射2.1 目錄結(jié)構(gòu)images/labels 的配對規(guī)則與 train/val 劃分YOLOV5 的目錄格式不是隨便把圖片和 txt 放在一起就行。標準做法是項目根目錄下分images和labels兩個主目錄各自內(nèi)部再按train、val分子目錄圖片和標簽的文件名必須完全一致只是擴展名不同例如img_0001.jpg對應(yīng)img_0001.txt。yolov5 官方倉庫里找數(shù)據(jù)時也是這個標準結(jié)構(gòu)做目標檢測數(shù)據(jù)集轉(zhuǎn)換的第一步就是對齊這個格式。autopilot-dataset/ ├── images/ │ ├── train/ # 訓(xùn)練集圖片 │ └── val/ # 驗證集圖片 ├── labels/ │ ├── train/ # 訓(xùn)練集標簽 │ └── val/ # 驗證集標簽 ├── data.yaml # 類別名與路徑配置 └── classes.txt # 類別清單輔助文件我一般會在根目錄再放一個classes.txt內(nèi)容就是 11 個類別的名字每一行一個。它不是 YOLOV5 訓(xùn)練必需的文件但后面寫data.yaml、做標簽校驗、重新聚類 anchor 時都要用提前準備能省不少事。需要注意images和labels的文件配對是按文件名 strict 匹配的任何一方多出一個文件都不會報錯只會導(dǎo)致那個樣本被靜默跳過這是后面要重點排查的問題。2.2 標簽編碼歸一化坐標、類別索引從 0 開始YOLO 系列的標簽格式每行代表一個目標框共五項class x_center y_center width height。這里的x_center、y_center、width、height全部是相對圖像寬度和高度的比值取值范圍在 0 到 1 之間。這與 COCO 的像素坐標格式有本質(zhì)區(qū)別把 COCO 或 VOC 格式轉(zhuǎn)換成 YOLO 格式是必經(jīng)步驟轉(zhuǎn)換公式如下。x_center (x_min x_max) / 2 / image_widthy_center (y_min y_max) / 2 / image_heightwidth (x_max - x_min) / image_widthheight (y_max - y_min) / image_height其中x_min、y_min、x_max、y_max是像素坐標。跑這段轉(zhuǎn)換邏輯時有一個關(guān)鍵點類別索引必須從 0 開始不能從 1 開始。項目里類別索引一旦錯位訓(xùn)練不會報錯但模型會把car學(xué)成pedestrian驗證時 mAP 看起來還行實際推理全亂屬于最隱蔽的坑。所以我拿到數(shù)據(jù)集第一件事就是用腳本掃描所有標簽文件確認類別索引的范圍是 0 到 10而不是 1 到 11。2.3 11 類自動駕駛目標怎么映射到類別索引落實這份數(shù)據(jù)集之前先建一張類別映射表。11 個類別在自動駕駛場景里的常見構(gòu)成大概是car、truck、bus、motorcycle、bicycle、pedestrian、rider、traffic_light、traffic_sign、stop_sign、lane_marker 這類組合。具體類別名以數(shù)據(jù)集實際標注為準我按常見做法列出映射關(guān)系索引類別名典型尺寸規(guī)模說明0car中轎車、SUV數(shù)量最多1truck大貨車、掛車2bus大公交車3motorcycle小摩托車4bicycle小自行車5pedestrian小行人6rider小騎手含摩托車/自行車騎手7traffic_light極小紅綠燈遠距離時很小8traffic_sign小交通標志牌9stop_sign小停車讓行標志10lane_marker極小車道線標記細長形這 11 類里traffic_light和lane_marker是小目標的重災(zāi)區(qū)。遠距離紅綠燈在 640×640 分辨率下可能只有 10×10 像素模型在小目標上先天劣勢。這也是為什么后面要談訓(xùn)練分辨率、anchor 重聚類和數(shù)據(jù)增強策略這三件事都和類別特性強相關(guān)。2.4 為什么自動駕駛數(shù)據(jù)集偏愛 YOLOV5 目錄格式而不是 COCO/VOC目標檢測數(shù)據(jù)集的常見格式有 COCO 的 JSON、VOC 的 XML、YOLO 的 txt 三類。自動駕駛場景下我傾向于用 YOLOV5 目錄格式原因在于標簽讀取效率。COCO 的 JSON 有幾十 MB 到上百 MB每次 dataloader 啟動都要解析整個文件訓(xùn)練時讀樣本也要查字典VOC 的 XML 是單個文件一個目標框列表標注繁瑣但讀取不算慢可它的標簽層級結(jié)構(gòu)對自動駕駛這種大量重疊目標不友好。YOLO 的 txt 格式是純文本一行一個目標讀取零解析成本配合images/labels的目錄結(jié)構(gòu)dataloader 能隨機訪問任意樣本不需要維護全局索引。對自動駕駛這種動輒幾萬張圖片、每張十幾個目標的數(shù)據(jù)集訓(xùn)練時 IO 效率直接影響迭代速度。YOLOV5 目錄格式另一個好處是切分方便train/val就是兩個目錄換比例或換場景子集時只需移動文件不需要重新生成 JSON。數(shù)據(jù)增強工具如 mosaic、mixup 的實現(xiàn)也天然適配這種格式。數(shù)據(jù)可視化、邊界框檢查、訓(xùn)練集成、推理演示這些環(huán)節(jié)我都用 YOLOV5 目錄格式作為事實標準。3. 訓(xùn)練集與驗證集劃分比例、劃分策略和數(shù)據(jù)分布檢查3.1 按場景劃分還是按幀隨機劃分自動駕駛數(shù)據(jù)的特殊性很多人劃分訓(xùn)練集和驗證集時直接對所有圖片做隨機抽樣比如 8:2 分割這在自動駕駛數(shù)據(jù)集上是錯誤做法。自動駕駛數(shù)據(jù)通常來自連續(xù)視頻幀相鄰幀之間高度相似如果同一段視頻的前后幀被隨機分到訓(xùn)練集和驗證集驗證集相當(dāng)于在做開卷考試——模型見過幾乎一樣的畫面指標虛高部署到真實場景立刻打回原形。正確做法是按場景或按視頻序列 ID 劃分。數(shù)據(jù)集如果帶序列 ID 字段就按序列劃分如果沒有就按文件名前綴劃分。假設(shè)文件名是seq_001_frame_0001.jpg這種格式提取seq_001作為分組鍵同一組內(nèi)的所有幀必須全部進入同一個集合。我通常會把 80% 的場景進訓(xùn)練集、20% 的場景進驗證集而不是按圖片數(shù)量直接切分這樣驗證集的指標才有參考意義。3.2 寫一個劃分腳本按文件收集、場景分組、輸出圖片與標簽下面是我常用的按場景 ID 劃分腳本核心是先用os.walk收集圖片路徑再提取場景 ID最后用train_test_split以場景為單位切分。注意腳本要保證images/train、images/val、labels/train、labels/val四個目錄的內(nèi)容同步移動圖片復(fù)制過去標簽也必須對應(yīng)復(fù)制否則會出現(xiàn)圖片有標簽、標簽無圖片的錯位。import os import shutil from collections import defaultdict from sklearn.model_selection import train_test_split src_images raw_data/images src_labels raw_data/labels dst_dir autopilot_dataset train_ratio 0.8 # 收集所有圖片文件 image_paths [] for root, _, files in os.walk(src_images): for file in files: if file.lower().endswith((.jpg, .jpeg, .png)): image_paths.append(os.path.join(root, file)) # 按文件名前綴提取場景ID例如 seq_001_frame_0001.jpg - seq_001 def get_scene_id(path): fname os.path.basename(path) # 按數(shù)據(jù)集的命名規(guī)律調(diào)整分割規(guī)則 return fname.split(_frame_)[0] scene_to_images defaultdict(list) for img_path in image_paths: scene_id get_scene_id(img_path) scene_to_images[scene_id].append(img_path) scene_ids list(scene_to_images.keys()) train_scenes, val_scenes train_test_split( scene_ids, train_sizetrain_ratio, random_state42 ) # 按場景切分并復(fù)制到目標目錄 for split_name, scene_list in [(train, train_scenes), (val, val_scenes)]: for scene_id in scene_list: for img_path in scene_to_images[scene_id]: label_path img_path.replace(src_images, src_labels).rsplit(., 1)[0] .txt dst_img os.path.join(dst_dir, images, split_name, os.path.basename(img_path)) dst_lbl os.path.join(dst_dir, labels, split_name, os.path.basename(label_path)) os.makedirs(os.path.dirname(dst_img), exist_okTrue) os.makedirs(os.path.dirname(dst_lbl), exist_okTrue) shutil.copy2(img_path, dst_img) shutil.copy2(label_path, dst_lbl)邏輯說明先收集所有圖片路徑按場景 ID 分組再對場景 ID 做隨機劃分。這樣劃分的最小單位是場景而不是單張圖片驗證集中的任何幀都不會在訓(xùn)練集中出現(xiàn)同場景畫面。random_state42固定隨機種子保證重復(fù)執(zhí)行劃分結(jié)果一致這在我反復(fù)調(diào)試時很重要否則每次跑訓(xùn)練驗證集都變結(jié)果無法對比。參數(shù)說明train_ratio控制訓(xùn)練集比例一般取 0.8數(shù)據(jù)量少可以提到 0.85 或 0.9。get_scene_id函數(shù)里的分割規(guī)則需要按數(shù)據(jù)集的命名格式調(diào)整如果文件名是20240101_123456_01.jpg這種時間戳格式就直接取整段前綴。還要注意標簽路徑的替換邏輯img_path.replace(src_images, src_labels)要求圖片與標簽?zāi)夸浗Y(jié)構(gòu)對稱如果不對稱需要單獨映射。3.3 類別分布統(tǒng)計腳本統(tǒng)計每類框數(shù)發(fā)現(xiàn)類別不平衡劃分完成后第一件事不是訓(xùn)練而是統(tǒng)計類別分布。自動駕駛數(shù)據(jù)集的類別分布極不均衡car可能占一半以上stop_sign可能只有幾百個框。YOLOV5 的損失函數(shù)默認對每個類別一視同仁類別不平衡會導(dǎo)致少樣本類別幾乎學(xué)不出來。統(tǒng)計腳本可以直接解析每個 txt 文件的第一列。import os from collections import Counter label_dirs [autopilot_dataset/labels/train, autopilot_dataset/labels/val] class_counter Counter() for label_dir in label_dirs: for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue with open(os.path.join(label_dir, label_file), r) as f: for line in f: line line.strip() if not line: continue class_id int(line.split()[0]) class_counter[class_id] 1 for class_id in sorted(class_counter.keys()): print(fclass {class_id}: {class_counter[class_id]} boxes)邏輯說明遍歷所有標簽文件讀取每一行的第一個字段作為類別索引累加計數(shù)。Counter會按類別自動聚合。打印結(jié)果后對照映射表如果發(fā)現(xiàn)某個類別的框數(shù)比其他少一個數(shù)量級訓(xùn)練前就要處理。參數(shù)說明這個腳本是純讀取不修改數(shù)據(jù)。如果類別不平衡嚴重常見做法是給 YOLOV5 的data.yaml中的類別配置權(quán)重或者在訓(xùn)練時開啟針對少樣本的過采樣。但更穩(wěn)妥的做法是直接增加該類別的數(shù)據(jù)量因為權(quán)重提升容易讓模型對多數(shù)的誤檢率上升。少樣本類別如stop_sign框數(shù)低于 1000 時我會優(yōu)先考慮補數(shù)據(jù)而不是調(diào)權(quán)重。3.4 data.yaml 的寫法路徑、類別名與索引保持一致data.yaml是 YOLOV5 訓(xùn)練的入口配置路徑寫錯或類別索引不匹配都會直接導(dǎo)致訓(xùn)練失敗或?qū)W錯。YOLOV5 目錄格式下的data.yaml基本結(jié)構(gòu)如下。path: ../autopilot_dataset train: images/train val: images/val nc: 11 names: 0: car 1: truck 2: bus 3: motorcycle 4: bicycle 5: pedestrian 6: rider 7: traffic_light 8: traffic_sign 9: stop_sign 10: lane_markerpath是相對data.yaml文件的上一級目錄路徑train和val是相對于path的目錄。nc是類別總數(shù)必須與實際類別數(shù)一致。names字典的 key 和標簽文件里的 class 索引必須一一對應(yīng)順序不能錯。有一個容易被忽略的點path配置寫絕對路徑時換電腦訓(xùn)練就會失效我習(xí)慣用相對路徑配合在工作目錄下啟動訓(xùn)練命令這樣整個項目可以遷移到另一臺機器直接跑。4. 訓(xùn)練前必須做的三件校驗圖像、標簽、配對關(guān)系4.1 壞圖與截斷圖檢測PIL 驗證 SHA 校驗訓(xùn)練集圖片來自采集車或公開道路數(shù)據(jù)集下載、壓縮、傳輸過程都可能產(chǎn)生損壞圖片。YOLOV5 的 dataloader 讀圖失敗時通常不會中斷訓(xùn)練而是跳過該樣本但如果壞圖占比超過 1%實際訓(xùn)練樣本數(shù)就和配置對不上影響收斂效果。我用 PIL 做基礎(chǔ)驗證再配合文件大小過濾。from PIL import Image import os img_dir autopilot_dataset/images/train bad_images [] for img_file in os.listdir(img_dir): if not img_file.lower().endswith((.jpg, .jpeg, .png)): continue img_path os.path.join(img_dir, img_file) try: with Image.open(img_path) as img: img.verify() # 檢查文件完整性 # 額外驗證分辨率 width, height img.size if width 32 or height 32: bad_images.append((img_path, too_small)) except Exception as e: bad_images.append((img_path, str(e))) for path, reason in bad_images: print(fBAD: {path} - {reason})邏輯說明img.verify()會讀取文件的解碼結(jié)構(gòu)遇到截斷或損壞會拋異常比單純檢查文件大小可靠。文件實際上能打開但分辨率過低的圖片也要過濾因為 YOLOV5 在訓(xùn)練時會將圖片 resize 到 640×640一張 20×20 的圖片會被拉伸到嚴重失真模型學(xué)到的是噪聲。參數(shù)說明分辨率閾值 32×32 是我常用的下限具體取決于你的img_size訓(xùn)練參數(shù)。YOLOV5 的 mosaic 增強會拼接四張圖如果一張圖過小mosaic 增強時拉大變形的程度會很夸張?;叶葓D不需要過濾YOLOV5 的 dataloader 會自動轉(zhuǎn)成三通道但如果你發(fā)現(xiàn)灰度圖占比過高建議檢查數(shù)據(jù)來源是否采集出了問題。4.2 標簽內(nèi)容合法性校驗坐標范圍、越界、重復(fù)框YOLO 標簽里五個字段都是浮點數(shù)類別索引應(yīng)為整數(shù)中心點坐標和寬高都應(yīng)滿足約束條件。我見過不少標簽文件里的坐標小于 0 或大于 1原因多是標注工具導(dǎo)出時未歸一化、或者在轉(zhuǎn)換腳本里把長邊除以了寬邊。這類數(shù)據(jù)直接訓(xùn)練會導(dǎo)致 loss 不收斂或精度極差。下面這個腳本能找出所有非法標簽。import os label_dirs [autopilot_dataset/labels/train, autopilot_dataset/labels/val] errors [] for label_dir in label_dirs: for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue label_path os.path.join(label_dir, label_file) with open(label_path, r) as f: lines f.readlines() if len(lines) 0: errors.append((label_path, empty_file)) continue seen_boxes set() for line_num, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: errors.append((label_path, fline {line_num}: field_count ! 5)) continue try: cls int(float(parts[0])) x_c, y_c, w, h map(float, parts[1:]) except ValueError: errors.append((label_path, fline {line_num}: non-numeric value)) continue if cls 0 or cls 10: errors.append((label_path, fline {line_num}: class index {cls} out of range)) if not (0 x_c 1 and 0 y_c 1 and 0 w 1 and 0 h 1): errors.append((label_path, fline {line_num}: coord out of range)) # 檢查重復(fù)框類別坐標完全一致 box_key (cls, round(x_c, 6), round(y_c, 6), round(w, 6), round(h, 6)) if box_key in seen_boxes: errors.append((label_path, fline {line_num}: duplicate box)) seen_boxes.add(box_key) for path, msg in errors: print(fINVALID: {path} - {msg}) print(ftotal errors: {len(errors)})邏輯說明逐行解析每個標簽文件驗證字段數(shù)量、數(shù)值類型、類別范圍、坐標范圍。坐標等于 0 的寬度或高度是無效的因為模型無法學(xué)習(xí)到零大小的目標。重復(fù)框做精確匹配對比標注工具在合并多次標注結(jié)果時容易產(chǎn)生完全一樣的重復(fù)行重復(fù)框會讓模型對同一個目標重復(fù)計算損失導(dǎo)致對該目標的預(yù)測置信度異常偏高。參數(shù)說明類別索引范圍cls 10要按你數(shù)據(jù)集的類別數(shù)調(diào)整。坐標范圍校驗中x_c 0合法但width 0非法需要區(qū)分。如果文件里只有一行0.5 0.5 0.1 0.1這種坐標但寬高為 0就是標注工具導(dǎo)出問題這種文件訓(xùn)練時不會報錯只會默默影響那個樣本的 loss。4.3 圖像與標簽配對檢查找出未配對文件配對檢查是訓(xùn)練前最容易被跳過的步驟。YOLOV5 的 dataloader 在加載時會先查找同名標簽文件找不到就跳過該圖或該標簽。如果 labels 目錄里的文件名與 images 不對應(yīng)錯誤不會報出來訓(xùn)練日志里的樣本數(shù)會變少。import os images_train set(os.listdir(autopilot_dataset/images/train)) labels_train set(os.listdir(autopilot_dataset/labels/train)) img_stems {os.path.splitext(f)[0] for f in images_train} lbl_stems {os.path.splitext(f)[0] for f in labels_train} missing_labels img_stems - lbl_stems missing_images lbl_stems - img_stems if missing_labels: print(fimages without labels: {len(missing_labels)}) for s in list(missing_labels)[:10]: print(f {s}) if missing_images: print(flabels without images: {len(missing_images)}) for s in list(missing_images)[:10]: print(f {s}) if not missing_labels and not missing_images: print(all paired OK)邏輯說明把images/train和labels/train下的文件名去掉擴展名后取集合兩個集合做差集就能找出未配對的文件。圖片有標簽缺失的要么補標簽要么刪圖標簽有但圖片缺失的要么補圖要么刪標簽。需要特別注意的是這個腳本只檢查 train 目錄val 目錄要再跑一遍。參數(shù)說明腳本本身沒有參數(shù)但如果數(shù)據(jù)集按子目錄組織比如images/train/daytime和images/train/night需要把os.listdir改成os.walk遞歸收集文件。很多自動駕駛數(shù)據(jù)集會按天氣或時段分子目錄但 YOLOV5 標準格式要求所有訓(xùn)練圖片在同一個目錄下下載后需要合并目錄。4.4 抽幀可視化把標注框畫回圖像上肉眼檢查偏移腳本校驗只能查數(shù)值合法性標注框是否真的框住了目標要靠可視化確認。每類隨機抽 20 張圖把標簽畫回圖上檢查框的位置是否偏移、類別是否正確、有沒有漏標注嚴重的圖。下面這段用 OpenCV 畫框的腳本可以直接跑。import cv2 import os import random label_dir autopilot_dataset/labels/val image_dir autopilot_dataset/images/val output_dir visual_check os.makedirs(output_dir, exist_okTrue) class_names [ car, truck, bus, motorcycle, bicycle, pedestrian, rider, traffic_light, traffic_sign, stop_sign, lane_marker ] colors [ (0, 255, 0), (0, 165, 255), (0, 0, 255), (255, 0, 0), (255, 255, 0), (255, 0, 255), (0, 255, 255), (128, 128, 0), (128, 0, 128), (0, 128, 128), (255, 128, 0) ] label_files random.sample(os.listdir(label_dir), min(20, len(os.listdir(label_dir)))) for label_file in label_files: stem os.path.splitext(label_file)[0] label_path os.path.join(label_dir, label_file) img_path os.path.join(image_dir, stem .jpg) if not os.path.exists(img_path): continue img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls int(float(parts[0])) x_c, y_c, bw, bh map(float, parts[1:]) x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls], 2) cv2.putText( img, class_names[cls], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[cls], 1 ) out_path os.path.join(output_dir, stem _check.jpg) cv2.imwrite(out_path, img) print(fsaved: {out_path})邏輯說明隨機抽取驗證集中的 20 個標簽文件讀對應(yīng)圖片把每個歸一化坐標乘以圖像寬高還原成像素坐標用不同顏色畫框并標注類別名。重點看三件事框是否貼住目標邊緣、類別和實際目標是否匹配、有沒有一張圖里被重復(fù)標注或完全漏標的情況。參數(shù)說明random.sample的 20 是抽樣數(shù)量類別少時可以每類固定抽固定數(shù)量保證每個類都覆蓋到。顏色列表長度要大于等于類別數(shù)否則索引越界。輸出目錄visual_check是新建的不會覆蓋原圖。這一步不能省標注偏移在自動駕駛場景中尤其危險——紅綠燈偏移一兩個像素在 640 分辨率下還能忍但行人框偏了半個身位訓(xùn)練出來的模型在真車上就得誤判。5. 訓(xùn)練集和驗證集的常見翻車現(xiàn)場與排查清單5.1 現(xiàn)象訓(xùn)練日志顯示的樣本數(shù)比 images 目錄文件數(shù)少第一次用這個數(shù)據(jù)集訓(xùn)練時啟動日志里顯示的train images數(shù)量比ls images/train | wc -l的數(shù)字少了幾百張當(dāng)時沒在意后來才發(fā)現(xiàn)是數(shù)據(jù)里有壞圖和未配對標簽。YOLOV5 的 dataloader 初始化時會掃描所有圖片并嘗試匹配對應(yīng)標簽匹配失敗的靜默跳過不報錯。原因一是 images 目錄里有一些損壞或格式不被支持的圖片文件二是 labels 目錄里有圖片找不到對應(yīng)標簽三是文件擴展名大小寫不一致比如圖片是.JPG而標簽生成時用的是.jpg。解決用 4.1 的壞圖檢測腳本掃一遍 images用 4.3 的配對腳本比對 train 和 val 兩個集合。把無法配對的圖片直接移出目錄不要留在原目錄里否則每次訓(xùn)練啟動日志都嚇人一跳。擴展名大小寫問題在 Linux 上訓(xùn)練時最常出現(xiàn)建議統(tǒng)一轉(zhuǎn)成小寫。5.2 現(xiàn)象loss 不下降mAP 約等于 0訓(xùn)練跑了 50 個 epochbox_loss 和 cls_loss 都在高位震蕩驗證集 mAP 接近 0這時模型實際上什么都沒學(xué)到。我排查了一圈發(fā)現(xiàn)標簽類別索引和data.yaml里的類別映射錯位了。原因標簽文件中的類別索引從 1 開始而 YOLOV5 的類別索引從 0 開始。比如原本應(yīng)該是class 0 car但標簽文件里寫的是class 1 car模型把 car 當(dāng)成第二類所有類別整體偏移一位。還有一種可能是data.yaml里的nc和實際類別數(shù)不一致比如nc: 11但標簽文件里出現(xiàn)索引 11 的行超出范圍的那部分樣本在損失計算時行為異常。解決用 4.2 的類別范圍校驗?zāi)_本掃描所有標簽文件看最小索引是多少、最大索引是多少。如果最小索引是 1寫一個批量轉(zhuǎn)換腳本把所有標簽文件的第一個字段減 1。然后同步檢查data.yaml的names列表順序和標簽索引一一對應(yīng)。這個邏輯必須嚴格執(zhí)行改標簽或改 yaml 選一個即可但兩個都改等于沒改。5.3 現(xiàn)象小目標類別 AP 極低尤其是 traffic_light 和 lane_marker訓(xùn)練結(jié)束后看每個類別的 APcar、truck能到 0.8 以上但traffic_light只有 0.2lane_marker甚至幾乎為 0。這不是模型的問題是數(shù)據(jù)集本身的性質(zhì)決定的。原因自動駕駛場景中遠距離的小目標在 640×640 分辨率下只有幾個像素。YOLOV5 默認的 anchor 尺寸是針對 COCO 的通用目標設(shè)計的對微小的交通燈和車道線并不友好另外這些小目標類別樣本數(shù)少在類別不平衡下模型傾向于忽略它們。解決有兩步第一步在訓(xùn)練時把--img提高到 1280小目標在更高分辨率下像素數(shù)變多檢測率明顯上升代價是顯存占用和訓(xùn)練時間增加第二步針對標簽數(shù)據(jù)重新聚類 anchorYOLOV5 提供腳本自動做這件事命令是python utils/autoanchor.py --cfg yolov5s.yaml它會分析你的標簽分布生成適配小目標的 anchor 集合。數(shù)據(jù)增強上我一般會調(diào)高--hsv_h和--hsv_s讓模型在顏色維度更魯棒但這對小目標的實際幫助有限主要還是分辨率和 anchor。如果數(shù)據(jù)集的類別分布統(tǒng)計顯示小目標框數(shù)遠低于其他類別也可以考慮用圖像切塊的方式把小目標放大后單獨訓(xùn)練。5.4 現(xiàn)象驗證集指標虛高部署后性能打回原形訓(xùn)練時驗證集 mAP 0.85模型導(dǎo)出后到實際道路視頻上一測大量漏檢和誤檢。最開始我以為是過擬合后來排查發(fā)現(xiàn)驗證集劃分方式不對。原因訓(xùn)練集和驗證集是從同一批視頻幀里隨機切分的同一段路的連續(xù)幀同時出現(xiàn)在兩邊。模型在訓(xùn)練時見過幾乎一樣的畫面驗證時當(dāng)然表現(xiàn)好但面對沒見過的場景就露餡。解決按 3.1 的方法重新劃分以場景 ID 為最小單位。具體做法是打開原始數(shù)據(jù)看文件名如果文件名包含時間戳或場景編號直接用字符串切分提取前綴把相同前綴的幀歸到同一個場景。重新劃分后驗證集 AP 掉到 0.7 都是正常的這個 0.7 才代表真實泛化能力。如果原始數(shù)據(jù)沒有場景信息可以用圖像相似度做聚類把相似度高的幀分到同一邊但這實現(xiàn)起來復(fù)雜優(yōu)先檢查文件名或目錄結(jié)構(gòu)。5.5 現(xiàn)象中文路徑或特殊字符導(dǎo)致訓(xùn)練中斷在 Windows 上用中文用戶名的電腦訓(xùn)練啟動時報錯找不到文件或者讀取到一半報編碼錯誤這在跨平臺協(xié)作時尤其常見。原因YOLOV5 依賴 OpenCV 和 numpy 處理圖片OpenCV 在 Windows 上對中文路徑支持差標簽文件里包含中文字符也會導(dǎo)致編碼錯誤。Linux 服務(wù)器上如果路徑里有空格或特殊符號有些 shell 命令和工具會解析出錯。解決最穩(wěn)妥的做法是一開始就統(tǒng)一規(guī)范數(shù)據(jù)集路徑和文件命名只用英文字母、數(shù)字、下劃線目錄結(jié)構(gòu)里不出現(xiàn)空格。如果已經(jīng)有一批中文目錄的文件寫個腳本把文件復(fù)制到新的純英文路徑下不要直接在原目錄上做重命名因為還要同步改標簽文件里的路徑引用。另外注意 txt 文件要用 UTF-8 編碼保存不要用 Windows 默認的 GBK。這個問題會反復(fù)出現(xiàn)在團隊協(xié)作中最好的習(xí)慣是把數(shù)據(jù)集放在服務(wù)器固定目錄~/data/autopilot_dataset這種位置所有人按同一套路徑規(guī)范操作。6. 訓(xùn)練后的驗證技巧混淆矩陣與類別 AP 決定數(shù)據(jù)集的去留訓(xùn)練結(jié)束后驗證集上的 mAP 只是門檻真正決定這份數(shù)據(jù)集值不值得投入的是驗證集上每個類別的詳細表現(xiàn)。我訓(xùn)練完第一件事是運行驗證腳本看混淆矩陣和每類 APYOLOV5 的命令很直接。python val.py --weights runs/train/exp/weights/best.pt --data autopilot_dataset/data.yaml --img 640 --conf 0.25 --iou 0.45運行完成后runs/val/exp目錄下會生成confusion_matrix.png、F1_curve.png和PR_curve.png。我重點看混淆矩陣里background那一行如果很多目標被預(yù)測成背景說明模型漏檢嚴重如果traffic_light經(jīng)常被預(yù)測成traffic_sign說明這兩類特征接近且訓(xùn)練數(shù)據(jù)里出現(xiàn)頻次差異太大。這些小細節(jié)直接決定下一步是加數(shù)據(jù)、調(diào)權(quán)重還是換模型結(jié)構(gòu)。對數(shù)據(jù)集質(zhì)量本身的評估我有一個習(xí)慣挑驗證集里所有 mAP 低于 0.5 的類別單獨跑一次預(yù)測把預(yù)測結(jié)果可視化出來逐張看圖判斷是標注問題還是數(shù)據(jù)量問題。如果發(fā)現(xiàn)某張圖里明明有 5 個行人但標簽文件里只有 2 個那份標簽就是漏標需要回爐。每次拿到新的自動駕駛數(shù)據(jù)集我都會做一遍這個檢查現(xiàn)在已經(jīng)成為固定動作。最后再說一句11 類道路目標的數(shù)據(jù)集做出來后先拿 500 張圖做一輪冒煙測試確認 loss 能降、mAP 不是 0再上全量訓(xùn)練這能幫你省下大量調(diào)試周期。希望這篇筆記對你有幫助。本文還有配套的精品資源點擊獲取