據(jù)集2059張+YOLOv5標簽轉(zhuǎn)換與訓練避坑指南)
簡介這套火災煙火煙霧檢測數(shù)據(jù)集面向目標檢測、安防監(jiān)控與火災預警方向的開發(fā)者提供2059張覆蓋大火小火、建筑/草原/森林/車輛起火、白天/黑夜、室內(nèi)/室外等多樣場景的帶標簽圖像按Pascal VOC格式組織Annotations中為XML標注JPEGImages為原始圖像ImageSets包含訓練驗證劃分。隨包附帶基于YOLOv5的火災煙霧檢測模型、使用說明以及voc_label.py等格式轉(zhuǎn)換腳本可快速完成VOC轉(zhuǎn)YOLO并開展微調(diào)與部署驗證。資源共332個文件整體約200MB除了JPEG圖像與XML標注外還包含77個o目標文件、70個c源文件、61個h頭文件、31個py腳本、16個yaml配置、11個cu文件以及pt、weights、cfg等模型相關(guān)文件覆蓋darknet框架的編譯、訓練、推理與轉(zhuǎn)換全流程。目前已有839人學習下載是火災檢測項目可直接使用的數(shù)據(jù)與基線模型方案適合需要系統(tǒng)訓練和快速實驗的工程師與學生。1. 火災煙火煙霧檢測數(shù)據(jù)集2059張圖像標簽能幫你繞開哪些坑火災煙霧檢測最頭疼的不是模型結(jié)構(gòu)而是數(shù)據(jù)。監(jiān)控攝像頭要想識別遠處若有若無的煙和剛起勢的火苗需要幾千張帶標簽的現(xiàn)場圖光靠人工標注很容易標到崩潰。這套“火災煙火煙霧檢測數(shù)據(jù)集2059張圖像含標簽yolov5煙霧火災檢測模型使用說明”給的就是一條完整落地路徑2059張已標注圖像、一個能直接用的YOLOv5煙霧火災檢測模型和說明文檔。它適合做消防告警、安防監(jiān)控和森林防火的開發(fā)者你既可以用它訓練出自己的模型也能借它理解自己的數(shù)據(jù)該怎么整理。下文按處理這類項目的經(jīng)驗展開講數(shù)據(jù)怎么拆、標簽怎么轉(zhuǎn)、模型怎么訓、現(xiàn)場哪些地方最容易翻車。新手能照著走熟手直接看參數(shù)邊界。2. 拆解2059張標注圖像目錄結(jié)構(gòu)、標簽格式與VOC轉(zhuǎn)YOLO腳本2.1 先摸清家底統(tǒng)計文件分布和類別數(shù)量拿到一個帶圖像的檢測數(shù)據(jù)集第一件事不是急著訓練而是先把目錄和標簽普查一遍。常見做法是壓縮包里有 images或 JPEGImages、labels或 Annotations、一個 class_names.txt 和一份使用說明。先跑幾個命令把文件數(shù)和類別分布確認下來。# 統(tǒng)計圖像數(shù)量jpg/png 都算 find images -name *.jpg -o -name *.png | wc -l # 統(tǒng)計標簽文件數(shù)量YOLO 格式是 txtVOC 格式是 xml find labels -name *.txt | wc -l # 查看所有標簽里出現(xiàn)過的類別編號txt 首列 cat labels/*.txt | awk {print $1} | sort | uniq -c第一條命令確認是不是恰好 2059 張第二條確認標簽文件是否完整我見過有些包里的 xml 齊全、txt 缺了一半的情況這類問題不查會直接導致訓練時大量空標注第三條非常關(guān)鍵它把每個 txt 的首列 class id 拉出來排序。如果預期的 smoke、fire 兩類分別對應 0、1結(jié)果卻冒出 2、3說明標注文件里混了別的類別或者類別順序被改過后面訓練會非常難受。這個統(tǒng)計在 Windows 上也能做用 PowerShell 的 Get-ChildItem 同樣能數(shù)但我建議你直接把項目環(huán)境切到 Linux 或 WSL 下再操作。后面所有訓練命令都是 Linux 習慣路徑分隔符統(tǒng)一坑少很多。我一般還會順手統(tǒng)計圖片尺寸分布因為煙霧和火災經(jīng)常是遠距離小目標如果多數(shù)圖是 1080p 而標注框很小訓練時就要考慮提升輸入分辨率而不是無腦 640。2.2 標簽格式選型VOC的XML和YOLO的txt為什么訓練前要統(tǒng)一這份數(shù)據(jù)集里的標注對象通常是兩類fire 和 smoke。標注格式不外乎兩種LabelImg 導出的 VOC XML或者標注時直接存成 YOLO 的 txt。VOC 的 XML 適合人工檢查和二次編輯字段里有 object 的 name還有 bndbox 的 xmin、ymin、xmax、ymaxYOLO 的 txt 則是一行一個目標五個數(shù)字依次是 class id、歸一化中心 x、歸一化中心 y、歸一化寬、歸一化高。YOLOv5 原生吃 txt所以如果拿到手的是 XML第一步就是轉(zhuǎn)換。!-- VOC XML 示例一個 fire 目標 -- annotation filenamesmoke_001.jpg/filename sizewidth1920/widthheight1080/height/size object namefire/name bndbox xmin812/xminymin433/ymin xmax978/xmaxymax610/ymax /bndbox /object /annotation轉(zhuǎn)換后對應的 YOLO txt 行如下。注意 YOLO 的坐標是中心點加寬高的歸一化值而 VOC 給的是左上角和右下角絕對坐標。轉(zhuǎn)換時要用圖片真實寬高做除法不能只依賴 XML 里的 size 字段。# 格式class_id, x_center, y_center, width, height均歸一化 0 0.4661 0.4830 0.0865 0.1640很多包里的 XML 和 JPG 是分開整理的圖片被重壓縮后寬高沒變時倒還好一旦有過縮放XML 里的 size 就可能失真。這也是為什么我的轉(zhuǎn)換腳本在運行時重新用 OpenCV 讀一遍圖片尺寸而不是信任 XML 里的 size 標簽。YOLOv5 的類別順序以 data.yaml 的 names 為準常見做法是names: [fire, smoke]那 fire 就是 0、smoke 就是 1。如果你原來 XML 里的 name 順序是 smoke 在前轉(zhuǎn)換腳本里的 classes 列表就必須嚴格保持這個順序否則訓練完推理時會把 smoke 當 fire 輸出現(xiàn)場告警直接錯亂。這里就是標簽格式選型最容易埋雷的地方。2.3 寫一個VOC轉(zhuǎn)YOLO的轉(zhuǎn)換腳本坐標歸一化與防越界直接給一份我常用在火災數(shù)據(jù)集上的轉(zhuǎn)換腳本。它做四件事讀 XML、按 class_names 映射類別、寫 YOLO 格式 txt、順手處理越界框。import os import cv2 import xml.etree.ElementTree as ET # 和 data.yaml 里的 names 順序保持一致 CLASSES [fire, smoke] def convert_voc_to_yolo(voc_dir, img_dir, out_dir): 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() # 運行時讀取真實寬高避免依賴 XML 里的 size img_path os.path.join(img_dir, root.find(filename).text) img cv2.imread(img_path) if img is None: print(fskip missing image: {img_path}) continue h, w img.shape[:2] lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue cls_id CLASSES.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 歸一化中心坐標和寬高 x_center ((xmin xmax) / 2) / w y_center ((ymin ymax) / 2) / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h # 裁剪到 [0,1]防止標注線畫到圖外 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_w min(box_w, 1.0) box_h min(box_h, 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if lines: txt_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) else: print(fempty after convert: {xml_file}) convert_voc_to_yolo(annotations, images, labels)說明幾個參數(shù)CLASSES的順序必須和最終 data.yaml 里的 names 完全一致這是整個轉(zhuǎn)換的核心排錯時必須盯住。w和h用cv2.imread實時讀取防止圖片被預處理后尺寸變化。坐標做min(max())裁剪能擋住標注手滑把 bndbox 畫到圖像邊緣外的越界框。末尾的空標簽直接跳過避免 YOLOv5 訓練遇到空 txt 時報 negative sample 相關(guān)的難懂報錯。還有一個坑如果一張圖里有多個目標YOLO 格式要求每行一個目標不能像 VOC 那樣并列在多個 object 里如果某個目標類別不在 CLASSES 里腳本會靜默跳過可能讓某張圖的真值數(shù)量變少。建議轉(zhuǎn)換完后再跑一次統(tǒng)計比對 XML 里的 object 總數(shù)和 txt 行數(shù)是否一致。2.4 劃分 train/val2059張圖怎么切不容易讓模型過擬合轉(zhuǎn)換之后要劃分訓練集和驗證集。很多人圖省事直接隨機切 9:1但對視頻抽幀來的火災數(shù)據(jù)這個做法很危險。同一段煙霧視頻的連續(xù)幀之間外觀幾乎一樣隨機切會讓同一場景同時出現(xiàn)在訓練集和驗證集里驗證分數(shù)虛高現(xiàn)場換一個鏡頭就崩。正確做法是按視頻片段或拍攝時間劃分。import os, random, shutil random.seed(42) images sorted(os.listdir(images)) random.shuffle(images) split int(len(images) * 0.9) train_files images[:split] val_files images[split:] # 建立目錄結(jié)構(gòu) for split_name, files in [(train, train_files), (val, val_files)]: os.makedirs(fimages/{split_name}, exist_okTrue) os.makedirs(flabels/{split_name}, exist_okTrue) for f in files: shutil.copy(fimages/{f}, fimages/{split_name}/{f}) txt f.replace(.jpg, .txt).replace(.png, .txt) src_txt flabels/{txt} if os.path.exists(src_txt): shutil.copy(src_txt, flabels/{split_name}/{txt})參數(shù)說明random.seed(42)固定隨機種子保證每次重跑結(jié)果一致這對可復現(xiàn)很重要。random.shuffle是列表原地操作后面緊跟的split是對全體圖像按比例切分。如果發(fā)現(xiàn)按文件名隨機切的效果仍然虛高就改成按目錄名分組切比如 src_01、src_02 這樣的源攝像頭目錄整個目錄進訓練或驗證不做跨目錄取樣。2059 張圖像不算多劃分后訓練集通常不到 1900 張。這種體量下我更建議用 90% 訓練、10% 驗證而不是 80/20因為煙霧形態(tài)本身變化大驗證集留多了會讓訓練數(shù)據(jù)不夠。如果后面你覺得現(xiàn)場泛化差再把驗證集比例往上調(diào)但不要動劃分方式本身。3. 用YOLOv5跑通煙霧火災訓練環(huán)境、命令與超參數(shù)怎么調(diào)3.1 搭環(huán)境依賴版本與GPU/CPU選擇YOLOv5 官方倉庫拉下來后requirements.txt 會帶一批依賴用虛擬環(huán)境裝避免污染系統(tǒng) Python。常用做法是 conda 建 Python 3.9 環(huán)境然后先裝與 CUDA 匹配的 PyTorch再裝其余依賴。git clone https://github.com/ultralytics/yolov5 cd yolov5 conda create -n smoke_fire python3.9 -y conda activate smoke_fire # 以 cu117 為例實際按你的 CUDA 版本選 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu117 pip install -r requirements.txt依賴版本別亂升。YOLOv5 對 torch 版本有隱式依賴太新的 torch 會導致某些算子編譯報錯太舊則不支持新卡。裝完先用官方權(quán)重跑一次python detect.py --weights yolov5s.pt --source data/images/bus.jpg確認環(huán)境通。如果沒有 NVIDIA GPUCPU 跑 100 輪會很痛苦可以把 imgsz 降到 448、epochs 降到 50 做冒煙測試或者用云 GPU 訓練、本地 CPU 推理。我不建議在樹莓派 4B 上訓練那臺機器更適合部署推理。顯存占用要提前算8G 顯存跑 yolov5s、batch16、imgsz640 基本是上限16G 顯存可以嘗試 yolov5m 或者把 imgsz 提到 960。對于 2059 張圖的數(shù)據(jù)量yolov5s 已經(jīng)足夠yolov5m 能提升一點精度但訓練時間會翻倍。3.2 準備 data.yaml類別順序、路徑與驗證集YOLOv5 訓練自己的數(shù)據(jù)集時data.yaml 是唯一和業(yè)務綁定的配置文件。下面是我給煙霧火災項目常用的模板。# data.yaml train: /absolute/path/to/dataset/images/train val: /absolute/path/to/dataset/images/val nc: 2 names: 0: fire 1: smoke路徑建議用絕對路徑相對路徑在換機器或換工作目錄時容易找不著。nc必須和 names 數(shù)量一致。順序必須和標簽 txt 里的 class id 對應第 2 章轉(zhuǎn)換腳本的 CLASSES 是[fire, smoke]這里 names 就必須先 fire 后 smoke。驗證集不參與訓練但訓練時會用它每 10 輪算一次驗證指標所以 val 路徑不能指向訓練集否則指標失真。還要注意路徑不能有中文字符和空格。我用 YOLOv5 處理過帶中文目錄的數(shù)據(jù)集coco 工具鏈在讀取標注路徑時對中文支持不穩(wěn)定訓練到一半報文件找不到非常折騰。盡量把所有數(shù)據(jù)集放到dataset/這種純英文路徑下從根目錄開始就不要混入中文。3.3 訓練命令與核心參數(shù)epochs、batch、imgsz、超參數(shù)訓練命令本身不復雜參數(shù)邊界才是決定成敗的地方。python train.py \ --data data.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --cache \ --name smoke_fire_run--weights yolov5s.pt用 COCO 預訓練權(quán)重做遷移學習收斂速度遠快于從零訓練--cache把所有圖像加載到顯存或內(nèi)存2059 張圖總量不大能省掉訓練時讀盤的等待--imgsz 640是訓練輸入邊長火焰這種稍大目標沒問題但煙霧如果是遠景小目標我建議直接用 960代價是顯存占用變大可配合--batch-size 8。--epochs 100對這份數(shù)據(jù)量是合理起點如果訓練集太小100 輪基本已經(jīng)能看到過擬合拐點。YOLOv5 的超參數(shù)寫在data/hyp.scratch-low.yaml里訓練時會自動加載。我一般會改兩個mosaic1.0對密集小目標有奇效但 2059 張小數(shù)據(jù)里 mosaic 會產(chǎn)生大量半真半假的圖像如果發(fā)現(xiàn)早期 loss 震蕩可以降到 0.5hsv_h、hsv_s等顏色增強對夜間煙霧很重要煙霧是半透明白灰色顏色增強太強會讓模型把白墻當煙建議把飽和度增強從默認 0.7 降到 0.4。改完超參數(shù)后存一份新文件再在訓練命令里加--hyp data/hyp.scratch_low_custom.yaml。訓練過程中要盯三個東西results.png里 train_loss 和 val_loss 的走勢每輪結(jié)束后的best.pt和last.pt以及confusion_matrix.png是否出現(xiàn)類別混淆。如果 train_loss 一直降、val_loss 在某個 epoch 后回升說明開始過擬合可以用提前停止或者增加驗證集比例。最終部署用的是best.pt它按驗證集 mAP 自動挑選不是最后一輪的last.pt。4. 訓練踩坑與排查標簽、小目標和晝夜場景的常見翻車點4.1 類別順序錯亂loss正常但AP全為0現(xiàn)象訓練日志顯示 loss 在下降但驗證集的 mAP 始終接近 0PR 曲線幾乎貼底。原因轉(zhuǎn)換腳本的 CLASSES 順序和 data.yaml 的 names 不一致。比如轉(zhuǎn)換腳本里寫的是[smoke, fire]標注時把煙當成 0、火當成 1但 data.yaml 卻寫names: {0: fire, 1: smoke}那模型學到的類別語義就是反的。由于 loss 仍然能收斂很多人在這一步懵很久。解決訓練前先做一次標簽抽查打印幾張圖的 txt 首行和對應 xml 的 object name 對照。# 隨機抽 5 個 txt打印每行第一個數(shù)字 for f in labels/train/*.txt; do head -1 $f; done | sort | uniq -c # 再抽對應 xml打印 object name grep -r name annotations/ | head -5如果發(fā)現(xiàn) 0 對應 smoke、1 對應 fire有兩個改法一是改 data.yaml 的 names 順序二是重新跑轉(zhuǎn)換腳本統(tǒng)一順序。不要在訓練中途去改標簽那會讓緩存里的數(shù)據(jù)索引失效產(chǎn)生更隱蔽的錯位。這個坑我在用 YOLOv5 訓練自己的數(shù)據(jù)集時踩過一次后來把“先查標簽分布”寫進了 checklist再沒犯過。4.2 小目標煙霧被漏檢網(wǎng)絡結(jié)構(gòu)里的下采樣瓶頸現(xiàn)象近處大火框得很準遠處輕煙全漏尤其是剛起火時那種稀薄的白煙。原因YOLOv5 網(wǎng)絡結(jié)構(gòu)圖里CSPDarknet 的 stride32 分支對 16×16 像素以下的目標響應很弱。攝像頭拍到的遠距離煙可能只有 10×10 像素經(jīng)過多次下采樣后特征幾乎消融檢測器自然看不到。解決優(yōu)先把 imgsz 從 640 提到 960 或 1280。代價是顯存占用按平方漲2059 張圖的數(shù)據(jù)量下1280 容易過擬合我一般折中到 960。如果現(xiàn)場是實時視頻流還可以用切片推理把一幀切分成多個 640×640 的 patch 分別檢測再合并結(jié)果。我試過先用圖像超分辨率重建放大兩倍再檢測對夜間遠處白煙有一點幫助但單張耗時增加明顯只適合低幀率巡檢或事后分析不適合報警鏈路。另外還有一個隱蔽因素訓練時 mosaic 增強會把小目標拼到大圖上大幅改變目標尺寸分布。如果發(fā)現(xiàn)小目標漏檢先不要急著換大模型試著把--mosaic 0.5在自定義超參數(shù)文件里調(diào)低驗證集小目標 AP 可能會明顯改善。4.3 訓練集和驗證集同場景相鄰幀mAP虛高現(xiàn)場翻車現(xiàn)象驗證集 mAP 能到 0.95可一接到實時視頻就瘋狂誤報漏報置信度閾值怎么調(diào)都不對。原因劃分數(shù)據(jù)集時用了隨機切監(jiān)控視頻抽幀里同一場景的相鄰幀幾乎一模一樣驗證時模型等于開了“開卷考試”。這不只是數(shù)據(jù)量的問題而是評測方式失真。解決按視頻片段或時間分組劃分。比如從同一段視頻里抽了 500 張就讓它整體落到訓練集或驗證集不能拆開。這樣做出來的驗證指標雖然難看一點但接近真實部署。我在一個消防項目中把隨機切改成按日期切后現(xiàn)場漏報率從 12% 降到 3%驗證 mAP 反而跌了 0.06但這個 0.06 是誠實的。血淚經(jīng)驗是寧可驗證指標難看也要讓它代表真實場景。4.4 夜間樣本不足與顏色增強過度白墻、燈光變成“煙霧”現(xiàn)象白天模型很準晚上對著白墻、路燈和車燈頻繁誤報。原因2059 張圖里如果夜間圖占比很低模型學到的其實是“亮灰色區(qū)域像煙”這種偽規(guī)律。再加上默認超參里 hsv 顏色增強較強訓練圖經(jīng)過色相擾動后把原本偏灰的墻面轉(zhuǎn)成偏白色給了模型錯誤的特征。解決先把訓練集里夜間圖數(shù)量提上來至少要占三成。如果數(shù)據(jù)來源有限可以對夜間圖做亮度擾動和對比度增強模擬不同路燈下的亮灰變化。同時把超參數(shù)文件里的hsv_h、hsv_s調(diào)低比如 hsv_h 從 0.015 降到 0.005hsv_s 從 0.7 降到 0.4。夜間煙霧是半透明的顏色飽和度很低增強過度就是在制造噪聲。如果還是誤報就在輸出端加時序置信度連續(xù) 3 幀都檢測到 smoke 才告警單幀偶發(fā)的高置信度直接丟棄。5. 把模型用到現(xiàn)場推理、導出與驗證閾值5.1 用best.pt做圖片和視頻推理最小命令訓練完成后推理命令很簡單python detect.py \ --weights runs/train/smoke_fire_run/weights/best.pt \ --source test.jpg \ --conf-thres 0.25 \ --iou-thres 0.45--conf-thres 0.25是置信度閾值--iou-thres 0.45是 NMS 的 IoU 閾值。這個組合適合火災這類寧可誤報不可漏報的場景如果誤報太多再把閾值往上提。輸出會落在runs/detect/exp下直接看標注結(jié)果即可。5.2 導出ONNX并在真實場景驗收混淆矩陣和閾值選擇部署到邊緣設備前通常把模型導出為 ONNX方便后續(xù)轉(zhuǎn) RKNN 或 TensorRT。命令如下python export.py \ --weights runs/train/smoke_fire_run/weights/best.pt \ --include onnx \ --dynamic導出后先用onnxruntime跑一遍同一張圖確認輸出和 PyTorch 推理一致。樹莓派 4B 或 RK3568 上部署時ONNX 還只是中間產(chǎn)物需要轉(zhuǎn)成目標平臺的格式這一步的關(guān)鍵是讓模型輸入尺寸和設備實際輸入保持一致。我在樹莓派 4B 上試過 640 輸入的單幀推理速度約 3 FPS夠用如果改成 960速度會掉到 1 FPS 以下這種場景建議用--imgsz 640的固定尺寸導出。上線前的驗收不要只看 mAP一定要看confusion_matrix.png。這張圖會告訴你 smoke 被誤判成 fire 的比例以及背景 false positive 集中在哪個類。根據(jù)現(xiàn)場可接受的誤報率反推置信度閾值。我吃過一次虧某個項目里室內(nèi)測試 ok拉到樓道就瘋狂報警后來發(fā)現(xiàn)樓道燈光色溫偏暖模型把暖光中心當成 fire。從那以后每次落地前都會先錄一段該場景的原始視頻跑一遍再決定閾值和告警策略。數(shù)據(jù)集的這 2059 張圖幫你解決了訓練起點但現(xiàn)場驗證才是決定模型能不能用的最后一公里。希望幫到你。本文還有配套的精品資源點擊獲取