據(jù)集VOC格式轉(zhuǎn)YOLO訓(xùn)練全流程實(shí)戰(zhàn)指南)
簡(jiǎn)介這是一份面向計(jì)算機(jī)視覺(jué)目標(biāo)檢測(cè)任務(wù)的疲勞駕駛數(shù)據(jù)集采用Pascal VOC格式圍繞駕駛疲勞狀態(tài)識(shí)別場(chǎng)景定義了closed_eye、closed_mouth、open_eye、open_mouth四類(lèi)標(biāo)注方便直接訓(xùn)練車(chē)輛駕駛艙內(nèi)的閉眼與張嘴檢測(cè)模型。壓縮包約368.57MB共8725個(gè)文件內(nèi)含4362張jpg原圖、4362個(gè)xml標(biāo)注文件及1份使用說(shuō)明txt目錄結(jié)構(gòu)清晰便于按類(lèi)別與圖片索引檢索。所有標(biāo)注均由labelImg工具按照矩形框規(guī)則完成四類(lèi)目標(biāo)框數(shù)統(tǒng)計(jì)為睜眼4903個(gè)、閉嘴3343個(gè)、閉眼2485個(gè)、張嘴936個(gè)可據(jù)此直觀(guān)了解類(lèi)別分布并劃分訓(xùn)練驗(yàn)證集。目前已有2802人學(xué)習(xí)下載對(duì)于需要構(gòu)建疲勞駕駛數(shù)據(jù)集或驗(yàn)證目標(biāo)檢測(cè)算法的開(kāi)發(fā)者來(lái)說(shuō)這套數(shù)據(jù)可放心用于模型訓(xùn)練與效果評(píng)測(cè)省去自行采集圖像與手工標(biāo)注的大量時(shí)間同時(shí)數(shù)據(jù)集只承諾準(zhǔn)確合理的標(biāo)注不夸大模型精度比較客觀(guān)可信。1. 疲勞駕駛數(shù)據(jù)集能做什么4類(lèi)別4362張的定位與選型做疲勞駕駛檢測(cè)的工程落地最卡人的往往不是模型結(jié)構(gòu)而是標(biāo)注數(shù)據(jù)。公開(kāi)的疲勞駕駛數(shù)據(jù)集VOC格式4類(lèi)別4362張正好卡在一個(gè)非常實(shí)用的位置量級(jí)不大不小類(lèi)別聚焦在駕駛艙內(nèi)的典型姿態(tài)格式又是目標(biāo)檢測(cè)里最通用的Pascal VOC。拿它做算法驗(yàn)證、跑通訓(xùn)練流程、做畢業(yè)設(shè)計(jì)或者給公司預(yù)研做可行性測(cè)試都比從零標(biāo)注省一到兩周時(shí)間。適合的人群很明確正在學(xué)YOLO訓(xùn)練流程的初學(xué)者以及需要快速驗(yàn)證檢測(cè)方案的一線(xiàn)工程師。下面從格式拆解、數(shù)據(jù)轉(zhuǎn)換、訓(xùn)練驗(yàn)證到踩坑記錄完整過(guò)一遍。2. VOC格式到底是什么XML標(biāo)注結(jié)構(gòu)、目錄組織與類(lèi)別映射2.1 從下載目錄看VOC組織方式拿到數(shù)據(jù)集后第一件事不是直接扔進(jìn)訓(xùn)練腳本而是先把目錄結(jié)構(gòu)摸清楚。Pascal VOC格式的約定組織方式很固定這個(gè)數(shù)據(jù)集沿用了同一套規(guī)范fatigue_driving_dataset/ ├── JPEGImages/ # 4362張?jiān)紙D片jpg格式 ├── Annotations/ # 與圖片同名的xml標(biāo)注文件 ├── ImageSets/ │ └── Main/ # train.txt / val.txt / trainval.txt └── labels/ # 轉(zhuǎn)換后的yolo格式標(biāo)注自己生成JPEGImages目錄存放原始圖片Annotations目錄存放每張圖片對(duì)應(yīng)的XML標(biāo)注文件兩者通過(guò)文件名一一對(duì)應(yīng)。ImageSets/Main目錄下的txt文件記錄了哪些圖片用于訓(xùn)練、哪些用于驗(yàn)證這是VOC格式與COCO格式最明顯的區(qū)別——VOC用txt文件維護(hù)數(shù)據(jù)集劃分而COCO用json文件一次搞定。我一般會(huì)先寫(xiě)一個(gè)統(tǒng)計(jì)腳本核對(duì)圖片數(shù)量和標(biāo)注數(shù)量的對(duì)應(yīng)關(guān)系。4362張圖的規(guī)模不算大但即便這樣也會(huì)出現(xiàn)少數(shù)圖片缺標(biāo)注或者標(biāo)注文件損壞的情況提前暴露問(wèn)題比訓(xùn)練到一半再排查省事得多。2.2 XML標(biāo)注字段逐個(gè)拆解打開(kāi)任意一個(gè)XML標(biāo)注文件核心結(jié)構(gòu)大概是這樣的annotation folderJPEGImages/folder filenameimg_20231105_142358.jpg/filename size width1920/width height1080/height depth3/depth /size object nameyawn/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin632/xmin ymin401/ymin xmax891/xmax ymax587/ymax /bndbox /object /annotationfilename字段是圖片文件名size字段記錄圖片的寬高和通道數(shù)bndbox里是標(biāo)注框的左上角和右下角坐標(biāo)。這些坐標(biāo)是絕對(duì)值單位是像素這也是VOC轉(zhuǎn)YOLO時(shí)最需要關(guān)注的地方——YOLO要求的是歸一化的中心點(diǎn)坐標(biāo)和寬高。標(biāo)注框的坐標(biāo)精度直接影響訓(xùn)練效果。標(biāo)注邊界貼合目標(biāo)邊緣模型的定位損失才能收斂得干凈。如果數(shù)據(jù)集里的標(biāo)注本身就畫(huà)得松訓(xùn)練出來(lái)的模型預(yù)測(cè)框也會(huì)偏大這屬于數(shù)據(jù)質(zhì)量問(wèn)題后期用任何后處理手段都很難完全彌補(bǔ)。2.3 類(lèi)別信息與標(biāo)簽順序這個(gè)數(shù)據(jù)集包含4個(gè)類(lèi)別針對(duì)疲勞駕駛場(chǎng)景常見(jiàn)的類(lèi)別劃分是正常駕駛、打哈欠、閉眼和使用手機(jī)具體類(lèi)別名要以Annotations里object的name字段為準(zhǔn)。這里有一條重要的工程規(guī)則類(lèi)別順序一旦確定在整個(gè)訓(xùn)練和推理鏈路中就不能再變。# 在數(shù)據(jù)集根目錄下執(zhí)行提取全部類(lèi)別名并去重排序 grep -h name Annotations/*.xml | sort | uniq -c這條命令會(huì)輸出每個(gè)類(lèi)別出現(xiàn)的次數(shù)同時(shí)確認(rèn)類(lèi)別名的拼寫(xiě)是否一致。比如閉眼這個(gè)類(lèi)別如果在部分標(biāo)注文件里寫(xiě)成了close_eye在另一些文件里寫(xiě)成了closed_eye就會(huì)被統(tǒng)計(jì)成兩個(gè)類(lèi)別而實(shí)際標(biāo)注的語(yǔ)義是一樣的。這種問(wèn)題在人工標(biāo)注的數(shù)據(jù)集里非常常見(jiàn)也是數(shù)據(jù)清洗的第一步。確認(rèn)無(wú)誤后把所有類(lèi)別寫(xiě)成一個(gè)固定的類(lèi)別映射表按字母順序或者按邏輯順序排列均可但必須記住訓(xùn)練時(shí)用的類(lèi)別順序要和推理時(shí)類(lèi)別文件里的順序完全一致否則模型輸出結(jié)果的類(lèi)別對(duì)應(yīng)關(guān)系就是錯(cuò)的。3. 用Python把VOC轉(zhuǎn)成YOLO格式腳本與邊界坑3.1 轉(zhuǎn)換代碼與關(guān)鍵參數(shù)YOLO系列模型不認(rèn)XML只認(rèn)txt格式的標(biāo)注文件每一行對(duì)應(yīng)一個(gè)目標(biāo)框。所以動(dòng)手訓(xùn)練之前必須先把VOC格式轉(zhuǎn)成YOLO格式。轉(zhuǎn)換的核心邏輯并不復(fù)雜讀取XML里的bndbox坐標(biāo)計(jì)算中心點(diǎn)和寬高再分別除以圖片的寬和高做歸一化。import os import xml.etree.ElementTree as ET # 類(lèi)別映射表順序一旦確定不要隨意調(diào)整 CLASS_MAPPING { normal: 0, # 正常駕駛 yawn: 1, # 打哈欠 close_eye: 2, # 閉眼 phone: 3 # 使用手機(jī) } def convert_voc_to_yolo(xml_path, output_dir, image_width, image_height): 將單個(gè)VOC xml標(biāo)注轉(zhuǎn)換為YOLO格式的txt標(biāo)注 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAPPING: print(f警告: 未知類(lèi)別 {name} 出現(xiàn)在 {xml_path}) continue # 讀取像素坐標(biāo) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 防御過(guò)濾無(wú)效坐標(biāo) if xmax xmin or ymax ymin: print(f警告: 無(wú)效坐標(biāo) {xml_path}) continue # 轉(zhuǎn)成中心點(diǎn)坐標(biāo)和寬高并歸一化 center_x (xmin xmax) / 2.0 / image_width center_y (ymin ymax) / 2.0 / image_height box_width (xmax - xmin) / image_width box_height (ymax - ymin) / image_height # 防御歸一化后數(shù)值必須落在0~1之間 if not (0 center_x 1 and 0 center_y 1): print(f警告: 歸一化越界 {xml_path}) continue lines.append(f{CLASS_MAPPING[name]} {center_x:.6f} {center_y:.6f} {box_width:.6f} {box_height:.6f}) # 寫(xiě)輸出文件名稱(chēng)與xml保持一致 txt_path os.path.join(output_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))這段代碼做好了三層防御是我在實(shí)際轉(zhuǎn)換過(guò)程中逐步補(bǔ)上的。第一層是過(guò)濾未知類(lèi)別避免拼寫(xiě)不一致的類(lèi)別混進(jìn)訓(xùn)練集第二層是過(guò)濾坐標(biāo)反轉(zhuǎn)的無(wú)效框這種問(wèn)題通常出現(xiàn)在標(biāo)注工具導(dǎo)出異常時(shí)第三層是過(guò)濾歸一化越界的坐標(biāo)正常情況下不應(yīng)發(fā)生一旦出現(xiàn)就說(shuō)明XML里的坐標(biāo)值或圖片尺寸讀取有誤。3.2 按固定隨機(jī)種子劃分train/val數(shù)據(jù)集自帶的ImageSets/Main目錄里可能已經(jīng)有劃分好的train.txt和val.txt但穩(wěn)妥的做法是自己重新劃分一次因?yàn)樵紕澐值念?lèi)別分布未必符合你的驗(yàn)證需求。劃分時(shí)必須固定隨機(jī)種子保證每次運(yùn)行結(jié)果一致。import random from pathlib import Path random.seed(42) # 固定隨機(jī)種子保證劃分結(jié)果可復(fù)現(xiàn) image_files list(Path(JPEGImages).glob(*.jpg)) random.shuffle(image_files) val_ratio 0.15 val_count int(len(image_files) * val_ratio) val_files image_files[:val_count] train_files image_files[val_count:] # 寫(xiě)入train.txt和val.txt with open(train.txt, w) as f: for img in train_files: f.write(str(img.resolve()) \n) with open(val.txt, w) as f: for img in val_files: f.write(str(img.resolve()) \n) print(f訓(xùn)練集 {len(train_files)} 張驗(yàn)證集 {len(val_files)} 張)15%的驗(yàn)證集比例在這個(gè)數(shù)據(jù)規(guī)模下比較合理。4362張圖驗(yàn)證集約654張對(duì)評(píng)估模型性能來(lái)說(shuō)已經(jīng)足夠。劃分完成后強(qiáng)烈建議統(tǒng)計(jì)一下每類(lèi)目標(biāo)在訓(xùn)練集和驗(yàn)證集里的數(shù)量分布如果打哈欠這個(gè)類(lèi)別在訓(xùn)練集里有1500個(gè)目標(biāo)、在驗(yàn)證集里只有80個(gè)分布還算均衡如果出現(xiàn)訓(xùn)練集有1600個(gè)、驗(yàn)證集只有30個(gè)的情況就說(shuō)明劃分隨機(jī)性不夠好需要重新劃分或者做類(lèi)別分層采樣。3.3 轉(zhuǎn)換前后怎么自檢轉(zhuǎn)換完成后不要急著訓(xùn)練先用可視化和統(tǒng)計(jì)手段自檢一遍。我習(xí)慣做法是生成一個(gè)檢測(cè)結(jié)果標(biāo)注預(yù)覽圖把YOLO格式的坐標(biāo)還原到圖片上直觀(guān)看看標(biāo)注框的位置是否正確。import cv2 def visualize_yolo_annotation(image_path, txt_path): 將yolo格式標(biāo)注繪制到圖片上用于檢查轉(zhuǎn)換結(jié)果 img cv2.imread(image_path) height, width img.shape[:2] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) center_x float(parts[1]) * width center_y float(parts[2]) * height box_w float(parts[3]) * width box_h float(parts[4]) * height x1 int(center_x - box_w / 2) y1 int(center_y - box_h / 2) x2 int(center_x box_w / 2) y2 int(center_y box_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls_id), (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) return img # 抽查5張圖片 import random test_files random.sample(train_files, 5) for img_path in test_files: txt_path str(img_path).replace(JPEGImages, labels).replace(.jpg, .txt) result visualize_yolo_annotation(str(img_path), txt_path) cv2.imwrite(fcheck_{img_path.stem}.jpg, result)自檢這一步看似費(fèi)時(shí)間實(shí)際上是整個(gè)流程里性?xún)r(jià)比最高的一環(huán)。一次坐標(biāo)轉(zhuǎn)換錯(cuò)誤如果沒(méi)被及時(shí)發(fā)現(xiàn)后續(xù)訓(xùn)練出來(lái)的模型會(huì)帶著系統(tǒng)性偏差預(yù)測(cè)框要么整體偏左上要么寬高比例失真到時(shí)候排錯(cuò)反而要花幾倍時(shí)間。4. 用YOLOv8訓(xùn)練自己的數(shù)據(jù)集配置、命令與驗(yàn)證4.1 數(shù)據(jù)配置文件轉(zhuǎn)換完成后接下來(lái)就是準(zhǔn)備YOLOv8的數(shù)據(jù)配置。以YOLOv8為例訓(xùn)練前需要準(zhǔn)備一個(gè)yaml格式的數(shù)據(jù)配置文件里面寫(xiě)明訓(xùn)練集路徑、驗(yàn)證集路徑、類(lèi)別數(shù)和類(lèi)別名YOLOv5及后續(xù)版本都沿用類(lèi)似的配置規(guī)范。# fatigue.yaml path: /data/fatigue_driving_dataset # 數(shù)據(jù)集根目錄按實(shí)際路徑修改 train: train.txt # 訓(xùn)練集圖片路徑列表 val: val.txt # 驗(yàn)證集圖片路徑列表 # 類(lèi)別定義順序必須和轉(zhuǎn)換腳本里的CLASS_MAPPING一致 nc: 4 names: 0: normal 1: yawn 2: close_eye 3: phone類(lèi)別名并不影響訓(xùn)練過(guò)程本身影響的是訓(xùn)練完成后的輸出可讀性以及推理時(shí)標(biāo)簽的顯示名。但類(lèi)別的順序映射與數(shù)據(jù)格式的轉(zhuǎn)換是強(qiáng)相關(guān)的如果轉(zhuǎn)換腳本里定義的映射是yawn為0、close_eye為1這里就也要對(duì)應(yīng)寫(xiě)成這樣。兩處順序不一致是訓(xùn)練結(jié)果類(lèi)別錯(cuò)亂的最常見(jiàn)原因。這里的路徑寫(xiě)法要注意train字段指向的是train.txt文件本身而不是JPEGImages目錄。YOLOv8讀取這個(gè)txt文件后會(huì)逐行讀取圖片絕對(duì)路徑再去相同目錄結(jié)構(gòu)下找對(duì)應(yīng)的標(biāo)注txt文件。所以轉(zhuǎn)換后的labels目錄建議放在JPEGImages的同級(jí)位置比如數(shù)據(jù)集根目錄下建一個(gè)labels文件夾里面的txt文件名與圖片名完全一致。4.2 訓(xùn)練啟動(dòng)命令與參數(shù)調(diào)優(yōu)數(shù)據(jù)配置完成后訓(xùn)練命令本身不復(fù)雜。關(guān)鍵是把幾個(gè)影響訓(xùn)練質(zhì)量的參數(shù)根據(jù)這個(gè)數(shù)據(jù)集的實(shí)際情況調(diào)好。yolo detect train \ datafatigue.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ device0 \ project./runs \ namefatigue_yolov8s逐項(xiàng)說(shuō)明參數(shù)含義。model指定預(yù)訓(xùn)練權(quán)重yolov8s.pt是small版本參數(shù)約1100萬(wàn)在4362張圖的數(shù)據(jù)規(guī)模下從yolov8s開(kāi)始是比較合理的選擇直接上yolov8l或yolov8x模型容量大而數(shù)據(jù)量不足反而容易過(guò)擬合。epochs設(shè)為100配合patience20的早停策略如果連續(xù)20個(gè)epoch驗(yàn)證集指標(biāo)沒(méi)有提升訓(xùn)練會(huì)自動(dòng)終止。batch16取決于顯存大小8G顯存跑yolov8s加640分辨率batch16通??梢耘苋绻@存不足降到8或者把imgsz降到512。imgsz的取舍值得單獨(dú)說(shuō)一遍。數(shù)據(jù)集的原始圖片如果是1920x1080的高分辨率而駕駛艙內(nèi)的人臉區(qū)域往往只占畫(huà)面的10%到25%大小。直接縮放到640x640后人臉區(qū)域可能只有64到160像素見(jiàn)方屬于小目標(biāo)范疇。這種情況下可以試試imgsz960甚至1088檢測(cè)精度通常會(huì)有明顯提升代價(jià)是訓(xùn)練速度和顯存占用都會(huì)增加。我一般會(huì)先用640跑一輪看baseline再用960跑一輪做對(duì)比用數(shù)據(jù)決定用哪個(gè)尺寸。4.3 結(jié)果驗(yàn)證mAP、混淆矩陣與壞樣本訓(xùn)練完成后驗(yàn)證工作從三個(gè)層面展開(kāi)。第一層看總體指標(biāo)也就是驗(yàn)證集上的mAP50和mAP50-95第二層看每個(gè)類(lèi)別的單獨(dú)指標(biāo)判斷哪些類(lèi)容易混淆第三層把驗(yàn)證集的預(yù)測(cè)結(jié)果可視化系統(tǒng)的檢查誤檢和漏檢的圖片。from ultralytics import YOLO # 加載訓(xùn)練好的模型 model YOLO(./runs/fatigue_yolov8s/weights/best.pt) # 在驗(yàn)證集上做評(píng)估自動(dòng)輸出mAP等指標(biāo) results model.val(datafatigue.yaml) # 單獨(dú)評(píng)估閉眼類(lèi)別 for cls_name, cls_result in results.class_result.items(): if cls_name close_eye: print(fclose_eye mAP50: {cls_result[0]:.4f}) print(fclose_eye mAP50-95: {cls_result[1]:.4f})看mAP指標(biāo)時(shí)我關(guān)注的是類(lèi)間差異而不是均值本身。4個(gè)類(lèi)別里正常駕駛和打哈欠通常比較容易區(qū)分因?yàn)樽觳繀^(qū)域的特征差異明顯閉眼和使用手機(jī)這兩個(gè)類(lèi)別如果標(biāo)注框沒(méi)有包含足夠的區(qū)域背景模型容易混淆。如果某個(gè)類(lèi)別的mAP比其他類(lèi)低15個(gè)點(diǎn)以上優(yōu)先懷疑標(biāo)注框本身而不是模型結(jié)構(gòu)的問(wèn)題。5. 疲勞駕駛數(shù)據(jù)集避坑記錄4條真實(shí)踩坑5.1 類(lèi)別編號(hào)錯(cuò)位VOC順序與YOLO類(lèi)號(hào)不一致現(xiàn)象訓(xùn)練過(guò)程正常loss曲線(xiàn)收斂良好但推理時(shí)輸出的類(lèi)別標(biāo)簽和實(shí)際畫(huà)面內(nèi)容總是對(duì)不上——檢測(cè)到打哈欠卻顯示成了正常駕駛閉眼顯示成了手機(jī)。原因VOC標(biāo)注里的類(lèi)別名是沒(méi)有順序概念的YOLO格式必須把類(lèi)別名編碼成從0開(kāi)始的整數(shù)。如果轉(zhuǎn)換腳本里的類(lèi)別映射表和數(shù)據(jù)配置文件里的names順序不一致訓(xùn)練過(guò)程不會(huì)報(bào)任何錯(cuò)誤因?yàn)槟P椭皇前凑諗?shù)值在學(xué)最終輸出就會(huì)錯(cuò)位。解決把所有環(huán)節(jié)的類(lèi)別順序收斂到一個(gè)文件中。我會(huì)在項(xiàng)目根目錄放置一個(gè)classes.yaml作為唯一事實(shí)來(lái)源轉(zhuǎn)換腳本和數(shù)據(jù)配置文件都從這個(gè)文件讀取避免手工抄寫(xiě)導(dǎo)致的不一致。5.2 無(wú)標(biāo)注圖片導(dǎo)致的訓(xùn)練崩潰現(xiàn)象訓(xùn)練啟動(dòng)后很快報(bào)錯(cuò)日志里出現(xiàn)KeyError或者FileNotFoundError指向某張圖片找不到對(duì)應(yīng)的標(biāo)注文件。原因4362張圖片里混入了少量沒(méi)有對(duì)應(yīng)標(biāo)注的圖片即JPEGImages里有jpg文件但Annotations里沒(méi)有對(duì)應(yīng)xml文件轉(zhuǎn)換后labels目錄里也就沒(méi)有對(duì)應(yīng)txt文件。YOLO訓(xùn)練時(shí)每讀一張圖片就去查找同名txt標(biāo)注找不到就會(huì)中斷。解決轉(zhuǎn)換前先做一次文件名差集檢查。用Python對(duì)兩個(gè)目錄的文件名做set差運(yùn)算把缺標(biāo)注的圖片列出來(lái)單獨(dú)處理如果標(biāo)注確實(shí)缺失就把這些圖片從訓(xùn)練列表里剔除而不是保留在目錄中。這一點(diǎn)在數(shù)據(jù)量越大的時(shí)候越容易出現(xiàn)因?yàn)槿斯ふ磉^(guò)程中很難保證每張圖都完成標(biāo)注。5.3 分辨率不均衡導(dǎo)致小目標(biāo)漏檢現(xiàn)象訓(xùn)練和驗(yàn)證的mAP都不低但實(shí)際測(cè)試時(shí)駕駛員稍微離攝像頭遠(yuǎn)一點(diǎn)閉眼檢測(cè)就大面積失效。原因這個(gè)數(shù)據(jù)集的圖片分辨率并不統(tǒng)一有的圖片是1280x720有的是1920x1080還有少量800x600。統(tǒng)一縮放到640x640后原本在大分辨率圖片中人臉占比較小的目標(biāo)縮放后可能只剩下40到60像素檢測(cè)器對(duì)這樣的小目標(biāo)非常敏感訓(xùn)練時(shí)收斂不充分自然無(wú)法泛化到實(shí)車(chē)場(chǎng)景。解決一方面把訓(xùn)練和推理的imgsz提高到960另一方面對(duì)原始圖片里目標(biāo)像素面積做過(guò)一次分析。對(duì)每個(gè)標(biāo)注框計(jì)算占圖片總面積的比例畫(huà)出分布直方圖如果大量目標(biāo)框占比小于1%優(yōu)先考慮在訓(xùn)練時(shí)加入多尺度訓(xùn)練或者把數(shù)據(jù)按分辨率分組處理。這類(lèi)問(wèn)題的修復(fù)優(yōu)先級(jí)高于調(diào)模型結(jié)構(gòu)。5.4 劃分不洗牌驗(yàn)證集類(lèi)別分布失衡現(xiàn)象訓(xùn)練集mAP接近0.98驗(yàn)證集mAP只有0.82兩者差距過(guò)大而且每次訓(xùn)練結(jié)果波動(dòng)明顯。原因直接使用了數(shù)據(jù)集自帶的train.txt和val.txt劃分文件而這些文件可能是按時(shí)間順序生成的圖片列表。駕駛行為數(shù)據(jù)天然有時(shí)間連續(xù)性前5000張圖片可能是同一批司機(jī)的白天駕駛數(shù)據(jù)后面的圖片是另一批司機(jī)的夜間數(shù)據(jù)。按時(shí)間順序劃分驗(yàn)證集里可能集中了大量夜間樣本類(lèi)別分布和訓(xùn)練集差異明顯。解決強(qiáng)制重新劃分并且使用類(lèi)別分層采樣確保每個(gè)類(lèi)別在訓(xùn)練集和驗(yàn)證集中的比例基本一致。具體做法是先把所有圖片按類(lèi)別標(biāo)簽分組然后再在每個(gè)組內(nèi)部按比例切分而不是對(duì)全部圖片做全局隨機(jī)抽樣。6. 進(jìn)階類(lèi)別不均衡的采樣策略與數(shù)據(jù)增強(qiáng)習(xí)慣疲勞駕駛數(shù)據(jù)集里類(lèi)別不均衡是不可避免的問(wèn)題。正常駕駛的樣本數(shù)量往往遠(yuǎn)多于閉眼和打哈欠直接訓(xùn)練會(huì)讓模型對(duì)少數(shù)類(lèi)的檢測(cè)能力偏弱。處理這個(gè)問(wèn)題有兩個(gè)常用的手段采樣策略和數(shù)據(jù)增強(qiáng)。采樣策略上最簡(jiǎn)單的做法是在數(shù)據(jù)加載階段對(duì)少數(shù)類(lèi)的圖片做重復(fù)采樣。YOLO訓(xùn)練時(shí)每個(gè)epoch都會(huì)遍歷全部訓(xùn)練圖片可以在自定義數(shù)據(jù)加載器中對(duì)包含少數(shù)類(lèi)目標(biāo)的圖片提高采樣概率。比如統(tǒng)計(jì)每張圖片包含的類(lèi)別標(biāo)簽含閉眼標(biāo)注的圖片采樣權(quán)重設(shè)為2.0其余為1.0這樣每輪訓(xùn)練中少數(shù)類(lèi)圖片被看到的頻率大概翻倍。數(shù)據(jù)增強(qiáng)方面YOLOv8默認(rèn)開(kāi)啟的馬賽克增強(qiáng)對(duì)豐富背景多樣性很有幫助。如果最終應(yīng)用場(chǎng)景是駕駛艙內(nèi)的固定攝像頭視角還可以針對(duì)性加入輕微的角度旋轉(zhuǎn)和亮度擾動(dòng)模擬不同時(shí)間段的光線(xiàn)變化。但要控制增強(qiáng)幅度旋轉(zhuǎn)角度不超過(guò)10度為好過(guò)大的旋轉(zhuǎn)會(huì)讓檢測(cè)框失真。我做疲勞駕駛檢測(cè)項(xiàng)目時(shí)養(yǎng)成的一個(gè)習(xí)慣是訓(xùn)練前先跑一個(gè)50個(gè)epoch的快速實(shí)驗(yàn)驗(yàn)證標(biāo)注質(zhì)量只看訓(xùn)練集的loss能不能降到合理區(qū)間再用完整配置跑最終版本。這個(gè)習(xí)慣幫我提前發(fā)現(xiàn)過(guò)好幾次標(biāo)注框偏移的問(wèn)題省下了大量重訓(xùn)時(shí)間。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取