據(jù)集訓(xùn)練與避坑指南)
簡介本數(shù)據(jù)集面向計算機視覺入門與目標(biāo)檢測實踐者聚焦“電瓶車進入電梯”這一社區(qū)安防場景可用于訓(xùn)練和驗證單類別檢測模型幫助讀者快速搭建針對電動踏板車闖入電梯的識別方案。資源包共602個文件包含200張jpg原圖、200個Pascal VOC格式xml標(biāo)注和200個YOLO格式txt標(biāo)注另有少量說明文件壓縮包約11.07MB兩種標(biāo)注格式可分別適配不同檢測框架省去格式轉(zhuǎn)換步驟。所有圖片均使用labelImg手工繪制矩形框標(biāo)注類別為electric scooter共210個標(biāo)注框標(biāo)注準(zhǔn)確合理。目前已有205人學(xué)習(xí)下載適合作為課程設(shè)計、畢業(yè)設(shè)計或算法驗證的小型數(shù)據(jù)集讀者可直接用于模型訓(xùn)練、數(shù)據(jù)增強與檢測效果對比快速驗證單類別檢測流程。1. 電瓶車進電梯檢測200 張 VOCYOLO 數(shù)據(jù)集到底能跑出什么電瓶車進電梯這件事物業(yè)頭疼、業(yè)主害怕但真正落到算法層面它其實是一個典型的「小目標(biāo) 固定場景 單類別」檢測問題。你拿到的這份電瓶車進入電梯檢測數(shù)據(jù)集200 張圖VOC 和 YOLO 兩種標(biāo)注格式核心目標(biāo)只有一個把電梯轎廂里的電瓶車框出來。聽起來簡單但 200 張這個量級決定了它不是一個能直接沖 SOTA 的數(shù)據(jù)集而是一個用來驗證 pipeline、做 demo、跑通訓(xùn)練流程的起點。適合誰適合手頭有電梯監(jiān)控畫面、想快速驗證檢測可行性的人也適合剛接觸 YOLO 訓(xùn)練、需要一個真實場景數(shù)據(jù)集練手的人。別指望 200 張能覆蓋所有電梯型號和光照條件但它足夠讓你把「數(shù)據(jù)標(biāo)注→格式轉(zhuǎn)換→訓(xùn)練→推理→部署」這條鏈路完整走一遍知道坑在哪。2. 拆開這份數(shù)據(jù)集VOC 與 YOLO 雙格式的選型邏輯與目錄結(jié)構(gòu)2.1 為什么同一批圖要同時給 VOC 和 YOLO 兩種格式VOC 格式是 XML 文件每個圖對應(yīng)一個 XML里面用bndbox記錄xmin/ymin/xmax/ymax坐標(biāo)是絕對像素值。YOLO 格式是 TXT 文件每行class_id x_center y_center width height全部歸一化到 0~1。兩種格式同時給本質(zhì)上是照顧不同訓(xùn)練框架的輸入習(xí)慣。YOLOv5/v8/v11 系列默認(rèn)吃 YOLO 格式而一些老代碼、MMDetection 配置、或者你自己寫的 PyTorch Dataset 可能更順手用 VOC。常見做法是拿 YOLO 格式直接喂 ultralytics拿 VOC 格式做數(shù)據(jù)校驗和可視化因為 XML 可讀性更好出問題容易定位。注意兩種格式的類別索引必須對齊。VOC 里類別名是字符串YOLO 里是數(shù)字如果轉(zhuǎn)換時類別映射寫錯訓(xùn)練時 loss 會正常降但 mAP 極低這是血淚經(jīng)驗。2.2 200 張圖的目錄應(yīng)該怎么擺拿到壓縮包解壓后別急著改路徑。先按下面這個結(jié)構(gòu)整理后面所有腳本都基于這個結(jié)構(gòu)寫dataset/ ├── images/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... ├── annotations_voc/ │ ├── 001.xml │ ├── 002.xml │ └── ... ├── labels_yolo/ │ ├── 001.txt │ ├── 002.txt │ └── ... └── classes.txtclasses.txt里只寫一行ebike或者你數(shù)據(jù)集里定義的類別名。這個文件是后面轉(zhuǎn)換和訓(xùn)練時類別映射的唯一依據(jù)不要散落在代碼里硬編碼。2.3 用腳本檢查 VOC 標(biāo)注有沒有越界和漏標(biāo)200 張圖雖然少但標(biāo)注錯誤率往往不低。先跑一個校驗?zāi)_本把xminxmax、yminymax、坐標(biāo)超出圖像寬高的 XML 全部揪出來import os import xml.etree.ElementTree as ET from PIL import Image voc_dir dataset/annotations_voc img_dir dataset/images for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): print(f[缺失圖像] {xml_file} - {img_name}) continue w, h Image.open(img_path).size for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) if xmin xmax or ymin ymax: print(f[坐標(biāo)顛倒] {xml_file} {name} ({xmin},{ymin},{xmax},{ymax})) if xmin 0 or ymin 0 or xmax w or ymax h: print(f[越界] {xml_file} {name} 圖像尺寸({w},{h}) 框({xmin},{ymin},{xmax},{ymax}))邏輯說明逐 XML 解析先確認(rèn)對應(yīng)圖像存在再讀圖像寬高最后對每個 object 做坐標(biāo)合法性判斷。參數(shù)說明voc_dir和img_dir按你實際路徑改如果類別名不是ebike腳本不關(guān)心名字只查坐標(biāo)所以不用改類別相關(guān)邏輯。跑完如果輸出很多越界說明標(biāo)注時可能用了縮放后的圖需要統(tǒng)一回原圖坐標(biāo)。3. 從 VOC 轉(zhuǎn) YOLO轉(zhuǎn)換腳本、歸一化參數(shù)與四個邊界坑3.1 轉(zhuǎn)換腳本的核心邏輯與類別映射VOC 轉(zhuǎn) YOLO 的公式很簡單x_center (xmin xmax) / 2 / wy_center (ymin ymax) / 2 / hwidth (xmax - xmin) / wheight (ymax - ymin) / h。但寫腳本時最容易翻車的是類別索引和文件名對齊。下面這個腳本直接可用import os import xml.etree.ElementTree as ET from PIL import Image voc_dir dataset/annotations_voc img_dir dataset/images out_dir dataset/labels_yolo classes [ebike] # 按 classes.txt 順序 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() img_name root.find(filename).text img_path os.path.join(img_dir, img_name) w, h Image.open(img_path).size lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: print(f[未知類別] {xml_file} - {name}) continue cls_id classes.index(name) 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) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))邏輯說明先建輸出目錄逐 XML 讀圖像尺寸再對每個 object 做歸一化最后按同名 txt 寫出。參數(shù)說明classes列表必須和classes.txt完全一致順序不能錯:.6f保留六位小數(shù)YOLO 訓(xùn)練時精度足夠不要用科學(xué)計數(shù)法。3.2 轉(zhuǎn)換后必須做的三項校驗轉(zhuǎn)完不是就完了至少做三件事第一隨機抽 10 個 txt用cat看有沒有空文件或者坐標(biāo)超過 1第二用 Python 統(tǒng)計每個 txt 的行數(shù)和 XML 里 object 數(shù)量對比不一致的記下來第三把 YOLO 格式反畫回圖上肉眼確認(rèn)框位置沒偏。反畫腳本很多核心就是x_center*w還原成像素坐標(biāo)再畫矩形。這一步能抓住 90% 的轉(zhuǎn)換錯誤。3.3 四個邊界坑空標(biāo)注、截斷框、類別名大小寫、文件名空格空標(biāo)注有些圖可能沒有電瓶車XML 里沒有 object轉(zhuǎn)出來是空 txt。YOLO 訓(xùn)練時空 txt 是合法的負樣本但如果你數(shù)據(jù)集里全是正樣本突然混入空文件要確認(rèn)是不是漏標(biāo)。截斷框電瓶車只露出一半時標(biāo)注可能貼著圖像邊緣歸一化后坐標(biāo)接近 0 或 1訓(xùn)練時要注意 YOLO 的rect推理模式可能裁掉邊緣。類別名大小寫Ebike和ebike在 Python 里是兩個字符串映射會失敗統(tǒng)一用小寫。文件名空格如果圖像文件名帶空格XML 里filename可能被截斷轉(zhuǎn)換時讀不到圖建議批量重命名去掉空格。4. 用 YOLOv8/v11 訓(xùn)練 200 張電瓶車數(shù)據(jù)參數(shù)怎么設(shè)、多久能收斂4.1 環(huán)境配置與數(shù)據(jù) YAML 寫法ultralytics 環(huán)境配置不復(fù)雜但版本要對齊。常見做法是 Python 3.9pip install ultralyticsGPU 驅(qū)動和 CUDA 按你機器來。數(shù)據(jù) YAML 文件這樣寫path: /abs/path/to/dataset train: images val: images names: 0: ebike200 張圖train 和 val 都指向images是無奈之舉因為量太少再切 20% 驗證就只剩 160 張訓(xùn)練模型根本學(xué)不動。更合理的做法是train 用全部 200 張val 也用全部 200 張只看 loss 和訓(xùn)練集 mAP當(dāng)作過擬合驗證。等有更多數(shù)據(jù)再切分。4.2 訓(xùn)練命令與關(guān)鍵參數(shù)解釋yolo detect train \ datadataset.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch8 \ lr00.001 \ patience50 \ device0modelyolov8n.pt用 nano 版200 張圖用大模型純屬浪費。epochs200是因為數(shù)據(jù)少需要多輪才能記住但patience50防止過擬合后繼續(xù)跑。batch8看顯存8G 顯存跑 640 分辨率 batch 8 沒問題。lr00.001比默認(rèn) 0.01 小小數(shù)據(jù)集用大學(xué)習(xí)率容易震蕩。imgsz640是 YOLO 默認(rèn)電梯場景電瓶車占畫面比例不小640 夠用。4.3 訓(xùn)練過程看什么loss 曲線、mAP50 和混淆矩陣200 張圖訓(xùn)練loss 下降快是正常的可能 30 輪就降到很低。重點看mAP50有沒有到 0.8 以上如果一直在 0.5 徘徊大概率是標(biāo)注問題或者類別映射錯了。混淆矩陣在runs/detect/train/下如果出現(xiàn)大量背景誤檢說明電梯里其他物體比如人、推車被誤判成電瓶車需要加負樣本。訓(xùn)練完的results.csv可以用 pandas 讀出來畫圖看有沒有過擬合。5. 避坑與排查200 張小數(shù)據(jù)集訓(xùn)練電瓶車檢測的 5 個真實翻車記錄5.1 現(xiàn)象訓(xùn)練 loss 正常降但推理時框全圖亂飛原因VOC 轉(zhuǎn) YOLO 時坐標(biāo)沒歸一化或者歸一化用了錯誤的寬高比如用了縮放后圖像的尺寸。解決回頭檢查轉(zhuǎn)換腳本確認(rèn)w, h來自原圖且x_center等值都在 0~1 之間。用cat看一個 txt如果出現(xiàn)大于 1 的數(shù)就是沒歸一化。5.2 現(xiàn)象mAP50 始終為 0但 loss 在降原因類別索引不匹配。YAML 里names: 0: ebike但 txt 里寫的是1或者 VOC 里類別名是Ebike而 classes 列表寫的是ebike。解決統(tǒng)一類別名大小寫檢查classes.txt、YAML 和轉(zhuǎn)換腳本里的classes列表三者一致。5.3 現(xiàn)象驗證集 mAP 很高但實際電梯監(jiān)控畫面里檢測不到原因200 張圖可能來自同一部電梯、同一角度、同一光照模型過擬合到背景。解決如果條件允許拿幾段不同電梯的監(jiān)控截圖做測試不要只看驗證集。沒有新數(shù)據(jù)的話至少做數(shù)據(jù)增強比如hsv_h0.015、hsv_s0.7、hsv_v0.4、fliplr0.5在 YAML 里加augment: True。5.4 現(xiàn)象訓(xùn)練時提示No labels found原因YOLO 默認(rèn)在images同級找labels目錄但你的 txt 放在labels_yolo。解決要么把labels_yolo改名為labels要么在 YAML 里用train: images和val: images的同時確保 ultralytics 能通過路徑替換找到標(biāo)簽。最穩(wěn)的辦法是目錄結(jié)構(gòu)改成images/train和labels/train但 200 張圖沒必要直接改名labels最快。5.5 現(xiàn)象推理時電瓶車被切成兩半只框住一半原因電瓶車在電梯里可能被門或人遮擋標(biāo)注時只標(biāo)了可見部分模型學(xué)到的是部分特征。解決標(biāo)注時盡量標(biāo)完整車身遮擋嚴(yán)重就標(biāo)可見部分并在訓(xùn)練時用mosaic增強讓模型適應(yīng)遮擋。YOLO 默認(rèn)開啟 mosaic不用額外設(shè)。6. 200 張之后把電瓶車檢測推到可用的三個進階技巧200 張圖跑通訓(xùn)練只是起點真要放到電梯里用還得做三件事。第一用訓(xùn)練好的模型對未標(biāo)注的電梯監(jiān)控視頻做推理把置信度高于 0.6 的幀自動保存人工篩選后加入訓(xùn)練集這是最省力的數(shù)據(jù)擴充方式。第二導(dǎo)出 ONNX 或 TensorRT 模型在邊緣設(shè)備上測幀率電梯場景不需要 30 幀5 幀就夠報警但延遲要低。第三加一個簡單的跟蹤邏輯連續(xù) 3 幀檢測到電瓶車才觸發(fā)報警避免單幀誤檢導(dǎo)致誤報。我自己的習(xí)慣是每次擴充數(shù)據(jù)后重新訓(xùn)練時把lr0再降一半因為新數(shù)據(jù)分布和老數(shù)據(jù)有差異小學(xué)習(xí)率更穩(wěn)。希望幫到你。本文還有配套的精品資源點擊獲取