戰(zhàn):從數(shù)據(jù)準(zhǔn)備到部署的完整指南)
簡介下載包提供基于 YOLO11n 訓(xùn)練的安全帽檢測模型面向施工現(xiàn)場、廠區(qū)安防等場景用于自動識別人員是否佩戴安全帽。模型覆蓋 .pt、.onnx、.rknn 等主流格式既適合在 PyTorch 環(huán)境中繼續(xù)訓(xùn)練與調(diào)試也可通過 ONNX 接入通用推理框架或部署到 RK3588/RK3576 等 Rockchip NPU 設(shè)備滿足實(shí)時(shí)監(jiān)測需求。資源共 9 個(gè)文件壓縮包約 49.47MB另有 .bin、.xml、.yaml、.tar 等配置與網(wǎng)絡(luò)結(jié)構(gòu)文件便于理解模型結(jié)構(gòu)和二次開發(fā)。已有 361 人學(xué)習(xí)下載。使用者可根據(jù)目標(biāo)硬件選擇對應(yīng)格式快速部署用于搭建安全帽佩戴檢測系統(tǒng)或作為 YOLO11 工業(yè)落地的參考案例有助于節(jié)省模型訓(xùn)練與格式轉(zhuǎn)換時(shí)間。 工地安全帽檢測這東西看著簡單真正做成一個(gè)能用的 YOLO11n 模型里面坑是真不少。我最初以為拿開源數(shù)據(jù)集一訓(xùn)就完事結(jié)果放到現(xiàn)場監(jiān)控畫面里漏檢、誤檢全來了反光、遠(yuǎn)景小人、遮擋每一類都能讓你懷疑人生。這篇就把我用 YOLO11n 從頭訓(xùn)安全帽模型的完整過程寫下來包括數(shù)據(jù)準(zhǔn)備、標(biāo)注格式、訓(xùn)練參數(shù)、踩坑記錄給準(zhǔn)備上手或者正在調(diào)模型的朋友一個(gè)可以直接照著做的參考。1. 為什么選 YOLO11n 做安全帽檢測1.1 安全帽檢測的實(shí)際需求與難點(diǎn)安全帽檢測是工地、工廠、園區(qū)等場景下的標(biāo)準(zhǔn)視覺任務(wù)核心是自動判斷人員是否佩戴安全帽。它的難點(diǎn)不在“認(rèn)識安全帽”而在真實(shí)場景的復(fù)雜度鏡頭角度俯視、逆光、帽子和頭發(fā)顏色接近、人員密集遮擋、遠(yuǎn)處目標(biāo)小到只有十幾個(gè)像素。所以模型不能只會在公開數(shù)據(jù)集上表現(xiàn)好還得能扛住現(xiàn)場部署環(huán)境的折騰。這類任務(wù)通常是邊端部署用 Jetson、工控機(jī)甚至樹莓派這種算力有限的設(shè)備所以模型體積和推理速度必須兼顧。YOLO11n 是 ultralytics 系列里最輕量的版本參數(shù)量只有大概 2.6MFP16 精度下模型文件才 5MB 出頭用 GPU 推理單幀能跑到毫秒級用 CPU 也能做到實(shí)時(shí)邊緣部署。對安全帽檢測這種需要多路攝像頭并發(fā)分析的項(xiàng)目選 n 版本就是奔著“穩(wěn)、快、省顯存”去的。1.2 YOLO11n 相比 YOLOv5/v8 的選型邏輯很多人問YOLOv5 和 YOLOv8 已經(jīng)很成熟了為什么還要用 YOLO11n我個(gè)人的理解是YOLO11 把 C3/C2f 模塊換成了 C3k2訓(xùn)練收斂更穩(wěn)對小目標(biāo)的特征提取也有優(yōu)化。雖然精度提升幅度不像版本號看起來那么大但在安全帽這種中小型目標(biāo)為主的場景里mAP 能漲 1-3 個(gè)點(diǎn)就很值得了。更關(guān)鍵的是 YOLO11n 的模型結(jié)構(gòu)更輕同樣的 batch size 下訓(xùn)練顯存占用比 YOLOv8n 低。我是用單張 RTX 3090 訓(xùn)的24G 顯存能開到 batch 64訓(xùn)練速度明顯比 v8 流暢。對于個(gè)人開發(fā)者和小團(tuán)隊(duì)來說這決定了你能否快速迭代數(shù)據(jù)。YOLO11 還有原生支持旋轉(zhuǎn)檢測的 OBB 版本但目前安全帽檢測用普通水平框就夠了不需要引入額外復(fù)雜度。2. 數(shù)據(jù)集準(zhǔn)備與標(biāo)注規(guī)范2.1 數(shù)據(jù)采集別只依賴公開數(shù)據(jù)集安全帽檢測的開源數(shù)據(jù)集不少比如 SHWD、Global Wheat 等但直接用它們訓(xùn)練出來的模型換到自己的現(xiàn)場很容易廢掉。這些數(shù)據(jù)集的相機(jī)視角、光線、安全帽樣式和你實(shí)際布控的場景差別很大。我的做法是“公開數(shù)據(jù)打底 現(xiàn)場數(shù)據(jù)增強(qiáng)”先用 SHWD 這類數(shù)據(jù)集做預(yù)訓(xùn)練然后用攝像頭真實(shí)抓拍畫面補(bǔ)充訓(xùn)練。現(xiàn)場采集時(shí)注意三點(diǎn)一是多時(shí)段采集早上逆光、中午強(qiáng)光、晚上補(bǔ)光都必須有二是多角度采集球機(jī)變倍后的近景和遠(yuǎn)處全景都要收集三是把沒戴帽子、戴帽子但帽子顏色和背景接近、帽子拿在手上這些特殊樣本單獨(dú)整理。大概采了 8000 多張現(xiàn)場圖片加上公開數(shù)據(jù)集最終篩出有效圖片約 1.2 萬張。數(shù)據(jù)安全方面也要留意工地現(xiàn)場畫面可能涉及人員隱私和安全管理規(guī)定訓(xùn)練數(shù)據(jù)不要隨意上傳公開平臺自己本地標(biāo)注、本地訓(xùn)練最穩(wěn)妥。2.2 標(biāo)注方案單類還是多類安全帽檢測的標(biāo)注方案有兩種主流選擇。一種是直接用 helmet 單類把所有戴帽子的頭標(biāo)出來。另一種是檢測 person、head、helmet 三個(gè)類別再通過后處理邏輯判斷 head 和 helmet 的匹配關(guān)系。前者簡單但很難處理“身邊有人戴帽子但自己沒戴”這種場景后者邏輯更靈活可以統(tǒng)計(jì)戴帽率和違規(guī)行為但標(biāo)注工作量翻倍。我采用的是 head helmet 雙類別方案。標(biāo)注時(shí)只標(biāo)頭部區(qū)域和頭盔區(qū)域不標(biāo)整個(gè)人體。這樣模型專注在最關(guān)鍵的部位避免 person 類別遮擋嚴(yán)重時(shí)監(jiān)督信號被干擾。判定是否佩戴時(shí)計(jì)算 head bbox 和 helmet bbox 的 IoUIoU 大于 0.3 就認(rèn)為已佩戴。這個(gè)方法在多人密集場景下比單類檢測穩(wěn)定得多。2.3 YOLO 標(biāo)注格式轉(zhuǎn)換與目錄劃分YOLO 系列使用 txt 格式標(biāo)注一行代表一個(gè)目標(biāo)內(nèi)容為類別id x_center y_center width height坐標(biāo)是歸一化到 0-1 的數(shù)值。比如圖片寬 1920目標(biāo)框中心 x 坐標(biāo) 960那么x_center0.5。用 LabelImg 或 X-AnyLabeling 標(biāo)注時(shí)導(dǎo)出成 YOLO 格式即可。如果你拿到的是 COCO 格式或 VOC 格式的數(shù)據(jù)集需要寫個(gè)轉(zhuǎn)換腳本。import os def convert_voc_to_yolo(xml_path, out_dir, classes): # 這里只展示核心邏輯 import xml.etree.ElementTree as ET tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) with open(out_dir, w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h f.write(f{classes.index(cls)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)目錄結(jié)構(gòu)按 ultralytics 的習(xí)慣來圖片和標(biāo)注文件放同一級目錄圖片名和 txt 名一致。數(shù)據(jù)集根目錄下面建images/train、images/val、labels/train、labels/val四個(gè)文件夾然后寫一個(gè) data.yamlpath: D:/projects/helmet_detection/dataset train: images/train val: images/val names: 0: head 1: helmet劃分比例我用的 9:1因?yàn)榘踩眻鼍爸貜?fù)背景太多驗(yàn)證集不需要太大但要保證每個(gè)工地、每個(gè)光線條件下的畫面都有覆蓋。3. 訓(xùn)練環(huán)境與超參數(shù)配置3.1 本地訓(xùn)練環(huán)境搭建YOLO11n 訓(xùn)練依賴 ultralytics 庫、PyTorch 和 CUDA。安裝很簡單但有幾個(gè)版本坑需要注意。Python 我用的是 3.10PyTorch 2.x 配 CUDA 11.8ultralytics 用 8.3.x 以上版本因?yàn)?YOLO11 是 8.3.0 之后才正式支持的。如果你還在用 8.0.x直接訓(xùn)練會出現(xiàn) model 不支持的報(bào)錯(cuò)。conda create -n yolo11 python3.10 conda activate yolo11 pip install ultralytics8.3.7 torch2.1.2 torchvision0.16.2 --index-url https://download.pytorch.org/whl/cu118裝完后先跑一次官方示例確認(rèn)環(huán)境沒問題不要一上來就用自己的數(shù)據(jù)。yolo predict modelyolo11n.pt sourcehttps://ultralytics.com/images/bus.jpg這行命令能出結(jié)果說明環(huán)境正常。建議用 CPU 推理測試一次再用 GPU 測試一次排查驅(qū)動層面的問題。訓(xùn)練時(shí)我開了 CUDA、CUDNN確保用的是 GPU 而不是 CPU否則訓(xùn)練速度慢到懷疑人生。3.2 核心超參數(shù)的選擇思路安全帽檢測訓(xùn)練中最影響結(jié)果的是imgsz、batch、epochs、optimizer和lr0。我實(shí)驗(yàn)下來比較穩(wěn)的配置如下參數(shù)推薦值說明imgsz640安全帽目標(biāo)不算極小640 平衡精度和速度batch6424G顯存顯存不夠用 16-32別硬上epochs150數(shù)據(jù)量 1 萬張左右150 輪基本收斂optimizerAdamW比 SGD 收斂快適合快速迭代lr00.0005~0.001預(yù)訓(xùn)練模型微調(diào)用 0.001 以內(nèi)合理warmup_epochs3.0穩(wěn)定初期訓(xùn)練weight_decay0.0005防止過擬合imgsz 不建議一開始就設(shè)成 1280訓(xùn)練顯存會翻 4 倍而且安全帽檢測用 640 已經(jīng)足夠。真要到小目標(biāo)場景先把 mosaic 增強(qiáng)開大再考慮提升分辨率成本更低。有個(gè)容易被忽略的參數(shù)是close_mosaic。ultralytics 會在最后 10 輪自動關(guān)閉 mosaic 增強(qiáng)防止模型過度依賴拼接圖的上下文。訓(xùn)練輪數(shù)少于 50 時(shí)建議手動把close_mosaic調(diào)到 5避免增強(qiáng)策略影響真實(shí)場景表現(xiàn)。3.3 訓(xùn)練命令與參數(shù)文件我不用命令行堆參數(shù)而是寫一個(gè) training.yaml 繼承模型的默認(rèn)配置。這樣可以隨時(shí)回滾參數(shù)也方便多人協(xié)作。# train_config.yaml task: detect mode: train model: yolo11n.pt data: D:/projects/helmet_detection/dataset/data.yaml epochs: 150 time: null patience: 50 batch: 64 imgsz: 640 save: true device: 0 workers: 8 optimizer: AdamW lr0: 0.0007 lrf: 0.01 warmup_epochs: 3.0 close_mosaic: 10執(zhí)行訓(xùn)練yolo train cfgtrain_config.yaml訓(xùn)練開始后終端會先打印模型參數(shù)量、FLOPs 和每層結(jié)構(gòu)。這時(shí)候注意看Parameters大約是 2.6M如果是幾千萬參數(shù)說明你加載成了 s/m/l 版本檢查一下模型權(quán)重路徑。4. 訓(xùn)練過程監(jiān)控與常見問題排查4.1 怎么判斷模型有沒有在好好學(xué)訓(xùn)練過程中每隔一段時(shí)間輸出一組指標(biāo)重點(diǎn)看train/box_loss、train/cls_loss、val/box_loss和metrics/mAP50(B)。安全帽檢測任務(wù)里mAP50 比 mAP50-95 更直觀因?yàn)榘踩笔菃晤惸繕?biāo)框交并比稍差一點(diǎn)也被算對mAP50 一般要做到 0.95 以上才算合格。Loss 曲線整體趨勢是下降。如果 train loss 一直在降而 val loss 不降甚至上升就是過擬合說明模型在背訓(xùn)練集泛化能力差。我在第二輪迭代時(shí)把 epochs 從 200 降到 150再加了 weight_decayval loss 馬上就平穩(wěn)了。4.2 顯存不足OOM與訓(xùn)練中斷顯存不足是最常見的報(bào)錯(cuò)尤其是 batch 開得太大、圖片分辨率高、workers 設(shè)置過多時(shí)。遇到CUDA out of memory先別急著換顯卡按順序排查batch 降到 32 或 16imgsz從 640 降到 512看效果跌幅大不大workers 改成 4有時(shí)候數(shù)據(jù)加載線程太多會占額外顯存用device0指定單卡避免多卡同步浪費(fèi)顯存。如果是 Windows 下報(bào)Dataloader worker (pid(s)) exited unexpectedly多數(shù)是路徑問題或者 labels 文件和圖片不對應(yīng)。檢查 data.yaml 路徑是否絕對路徑以及 labels 目錄里 txt 命名是否和圖片完全一致包括后綴比如圖片是.jpg標(biāo)簽就是.txt不能用.jpeg混著來。4.3 精度上不去的常見原因訓(xùn)練完了 mAP50 只有 0.7不要急著調(diào)模型先檢查幾個(gè)地方標(biāo)注有沒有錯(cuò)位尤其是遮擋場景下 head 和 helmet 框是否貼合我抽查過一個(gè)樣本標(biāo)注框比實(shí)際頭大 30%導(dǎo)致模型學(xué)偏類別是否均衡。我的數(shù)據(jù)集里 head 和 helmet 目標(biāo)數(shù)量差不多是 1:0.9還算健康如果有一類只有幾百個(gè)就要用instance balance或者多復(fù)制一些這類樣本背景物體是不是干擾太大。比如安全帽顏色和墻面接近模型就會漏檢。這時(shí)不要盲目加數(shù)據(jù)而是針對性收集“帽子與背景同色”的負(fù)樣本增強(qiáng)模型對邊緣特征的敏感度。如果是小目標(biāo)漏檢可以嘗試在訓(xùn)練時(shí)開啟multi-scale讓模型適應(yīng)不同分辨率下的大小變化。我就在訓(xùn)練參數(shù)里加了scale0.5和fliplr0.5對遠(yuǎn)距離小目標(biāo)改善明顯。5. 模型評估、導(dǎo)出與部署經(jīng)驗(yàn)5.1 用測試集做最終評估訓(xùn)練結(jié)束后用predict跑一批訓(xùn)練時(shí)沒見過的現(xiàn)場測試圖按工地、室內(nèi)、夜間三種場景分別統(tǒng)計(jì)準(zhǔn)確率和召回率。不要只看總 mAP因?yàn)椴煌瑪z像頭覆蓋區(qū)域差別很大統(tǒng)一 mAP 會掩蓋特定場景下崩掉的問題。我把測試圖分成三個(gè)子集500 米外遠(yuǎn)景、近景、夜間補(bǔ)光。結(jié)果發(fā)現(xiàn)模型在近景下 mAP50 達(dá)到 0.97而遠(yuǎn)景只有 0.82。原因是遠(yuǎn)距離的頭部目標(biāo)太小部分只有 10x10 像素。后來我給遠(yuǎn)焦鏡頭單獨(dú)補(bǔ)了 2000 張訓(xùn)練圖mAP 從 0.82 提升到 0.89。這種分場景評估方式比看一個(gè)總分?jǐn)?shù)實(shí)用得多。5.2 導(dǎo)出 ONNX 與 TensorRT 加速訓(xùn)練好的best.pt不能直接部署到生產(chǎn)環(huán)境通常要導(dǎo)出成 ONNX 或 TensorRT engine。導(dǎo)出命令yolo export modelbest.pt formatonnx opset12 simplifyTrue導(dǎo)出后用 onnxruntime 驗(yàn)證一次import onnxruntime as ort import numpy as np from PIL import Image session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img Image.open(test.jpg).resize((640, 640)) img_array np.array(img).astype(np.float32) / 255.0 img_array np.transpose(img_array, (2, 0, 1))[None, ...] outputs session.run(None, {input_name: img_array}) print(outputs[0].shape)ONNX 對多路視頻流部署來說速度足夠。如果想進(jìn)一步榨干 GPU 性能再用 TensorRT 導(dǎo)出 FP16 精度模型。一次實(shí)測中ONNX 在 RTX 3090 上單幀推理約 3msTensorRT FP16 約 1.8ms差距明顯。如果你的攝像頭推流是 25 幀每秒ONNX 也能輕松扛住TensorRT 更適合要接十幾路視頻的服務(wù)器。5.3 部署時(shí)最容易被忽略的細(xì)節(jié)部署階段要小心預(yù)處理環(huán)節(jié)。訓(xùn)練時(shí) ultralytics 默認(rèn)用letterbox把圖片縮放成 640x640 并填充灰色邊推理時(shí)如果直接用 cv2 的 resize 把圖片硬拉到 640x640人物會被拉伸變形檢測框位置也會偏移。我建議部署代碼里復(fù)現(xiàn)相同的 letterbox 操作否則模型精度會打折扣。另一個(gè)細(xì)節(jié)是置信度閾值。訓(xùn)練時(shí)默認(rèn) conf0.25但現(xiàn)場場景誤報(bào)多時(shí)可以把 conf 調(diào)到 0.35 甚至 0.4用少量的召回率換更少的誤報(bào)。安全帽檢測這種安全場景寧可偶爾漏報(bào)也不能頻繁誤報(bào)否則現(xiàn)場人員會對報(bào)警產(chǎn)生麻木。最后提醒一句模型要持續(xù)迭代。我上線后每周收集一次誤報(bào)和漏報(bào)數(shù)據(jù)用難例重新標(biāo)注并做增量訓(xùn)練。第一次增量訓(xùn)練只加了 500 張圖就把夜間反光導(dǎo)致的誤報(bào)降了一半。模型不是訓(xùn)練完就結(jié)束而是跟著現(xiàn)場數(shù)據(jù)一起成長。這里分享一個(gè)我踩過最深的坑訓(xùn)練中期我發(fā)現(xiàn) loss 降得很慢排查了很久才發(fā)現(xiàn)是數(shù)據(jù)集里有幾百張帶水印的網(wǎng)圖水印區(qū)域被標(biāo)注成了頭盔導(dǎo)致分類混亂。后來加了數(shù)據(jù)清洗腳本把所有非現(xiàn)場拍攝的圖片全部剔除訓(xùn)練立刻順暢了。別小看數(shù)據(jù)集質(zhì)量YOLO11n 模型能力再強(qiáng)喂進(jìn)去垃圾標(biāo)注出來的照樣是垃圾預(yù)測。本文還有配套的精品資源點(diǎn)擊獲取