☰
YOLOv5数据集目录格式详解:从标注到训练一次搞定
2026/10/11 19:48:37 网站建设 项目流程

简介:面向智能小车物品识别与智能购买等目标检测场景,提供一套已按YOLOV5标准目录结构整理的数据集,共1777个文件,以jpg图片、txt标签为主,另含py脚本和png示例,包体约26.48MB。图像统一为416×416 RGB格式,标注为单类别purchase(工具箱),训练集含710张图片与711个txt标签,测试集含177张图片与177个txt标签,并附带类别字典文件,方便快速映射类别ID。针对此类数据集常因目录混乱、标签不统一而难以直接使用的痛点,数据包严格按train/val划分,同时随包附赠一个无需修改即可运行的Python可视化脚本,随机传入一张图片即可绘制边界框并保存到当前目录,便于逐一核对标注质量。资源下载后即可直接接入YOLOV5训练流程,省去格式转换与手工整理环节。该资源浏览/学习人数达157,适合需要快速获取规范格式数据集、用于课程设计、毕业设计或智能小车视觉识别算法实验的开发者,尤其适合作为项目前期验证或算法对比的基准数据。

1. 智能小车物品识别为什么要先学会包装 YOLOv5 目录格式

做智能小车物品识别的人,十有八九都卡在同一个地方:摄像头装好了、小车能跑了、甚至用现成权重能 detect 到几个物体,可一旦换成自己采集的“货架商品”“桌面杂物”这类场景,模型立刻失灵。原因往往不在模型,而在你根本没按 YOLOv5 的“规矩”把数据装进它规定的目录格式。判断任务用的目标检测数据集,不只是“一堆图片”,它必须同时包含图像、标签、划分清单和配置文件四样东西,而且标签文件要和图片同名、放在指定子目录、坐标归一化到 0~1。这个格式不满足,训练器轻则报错找不到标签,重则训练全程 loss 正常但 mAP 永远是 0。

这篇笔记写给两类人:一是给智能购物车、巡检小车做物品识别,准备自己采集数据训练的工程师;二是想用 YOLOv5 训练自己的数据集,但被网上各种“数据集打包下载”搞晕的新手。我会按“格式拆解 → 数据构建 → 清洗检查 → 常见翻车 → 部署验证”的顺序,把目录格式的每个坑都摊开讲,最终让你能独立做出一个可以交付给训练脚本的数据集,而不是拿着图片和 XML 发愁。

2. YOLOv5 数据集目录格式拆解:images 与 labels 的对应关系、三组文件

2.1 标准目录树与 train/val/test 划分

YOLOv5 的数据集目录结构,不是随便建一个文件夹把图片丢进去就行。它要求一组严格的对称目录:images下面分train、val、test,labels下面同样分train、val、test,且两个目录下的文件一一对应。常见做法是先在工程根目录下建datasets文件夹,然后按下面的结构组织:

datasets/ ├── smart_cart/ # 数据集名称,自己起 │ ├── images/ │ │ ├── train/ │ │ │ ├── 001.jpg │ │ │ └── 002.jpg │ │ ├── val/ │ │ │ └── 003.jpg │ │ └── test/ # test 非必须,但建议留一份 │ ├── labels/ │ │ ├── train/ │ │ │ ├── 001.txt │ │ │ └── 002.txt │ │ ├── val/ │ │ │ └── 003.txt │ │ └── test/ │ └── data.yaml # 必须放在这个层级或任意可访问位置

这里的关键是images和labels的目录深度必须一致,而且里面文件名(不含扩展名)必须完全一样。YOLOv5 训练时靠文件名去匹配图片和标签,不会自己去猜哪个文件夹对应哪个。所以我在做智能购物车识别数据集时,总是让标注工具直接输出 txt 格式,然后用脚本把 jpg/txt 同时挪进对应子目录,尽量避免手动重命名。

test文件夹不是必须的。官方默认用val来评估模型,test仅在你需要单独对最终模型做一次无偏评估时才有意义。如果你只有少量样本,可以不建test,但train和val必须严格分开,因为验证集的真实性直接影响你判断模型有没有过拟合。

2.2 labels 里的 YOLO 坐标怎么算:从 VOC 框到 txt 坐标的换算

YOLO 的标签格式是.txt文件,每行一项,每项包含五个数字:

class_id x_center y_center width height

它们全部是归一化坐标,取值范围在 0~1 之间。class_id从 0 开始计数,x_center和y_center是目标框中心点相对于图片宽高的比例,width和height是框宽高相对于图片宽高的比例。比如一张 640×480 的图片里有一个苹果,它的实际边界框是x_min=160, y_min=120, x_max=320, y_max=240,那么归一化后就是:

x_center = ((160 + 320) / 2) / 640 = 0.375 y_center = ((120 + 240) / 2) / 480 = 0.375 width = (320 - 160) / 640 = 0.25 height = (240 - 120) / 480 = 0.25

对应 txt 里的一行就是0 0.375 0.375 0.25 0.25。很多人手动算错,是因为把x_center写成了(x_min + x_max) / 2而忘了除以图片宽度,结果坐标全部大于 1,训练时 loss 会在 NaN 附近反复横跳。我的建议是不要手算,写一个几行的小脚本批量转,见下一章。

如果你拿到的标注是 VOC 的 XML 格式,它的坐标是绝对像素值x_min, y_min, x_max, y_max。转换时要注意 XML 里的坐标是整数,但转换后的 YOLO 坐标是浮点数,不能取整,否则小目标的框会偏移好几个像素,对智能小车识别这种近距离场景影响尤其明显。

2.3 data.yaml 的写法与两个常见坑

data.yaml是 YOLOv5 训练时读取的唯一配置入口。它的写法很简单,但犯错的频率极高:

# data.yaml path: ../datasets/smart_cart # 数据集根目录,建议用相对路径或绝对路径 train: images/train # 训练图片相对于 path 的目录 val: images/val # 验证图片相对于 path 的目录 test: images/test # 可选 nc: 5 # 类别数量,必须和 labels 里的 class_id 最大值 + 1 一致 names: ['apple', 'bottle', 'box', 'cup', 'can'] # 类别名列表,索引对应 class_id

第一个常见坑是path写成 Windows 风格的反斜杠路径,比如path: D:\datasets\smart_cart。YOLOv5 内部用的是Path对象,在 Linux 或者 WSL 下反斜杠会被当成转义符,导致找不到图片。统一改成正斜杠/或者直接用当前工作目录的相对路径最稳。

第二个常见坑是nc和names的长度/顺序不匹配。YOLOv5 在读标签时会直接用int(line[0])作为类别索引,如果你的标签里出现了class_id=5,但nc只写了 5(意味着最大索引是 4),训练会报IndexError或者把第五类静默丢弃。我一般会在训练前加一句检查:扫描所有 txt 文件,取最大的class_id,然后和nc比对,不等就报错。

3. 从零构建自己的物品识别数据集:采集、标注、格式校验三步走

3.1 采集策略:真实场景比“干净”背景更重要

智能小车物品识别和通用目标检测有个显著差异:相机永远在运动,光照、角度、背景都在变化。很多人第一次做数据集时喜欢把物品摆在桌子正中央、用白色背景、固定角度拍一堆照片,结果训练出来的模型一到实际小车上就翻车,因为它的“泛化”被干净背景骗了。正确做法是模拟小车真实运行环境:让物品出现在货架、桌面、地面不同高度;手持摄像头边走边拍;故意引入部分遮挡、反光、模糊。数量上,每类物品至少 300 张,最好 500 张以上;如果时间不够,可以用同类的公开数据集做预训练,再用自己的数据进行微调。

采集时还要注意分辨率和尺寸。YOLOv5 训练时默认超参数里img_size=640,如果你的原始图片分辨率远大于这个值,比如 4000×3000,训练时会直接 resize,小目标的标注框可能被压得只剩几个像素,导致模型学不到特征。所以采集时建议把相机输出设为 1280×720 或 1920×1080,训练时img_size=640也够用。

3.2 标注工具选择与导出的关键参数

现在标注工具选择很多:LabelImg、Labelme、Roboflow、X-AnyLabeling 等。对于 YOLOv5 目录格式,我偏向直接使用支持“YOLO 格式导出”的工具。其中开源免费的 LabelImg 最常用,但要注意它保存的文件后缀是.txt,格式与 YOLO 完全一致;而 Labelme 默认保存为 JSON,需要额外转换。

如果你用 LabelImg,打开软件后第一件事是在“PascalVOC”和“YOLO”之间选择。很多人忽略了这个开关,标注了一下午,导出后全是 XML,又得二次转换。另一个关键是类别名称定义。LabelImg 的classes.txt顺序直接决定class_id,比如第一行是apple,那苹果就是 0。这个顺序不能之后随便改,否则整个数据集的标签索引全乱。

标注框的精度。拿到图片后不要为了节省时间把框画得过大或过小。过大把背景包进来,模型会学错纹理;过小漏掉边缘,模型预测位置偏差。我一般让框贴着目标轮廓,留 2~3 像素的余量即可。对智能小车而言,远距离的小物品要尽量放大图像再标注,否则width和height变成 0.01 量级,训练时容易丢失。

3.3 用脚本一次性完成目录生成、文件重命名与标签归一化

当标注完成后,你手上通常是一堆 jpg 加一堆 txt 或 xml,文件名各不相同,散落在若干文件夹里。手动整理很痛苦,而且容易出错。我会用一个 Python 脚本把这些事一次性做完:创建目录结构、重命名文件、把 VOC XML 转成 YOLO txt、划分 train/val、生成 data.yaml。下面是一个可以直接拿去改的模板:

# prepare_dataset.py import os import shutil import random import glob import xml.etree.ElementTree as ET # ---------- 参数区 ---------- source_images = 'raw_images' # 原始图片文件夹 source_labels = 'raw_labels' # 原始标签文件夹(xml 或 txt) output_root = 'datasets/smart_cart' # 如果你的标注是 YOLO txt 格式,把 voc_mode 设为 False voc_mode = True class_names = ['apple', 'bottle', 'box', 'cup', 'can'] train_ratio = 0.8 val_ratio = 0.2 random_seed = 42 # ---------- 参数区结束 ---------- random.seed(random_seed) def voc_to_yolo(xml_file, img_w, img_h): tree = ET.parse(xml_file) root = tree.getroot() boxes = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_names: continue class_id = class_names.index(name) bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) # 归一化到 0~1,注意除以真实宽高 x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h # 防止越界,把坐标限制在 0~1 之间 x_center = min(max(x_center, 0), 1) y_center = min(max(y_center, 0), 1) width = min(max(width, 0), 1) height = min(max(height, 0), 1) boxes.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") return boxes # 创建目录结构 for split in ['train', 'val', 'test']: os.makedirs(f"{output_root}/images/{split}", exist_ok=True) os.makedirs(f"{output_root}/labels/{split}", exist_ok=True) # 收集所有图片和对应标注 image_files = glob.glob(f"{source_images}/*.jpg") + glob.glob(f"{source_images}/*.jpeg") + glob.glob(f"{source_images}/*.png") random.shuffle(image_files) num_images = len(image_files) num_train = int(num_images * train_ratio) num_val = int(num_images * val_ratio) for idx, img_path in enumerate(image_files): if idx < num_train: split = 'train' elif idx < num_train + num_val: split = 'val' else: split = 'test' # 统一图片名和标签名 base_name = f"{idx:06d}" img_ext = os.path.splitext(img_path)[1] new_img_name = f"{base_name}{img_ext}" new_label_name = f"{base_name}.txt" shutil.copy(img_path, f"{output_root}/images/{split}/{new_img_name}") # 处理标签 label_path = os.path.join(source_labels, os.path.splitext(os.path.basename(img_path))[0] + ('.xml' if voc_mode else '.txt')) if not os.path.exists(label_path): print(f"警告:{img_path} 没有对应标签,跳过") continue if voc_mode: # 读取图片尺寸。这里用 PIL 读取,实际生产环境可以用 OpenCV 加速 from PIL import Image img = Image.open(img_path) w, h = img.size yolo_lines = voc_to_yolo(label_path, w, h) else: # 如果已经是 YOLO txt,直接复制,但不检查坐标范围 with open(label_path, 'r') as f: yolo_lines = [line.strip() for line in f.readlines()] with open(f"{output_root}/labels/{split}/{new_label_name}", 'w') as f: f.write("\n".join(yolo_lines) + "\n") # 生成 data.yaml with open(f"{output_root}/data.yaml", 'w') as f: f.write(f"path: {output_root}\n") f.write(f"train: images/train\n") f.write(f"val: images/val\n") if num_images - num_train - num_val > 0: f.write(f"test: images/test\n") f.write(f"nc: {len(class_names)}\n") f.write(f"names: {class_names}\n") print(f"完成:共处理 {num_images} 张图片,训练 {num_train},验证 {num_val},测试 {num_images - num_train - num_val}")

这段脚本的逻辑很直白:先把所有图片打乱并划分到三个子集,再根据图片名找对应的标注文件。voc_mode=True时,从 XML 里读出目标框并用图片真实宽高做归一化,坐标的小数点保留 6 位;如果已经是 YOLO txt,则直接复制。最后生成的data.yaml里path我写的是绝对路径,你可以改成相对路径。

几个参数要留意:random_seed固定后,每次运行划分结果一致,方便复现;class_names的顺序必须和标注时一致,否则class_id会错乱;val_ratio一般取 0.1~0.2,如果数据不够多,宁可从训练集里匀一点出来给验证集,也不要验证集只有几十张,那样评估结果抖动很大。还有一点,脚本里对坐标做了 clamp 到 0~1 的处理,这是为了防止 XML 里有超出图片边界的框;但如果你手头的标注本来就漂移严重,这个 clamp 会把错误“掩盖”起来,所以之后还要做第 4 章的可视化检查。

4. 训练前必做的数据清洗与检查:避免模型“学了个寂寞”

4.1 用 out-of-range 检查框坐标异常

训练脚本不报错,不代表你的数据没问题。最常见的“隐性毒药”是标签坐标越界:x_center大于 1、width是负数、某个框的height是 0.0 等。YOLOv5 在读取标签时没有严格的校验,这些坏数据进入训练后,会让 loss 曲线剧烈震荡,或者直接导致某些类别 mAP 掉到 0。所以训练前一定要跑一次全量扫描:

# check_labels.py import os import glob label_dirs = ["datasets/smart_cart/labels/train", "datasets/smart_cart/labels/val", "datasets/smart_cart/labels/test"] issues = [] for label_dir in label_dirs: if not os.path.exists(label_dir): continue for txt_file in glob.glob(f"{label_dir}/*.txt"): with open(txt_file, 'r') as f: for line_no, line in enumerate(f.readlines(), 1): parts = line.strip().split() if len(parts) != 5: issues.append(f"{txt_file}:{line_no} 字段数不为5, 内容: {line}") continue try: class_id, x_center, y_center, width, height = map(float, parts) except ValueError: issues.append(f"{txt_file}:{line_no} 非数字, 内容: {line}") continue if class_id < 0 or class_id != int(class_id): issues.append(f"{txt_file}:{line_no} 类别索引异常: {class_id}") if not (0 < x_center < 1 and 0 < y_center < 1): issues.append(f"{txt_file}:{line_no} 中心点越界: {line}") if width <= 0 or height <= 0 or width > 1 or height > 1: issues.append(f"{txt_file}:{line_no} 框尺寸异常: {line}") if issues: print(f"发现 {len(issues)} 个问题:") for issue in issues[:20]: print(issue) else: print("所有标签坐标都在合法范围内")

这段脚本会打印出所有不合格的标签行。注意class_id检查里我用了class_id != int(class_id),因为 YOLOv5 期望类别是整数,但标签文件可能混入浮点数如1.5,这会让训练时索引崩溃。

这类检查还有一个容易被忽略的对象:width或height极小,比如小于0.005。从数值看合法,但实际物体在图片上只有 3 个像素宽,训练时会被下采样丢掉。如果你的数据集里有大量这种小框,就算标签合法,模型也基本学不到。建议同时统计一下框尺寸分布,过滤掉明显异常的小目标。

4.2 类别平衡与空标签文件排查

智能小车物品识别最容易出现类别不平衡。比如你拍了 600 张可乐瓶,但 只有 80 张苹果,模型最终会偏向可乐瓶,对苹果的召回率很低。解决办法有三个方向:一是补拍少的类别;二是用复制粘贴增强(把目标物体粘贴到多种背景上);三是调整 YOLOv5 训练参数,比如class_weights或使用更小的hyp['cls']值。但在动手之前,你得先知道你的数据到底平不平衡。写个脚本统计一下:

# 统计每个类别在 train/val 中出现多少次 find datasets/smart_cart/labels/train -name "*.txt" -exec cat {} + | awk '{count[$1]++} END {for (c in count) print "class", c, ":", count[c]}' | sort -n -k3

如果某一类占比不足 10%,建议先补数据,不要急着调超参数。空标签文件也要排查:图片存在但对应的 txt 文件是空的,或者根本没有 txt。txt文件为空意味着这张图片被视为“背景”,YOLOv5 会用它训练负样本,但如果val集里空文件过多,会导致验证集的 mAP 计算时没有正样本,结果看不懂。检查空文件用一条命令即可:

find datasets/smart_cart/labels/train -name "*.txt" -empty -print

4.3 可视化验证:把标注画回图像上的最快办法

坐标、类别、边界都检查完之后,最后一步、也是最重要的一步:把标签可视化画到原图上,人眼检查。这一步能发现脚本检查不出来的问题,比如框和语义不匹配、类别标错、物体遮挡后被画了大框、两张图的文件名错位导致标签张冠李戴等。最快的方式是用 YOLOv5 自带的detect.py --source配合训练好的模型,但那是训练之后的事;训练前可以用下面的 OpenCV 脚本抽查:

# visualize_boxes.py import cv2 import glob import os image_dir = "datasets/smart_cart/images/val" label_dir = "datasets/smart_cart/labels/val" class_names = ['apple', 'bottle', 'box', 'cup', 'can'] image_files = sorted(glob.glob(f"{image_dir}/*.jpg"))[:10] # 抽查前 10 张 for img_path in image_files: img = cv2.imread(img_path) h, w = img.shape[:2] txt_path = os.path.join(label_dir, os.path.splitext(os.path.basename(img_path))[0] + ".txt") if not os.path.exists(txt_path): continue with open(txt_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() if len(parts) != 5: continue class_id = int(float(parts[0])) x_center = float(parts[1]) * w y_center = float(parts[2]) * h box_w = float(parts[3]) * w box_h = float(parts[4]) * h x_min = int(x_center - box_w / 2) y_min = int(y_center - box_h / 2) x_max = int(x_center + box_w / 2) y_max = int(y_center + box_h / 2) cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) cv2.putText(img, class_names[class_id], (x_min, y_min - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) out_path = os.path.join("visual_check", os.path.basename(img_path)) os.makedirs("visual_check", exist_ok=True) cv2.imwrite(out_path, img) print(f"已保存 {out_path}")

这个脚本把归一化坐标还原成像素坐标,画框和类别名后保存到visual_check目录。class_names必须和训练用的一致,否则显示错乱。我通常会把train和val各抽查 30 张,重点看边缘:目标是否被裁剪、框是否压到了旁边物体、同一类在不同角度下标注是否稳定。如果抽查中发现某张图片的框明显偏移,就回头检查对应的图片文件名是否和标签文件名错位了。这一步做完,你的数据集才算真正“干净”。

5. 避坑/常见问题:YOLOv5 目录格式里最容易翻车的 5 个细节

5.1 现象:训练 loss 正常但 mAP 一直是 0

这是新手最容易遇到的情况。训练了十几个 epoch,loss 在下降,但每次验证结束,mAP@0.5 显示 0,或者只有 0.001。原因几乎可以锁定在标签类别索引和data.yaml的names不匹配。比如你的物体是“可乐”,标注时在 LabelImg 里是第 2 个类,导出后 txt 里class_id=1,但data.yaml的names里第 1 个名字写成了“雪碧”,模型预测时会去找“雪碧”对应的正样本,自然匹配不上。解决办法是把names顺序调成和标注工具classes.txt完全一致,然后重新生成data.yaml。

另一种少见但真实的原因:val集里的标签文件全部为空,或者所有标签文件名与图片名不同步。检查方法就是上面的可视化脚本,多抽查几张val图,如果有图无框,那就是数据划分脚本有 bug。

5.2 现象:val 阶段报“Image not found”或找不到标签

报错信息里明确写着Image not found,但图片明明就在目录里。这通常因data.yaml里的path、train、val三者的拼接结果不对。YOLOv5 会执行path + train来定位图片,如果你的path: datasets/smart_cart没带斜杠,而train: images/train也没带前导斜杠,最终路径可能是datasets/smart_cartimages/train,少了个/。解决方法是统一使用path: datasets/smart_cart/尾部带斜杠,或者train: /images/train前带斜杠,我习惯前者。

另外,如果你是从 Windows 把整个数据集拷到 Linux 服务器,注意文件名大小写问题。Windows 不区分JPG和jpg,Linux 区分。我在实际项目里吃过亏:明明本地训练没问题,传到 Linux 服务器,因为标签文件是.JPG而图片是.jpg,所有标签全部匹配不上。所以数据集在生成时就统一改成小写后缀。

5.3 现象:自己标注的框偏移严重,模型预测位置偏

训练完成后,用detect.py测试,发现框总是往左上或右下偏移半截。这个问题的根源大多不在训练,而在标注质量。很多人用 LabelImg 时,为了让框更好看,会刻意把四个边贴得非常紧,甚至压到物体边缘;但 YOLO 回归的是中心点和宽高,它学习的是“框应该往哪放”,如果你的标注框中心偏离目标重心,它就会学偏。

解决办法:第一,标注时不要只凭肉眼“觉得”对称,可以打开网格线辅助;第二,训练前做可视化检查,如果发现同一物体在不同图片里框的位置变动很大,说明标注风格不稳定,要重新标注;第三,检查是不是图片 EXIF 旋转导致。手机拍摄的 JPG 经常带旋转信息,OpenCV 读取时不会自动转正,导致你标注时看到的图片和训练 feed 进去的图片实际相差 90 度。可以用cv2.imread后检查img.shape的宽高比,对比原图资源管理器中看到的宽高,不一致就说明旋转问题存在。处理方式是先把所有图片统一转正再标注,或者在读取时禁用 EXIF 自动旋转。

5.4 现象:类别数量变了,但 model.names 没改

这个问题主要出现在用别人训练好的权重继续微调时。你下载了一个coco.pt的预训练权重,然后直接用自己的数据集训练,但没有修改模型的类别数。YOLOv5 会从data.yaml读取nc,自动调整检测头,但如果你的数据集是 5 个类,而预训练权重是 80 个类,且你用的是yolov5s.pt这个官方权重,会自动适配,只是会看到提示“Transferred 345/362 items from pretrained weights”。这个提示是正常的。但如果你复制了一份别人改过的配置文件,比如把nc: 80写死,就会报维度不匹配。排查方法:训练日志里看model: nc=5是不是对的,以及是否出现类似RuntimeError: size mismatch的报错。

另外还有一类更隐蔽的:你只训练了一个类,代码里names是['person'],但验证时用了别的工具的混淆矩阵脚本,脚本里硬编码了 80 类的 COCO 名字,导致可视化结果全乱。这不算 YOLOv5 的问题,但我会在推理脚本中始终从data.yaml动态读names,避免写死。

5.5 现象:换电脑后相对路径失效

你在自己电脑上训练一切正常,把datasets文件夹复制到另一台机器上,运行同样的训练命令,结果找不到数据集。原因很简单:data.yaml里的path写的是你第一台机器的绝对路径,比如/home/user/project/datasets/smart_cart,换机器后这个路径不存在了。我一开始也喜欢写绝对路径,因为省事,但协作项目里这就是一颗定时炸弹。后来养成了习惯:data.yaml统一用相对路径,并且让 YOLOv5 的--data参数指向data.yaml的绝对位置,而path留空或写../datasets/smart_cart。这样整个工程文件夹移动后,只用修改一个启动脚本里的工作目录,其他都不用动。

如果你已经用了绝对路径,最稳妥的办法是检查后重新生成一次data.yaml,别手动编辑器改,容易漏掉斜杠。配合第 3 章那个生成 yaml 的 Python 脚本,把output_root改成相对目录即可。

6. 进阶:把 YOLOv5 训练好的模型部署到智能小车上:验证与性能调优

训练完的.pt权重不能直接塞进小车。常见部署流程是把模型导出为 ONNX 或 OpenVINO,再量化到 INT8,尤其是树莓派或 Jetson 这类边缘设备。导出命令很简单,但有两处要调:一是--img-size要和训练时一致,二是--dynamic是否开启。对于固定分辨率的小车相机,我建议关闭动态 batch,尺寸固定为 640,推理速度能提升不少。

cd yolov5 python export.py --weights runs/train/smart_cart/weights/best.pt --include onnx --img-size 640

导出后先用detect.py验证 ONNX 和 pytorch 输出是否一致,再在小车上通过 OpenVINO Runtime 或 onnxruntime 加载。这里最容易踩坑的是阈值。默认conf_thres=0.25,在实验室环境没问题,但小车上画面抖动、光照不稳,置信度会普遍降低,导致漏检。我一般把conf_thres降到 0.2 或 0.15,同时把iou_thres从 0.45 调到 0.5,减少遮挡场景下相邻框的误抑制。这个调整最好在实车测试时用遥控滑条动态观察,不要一次定死。

从数据集角度反推漏检,是很多人忽略的。如果你的小车对某种物品总是漏检,先别急着改模型结构,回看该类别在训练集里的样本数量、拍摄角度、背景多样性。我有一回做购物车识别,detect.py在测试视频里漏掉了七八个远处的罐头,后来翻了数据集发现所有罐头照片都离镜头很近,几乎没有远距离样本。补拍了一批距离 2~3 米的照片重新训练,召回率立刻上来。这给我的教训是:数据集决定的性能上限,模型只能去逼近这个上限。已经测试通过的数据集,一定要把样本分布、标注版本、data.yaml一起存档,否则三个月后你根本猜不到当前模型是基于哪批数据训出来的。我从那以后每个版本都会把可视化抽查的缩略图存在数据集目录里,为的就是回看时间况判断到底改变了什么。希望这套目录格式的整理方法能让你少走我当初的弯路,也帮你的智能小车在真实场景里多稳住几个 epoch。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询