數(shù)據(jù)集實(shí)戰(zhàn):從標(biāo)注格式到訓(xùn)練推理全流程拆解)
簡(jiǎn)介這份YOLO抽煙目標(biāo)檢測(cè)數(shù)據(jù)集面向計(jì)算機(jī)視覺(jué)初學(xué)者與目標(biāo)檢測(cè)實(shí)踐者用于訓(xùn)練和驗(yàn)證吸煙行為識(shí)別模型可應(yīng)用于公共場(chǎng)所禁煙監(jiān)控、工地安全巡檢等場(chǎng)景。資源包共594個(gè)文件由297張jpg圖片與297個(gè)xml標(biāo)注文件一一對(duì)應(yīng)組成壓縮包約59.11MBxml采用Pascal VOC格式記錄目標(biāo)類(lèi)別與邊界框坐標(biāo)可直接轉(zhuǎn)換為YOLO訓(xùn)練所需的txt標(biāo)簽省去自行標(biāo)注的時(shí)間成本。目前已有376人學(xué)習(xí)下載具備一定的參考熱度。數(shù)據(jù)集覆蓋多種人物姿態(tài)與拍攝角度圖片命名規(guī)范、標(biāo)注與圖像嚴(yán)格配對(duì)便于按比例劃分訓(xùn)練集、驗(yàn)證集與測(cè)試集快速搭建數(shù)據(jù)加載與增強(qiáng)流程。讀者可基于此完成模型訓(xùn)練、精度評(píng)估與推理部署的完整鏈路也可用于對(duì)比不同檢測(cè)算法的性能差異是入門(mén)目標(biāo)檢測(cè)與行為識(shí)別任務(wù)的實(shí)用素材。1. 從一次翻車(chē)現(xiàn)場(chǎng)說(shuō)起這套 YOLO 抽煙檢測(cè)數(shù)據(jù)集到底能干什么去年幫一個(gè)做智慧工地安全巡檢的朋友調(diào)模型他手里有一批現(xiàn)場(chǎng)攝像頭截圖想自動(dòng)識(shí)別工人有沒(méi)有在禁煙區(qū)抽煙。想法很直接但卡在了第一步——沒(méi)有標(biāo)注數(shù)據(jù)。網(wǎng)上找的開(kāi)源數(shù)據(jù)集要么類(lèi)別對(duì)不上要么標(biāo)注格式是 VOC XML跟他用的 YOLO 訓(xùn)練腳本對(duì)不上號(hào)。折騰了三天最后發(fā)現(xiàn)真正缺的不是模型結(jié)構(gòu)而是一份拿來(lái)就能用的抽煙目標(biāo)檢測(cè)數(shù)據(jù)集。這次要拆的這份資源就是沖著這個(gè)場(chǎng)景來(lái)的一批已經(jīng)標(biāo)好的抽煙行為圖片標(biāo)注文件直接適配 YOLO 系列訓(xùn)練流程。它解決的不是“模型怎么設(shè)計(jì)”的問(wèn)題而是“數(shù)據(jù)從哪來(lái)、格式怎么對(duì)齊、訓(xùn)練腳本怎么接”的問(wèn)題。適合兩類(lèi)人一是剛接觸目標(biāo)檢測(cè)、想拿一個(gè)真實(shí)場(chǎng)景數(shù)據(jù)集跑通訓(xùn)練和推理全流程的新手二是手頭有類(lèi)似安防、巡檢需求需要快速驗(yàn)證方案可行性的開(kāi)發(fā)者。數(shù)據(jù)集本身不復(fù)雜但“能直接跑起來(lái)”這件事比想象中省事得多。2. 數(shù)據(jù)集結(jié)構(gòu)與標(biāo)注格式先搞清楚手里拿的是什么2.1 目錄組織與文件命名習(xí)慣拿到一份目標(biāo)檢測(cè)數(shù)據(jù)集第一件事不是急著寫(xiě)訓(xùn)練腳本而是把目錄結(jié)構(gòu)摸清楚。這份抽煙檢測(cè)數(shù)據(jù)集的組織方式比較典型常見(jiàn)做法是分成 images 和 labels 兩個(gè)平行目錄各自下面再按 train、val 甚至 test 劃分。圖片格式一般是 jpg 或 png標(biāo)注文件是同名的 txt放在 labels 對(duì)應(yīng)子目錄里。一個(gè)典型的目錄長(zhǎng)這樣smoking_dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ ├── 0002.jpg │ │ └── ... │ └── val/ │ ├── 0101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ ├── 0002.txt │ │ └── ... │ └── val/ │ ├── 0101.txt │ └── ... └── data.yaml這里有個(gè)容易翻車(chē)的點(diǎn)圖片和標(biāo)注文件的文件名必須嚴(yán)格對(duì)應(yīng)包括大小寫(xiě)。有些數(shù)據(jù)集從不同來(lái)源拼湊會(huì)出現(xiàn)圖片叫IMG_001.JPG、標(biāo)注叫img_001.txt的情況訓(xùn)練時(shí)找不到標(biāo)簽loss 直接不降。我一般會(huì)先跑一段腳本做文件名一致性校驗(yàn)確認(rèn)沒(méi)問(wèn)題再往下走。2.2 YOLO 標(biāo)注格式逐字段拆解YOLO 用的標(biāo)注格式是每張圖一個(gè) txt每行代表一個(gè)目標(biāo)框格式為class_id x_center y_center width height這五個(gè)字段都是歸一化到 0 到 1 之間的浮點(diǎn)數(shù)。class_id從 0 開(kāi)始編號(hào)這份數(shù)據(jù)集如果只檢測(cè)“抽煙”一個(gè)類(lèi)別那所有行的 class_id 都是 0。x_center和y_center是框中心點(diǎn)相對(duì)于圖片寬高的比例width和height是框的寬高相對(duì)于圖片寬高的比例。舉個(gè)例子一張 640x480 的圖某個(gè)人手部夾煙的區(qū)域框在像素坐標(biāo) (320, 240) 為中心寬 80 像素、高 60 像素那標(biāo)注行就是0 0.5 0.5 0.125 0.125計(jì)算方式x_center 320/640 0.5y_center 240/480 0.5width 80/640 0.125height 60/480 0.125。注意如果標(biāo)注文件里出現(xiàn)大于 1 的數(shù)值說(shuō)明標(biāo)注時(shí)沒(méi)有做歸一化直接用了像素坐標(biāo)。這種情況需要自己寫(xiě)腳本轉(zhuǎn)換否則訓(xùn)練時(shí)框會(huì)跑到圖片外面去。2.3 data.yaml 配置文件的寫(xiě)法YOLO 訓(xùn)練時(shí)需要一個(gè) yaml 文件告訴它數(shù)據(jù)在哪、有幾個(gè)類(lèi)別、類(lèi)別名是什么。這份數(shù)據(jù)集的 data.yaml 常見(jiàn)寫(xiě)法如下path: ./smoking_dataset train: images/train val: images/val nc: 1 names: 0: smokingpath是數(shù)據(jù)集根目錄train和val是相對(duì)于根目錄的圖片路徑。nc是類(lèi)別數(shù)量這里只有抽煙一個(gè)類(lèi)所以是 1。names是類(lèi)別索引到名稱(chēng)的映射推理時(shí)輸出的類(lèi)別名就來(lái)自這里。如果后續(xù)想擴(kuò)展成“抽煙”和“打電話”兩個(gè)類(lèi)只需要把 nc 改成 2names 里加一行1: calling然后確保標(biāo)注文件里對(duì)應(yīng)類(lèi)別的 class_id 正確即可。這個(gè)配置文件看著簡(jiǎn)單但路徑寫(xiě)錯(cuò)、縮進(jìn)用 tab 而不是空格都會(huì)導(dǎo)致訓(xùn)練啟動(dòng)時(shí)報(bào)錯(cuò)。我習(xí)慣用python -c import yaml; yaml.safe_load(open(data.yaml))先驗(yàn)證一下 yaml 能不能正常解析。3. 從零跑通訓(xùn)練環(huán)境、命令與參數(shù)調(diào)優(yōu)3.1 環(huán)境準(zhǔn)備與依賴(lài)安裝這份數(shù)據(jù)集適配的是 Ultralytics 版本的 YOLO也就是常說(shuō)的 YOLOv5 或 YOLOv8 那一套。環(huán)境準(zhǔn)備不復(fù)雜但版本對(duì)齊很重要。我一般用 conda 建一個(gè)干凈環(huán)境避免和系統(tǒng)里的包打架conda create -n yolo_smoke python3.9 -y conda activate yolo_smoke # 安裝 PyTorch根據(jù)自己顯卡的 CUDA 版本選對(duì)應(yīng)命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安裝 ultralytics pip install ultralytics裝完之后用yolo checks看一眼環(huán)境確認(rèn) PyTorch 能識(shí)別到 GPU。如果顯示 CPU only訓(xùn)練會(huì)慢到懷疑人生。顯卡驅(qū)動(dòng)和 CUDA 版本對(duì)不上是新手最常見(jiàn)的翻車(chē)點(diǎn)表現(xiàn)是torch.cuda.is_available()返回 False這時(shí)候別急著換代碼先查驅(qū)動(dòng)版本和 CUDA 版本是否匹配。3.2 訓(xùn)練命令與關(guān)鍵參數(shù)說(shuō)明環(huán)境沒(méi)問(wèn)題之后訓(xùn)練命令其實(shí)就一行yolo detect train \ data./smoking_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ project./runs/smoke \ nameexp1逐項(xiàng)說(shuō)一下這幾個(gè)參數(shù)。data指向剛才的 yaml 文件。model是預(yù)訓(xùn)練權(quán)重yolov8n 是最小的 nano 版本速度快、精度夠用適合先跑通流程如果顯存充足可以換 yolov8s 或 yolov8m。epochs是訓(xùn)練輪數(shù)100 輪對(duì)這個(gè)小數(shù)據(jù)集通常夠收斂但要看 loss 曲線決定要不要加。imgsz是輸入圖片尺寸640 是默認(rèn)值如果原始圖片分辨率很高可以調(diào)到 1280但顯存占用會(huì)明顯上升。batch是批大小16 在 8G 顯存上比較穩(wěn)爆顯存就降到 8 或 4。lr0是初始學(xué)習(xí)率0.01 是 YOLO 系列的常用值數(shù)據(jù)集小的時(shí)候可以降到 0.001 防止過(guò)擬合。訓(xùn)練過(guò)程中會(huì)在runs/smoke/exp1下生成權(quán)重文件、loss 曲線和驗(yàn)證結(jié)果。重點(diǎn)看results.png里的 box_loss 和 cls_loss 是否穩(wěn)定下降如果震蕩劇烈多半是學(xué)習(xí)率太大或者 batch 太小。3.3 推理驗(yàn)證與結(jié)果解讀訓(xùn)練完之后拿驗(yàn)證集或自己的圖片跑推理yolo detect predict \ model./runs/smoke/exp1/weights/best.pt \ source./smoking_dataset/images/val \ conf0.25 \ saveTrueconf是置信度閾值低于這個(gè)值的框會(huì)被過(guò)濾掉。0.25 是常用起點(diǎn)如果發(fā)現(xiàn)漏檢多就降到 0.1誤檢多就升到 0.5。推理結(jié)果默認(rèn)保存在runs/detect/predict下每張圖會(huì)畫(huà)出檢測(cè)框和類(lèi)別置信度??唇Y(jié)果的時(shí)候別只看畫(huà)框的圖要對(duì)照原始標(biāo)注看有沒(méi)有漏標(biāo)或錯(cuò)標(biāo)。數(shù)據(jù)集質(zhì)量比模型結(jié)構(gòu)更影響最終效果如果標(biāo)注本身把煙頭、手指、打火機(jī)混在一起標(biāo)成 smoking模型學(xué)出來(lái)的框會(huì)很不穩(wěn)定。我一般會(huì)隨機(jī)抽 20 張訓(xùn)練圖把標(biāo)注框畫(huà)出來(lái)和原圖疊在一起看確認(rèn)標(biāo)注區(qū)域確實(shí)是抽煙行為本身而不是周?chē)鸁o(wú)關(guān)物體。4. 避坑與排查訓(xùn)練不收斂、顯存爆炸、標(biāo)注錯(cuò)位怎么處理4.1 現(xiàn)象loss 不下降或震蕩劇烈原因最常見(jiàn)的是學(xué)習(xí)率過(guò)大、batch 太小導(dǎo)致梯度噪聲大或者標(biāo)注文件里有大量越界框坐標(biāo)大于 1 或小于 0。另外如果 data.yaml 里 nc 寫(xiě)錯(cuò)比如實(shí)際有 2 類(lèi)但 nc 寫(xiě)了 1模型會(huì)把第二類(lèi)當(dāng)成背景l(fā)oss 也會(huì)異常。解決先把 lr0 降到 0.001 試 10 個(gè) epoch看 loss 是否開(kāi)始下降。同時(shí)寫(xiě)腳本掃描所有標(biāo)注文件檢查每行是否有超出 [0,1] 范圍的數(shù)值。如果 nc 寫(xiě)錯(cuò)修正后重新訓(xùn)練。我習(xí)慣在訓(xùn)練前跑一遍數(shù)據(jù)校驗(yàn)?zāi)_本把越界框、空標(biāo)注文件、圖片損壞這些問(wèn)題提前暴露出來(lái)。4.2 現(xiàn)象CUDA out of memory原因batch 太大、imgsz 太高或者顯卡被其他進(jìn)程占用。YOLO 訓(xùn)練時(shí)顯存占用和 batch * imgsz 基本成正比。解決先把 batch 減半如果還爆就把 imgsz 從 640 降到 416。另外用nvidia-smi看一下是不是有殘留進(jìn)程占著顯存有的話 kill 掉再跑。如果顯卡本身顯存小于 6G建議直接用 yolov8n 加 imgsz416別硬上大模型。4.3 現(xiàn)象推理時(shí)框的位置明顯偏移原因標(biāo)注時(shí)用的歸一化基準(zhǔn)和推理時(shí)不一致。比如標(biāo)注時(shí)按圖片原始尺寸歸一化但推理時(shí) YOLO 會(huì)把圖片 resize 到 imgsz如果 resize 時(shí)沒(méi)有保持寬高比框就會(huì)偏。另一種可能是標(biāo)注文件里的坐標(biāo)是像素值而不是歸一化值訓(xùn)練時(shí)被當(dāng)成歸一化值用了。解決檢查標(biāo)注文件里是否有大于 1 的數(shù)值有的話說(shuō)明沒(méi)歸一化。另外確認(rèn)推理時(shí)的 imgsz 和訓(xùn)練時(shí)一致YOLO 默認(rèn)會(huì)做 letterbox 保持寬高比一般不會(huì)偏但如果自己改了預(yù)處理邏輯就要注意。4.4 現(xiàn)象驗(yàn)證集指標(biāo)很好但實(shí)際場(chǎng)景漏檢嚴(yán)重原因訓(xùn)練集和驗(yàn)證集分布太接近或者數(shù)據(jù)量太少導(dǎo)致過(guò)擬合。這份數(shù)據(jù)集如果場(chǎng)景單一比如都是同一個(gè)攝像頭角度模型學(xué)到的是背景特征而不是抽煙行為本身。解決盡量在驗(yàn)證集里放不同角度、不同光照的圖片。如果數(shù)據(jù)量確實(shí)少可以用 YOLO 自帶的增強(qiáng)參數(shù)比如degrees10、hsv_h0.015、flipud0.5在訓(xùn)練時(shí)隨機(jī)旋轉(zhuǎn)、調(diào)色、翻轉(zhuǎn)增加樣本多樣性。但增強(qiáng)不是萬(wàn)能的如果原始數(shù)據(jù)里抽煙行為本身就不清晰增強(qiáng)也救不回來(lái)。4.5 現(xiàn)象訓(xùn)練完模型文件很大推理速度慢原因用了較大的模型如 yolov8l 或 yolov8x或者沒(méi)有做模型導(dǎo)出優(yōu)化。解決如果只是驗(yàn)證方案yolov8n 足夠。如果要部署到邊緣設(shè)備可以把訓(xùn)練好的 pt 文件導(dǎo)出成 ONNX 或 TensorRTyolo export model./runs/smoke/exp1/weights/best.pt formatonnx導(dǎo)出后推理速度通常能提升 20% 到 50%具體取決于硬件。導(dǎo)出時(shí)注意 opset 版本和推理引擎的兼容性O(shè)NNX Runtime 一般用 opset 12 或 13 比較穩(wěn)。5. 二次開(kāi)發(fā)與改造從單類(lèi)檢測(cè)到多場(chǎng)景適配的實(shí)用技巧5.1 擴(kuò)展類(lèi)別加一個(gè)“打電話”檢測(cè)如果業(yè)務(wù)場(chǎng)景里還需要檢測(cè)打電話行為最直接的做法是在現(xiàn)有數(shù)據(jù)集基礎(chǔ)上補(bǔ)充打電話的標(biāo)注圖片然后把 data.yaml 改成nc: 2 names: 0: smoking 1: calling標(biāo)注文件里打電話的框 class_id 寫(xiě) 1。重新訓(xùn)練時(shí)建議用之前訓(xùn)練好的權(quán)重做初始化而不是從 COCO 預(yù)訓(xùn)練權(quán)重開(kāi)始這樣收斂更快。命令里把model換成./runs/smoke/exp1/weights/best.pt即可。5.2 替換主干網(wǎng)絡(luò)換更輕量的 backbone如果部署設(shè)備算力有限可以把 YOLOv8 的 backbone 換成 MobileNet 或 ShuffleNet。Ultralytics 支持通過(guò) yaml 配置文件自定義模型結(jié)構(gòu)常見(jiàn)做法是復(fù)制一份 yolov8n.yaml把 backbone 部分的 Conv 模塊替換成輕量卷積。改完之后用yolo detect train model./custom_yolov8n.yaml從頭訓(xùn)練。這個(gè)操作對(duì)新手不太友好建議先把默認(rèn)模型跑通再折騰。5.3 數(shù)據(jù)增強(qiáng)策略調(diào)整針對(duì)抽煙場(chǎng)景的定制抽煙檢測(cè)有個(gè)特殊點(diǎn)煙頭很小如果增強(qiáng)時(shí)縮放太狠煙頭可能直接消失。我一般會(huì)把scale參數(shù)控制在 0.5 以?xún)?nèi)避免過(guò)度縮小。另外mosaic增強(qiáng)默認(rèn)開(kāi)啟它會(huì)把四張圖拼成一張對(duì)小目標(biāo)檢測(cè)有幫助但如果圖片里抽煙區(qū)域本來(lái)就小mosaic 后可能更小這時(shí)候可以適當(dāng)降低 mosaic 概率。5.4 模型導(dǎo)出與部署驗(yàn)證訓(xùn)練完的模型最終要落到實(shí)際業(yè)務(wù)里。如果部署在服務(wù)器上可以用 ONNX Runtime 或 TensorRT如果部署在邊緣設(shè)備可以用 NCNN 或 OpenVINO。導(dǎo)出命令前面提過(guò)這里補(bǔ)充一個(gè)驗(yàn)證導(dǎo)出模型是否正確的技巧用同樣的驗(yàn)證集分別跑 pt 和 onnx 模型對(duì)比檢測(cè)框數(shù)量和置信度如果差異超過(guò) 5%說(shuō)明導(dǎo)出過(guò)程有問(wèn)題重點(diǎn)檢查輸入尺寸和歸一化參數(shù)是否一致。5.5 一個(gè)我踩過(guò)的坑別在訓(xùn)練集上調(diào)參剛開(kāi)始做這個(gè)項(xiàng)目的時(shí)候我習(xí)慣在訓(xùn)練集上看 loss 和指標(biāo)覺(jué)得降得不錯(cuò)就收工。結(jié)果上線后發(fā)現(xiàn)實(shí)際場(chǎng)景漏檢嚴(yán)重。后來(lái)才意識(shí)到訓(xùn)練集上的指標(biāo)只能說(shuō)明模型“記住”了這些圖不能說(shuō)明它“學(xué)會(huì)”了抽煙行為。從那以后我每次訓(xùn)練都強(qiáng)制留出一部分完全沒(méi)參與訓(xùn)練的圖片做最終驗(yàn)證哪怕數(shù)據(jù)量少也要留 10% 出來(lái)。這個(gè)習(xí)慣幫我省了很多次返工。希望這份拆解能幫你把這份抽煙檢測(cè)數(shù)據(jù)集順利跑起來(lái)少走點(diǎn)我當(dāng)年走過(guò)的彎路。本文還有配套的精品資源點(diǎn)擊獲取