別實(shí)戰(zhàn):從數(shù)據(jù)集訓(xùn)練到TensorRT部署全流程)
簡介這份資源面向深度學(xué)習(xí)初學(xué)者、目標(biāo)檢測方向的學(xué)生及畢業(yè)設(shè)計(jì)開發(fā)者提供基于YOLOv5實(shí)現(xiàn)車牌識(shí)別的完整工程實(shí)踐。壓縮包內(nèi)共77個(gè)文件以29個(gè)Python腳本、17個(gè)YAML配置、21張示例圖片及2個(gè)預(yù)訓(xùn)練權(quán)重文件為主涵蓋模型定義、訓(xùn)練推理、數(shù)據(jù)轉(zhuǎn)換與權(quán)重下載等模塊整體約25.4MB。資源圍繞數(shù)據(jù)預(yù)處理、模型訓(xùn)練、參數(shù)調(diào)優(yōu)、驗(yàn)證測試與實(shí)時(shí)部署五個(gè)環(huán)節(jié)展開讀者可借助其中的網(wǎng)絡(luò)結(jié)構(gòu)配置、訓(xùn)練腳本與已訓(xùn)練權(quán)重快速復(fù)現(xiàn)車牌檢測流程并在此基礎(chǔ)上調(diào)整超參數(shù)、替換數(shù)據(jù)集或遷移到安防監(jiān)控與智能交通場景。目前已有2591人學(xué)習(xí)下載適合作為課程實(shí)踐、課題研究或畢業(yè)設(shè)計(jì)的起步模板幫助理解卷積神經(jīng)網(wǎng)絡(luò)如何自動(dòng)提取車牌特征并完成端到端定位識(shí)別。1. 車牌識(shí)別為什么選 YOLOv5從一張違章抓拍說起前陣子幫朋友看一個(gè)停車場出入口的抓拍項(xiàng)目相機(jī)像素不低但車牌識(shí)別率死活上不去白天還行一到傍晚逆光就大面積漏檢。翻了他的代碼才發(fā)現(xiàn)檢測環(huán)節(jié)用的是傳統(tǒng)邊緣加顏色分割閾值全靠手調(diào)光照一變就崩。這類場景其實(shí)特別適合把檢測環(huán)節(jié)換成 YOLOv5——它把車牌當(dāng)成一個(gè)普通目標(biāo)來框魯棒性比手工特征強(qiáng)太多而且工程化程度高從訓(xùn)練到部署一條龍都有現(xiàn)成腳本。這份資源就是圍繞「YOLOv5 實(shí)現(xiàn)車牌識(shí)別」這條主線展開的包含數(shù)據(jù)集組織、模型訓(xùn)練、推理后處理到部署落地的完整鏈路。它解決的核心問題是讓你不用從零搭檢測框架直接在一個(gè)驗(yàn)證過的工程結(jié)構(gòu)上把車牌檢測加字符識(shí)別跑通。適合兩類人一是剛接觸深度學(xué)習(xí)目標(biāo)檢測、想拿一個(gè)真實(shí)場景練手的工程師二是手里有車牌識(shí)別需求、想快速驗(yàn)證方案可行性的開發(fā)者。下面我按自己拆包復(fù)現(xiàn)的順序把關(guān)鍵環(huán)節(jié)和踩過的坑講清楚。2. YOLOv5 車牌檢測的工程結(jié)構(gòu)與環(huán)境配置2.1 為什么是 YOLOv5 而不是 v8 或 Faster R-CNN先說說選型。車牌檢測這個(gè)任務(wù)有幾個(gè)特點(diǎn)目標(biāo)尺寸相對(duì)固定、長寬比偏扁、單張圖里目標(biāo)數(shù)量少通常一到兩個(gè)、對(duì)實(shí)時(shí)性要求高。YOLOv5 在這個(gè)場景下的優(yōu)勢很明顯。它的 anchor 機(jī)制對(duì)扁長目標(biāo)友好只要聚類出來的先驗(yàn)框貼合車牌比例召回率就穩(wěn)推理速度快n/s 版本在普通顯卡上輕松跑到幾十幀生態(tài)成熟導(dǎo)出 ONNX、TensorRT 的腳本都是現(xiàn)成的。Faster R-CNN 精度不差但兩階段推理慢部署到邊緣設(shè)備上很吃力。YOLOv8 更新但如果你手里的部署工具鏈、后處理代碼都是圍繞 v5 寫的貿(mào)然換版本反而增加遷移成本。常見做法是驗(yàn)證階段用 YOLOv5s 快速迭代確認(rèn)方案可行后再考慮換更輕或更新的骨干。這個(gè)資源選 v5我認(rèn)為是務(wù)實(shí)的選擇不是追新。2.2 目錄結(jié)構(gòu)與關(guān)鍵文件拿到包之后別急著跑先把結(jié)構(gòu)理清楚。典型的 YOLOv5 車牌項(xiàng)目會(huì)包含這幾塊目錄/文件作用復(fù)現(xiàn)時(shí)要?jiǎng)拥牡胤絛ata/數(shù)據(jù)集配置與圖片標(biāo)簽改 yaml 里的路徑和類別數(shù)models/網(wǎng)絡(luò)結(jié)構(gòu)定義一般不動(dòng)除非改 anchorweights/預(yù)訓(xùn)練權(quán)重放 yolov5s.pt 做遷移學(xué)習(xí)utils/數(shù)據(jù)加載、后處理、指標(biāo)車牌后處理常在這里加train.py訓(xùn)練入口調(diào)超參detect.py推理入口調(diào)置信度和 NMS車牌識(shí)別和通用檢測最大的不同在于檢測框出來之后還要做字符識(shí)別。所以工程里通常會(huì)有兩個(gè)模型或者一個(gè)檢測加一個(gè) CRNN 識(shí)別。這份資源的主線是檢測部分識(shí)別部分一般接一個(gè)輕量 OCR。你得先明確自己要的是「只檢測車牌位置」還是「檢測加識(shí)別字符」這決定了后面要不要再接一個(gè)識(shí)別模型。2.3 環(huán)境配置conda 建環(huán)境與依賴安裝環(huán)境這塊翻車最多我一般強(qiáng)制用 conda 隔離避免和系統(tǒng)里的 torch 打架。步驟和命令如下# 創(chuàng)建獨(dú)立環(huán)境python 版本按項(xiàng)目要求一般 3.8 比較穩(wěn) conda create -n plate_yolo python3.8 -y conda activate plate_yolo # 安裝 pytorch注意 cuda 版本要和驅(qū)動(dòng)匹配這里以 cu118 為例 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 安裝項(xiàng)目其余依賴 pip install -r requirements.txt # 驗(yàn)證 torch 是否能識(shí)別到顯卡 python -c import torch; print(torch.cuda.is_available())邏輯說明先建環(huán)境再裝 torch是因?yàn)?requirements.txt 里往往也寫了 torch如果順序反了pip 可能裝成 CPU 版本訓(xùn)練時(shí)慢到懷疑人生。參數(shù)上python 版本別盲目上 3.11很多老項(xiàng)目的 numpy、opencv 輪子對(duì)高版本支持不好。最后那句驗(yàn)證一定要跑輸出 True 才算環(huán)境通了。如果輸出 False先查驅(qū)動(dòng)和 cuda 版本對(duì)應(yīng)關(guān)系別急著改代碼。提示requirements.txt 里的版本號(hào)如果和你的 cuda 沖突優(yōu)先保證 torch 和 cuda 匹配其余依賴可以適當(dāng)放寬版本。3. 數(shù)據(jù)集準(zhǔn)備與 YOLOv5 訓(xùn)練調(diào)參實(shí)戰(zhàn)3.1 車牌數(shù)據(jù)集標(biāo)注格式與目錄組織YOLOv5 用的是 YOLO 格式標(biāo)簽每張圖對(duì)應(yīng)一個(gè) txt每行是類別 中心x 中心y 寬 高全部歸一化到 0 到 1。車牌場景一般就一個(gè)類別所以類別 id 恒為 0。目錄組織常見兩種我推薦按下面這種dataset/ images/ train/ val/ labels/ train/ val/圖片和標(biāo)簽文件名必須一一對(duì)應(yīng)只是后綴不同。這里有個(gè)血淚經(jīng)驗(yàn)標(biāo)注時(shí)框要貼緊車牌邊緣別把車牌外的螺絲孔、邊框留太多否則模型學(xué)到的框會(huì)偏大后處理裁剪字符時(shí)容易帶進(jìn)干擾。另外車牌如果是傾斜的標(biāo)注框用水平矩形就行YOLOv5 默認(rèn)不做旋轉(zhuǎn)框傾斜靠數(shù)據(jù)增強(qiáng)和后續(xù)透視校正解決。數(shù)據(jù)配置文件一般叫plate.yaml內(nèi)容長這樣# 數(shù)據(jù)集根路徑建議寫絕對(duì)路徑避免相對(duì)路徑找不到 path: /home/user/dataset train: images/train val: images/val # 類別數(shù)車牌只有一類 nc: 1 names: [plate]參數(shù)說明path寫絕對(duì)路徑能省掉很多「找不到文件」的玄學(xué)問題nc一定要和標(biāo)簽里的最大類別 id 加一一致寫錯(cuò)了訓(xùn)練不報(bào)錯(cuò)但結(jié)果全亂。改完 yaml先用官方腳本檢查一遍標(biāo)簽有沒有越界、有沒有空文件比訓(xùn)練到一半才發(fā)現(xiàn)問題劃算得多。3.2 遷移學(xué)習(xí)與超參數(shù)設(shè)置車牌數(shù)據(jù)量通常不大幾千張就算多了所以必須用預(yù)訓(xùn)練權(quán)重做遷移學(xué)習(xí)。命令如下# 用 yolov5s 預(yù)訓(xùn)練權(quán)重訓(xùn)練 100 輪批次大小按顯存調(diào) python train.py \ --weights yolov5s.pt \ --data data/plate.yaml \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --cfg models/yolov5s.yaml \ --hyp data/hyps/hyp.scratch-low.yaml \ --name plate_exp邏輯說明--weights指定預(yù)訓(xùn)練權(quán)重這是小數(shù)據(jù)集能訓(xùn)出來的關(guān)鍵--img-size設(shè) 640 是 v5 的默認(rèn)車牌目標(biāo)不大如果顯存夠可以上 1280小目標(biāo)召回會(huì)更好--hyp選低增強(qiáng)配置車牌顏色和紋理比較固定過強(qiáng)的色彩抖動(dòng)反而有害。批次大小 16 是 8G 顯存的經(jīng)驗(yàn)值爆顯存就往下調(diào)別硬撐。訓(xùn)練過程中重點(diǎn)看三個(gè)指標(biāo)mAP0.5、precision、recall。車牌場景我更看重 recall漏檢比誤檢麻煩因?yàn)槁┝司蜎]法補(bǔ)救。如果 recall 上不去先查標(biāo)注質(zhì)量再考慮調(diào)低置信度閾值或增加正樣本。3.3 anchor 聚類讓先驗(yàn)框貼合車牌比例YOLOv5 默認(rèn) anchor 是給通用目標(biāo)用的車牌又扁又寬直接訓(xùn)也能收斂但用聚類重新生成 anchor 會(huì)明顯提升。項(xiàng)目里一般帶一個(gè)聚類腳本跑法如下# 對(duì)訓(xùn)練集標(biāo)簽做 k-means生成 9 個(gè) anchor python utils/autoanchor.py --data data/plate.yaml邏輯說明這個(gè)腳本會(huì)讀取所有訓(xùn)練標(biāo)簽的寬高聚類出最貼合數(shù)據(jù)分布的 9 個(gè)框然后你可以把它們替換到模型 cfg 里。參數(shù)上聚類數(shù)保持 9 和 v5 的三個(gè)檢測頭對(duì)應(yīng)。替換后重新訓(xùn)練通常 mAP 能漲一兩個(gè)點(diǎn)。注意別在驗(yàn)證集上聚類那是數(shù)據(jù)泄露要用訓(xùn)練集。4. 推理、后處理與車牌識(shí)別串聯(lián)4.1 detect.py 推理與置信度閾值訓(xùn)練完拿到best.pt先用推理腳本看看效果python detect.py \ --weights runs/train/plate_exp/weights/best.pt \ --source test_images/ \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt邏輯說明--conf-thres是置信度閾值低于它的框直接丟--iou-thres控制 NMS 的重疊閾值。車牌場景目標(biāo)少NMS 壓力不大iou 設(shè) 0.45 夠用。--save-txt會(huì)把檢測框坐標(biāo)存下來方便你接后續(xù)的字符識(shí)別。如果發(fā)現(xiàn)漏檢先把 conf 降到 0.15 看看是不是閾值卡太死如果誤檢多再往上提。4.2 從檢測框到字符識(shí)別裁剪與透視校正檢測框出來只是第一步要識(shí)別字符還得把車牌區(qū)域摳出來。這里有兩個(gè)坑一是框可能帶背景二是車牌有傾斜。常見做法是先按框裁剪再做一次透視校正。核心代碼邏輯如下import cv2 import numpy as np def crop_plate(img, box): # box 格式為 xyxy轉(zhuǎn)成整數(shù) x1, y1, x2, y2 map(int, box) # 適當(dāng)外擴(kuò)幾個(gè)像素避免切掉字符邊緣 pad 2 x1, y1 max(0, x1 - pad), max(0, y1 - pad) x2, y2 min(img.shape[1], x2 pad), min(img.shape[0], y2 pad) plate img[y1:y2, x1:x2] # 轉(zhuǎn)灰度再做自適應(yīng)閾值逆光場景更穩(wěn) gray cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) return binary邏輯說明外擴(kuò) pad 是為了防止框太緊切掉字符自適應(yīng)閾值比固定閾值抗光照變化參數(shù) 11 是鄰域塊大小2 是常數(shù)項(xiàng)逆光時(shí)可以適當(dāng)調(diào)大常數(shù)。裁剪出來的圖再送進(jìn) CRNN 或輕量 OCR 做字符識(shí)別。如果車牌傾斜明顯還得先做角點(diǎn)檢測加透視變換把車牌擺正再識(shí)別否則字符分割會(huì)亂。4.3 端到端串聯(lián)與結(jié)果輸出把檢測和識(shí)別串起來流程是讀圖 → YOLOv5 推理 → 遍歷每個(gè)框 → 裁剪校正 → 識(shí)別字符 → 輸出結(jié)構(gòu)化結(jié)果。輸出格式建議統(tǒng)一成 JSON方便下游系統(tǒng)對(duì)接results [] for box, conf in zip(boxes, confs): plate_img crop_plate(img, box) text ocr_model(plate_img) # 識(shí)別模型返回字符串 results.append({ bbox: box, confidence: float(conf), plate_text: text })邏輯說明把置信度和識(shí)別文本一起返回下游可以根據(jù)置信度做二次校驗(yàn)。車牌識(shí)別里常見的「0 和 O」「1 和 I」混淆可以在識(shí)別后加一層規(guī)則校正比如按車牌格式校驗(yàn)位數(shù)和字符集不合規(guī)的標(biāo)記為可疑。這一步能顯著降低誤識(shí)率屬于工程上很實(shí)用的后處理。5. 避坑與常見問題排查5.1 訓(xùn)練 loss 不降反升現(xiàn)象訓(xùn)練幾輪后 box loss 或 obj loss 突然飆升。原因通常是學(xué)習(xí)率太大或者數(shù)據(jù)里有臟標(biāo)注框越界、寬高為 0。解決先把學(xué)習(xí)率降一個(gè)數(shù)量級(jí)試再用腳本掃一遍標(biāo)簽把寬高小于 0.001 的框刪掉。我遇到過一張圖標(biāo)簽坐標(biāo)全是 0就是標(biāo)注工具導(dǎo)出時(shí)出的錯(cuò)。5.2 驗(yàn)證集 mAP 很高但實(shí)際推理漏檢現(xiàn)象訓(xùn)練日志里 mAP0.5 到 0.9 了實(shí)際跑圖卻漏。原因多半是驗(yàn)證集和實(shí)際場景分布不一致比如驗(yàn)證集都是白天圖實(shí)際有夜間。解決重新劃分驗(yàn)證集保證覆蓋各種光照和角度推理時(shí)適當(dāng)降低 conf 閾值并檢查預(yù)處理是否和訓(xùn)練一致歸一化方式、通道順序。5.3 顯存溢出 CUDA out of memory現(xiàn)象訓(xùn)練到一半報(bào)顯存不足。原因批次太大、img-size 太高或者沒釋放中間變量。解決先降 batch-size再降 img-size兩者按平方關(guān)系影響顯存。如果還不夠開啟梯度累積模擬大批次。別一味加顯存先把參數(shù)調(diào)合理。5.4 導(dǎo)出 ONNX 后推理結(jié)果對(duì)不上現(xiàn)象PyTorch 里結(jié)果正常導(dǎo)出 ONNX 后框全亂。原因?qū)С鰰r(shí)沒指定動(dòng)態(tài)軸或者后處理里的坐標(biāo)變換依賴了框架特有算子。解決導(dǎo)出時(shí)加--dynamic并核對(duì)預(yù)處理和后處理是否和原腳本一致。ONNX 的 NMS 有時(shí)要單獨(dú)實(shí)現(xiàn)別指望它自動(dòng)帶上。5.5 車牌字符識(shí)別串位現(xiàn)象檢測框沒問題但識(shí)別出來的字符順序亂或丟字。原因裁剪時(shí)沒做透視校正字符傾斜導(dǎo)致分割錯(cuò)位。解決在裁剪后加一步透視變換用車牌四個(gè)角點(diǎn)做映射把車牌擺正再送識(shí)別。這一步對(duì)傾斜抓拍場景提升明顯。6. 進(jìn)階技巧用 TensorRT 加速與精度回歸驗(yàn)證方案跑通之后下一步就是提速。車牌識(shí)別在出入口場景對(duì)延遲敏感YOLOv5 導(dǎo)出 TensorRT 是常見做法。流程是先把 best.pt 導(dǎo)出 ONNX再用 trtexec 轉(zhuǎn) engine# 導(dǎo)出 onnx指定動(dòng)態(tài)批次 python export.py --weights best.pt --include onnx --dynamic # 轉(zhuǎn) TensorRT enginefp16 精度速度更快 trtexec --onnxbest.onnx --saveEnginebest.engine --fp16邏輯說明--dynamic讓批次維度可變部署時(shí)更靈活--fp16用半精度推理速度通常能翻倍精度損失很小。轉(zhuǎn)完之后一定要做精度回歸別只看速度。我的習(xí)慣是拿同一批測試圖分別跑 PyTorch 和 TensorRT逐張比對(duì)檢測框的 IoU 和識(shí)別文本IoU 低于 0.9 的挑出來看確認(rèn)不是精度塌方。驗(yàn)證方法上我一般會(huì)建一個(gè)小型回歸集覆蓋白天、夜間、逆光、傾斜、污損車牌這幾類每類幾十張每次改模型或換部署方式都跑一遍記錄 mAP 和識(shí)別準(zhǔn)確率。這個(gè)習(xí)慣幫我擋掉過好幾次「訓(xùn)練指標(biāo)好看、上線就崩」的情況。參數(shù)上TensorRT 的 workspace 大小按顯存給一般 1G 到 2G 夠用給太大反而占資源。還有個(gè)實(shí)用技巧把檢測和識(shí)別拆成兩個(gè) engine檢測用 fp16識(shí)別用 fp32因?yàn)樽址R(shí)別對(duì)精度更敏感混著用能在速度和準(zhǔn)確率之間找平衡。部署到邊緣設(shè)備時(shí)先測功耗和散熱別只看幀率長時(shí)間跑降頻是常事。從那以后我每次換部署后端都強(qiáng)制走一遍回歸集比對(duì)不省這一步。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取