简介面向车牌识别与目标检测开发者这份数据集提供1019张带标签图像可直接用于YOLO系列模型的训练与验证。压缩包共2000个文件包含YOLO格式txt标签980个、VOC格式xml标签1019个以及数据集配置文件data.yaml已清晰划分训练集、验证集与测试集免去手动划分与格式转换的麻烦。TXT标签采用 x_center y_center 格式坐标均归一化到0~1之间适配yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流算法可直接加载训练并对比验证效果。资源整体大小47.28MB已有153人学习适合正在做车牌检测项目或入门YOLO实战的开发者直接调用与快速上手。1. 这批车牌检测数据集到底值不值得解压先看清它的边界做车牌检测的人最容易卡住的往往不是模型而是数据。想用 yolo 算法跑通一个车牌检测 demo打开标注软件才发现要自己拉几百个框等真拿到一份“1019张图像带标签”的 zip又开始怀疑里面标签格式对不对、能不能直接喂给 YOLOv8。这个数据集就是干这个用的1019 张真实场景的车牌图像每张配好了 YOLO 格式的 txt 标签解压、整理、写个 data.yaml 就能开始训练。适合刚跑通 YOLO 环境、想拿真实数据做车牌检测的入门者也适合需要在白天/夜间/不同车牌颜色上快速验证模型的从业者。不过别高兴太早这份数据能省你一周打标时间但能不能训练出稳定模型还得看你会不会处理类别不平衡、坐标归一化这些隐藏问题。2. 解压与目录摸底从 zip 到可训练数据集的第一步拿到这个 zip 之后先别急着双击解压也别急着往 YOLOv8 里塞。车牌检测数据集这种资源最常见的翻车点不是模型而是文件本身在传输过程中损坏、标签和图像对不上、标签格式根本不是 YOLO 能认的。先花十分钟把压缩包摸清楚后面能省下好几个小时的排错时间。2.1 先校验 zip 完整性再解压避免训到一半才发现图片损坏压缩包是 zip 格式但 zip 也有损坏的可能。尤其从网盘、微信、邮件附件下下来的文件经常出现“能解压但解出来一半是坏的”这种状态。最直接的办法是先用测试模式跑一遍不实际解压只校验压缩包结构。# Linux 下先测试压缩包完整性不实际解压 unzip -t yolo算法-车牌检测数据集-1019张图像带标签.zip | tail -n 20 # Windows 下用 7-Zip 的命令行版本 7z t yolo算法-车牌检测数据集-1019张图像带标签.zip-t是 unzip 的 test 选项它会逐个读取压缩包里的文件并做 CRC 校验最后告诉你有多少个文件通过、多少个出错。输出比较多所以用tail -n 20只看最后几行。Windows 下用 7-Zip 的命令行7z t也是同样的逻辑它不会把文件解出来只做完整性测试。这一步看着多余实际很值得做尤其是文件超过 1GB 或者从非官方渠道下载的时候。如果测试结果里出现 “bad CRC” 或 “packed data CRC failed”别犹豫重新下载或者找对方重新打包硬解压只会让你在训练时突然遇到打不开的图片。校验通过后再正常解压。建议单独建一个目录不要把文件直接甩到桌面或项目根目录里否则后面写 data.yaml 时路径很容易乱。# Linux/macOS 下解压到专门的数据目录 mkdir -p license_plate_data unzip yolo算法-车牌检测数据集-1019张图像带标签.zip -d license_plate_data/ # 解压后先看一层目录结构 find license_plate_data -maxdepth 2 -type d | head -n 30-d指定了解压目标目录find加上maxdepth 2只列两层目录避免一次性刷出几百个文件。这一步的目的是确认压缩包顶层是不是套了一个外层文件夹如果套了后面写路径时就要多包一层。2.2 看懂 YOLO 标签格式txt 里的五个数字到底是什么解压之后重点看labels或annotations目录下的.txt文件。YOLO 系列算法从 YOLOv5 到 YOLOv8用的都是同一种标签格式每一行代表一个目标五个数字分别是class_id, x_center, y_center, width, height注意后四个都做了归一化除以了图片宽高。用文本编辑器打开一个标签文件典型内容长这样# 假设这是某张车牌图片对应的 label 文件内容 # class_id x_center y_center width height 0 0.520157 0.432031 0.073438 0.026563第一列的0是类别编号对应训练配置里names列表的下标。后四个数全是 0 到 1 之间的小数表示车牌框中心点的相对位置和框的相对宽高。这里最容易被坑的是有人拿到的是 VOC 格式的 XML 或 JSON 标注里面存的是像素坐标如果不做转换直接当成 YOLO 标签用训练时 loss 会异常甚至直接报错。如果你发现 txt 里出现大于 1 的数基本可以确定这份标签不是 YOLO 原生格式需要转换后再训练。检验标签格式是否规范可以写个几行脚本扫描一遍所有 txt看看每行的数字数量和范围是否合法。常见的做法是直接用 Python 读文件不加载图片速度很快。from pathlib import Path label_dir Path(license_plate_data/labels) bad_label [] for txt in label_dir.glob(*.txt): for line in txt.read_text(encodingutf-8).splitlines(): line line.strip() if not line: continue parts line.split() # 每行必须是 class 4 个坐标且坐标值在 [0, 1] 区间 if len(parts) ! 5: bad_label.append((txt.name, 字段数不对, line)) else: try: cls int(parts[0]) coords [float(v) for v in parts[1:]] if cls 0 or not all(0.0 v 1.0 for v in coords): bad_label.append((txt.name, 数值越界, line)) except ValueError: bad_label.append((txt.name, 非数字内容, line)) print(f异常标签文件数: {len(bad_label)}) for item in bad_label[:10]: print(item)这个脚本的价值在于快速筛查len(parts) ! 5能抓住多列少列的标签坐标越界能抓住没归一化的像素值。如果你的标签里有空 txt 文件说明那张图可能没有标注任何车牌这在数据集中不算罕见但要留意数量占比空标签太多会干扰训练。2.3 用脚本核对图像和标签是否一一对应有了图像目录和标签目录之后下一步就是确认每一张图都有且只有一个对应的 txt 文件。这个检查必须做因为模型训练是随机读取图像和标签的如果某个图像没有标签训练过程的collate_fn可能会崩或者在验证时 mAP 统计出现异常。from pathlib import Path image_dir Path(license_plate_data/images) label_dir Path(license_plate_data/labels) images {p.stem for p in image_dir.glob(*.jpg)} labels {p.stem for p in label_dir.glob(*.txt)} no_label images - labels # 有图无标签 no_image labels - images # 有标签无图 print(f图像总数: {len(images)}) print(f标签总数: {len(labels)}) print(f缺标签的图像: {len(no_label)}) print(f缺图像的标签: {len(no_image)}) if no_label: print(示例:, sorted(no_label)[:5])这里用Path.glob(*.jpg)只匹配了 jpg如果数据里有 png 或 bmp需要一并加上。如果你解压后发现图像总数不是 1019或者标签数和图像数对不上先别急着训练先看看到底是差在了哪些文件上。多数情况是解压不完整或者有人提前删过失败样本这两种情况都会直接影响训练集分布需要手动处理。核对完成后再把图像和标签整理成 YOLOv8 默认认的目录结构方便后面直接引 data.yaml。3. 用 YOLOv8 训练车牌检测模型最小可复现流程目录结构和标签格式确认无误之后就可以正式碰模型了。这一章我按最常见、最稳的路线来写YOLOv8 官方 CLI配合一个 1019 张的小型车牌检测数据集目标是把训练流程跑通同时不把参数调得过于激进。很多新手在这一步会直接套用网上默认训练指令结果发现 loss 不降或者 mAP 停在 0.1 上下问题往往出在 data.yaml 和超参配置上。3.1 准备 data.yaml路径、类别数、类别名一个都不能错YOLOv8 训练时读的 data.yaml 是它的“地图”里面写死了训练集、验证集路径、类别数和类别名。很多人在这一步翻车有人用 Windows 路径直接塞进去导致转义出错有人把验证集路径指向不存在的目录还有人类别名写成了0、1这种数字。正确写法很简单但每一步都要核对。# data.yaml # 建议用绝对路径避免 CLI 在不同工作目录下执行时找不到数据 path: /home/user/license_plate_data train: images/train val: images/val nc: 3 names: 0: blue_plate 1: green_plate 2: yellow_plate需要注意path是数据集根目录train和val是相对path的子目录。如果你的数据集没有预先划分 train/val只有一个 images 目录最常见的做法是先按 8:2 划分然后用软链接或直接移动文件来组织目录。这里类别数和类别名必须和标签里的 class_id 完全对应如果标签里只有 0 这个类别nc填 3 不会直接报错但训练出来大概率是乱的。先确认数据实际有几个类别再写这个文件。分数据集时推荐写个固定随机种子的小脚本保证每次划分结果一致避免反复调参时训练集不固定导致无法对比。import random from pathlib import Path import shutil random.seed(42) image_dir Path(license_plate_data/images) train_dir Path(license_plate_data/images/train) val_dir Path(license_plate_data/images/val) for split_dir in (train_dir, val_dir): split_dir.mkdir(parentsTrue, exist_okTrue) all_images list(image_dir.glob(*.jpg)) random.shuffle(all_images) split_index int(len(all_images) * 0.8) for img in all_images[:split_index]: shutil.move(str(img), str(train_dir / img.name)) for img in all_images[split_index:]: shutil.move(str(img), str(val_dir / img.name))注意这个脚本只移动了图像没有移动标签所以还要同步把 labels 目录拆成和 images 对应的结构。多数 YOLO 训练框架会按图像路径的 stem 去找同名 txt如果标签目录仍然是扁平结构训练时会报找不到 label 的警告。拆标签时最稳的方法是从图像文件名反推训练集里的图它的同名标签就放到 labels/train 下。这一步可以用shutil.move的变体完成但更推荐直接写一个按 stem 匹配的脚本避免操作失误。3.2 训练命令与关键参数batch、epochs、imgsz 怎么定目录组织好、data.yaml 写好后训练命令本身不长但参数选择直接影响 1019 张这种小数据量下的训练效果。# 用 YOLOv8n 做基础版训练 yolo detect train \ data/home/user/license_plate_data/data.yaml \ modelyolov8n.pt \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectruns/plate_detect \ namebaseline这里逐项说下关键参数。modelyolov8n.pt表示用 YOLOv8n 的预训练权重做起点n 是 nano 版本模型最小、训练最快对 1019 张数据最合适。epochs100看着多但因为加了patience20模型在验证集上连续 20 轮不提升就会提前停实际通常 50 轮左右就能收敛。batch16取决于显存如果是 6GB 显存就把 batch 降到 8否则容易爆显存。imgsz640是输入分辨率车牌是中小目标不建议低于 640否则小车牌在缩小后几乎不可辨。训练跑起来后你要盯的不是终端打印的进度条而是两个指标Box P和mAP50。正常情况下前 20 轮 mAP50 会从 0 爬到 0.5 以上如果 30 轮后还在 0.2 以下徘徊先别继续等了检查标签格式和数据划分。用 YOLOv8 的 Python API 也能跑效果一样只是便于把训练记录嵌入你自己的训练脚本from ultralytics import YOLO model YOLO(yolov8n.pt) results model.train( data/home/user/license_plate_data/data.yaml, epochs100, batch16, imgsz640, patience20, projectruns/plate_detect, namebaseline, )两种方式底层走的是同一套训练逻辑CLI 适合快速跑基线Python API 适合后面要做学习率调度、自定义回调的进阶场景。数据量小的时候我建议你先跑 CLI因为输出排版更直观方便直接截图对比实验结果。3.3 验证集与指标mAP 要看到什么样的数才算能上车训练结束后项目目录下会生成runs/plate_detect/baseline文件夹里面有weights/last.pt、weights/best.pt和一堆图表。注意先区分 last 和 bestbest 是验证集 mAP 最高时的权重last 是最后一轮的权重平时测试、部署都用 best不要习惯性拿 last 当成品。用 best.pt 跑一遍验证集看最终指标yolo detect val \ modelruns/plate_detect/baseline/weights/best.pt \ data/home/user/license_plate_data/data.yaml输出里的关键指标有三个mAP50是 IoU 阈值 0.5 时的平均精度mAP50-95是 0.5 到 0.95 每隔 0.05 的平均值Precision是所有预测框里真正检测到车牌的比例。对于车牌检测这种单类目标、背景相对简单的任务mAP50低于 0.8 说明模型还没学好mAP50-95因为 IoU 要求高0.5 左右是正常水平。如果 mAP50 能到 0.85 以上这个模型基本可以拿去跑实际图片测试了。这里有个容易误判的点验证集 mAP 高不代表手机拍一张就一定能框中因为数据集的图片和实际场景分布可能差别很大。所以训练完第一件事不是急着部署而是拿几十张没见过的图做推理看看误检和漏检都发生在什么场景下。4. 车牌检测训练与推理的五个必踩坑从现象到排查这部分是我最想写的。1019 张的车牌数据集本身不大训练起来问题也特别典型每个坑都是真实场景里反复出现过的。按照“现象 → 原因 → 解决”的顺序来记录方便你在自己的项目里对照排查。4.1 训练 loss 正常但预测框乱跳标签坐标归一化被二次缩放现象是训练 loss 一路下降推理时框也能落在车牌附近但框的大小和位置每张图都不太对比如框总是偏左上角或者框的宽高明显是真实车牌的 1.5 倍。最初我以为是模型没收敛后来发现是标签坐标出了问题。原因很简单这份数据集的标签虽然叫 YOLO 格式但坐标可能是基于一张 1920x1080 的原图归一化的而训练时imgsz640是直接缩放整张图没有做 letterbox导致坐标和实际输入图像的空间映射错位。另一个常见原因是有人用 LabelImg 导出了 YOLO 格式但导出时又把图像做了一次裁剪标签没跟着裁。解决方法是随机抽出几张图把标签坐标画回去看看import cv2 img cv2.imread(license_plate_data/images/train/0001.jpg) label_path license_plate_data/labels/train/0001.txt h, w img.shape[:2] with open(label_path, encodingutf-8) as f: for line in f: cls, x_center, y_center, box_w, box_h map(float, line.split()) x1 int((x_center - box_w / 2) * w) y1 int((y_center - box_h / 2) * h) x2 int((x_center box_w / 2) * w) y2 int((y_center box_h / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_visualized.jpg, img)把可视化结果和原图叠着看如果框没有贴合车牌说明标签坐标和图像不是同一次 resize 下的产物这时要么用原始分辨率去训练要么统一重算归一化坐标。4.2 蓝牌能检、绿牌黄牌全漏类别不平衡与样本倾向现象是训练完模型后蓝牌检测效果不错但绿牌、黄牌几乎一个都检不出来。原因基本是数据集里蓝牌图片占了绝大多数其他颜色的车牌样本太少模型训练时对少数类的梯度贡献被淹没了。1019 张图如果按 7:2:1 分给蓝绿黄绿牌黄牌每类可能只有 100 张不到这么少的样本很难学到稳定的特征。解决这类不平衡问题最直接的办法不是马上加增广而是先统计一下各类别的真实分布。如果少数类样本确实太少可以考虑把类别合并成“车牌”单类先把检测能力跑通再做细分类如果坚持分颜色就要对少数类做过采样复制或在训练时提高少数类在 loss 中的权重。实际项目里我更推荐先合并成单类因为车牌检测的本质是先定位颜色识别可以放在检测框之后加一个轻量分类器这个思路对数据量小的场景友好得多。4.3 解压后图片打不开zip 传输损坏与隐藏的截断文件这里说的不只是解压时报错更是解压后部分图片在读取时cv2.imread返回 None。现象是训练过程中突然报出错在某张图上或者验证集 mAP 极低经过排查发现那部分损坏图片根本加载不出来。原因一般有两个一是 zip 在传输过程中损坏解压程序没报错但写出的文件不完整二是源文件本身就没有写完整比如相机中断导致 jpg 文件缺尾。解决的第一步是把损坏文件找出来import cv2 from pathlib import Path bad_images [] for p in Path(license_plate_data/images).rglob(*.jpg): img cv2.imread(str(p)) if img is None: bad_images.append(p) print(f损坏图片数量: {len(bad_images)}) for p in bad_images[:20]: print(p)找到之后把损坏图片和它对应的标签一起移到 backup 目录不要直接删除防止后面需要重新从原始 zip 恢复时找不到参考。处理完后再看一眼总数如果缺失超过 5%建议回头重新校验原始 zip。4.4 标签数量对不上图像数量空 txt 和漏标现象是训练前脚本报“found no labels”警告或者训练过程完全没有车牌类别在跑。原因多数是标签目录里某些 txt 文件是空的还有一部分图根本没有对应的 txt。这种问题在下载的数据集里特别常见属于打包时的疏漏。解决方法是把空 txt 对应的图找出来根据实际情况做两种处理之一如果那辆车确实太模糊、人或物遮挡严重直接把这组图像和空标签都移除避免干扰如果图像明显包含清晰车牌但没标建议自己补标。但 1019 张数据集里如果空标签只占一两个最省事的做法是直接剔除。剔除后注意重新统计个数保持一致。4.5 置信度门限调高后框没了看看你用的是 P 还是 R 曲线这种现象出现在推理阶段训练时 mAP 看着不错但部署时把conf_thres从 0.25 调到 0.5 之后一张图上原本能框出的车牌全消失了。很多人第一反应是模型不行实际上可能是模型处于“低置信度高召回”的状态也就是它学到了非常保守的特征预测出的置信度普遍不高。YOLOv8 默认推理conf0.25这个门限对车牌检测来说偏松会有很多误检但调到 0.5 可能把真车牌也过滤掉了。解决方法是先跑一遍验证集把 precision-recall 曲线打出来看置信度在哪个区间可以让 precision 和 recall 达到一个可接受的平衡点。常见做法是写一段循环统计不同 conf 下的 P/R 值找到一个“框基本可信且不会漏太多”的阈值。这个阀值通常不是常规的 0.5而是 0.3 到 0.4 之间取决于你的训练数据和部署场景的相似程度。5. 数据增广与模型选型把 1019 张的价值榨干数据量就 1019 张模型性能的天花板很大程度上由数据增广和模型容量决定。很多人在这一步会犯两个极端要么完全不开增广导致模型严重过拟合要么把 mosaic、mixup、copy-paste 全量开满结果小模型学不动loss 震荡。正确姿势是先跑一个裸参数基线再根据过拟合程度逐步加增广。5.1 先跑基线再谈增广1019张什么时候会过拟合判断过拟合不需要等训练结束观察训练集和验证集 loss 的分离程度就行。在 YOLOv8 的输出目录里results.png里有两条 loss 曲线如果训练集 loss 持续下降、验证集 loss 在第 20 轮开始反弹基本就是过拟合了。1019 张数据在二三十轮之后出现这种分离是大概率事件区别只是严重程度。处理顺序很重要先用不做任何增广的默认参数跑一次确认数据本身没硬伤再开数据增广观察过拟合是否缓解。如果跳过基线直接调增广参数你根本分不清指标变化是因为增广起作用还是因为随机种子变了。判断过拟合也可以直接看推理结果训练集图片检测得很好换几张没见过的同场景图片就开始漏检。这说明模型记住了训练图的具体纹理而不是车牌共性。5.2 YOLOv8 内置增广参数怎么调mosaic、copy_paste、hsvYOLOv8 在训练时默认已经开了部分增广例如 mosaic、hsv 增强、随机平移和缩放。对小数据集来说最有用的三个是mosaic1.0把 4 张图拼成一张训练能显著增加单图里的目标数量也可以让模型看到更多样的背景。但 mosaic 对车牌这种小目标偶尔会带来截断问题如果发现小目标漏检可以降到 0.5。hsv_h、hsv_s、hsv_v对颜色做扰动车牌颜色本身是强特征不要扰动过大否则蓝牌会被调到偏紫。建议保持默认或把hsv_v降到 0.3。copy_paste把一张图里的目标复制粘贴到另一张图相当于白送样本。这个增广对车牌这类形体规整的目标效果不错但要注意如果场景里有重叠的车牌粘贴会制造错误标注。修改方法很简单可以在 CLI 里直接指定yolo detect train \ data/home/user/license_plate_data/data.yaml \ modelyolov8n.pt \ epochs100 \ batch16 \ imgsz640 \ mosaic0.8 \ copy_paste0.5 \ hsv_v0.3这些参数值是经验值不是固定标准。如果你发现验证集 mAP50 在加了增广后反而轻微下降不要慌可能只是模型在验证集上见过的等价样本变难了再继续多训几十轮往往能超过未增广的效果。5.3 选 yolov8n 还是 yolov8s按推理设备倒推参数模型选型最忌讳的是盲目追大。1019 张数据撑不起 yolov8x 这种大模型强行训练大概率在验证集上过拟合到无法用。常见选择是yolov8n和yolov8s不能说哪个绝对更好要看你的部署条件。模型参数量车牌小目标倾向训练显存需求推理速度适用场景yolov8n约 3.2M够用适合漏检率要求不极端的场景4GB 可跑快CPU 也能勉强推理移动端、嵌入式、实时视频流yolov8s约 11.2M稍好尤其对远距离小车牌建议 8GB 以上中速GPU 流畅服务端、抓拍机后处理选型建议很简单如果你部署在 Jetson Nano、树莓派或者低功耗 IPC 上直接选 n如果你有 GPU 服务器而且对检测精度有要求可以先从 s 开始试。用 s 训练 1019 张数据时batch 可以降到 8epochs 适当增加但 patience 要保留防止后期训练浪费。还有一个容易忽略的点无论选 n 还是 s都要在训练前统一imgsz和部署时的输入尺寸。训练用 640部署时却用 1280精度和速度都不会达到预期。6. 验证效果时最容易忽略的一个小动作按车牌形态分组测一次很多人在跑完训练、看到 mAP50 有 0.85 之后就觉得大功告成直接拿 best.pt 去做演示结果现场一测就翻车。原因很简单整体指标掩盖了场景差异。1019 张数据里可能大部分是白天正面拍摄的清晰蓝牌夜间、逆光、倾斜、双层货车牌这些极端样本数量少整体 mAP 会被“好样本”拉高。所以我习惯在最终验证时多做一步把测试图片按车牌形态分组分别统计精度和召回。分组的维度不用太复杂常见做法是分成“常规蓝牌”“绿牌/黄牌”“倾斜或模糊”“夜间或逆光”四组。如果手头没有这么多分类好的图也可以用一句话说清楚找 30 张普通干净的车牌图再找 30 张有明显遮挡、模糊、偏转的车牌图分别跑推理。from ultralytics import YOLO from pathlib import Path model YOLO(runs/plate_detect/baseline/weights/best.pt) groups { normal: Path(test_images/normal), hard: Path(test_images/hard), } for group_name, img_dir in groups.items(): total 0 detected 0 for img_path in img_dir.glob(*.jpg): result model.predict(str(img_path), conf0.35, verboseFalse) boxes result[0].boxes total 1 if boxes is not None and len(boxes) 0: detected 1 print(f{group_name}: {detected}/{total} {detected / total:.2%})这段代码不追求复杂就干一件事按分组统计检出率。conf0.35是前面排错后定的推理阈值你不用照抄先跑几次看看哪些图漏检再决定是提高阈值还是换更大模型。如果 normal 组 100% 检出但 hard 组只有 30%说明模型的泛化能力还撑不住实际环境这不是调参能解决的问题得考虑补充困难样本继续训练。分组验证这个习惯帮我避过好几次上线前的重大失误。印象最深的一次是模型整体 mAP 看起来很好上线前一天拿停车场夜间照片一测基本全漏才发现训练集里只有不到 10 张夜间图。从那以后我再也不只看单一指标而是先问自己这些测试图里最难的那部分占比多少希望这个分组验证的思路也能帮你少踩一次坑。验证完成之后如果你发现自己手里的这份车牌检测数据集本身偏科别急着删把读不懂的图片挑出来配合已知的模型短板去补充采集或爬取才是让模型真正具备落地能力的关键路径。祝你训练顺利希望帮到你。本文还有配套的精品资源点击获取