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