简介:面向工业视觉缺陷检测与目标识别场景的三星油污缺陷数据集,聚焦头发丝和小黑点两类典型瑕疵,共提供660张带标注图片。每张jpg原图均配有一个xml标注文件,可直接用于YOLO、Faster R-CNN等主流检测模型的训练与验证,也可支撑数据增强、模型鲁棒性对比等实验。压缩包内另含4个txt说明文件,有助于快速了解类别定义与数据集结构。整个压缩包约314.54MB,共1324个文件,以jpg图像、xml标注和txt文本为主,图片命名中的normal与缺陷标识能帮助区分正常样本与缺陷样本。目前已有1412人学习下载,适合需要真实工业质检数据进行算法调优、毕业论文实验或入门缺陷检测的开发者使用。使用这套数据,可快速搭建自己的检测流程,并将训练经验迁移到油污、划痕等相似外观缺陷场景。
1. 油污缺陷数据集:660 张图里,头发丝和小黑点为什么是最难啃的两类
表面缺陷检测是最不缺数据集、又最缺好数据集的方向。这份油污缺陷数据集一共 660 张图,类别只有两类:头发丝缺陷和小黑点缺陷,全部来自显示面板产线的真实油污场景。别觉得两类少就好做——头发丝是长宽比动辄几十比一的细线,宽度常常只有一两个像素;小黑点则往往只有几个像素大,这两类放进同一个检测模型,几乎把目标检测里小目标、极端长宽比两大难点全占齐了。适合谁用?做工业质检且要拿真实缺陷验证算法的工程师、做表面缺陷课题的学生、想评估小样本上限的算法同学。它不是拿来刷分的,是拿来踩坑、练手感、验证产线方案的。
2. 先看清资源底细:图像、标注与两类缺陷的成像特性
2.1 头发丝和小黑点在油污场景里的真实形态
油污缺陷不是单一形态。同一摊油渍在面板表面会因为重力方向、擦拭路径和干燥时间不同,呈现出完全不同的视觉样貌。头发丝缺陷是油污被拖拽之后形成的细长条,宽度通常只有 1 到 3 个像素,长度却可能跨过图像的三分之一甚至一半;小黑点则是油污聚集成的近圆形小目标,常见直径在 3 到 8 个像素之间,有时候还带着一圈淡淡的油渍晕边。
这两类缺陷有一个共同点:在 640×640 的常规检测分辨率下都很难看全。头发丝因为太细,下采样过程中经常断线,标注框里有一大半区域其实是干净的背景;小黑点因为太小,经过几次卷积和池化之后特征几乎被抹平。我拿到这份数据集第一件事不是跑模型,而是先逐张翻图,把这两类缺陷的像素占比统计出来。实际统计下来,头发丝目标占图像面积的比例大多在 0.5% 以下,小黑点更是千分之一量级,这个数值直接决定了后面训练分辨率的选择,也决定了你不可能用 416 这种小分辨率去训练。
如果你是用线扫相机采集的产线图像,还要注意图像里可能存在纵向的条纹噪声。这种噪声在灰度直方图上的表现和头发丝缺陷很像,如果数据集里混入了这类图,模型很容易把正常纹理学成缺陷特征。处理办法是训练前对图像做一次傅里叶滤波或者减去背景,但更省事的做法是先用直方图均衡化看几组图的灰度分布,确认缺陷和背景在灰度层面的可分性再去谈训练。
2.2 标注格式核对:YOLO txt 还是 VOC XML
数据集下载下来之后,先别急着照着 README 开跑,第一步要确认标注格式。市面上这类缺陷数据集常见三种格式:YOLO 格式的 txt 归一化坐标、VOC 格式的 XML 绝对坐标,以及 COCO 格式的 JSON。这份数据集我这边拿到的是 YOLO txt 格式,每张图片对应一个同名 txt,每一行是 class_id cx cy w h,坐标全部归一化到 0 到 1 之间。
先核一下类别顺序。txt 里的 class_id 如果 0 是头发丝、1 是小黑点,后面训练配置里的类别名就必须按同一顺序写。很多翻车现场就是这里出的:训练脚本里类名写反了,头发丝被预测成小黑点,误检率直接爆炸。拿到数据后我建议先写几行脚本把标注可视化画出来,逐张确认框和缺陷本体对齐。这一步花十分钟,能省掉后面排错的两小时。
import os import cv2 img_dir = "images" label_dir = "labels" out_dir = "visual_check" os.makedirs(out_dir, exist_ok=True) for img_name in os.listdir(img_dir): name = os.path.splitext(img_name)[0] img = cv2.imread(os.path.join(img_dir, img_name)) if img is None: continue h, w = img.shape[:2] label_path = os.path.join(label_dir, name + ".txt") if not os.path.exists(label_path): continue with open(label_path) as f: for line in f: cid, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) color = (0, 255, 0) if int(cid) == 0 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 1) cv2.imwrite(os.path.join(out_dir, img_name), img)这段脚本做的事很简单:遍历图片目录,读取同名 txt 标注,把归一化坐标乘回原图宽高,画框保存到 visual_check 目录。绿色框是 0 号类,红色框是 1 号类。跑完之后重点看两类现象:一是框有没有明显偏移目标本体,二是小黑点的框是不是只框住目标的一小半。如果大面积偏移,说明标注本身有噪声,后面训练时要么清洗,要么在损失函数里降低定位项的权重。代码里 cv2.rectangle 的 line 参数我故意设成 1,因为细线框在放大看的时候不会盖住缺陷本身,方便判断框的贴合度。
2.3 用脚本先做一遍数据集体检
画框只是第一步,正式训练之前我还要做三件体检:类别数量分布、图像分辨率分布、标注框长宽比分布。类别数量分布决定要不要处理类别不平衡;分辨率分布决定数据加载时要不要统一缩放;长宽比分布直接反映头发丝这种极端形状目标占比有多大。
统计脚本一般写成这样,输出几个关键数字到控制台:
from collections import Counter import os import cv2 label_dir = "labels" img_dir = "images" class_counter = Counter() aspect_ratios = [] resolutions = [] for label_name in os.listdir(label_dir): with open(os.path.join(label_dir, label_name)) as f: for line in f: cid, cx, cy, bw, bh = map(float, line.split()) class_counter[cid] += 1 aspect_ratios.append(bw / bh if bh > 0 else 0) for img_name in os.listdir(img_dir): img = cv2.imread(os.path.join(img_dir, img_name)) if img is not None: resolutions.append((img.shape[1], img.shape[0])) print("类别统计:", dict(class_counter)) print("长宽比前20:", sorted(aspect_ratios, reverse=True)[:20]) print("分辨率示例:", resolutions[:10])这段脚本输出的三个信息各有用途。类别统计如果发现 0 类比 1 类多出三四倍,训练时要给少数类加权或者做过采样;长宽比列表里如果出现 30 以上的值,说明头发丝几乎没有被切段处理过,常规水平框检测器对这类目标只能框出中间一段,两端会被判成背景;分辨率示例则告诉你图像原始尺寸是否统一。如果分辨率差异大,训练时最好统一 letterbox 到同一尺寸,而不是直接拉伸变形。
我做数据集体检有个习惯:体检结果单独存一个 txt 文件,每次训练前对比一次。这不是玄学,是防止自己忘了上次用的哪份数据。标注文件被动过、图片被裁剪过、类别顺序被改过,这些坑都是这么救回来的。体检脚本本身也可以扩展成清洗脚本,把宽高异常、坐标越界的标注直接过滤掉,后面第 4 章会专门讲这个坑。
3. 训练链路搭建:把 660 张图跑进 YOLOv8 并拿到可用指标
3.1 环境与依赖版本
660 张图不算大,不需要上多卡分布式那一套。我一般用单张消费级显卡就能跑,显存 8G 以上就够。环境方面最省事的方式是建一个独立的 conda 环境,把 torch、ultralytics、opencv-python 这几个核心依赖装齐。版本这里有个小讲究:ultralytics 的接口在 8.x 系列里改过几次,yolo 命令行和 Python API 的对应关系不完全一致,建议锁定一个版本号,避免复现时接口对不上。
我常用的安装命令是这样:
conda create -n defect python=3.10 -y conda activate defect pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics==8.2.0 opencv-python pillow tqdm这里锁了 ultralytics 8.2.0,因为 8.2 系列的配置文件和训练参数回调都比较稳定,网上能找到的踩坑记录也最多。PyTorch 装 CUDA 11.8 的 wheel 是为了省去本地编译的时间,如果机器上已经装了匹配的 CUDA 版本,直接用官方推荐的安装方式也行。装完之后跑一句 python -c "from ultralytics import YOLO; print('ok')" 确认导入正常再往下走。环境这块最怕的是 torch 和 CUDA 版本不匹配导致训练时直接报显存非法访问,那个错排查起来很痛苦,而且往往不是代码问题。
3.2 数据划分与标签转换脚本
数据集没有现成的 train/val 划分文件是常态。660 张图,我的划分策略是 8:1:1,也就是 528 张训练、66 张验证、66 张测试。这里要特别强调一点:划分时必须按图而不是按标注行来划分。一个常见错误是把所有标注合并之后再随机分,导致同一张图的缺陷被拆到训练集和验证集两边,验证指标虚高,看起来美好,一上线就露馅。
下面这个脚本按图片名字做随机划分,图片和同名标注一起拷过去:
import os import random import shutil random.seed(42) img_dir = "images" label_dir = "labels" train_img, val_img, test_img = "train/images", "val/images", "test/images" train_lbl, val_lbl, test_lbl = "train/labels", "val/labels", "test/labels" for d in [train_img, val_img, test_img, train_lbl, val_lbl, test_lbl]: os.makedirs(d, exist_ok=True) names = [f for f in os.listdir(img_dir) if f.endswith(".jpg") or f.endswith(".png")] random.shuffle(names) n_train = int(len(names) * 0.8) n_val = int(len(names) * 0.9) for i, name in enumerate(names): base = os.path.splitext(name)[0] src_img = os.path.join(img_dir, name) src_lbl = os.path.join(label_dir, base + ".txt") if i < n_train: shutil.copy(src_img, train_img) if os.path.exists(src_lbl): shutil.copy(src_lbl, train_lbl) elif i < n_val: shutil.copy(src_img, val_img) if os.path.exists(src_lbl): shutil.copy(src_lbl, val_lbl) else: shutil.copy(src_img, test_img) if os.path.exists(src_lbl): shutil.copy(src_lbl, test_lbl) print(f"train={n_train}, val={n_val - n_train}, test={len(names) - n_val}")这个脚本的逻辑是先把所有图片名字打乱,然后按比例切成三段,图片和同名标注一起拷过去。random.seed(42) 这行不是摆设,固定随机种子之后,任何人用这个脚本复现的都是同一份划分,实验结果可对比。这里有个边界情况:如果某张图没有对应标注文件,脚本会直接跳过标注拷贝,但图片仍然会进入训练集,训练时这张图会被当成背景样本参与负样本学习。要不要保留这种背景图取决于你的场景,我一般会保留,因为真实产线里大量图像确实没有缺陷,模型需要见过干净的背景才能学会区分。
3.3 训练配置与参数说明
数据和环境都就绪之后,训练配置是关键。660 张小数据集最忌讳的是盲目抄大模型的训练参数。我用 YOLOv8s 做基准,输入分辨率设 640,但心里清楚头发丝和小黑点这种小目标在 640 下会吃亏,所以后面单独跑了 1280 的对比实验。训练轮数设 200 轮,配早停策略,实际上一般 80 到 120 轮就收敛了。
训练脚本用 ultralytics 的 Python API 写,比命令行好调参:
from ultralytics import YOLO model = YOLO("yolov8s.pt") results = model.train( data="defect.yaml", epochs=200, imgsz=640, batch=16, lr0=0.01, patience=30, augment=True, mosaic=1.0, seed=42, workers=4, device=0, project="runs/defect", name="yolov8s_640", )训练完成后会输出 best.pt 和 last.pt。best.pt 按验证集 mAP50 最优保存,真正要用的是它,不是 last.pt。几个关键参数的作用整理在下面这张表里:
| 参数 | 值 | 作用与调整思路 |
|---|---|---|
| imgsz | 640 / 1280 | 输入分辨率。小目标优先 1280,但显存占用和训练时间会明显上升 |
| lr0 | 0.01 | 初始学习率。小数据集降到 0.005 更稳,0.1 基本必发散 |
| patience | 30 | 连续 30 轮验证指标不升就早停,省时间 |
| mosaic | 1.0 | 四图拼接增强。对小目标有效,但注意别让它切碎头发丝 |
| batch | 16 | 8G 显存跑 640 分辨率没问题,1280 分辨率要降到 8 |
defect.yaml 的数据配置写法是标准 YOLO 格式:
path: ./ train: train/images val: val/images names: 0: hairline 1: spot这里 path 指 YAML 文件所在目录作为根目录。如果数据集放在别处,要写绝对路径或者相对于 YAML 文件的正确相对路径。names 的类别顺序必须和第 2 章核对过的标注顺序一致,0 是头发丝 hairline,1 是小黑点 spot,顺序反了模型训练出来预测结果全是反的,而且训练过程不会报任何错,只能靠可视化推理结果才能发现。
3.4 结果指标怎么读:mAP50 够用,mAP50-95 看边界
训练结束后,终端和 results.csv 里会有一堆指标,挑重点看。对于工业缺陷检测,我一般先看 mAP50 和 recall,这两个直接反映漏检情况。头发丝和小黑点这类小目标,mAP50 能到 0.7 以上算及格,0.8 以上算是能用的水平。mAP50-95 会低不少,因为 IoU 阈值往上提之后,细长目标只要框偏几个像素就被判为负样本,这个指标更多用来对比不同模型版本之间的边界质量。
指标解读要连起来看:precision 高而 recall 低,说明模型预测出来的框多半是对的,但漏掉了不少真实缺陷,这时候要降低置信度阈值或者增强小目标分支;recall 高而 precision 低,说明模型把背景当成了缺陷,误检多,要反方向调整。660 张图训练出来的模型,验证集指标会有波动,不要因为某一轮 mAP 高就下结论,至少跑三组不同的随机种子取均值,再决定模型版本。这个习惯在数据集小的时候尤其重要,不然你很难分清精度的提升到底来自模型改动还是随机性。
4. 避坑指南:小数据集训练油污缺陷的五个翻车点
4.1 现象:训练 loss 不降反升,最终模型全是漏检
原因:学习率设太大,小数据集上梯度震荡,模型参数在最优解附近来回跳。我见过有人直接把大模型训练参数原样抄过来,lr0 设 0.1,跑了 50 轮 loss 还在 2 附近波动,所有预测框都落在背景上。
解决:把 lr0 降到 0.005 甚至 0.001,并确保 warmup 轮数足够。看 loss 曲线时关注前 20 轮的下降趋势,如果前 20 轮 loss 不降,大概率不是轮数不够,而是学习率或者数据加载出了问题,先检查这两处再继续跑。
4.2 现象:小黑点验证集上几乎全部漏检,头发丝却正常
原因:小黑点目标太小,在 640 分辨率下经过模型的下采样层之后,特征图上只剩几个像素的响应,回传的梯度也很微弱。加上小黑点在数据集里数量占少数,总损失被头发丝类主导,模型学成了偏向头发丝的检测器。
解决:两个方向一起做。一是把输入分辨率提到 1280,让小黑点在特征图里占据更多像素;二是给小黑点类单独设置 loss 权重或做少数类过采样。我实测过,1280 分辨率下小黑点 recall 能提升 10 到 15 个百分点,代价是训练时间增加约一倍。
4.3 现象:验证集 mAP 很高,测试集惨不忍睹
原因:数据划分时同源图片泄漏。同一块缺陷在相邻的多张图里出现,随机划分时这些图被分到了训练集和验证集两侧,验证时模型其实是在记忆相似背景,而不是泛化识别缺陷。
解决:划分前先按图像来源分组。如果数据集是按批次拍摄的,文件名前缀或目录通常能反映来源,按批次整体划分,训练集和验证集不要出现同一批次的图。660 张的数据集比较小,我建议把同源图全放训练集,验证集留另一批完全独立的图。
4.4 现象:训练时内存暴涨,跑了几百张图之后直接卡死
原因:标注文件里出现异常值,比如归一化坐标小于 0 或者大于 1,或者宽高为负,数据加载阶段计算出非法框导致内存申请异常。这种问题在人工标注的数据集里非常常见,尤其是标注工具拖动边界时容易产生负宽高。
解决:在数据加载前加一道清洗脚本,把坐标裁剪到 0 到 1 之间,把宽高小于 0.0001 的框直接丢弃。第 2 章的体检脚本可以扩展出来做这件事,每次训练前跑一遍,日志里打印出清洗了多少条异常标注,方便追踪数据版本。
4.5 现象:头发丝被断成几段检测,一个目标出三四个框
原因:头发丝过长,水平框很难覆盖完整目标,模型倾向于在局部高响应段分别输出框,验证阶段 NMS 阈值不合理,相邻框没有被合并。
解决:训练时把旋转增强角度调小或者关掉,避免长条目标被旋转到不合理形态;推理时把 NMS 的 IoU 阈值从默认的 0.45 调高到 0.6 左右,让重叠框更容易合并。如果还是很碎,就上第 5 章要讲的切片推理,把长图切成几段分别检测再合并结果。
5. 小样本救火:660 张图怎么把漏检率压下来
5.1 增强策略:哪些该开、哪些必须关
660 张图训练目标检测模型,数据增强不是可选项,而是决定成败的选项。但要命的是增强并不是全开就最好。我做过一组对比实验,开全增强时 mAP 反而比只开基础增强低了 3 个点,原因在于旋转和透视变换把头发丝这种细长缺陷扭曲成了现实中不可能出现的形态,模型学到了错误的特征。
我现在的增强策略是分层的。基础层必开:HSV 扰动、随机平移、缩放、轻微翻转,这些对油污缺陷的光照变化和位置变化有正面作用。进阶层谨慎开:mosaic 可以开但关掉其中的旋转分量,copy-paste 适合小黑点但会让头发丝出现拼接断痕。禁用层:大角度旋转、shear、透视变换,除非产线图像确实存在大角度姿态变化。
YOLOv8 里增强参数可以这样精确控制:
model.train( data="defect.yaml", epochs=200, imgsz=640, hsv_h=0.02, hsv_s=0.5, hsv_v=0.3, degrees=5, translate=0.1, scale=0.4, shear=0.0, perspective=0.0, mosaic=1.0, )degrees=5 表示只允许正负 5 度的旋转,防止长条形缺陷被转到不合理的角度。shear 和 perspective 直接设 0,因为透视变换对工业质检图像没有物理依据,只会引入假纹理。mosaic 保持 1.0 但配合它内部的拼接参数调整强度,实际训练里如果发现 loss 曲线震荡得厉害,把 mosaic 降成 0.5 试试。HSV 三个值我给的比较保守,油污缺陷的灰度特征如果被过度扰动,模型会分不清油污和正常纹理的边界。
这里有个容易被忽略的点:增强参数不是调完就固定了,要和 imgsz 联动。imgsz 提到 1280 之后,同样的 translate=0.1 实际平移的像素数翻倍,对长目标的破坏性更大,所以高分辨率训练时我会把 translate 降到 0.05。本质上每条增强规则都要在脑中过一遍它的物理意义:这个变换在真实产线上会不会出现?不会出现就别开。
5.2 迁移学习与冻结骨干权重
小数据集的第二个救星是迁移学习。直接用 COCO 预训练权重加载 yolov8s.pt 作为起点,而不是从零训练,收敛速度和最终精度都会好很多。加载预训练权重之后还能再进一步:冻结骨干网络的前若干层,只训练 head。
冻结骨干的原理是,预训练模型的浅层已经学到了通用的边缘、纹理、颜色特征,这些特征对油污缺陷同样适用,不需要在小数据集上重新学。冻结之后参数量大幅减少,训练更快,也不容易过拟合。做法是加载模型后手动冻结:
from ultralytics import YOLO model = YOLO("yolov8s.pt") for name, param in model.model.named_parameters(): if name.startswith("model.0") or name.startswith("model.1") or name.startswith("model.2"): param.requires_grad = False model.train(data="defect.yaml", epochs=200, imgsz=640, batch=16)这段代码把前三个 stage 的权重冻结,这几个 stage 对应骨干网络的浅层卷积。冻结之后训练日志里可以看到可训练参数明显减少,显存占用也降下来。注意一个细节:如果输入分辨率从 640 改到 1280,骨干网络输入层的参数形状不变,因为 YOLO 用的是全卷积结构,这点不需要额外处理。另一个细节是冻结之后前几个 epoch 的 loss 下降会比完全不冻结慢,这是正常的,因为骨干不更新,只有 head 在学,不要因为这个就解冻。
冻结几层其实是个经验活。数据量越少,冻结层数应该越多。660 张图我一般冻到 model.3,也就是前四个 stage,如果发现验证集精度上不去,再逐步解冻最后的骨干层,让高层特征也参与微调。这种由深到浅的渐进解冻策略比一次性全量微调稳定得多。
5.3 切片推理:让小黑点在 1280 分辨率下现形
就算训练时用 640 分辨率,推理阶段也可以用切片推理补偿小目标的先天劣势。切片推理的思路很简单:把一张大图切成若干有重叠的小块,分别送入模型检测,再把结果坐标映射回原图,最后做一次全局 NMS 合并重叠区域的重复框。这个方法对小黑点的效果尤其明显,因为切片之后小目标在切片里占据了更大的相对像素面积。
切片推理的 Python 实现核心逻辑是滑动窗口加坐标还原:
import cv2 from ultralytics import YOLO model = YOLO("best.pt") def slice_infer(img_path, slice_size=640, overlap=0.2, conf=0.25): img = cv2.imread(img_path) h, w = img.shape[:2] stride = int(slice_size * (1 - overlap)) dets = [] for y in range(0, h, stride): for x in range(0, w, stride): x2 = min(x + slice_size, w) y2 = min(y + slice_size, h) crop = img[y:y2, x:x2] res = model.predict(crop, imgsz=640, conf=conf, verbose=False) for r in res[0].boxes: x1, y1, x2b, y2b = r.xyxy[0].cpu().numpy() dets.append([x1 + x, y1 + y, x2b + x, y2b + y, float(r.conf), int(r.cls[0])]) return dets这个函数的重点是坐标还原那一行:检测框坐标加上切片在整图中的起始偏移 (x, y),得到原图坐标。overlap 设 0.2 是让相邻切片有 20% 重叠,防止缺陷正好被切在边界上。切片边缘的目标虽然可能被截断,但重叠区域的存在让同一目标在相邻切片里都能被检测到,最后依据置信度选出最优框。实际使用中我把 overlap 调到 0.3,漏检率进一步下降,代价是推理时间增加 20% 左右,产线如果有实时性要求就要权衡。
切片推理还有一个附带好处:它等于隐式地用高分辨率跑了一遍推理。如果推理原图是 2000×1500,切成 640 的切片后,每个切片里的缺陷在模型看来比直接缩放到 640 要大得多。这也是为什么切片推理经常能把训练时漏掉的小黑点救回来。如果你觉得纯 Python 滑动窗口太慢,可以换成带步长的批量推理,把一批切片拼成一个 batch 送进模型,吞吐量能翻两三倍。
6. 产线落地前的最后验证:用留出集把阈值和误检钉死
模型训练完,指标好看,不等于能上线。我习惯在部署前做最后一轮验证,这轮验证不跑 mAP,而是跑两个更接近产线真实需求的指标:固定置信度阈值下的漏检率和误检数量。做法是把第 3 章划分出来的 66 张测试图当成留出集,全程不参与任何调参,最后拿来一锤定音。
具体操作是先用训练好的模型对测试集做全量推理,把每个预测框的置信度、类别、坐标存下来。然后画一张 precision-recall 曲线,在曲线上找漏检率和误检率的平衡点。油污缺陷的产线场景里,漏检一台等于把不良品放出去,代价远大于误检,所以我一般把置信度阈值压在 0.15 到 0.2 区间,宁可多框几个背景,也不能放过真实缺陷。阈值调整在 ultralytics 里就是 predict 时改一下 conf 参数,但改完一定要重新跑一遍留出集看漏检数,而不是凭感觉定。
阈值定了之后还要做一次误检聚类分析。把测试集里所有误检框裁剪出来、缩放到统一尺寸、按类别分组肉眼过一遍。这一步看起来糙,却是最有效的黑匣子排查手段:误检框通常高度相似,比如镜头反光、面板边缘划痕、灰尘颗粒,聚类之后就能看出模型把哪类背景当成了缺陷。看完误检你会发现,660 张图里有一小部分标注本身是错的,模型对错标注的拟合恰恰是你调参永远调不过去的坎。
从那以后我每次拿到新数据集,都会先花一个晚上做误检聚类,再决定要不要重新清洗标注。这个习惯帮我避开了至少三次上线即翻车的尴尬。定型模型之后记得把训练用的 YAML、增强参数、阈值、切片参数一起存进同一个配置目录,下次换产线型号时直接对照着改,能省掉大半重复调参的时间。希望帮到你。
本文还有配套的精品资源,点击获取