YOLOv8针织品瑕疵检测:数据集训练与部署实践
简介这份针织品瑕疵检测数据集以YOLOv8格式标注适合需要训练目标检测模型的算法工程师、研究人员及纺织行业质检相关开发者使用。压缩包共含105个文件其中52张JPG原图与52个配套TXT标注文件一一对应另附1个YAML配置文件可直接划分训练集并启动YOLOv8训练流程。图片来自实际生产场景中的针织面料拍摄涵盖多种典型瑕疵形态便于验证模型泛化能力。资源包仅5.37MB结构轻量、开箱即用省去自行采集图像与转换标注格式的时间。目前已有1176人浏览学习适合作为瑕疵检测入门实验数据或工业视觉检测方案的初期验证样本。1. 针织品瑕疵检测数据集小样本也要把 yolov8 跑起来拿到这份「针织品瑕疵检测数据集 yolov8标记.zip」之前我其实已经见过太多号称「缺陷数据集」的压缩包解压后全是手机拍的模糊原图配一个简陋的 Excel 坐标表根本没法直接喂给 yolov8。这份资源不一样从文件名里的 rf 命名规则和哈希后缀可以判断它来自 Roboflow 导出目录结构、标注文件和 data.yaml 都是标准的解压之后图片、labels、配置文件一应俱全属于「拿到手就能开始训练」的规格。它解决的正是针织品产线上的真实问题布面破洞、油污、印染不均、飞花夹杂这类瑕疵靠人眼质检疲劳度高、漏检率不稳定而通用目标检测模型又缺少贴合面料的现成样本。如果你是刚接触缺陷检测的工程师或者正在做纺织质检相关的视觉项目这份资源能让你省掉到处凑图的阶段直接把精力放在训练和调参上。2. 先看清家底Roboflow 导出的 yolov8 数据集结构与标签格式2.1 从文件名还原采集场景IMG_ 与 z487 系列的分工解压后你会看到两类图片前缀一类是 IMG_ 开头一类是 z487486 这种长数字开头再配合 rf 哈希后缀。我的判断是这两组图片分别来自两个采集端IMG_ 系列大概率是手机或普通工业相机近拍的面料局部图光线环境不稳定背景也偏杂乱z487 系列从命名规律看更像固定工位或验布机上的俯拍采样角度相对统一光照也更可控。这两组混合在一起反而是好事。同一个模型如果能在两种成像条件下都检测出瑕疵它落到产线上时的鲁棒性会好很多。但你要留意一个问题混合采集意味着图片尺寸、亮度、纹理方向都不一致训练前的统一预处理就格外重要。我一般解压完先不看标注而是先把所有图片扫一遍统计尺寸分布和平均亮度心里有底了再谈训练。# 解压后先看目录长什么样 unzip 针织品瑕疵检测数据集_yolov8标记.zip -d knit_fault_dataset cd knit_fault_dataset # 统计图片尺寸分布判断是否需要统一resize python -c import glob from PIL import Image imgs glob.glob(**/*.jpg, recursiveTrue) sizes {} for p in imgs: im Image.open(p) sizes[im.size] sizes.get(im.size, 0) 1 print(总图片数:, len(imgs)) for k, v in sorted(sizes.items(), keylambda x: -x[1])[:8]: print(k, -, v, 张) 这段代码不复杂但值得跑一遍。glob递归找出所有 jpgPIL读尺寸做统计你就能快速判断这批数据里有没有超大图或超小图。如果大部分集中在 640x640 到 1920x1080 之间那用默认的 imgsz640 压力不大如果混着 4000x3000 的原图训练时 imgsz 就得谨慎选择否则要么显存扛不住要么缩得太狠丢细节。补一句解压时的经验zip 解压出现文件损坏时别急着重新下先unzip -t测试完整性很多所谓「坏包」只是下载中断。2.2 标签怎么读txt 每行的含义与归一化坐标换算yolov8 标记格式的核心是每张图片对应一个同名 txt 文件放在 labels 目录下文件名不含扩展名。每一行代表一个目标框格式是五个数值空格分隔class_id x_center y_center width height这四个坐标值全部做了归一化除以图片本身的宽和高取值区间 0 到 1。读标签这件事几乎没有玄学但换算回绝对值时的错误非常典型。下面这段脚本把归一化坐标还原成像素坐标并顺手打印每个类别的目标框数量import os label_dir knit_fault_dataset/labels/train classes {} for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn)) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cid int(parts[0]) x_c, y_c, w, h map(float, parts[1:5]) # 还原像素坐标假设原图 640x640按实际图片尺寸替换 img_w, img_h 640, 640 x1 int((x_c - w / 2) * img_w) y1 int((y_c - h / 2) * img_h) x2 int((x_c w / 2) * img_w) y2 int((y_c h / 2) * img_h) classes[cid] classes.get(cid, 0) 1 print(f{fn}: class{cid} box({x1},{y1})-({x2},{y2})) print(类别统计:, classes)这里的cid就是类别索引对应 data.yaml 里 names 列表的下标从 0 开始。x_center、y_center 是矩形中心的相对坐标width、height 是框的相对宽高。把归一化值还原成像素的时候很多人会拿 640 直接当分母如果你计划用 imgsz640 训练这样换算没问题但如果你要先看标签在原图上的位置是否准确必须用图片真实宽高否则框会整体偏移。另外txt 里出现全零行0 0 0 0 0或者 w、h 小于 0.001 的基本都是标注异常。Roboflow 导出的数据一般不会出现这种问题但如果是自己转的清洗时要把这类行单独挑出来别直接删文件先确认是不是空标注图片。2.3 一份可用的 data.yaml 长什么样Roboflow 导出的包在根目录会带一份 data.yaml内容大致是这个结构train: ../train/images val: ../valid/images nc: 3 names: [hole, stain, crease]特别提醒这个train和val路径是相对路径而且带..在 Roboflow 自己的目录结构里成立但你解压到自己的项目目录后相对路径可能就断了。我常规做法是直接改成绝对路径或者把数据集整体放到 ultralytics 项目下的 datasets 目录里用相对 datasets 根的路径。下面是我改完的版本path: /home/user/knit_fault_dataset train: images/train val: images/valid nc: 3 names: [hole, stain, crease]注意names的顺序和 txt 里的类别索引强相关不要凭印象改。先打开任意一个 txt 看第一列最大数字是几再对照 data.yaml 的 names确认类别数量对得上。这份资源具体的类别名应以你的解压内容为准我只提供核对思路。nc 写错了训练会直接报错或类别对应混乱这是我们最容易翻车的地方。3. 训练前必修数据划分、类别校准与图像预处理3.1 训练集/验证集划分与 data.yaml 的路径修正很多初学者拿到数据集第一反应就是直接训练忽略了先做数据集划分检查。Roboflow 导出时通常自带划分但比例不一定合理小样本数据往往验证集抽得太多导致训练图片不够。我建议先看目录结构train、valid 和 test 各有多少张再决定是否重新划分。import os, random, shutil random.seed(42) base knit_fault_dataset img_dir images label_dir labels # 重新划分 80% 训练 / 20% 验证 all_imgs [f for f in os.listdir(f{base}/{img_dir}) if f.endswith(.jpg)] random.shuffle(all_imgs) split int(len(all_imgs) * 0.8) train_imgs all_imgs[:split] val_imgs all_imgs[split:] for sub, imgs in [(train, train_imgs), (valid, val_imgs)]: os.makedirs(f{base}/images/{sub}, exist_okTrue) os.makedirs(f{base}/labels/{sub}, exist_okTrue) for im in imgs: label im.rsplit(., 1)[0] .txt shutil.copy(f{base}/img_dir/{im}, f{base}/images/{sub}/{im}) shutil.copy(f{base}/label_dir/{label}, f{base}/labels/{sub}/{label})这段脚本用random.seed(42)固定随机种子保证每次划分结果一致方便复现实验。80/20 是最常规的比例如果样本量极小比如只有几十张我会改成 85/15 甚至 90/10确保训练集尽量多。重新划分时要注意原目录里可能已有 valid 和 train 子文件夹Roboflow 导出的结构就是分好的你要么直接沿用要么像我这样先备份、再整体重分避免新旧文件混在一起。划分完成之后还有一个关键动作确认每张图片都有对应的 txt 文件以及两者文件名严格一致。这个检查很容易遗漏我见过太多因为大小写后缀不一致导致训练时 label 缺失模型静默跳过不报错最后精度怎么提都上不去。# 检查图片和标签是否一一对应 python -c import os for split in [train, valid]: imgs set(os.path.splitext(f)[0] for f in os.listdir(fimages/{split})) labels set(os.path.splitext(f)[0] for f in os.listdir(flabels/{split})) print(split, 仅图片无标签:, len(imgs - labels), 仅标签无图片:, len(labels - imgs)) 3.2 类别映射先把 txt 第一列读明白真实项目里最常翻车的点不是模型结构而是类别列表对不上。假设一份数据集标注了两种缺陷但训练时 data.yaml 里写了三个类别模型会把所有框硬分到三类里精度看起来还行实际语义全乱。所以我任何数据集到手都先跑一遍类别统计把所有 txt 里出现过的类别索引都拉出来。实际上 2.2 节那段标签读取脚本已经顺带统计了类别分布你只需要关注两点一是类别索引是否连续从 0 开始不跳号二是每个类别的样本量如果某个类别只有个位数样本这种类别 yolo 基本学不好要么补数据要么训练时用 class weights。小数据集上我更推荐先合并相似类别比如把「油污」和「深色污渍」归为同一类 stain先让模型学出「有没有瑕疵」分类细化放到第二轮迭代再做。类别分布不均匀在缺陷检测里几乎是常态。破洞这种「频发且好标」的样本可能占 70%飞花夹杂可能只有 5%最终模型对少数类几乎无效。如果你做完统计发现某类低于总框数的 10%建议你调训练参数把loss里针对该类的权重提上去或者用focal loss的思路。yolov8 的官方实现里就可以通过修改数据集中各类别的重复采样来缓解我自己常用的土办法是把少数类样本在训练集里复制两份让它在每个 epoch 里被看到更多次简单直接效果也很稳。3.3 尺寸、颜色与增强策略小数据集要克制缺陷检测和常规物体检测有个最大的不同瑕疵往往是小目标。布面上的小破洞可能只有几十个像素油污范围大一些但也相对集中。这种场景下 imgsz 的选择直接影响检测上限你用 640 训练小于 32x32 的瑕疵几乎不可能被有效学习。我的建议是优先尝试 imgsz960 或 1280代价是显存和训练时间翻倍但小目标召回率提升明显。数据增强方面mosaic 和 mixup 是 yolov8 默认给开的东西对小数据集帮助巨大但这两个增强对缺陷检测也有副作用。mosaic 会把四张图拼在一起如果瑕疵恰好被切割线截断模型会学到「半个瑕疵也算」推理时反而对不完整缺陷过于敏感。YOLOv8 的增强策略里我个人建议# data.yaml 旁挂一份增强配置训练时通过 hyp 引用 mosaic: 0.5 mixup: 0.1 hsv_h: 0.015 hsv_s: 0.7 fliplr: 0.5 scale: 0.5 translate: 0.1mosaic缩小到 0.5只对一半的样本做拼接降低截断瑕疵的影响mixup降到 0.1防止两张图叠加后瑕疵纹理被稀释hsv 微调色相和饱和度是为了模拟同一批次面料因染色工艺波动出现的色差这个在针织品场景里特别实用fliplr 左右翻转对布面这种上下方向不敏感的目标没问题但如果你后续要检测的方向性瑕疵比如纬斜就别开翻转。小数据集的核心原则是增强可以开但强度克制宁缺毋滥先把真实分布学明白再去靠增强拓宽边界。4. yolov8 训练参数解析epochs、imgsz 与 batch 的取舍4.1 模型选择n/s/m 的取舍依据yolov8 按参数量分成了 n、s、m、l、x 五个尺寸。瑕疵检测场景我最常用的是 s 和 mn 在某些实时检测机上也会尝试。选型逻辑是这么个逻辑n 模型太小对细小瑕疵的特征表达能力有限破洞这种几个像素的目标很容易漏检m 精度上去明显但训练和推理速度都会下来。也有个折中的办法先用 s 跑通全流程确认数据没问题再换成 m 做正式训练这样排查数据问题的时间成本最低。模型参数量推理速度小目标能力适用场景yolov8n最小最快弱边缘设备快速初筛yolov8s小快中等常规产线检测推荐起点yolov8m中中等较强缺陷较小或精度要求高如果你用的是本项目的 zip 资源我的建议是不管最终选什么第一轮都用 yolov8s 跑。s 的权重文件小CPU 也能跑训练一轮的时间够你验证数据划分和路径是否真的没问题。等确认无误了再上 m 甚至 l 追求精度一套流程走下来不会浪费太多时间。4.2 训练超参epochs、imgsz 与 batch 的确定训练命令看起来只有几个参数但真正跑过的人都知道参数组合对最终效果的影响比模型结构还大。先给出一份我常用的训练脚本模板yolo detect train \ --model yolov8s.pt \ --data /home/user/knit_fault_dataset/data.yaml \ --epochs 300 \ --imgsz 960 \ --batch 16 \ --patience 30 \ --device 0 \ --workers 8 \ --optimizer AdamW \ --lr0 0.001 \ --cos_lr True \ --seed 42逐步解释这些参数的含义。epochs 300在小数据集上不算多因为每个 epoch 看到的图片有限模型收敛需要足够的迭代次数patience 30表示连续 30 个 epoch 验证集指标不提升就提前停止防止过拟合imgsz 960是综合了小目标检测和显存占用的选择如果显存只有 8G就把 imgsz 降回 640batch 降到 8optimizer AdamW在瑕疵检测这种小数据集上比默认的 SGD 收敛更稳这是我踩了很多次坑之后的结论lr0 0.001是相对稳妥的初始学习率数据集越小学习率越低否则震荡会很剧烈cos_lr True让学习率按余弦曲线衰减后期微调更细腻。显存不足的时候最优先调整的是batch不是imgsz。因为 batch 减小只是每个 step 看到的样本变少梯度方向会有点噪声但模型能跑起来imgsz 减小则直接牺牲小目标的检测能力。当然 batch 也不能无脑调小太小的 batch 在 BN 层的统计上极不稳定实践下来 batch 至少得是 8。CPU 环境下训练的话参考热搜里「ubuntu20.04 搭建 yolov8 环境 cpu 版本」的思路把 device 换成 cpuworkers 砍到 4epochs 降到 100 先验证流程。4.3 训练过程中的监控点loss 曲线、map50、混淆矩阵跑起来之后别挂着等结果训练日志里信息量很大。我训练时习惯开两个终端一个挂着训练进度另一个随时准备画曲线和看指标。途中最关键的三个监控点box_loss、cls_loss、map50。box_loss 是定位损失它下降慢通常说明框回归有困难对瑕疵这种变形目标很常见。如果 box_loss 降到一定程度不再动往往不是模型问题而是标注框本身不一致同一类缺陷有的标得很紧有的标得松模型学不到稳定的「紧度」。cls_loss 是分类损失如果它反复震荡不掉大概率是类别混淆严重比如油污和阴影这种视觉边界模糊的样本。这类曲线保存成图片 是yolov8 默认行为训练结束后runs/detect/train/下就有results.png。我拿到 results.png 第一时间不看 map50而是先看 val 的损失曲线验证集损失如果先降后升而训练集损失还在降说明已经过拟合验证集损失全程平坦说明学习率太低或者模型容量不够。等到 map50 曲线开始抬头再去看混淆矩阵确认哪些类别互相污染。这一步的意义不在于现在调什么而在于让你对后续迭代的方向有判断不至于对着一个 0.9 的 map50 盲目欢呼打开混淆矩阵却发现最关键的破洞类被大量漏掉。5. 避坑记录数据集落地训练最常见的六个坑5.1 数据层面的三个坑坑一解压后路径断掉训练直接报错找不到图片。现象训练命令启动后立刻报错提示 No such file or directory。原因Roboflow 导出的 data.yaml 里 train、val 是用相对路径加..写的解压位置一变路径就失效。解决把 data.yaml 改成绝对路径或者把数据集放进 ultralytics 约定的 datasets 目录下用相对 datasets 根的路径。注意改完再跑一次yolo detect train --data之前先单独验证一下路径确实可读。坑二图片与标签文件名不匹配某类样本被模型默默忽略。现象训练能跑完loss 也降了但某个类别 mAP 始终接近 0。原因图片是 .jpg标签是 .txt文件名如果大小写不一致或者图片多后缀名不同yolo 会静默跳过找不到标签的图片不回显警告。解决训练前强制跑一遍对齐检查把图片名和标签名做 set 差集逐个补齐或剔除异常项。坑三类别索引不连续data.yaml 与标注错位。现象训练过程中 cls_loss 出现怪异的跳动最终混淆矩阵全乱类别张冠李戴。原因txt 中可能用了 0、1、3 这种跳号索引而 data.yaml 的 names 列表和 nc 没有对应调整。解决把 txt 第一列的全部取值拉出来确认是 0 到 nc-1 的连续整数不连续就写脚本重新映射同时同步改 names。这一步做完之后我还会随机挑 3 到 5 张图把归一化坐标画成画框人眼确认类别和位置都对得上。5.2 训练与环境的三个坑坑四显存不够但一开始不知道。现象训练到了第一个 epoch 结束保存 checkpoint 时进程崩溃报 CUDA out of memory。原因前向传播能过反向传播保存权重时额外显存瞬间暴涨很多人以为是代码问题实际上是 batch 和 imgsz 配错。解决用nvidia-smi先看剩余显存再按「batch 减半 → 若仍不够再降 imgsz」的顺序调整。常规 8G 显存跑 imgsz640、batch8 的 yolov8s 是可行的超过这个组合就得往下调。坑五损失函数曲线「看起来正常」实际过拟合已经发生。现象训练集 mAP 一路飙到 0.95验证集 mAP 停在 0.7 不再涨。原因小数据集加多轮训练模型记住了训练集的纹理细节布面的纹理过于独特泛化被破坏。解决一是调大patience之外的weight_decay代码参数上可以给--weight_decay 0.0005二是减少 epochs三是检查增强配置是否被调整过度。我自己最常用的做法是直接打断训练把 best.pt 拿出来在未参与训练的同类型图片上做验证用真实产线图片代替验证集比任何指标都靠谱。坑六CPU 训练超慢等到怀疑人生。现象epochs300 在 CPU 上可能跑十几个小时还没有一个 epoch 完成日志进度条像卡死。原因CPU 推理本身慢加上 yolov8 默认开启多线程增强数据加载反而拖垮了计算。解决前面提到的 CPU 环境参数——workers 降到 4 或 2epochs 降到 100imgsz 降到 640可以先跑通验证数据没问题再去找机器或云 GPU 做正式训练。跑通和跑好本来就不是一件事。6. 验证与部署从 val 到 ONNX 导出的完整动作6.1 用验证脚本确认模型真实水平训练结束之后我的习惯是不直接用 best.pt 去做推理而是先跑一遍官方验证脚本把模型在当前验证集上的指标打印出来确认它没被检查点保存的一些异常干扰yolo detect val \ --model runs/detect/train/weights/best.pt \ --data /home/user/knit_fault_dataset/data.yaml \ --imgsz 960重点关注 per-class map50 和 map50-95。map50-95 是更严苛的指标它计算了 0.5 到 0.95 多个 IoU 阈值下的平均精度对框的精确定位更敏感。瑕疵检测里很多框标得偏松map50 好看但 map50-95 上不去这说明模型虽然找到了位置但边界框不够精准。这种情况我会看几个真值框的可视化对比写一个小脚本把模型预测框画到原图上人眼检查瑕疵边缘到底贴近多少。6.2 导出 ONNX从 pytorch 到通用推理的转换产线部署大概率要离开 pytorch 环境ONNX 是目前兼容性最好的中间格式。导出命令很简单yolo export \ --model runs/detect/train/weights/best.pt \ --format onnx \ --imgsz 960 \ --opset 12opset 12是兼容性和算子支持度的平衡点太低的 opset 有些算子导出不了太高了部分推理引擎不支持。导出成功后推荐用 onnxruntime 做一次推理验证确认转换没丢东西import onnxruntime as ort import numpy as np from PIL import Image sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name img Image.open(test_sample.jpg).resize((960, 960)) img_np np.expand_dims(np.array(img).transpose(2, 0, 1), 0).astype(np.float32) / 255.0 outputs sess.run(None, {input_name: img_np}) print(outputs[0].shape) # 输出 shape 是 [1, 84, 8400] 等这段代码的核心是把输入转成 NCHW 格式、归一化到 0 到 1然后拿到模型的原始输出。[1, 84, 8400]的含义是8400 个候选框每个框有 4 个坐标值加 80 个类别分数这里是 nc 加 4对应你的类别数。后续要做的 NMS 和坐标解码就是你对检测后处理的理解了这部分不同部署框架的写法差异不小建议导出后先用官方推理脚本跑通再在目标推理引擎上复现结果。6.3 一个进阶技巧难例挖掘的迭代循环验证和部署不是终点。我每做一个数据集最有价值的不是训练本身而是「难例挖掘」这轮循环。具体动作是拿训练好的 best.pt 去跑一批没参与训练的历史图片把预测框置信度低于 0.5 但人工确认确实有瑕疵的样本挑出来人工补标后并入原数据集重新训练。这个过程相当于让模型告诉你它哪里不会你再针对性补课。我当时做针织品时也遇到类似困境破洞漏检很少但浅色油污在白色面料上几乎怎么都检不出来因为训练集里粉色面料上的油污样本占了大多数。后来我把历史素材里浅色面料的油污图全翻出来补了大概 100 多张标注第二轮训练的 map50 从 0.81 提到了 0.87浅色油污的召回率涨了接近两成。从那以后我每个瑕疵检测项目都强制走一遍「训练-难例挖掘-补标-重训」的循环直到模型在人工挑不出系统性盲区时才收手。这个习惯帮我省下的返工时间远比那几轮训练时间多得多希望这次的拆解过程和经验也能帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →