:8300張YOLO數(shù)據(jù)集從訓(xùn)練到TensorRT部署)
做智慧交通項目這些年頭盔檢測是我被問得最多的場景之一。原因很簡單道路監(jiān)控里識別摩托車、電動車騎乘人員有沒有戴頭盔既是交警非現(xiàn)場執(zhí)法的輔助手段也是工地、園區(qū)、校園周邊安全管理的剛需。但真正動手做的人都知道第一關(guān)往往不是模型選型而是數(shù)據(jù)集——你手上沒有一套標注規(guī)范、場景齊全、拿來就能訓(xùn)練的圖后面一切算法都是空中樓閣。這篇博文要聊的就是一套8300張YOLO格式的頭盔檢測數(shù)據(jù)集在智慧交通場景下的完整使用思路。我按自己實際做項目的流程來寫先講清楚這套數(shù)據(jù)到底能解決什么問題、8300張是什么概念再拆解YOLO的標注格式和類別設(shè)計然后是模型選型邏輯、訓(xùn)練實操、踩坑排查最后落到TensorRT部署和多路監(jiān)控的工程化。無論你是剛接觸目標檢測的學(xué)生還是已經(jīng)在做智慧交通落地的工程師照著這條線走一遍應(yīng)該能把一個頭盔檢測任務(wù)從零跑到上線。1. 盔檢測數(shù)據(jù)集的真實定位不只是有沒有戴帽子這么簡單1.1 任務(wù)邊界檢測、分類與跟蹤的層次關(guān)系頭盔檢測表面上是一個目標檢測任務(wù)但落到實際業(yè)務(wù)里它其實是人車關(guān)系判斷的一環(huán)。監(jiān)控畫面里有騎手、有車輛、有行人你要做的不是把畫面里所有像頭盔的圓形物體框出來而是先鎖定騎乘人員rider再判斷他頭部區(qū)域有沒有頭盔。這個順序很重要——如果先檢測頭盔再關(guān)聯(lián)人員很容易被路邊安全帽、車筐里的頭盔、甚至圓形井蓋干擾。所以很多高質(zhì)量的頭盔檢測數(shù)據(jù)集標注類別并不是簡單的一類helmet而是包含 rider、helmet、head未戴頭盔的頭部這樣的多類別組合。模型同時學(xué)會這個人和這個人頭上有沒有東西下游做業(yè)務(wù)判斷時直接用坐標關(guān)聯(lián)就能得出結(jié)論一個rider框內(nèi)如果helmet框覆蓋了頭部區(qū)域判定為佩戴否則判定為未佩戴。這種設(shè)計的魯棒性遠高于單純做二分類。1.2 8300張的真實體感數(shù)據(jù)量夠不夠用先說8300張這個規(guī)模。視頻抽幀做數(shù)據(jù)集的人都有概念一段10分鐘的25fps監(jiān)控視頻原始幀數(shù)是15000幀。但連續(xù)幀高度相似去重、清洗、挑不同角度和時段之后能留下幾百張有效圖就不錯了。所以8300張如果來源是路口監(jiān)控、卡口抓拍、工地門口等多路視頻清洗篩選的工作量其實相當可觀按人工標注一小時50到80張的速度算光標注就是100多個工時。這個量級對訓(xùn)練一個YOLO檢測模型來說屬于夠用但不算富余。按常見的8:1:1劃分訓(xùn)練集約6600張驗證集約830張測試集約830張。用預(yù)訓(xùn)練權(quán)重遷移學(xué)習(xí)在單張RTX 3090上訓(xùn)練100到150個epoch兩三個小時就能看到收斂趨勢。如果你的目標不是刷學(xué)術(shù)榜單而是做一個能上線的交通檢測模型這個數(shù)據(jù)規(guī)模配YOLOv8s或YOLO11n完全夠用。當然夠用是建立在數(shù)據(jù)質(zhì)量過關(guān)的前提下。8300張如果大量來自同一場景、同一時間段、同一角度那它實際的信息量會大打折扣。拿到數(shù)據(jù)集的第一件事不是急著開訓(xùn)而是先做一通分布摸底——這個放到第二章細說。2. 8300張圖怎么拆格式規(guī)范、類別設(shè)計、場景分布評估2.1 YOLO標注格式的底層邏輯YOLO格式的標注不復(fù)雜但容易在細節(jié)上翻車。每張jpg圖片對應(yīng)一個同名的txt文件每一行代表一個目標框格式是class_id x_center y_center width height其中x_center、y_center、width、height全部除以圖片寬高做了歸一化取值在0到1之間。舉個例子一張1920x1080的圖上一個人頭框左上角在(500, 300)寬200、高250那么歸一化后的值就是x_center (500 200 / 2) / 1920 0.3125 y_center (300 250 / 2) / 1080 0.3935 width 200 / 1920 0.1042 height 250 / 1080 0.2315我做數(shù)據(jù)檢查時一定會寫個小腳本把所有txt掃描一遍檢查有沒有x_center width / 2 1或者坐標出現(xiàn)負值的情況。這類越界標注在人工標注時非常常見尤其當目標被畫面邊緣裁切時標注員容易把框直接拉到畫面外。YOLO訓(xùn)練時這些異??驎箦e或者被內(nèi)部算子靜默裁掉浪費數(shù)據(jù)還干擾訓(xùn)練。2.2 類別劃分三類模型比二類更實用頭盔檢測數(shù)據(jù)集通常有兩種類別設(shè)計思路。一種只標兩類——with_helmet和without_helmet框住整個人由類別區(qū)分有沒有戴。這種標法快但問題很明顯判斷依據(jù)是整人的語義而不是頭部區(qū)域當畫面里同時出現(xiàn)戴頭盔和沒戴頭盔的兩個人挨得很近時模型容易學(xué)成按人周圍環(huán)境猜泛化能力差。另一種是標三類或四類比如rider騎手、helmet頭盔、head未佩戴頭盔的頭部。模型輸出的邏輯更接近人眼判斷先看到人再看頭。這種數(shù)據(jù)集做出來的模型在業(yè)務(wù)層可以拿到人頭-頭盔配對關(guān)系方便接后續(xù)的跟蹤與取證邏輯。代價是標注成本高、類別間容易混淆——遠處的人頭只有二三十像素標rider還是head標注員自己都可能猶豫。我比較推薦的是以三類為底線rider、helmet、head。rider幫助模型把注意力集中在騎乘人員身上helmet和head再對頭部區(qū)域細分。這樣既保證了檢測穩(wěn)定性又為后續(xù)邏輯留出空間。2.3 拿到數(shù)據(jù)集先做場景摸底判斷一套數(shù)據(jù)集值不值得信任我一般看三張圖場景分布圖、目標尺寸分布圖、類別數(shù)量柱狀圖。場景分布上智慧交通頭盔檢測至少要覆蓋城市十字路口、非機動車道、小區(qū)門口、工地/園區(qū)門口、地下停車場。如果全部來自同一品牌同一角度的槍機模型換一個場景就崩。目標尺寸分布上頭盔檢測是典型的小目標問題——1080p畫面里遠處的騎手頭部可能只有20x30像素而YOLO默認的特征圖下采樣倍率是8、16、32太小目標經(jīng)過多次下采樣后特征基本丟光。類別數(shù)量上要看正負樣本比例如果未戴頭盔的樣本只有佩戴頭盔的十分之一訓(xùn)練時類別傾向會非常明顯。這套8300張的數(shù)據(jù)集我建議你拿到后先用腳本統(tǒng)計一遍這些維度。如果發(fā)現(xiàn)夜間樣本偏少就自己補采一些夜間幀如果小目標占比低后面的增強策略就要往放大、切圖方向傾斜。3. 模型選型為什么智慧交通場景繞不開YOLO3.1 YOLO版本怎么選從v5到v8到v11頭盔檢測任務(wù)選擇YOLO不是因為它最前沿而是因為它在精度、速度、部署難度和社區(qū)生態(tài)之間做到了最好的平衡。以當前主流版本為例YOLOv8是應(yīng)用最廣泛的選擇anchor-free設(shè)計讓回歸頭更簡單C2f模塊在特征復(fù)用上比v5的C3更好解耦頭讓分類和回歸任務(wù)不再互相干擾YOLO11也叫v11的Backbone和Neck做了進一步優(yōu)化同等參數(shù)下mAP略高。YOLOv5雖然老了但生態(tài)成熟、文檔多仍有大量存量項目在用。我個人的選型習(xí)慣是邊緣設(shè)備Jetson、RK3588、海思等用n或s尺寸服務(wù)器單卡多路推流用s或m離線批量分析、對精度要求苛刻的場景用l或x以YOLOv8系列為例幾個版本的差距可以用一張表看明白模型參數(shù)量輸入尺寸mAP0.5 (COCO參考)單張1080p推理速度T4, FP16YOLOv8n3.2M640約37.3約2msYOLOv8s11.2M640約44.9約4msYOLOv8m25.9M640約50.2約7msYOLOv8l43.7M640約53.0約12ms對頭盔檢測這種小目標居多的任務(wù)我建議從s起步在RTX 3090上把完整流程跑通再根據(jù)部署硬件裁剪到n或升到m。一上來就用x訓(xùn)練慢、部署難實際收益卻不一定大。3.2 為什么不選Faster R-CNN或DETR不是說兩階段檢測器和Transformer檢測器沒有優(yōu)勢——Faster R-CNN在小目標上的歷史表現(xiàn)確實不差但其推理速度在幾十路視頻面前毫無競爭力DETR類模型在同等數(shù)據(jù)量下訓(xùn)練不穩(wěn)定收斂需要的epoch更多而且部署鏈路上TensorRT的適配成熟度參差不齊。智慧交通項目的核心約束是幾十路視頻同時跑每路25幀這種情況下每幀5ms和每幀50ms的差距是決定性的。當然如果數(shù)據(jù)集只有一兩百張圖YOLO也很難發(fā)揮那種極端情況更適合用成熟的第三方模型直接做零樣本推理而不是自己從頭訓(xùn)。3.3 預(yù)訓(xùn)練權(quán)重遷移別從零開始頭盔是自然圖像里的常見物體但在ImageNet或COCO上預(yù)訓(xùn)練過的模型并不會天生理解頭盔這個概念。你做的遷移學(xué)習(xí)是利用它在海量自然圖上學(xué)會的紋理、邊緣、形狀等基礎(chǔ)特征然后在自己的數(shù)據(jù)集上微調(diào)。實操時下載YOLOv8官方提供的yolov8s.pt作為初始權(quán)重設(shè)置pretrainedTrue訓(xùn)練時會自動加載。這里有個細節(jié)如果你的類別數(shù)量和數(shù)據(jù)集的類別數(shù)量不一致訓(xùn)練框架會自動丟棄原模型最后一層的分類頭隨機初始化新的分類頭所以你只需要關(guān)心backbone和neck部分的遷移效果。4. 說出來你可能不信訓(xùn)練流程里最花時間的其實是數(shù)據(jù)準備4.1 目錄結(jié)構(gòu)與data.yaml拿到數(shù)據(jù)集后我習(xí)慣先規(guī)整目錄。YOLO訓(xùn)練的標準目錄結(jié)構(gòu)是這樣的helmet_dataset/ ├── train/ │ ├── images/ │ │ ├── 0001.jpg │ │ └── ... │ └── labels/ │ ├── 0001.txt │ └── ... ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/然后寫一個data.yamlpath: /path/to/helmet_dataset train: train/images val: val/images test: test/images nc: 3 names: [rider, helmet, head]如果你的數(shù)據(jù)集里所有圖都還堆在一起需要先寫腳本劃分目錄。我習(xí)慣按9:0.5:0.5劃分訓(xùn)練驗證測試再用隨機種子固定結(jié)果保證每次復(fù)現(xiàn)一致。劃分時有一個經(jīng)驗先用一個腳本統(tǒng)計每一幀圖片的拍攝時間戳或文件名hash保證同一段連續(xù)視頻的幀不要同時落在訓(xùn)練集和測試集里否則測試指標會被嚴重高估——模型記憶的是場景不是目標本身。4.2 訓(xùn)練參數(shù)這些值怎么定訓(xùn)練頭盔檢測模型我常用的參數(shù)基線如下yolo detect train \ datadata.yaml \ modelyolov8s.pt \ pretrainedTrue \ epochs150 \ imgsz640 \ batch16 \ device0 \ optimizerAdamW \ lr00.001 \ mosaic1.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4每個參數(shù)都不是隨便拍的。imgsz640是YOLO的黃金輸入尺寸精度和速度的平衡點如果小目標多我會先試imgsz768通過提升輸入分辨率讓頭盔占更多像素通常mAP能漲2到3個點。batch16在16G顯存的卡上比較穩(wěn)如果你顯存只有8G就降到8或4。mosaic1.0是數(shù)據(jù)增強里的核心武器它把四張圖拼成一張讓模型在一個batch里看到更多樣的場景、更多尺度的目標對頭盔這種小目標場景幫助極大。hsv系列的擾動模擬了不同光照條件尤其能彌補夜間樣本不足的短板。關(guān)于訓(xùn)練輪數(shù)我不建議無腦設(shè)置300個epoch。先用150跑通看訓(xùn)練日志里的val/box_loss、val/cls_loss、val/dfl_loss是否在最后20個epoch還在下降。如果val損失連續(xù)30個epoch沒有明顯變化說明已經(jīng)收斂再多的epoch只會過擬合。頭盔檢測的數(shù)據(jù)量不算海量數(shù)據(jù)集8300張訓(xùn)150輪已經(jīng)能拿到一個不錯的模型。4.3 損失函數(shù)與評估指標怎么讀YOLOv8的損失由三部分組成box_loss回歸損失用CIoU計算、cls_loss分類損失用BCE計算、dfl_loss分布焦點損失用來細化邊界框。訓(xùn)練時你會看到三個loss在results.csv里分別記錄。我對loss曲線的要求是訓(xùn)練集三個loss穩(wěn)步下降驗證集三個loss在下降后進入平臺期。如果驗證集loss在某個epoch后開始反彈說明過擬合開始。評估指標主要看mAP0.5和mAP0.5:0.95。前者是IoU閾值取0.5時的平均精度后者是0.5到0.95每隔0.05取一組IoU閾值的平均。對頭盔檢測這種框大一點小一點都不致命的任務(wù)我會優(yōu)先盯mAP0.5能達到90%以上算合格mAP0.5:0.95則是模型定位精度的硬指標一般60%到70%就說明邊界框已經(jīng)壓得很準了。還有一份必看的輸出是confusion_matrix.png。如果發(fā)現(xiàn)head被大量誤檢成helmet說明兩者在特征空間里的距離太近可能需要更細致的標注或更高質(zhì)量的訓(xùn)練圖如果未佩戴樣本大量漏檢大概率是訓(xùn)練集里未佩戴樣本的比例偏低需要補數(shù)據(jù)或調(diào)類別權(quán)重。4.4 可視化驗證訓(xùn)練完一定要看bad case模型訓(xùn)完我會寫一個推理腳本把測試集預(yù)測結(jié)果畫出來逐張看。這一步花不了半小時卻能暴露指標看不出的問題。比如模型把車筐里的頭盔當成佩戴頭盔把路旁的安全帽當成rider的頭盔這些在mAP上其實都會體現(xiàn)為誤檢框但只有肉眼看圖才能快速定位原因。我見過不少團隊訓(xùn)練完只看mAP數(shù)字覺得90%就上線了結(jié)果在真實路口的監(jiān)控畫面上被各種奇怪物體打到滿地是框??梢暬炞C這一關(guān)不能省。5. 要命的高頻踩坑小目標、夜間低光、標注噪聲5.1 小目標漏檢頭盔檢測最大的痛點頭盔檢測和普通目標檢測最大的不同是頭盔在畫面里太小了。1080p畫面里一個10米外的騎手頭部可能只占30x30像素而YOLO的默認檢測層是P38倍下采樣、P416倍、P532倍經(jīng)過8倍下采樣后30x30的目標只剩約4x4個特征點信息損失嚴重。我在第一個版本就踩了這個坑訓(xùn)練集mAP0.5有91%但實際測試中畫面遠處騎手幾乎全部漏檢。排查的思路是分三步把所有漏檢目標框統(tǒng)計出來算它們的面積分布確認是否集中在極小尺寸區(qū)間。直接把輸入分辨率提高到768或960看看漏檢率是否明顯下降。使用切圖推理SAHI把1080p原圖切成兩三個640x640的子圖分別檢測再合并結(jié)果。實際操作下來提高輸入分辨率效果最直接但推理變慢切圖推理效果極好但每路視頻的推理時間會翻倍。折中方案是線上用640輸入配合NMS后處理優(yōu)化線下分析或取證場景用切圖離線刷一遍。還有一個思路是給模型加P2檢測頭。YOLOv8官方支持自定義head結(jié)構(gòu)在P2層4倍下采樣增設(shè)一個檢測頭專門負責(zé)小目標。代價是計算量上升、訓(xùn)練變慢但小目標的召回率確實能漲。5.2 夜間低光數(shù)據(jù)增強救不了一切頭盔檢測的夜間場景是所有智慧交通項目繞不過去的坎。監(jiān)控攝像頭在夜間的畫面要么是紅外的黑白圖像要么是低照度彩噪圖。如果你訓(xùn)練集里沒有這類樣本白天的模型放到晚上幾乎等于失明。我遇到過最狼狽的一次模型在白天測試mAP 93%結(jié)果現(xiàn)場驗收是晚上整個畫面全黑模型輸出了幾百個毫無意義的低置信框。后來總結(jié)經(jīng)驗夜間問題的解法有三個方向按見效程度排序補采真實的夜間/黃昏數(shù)據(jù)讓模型直接見過這個光照分布。沒有條件補采就從現(xiàn)有視頻里抽取低光幀用Gamma校正模擬夜間效果。用圖像增強技術(shù)如HE、CLAHE對夜間幀做預(yù)處理讓單幀的紋理更明顯再送進檢測器。如果監(jiān)控攝像頭本身就是紅外夜視訓(xùn)練時把圖像轉(zhuǎn)成灰度或使用IR風(fēng)格化的增強讓模型適應(yīng)單通道紋理。我個人強烈建議不管數(shù)據(jù)集多好落地前一定要找?guī)锥文繕藞鼍暗恼鎸嵰归g視頻哪怕只有幾百幀跑一遍看效果。夜間模型翻車幾乎都是因為訓(xùn)練分布和部署分布不一致。5.3 標注噪聲漏標和錯標比想象中更致命8300張數(shù)據(jù)集的標注質(zhì)量參差不齊這是常態(tài)。最常見的標注問題是密集場景漏標注一排等紅燈的騎手標注員只標了兩三個其余全部漏掉。模型學(xué)到騎手周圍可以有未標注目標推理時就會抑制本該輸出的框。遮擋目標只標可見部分一個騎手被汽車擋住一半標注框只框住了露出的一小半身體導(dǎo)致模型對半身rider產(chǎn)生了錯誤先驗。類別混淆遠處的head被標成helmet或者反過來。排查標注噪聲核心手段是訓(xùn)練前可視化。我把所有訓(xùn)練圖的標注畫出來輸出成一張大拼圖然后快速翻看。這個方法雖然土但效率極高。還有一個技巧訓(xùn)練一個基線模型后把置信度極高的誤檢框和置信度極低的漏檢圖挑出來和原圖標注對比如果發(fā)現(xiàn)模型認為有目標但標注里沒有大概率是標注漏了。5.4 類別不平衡別急著調(diào)loss權(quán)重頭盔數(shù)據(jù)集里未戴頭盔的樣本天然偏少因為大多數(shù)騎手還是守規(guī)矩的。類別不平衡會導(dǎo)致召回率偏向多數(shù)類。但我建議不要一上來就調(diào)cls_loss的權(quán)重或強行加負樣本那樣容易讓模型在騎手和頭盔的判斷上變得猶猶豫豫。優(yōu)先做的是把已有的少數(shù)類樣本復(fù)制一份配合隨機幾何變換翻轉(zhuǎn)、旋轉(zhuǎn)、縮放生成合成樣本相當于給少數(shù)類做了過采樣同時不改變語義。如果補完數(shù)據(jù)后依然不平衡再考慮在loss里給少數(shù)類加權(quán)重也不遲。6. 從模型到上線TensorRT加速與多路監(jiān)控的工程化6.1 導(dǎo)出與加速pt到onnx再到engine訓(xùn)練完成的.pt權(quán)重只是一個訓(xùn)練產(chǎn)物直接拿來推理可以但效率和穩(wěn)定性都不適合生產(chǎn)環(huán)境。我通常的導(dǎo)出鏈路是yolo export modelbest.pt formatonnx dynamicTrue opset12然后通過TensorRT把onnx轉(zhuǎn)成engine。以YOLOv8為例可以用官方提供的trtexec或tensorrt_yolo項目完成轉(zhuǎn)換。FP16精度對精度影響很小推理速度能比FP32快一倍以上INT8還要做校準集標定精度損失不確定我一般只在極端邊緣設(shè)備上才用。轉(zhuǎn)換過程中有一個高頻坑dynamic shape。如果你的部署場景輸入尺寸固定比如統(tǒng)一640x640那直接關(guān)閉dynamic用固定shape轉(zhuǎn)省心省力還快如果必須支持不同輸入尺寸再把-1維度的動態(tài)范圍配好。TensorRT 8.x以上版本對YOLO的插件支持已經(jīng)比較成熟但不同版本之間的兼容性還是要反復(fù)驗證。6.2 多路視頻實測25幀1080p的算力估算在實際智慧交通項目里支持多少路視頻幾乎是甲方必問的問題。以一個典型配置估算T4 GPUTensorRT FP16YOLOv8s輸入640x640單幀推理大約4到6ms。如果只算推理理論上每秒能處理160到250幀即6到10路25fps的1080p視頻。但這只是理論值。實際工程里每路視頻都要先解碼——1080p H.264解碼本身也吃GPU或CPU資源檢測后還有跟蹤器ByteTrack/DeepSORT、業(yè)務(wù)邏輯判斷、抓拍入庫、告警推送這些都要占用算力。所以工程上都留有余量單張T4跑6到8路25fps比較穩(wěn)。再往上就要上DeepStream這類硬解多路解碼框架或者用多卡分負載。這里啰嗦一句YOLO推理時間和視頻解碼時間經(jīng)常被分頭評估但真正上線時它們共享同一塊顯存和PCIe帶寬內(nèi)存帶寬競爭比你想的更嚴重。批量推理時把多路視頻幀拼成一個batch能有效攤薄調(diào)度開銷這是摸過性能瓶頸的人都會做的事。6.3 業(yè)務(wù)閉環(huán)檢測之后的路頭盔檢測模型只是系統(tǒng)里的一個組件。完整業(yè)務(wù)閉環(huán)一般是攝像頭視頻流 → 解碼抽幀 → 目標檢測rider/helmet/head → 多目標跟蹤ByteTrack關(guān)聯(lián)跨幀同一人 → 業(yè)務(wù)規(guī)則判斷rider框內(nèi)是否有helmet覆蓋頭部區(qū)域 → 觸發(fā)抓拍、取證、上報。跟蹤環(huán)節(jié)容易被忽略但非常重要。單幀檢測結(jié)果波動大同一輛車在連續(xù)幾幀里可能一幀檢出一幀漏掉。用ByteTrack做ID關(guān)聯(lián)后可以在一個ID的連續(xù)軌跡上做幀級投票——超過70%的幀判定為未佩戴才觸發(fā)告警能極大降低誤報率。我在實際項目里還加了一個緩沖機制檢測到未佩戴后延遲2秒再抓拍目的是等車輛行駛到畫面中央、目標尺寸最大的時刻再取證避免拍到一張幾十像素的模糊人影。工程上還有兩個實用細節(jié)。一是原始視頻幀要保存至少7天便于事后復(fù)核和模型迭代時回放bad case。二是告警圖片的水印和坐標信息抓拍時間、地點、車牌、置信度要在檢測端就寫入不能在云端二次處理時丟失。前者是運維需要后者是取證合規(guī)需要。最后分享一個小技巧是我做頭盔檢測迭代三輪之后總結(jié)出來的先用小模型n/s尺寸以最快速度跑通整個鏈路拿到真實場景的bad case再用大模型m/l尺寸離線批量刷歷史監(jiān)控視頻把所有誤檢、漏檢幀自動抽出來回填到訓(xùn)練集重新訓(xùn)練。這個小模型圈定問題、大模型補充數(shù)據(jù)的循環(huán)跑兩到三輪模型的真實場景mAP提升比單純調(diào)參明顯得多。數(shù)據(jù)、模型、部署三者互相喂才是把一套公開數(shù)據(jù)集真正變成可落地系統(tǒng)的正確姿勢。