
簡介基于Yolov5的吸煙行為檢測資源面向計算機視覺開發者與安防監控領域從業者提供從模型訓練到實時檢測的完整工程方案。資源包共100個文件涵蓋41個Python腳本、31個YAML配置文件、2個預訓練模型權重以及Dockerfile、Jupyter Notebook教程、說明文檔等既有可直接調用的推理代碼也有用于二次開發的配置與容器化部署支持。壓縮包整體約25.74MB體積精簡易用已有334人學習下載。內容覆蓋數據預處理、模型加載訓練、實時檢測全流程可幫助讀者理解Yolov5架構與吸煙行為特征學習過程支持調整檢測閾值、擴展行為類別適用于智慧工地、公共安全等場景。1. 拿到“基于yolov5的吸煙行為檢測源碼模型.zip”先想清楚要改什么這個標題在高校畢設和個人項目中非常常見一個壓縮包里放著yolov5工程目錄、一份標注好的數據集配置、一個訓練好的best.pt權重以及若干推理腳本。很多人解壓后第一件事是python detect.py跑通demo看到畫面里框出“smoke”就算項目完成——但這樣交付的項目到現場十個有九個會翻車。吸煙檢測和普通的目標檢測不一樣煙頭是極小目標在1080p畫面里可能只有10×10像素手和煙的組合形態又和打火機、筆、手指高度混淆更麻煩的是攝像頭視角通常不是正對桌面而是斜上方或遠處目標形變嚴重。真正決定這個項目成敗的不是yolov5本身能不能跑通而是你如何針對“小目標、高誤報、連續幀”這三個特性去做數據標注和推理策略調整。本文就按“數據準備→訓練調參→部署優化→誤報治理”這條線把這個zip變成一套能扛住真實攝像頭場景的方案。2. 先把數據關過了標注邊界、小目標增強與類別平衡2.1 標注策略先定下來框煙頭而不是框手、框嘴吸煙檢測有一個常見的誤區覺得檢測目標是“人吸煙”這個動作于是去框整只手、框嘴附近區域甚至框整個上半身。這樣訓練出來的模型有兩個毛病一是框太大導致定位粗糙二是類別語義不干凈。吸煙動作是一個連續過程煙頭和嘴、手的相對位置關系隨時變化真正穩定的視覺特征是“燃燒的煙頭”和“夾煙的手型”其中煙頭因為是亮橙色、高溫區域在圖像上有強對比度是最好學、最穩定的錨點。我一般會把標注策略定為只標注煙頭同時把類別名從smoking改成cigarette。如果你手里只有一份已經按“人”或“手”標注的數據集也不要急著重標可以在訓練時把類別合并但推理時仍然只輸出煙頭框再用手部檢測或人臉位置做二次規則判斷。這樣做的理由是yolov5對類別數量的變化非常敏感一個類別和兩個類別的訓練收斂速度和誤報率差很多。標注工具建議用labelImg或labelmeyolov5原生支持這兩種工具導出的格式。labelImg導出的是VOC XML需要轉成yolo txt格式labelme導出json需要寫腳本轉。貼一個常見轉換腳本片段import os import json from PIL import Image def labelme_to_yolo(json_path, out_dir, class_map): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w, img_h data[imageWidth], data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue # labelme 用多邊形或矩形這里統一取外接矩形 xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) # 歸一化到 0-1同時防止越界 cx ((x1 x2) / 2.0) / img_w cy ((y1 y2) / 2.0) / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h lines.append(f{class_map[label]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, os.path.basename(json_path).replace(.json, .txt)), w) as f: f.write(\n.join(lines))腳本的核心邏輯是把labelme的json標注轉成yolo格式的txt文件每個txt文件一行一個目標格式是“類別id 中心點x 中心點y 寬 高”全部歸一化到0~1。需要注意的點是越界處理煙頭貼近畫面邊緣時外接矩形可能超出圖片邊界yolov5訓練時這類邊框會被忽略或產生異常loss最好是先裁剪掉越界部分再歸一化。2.2 小目標增強是這類的命門Mosaic和Copy-Paste怎么取舍吸煙檢測的訓練樣本里煙頭在整張圖中的占比通常不到2%yolov5默認的Mosaic增強對這類極小目標反而是負擔。Mosaic把四張圖拼成一張相當于把每個目標再縮小一倍對小目標檢測器來說等于雪上加霜。這是yolov5在COCO上效果好但在吸煙檢測這種極端小目標場景下效果平平的直接原因。一個實用的做法是關掉Mosaic或把mosaic概率調低同時開啟Copy-Paste增強。Copy-Paste能把煙頭區域從原圖裁剪出來隨機粘貼到其他圖的任意位置既增加了目標的出現頻次又模擬了不同背景下煙頭的形態變化。在yolov5的hyp.scratch-low.yaml里這樣改mosaic: 0.5 # 默認1.0調低到0.5 copy_paste: 0.3 # 默認0.0開啟 mixup: 0.0 # 小目標場景不建議開mixup容易丟細節 hsv_h: 0.015 # 色相增強幅度保持小值煙頭橙色調不能亂偏 hsv_s: 0.7 hsv_v: 0.4這些超參數的調整邏輯是mosaic和mixup都會改變目標在圖像中的尺度分布對小目標不友好而copy-paste對尺度沒有影響只是換了背景。hsv增強里最敏感的是色相hsv_h煙頭的橙色是重要特征色相偏移過大會讓模型學到錯誤的顏色關聯反而增加誤檢。除了增強策略另一個關鍵參數是訓練分辨率。默認的imgsz640對煙頭來說太低了建議至少用到img1280。在顯存夠的情況下把推理分辨率從640提高到1280小目標的mAP通常能提升10個點以上。代價是訓練時間變長、顯存占用翻倍如果你只有一塊8G顯存的卡可以把batch降到8或者用yolov5n這種輕量結構。2.3 類別不平衡和數據清理的土辦法自己采集的數據集最常見的問題是正樣本有煙頭的幀遠多于負樣本沒有煙頭的幀。訓練時負樣本不能完全不加否則模型會瘋狂把打火機、筆芯、白色反光點都當成煙頭。負樣本的配比我一般控制在正負比 3:1 到 2:1。負樣本不需要一張張標只要保證沒有遺漏目標圖片放到images目錄下就行。另外值得做的一步是把模糊幀、嚴重過曝的幀直接刪掉。吸煙檢測要用在室內監控場景過曝會直接把煙頭的高溫特征抹平模型學到了這種壞樣本會讓白天和晚上的表現差異變大。清理時寫一個簡單腳本統計每張圖的亮度均值亮度分布跨越很大的幀優先檢查一遍。3. 模型選型與訓練流程從yolov5n到yolov5s別一上來就跑大模型3.1 yolov5網絡結構里哪些層對吸煙檢測最敏感yolov5的網絡結構看似統一但針對不同任務選擇不同depth_multiple和width_multiple會得到截然不同的效果。這兩個參數決定了CSPDarknet的深度和通道數。具體到吸煙檢測影響最大的是檢測頭部分的anchor尺寸。默認anchor是針對COCO的較大目標設計的最小尺寸是10×13也就是在640分辨率下一個目標至少要有10×13像素才能被第一層檢測頭感知。煙頭的像素占比遠低于這個值。所以拿到的zip里如果self._model.pt是直接拿官方預訓練權重不做任何修改去訓練自己的數據集效果一定會差。正確路徑是加載coco預訓練權重在自定義數據集上微調。在yolov5目錄下執行python train.py \ --data smoking.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch 16 \ --epochs 150 \ --cache ram \ --hyp hyp.scratch-low.yaml \ --device 0參數說明--data指向你寫好的smoking.yaml數據集配置--weights使用coco預訓練權重可以大大縮短收斂時間比從零訓練減少一半以上的epoch--img 1280是把輸入分辨率抬高到1280這是針對小目標最直接有效的一步--cache ram把圖片緩存到內存訓練速度能快很多前提是你的內存大于16G--hyp選擇增強配置較低的超參文件配合前面說的關閉mosaic、開啟copy_paste。yolov5會自動做autoanchor重新聚類anchor不需要手動改anchor配置。但訓練開始后建議看日志里anchors的收斂值如果聚類結果里最小的anchor仍然大于6×6說明數據集里煙頭的標注框還是偏大需要回頭檢查標注是否框得比煙頭大太多。3.2 用yolov5n換實時性用yolov5m換準確率如果最終要跑在Jetson Nano或者樹莓派這類邊緣設備上就別用yolov5s了。yolov5s在Jetson Nano上用TensorRT加速也只能跑到10~15 FPS而yolov5n可以穩定跑30 FPS。準確率差距在小目標場景下確實存在但可以用一個技巧彌補把yolov5n的檢測結果做多幀融合用一個滑窗去判斷連續5幀里同一位置是否都有煙頭檢出有才觸發報警。這樣單幀準確率的損失被時間維度的置信度補償了實時性和準確率都能保住。用yolov5n重訓的命令如下python train.py \ --data smoking.yaml \ --weights yolov5n.pt \ --img 960 \ --batch 32 \ --epochs 150 \ --device 0 \ --exist-ok這里把分辨率從1280降到960換取n模型在小顯存設備上的推理速度。--exist-ok是允許覆蓋已有訓練結果適合反復調參的場景。3.3 訓練完必看的三個指標文件訓練結束后不要只盯著P曲線和R曲線看小目標檢測場景最容易出現的是precision高但recall低也就是模型傾向保守只檢確定性極高的目標導致漏報。打開runs/train/exp/下的confusion_matrix.png重點關注cigarette這一類有多少真實樣本被預測成了background。如果這個比例超過15%說明模型對煙頭的特征學習不夠充分優先回去調整增強策略而不是加訓練輪數。另一個必看的是results.csv里的mAP_0.5:0.95這個值對煙頭這種小目標非常苛刻通常只有0.3~0.5比mAP_0.5能低一半還多。只要mAP_0.5能到0.85以上對于報警場景就算合格不要追求mAP_0.5:0.95的數值。4. 部署推理與連續幀判定把“檢測到煙”變成“判定正在吸煙”4.1 導出成ONNX再上TensorRT別直接跑python推理訓練得到的best.pt直接用torch推理在GPU上效果尚可但邊緣設備上性能很差。常見做法是先導出ONNX再轉TensorRT引擎或者直接用自帶的export.py轉torchscriptpython export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --img 1280 \ --batch 1 \ --opset 12 \ --simplify導出參數中--simplify會調用onnx-simplifier做計算圖優化把常量折疊、冗余節點刪掉對推理性能有直接幫助。--opset 12是兼容性和算子的平衡點太高的opset在舊版onnxruntime上跑不了。導出完成后可以用onnxruntime自帶的session.run做一次前向驗證確認輸出shape和檢測數量是否符合預期。如果你要上TensorRT建議直接用trtexec離線轉engine/usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --saveEnginebest.engine \ --fp16 \ --minShapesimages:1x3x1280x1280 \ --optShapesimages:1x3x1280x1280 \ --maxShapesimages:4x3x1280x1280這套配置里--fp16是性能翻倍的關鍵但前提是煙頭檢測對精度損失不敏感——實際測試中fp16在吸煙檢測上的掉點通常在0.5%以內可以放心開。--minShapes和--maxShapes要按實際推理batch來設動態shape的引擎啟動會變慢如果固定單路視頻流推理直接固定shape能省去動態shape的額外開銷。4.2 單幀檢測不夠滑窗判定才是吸煙行為的本質吸煙是一個持續數秒鐘的動作單幀檢測的抖動和誤報在視頻流里會被放大。部署推理時維護一個關鍵點緩存隊列對每個煙頭目標記錄它的檢測框中心點和置信度當連續三幀中出現位移小于閾值的目標就判定為同一目標并累加計數。這個邏輯用python偽碼表示class SmokingDetector: def __init__(self, frame_buffer5, score_thres0.5): self.frame_buffer frame_buffer self.score_thres score_thres self.history [] # 每幀的檢測結果 self.confirmed {} def update(self, frame_id, detections): # detections: list of (cx, cy, score) self.history.append((frame_id, detections)) self.history [h for h in self.history if frame_id - h[0] self.frame_buffer] # 跟蹤同一目標的簡易辦法按位置距離聚類 current_hits {} for tid, (fid, dets) in enumerate(self.history): for cx, cy, score in dets: if score self.score_thres: continue # 用前一幀最近距離匹配這里簡化為網格哈希 key (round(cx / 50), round(cy / 50)) current_hits[key] current_hits.get(key, 0) 1 return current_hits這段代碼的思路是用網格哈希對連續幀中的煙頭位置做粗匹配然后在網格上統計命中次數。超過3次命中即可確認吸煙行為并觸發報警。好處是不需要引入ByteTrack之類的跟蹤器在算力受限的板卡上能跑得動缺點是對目標移動速度敏感煙頭移動過快時網格哈希會斷裂此時可以把網格調大到80像素或者改用歐氏距離最近鄰匹配。4.3 CPU推理條件下的替代方案并非所有場景都有GPU。如果只能跑CPU上的onnxruntime要把輸入分辨率從1280降回640同時把模型換成yolov5n。此時精度損失比較明顯但配合上面的滑窗判定依然能保證一定的實用性。用onnxruntime跑CPU推理的命令很簡單但有一個容易被忽略的配置項import onnxruntime as ort sess ort.InferenceSession( best.onnx, providers[CPUExecutionProvider], sess_optionsort.SessionOptions() ) # 關鍵參數線程數設成物理核心數而不是邏輯核心數 sess.set_session_configs(intra_op_num_threads4)intra_op_num_threads設置得太高反而會因為線程切換開銷導致延遲上升4~6個物理核是常見甜點值。另一個優化是開啟ORT的圖優化級別為ORT_ENABLE_ALL可以在加載時自動融合部分算子。這兩項疊加通常能把CPU推理速度提升20%~40%。5. 現場誤報調優三板斧降閾值、做RoI、查數據分布5.1 用小目標專項數據把置信度閾值拉回0.3訓練完的模型默認置信度閾值是0.25這在一般目標檢測里沒問題但在吸煙檢測場景會造成大量誤報。打火機的火苗、白墻反光、煙灰缸里的煙頭殘留都會被框出來。正確做法是把置信度閾值提高到0.5~0.6然后用針對性數據補充訓練把真正煙頭的置信度頂上去。如果補充數據不方便就在推理腳本里對寬度小于32像素的檢測框額外加一個懲罰因子def adjust_score(box, raw_score): w, h box[2] - box[0], box[3] - box[1] area_ratio (w * h) / (1280 * 1280) if area_ratio 0.001: return raw_score * 0.7 # 小框降權 return raw_score這個懲罰因子的邏輯是煙頭雖然是極小目標但訓練充分的模型對它的置信度普遍在0.6以上。如果一個框面積占比不到千分之一且置信度在0.5以下大概率是背景噪聲降權后過不了閾值能有效清理一部分零散誤報。5.2 置信度閾值與NMS的聯動yolov5在導出模型后通常會把NMS放在后端實現。推理腳本里的NMS參數對結果影響很大。默認的IoU閾值是0.45吸煙檢測場景建議調到0.5~0.6因為煙頭目標小兩個煙頭距離很近時容易被NMS合并掉一個。而score_thres建議放在0.3而不是0.25配合上面的面積降權整體誤報能下降一大截。另外一個隱蔽細節yolov5的detect.py默認每幀獨立做NMS如果你在滑窗判定里已經做了多幀去重單幀NMS的IoU閾值就可以放得更寬保留更多候選框給后續時序判定。5.3 只在RoI區域做識別把誤報半徑縮小攝像頭畫面里不是所有區域都需要檢測吸煙行為。把畫面三分天下左上區域通常是過道中間區域是工位右下角是窗戶——窗戶上的光斑和外面移動的車輛是天然干擾源。部署時畫一個多邊形RoI檢測框的中心點不在RoI內就直接丟棄。這個處理能直接砍掉40%以上的室外場景誤報。RoI的配置不要寫死在代碼里用一個json文件存多邊形頂點坐標現場調試時用鼠標在畫面上點幾個點就能完成配置。這是整個系統里性價比最高的一個功能但很多從網上下載的zip源碼里沒有實現。自己補上后尤其適合工廠車間、工地出入口這類固定機位場景。輸出幾行負載日志能很直觀地看到指標提升RoI啟用前單路視頻每10分鐘平均誤報6.2次啟用后變成1.5次再配合滑窗三次命中判定最終基本能做到兩到三天零誤報。先看誤報日志再調參比盲目調置信度閾值靠譜得多。5.4 批量驗證視頻回放而不是看幾張測試圖訓練時隨手抽幾張測試集圖片看效果覺得不錯就上線后面大概率會返工。更實際的做法是準備一段30分鐘的現場視頻跑完推理后把每一幀的檢測結果疊加的框畫到一個新視頻里人眼快速瀏覽同時導出每條報警記錄對應的時間戳和置信度。回放時重點關注三個時間段中午陽光直射、傍晚光線變暖、晚上燈光開啟這三個時間段的顏色分布差異是吸煙檢測誤報的三大來源。比如暖色燈光下人的膚色會偏橙與煙頭顏色接近——這也是前面hsv增強不讓色相偏太多的原因。用視頻回放的方式做驗收一次就能看清模型的色彩魯棒性到底行不行。本文還有配套的精品資源點擊獲取