ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

小样本煤矸石检测实战:YOLOv11训练调优与部署全流程

小样本煤矸石检测实战:YOLOv11训练调优与部署全流程 简介面向煤矿智能分选与目标检测应用这份煤矸石识别数据集提供了现场采集的原始图像覆盖煤炭、煤矸石、高岭石三类目标可用于YOLOv11等模型的训练和效果验证。数据集共205个文件包括102张jpg图片、102个txt标注文件及1个yaml配置文件总大小2.23MB。其中txt文件为YOLO格式的标注框信息yaml文件定义了类别与路径配置结构简洁方便直接接入常用检测框架。由于图像全部来自实际现场背景与光照条件相对真实有助于提升模型在煤矿环境下的泛化能力。目前已有1077人学习下载适合从事矿物识别、煤矿自动化检测的开发者以及刚接触YOLO目标检测的学员作为小规模实战数据集使用。1. 102张图也能训出能用的煤矸石检测模型煤矿现场搞视觉识别第一道坎不是模型选型而是数据。公开数据集里几乎没有专门针对煤矸石、煤炭、高岭石的检测集想从零采集又面临现场光线差、粉尘大、标注成本高的现实。这份煤矸石识别数据集是现场采集的102张原始图片用YOLOv11格式做了标注类别覆盖煤炭、煤矸石、高岭石三类适合直接用来做固废分选、皮带运输监控、洗煤厂工况识别这类落地场景。你可能第一反应是102张太少——确实少但这不是缺点而是真实项目里最常见的起点。这类小样本场景下训练策略、数据划分、类别平衡的取舍往往比换个更大的预训练模型更影响最终效果。本文就按我实际拆过这类项目的流程把数据集校验、YOLOv11训练、指标诊断到部署验证的完整链路走一遍。2. YOLOv11格式标注解析与数据校验2.1 Roboflow导出的YOLO标注文件到底长什么样文件名里带着_png.rf.前缀说明这批图片经过Roboflow平台预处理后导出这解释了为什么原始图片被统一重命名。每张 jpg 图片对应一个同名的 .txt 标注文件放在 labels 目录下。YOLOv11格式的每一行标注是class_id x_center y_center width height四项坐标值都是相对于图片宽高的归一化比例范围 0~1不是像素坐标。比如某行0 0.4532 0.7184 0.1245 0.1867含义是目标中心在图片横向 45.32% 位置、纵向 71.84% 位置目标宽度占整图 12.45%高度占 18.67%。类别 0 对应 data.yaml 里 classes 列表的第一项。拿到数据集第一件事不是训练而是先把标注完整性和边界规范性校验一遍。Roboflow 导出偶尔会出现标注超出图片边界、类别索引越界、空标签文件这类问题。我一般写一段脚本批量扫描import os from PIL import Image img_dir images/train label_dir labels/train for img_name in os.listdir(img_dir): if not img_name.endswith(.jpg): continue stem os.path.splitext(img_name)[0] label_path os.path.join(label_dir, stem .txt) if not os.path.exists(label_path): print(f[MISSING] {img_name} 缺少标注文件) continue img Image.open(os.path.join(img_dir, img_name)) w, h img.size with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f[FORMAT] {label_path} 格式异常: {line.strip()}) continue cid, xc, yc, bw, bh int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if cid 3: print(f[CLASS] {label_path} 类别索引越界: {cid}) if xc - bw/2 0 or xc bw/2 1 or yc - bh/2 0 or yc bh/2 1: print(f[BOUNDARY] {label_path} 标注超出图像边界: {line.strip()})这段脚本逐张检查三件事标注文件是否存在、每行是否满足五个字段、坐标是否越界。跑完之后如果只有零星边界告警可以直接用不必逐一修正——YOLOv11训练时的矩形填充增强能容忍小幅越界。但如果出现大量缺失标注文件就要找回原始导出包重新解压不要硬训。2.2 类别分布统计102 张图三分类最怕的是某类只有十几个实例。先统计每张图各目标数for f in labels/train/*.txt; do awk {print $1} $f done | sort | uniq -c这个命令把 train 集下所有标注文件的第一个字段类别ID提取出来归类计数。得到类似0 328、1 47、2 23这样的结果就说明类别严重不平衡。煤炭是主类别、煤矸石次之、高岭石最少这是煤系固废识别场景的典型分布。高岭石样本量只有 23 个目标如果直接训练模型大概率会把高岭石全部学成背景或者混淆到煤矸石里。2.3 数据集目录结构与划分YOLOv11 训练前需要把数据整理为固定结构datasets/gangue/ ├── data.yaml ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/data.yaml 内容如下path: /home/user/datasets/gangue train: images/train val: images/val nc: 3 names: 0: coal 1: gangue 2: kaolinitepath写绝对路径避免相对路径解析的坑train和val写相对于path的子目录路径。类别顺序必须和标注文件里的 class_id 完全对齐我自己曾经在复制别人项目时把 names 顺序写错导致模型训完煤炭高岭石全反了这个比参数调错更隐蔽。102 张图一般按 8:2 划分即 82 张训练、20 张验证。shuffle 时注意保证三分类在训练集和验证集都有覆盖避免某一类全被分到验证集导致训练集缺类。实际划分逻辑YOLOv11 命令行里不直接支持分层抽样需要先用 Python 脚本完成按类别分层划分再写入目录结构。3. YOLOv11 环境配置与训练参数选择3.1 从零搭一个 YOLOv11 训练环境YOLOv11 依托 ultralytics 框架训练前先确认 GPU 驱动可用nvidia-smi python -c import torch; print(torch.__version__, torch.cuda.is_available())nvidia-smi 能看到显卡型号和驱动版本torch 输出应为2.x.x True。如果 torch.cuda.is_available() 返回 False先检查 PyTorch 版本和 CUDA 版本的匹配关系常见的坑是 conda 默认装了 CPU 版 PyTorch需要去 PyTorch 官网用指定 CUDA 版本的命令重装。安装 ultralyticspip install ultralytics装完之后验证是否能正常调用yolo predict modelyolo11n.pt sourcetest.jpg这条命令下载官方 yolo11n 预训练权重并对 test.jpg 做一次推理能跑通说明环境完整。我在 CPU 机器上也跑过这套流程yolo11n 前向推理一张 640x640 图片大约 2~3 秒训练则不建议用 CPU102 张图虽然数据量小但 CPU 上跑 100 个 epoch 也要几个小时起步有 GPU 哪怕 GTX 1660 都能控制在十几分钟内完成一轮实验。3.2 关键的训练参数为什么不能照搬默认值训练命令yolo detect train \ modelyolo11n.pt \ data/home/user/datasets/gangue/data.yaml \ epochs200 \ imgsz640 \ batch16 \ patience50 \ projectruns/gangue \ nameexp1 \ pretrainedTrue \ optimizerAdamW \ lr00.001 \ mosaic1.0逐项说明modelyolo11n.pt选用 nano 版本预训练权重作为起点。数据量只有 102 张模型容量不能大yolo11s 及以上版本在这么少的数据下极易过拟合nano 参数量小、正则化压力低是当前场景的最优解。区别是 yolo11n 大概 2.6M 参数yolo11s 大概 9.4M差了一个量级。epochs200不是越多越好。用patience50做早停连续 50 个 epoch 验证集 mAP 不提升就自动终止。小数据集上通常 120~160 epoch 就收敛了如果 200 epoch 还没停基本说明学习率有问题或者数据划分有问题。batch16在 102 张图的规模下足够。batch 过大配合小数据集会加剧过拟合batch 过小则 BN 层统计不稳定。imgsz640是 YOLOv8 时代的默认训练尺寸煤矸石和煤炭在采集图片里通常占比不小640 能保留足够细节不需要为了小目标刻意升到 1280——102 张图下升分辨率反而让模型更容易死记训练集。lr00.001是一个相对保守的初始学习率。YOLOv11 默认是 0.01但那是针对大规模数据集的小数据集大学习率会让损失在前几个 epoch 就震荡甚至发散。mosaic1.0保持开启。Mosaic 增强把四张图拼成一张变相扩大了有效样本数量对 102 张的小数据集来说是性价比最高的数据增强手段。3.3 训练过程的关键监控指标训练开始后终端会周期性打印损失值和指标。需要盯住三个东西第一条是box_loss和cls_loss的下降曲线。正常情况是前 20 个 epoch 快速下降之后缓慢收敛两条曲线不应该出现突然回升的尖峰。第二条是验证集指标通常每 10 个 epoch 评估一次查看mAP50和mAP50-95的变化。mAP50是 IoU 阈值 0.5 下的平均精度对检测框位置要求较宽松适合先看模型学没学到目标mAP50-95是多个 IoU 阈值的平均更严格适合对比不同实验的细微差距。第三条看训练集和验证集 loss 的差值如果训练集 loss 持续下降而验证集 loss 停滞甚至上升说明过拟合已经开始应提前停止而非跑满 epochs。训练结束后权重保存在runs/gangue/exp1/weights/下best.pt是验证集指标最优权重last.pt是最后一个 epoch 的权重。后续推理和部署都用best.pt不要为了好奇去用last.pt。4. 训练结果诊断混淆矩阵与过拟合排查4.1 用混淆矩阵定位类别混淆训练完成后ultralytics 会在runs/gangue/exp1/目录生成confusion_matrix.png和results.png。混淆矩阵能直观看出哪两个类别互相误判。煤矸石和高岭石在视觉上都是灰白偏黄、块状堆叠模型最容易把两者混淆。如果混淆矩阵显示gangue类大量被预测为kaolinite或者反过来不要急着加数据先检查标注质量。找几张被误判的图片放大看标注框是否紧贴目标边界——很多现场采集的图片里煤矸石和高岭石在同一个料堆里互相遮挡标注框边缘压到了另一类物体上这会让模型学到错误的边界特征。标注框的具体位置可以用下面这段推理脚本叠加可视化from ultralytics import YOLO model YOLO(runs/gangue/exp1/weights/best.pt) results model.predict( sourcedatasets/gangue/images/val, saveTrue, conf0.25, iou0.45, projectruns/gangue, nameinspect )conf0.25表示只保留置信度大于 0.25 的检测框这个阈值比默认的 0.25 低方便在诊断阶段看到更多低置信度预测有助于发现漏检和边界模糊的情况。iou0.45是 NMS 的 IoU 阈值设置过低会导致同一目标输出多个重叠框设置过高会让密集堆叠的目标被合并成一个框。可视化结果保存在runs/gangue/inspect/逐张翻一遍基本能定位到是标注问题还是模型能力不足。4.2 train 和 val 指标的差值意味着什么打开 results.png 看训练曲线。一个常见错觉是训练集 loss 降到很低就认为模型训练得很好。小样本场景下训练集 loss 趋近于零恰恰是风险信号。看训练集和验证集的mAP50曲线如果训练集 mAP50 接近 1.0验证集只有 0.6 左右中间落差大说明模型把训练集细节背下来了在没见过的新图上表现打折扣。这时优先考虑的不是换更大的模型而是引入更强的数据增强策略。4.3 数据增强的实战调节ultralytics 支持在训练命令中直接开关或调节增强参数。小样本数据集上我按以下顺序调整yolo detect train \ modelyolo11n.pt \ data/home/user/datasets/gangue/data.yaml \ epochs200 \ imgsz640 \ batch16 \ hsv_h0.02 \ hsv_s0.8 \ hsv_v0.5 \ degrees10.0 \ translate0.1 \ scale0.4 \ fliplr0.5hsv_h0.02色调变化幅度降低到 0.02因为煤矸石和煤炭的颜色是强判别特征色调调太大容易让模型忽略颜色差异。hsv_s0.8和hsv_v0.5保持默认适度变化饱和度和明度可以改善模型对现场不同光线条件的鲁棒性。degrees10.0旋转范围限制在 10 度以内旋转过大在堆叠场景下会导致语义异常——煤矸石没有固定的朝向但大幅旋转会把图片转出正常拍摄视角。scale0.4控制目标缩放范围配合现场拍摄距离远近不一的情况。调节增强后对比两次实验的mAP50-95如果差距在 0.05 以内说明过拟合主要受数据多样性限制增强手段已经到瓶颈。这一步做完基本能判断模型的上限在哪里。5. 模型推理与部署验证5.1 导出 ONNX 做跨平台推理PyTorch 权重不能直接部署到生产环境一般先导出 ONNXyolo export modelruns/gangue/exp1/weights/best.pt formatonnx opset12导出后得到best.onnx可以用 ONNX Runtime 做推理。ONNX 格式的好处是摆脱 PyTorch 运行环境依赖部署到无 GPU 的边缘设备或 Windows 工控机只需安装 onnxruntime 库。验证导出的 ONNX 模型和原 PyTorch 模型输出一致性import onnxruntime as ort import numpy as np import cv2 session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img cv2.imread(test.jpg) img cv2.resize(img, (640, 640)) img img[:, :, ::-1].transpose(2, 0, 1) img np.ascontiguousarray(img, dtypenp.float32) img / 255.0 input_tensor img[None] outputs session.run(None, {input_name: input_tensor})这里做了三步预处理BGR 转 RGB、HWC 转 CHW、归一化到 0~1。这些都是 YOLOv11 训练时默认的预处理逻辑用 ONNX Runtime 推理时必须在外部手动完成。输出是三个维度的数组拼接包含边界框坐标、置信度和类别概率。自己写后处理时NMS 参数建议使用和训练时一致的iou0.45。5.2 现场视频流的检测稳定性验证部署到生产环境前用视频流跑一遍稳定性测试很有必要。我通常的做法是循环读取一段现场视频逐帧做推理并统计检测结果yolo predict modelruns/gangue/exp1/weights/best.pt \ sourcesite_video.mp4 \ conf0.35 \ iou0.45 \ saveTrue \ vid_stride5conf0.35比训练时诊断用的 0.25 高这是生产环境下的保守阈值宁可漏检也不误检——误检会把煤炭当作矸石一旦进入分选逻辑就会造成错误剔除。vid_stride5表示每 5 帧处理一帧跳帧能有效降低计算负载。检测结果导出后把每一帧的检测框数量变化拉成曲线如果出现某一帧突然大量检测到目标通常是料流经过的瞬间正常现象如果检测数量剧烈抖动则要检查模型是否把阴影或粉尘误识成目标。5.3 类别过滤只保留需要的检测结果实际项目中有时候只需要煤矸石的检测结果忽略煤炭和高岭石。推理阶段通过类名过滤from ultralytics import YOLO model YOLO(best.pt) names model.names # {0: coal, 1: gangue, 2: kaolinite} results model.predict(site_video.mp4, conf0.35, iou0.45) gangue_boxes [] for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0]) if names[cls_id] gangue: gangue_boxes.append(box.xyxy[0].tolist())model.names返回的是当前权重的类别映射字典通过类别 ID 查询名称做条件过滤。这个逻辑比在训练时去掉某一类更灵活——重新训练需要重新标注和调参推理端过滤只是改几行代码的事。同时保留所有类别的检测能力后续如果需求变化想输出高岭石统计不需要重新走一遍模型迭代流程。6. 数据扩充把小样本数据集用出几倍的增量效果样本量只有 102 张最直接的增量方式是离线数据增强。我用的是 Copy-Paste 增强策略把标注出来的煤矸石实例切下来随机粘贴到训练集的其他图片上。这个思路比简单的翻转和调色更能提升模型的遮挡鲁棒性——现场传送带上的煤矸石经常互相堆叠遮挡模型需要学会在被遮挡的情况下仍然能检出目标。具体做法是把labels中的真实目标按原始尺寸裁剪粘贴到新图上再同步生成新的标注坐标。经典的 Copy-Paste 增强代码逻辑如下import cv2 import numpy as np import random import os def copy_paste_augment(src_img, src_boxes, dst_img, dst_boxes): h, w dst_img.shape[:2] for box in src_boxes: x1, y1, x2, y2 box obj src_img[int(y1):int(y2), int(x1):int(x2)] oh, ow obj.shape[:2] if oh h or ow w: continue paste_x random.randint(0, w - ow) paste_y random.randint(0, h - oh) dst_img[paste_y:paste_y oh, paste_x:paste_x ow] obj dst_boxes.append([ (paste_x paste_x ow) / 2 / w, (paste_y paste_y oh) / 2 / h, ow / w, oh / h ]) return dst_img, dst_boxes这段增强脚本按真实标注框裁剪前景目标随机位置粘贴到另一张图的任意位置。注意dst_boxes追加的是归一化格式的[x_center, y_center, width, height]和 YOLO 训练格式一致增强生成的新标注可以直接追加到原标注文件中。这里有个细节粘贴时不要超过目标图的边界random.randint(0, w - ow)限定了横坐标范围超界会导致标注坐标计算出负数。现场拍的图片中目标往往集中在画面中部边角区域粘贴适当数量的复制目标反而能提升模型对画面边缘目标的敏感度。一个值得留意的策略是背景替换。把煤矸石从原图中抠出粘贴到不同纹理背景——水泥地面、黑色传送带、沙土地——上能有效逼着模型学习目标本身的特征而不是背景色块。有一类常见的失败案例模型把煤矸石检测成高岭石细看发现是图片背景是灰白色混凝土颜色和高岭石重叠。复制粘贴到多样背景是解决这类问题的最直接手段。最后建议保留原始验证集不动只用增强数据扩充训练集。验证集保持原始分布才能真实反映模型在现场环境的表现把增强图放进验证集会虚高指标。扩充后的训练集建议控制到原始数据的 5 倍以内超过这个量级增强噪声会占主导模型开始对着增强伪影学习反而是负优化。我通常把 102 张原始图扩到 300 张左右训练集提升明显验证集保持 20 张不动最终 mAP50 相比只用原始数据能提升 3 到 5 个百分点。本文还有配套的精品资源点击获取
返回列表