简介:面向工业质检与目标检测学习场景,这份资源提供铝片表面缺陷检测数据集,包含1000张真实场景图片,使用LabelImg标注,标注质量高,并同步提供VOC(xml)、COCO(json)、YOLO(txt)三种格式标签,分别存于不同文件夹,可直接接入YOLO系列模型训练。压缩包内共2000个文件,除核心图片与三类标签外,还附有三套数据集划分脚本、YOLO环境搭建与训练教程(含Windows/Linux版本),文件类型涵盖xml、txt、html、py、yaml,整体约60.69MB,下载和部署都很便捷。该资源已有933人学习使用,配套说明详实。无论是课程设计还是实际产线质检,通过该包都能完成从环境配置、自定义划分训练/验证/测试集到模型训练的全流程闭环,特别适合需要快速上手YOLO的目标检测初学者,也可作为工业视觉项目落地的直接数据支撑。
1. 铝片缺陷检测上手:这份YOLO数据集资源到底能给你什么
做铝片表面缺陷检测的人,最头疼的往往不是模型选型,而是数据整理。1000张图拍回来,labelimg 一张张标完,紧接着就是一连串问题:导出格式不对、训练集验证集边界模糊、标签文件名对不上图片、跑训练时提示找不到标签。任何一个问题都能让训练直接报废。这个 YOLO 铝片表面缺陷检测数据集抓的就是这一整条链路:1000 张真实场景图片,VOC、COCO、YOLO 三种格式标签齐全,不用自己写转换脚本;附带的划分脚本可以按自己需求切训练集、验证集、测试集;教程文档覆盖 Windows 和 Linux 两套环境搭建与训练流程,照着改数据 yaml 就能换成自己的数据集。适合正在做工业质检项目、准备机器人视觉毕设,或者刚接触目标检测想用真实数据跑通 YOLO 的从业者——拿到手不用重标数据,精力可以全放在训练和调参上。
2. 三种标签格式解剖:VOC、COCO、YOLO 选哪个喂给模型
很多人拿到数据集第一反应是直接开训,结果被标签格式卡住。先花十分钟搞清楚三种格式长什么样、各有什么脾气,后面能少踩一晚上的坑。这个数据集把三类标签分别放在不同文件夹下,图片是同一套,标签是三种视角,正好拿来对比理解。
2.1 三种格式与图片的存储对应关系
解压后目录结构一般是这样:
aluminum_defect_dataset/ ├── images/ # 1000张原始图片 │ ├── aluminum_0001.jpg │ └── ... ├── voc/ # VOC格式,每张图对应一个xml │ ├── aluminum_0001.xml │ └── ... ├── coco/ # COCO格式,所有标注汇总在一个json │ └── annotations.json └── yolo/ # YOLO格式,每张图对应一个txt ├── aluminum_0001.txt └── ...图片文件名和 xml、txt 标签文件名一一对应,靠文件名前缀关联。COCO 是例外,它把所有标注信息集中在一个 json 文件里,图片和标注靠 id 关联,不依赖同名文件。这个区别在划分数据集时特别重要——复制 YOLO 和 VOC 标签时按文件名前缀走就行,COCO 的 json 是整体文件,要么整个用,要么写专门脚本按 image_id 过滤,不能直接照搬复制逻辑。
2.2 同一条标注在三种格式里的写法差异
假设 aluminum_0001.jpg 尺寸是 640x480,图里有一个划痕缺陷,左上角 (120, 80),右下角 (260, 190)。VOC 格式的 xml 长这样:
<annotation> <folder>JPEGImages</folder> <filename>aluminum_0001.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>scratch</name> <bndbox> <xmin>120</xmin> <ymin>80</ymin> <xmax>260</xmax> <ymax>190</ymax> </bndbox> </object> </annotation>VOC 格式是像素绝对坐标,xmin、ymin、xmax、ymax 全都对着原图的实际像素位置。好处是直观,打开 xml 就能看出标注框在哪;坏处是图片分辨率一旦改变,坐标全部失效,必须跟着缩放。
COCO 格式的 json 片段是这样:
{ "images": [ {"id": 1, "file_name": "aluminum_0001.jpg", "width": 640, "height": 480} ], "annotations": [ {"id": 1, "image_id": 1, "category_id": 1, "bbox": [120, 80, 140, 110], "area": 15400, "iscrowd": 0} ], "categories": [ {"id": 1, "name": "scratch"} ] }COCO 的 bbox 不是左上右下两点,而是 [x, y, width, height],也就是左上角坐标加上框的宽高。area 字段可以用来过滤小目标,iscrowd 标记重叠目标。COCO 最大的优势是类别和图片信息结构化,做分割、关键点任务时扩展字段不用改格式。
YOLO 格式的 txt 只有一行:
1 0.296875 0.28125 0.21875 0.22916666666666666YOLO 格式五个数依次是:类别 id、中心点 x、中心点 y、宽度 w、高度 h,全部除以图片宽高做了归一化。算一下上面那个框:中心横坐标 (120+260)/2 = 190,190/640 = 0.296875;中心纵坐标 (80+190)/2 = 135,135/480 = 0.28125;宽度 (260-120) = 140,140/640 = 0.21875;高度 (190-80) = 110,110/480 ≈ 0.2292。归一化的好处是模型训练时不管输入尺寸是 640、1280 还是别的,标签都不用改。
2.3 选型理由与实际使用建议
三种格式不是随便选的,对应着不同的使用阶段。
YOLO 格式是训练主力。train.py 直接读 txt,速度快,省去 xml 解析和 json 解析的开销,而且归一化坐标在数据增强时不用二次处理。这个数据集默认把 YOLO 格式放一个文件夹,就是为了让你直接开训。
VOC 格式适合微调和可视化。很多老牌检测框架、可视化工具(比如读取 xml 画框调试)对 VOC 支持得最好,而且 xml 是人类可读的,排查标注问题时用文本编辑器打开看一眼就明白了。如果训练出的模型误检率高,我一般先抽几张图,把 xml 里的框画出来看看原始标注是不是有问题。
COCO 格式适合跨框架迁移。COCO 是目标检测领域的通用语言,mmdetection 之类的框架原生吃 COCO 格式,而且 COCO 的 json 里能带 iscrowd、segmentation 等扩展字段。如果之后想切到 mmdetection 或者其他新框架,直接一个 json 就能喂进去。
实际用的时候,我的建议是主用 YOLO 格式训练,出问题了回 VOC 格式排查标注,需要换框架时再读 COCO json。这个数据集三种格式都给了,省掉最让人头大的格式转换环节——格式转换脚本本身不难,但转换过程中类别编号错位、坐标归一化出错这类问题很磨人。
| 格式 | 存储方式 | 坐标系统 | 主要用途 |
|---|---|---|---|
| VOC (xml) | 每图一个文件 | 像素绝对坐标 | 可视化、标注审查 |
| COCO (json) | 全部汇总一个文件 | 左上角+宽高 | 跨框架迁移 |
| YOLO (txt) | 每图一个文件 | 归一化中心点+宽高 | 直接训练 |
3. 划分脚本实战:三步切出训练集、验证集和测试集
数据格式搞清楚之后,下一步就是划分数据集。这个资源里带了好几个 Python 脚本,功能有重叠,第一次用容易搞混。这一章把它们掰开揉碎讲清楚。
3.1 三个脚本各自干什么
压缩包里带三个划分脚本,名字已经说明了各自职责:
训练集、验证集、测试集划分脚本(图片标签划分写入新文件夹).py——按比例把图片和标签一起复制到新文件夹,生成 train 和 val(或者 train、val、test)两到三个集合,是 YOLO 训练最常用的那种目录结构。训练集、验证集划分脚本(图片标签划分写入新文件夹).py——不生成测试集,只做训练集和验证集二分,适合 1000 张这种小数据集,或者你打算把所有数据都用于训练和验证,测试单独用手头的新拍图片。split_train_val生成ImageSets下txt文件划分脚本.py——不复制文件,只生成 ImageSets/Main 下的 train.txt、val.txt 文件列表,每行一个图片文件名,这个是给 VOC 训练流程用的。
这三个脚本对应两种主流数据组织方式。前两个是 YOLO 惯例:直接把文件复制到 train/images、train/labels 这种目录。第三个是 VOC 惯例:图片和标签不动,只写一份文件清单,训练框架按清单路径去读。
train_list.txt是脚本运行后的输出产物,里面每一行是一个图片的文件名或路径。你要是用旧版 YOLO 或者某些基于 VOC 框架的代码,训练时就靠这个文件指定图片集合。
3.2 实操:按 7:2:1 划分并复制到新文件夹
这个资源里带的脚本是现成的,但理解它的内部逻辑很重要,这样你才能改比例、改路径。常见做法是这样实现的:
# train_val_test_split.py # 功能:按比例把图片和对应标签划分到 train/val/test 三个子集目录 import os import random import shutil random.seed(42) # 固定随机种子,保证每次划分结果可复现 IMG_DIR = "images" # 原始图片目录 LABEL_DIR = "yolo" # YOLO格式txt标签目录 OUTPUT_DIR = "dataset" # 划分结果输出目录 RATIOS = (0.7, 0.15, 0.15) # 训练/验证/测试比例,三项加起来必须等于1.0 # 1. 收集所有图片文件 images = [f for f in os.listdir(IMG_DIR) if f.lower().endswith((".jpg", ".jpeg", ".png"))] random.shuffle(images) # 2. 按比例计算三个集合的图片数量切分 total = len(images) train_end = int(total * RATIOS[0]) val_end = train_end + int(total * RATIOS[1]) splits = { "train": images[:train_end], "val": images[train_end:val_end], "test": images[val_end:], } # 3. 创建目录并复制图片与对应txt标签 for split_name, img_list in splits.items(): img_out = os.path.join(OUTPUT_DIR, split_name, "images") label_out = os.path.join(OUTPUT_DIR, split_name, "labels") os.makedirs(img_out, exist_ok=True) os.makedirs(label_out, exist_ok=True) for img in img_list: stem = os.path.splitext(img)[0] shutil.copy2(os.path.join(IMG_DIR, img), os.path.join(img_out, img)) src_label = os.path.join(LABEL_DIR, stem + ".txt") if os.path.exists(src_label): shutil.copy2(src_label, os.path.join(label_out, stem + ".txt")) else: print(f"警告: {stem}.txt 标签不存在,图片已复制但无标签")逻辑说明:第二步先对所有图片文件名做一次随机打乱,再按比例切片。第三步创建目标目录,用os.path.splitext取图片文件名去掉扩展名的部分,拼出对应标签路径,用shutil.copy2复制而不是shutil.move,保证原始数据不被破坏。复制标签时判断文件是否存在,不存在就打印警告——实际标注过程中总有漏标的图,这个警告能帮你发现那些"有图没标签"的问题数据。
参数说明:RATIOS改成(0.8, 0.2)就是只分训练集和验证集,但脚本里会报错因为三个值对不上,这时候用第二个二分脚本更省事。random.seed(42)里的 42 换成别的数字划分结果就变了,要想每次划分一致必须固定这个值。
运行完之后的目录结构长这样:
dataset/ ├── train/ │ ├── images/ # 约700张图 │ └── labels/ # 对应700个txt ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/这种结构正好是 YOLO 训练时 yaml 文件里要填的路径格式。
3.3 生成 ImageSets 文件列表脚本
如果你的训练框架走 VOC 路线,需要的是文件列表而不是复制文件。这个脚本的逻辑更简单:
# split_train_val.py # 功能:生成 ImageSets/Main 下的 train.txt、val.txt,只写文件名不复制文件 import os import random random.seed(42) IMG_DIR = "JPEGImages" # VOC风格图片目录 OUTPUT_DIR = "ImageSets/Main" # 输出目录 TRAIN_RATIO = 0.8 # 训练集占比 os.makedirs(OUTPUT_DIR, exist_ok=True) all_imgs = [os.path.splitext(f)[0] for f in os.listdir(IMG_DIR) if f.endswith((".jpg", ".jpeg", ".png"))] random.shuffle(all_imgs) train_count = int(len(all_imgs) * TRAIN_RATIO) with open(os.path.join(OUTPUT_DIR, "train.txt"), "w") as f: f.write("\n".join(all_imgs[:train_count])) with open(os.path.join(OUTPUT_DIR, "val.txt"), "w") as f: f.write("\n".join(all_imgs[train_count:]))逻辑说明:这个脚本只取图片文件名的不带扩展名前缀,写入 txt 时每行一个。注意它只写了文件名,没有写相对路径前缀。有的框架要求行内容是相对路径比如JPEGImages/aluminum_0001.jpg,有的只需要不带扩展名的文件名,取决于训练代码怎么读。跑完用head -n 5 ImageSets/Main/train.txt看一眼内容,再决定要不要改成带路径的写法。
参数说明:TRAIN_RATIO = 0.8表示 80% 训练、20% 验证,没有测试集。VOC 传统做法就是这样,test 集通常最后单独用test.txt指定,或者直接在验证集上评估。对于 1000 张的小数据集,我更倾向于把测试集留出来不参与任何验证调参,等模型收敛后用测试集做最终评估,这样 mAP 数字才可信。
3.4 划分完必须做的一步检查
划分完直接开训大概率翻车,先花两分钟做三件事:
第一,抽查几个目录的文件数量对齐情况。ls dataset/train/images | wc -l和ls dataset/train/labels | wc -l两个数应该一致,如果标签数少于图片数,说明源标签目录里有漏标的图。
第二,随机打开一个 txt 标签,确认里面的坐标值都在 0 到 1 之间。如果看到大于 1 的数,说明标注标签没做归一化,YOLO 训练会直接当作无标签或者报错。
第三,确认图片能正常打开。用 Python 跑一句python -c "from PIL import Image; Image.open('dataset/train/images/aluminum_0001.jpg').load()",能过说明图片没损坏。铝片表面的图片有的是生产线相机拍的,偶尔会出现零字节的坏图,不检查的话训练到一半崩溃,要倒回去找是哪个文件的问题,那才是真血泪体验。
4. 环境搭建与训练:从零跑通 YOLO 的完整流程
数据准备好了,接下来就是环境。这个资源附带了两套环境的搭建教程——Windows 版和 Linux 版,还有 Linux 下 Ubuntu 的安装教程。很多人卡在这一步,不是因为难,而是因为网上教程版本混乱,照着敲了一半发现命令对不上。这章把关键节点和坑位都标出来。
4.1 conda 环境搭建:Windows 和 Linux 共用一套命令
环境搭建的核心是先把 Python 环境和 PyTorch 装对。Windows 和 Linux 的命令几乎一模一样,差别主要在 CUDA 版本的选择上。
# 创建独立环境,避免污染系统Python,Python版本建议3.9或3.10 conda create -n yolo python=3.9 -y conda activate yolo # 先装PyTorch,CUDA版本根据本机显卡驱动来选 # nvidia-smi 查看驱动最高支持的CUDA版本 nvidia-smi # CUDA 11.8的装法,Torch版本要和CUDA匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118逻辑说明:conda create -n yolo创建一个名叫 yolo 的独立环境,Python 版本固定在 3.9,防止和你本来的其他项目依赖冲突。nvidia-smi查看的是显卡驱动,不是已安装的 CUDA 工具包,驱动版本支持 CUDA 12.x 就装 cu121 的 torch,只支持到 11.x 就装 cu118 的。
参数说明:如果电脑没有 N 卡或者跑 CPU 版,把最后一行换成pip install torch torchvision就行,但训练速度会慢几十倍,1000 张图、100 个 epoch,CPU 可能要跑十几个小时,GPU 大概几十分钟,有条件还是用 GPU。
装完 torch 之后装 YOLO 框架。这个数据集附带的训练教程,从"根据案例修改训练自己的数据集"这个描述看,走的是 YOLOv5 路线。YOLOv5 的装法:
# YOLOv5 官方仓库 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果你用的是较新的 YOLOv8 或者更新版本,装法更简单:
pip install ultralytics这里有个选择问题。YOLOv5 的代码结构更直白,train.py、val.py、detect.py三个脚本入口清清楚楚,出问题了容易排查,配套教程也多。YOLOv8 把一切都收敛到yolo命令里,入口统一,但内部封装层级多,调试时黑匣子成分更大。对第一次跑通目标检测流程的人,我更推荐按资源自带教程走 YOLOv5,至少能看见每个环节在干什么。装完跑一句python train.py --help,能弹出参数说明就说明环境没问题。
4.2 训练命令与关键参数解读
环境跑通之后,开始训练。YOLOv5 的训练命令长这样:
python train.py \ --data aluminum.yaml \ # 数据配置文件,告诉框架图片和标签在哪 --weights yolov5s.pt \ # 预训练权重,从COCO迁移过来的初始参数 --epochs 100 \ # 训练轮数 --batch-size 16 \ # 每轮迭代喂给模型的图片数量 --imgsz 640 \ # 输入图片尺寸 --device 0 # 使用第0块GPU| 参数 | 常用取值 | 说明 |
|---|---|---|
| --data | aluminum.yaml | 自定义数据配置文件路径 |
| --weights | yolov5s.pt | 预训练权重,小数据集别从零训练,收敛快得多 |
| --epochs | 100-300 | 100轮看趋势,效果不够再加到200、300 |
| --batch-size | 8-32 | 显存不够就减半,8G显存跑16没问题 |
| --imgsz | 640 | 铝片缺陷如果是小尺寸划痕,提高到1280能提升小目标mAP,但显存消耗翻倍 |
| --device | 0 | 多卡用 0,1 指定,CPU 用 cpu |
参数说明:--weights yolov5s.pt是最容易漏掉的一项。预训练权重相当于让模型带着 80 类 COCO 视觉知识起步,虽然铝片缺陷不在 COCO 类别里,但低层特征(边缘、纹理、颜色)是通用的,从预训练权重开始微调,一般 50 轮就能看到明显效果,从零训练跑到 100 轮可能还没收敛。--batch-size不是越大越好,显存不够时模型训练会直接崩掉,与其调小图片尺寸,不如先调小 batch。
4.3 把案例改成自己的数据集:aluminum.yaml 怎么写
附带的训练教程是"根据案例修改训练自己的数据集",核心工作就是写这个 yaml 文件。拿划分脚本生成的dataset目录为例:
# aluminum.yaml train: dataset/train/images # 训练集图片目录 val: dataset/val/images # 验证集图片目录 test: dataset/test/images # 测试集图片目录,可留空 nc: 3 # 缺陷类别数量 names: ['scratch', 'dent', 'stain'] # 类别名称,顺序必须和标签编号一致逻辑说明:nc后面填几,取决于你标注时有几个缺陷类别。names列表的顺序就是 YOLO 标签里 class id 的映射关系——txt 文件里第一行数字是 0,就对应 names 里的第一个名称。如果标注时定义了 3 个类别但 yaml 里只写了 2 个,训练不会报错,但类别预测会全部错位,训出来的模型一塌糊涂。
这里有个很容易翻车的细节:labelimg 标注时显示的类别序号是 1 开始,你看到的是 1、2、3,但 YOLO 的 txt 标签里存的是 0、1、2。写 names 时一定要以 txt 标签和data.yaml的顺序为准,而不是 labelimg 界面显示的序号。这个资源里的标签是已经导出的成品,直接看一眼yolo目录下某个 txt 里出现的最大值,加上 1,就是真实的类别数量,拿这个数填nc。
训练完成之后,看runs/train/exp目录下的 training 曲线图——results.png里的train/box_loss和val/box_loss两条曲线,如果随着 epoch 下降后趋于平缓,说明模型在收敛。再看val/mAP@0.5曲线,这个数值代表验证集上的检测精度,目标类别容易区分的时候,小数据集也能跑到 0.8 以上。曲线看着不对再去排查数据问题,别急着调参数。
5. 避坑与常见问题:五个反复出现的翻车现场
训练目标检测模型,坑几乎全在数据和环境上。这五个问题是我在多个数据集上反复见过的,每条都按"现象、原因、解决"的顺序展开,照着排查能省下大量时间。
5.1 训练时提示 0 labels found
现象:训练刚开始,日志里出现WARNING: 0 labels found in dataset/train/images/aluminum_0001.jpg,或者更严重的情况,全部图片都提示找不到标签,模型训练完 mAP 为 0。
原因:YOLO 训练通过图片文件名找同名的 txt 标签文件。如果标签目录放错了、文件名不匹配(比如图片叫aluminum_0001.jpg但标签叫aluminum_0001.txt的拼写不一致),或者 txt 文件内容是空的,都会出现这个警告。
解决:先看 yaml 里train字段指向的目录结构。YOLO 要求图片在train/images/、标签在train/labels/,如果你把标签直接放在train/下,框架找不到。再随机打开几个 txt 文件,确认里面确实有坐标数据,而不是零字节空文件。如果空文件很多,回去检查标注导出时的选项——labelimg 导出 YOLO 格式时,没有标注框的图片会生成空 txt,要单独清理掉。
5.2 Windows 下路径含中文导致读取失败
现象:同样的代码在 Linux 上跑得好好的,放到 Windows 上训练到一半报错,提示文件找不到或者编码错误,错误信息五花八门,有时是UnicodeDecodeError,有时是FileNotFoundError。
原因:YOLO 内部读取标签和图片时用了很多路径拼接和字节操作,中文目录名或中文文件名在 Windows 默认的 GBK 编码下和框架内部的 UTF-8 编码不一致,导致路径对不上。
解决:数据集解压后放到纯英文路径下,比如D:\datasets\aluminum,不要出现桌面、数据集这种中文目录名。图片文件名保持英文加数字的组合,标注时 labelimg 里设置默认保存路径也全部用英文。这个问题排查成本极高,因为报错位置可能跟真正的问题根源差得很远,最省事的办法就是从一开始就规范目录命名。
5.3 类别编号从 0 还是从 1 的问题
现象:训练正常完成,验证时 mAP 数值看起来还行,但用模型做推理时发现所有预测框的类别都错位,比如把 dent 预测成 scratch,而且错得有规律。
原因:labelimg 界面显示类别下拉框是从 1 开始编号的,第一项是 1,第二项是 2。但 YOLO 的 txt 标签从 0 开始,第一类是 0。如果训练代码里aluminum.yaml的names顺序和 txt 里的 id 对不上,模型学到的类别映射就全是错的。
解决:打开任意一个 YOLO 格式的 txt 文件,看第一行的第一个数字是多少。如果同一类目标有时是 0 有时是 1,说明标注过程不同批次混用了编号规则。再对照aluminum.yaml里的names列表顺序,确保 id 从 0 开始与类别一一对应。这个数据集的标签是统一导出的,一般不会出这个问题,但如果你之后自己在 labelimg 里补标了几张图再合并,就很容易踩进这个坑里。
5.4 显存不够导致训练中断
现象:训练命令跑起来,前几个 epoch 正常,突然报CUDA out of memory,训练进程直接退出。日志里能看到提示Tried to allocate 2.00 GiB之类的字眼。
原因:batch-size 和 imgsz 两个参数的乘积直接决定显存占用。8G 显存跑 batch 16、imgsz 640 在 YOLOv5s 下勉强够用,但如果同时开着浏览器、IDE,显存被其他程序占了,就会中途崩掉。铝片表面缺陷很多人想用 1280 分辨率提升小目标检测效果,显存需求直接翻四倍。
解决:先关掉其他占用显存的程序,再调参数。--batch-size 8 --imgsz 640是大多数情况下能稳定跑通的组合。如果还想再省显存,可以把--weights yolov5s.pt换成yolov5n.pt,模型参数量更小。我是这样做的:训练期间不开其他应用,给 GPU 留出全部显存,这比到处找省显存技巧有用得多。
5.5 loss 不降或者震荡
现象:训练日志里box_loss、cls_loss从一开始就不下降,或者降到一定程度后开始剧烈震荡,曲线像锯齿一样,val mAP 上不去。
原因:最常见的是学习率设置不当。YOLOv5 默认会自动调整学习率,但如果你手动指定了过大的--lr0,loss 会在最优点附近来回弹跳。另一个原因是数据本身有问题,比如标签框大面积错误、类别不均衡——铝片表面缺陷里划痕可能占了 80%,氧化斑只占 5%,模型学不好少样本类别,loss 也会停滞。
解决:先试着把--lr0 0.01改成--lr0 0.001,YOLOv5 默认的建议值在大多数小数据集上偏大。数据不均衡的情况,先去统计一下各个类别的框数量,用 Python 读一遍所有 txt 标签,把每个类别的框数打印出来。少样本类别如果占比太低,可以增加该类别的图片数量,或者用数据增强里的--mosaic参数(默认开启)来缓解。用这个数据集的 1000 张图,一般把 lr0 调低一档,训练 100 轮,loss 就能稳定下降。
6. 模型验证与置信度调优:让测试集说实话
训练完不等于结束,模型能不能上线,看的是测试集表现,不是训练集 loss。
用划分脚本留出来的test目录做最终验证,这一步很多人会跳过,直接用验证集 mAP 当最终结论。验证集在整个训练过程中参与了模型选择,数值会有轻微虚高,只有测试集是模型从没见过的数据,最能反映真实场景表现。YOLOv5 下的验证命令:
python val.py \ --data aluminum.yaml \ # 数据配置文件 --weights runs/train/exp/weights/best.pt \ # 训练出的最优权重 --task test \ # 在测试集上评估 --conf-thres 0.15 \ # 为了计算完整PR曲线,验证时置信度阈值设低 --iou-thres 0.5这个命令会输出一张详表,重点看mAP@0.5和mAP@0.5:0.95两列。小缺陷目标在mAP@0.5:0.95上通常会比mAP@0.5低 0.2 左右,这是正常现象,因为后者要求更高精度的框对齐。关键要看每个类别的 AP 值——AL 片缺陷里如果某个类别 AP 特别低,别急着调模型,回去看这个类别的标签框数量和质量。
验证通过之后,推理时的置信度门限需要单独调。训练时把--conf-thres设低是为了画出完整精确率召回率曲线,但实际部署场景要考虑误检代价。生产线上漏检一个缺陷可能造成整批产品返工,而误检一次只是多一次人工复检。我的经验是:先跑一遍detect.py用默认的 0.25 阈值看输出,统计误检和漏检的数量,再按比例调整。
python detect.py \ --source test_images/ \ # 待检测图片目录 --weights runs/train/exp/weights/best.pt \ --conf-thres 0.35 \ # 置信度门限,越高越保守 --iou-thres 0.45 # NMS重叠阈值--conf-thres 0.35是基准,误检多就往上调到 0.5,漏检多就往下调到 0.2。--iou-thres控制两个重叠框的合并,多目标密集场景下调低到 0.3,稀疏场景 0.5 够用。还有一种玄学情况:框位置准确但类别置信度上不去,尤其是氧化斑这类和铝片底色对比度低的缺陷——这时候不要在阈值上死磕,回去加对应类别的训练样本更有效。
从那以后我每次拿到新数据集,都强制自己走一遍固定流程:看三种格式标签结构、按比例划分并检查文件对齐、确认数据 yaml 无误、小 batch 试跑 5 个 epoch 验证环境、全量训练、测试集走一遍得出 mAP、最后调置信度门限。这套流程跑顺之后,铝片、钢带、焊缝各种表面缺陷数据集换着来,一周内都能从零出一个结果可复现的模型。希望这些经验对你的项目有帮助。
本文还有配套的精品资源,点击获取