公司动态
小样本编织袋数据集如何训练YOLOv8目标检测模型?600张图全流程解析
简介目标检测模型的落地离不开高质量的数据集然而在实际工业场景中标注数据往往稀缺且获取成本高昂。数据增强与迁移学习是解决小样本训练难题的关键手段。以仓储物流中的编织袋识别为例其表面纹理复杂、堆叠形态多变传统视觉方案难以应对。整套流程基于600余张标注图像从数据采集、标注规范到YOLOv8模型训练与优化完整展示了一条从零构建可用检测模型的技术路径并总结了训练过程中的典型踩坑问题与调优经验。 最近接了个朋友的需求说想做一个自动清点编织袋数量的系统用于仓库出货统计。我第一反应是这东西直接用人眼不就行了后来才明白一条流水线一小时过几千个袋子靠人盯着摄像头数眼睛根本扛不住而且编织袋堆叠在一起时轮廓极其模糊传统的视觉方案几乎无从下手。所以需求落到深度学习上第一步就需要一批评注好的编织袋图像数据。我把手头这批已经分类标注好的编织袋图像数据集整理了 出来总共600多张数量虽然不算夸张但对于编织袋这种背景相对单一、类别较固定的目标来说完全够跑通一个可用的检测模型也够做算法验证和迁移学习的底子。这篇文章就把这套数据集从采集思路、标注规范、目录结构到基于YOLOv8训练一个可用的编织袋检测模型的完整流程都写清楚。如果你也在做类似的工业物料识别项目或者对小样本数据集如何做出能用的模型这个话题感兴趣可以照着这套流程走一遍很多坑我都替你先踩过了。1. 为什么做编织袋识别这个数据集的定位和价值1.1 编织袋在视觉任务里到底难在哪编织袋这东西远看就是个有颜色的矩形块但真正让算法工程师头疼的地方在于它的纹理和形变。塑料编织袋表面是经纬交织的聚丙烯扁丝光照打上去会产生周期性的高光和暗纹这种纹理在图像里很容易被当成噪声或者边缘信号。更麻烦的是袋子里装的东西不同表面形态完全不一样装大米的袋子鼓鼓囊囊边缘有大量褶皱装化肥的袋子堆叠后彼此之间几乎没有缝隙矩形框一旦画得不够紧IoU计算就会非常吃亏。我做这批数据的时候发现很多初次接触编织袋识别的朋友会拿通用物体检测数据集的经验往里套一上来就搞几百个类别结果模型练完连袋子都框不准。实际上编织袋识别的核心难点不在类别多而在形状的可变性同一袋大米放在明亮灯光下和放在昏暗角落视觉特征差异很大。1.2 600多张标注图用于什么场景这批数据集定位非常明确支持单类或多类的编织袋目标检测。所谓已经标注分类意思是每张图里出现的编织袋都有对应的矩形边界框坐标和类别标签。基于这批数据可以做三件事直接训练YOLO系列检测器实现实时编织袋检测作为预训练后的微调数据集在已有通用模型基础上快速迁移到编织袋场景用于验证传统图像处理与深度学习结合方案的效果比如先用颜色分割提取候选区域再用检测模型确认。从实际项目角度看600张图不是用来冲击SOTA的而是用来解决从0到1的问题。很多工业场景里你连一张带标签的编织袋图都没有算法验证根本没法启动。先用600张把检测流程跑通形成初步可用的模型再在真实场景里采集更多难例、扩充边界情况这是更务实的路径。1.3 数据集的格式与标注粒度标注格式我采用的是YOLO通用的TXT格式每张图片对应一个同名TXT文件文件里的每一行代表一个目标框核心信息是类别编号 中心点x坐标 中心点y坐标 框宽 框高坐标值都是相对于图片宽高的归一化数值范围在0到1之间。这个格式的好处是主流检测框架都是直接支持不需要额外转换。标注粒度上我要求所有标框都紧贴编织袋的可见轮廓不把背景包进去也不把堆叠的下层袋子漏掉。# 示例一张含两个编织袋的图片对应的标注内容 0 0.453125 0.682292 0.265625 0.473958 0 0.776042 0.567708 0.218750 0.437500类别的粒度问题我当时纠结过是只标一个大类编织袋还是按颜色、用途细分成白色编织袋黄色编织袋化肥袋等多个类别最终选择了按用途和表面特征分成3个类别这样既能满足分类的要求又不会因为类别过碎导致样本不均衡。如果你想做细颗粒度的颜色分类这个数据也可以作为底子继续扩展。2. 600张图从哪来采集、清洗与标注规范2.1 采集方案的三种来源这批图像的来源主要有三部分。第一部分是真实的仓库和物流场景拍摄用手机和工业相机在不同光照、不同摆放角度下拍摄编织袋堆垛、单袋、散落的图片这部分最接近实际应用环境。第二部分是实验室条件下摆拍的样本把袋子平铺、折皱、侧放各拍几张用来增加形态多样性。第三部分是从开源数据集和网络图片中筛选出的编织袋图像这部分的筛选工作比较耗时淘掉了大量重复、模糊、画面主体过小的图片。我在采集时特别注意了分辨率的问题全部图片统一长边在1280像素以上。为什么要强调这个因为检测模型对输入尺寸有固定要求比如YOLOv8训练时常设为640或1280如果原图分辨率太低缩放后袋子边缘的纹理信息会丢失标注框也会显得边界模糊模型很难学到有效的边缘特征。2.2 清洗阶段淘汰了哪些图采集回来的原始图片数量比600张要多不少清洗阶段我淘汰了三类图第一类是严重过曝或欠曝的图。编织袋在高光下会呈现大片白色区域扁丝纹理完全消失这种图喂给模型只会让模型把高光白色块当成编织袋特征。这类图全部剔除不能靠后期调整亮度救回来。第二类是袋子占比过小的图也就是画面里10米外拍到的编织袋。虽然目标检测理论上支持小目标但在训练集里小目标框过多会严重拉低整体表现因为正负样本的比例失衡。这类图留给推理阶段的切片策略处理不放进训练集。第三类是堆叠严重导致边界无法辨认的图。如果人眼都无法通过纹理和颜色区分两个袋子的边界标注时只能凭感觉画框模型训练时会收到大量互相矛盾的标签信息。2.3 标注工具与团队协作配置标注工具我用的是LabelImg虽然它界面朴素了些但胜在轻量、免费、开箱即用。在标注前先做好三件事在项目目录下新建classes.txt文件按顺序写入所有类别名称每行一个。这个顺序必须和后面训练时YOLO配置文件的类别顺序保持一致否则标签编号会全部错位。设置标注输出格式为YOLO确保保存的是TXT文件而不是Pascal VOC的XML格式。要求所有标注人员使用同一台显示器和相同的缩放比例避免显示不一致导致框边缘对不齐。实际标注时的操作细节要注意每个编织袋只标它的主体部分不包含捆扎绳、袋口延伸出来的塑料条。如果两个袋子紧密贴合即使中间没有肉眼可见的分界线也要根据纹理走向判断分割标出两个独立的框。我最初就让标注员把贴合的两个袋子标成一个框结果训练出来的模型把所有并排的袋子都融合识别了。标注完成后我写了一个简单的校验脚本扫描所有标注文件检查是否存在越界坐标比如中心点加半宽超过1.0、负宽度、空标注等情况。这一步非常关键因为人工标注难免手滑而这些脏数据会直接导致训练时loss变成nan。import os def check_labels(img_dir, label_dir): bad_files [] for txt_name in os.listdir(label_dir): if not txt_name.endswith(.txt): continue img_name txt_name.replace(.txt, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): bad_files.append(fmissing image: {img_name}) continue with open(os.path.join(label_dir, txt_name), r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: bad_files.append(fbad format: {txt_name}) break _, cx, cy, w, h map(float, parts) if w 0 or h 0 or cx 0 or cx 1 or cy 0 or cy 1: bad_files.append(finvalid bbox: {txt_name}) break return bad_files3. 小数据集的底气增强策略和我在albumentations里的常用组合3.1 小数据集为什么更需要增强600张原始图片直接训练YOLOv8n结果大概率是过拟合训练集loss一路下降到很低验证集mAP却纹丝不动甚至出现每张图都在框画面里某个固定颜色区域的极端情况。原因很简单模型参数数量远超有效样本数量它会把训练集里的偶然特征当成通用规律。数据增强的作用不是增加样本量这么简单而是让模型看到同一个目标在不同形态下的样子相当于给它做视觉不变量学习。编织袋这个物体有很强的颜色和纹理先验所以增强策略必须围绕这两个维度设计。3.2 我常用的增强组合与参数这是我在实际项目中验证过的一套组合用albumentations库实现直接可用import albumentations as A from albumentations.pytorch import ToTensorV2 train_transform A.Compose([ A.RandomResizedCrop(size(640, 640), scale(0.7, 1.0), ratio(0.8, 1.2), p0.8), A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.7), A.HueSaturationValue(hue_shift_limit10, sat_shift_limit25, val_shift_limit20, p0.7), A.MotionBlur(blur_limit5, p0.2), A.CoarseDropout(max_holes8, max_height60, max_width60, fill_value0, p0.5), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ToTensorV2(), ])这套组合里每个操作都是有讲究的RandomResizedCrop模拟袋子在不同距离和不同裁剪下的视觉效果同时兼顾了尺度变化MotionBlur因为仓库里摄像头抓拍快速移动的传送带时经常出现运动模糊提前在训练阶段加入模糊样本推理时才不会把模糊的袋子当成背景CoarseDropout模拟袋子部分被遮挡的情况比如堆叠时下层袋子只露出一个角HueSaturationValue的色彩抖动幅度我没有开太大投射编织袋的颜色是重要特征调乱色调反而让模型学偏。3.3 增强的边界什么时候该停手数据增强不是越多越好。我之前试过把Mosaic和MixUp一起开训练loss确实降得更快但验证集上表现反而变差。原因是编织袋本身特征在Mosaic拼接后被切割得七零八落模型学到的是破碎的纹理碎片而不是完整的编织袋结构。对于小数据集我的经验是先开轻度的几何增强和色彩增强训练10个epoch后观察验证集loss如果验证集loss有明显回升而训练集loss继续下降说明增强强度过高模型学不到稳定特征此时需要降低增强概率或者关掉某些操作。这是反复试错出来的经验不是调参玄学而是模型稳定性在验证集上的真实反映。4. 基于YOLOv8的完整训练流程配置、启动与日志解读4.1 安装环境与验证GPU可用性训练之前先把环境装好。YOLOv8基于Ultralytics框架安装非常简单pip install ultralytics但我提醒一句在实际项目里不要用最新版最好固定一个已知稳定版本。我用的是8.0.136这个版本对自定义数据集的兼容性很好后续训练过程中踩到的坑也有大量社区讨论可以参考。装完以后用下面的命令确认GPU状态和框架版本防止装了个CPU版本等训练跑起来才发现一个epoch要等半小时python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only) python -c import ultralytics; print(ultralytics.__version__)如果GPU不可用也不是不能训练600张图用YOLOv8n在CPU上大概每epoch需要十几分钟勉强也能跑但明显不如GPU顺畅。我实际训练时用了一张6GB显存的入门级显卡YOLOv8n加上batch16、imgsz640的配置显存占用大约4GB跑300个epoch耗时约40分钟。这个配置对个人项目很友好。4.2 数据集配置文件写法在YOLOv8中训练自定义数据集关键是要写一个YAML格式的配置文件告诉框架类别名和图片路径。我的配置文件如下# bag_dataset.yaml path: ./datasets/bag_dataset train: images/train val: images/val nc: 3 names: 0: white_bag 1: yellow_bag 2: fertilizer_bag这里有几个容易出错的地方。path字段必须是包含images和labels文件夹的根目录train和val分别指向根目录下的子文件夹。类别编号必须和标注文件里的编号一致比如标注文件里是2 0.45 0.68 0.26 0.47那它对应的类别就是fertilizer_bag不能有错位。目录结构实际是下面这样的注意images和labels是两个独立的平行目录不是混合放在一起datasets/bag_dataset/ ├── images/ │ ├── train/ │ │ ├── bag_001.jpg │ │ ├── bag_002.jpg │ │ └── ... │ └── val/ │ ├── bag_561.jpg │ └── ... └── labels/ ├── train/ │ ├── bag_001.txt │ ├── bag_002.txt │ └── ... └── val/ ├── bag_561.txt └── ...4.3 训练命令与关键参数说明启动训练用这一条命令yolo detect train databag_dataset.yaml modelyolov8n.pt epochs300 imgsz640 batch16 patience50 project./runs namebag_detect逐个参数解释一下这些都是我反复调过之后觉得对于小数据集来说比较合理的设置modelyolov8n.pt加载COCO预训练权重。别小看这一步600张数据从零开始训练几乎不可能收敛但从预训练模型出发相当于模型已经知道了物体边缘颜色块纹理这些通用特征只需要适应编织袋这个特定类别。epochs300对小数据集来说300个epoch不算多。我之前试过100个epochloss还在缓慢下降提前停止后精度差了明显一截。patience50早停机制如果连续50个epoch验证集表现没有提升就自动停止训练。这能避免无效的等待。imgsz640YOLOv8默认输入尺寸。如果标注框较小可以试试1280但训练时间和显存占用都会显著增加。训练开始后终端会输出每个epoch的loss数值和验证集指标。需要注意的是在前20个epoch里loss有小幅震荡是完全正常的尤其cls_loss在预训练权重加载后需要一段时间适应新类别不要看到loss抖动就急着中断。4.4 训练日志里藏着什么信息训练结束后重点看results.png图表和weights/目录下生成的两个权重文件best.pt和last.pt。best.pt是验证集mAP最高的权重推理时应该用这个last.pt是最后一个epoch的权重通常比best差一些但如果训练结束前出现了严重过拟合last会明显退化。图表中我最关心的三个指标是train/box_loss、val/box_loss和metrics/mAP50。如果train/box_loss持续下降但val/box_loss在某个点掉头向上说明过拟合已经开始了此时best.pt往往已经产生不需要再纠结后面的epoch。mAP50对于编织袋场景比mAP50-95更有参考意义因为编织袋识别更关注框得准不准而不是和完美标注框的重合度有多精细。5. 训练阶段必踩的五个坑和排查过程5.1 中文路径导致图片读取失败第一次跑训练时我把数据集放在D:\数据集\编织袋\目录下结果程序报错说找不到图片但路径明明是对的。排查了很久才发现Ultralytics在Windows系统上对中文路径支持不完善OpenCV的imread函数在读取中文路径时直接返回空对象图片加载数为0。排查过程就是逐个换路径测试把train目录路径换成纯英文训练就正常了。所以训练数据集的目录名、文件名、标签名全部改用英文字符这是最省事的方案。5.2 类别编号错位导致训练效果崩溃这个问题比较隐蔽。我最初的标注文件里类别编号是按classes.txt的顺序来写的0是white_bag1是yellow_bag。但训练配置YAML里我误把yellow_bag写在了0的位置white_bag写在了1的位置。结果训练过程中类别标签全部错乱模型输出的自信度预测也混乱不堪把黄袋识别成白袋还把部分背景错误地预测为某一类。排查时我通过随机抽取图片和标注文件手动可视化了一部分标注框发现框的位置是对的但类别名称对不上这才定位到YAML文件配置问题。建议在训练前写一个脚本把标注文件里的类别编号和类别名称逐一对应打印出来人工核对一遍。5.3 小目标框训练效果不佳置信度阈值惹的祸训练结束后我在测试集上推理发现几个小尺寸编织袋没被检测出来。排查思路一开始认为是模型能力不够后来通过查看验证集指标发现这些小目标在训练时是有的但训练时它们贡献的损失权重太低模型把它们当成了背景的一部分。其中一个关键的修改是把训练imgsz从640提升到960小目标的像素占比变大模型看得更清楚推理时小袋子的召回率有明显提升。同时推理时把置信度阈值从默认的0.25降到0.15多过滤了一步虽然误检会多一些但真实场景中宁可多检也不能漏检后续再用业务规则过滤掉误检结果。5.4 训练过程loss爆炸数据标注越界有一次训练跑到第70个epoch时box_loss突然跳到几千然后变成nan整个训练直接报废。查了半天最后定位到一个标注文件里的边界框坐标超过1.0因为标注员画框时手滑把下边缘拉到了图像外导致损失计算时出现异常。这个问题的排查链路是这样的先看是哪个epoch开始爆炸确定是某个batch的问题再禁用shuffle逐batch跑一遍找到导致nan的那个样本最后打开这个样本的标注文件用可视化脚本画出标注框发现框严重越界。修复后就再也没出现过loss爆炸。现在我在每次训练前都会运行第三节里的校验脚本防患于未然。5.5 训练集和验证集的分配陷阱600张图如果直接随机划分有可能出现某几类在验证集中占比极低甚至为0的情况。我第一次划分时的错误做法是直接用sklearn的train_test_split做随机划分没有按类别分层。结果验证集里恰好没有fertilizer_bag类别训练过程中这个类别的mAP显示为0实际是验证集没有对应样本不是模型没学会。正确做法是使用分层抽样确保每个类别在训练集和验证集中的比例大致一致from sklearn.model_selection import train_test_split import os, shutil labels_dir labels all_files [f for f in os.listdir(labels_dir) if f.endswith(.txt)] # 读取每个文件包含的类别集合 file_labels {} for f in all_files: with open(os.path.join(labels_dir, f), r) as fh: lines fh.readlines() labels set() for line in lines: labels.add(line.split()[0]) file_labels[f] labels # 按类别分层划分 train_files, val_files train_test_split( list(file_labels.keys()), test_size0.15, stratify[sorted(file_labels[f])[0] for f in file_labels], random_state42 )当然如果一张图里同时含多个类别简单的按首类分层并不完美但已经能避免大部分分布失衡问题。更严格的做法是用IterativeStratification这种多标签分层采样方法我后面为了省事直接写进了数据准备脚本。6. 训练结果评估与小目标推理优化思路6.1 模型评估指标解读训练结束后YOLOv8会自动在验证集上计算各类别的精确率、召回率和mAP。我跑完的基准结果是mAP50约0.87mAP50-95约0.71这个结果在600张小数据集上已经算比较理想了。从各类别单独指标来看white_bag的召回率比yellow_bag低了大概6个百分点。原因也很好理解白色编织袋在仓库浅色地面上容易融入背景模型容易出现漏检。针对这个现象我加了一些对比度增强的样本后续迭代时白色袋子的召回率回升了。6.2 推理时的参数调节训练权重拿到手推理命令是这样yolo detect predict modelruns/bag_detect/weights/best.pt source./test_images/ conf0.2 iou0.45 saveTrueconf和iou两个参数需要按场景调。conf是置信度阈值调低会召回更多目标但也更容易误检iou是NMS的IoU阈值调低会让重叠的框保留更多如果编织袋堆叠严重可以稍微调高比如0.5让重叠的框合并得更激进一些。6.3 大图小目标的切片推理方案在实际项目里我经常遇到5000像素的超大图里面的编织袋只有指甲盖大小。直接resize到640再推理小目标直接失去特征。解决办法是采用切片推理SAHI方案from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/bag_detect/weights/best.pt, confidence_threshold0.2, image_size640, devicecuda:0, ) result get_sliced_prediction( imagelarge_image.jpg, detection_modeldetection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2, )切片推理的思路就是把大图切成640×640的小块分别检测然后把所有检测框映射回原图坐标。这里的overlap参数很关键设成0.2可以防止目标恰好被切在相邻两个slice的边界处导致漏检。实测下来用切片推理后小目标的召回率从0.4出头提升到0.75以上效果非常明显。7. 下一步扩展从600张到能够落地的识别模型7.1 数据闭环把误检样本变成新训练数据600张图训练的模型只能在有限场景里工作。想把模型真正应用到生产环境需要建立一个数据闭环模型部署后每天把误检和漏检的图片收集起来定期人工标注增量加入训练集。这样模型的性能会随着运行时间越变越好而不是永远停留在初始600张的水平。我用一个简单的脚本在生产环境定时抽取置信度在0.3到0.6之间的图片这些是模型犹豫不决的样本它们的标注价值最高能有效提升模型对边界情况的判别能力。7.2 半自动标注用模型辅助人工当模型已经有一定精度后可以用它来做预标注然后人工在LabelImg里修正框的位置和类别这能节省大量标注时间。我在扩展到1200张图时用预标注加人工修正的方式每张图的标注时间从原始的3分钟缩短到不到1分钟。具体做法是先用现有模型对一批新图片做推理把输出结果转为YOLO标注格式然后人工打开检查。对于模型漏检的袋子人工补框对于模型误检的背景块删除对应标注。这个流程看似简单实际能节约的生命周期远超想象。7.3 从检测到实例分割编织袋识别的下一步如果应用场景需要精确到每个袋子的轮廓面积比如计算堆垛体积、识别破损区域检测框就不够用了。可以考虑用YOLOv8-seg做实例分割标注时需要改用多边形标注工具。600张图像不够直接训练分割模型但可以用检测模型先做预标注生成分割草稿再人工精细调整把数据量撑到2000张以上再训练。每次我在项目里切换任务类型时都会提醒自己算法模型可以换但数据质量始终是最重要的基础。这套流程走完我自己最深的体会是600张图不是上限而是起点。很多人纠结于数据量不够而不肯启动结果项目一拖再拖。实际上先用小数据跑通流程、找到问题、再迭代扩充才是工业场景里最务实的做法。数据标注的一致性比数量更关键因为模型学到的上限很大程度上取决于标注给它的标签是否统一、准确。希望这篇分享能帮你省去一些走弯路的时间。本文还有配套的精品资源点击获取