YOLOv5数据集目录格式详解:从标注到训练一次搞定
简介面向智能小车物品识别与智能购买等目标检测场景提供一套已按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、testlabels下面同样分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_min160, y_min120, x_max320, y_max240那么归一化后就是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_id5但nc只写了 5意味着最大索引是 4训练会报IndexError或者把第五类静默丢弃。我一般会在训练前加一句检查扫描所有 txt 文件取最大的class_id然后和nc比对不等就报错。3. 从零构建自己的物品识别数据集采集、标注、格式校验三步走3.1 采集策略真实场景比“干净”背景更重要智能小车物品识别和通用目标检测有个显著差异相机永远在运动光照、角度、背景都在变化。很多人第一次做数据集时喜欢把物品摆在桌子正中央、用白色背景、固定角度拍一堆照片结果训练出来的模型一到实际小车上就翻车因为它的“泛化”被干净背景骗了。正确做法是模拟小车真实运行环境让物品出现在货架、桌面、地面不同高度手持摄像头边走边拍故意引入部分遮挡、反光、模糊。数量上每类物品至少 300 张最好 500 张以上如果时间不够可以用同类的公开数据集做预训练再用自己的数据进行微调。采集时还要注意分辨率和尺寸。YOLOv5 训练时默认超参数里img_size640如果你的原始图片分辨率远大于这个值比如 4000×3000训练时会直接 resize小目标的标注框可能被压得只剩几个像素导致模型学不到特征。所以采集时建议把相机输出设为 1280×720 或 1920×1080训练时img_size640也够用。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_okTrue) os.makedirs(f{output_root}/labels/{split}, exist_okTrue) # 收集所有图片和对应标注 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(fpath: {output_root}\n) f.write(ftrain: images/train\n) f.write(fval: images/val\n) if num_images - num_train - num_val 0: f.write(ftest: images/test\n) f.write(fnc: {len(class_names)}\n) f.write(fnames: {class_names}\n) print(f完成共处理 {num_images} 张图片训练 {num_train}验证 {num_val}测试 {num_images - num_train - num_val})这段脚本的逻辑很直白先把所有图片打乱并划分到三个子集再根据图片名找对应的标注文件。voc_modeTrue时从 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 -print4.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_okTrue) cv2.imwrite(out_path, img) print(f已保存 {out_path})这个脚本把归一化坐标还原成像素坐标画框和类别名后保存到visual_check目录。class_names必须和训练用的一致否则显示错乱。我通常会把train和val各抽查 30 张重点看边缘目标是否被裁剪、框是否压到了旁边物体、同一类在不同角度下标注是否稳定。如果抽查中发现某张图片的框明显偏移就回头检查对应的图片文件名是否和标签文件名错位了。这一步做完你的数据集才算真正“干净”。5. 避坑/常见问题YOLOv5 目录格式里最容易翻车的 5 个细节5.1 现象训练 loss 正常但 mAP 一直是 0这是新手最容易遇到的情况。训练了十几个 epochloss 在下降但每次验证结束mAP0.5 显示 0或者只有 0.001。原因几乎可以锁定在标签类别索引和data.yaml的names不匹配。比如你的物体是“可乐”标注时在 LabelImg 里是第 2 个类导出后 txt 里class_id1但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和jpgLinux 区分。我在实际项目里吃过亏明明本地训练没问题传到 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: nc5是不是对的以及是否出现类似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_thres0.25在实验室环境没问题但小车上画面抖动、光照不稳置信度会普遍降低导致漏检。我一般把conf_thres降到 0.2 或 0.15同时把iou_thres从 0.45 调到 0.5减少遮挡场景下相邻框的误抑制。这个调整最好在实车测试时用遥控滑条动态观察不要一次定死。从数据集角度反推漏检是很多人忽略的。如果你的小车对某种物品总是漏检先别急着改模型结构回看该类别在训练集里的样本数量、拍摄角度、背景多样性。我有一回做购物车识别detect.py在测试视频里漏掉了七八个远处的罐头后来翻了数据集发现所有罐头照片都离镜头很近几乎没有远距离样本。补拍了一批距离 2~3 米的照片重新训练召回率立刻上来。这给我的教训是数据集决定的性能上限模型只能去逼近这个上限。已经测试通过的数据集一定要把样本分布、标注版本、data.yaml一起存档否则三个月后你根本猜不到当前模型是基于哪批数据训出来的。我从那以后每个版本都会把可视化抽查的缩略图存在数据集目录里为的就是回看时间况判断到底改变了什么。希望这套目录格式的整理方法能让你少走我当初的弯路也帮你的智能小车在真实场景里多稳住几个 epoch。本文还有配套的精品资源点击获取