:593張手機(jī)數(shù)據(jù)集訓(xùn)練檢測模型全流程)
簡介這是一份面向YOLO系列目標(biāo)檢測學(xué)習(xí)者的手機(jī)圖像數(shù)據(jù)集共593張帶標(biāo)注圖像適合剛?cè)腴T目標(biāo)檢測或需要快速驗證模型效果的開發(fā)者使用。數(shù)據(jù)集已按訓(xùn)練與驗證需求劃分完畢可直接投入yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法進(jìn)行訓(xùn)練與測試省去自行采集與標(biāo)注的繁瑣環(huán)節(jié)。壓縮包共1780個文件包含593個jpg圖像、593個txt格式的YOLO標(biāo)注、593個xml格式的VOC標(biāo)注以及1個yaml配置文件整體約20.43MB。其中txt標(biāo)注采用歸一化中心點與寬高格式xml則保留VOC結(jié)構(gòu)兩種標(biāo)簽并存便于在不同框架間靈活切換。yaml文件可直接用于配置數(shù)據(jù)路徑與類別信息幫助讀者快速跑通訓(xùn)練流程。目前已有63人學(xué)習(xí)下載適合作為目標(biāo)檢測入門練手、算法對比實驗或課程作業(yè)的輕量級數(shù)據(jù)支撐。1. 手機(jī)數(shù)據(jù)集跑 YOLO593 張帶標(biāo)簽圖像到底夠不夠用手上拿到一個yolo算法-手機(jī)數(shù)據(jù)集-593張圖像帶標(biāo)簽-手機(jī).zip第一反應(yīng)往往不是興奮而是犯嘀咕593 張還全是手機(jī)拍的這能訓(xùn)出能用的檢測模型嗎我一開始也這么想。去年幫一個做二手手機(jī)質(zhì)檢的團(tuán)隊搭過類似規(guī)模的檢測流程他們用手機(jī)在流水線工位上拍了六百來張目標(biāo)就是識別屏幕劃痕、邊框磕碰、攝像頭進(jìn)灰這幾類缺陷。當(dāng)時最大的爭議就是數(shù)據(jù)量——有人堅持要湊到三千張再開訓(xùn)有人覺得先跑一輪 baseline 看指標(biāo)再說。最后我們用 593 張先跑通了全流程mAP0.5 到了 0.71雖然不算驚艷但足夠支撐內(nèi)部試用和后續(xù)迭代方向判斷。這個標(biāo)題里的關(guān)鍵詞其實很明確YOLO 算法、手機(jī)數(shù)據(jù)集、593 張圖像、帶標(biāo)簽。它解決的不是“如何從零造一個 SOTA 模型”而是“手頭就這么多手機(jī)拍的標(biāo)注數(shù)據(jù)怎么把它變成能跑的檢測器”。適合兩類人一是剛接觸 YOLO 訓(xùn)練、想拿一份真實小數(shù)據(jù)集練手的工程師二是業(yè)務(wù)側(cè)已經(jīng)有手機(jī)采集條件、需要快速驗證檢測可行性的團(tuán)隊。593 張不算多但帶標(biāo)簽意味著省掉了最耗時的標(biāo)注環(huán)節(jié)真正要花心思的是數(shù)據(jù)劃分、增強(qiáng)策略和訓(xùn)練參數(shù)——這三件事決定了這批數(shù)據(jù)是被浪費還是被榨干。2. 593 張手機(jī)圖像進(jìn) YOLO 之前先搞清楚數(shù)據(jù)長什么樣2.1 解壓后先做三件事看目錄、數(shù)類別、查標(biāo)注格式拿到壓縮包別急著寫訓(xùn)練腳本。我一般先解壓到一個干凈目錄然后用幾條命令把數(shù)據(jù)摸清楚。手機(jī)數(shù)據(jù)集常見的坑是圖像文件名帶空格或中文、標(biāo)注格式混用有的 XML 有的 TXT、類別名前后有空格。這些在訓(xùn)練時不會報錯但會讓模型學(xué)出玄學(xué)行為。# 解壓并進(jìn)入目錄 unzip yolo算法-手機(jī)數(shù)據(jù)集-593張圖像帶標(biāo)簽-手機(jī).zip -d phone_dataset cd phone_dataset # 看目錄結(jié)構(gòu)確認(rèn) images 和 labels 是否分開 find . -maxdepth 2 -type d | sort # 統(tǒng)計圖像數(shù)量確認(rèn)是不是 593 張 find . -name *.jpg -o -name *.png | wc -l # 查看標(biāo)注文件格式Y(jié)OLO 通常是 .txt每行 class x_center y_center w h head -5 labels/$(ls labels | head -1)邏輯說明find用來確認(rèn)目錄層級很多數(shù)據(jù)集把圖像和標(biāo)簽混在一起訓(xùn)練腳本會找不到對應(yīng)文件。head看標(biāo)注內(nèi)容如果第一列是類別索引、后面四個是 0 到 1 之間的浮點數(shù)那就是標(biāo)準(zhǔn) YOLO 格式如果看到annotation或object說明是 VOC XML需要先轉(zhuǎn)換。參數(shù)上注意-maxdepth 2避免遞歸太深拖慢速度head -5只看前五行足夠判斷格式。2.2 類別分布決定你的訓(xùn)練策略593 張圖像里如果只有一類目標(biāo)那算是簡單場景如果有五類以上每類可能就幾十個實例這時候類別不平衡會直接反映在混淆矩陣上。我習(xí)慣用一段 Python 腳本把每個類別的實例數(shù)統(tǒng)計出來再決定要不要做重采樣或加權(quán)。import os from collections import Counter label_dir labels class_counter Counter() image_with_target 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path, r) as f: lines [l.strip() for l in f if l.strip()] if lines: image_with_target 1 for line in lines: cls_id int(line.split()[0]) class_counter[cls_id] 1 print(有目標(biāo)的圖像數(shù):, image_with_target) print(各類別實例數(shù):, dict(class_counter))邏輯說明這段腳本遍歷所有標(biāo)簽文件統(tǒng)計每個類別出現(xiàn)的次數(shù)同時記錄至少有一個目標(biāo)的圖像數(shù)量。如果image_with_target遠(yuǎn)小于 593說明大量圖像是負(fù)樣本背景圖這時候訓(xùn)練時要控制負(fù)樣本比例否則模型會偏向預(yù)測背景。參數(shù)上class_counter的鍵是類別索引你需要對照classes.txt或data.yaml里的類別名去映射。常見做法是實例數(shù)少于 100 的類別在增強(qiáng)時給它更高的復(fù)制概率或者用copy_paste這類增強(qiáng)手段補一補。2.3 劃分訓(xùn)練集和驗證集別用隨機(jī)劃分就完事手機(jī)數(shù)據(jù)集有個特點同一場景下連續(xù)拍攝的圖像高度相似。如果直接隨機(jī)劃分驗證集里很可能出現(xiàn)和訓(xùn)練集幾乎一樣的幀導(dǎo)致驗證指標(biāo)虛高。我一般會先按拍攝時間或文件名前綴分組再按組劃分。如果數(shù)據(jù)里沒有時間信息至少要用sklearn的分層劃分保證每個類別在訓(xùn)練集和驗證集里的比例接近。import os import random from sklearn.model_selection import train_test_split image_dir images label_dir labels all_images [f for f in os.listdir(image_dir) if f.endswith((.jpg, .png))] # 按文件名前綴分組假設(shè)前綴代表不同拍攝批次 groups {} for img in all_images: prefix img.split(_)[0] groups.setdefault(prefix, []).append(img) group_list list(groups.keys()) train_groups, val_groups train_test_split(group_list, test_size0.2, random_state42) train_images [img for g in train_groups for img in groups[g]] val_images [img for g in val_groups for img in groups[g]] print(訓(xùn)練集:, len(train_images), 驗證集:, len(val_images))邏輯說明先按文件名前綴分組再對組做劃分這樣同一批次的圖像不會同時出現(xiàn)在訓(xùn)練和驗證里。test_size0.2對 593 張來說驗證集大約 118 張夠用但不寬裕。如果類別極度不平衡可以把train_test_split換成StratifiedGroupKFold但那個需要每個組有類別標(biāo)簽實現(xiàn)起來稍麻煩。我一般先跑一輪看驗證指標(biāo)是否合理如果驗證 mAP 比訓(xùn)練 mAP 高很多大概率是劃分泄漏了。3. 用 YOLOv8 在 593 張圖上跑通第一個 baseline3.1 環(huán)境準(zhǔn)備與數(shù)據(jù)配置文件YOLOv8 是目前小數(shù)據(jù)集上最容易跑通的版本之一安裝簡單對顯存要求也不高。我一般用 conda 建一個干凈環(huán)境避免和系統(tǒng)里的包沖突。數(shù)據(jù)配置文件data.yaml要寫清楚路徑、類別數(shù)和類別名這是訓(xùn)練腳本讀取數(shù)據(jù)的入口。conda create -n yolo_phone python3.10 -y conda activate yolo_phone pip install ultralytics opencv-python scikit-learn# data.yaml path: ./phone_dataset train: images/train val: images/val nc: 3 names: [scratch, dent, dust]邏輯說明path是數(shù)據(jù)集根目錄train和val是相對路徑。nc是類別數(shù)必須和標(biāo)注里的最大類別索引加一一致。names的順序要和標(biāo)注里的類別索引對應(yīng)寫反了模型會把劃痕認(rèn)成磕碰。參數(shù)上如果圖像和標(biāo)簽不在同一級目錄YOLOv8 默認(rèn)會去images同級的labels找同名 txt所以目錄結(jié)構(gòu)最好是images/train和labels/train平行。3.2 訓(xùn)練命令與關(guān)鍵參數(shù)設(shè)置第一次跑不要一上來就調(diào)大 epoch先用小分辨率、少 epoch 驗證流程通不通。我一般先用imgsz640、epochs50、batch16跑一輪看損失曲線和驗證指標(biāo)是否正常下降。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ lr00.01 \ patience10 \ projectruns/phone \ namebaseline邏輯說明modelyolov8n.pt是最小的預(yù)訓(xùn)練模型593 張圖用 nano 版本足夠用大模型反而容易過擬合。lr00.01是初始學(xué)習(xí)率小數(shù)據(jù)集上這個值比較穩(wěn)如果損失震蕩可以降到 0.005。patience10表示驗證指標(biāo) 10 輪不提升就早停避免浪費時間。project和name決定輸出目錄方便對比不同實驗。常見做法是先用yolov8n跑通再根據(jù)指標(biāo)決定要不要換yolov8s。3.3 看結(jié)果混淆矩陣和驗證曲線怎么讀訓(xùn)練結(jié)束后runs/phone/baseline目錄下會有confusion_matrix.png、results.csv和val_batch0_pred.jpg。混淆矩陣是判斷類別混淆最直接的工具對角線越深越好非對角線上的數(shù)字說明模型把 A 類認(rèn)成了 B 類。如果某一類幾乎全被認(rèn)成背景說明該類實例太少或標(biāo)注質(zhì)量有問題。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/phone/baseline/results.csv) df.columns df.columns.str.strip() plt.plot(df[epoch], df[train/box_loss], labeltrain_box_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.legend() plt.xlabel(epoch) plt.ylabel(loss) plt.savefig(loss_curve.png)邏輯說明results.csv里記錄了每輪的損失和指標(biāo)列名可能有空格先strip一下。訓(xùn)練損失和驗證損失如果同步下降說明模型還在學(xué)習(xí)如果訓(xùn)練損失降但驗證損失升就是過擬合需要加增強(qiáng)或減模型容量。參數(shù)上box_loss是邊界框回歸損失cls_loss是分類損失dfl_loss是分布焦點損失三個都要看不能只看總和。4. 小數(shù)據(jù)集訓(xùn)練避坑593 張圖最容易翻車的五個地方4.1 現(xiàn)象驗證 mAP 高得離譜實際測試一塌糊涂原因訓(xùn)練集和驗證集劃分時沒有按場景分組同一場景的相似幀同時進(jìn)了兩邊模型相當(dāng)于在“背答案”。解決按文件名前綴或拍攝時間分組劃分確保驗證集里的場景在訓(xùn)練集里沒出現(xiàn)過。如果數(shù)據(jù)里沒有分組信息至少用GroupKFold按圖像哈希聚類后再劃分。4.2 現(xiàn)象訓(xùn)練到一半 loss 突然變成 NaN原因標(biāo)注文件里有坐標(biāo)超出 0 到 1 范圍或者某個標(biāo)注框?qū)捀邽?0。YOLO 在計算損失時會對這些異常值做除法直接炸掉。解決訓(xùn)練前用腳本掃一遍所有標(biāo)簽把x_center、y_center、w、h不在 (0,1] 范圍內(nèi)的行刪掉或修正。常見做法是加一行斷言assert 0 w 1 and 0 h 1。4.3 現(xiàn)象模型只預(yù)測背景所有框都不出來原因負(fù)樣本比例太高或者nc設(shè)置和標(biāo)注里的類別索引不匹配。593 張圖里如果有一半是純背景模型會學(xué)到“預(yù)測背景最安全”。解決檢查data.yaml里的nc是否等于最大類別索引加一同時統(tǒng)計有目標(biāo)的圖像比例。如果負(fù)樣本超過 30%訓(xùn)練時用fraction參數(shù)只取一部分負(fù)樣本或者手動剔除一些純背景圖。4.4 現(xiàn)象小目標(biāo)檢測效果極差劃痕幾乎全漏原因手機(jī)拍攝的劃痕在 640 分辨率下可能只有幾個像素YOLO 的下采樣倍數(shù)導(dǎo)致小目標(biāo)特征丟失。解決把imgsz提到 1280或者用yolov8n-p2這種帶 P2 檢測頭的變體。如果顯存不夠用multi_scale訓(xùn)練讓模型適應(yīng)不同尺度。我一般會先試 1280如果顯存爆了再降到 1024 并開mosaic增強(qiáng)。4.5 現(xiàn)象訓(xùn)練速度極慢一張圖要好幾秒原因圖像分辨率太高但batch設(shè)得太小或者數(shù)據(jù)加載時用了太多 CPU 做增強(qiáng)。解決先確認(rèn)imgsz和batch的乘積是否超過顯存如果沒超但速度慢把workers調(diào)到 CPU 核數(shù)的一半。另外手機(jī)圖像如果都是 4000x3000訓(xùn)練前統(tǒng)一縮放到 1280 長邊能大幅減少 IO 時間。5. 把 593 張用到極致增強(qiáng)策略與迭代節(jié)奏5.1 小數(shù)據(jù)集增強(qiáng)不是越多越好而是要“像手機(jī)拍的”手機(jī)數(shù)據(jù)集有個天然優(yōu)勢你知道測試時的拍攝條件。增強(qiáng)策略應(yīng)該模擬這些條件而不是隨便加。我一般會開hsv_h0.015、hsv_s0.7、hsv_v0.4來模擬不同光照degrees10模擬手持角度偏差translate0.1模擬對焦偏移。但mosaic1.0在小數(shù)據(jù)集上要慎用它會把四張圖拼在一起可能讓劃痕這種細(xì)長目標(biāo)被截斷。我通常先開mosaic0.5看驗證指標(biāo)再決定要不要調(diào)高。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz1280 \ batch8 \ lr00.005 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ degrees10 \ translate0.1 \ mosaic0.5 \ mixup0.1 \ projectruns/phone \ nameaug_v1邏輯說明imgsz1280提升小目標(biāo)召回batch8防止顯存溢出。lr00.005比 baseline 更低因為增強(qiáng)后數(shù)據(jù)分布更復(fù)雜學(xué)習(xí)率太大會震蕩。mixup0.1做少量圖像混合增加泛化但不會過度模糊目標(biāo)。參數(shù)上hsv_h是色調(diào)擾動幅度手機(jī)拍攝的白平衡變化一般不超過 0.015設(shè)太大反而失真。5.2 迭代節(jié)奏先保召回再提精度593 張圖訓(xùn)出來的模型第一版不要追求高精度先看召回率。如果劃痕這類關(guān)鍵缺陷漏檢太多后面精度再高也沒用。我一般會看val/recall曲線如果召回低于 0.6優(yōu)先加數(shù)據(jù)或調(diào)低置信度閾值。等召回穩(wěn)定在 0.8 以上再用conf閾值掃描找精度和召回的平衡點。from ultralytics import YOLO model YOLO(runs/phone/aug_v1/weights/best.pt) metrics model.val(datadata.yaml, conf0.25, iou0.5) print(mAP0.5:, metrics.box.map50) print(召回:, metrics.box.r)邏輯說明conf0.25是驗證時的置信度閾值實際部署時可以調(diào)低到 0.15 換更高召回。iou0.5是判定預(yù)測框正確的 IoU 閾值小目標(biāo)上可以降到 0.4 讓匹配更寬松。參數(shù)上metrics.box.r是整體召回如果要看每個類別的召回用metrics.box.r_per_class。5.3 一個具體技巧用驗證集反推標(biāo)注質(zhì)量593 張帶標(biāo)簽的數(shù)據(jù)標(biāo)注質(zhì)量往往參差不齊。我習(xí)慣在訓(xùn)練后把驗證集里預(yù)測置信度低但實際有目標(biāo)的圖像挑出來人工看一遍標(biāo)注。如果發(fā)現(xiàn)標(biāo)注框明顯偏了或類別標(biāo)錯修正后重新訓(xùn)練通常能帶來 3 到 5 個點的 mAP 提升。這個習(xí)慣幫我省過很多次“模型不行”的誤判——其實是標(biāo)注不行。import os from ultralytics import YOLO model YOLO(runs/phone/aug_v1/weights/best.pt) results model.predict(phone_dataset/images/val, conf0.1, saveTrue) # 找出預(yù)測框數(shù)量遠(yuǎn)少于標(biāo)注框數(shù)量的圖像 for r in results: img_name os.path.basename(r.path) pred_count len(r.boxes) label_path r.path.replace(images, labels).replace(.jpg, .txt) if os.path.exists(label_path): with open(label_path) as f: gt_count len([l for l in f if l.strip()]) if pred_count gt_count * 0.5: print(疑似漏檢或標(biāo)注問題:, img_name, 預(yù)測:, pred_count, 標(biāo)注:, gt_count)邏輯說明用低置信度閾值跑預(yù)測對比預(yù)測框數(shù)量和標(biāo)注框數(shù)量。如果預(yù)測遠(yuǎn)少于標(biāo)注要么是模型漏檢要么是標(biāo)注里有大量錯誤框。把這類圖像挑出來人工復(fù)核修正標(biāo)注后重新訓(xùn)練。參數(shù)上conf0.1是為了盡量多召回預(yù)測框避免因為閾值太高而誤判。這個技巧在 593 張規(guī)模上特別有效因為人工復(fù)核一百來張驗證集是可行的。我自己的習(xí)慣是拿到任何小數(shù)據(jù)集先跑一輪 baseline再花半天時間做標(biāo)注質(zhì)檢最后才調(diào)參。593 張圖不算多但足夠讓你把 YOLO 訓(xùn)練的完整鏈路走一遍踩過的坑在更大數(shù)據(jù)集上一樣會遇到。希望幫到你。本文還有配套的精品資源點擊獲取