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