
简介面向煤矿智能化与选煤现场识别场景提供一套以102张现场采集原始图片为基础的煤矸石识别数据集覆盖煤炭、煤矸石、高岭石三类物料的典型形态适合目标检测、图像分类模型的训练与对比验证也适合机器视觉初学者理解真实工业样本的标注方法。压缩包共105个文件主体为102张jpg现场图片另含1个COCO JSON标注文件与2个txt说明文档整包约2.27MB轻量易用便于快速开展实验。目前已有426人学习浏览。数据集采用COCO JSON格式标注可直接接入主流检测框架省去手动标注时间txt文件说明类别与使用要点有助于理解现场样本构成与标注规则对煤矸石分选、高岭石识别等智能监控场景有直接参考价值。1. 煤矸石识别数据集让分选机看懂煤与非煤做选煤厂智能化改造的同行应该都有体会煤矸石分选环节靠人工手选工人盯着皮带看一天漏检率和误检率都靠“人肉扛”。这两年大家想用视觉方案替代人眼第一步就卡在数据集上——公网能下的煤流图片要么是实验室摆拍要么分辨率低到连边缘都看不清楚。这个煤矸石识别数据集的定位很直接102张现场采集的原始图片COCO JSON格式标注覆盖煤炭、煤矸石、高岭石三类目标拿过来就能喂给检测模型做训练或微调。适合的群体很明确做矿山固废识别、选煤厂自动化、矸石分选算法验证的算法工程师和学生尤其是手头没有现场数据、想先用真实工况图片把流程跑通的人。2. 标注格式与数据体检COCO JSON 里到底有什么拿到数据集第一件事不是开训练是把标注文件拆开看一遍。COCO JSON 是目标检测领域最常见的标注格式之一很多开源工具和模型框架原生支持但这个数据集是从现场图片人工标注后导出的字段里有一些细节需要先确认清楚否则后面转换格式或训练时容易踩坑。2.1 标注文件的三大核心字段COCO JSON 是一个典型的嵌套字典结构顶层包含images、annotations、categories三个数组。images数组记录每张图片的文件名、宽高和 IDannotations数组记录每个标注框属于哪张图、类别是什么、框的坐标和大小categories数组定义类别 ID 到类别名称的映射。看一个简化后的结构长什么样# 用 python 读取并概览 coco json 结构 import json from collections import Counter with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) # 1. 类别映射 cats {c[id]: c[name] for c in coco[categories]} print(categories:, cats) # 2. 每类目标数量 ann_cats Counter(a[category_id] for a in coco[annotations]) for cid, cnt in ann_cats.items(): print(fclass {cats[cid]:6s} count: {cnt}) # 3. 单张图片的目标数量分布 img_ann_cnt Counter() for a in coco[annotations]: img_ann_cnt[a[image_id]] 1 print(avg objects per image:, sum(img_ann_cnt.values()) / len(img_ann_cnt))这段代码的作用是把标注文件的“家底”摸清楚三类的样本量各有多少、平均每张图有多少个框。拿到手先跑一遍能立刻判断类别是否均衡、有没有空的标注文件。从实际数据来看煤炭和煤矸石的样本量会明显多于高岭石这是现场工况的天然特征——采煤工作面出来的物料本身就以煤和矸石为主高岭石属于伴生矿物出现频率低一些是正常的。2.2 bbox 坐标的读取细节COCO 格式的 bbox 是[x, y, width, height]四个值都是绝对像素坐标左上角为原点。注意它不是 YOLO 那种归一化的中心点坐标也不是[x1, y1, x2, y2]的对角坐标。如果之前只用过 YOLO 格式这里非常容易搞混。标注的框用[x, y, w, h]表示读出来要画框验证时直接按这个顺序解析就行# 把 coco bbox 画到原图上做可视化校验 import cv2 def draw_coco_bbox(img_path, bbox, label, color(0, 255, 0)): img cv2.imread(img_path) x, y, w, h [int(v) for v in bbox] cv2.rectangle(img, (x, y), (x w, y h), color, 2) cv2.putText(img, label, (x, y - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) return img可视化验证这一步千万别省。COCO JSON 是从标注工具导出的导出过程偶尔会出现坐标偏移、框和物体没对齐的情况。每类抽 5 张图把框画出来看一眼比训练完发现 mAP 异常再回头查数据要快得多。这里有一个常见做法是写一个批量可视化脚本把全部 102 张图的标注框拼成一张大图快速扫一遍大约 10 分钟能完成全部检查。2.3 文件名里的导出痕迹细看图片文件名比如012_png.rf.1fcc9085833b5c1b93d64d030804ac7e.jpg中间的_png和rf是标注平台导出的命名残留。这个信息对使用没有影响但能说明这批图片经历了从原始采集到平台标注再导出的流程。文件名末尾的哈希值是平台自动生成的唯一标识用来避免重名可以直接忽略。训练时如果后续自己补充数据建议统一重命名避免两批数据的文件名格式不一致导致image_id冲突。3. 从 COCO 转到 YOLO 格式转换脚本与实践细节YOLO 系列v5/v8/v11 等默认使用 txt 格式的标注每行一个目标内容是class_id x_center y_center width height前四个数值都归一化到 0~1。训练前把 COCO JSON 转成 YOLO txt 是必经步骤网上转换脚本很多但边界情况处理不干净翻车概率不低。3.1 一个稳妥的转换脚本下面这个脚本按图片为单位逐个转换重点处理了三个细节类别 ID 重新映射、宽高除零防护、坐标越界裁剪。注意 COCO 的 category_id 不一定是连续的比如从 1 开始但中间跳号YOLO 要求类别从 0 开始连续编号所以要做一层映射。# coco2yolo.py - 将 COCO JSON 转为 YOLO txt 标注格式 import os, json from tqdm import tqdm def coco2yolo(ann_path, img_dir, out_dir): with open(ann_path, r, encodingutf-8) as f: coco json.load(f) # 类别映射coco category_id - yolo class_id从0开始 cat_id_map {} for i, cat in enumerate(coco[categories]): cat_id_map[cat[id]] i print(category mapping:, cat_id_map) img_info {img[id]: img for img in coco[images]} for ann in tqdm(coco[annotations]): img img_info[ann[image_id]] img_name img[file_name] w, h img[width], img[height] txt_path os.path.join(out_dir, os.path.splitext(img_name)[0] .txt) x, y, bw, bh ann[bbox] # 坐标越界保护 x, y max(0, x), max(0, y) bw min(bw, w - x) bh min(bh, h - y) # bbox 可能是 float需要转 float 再归一化 x_c (x bw / 2) / w y_c (y bh / 2) / h bw_n bw / w bh_n bh / h cls_id cat_id_map[ann[category_id]] line f{cls_id} {x_c:.6f} {y_c:.6f} {bw_n:.6f} {bh_n:.6f}\n with open(txt_path, a, encodingutf-8) as fo: fo.write(line) print(fdone. labels saved to {out_dir})执行方式很简单python coco2yolo.py annotations.json images labels/。转换之后每张 jpg 旁边会生成一个同名 txt训练时只要图片和 txt 同名同目录YOLO 就能自动配对读取。参数说明x_c、y_c是归一化后的中心点坐标bw_n、bh_n是归一化后的宽高六位小数对 640 尺寸的训练图来说精度足够越界裁剪和负坐标保护主要是防标注平台导出时手滑画出去一点点不处理的话训练时会报坐标异常。3.2 转换后的抽查方法转换不是跑完就结束要随机抽几张图把 YOLO label 反向画到图上和原图对比。用 OpenCV 读 txt 画框脚本逻辑就是把归一化坐标乘以宽高还原成像素值再画矩形。这个操作是被验证数据是否正确的最后一道防线。如果画出来的框和 COCO 格式画出来的完全一致说明转换没问题才能进入训练环节。一个小插曲第一次转换时没做类别映射直接拿了category_id当 YOLO class结果高岭石因为 ID 不连续被跳过训练时类别错位严重mAP 直接崩了。从那以后我转换脚本里强制打印cat_id_map每次先人工确认类别对应关系无误再继续。4. 用 YOLOv8 训练煤矸石检测模型配置与参数调优数据格式搞定之后训练环节的核心问题有两个一是 102 张的小样本量怎么控制过拟合二是现场皮带场景的拍摄角度和光照变化怎么模拟。我用 YOLOv8 来跑通完整链路这个框架对自定义数据集的接入最省事先讲配置再讲训练参数的选择逻辑。4.1 数据划分与 dataset.yaml先按 8:2 划分训练集和验证集。注意划分时要按“场景”而不是简单随机打散。现场采集的图片往往是同一时段、同一角度连拍的看着是 102 张实际可能只覆盖了 3~4 个独立场景。随机划分会导致同一场景的图片同时出现在训练集和验证集里验证指标虚高。# split_dataset.py - 按文件名前缀做场景感知划分 import os, random, shutil from collections import defaultdict img_dir images/ label_dir labels/ imgs sorted(os.listdir(img_dir)) # 按文件名指纹聚类现场连拍通常前缀相同 scene_groups defaultdict(list) for img in imgs: scene_key img.split(_)[0] # 按前缀分组 scene_groups[scene_key].append(img) train_imgs, val_imgs [], [] for scene, imgs_in_scene in scene_groups.items(): random.shuffle(imgs_in_scene) split int(len(imgs_in_scene) * 0.8) train_imgs imgs_in_scene[:split] val_imgs imgs_in_scene[split:] print(ftrain: {len(train_imgs)}, val: {len(val_imgs)})按前缀分组的逻辑比较粗但对付现场连拍数据够用。如果图片命名没有明显规律建议手动按拍摄时间或皮带位置分组后再划分。data.yaml 内容如下# dataset.yaml train: /path/to/dataset/train/ val: /path/to/dataset/val/ nc: 3 names: [coal, gangue, kaolinite]nc是类别数names顺序必须和转换脚本里的cat_id_map一致顺序错了就是全部误检。这里有一个极大的坑如果 COCO 的 categories 定义顺序是[coal, gangue, kaolinite]但 names 列表写成了[kaolinite, gangue, coal]训练不会报错但每个类别全部错位推理结果会完全错误。4.2 训练命令与参数含义训练命令用官方推荐的默认配置起步但针对小数据集微调几个关键参数yolo detect train \ datadataset.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ patience30 \ optimizerAdamW \ lr00.001 \ augmentTrue \ close_mosaic10参数含义拆开讲modelyolov8n.pt是 nanon 版本预训练权重煤矸石识别不是特别复杂的任务n 模型精度够用且训练快epochs150对 82 张训练图来说足够充分更多的轮次不会带来明显收益close_mosaic10是最后 10 个 epoch 关闭马赛克增强避免增强太强导致模型在最后阶段学不到真实分布patience30是早停耐心值验证集指标连续 30 轮不升就停止训练防止无效等待。写数据集资源时为什么要强调这一点因为用 YOLOv8n 跑 150 轮在单张 RTX 3060 上大约需要 20~30 分钟如果参数不当一个下午就搭进去了。4.3 过拟合控制策略82 张训练图的体量模型很容易在训练集上刷到 99% 的准确率但验证集表现惨淡。这是小样本训练的通病控制策略按优先级排列第一是数据增强。YOLOv8 自带的增强策略已经不错煤矸石检测里最有效的是hsv_h和hsv_s模拟不同光照条件下煤流的颜色偏移degrees旋转增强要谨慎开大皮带上的煤块不会倒着放fliplr翻转可以放心开水平翻转不改变语义。第二是冻结 backbone 训练。用freeze10冻结前 10 层让模型先专注学习检测头后期再解冻微调。对煤矸石这种纹理特征偏底层的任务冻结策略能明显减少过拟合。我的经验是先用freeze10训练 50 轮再解冻全模型训练 50 轮效果比一口气训 100 轮稳定。5. 常见问题与避坑记录现场数据集的五个翻车点拿这个数据集跑训练过程中有几个高频问题基本每批用户都会遇到。我不写大而全的排错手册只挑实际复现时最容易栽的五个按现象、原因、解决的顺序梳理。5.1 验证集 mAP 高但现场新图检测效果差现象训练时验证集 mAP0.5 能到 90% 以上拿到现场拍的新图测试漏检严重尤其是高岭石基本检不到。原因前文提到的场景划分问题。数据集内部图片相似度高随机划分导致验证集和训练集出现了“同源泄露”。模型相当于先偷看了答案测试时换一批真正没见过的新场景就露馅了。这类数据集常见于此。解决严格按场景或拍摄批次重新划分数据集验证集只放模型从未见过的场景宁可少放验证集也不能混入同源图片。5.2 COCO bbox 画出来偏了半个身位现象用脚本把标注可视化发现有些框明显没包住目标物有的框比目标大了一圈。原因标注平台的框有的是用[x1, y1, x2, y2]导出的有些工具为了节省存储用归一化相对坐标导出到 COCO JSON 时若没有正确反算就会出现偏移。另外人工标注本身也有误差现场图片中煤块和矸石相互堆叠框的边界模糊。解决可视化脚本抽查是第一步发现偏移量呈系统性规律比如每个框都向右偏 10%说明是格式转换 bug统一修复如果是个别框不准需要人工修正或者训练时把iou损失权重提高一点点。5.3 训练 loss 降不下去卡在某个值上不动现象训练集 loss 前几十轮正常下降到 80 轮左右开始震荡不再继续下降。原因数据里存在明显的漏标。现场采集的图片目标密集标注时漏了几个框是常态。模型预测到了漏标目标但没被计入 loss导致梯度方向被这些“未标注目标”干扰。解决用训练好的模型对训练集做一次预测把预测框和人工标注框叠在一起找没有标注框但置信度高的区域重点检查。这是补标最快的方法也是小数据集质量提升性价比最高的一步。5.4 高岭石类别 AP 惨不忍睹现象confusion matrix 里高岭石频繁被识别成煤矸石AP0.5 只有不到 40%。原因两类样本量不均衡且高岭石常和矸石伴生视觉特征上都是浅灰色块状边界本来就模糊。解决除了数据增强试试把loss_gain里的cls权重从 0.5 提到 1.0让模型更重视分类损失或者用weighted sampler对高岭石样本过采样每轮多抽几次。如果还不行只能用更大的模型yolov8s替换 n给分类头更多容量。5.5 导出 ONNX 后推理结果和原模型不一致现象pt 模型推理正常导出 ONNX 后在部署环境推理同样的图结果差很多。原因导出时opset版本跟部署环境不兼容是最常见原因另外部分增强层如Mosaic的随机性在静态图里和动态图不一致。解决导出时固定opset12如果是 N 卡部署用 TensorRT导出后先做精度比对用 10 张图分别跑 pt 和 ONNX对比框坐标偏差不超过iou0.5才算通过# export_and_compare.py - 导出 onnx 并做精度比对 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) model.export(formatonnx, opset12) onnx_model YOLO(runs/detect/train/weights/best.onnx) img val_sample.jpg res_pt model(img) res_onnx onnx_model(img) # 对比两个结果的 boxes 坐标偏差满足阈值则通过6. 数据增强调参与部署验证把小模型用到现场的最后一个动作如果前面的步骤都走完了训练出的模型在验证集上表现正常下一步是把它推向现场。这个阶段有两个关键工作微调增强参数让模型泛化能力更强以及完成部署前的最后验证。增强参数调整不光是开开关要按现场工况对齐。皮带上的煤流有两个特征一是光照随车间环境波动白天自然光和晚上灯光色温差异很大二是煤块矸石在运动中会产生运动模糊。模拟这两类扰动我一般会在 YOLOv8 的训练配置里手动覆盖增强参数augment: hsv_h: 0.02 # 色调偏移模拟不同色温 hsv_s: 0.6 # 饱和度变化 hsv_v: 0.5 # 亮度变化模拟自然光/灯光切换 degrees: 5.0 # 小角度旋转适应皮带抖动 translate: 0.1 scale: 0.4 # 尺度变化模拟物距波动 fliplr: 0.5这些值的设置逻辑hsv_h不要开太大煤块本身颜色变化有限太大反而引入不真实的色彩degrees5就够了皮带上的煤块姿态相对固定过大旋转会让模型学到不可能出现的倒置形态scale0.4比较关键现场相机高度固定但不同粒径矸石在画面里的尺寸差异大尺度增强帮助模型适应不同大小的目标。最后聊聊部署验证的兜底方案。现场环境是动态的模型在离线测试集上表现好不代表上线一定稳。一个低成本的验证方法是连续录制 30 分钟的皮带视频把视频切成帧用模型跑一遍统计每帧的检测框数量和时间分布。如果出现检测框数量突然归零又恢复的间歇性失灵多半是光线突变或者煤流密度异常需要检查相机的自动曝光设置而不是急着调模型。如果每帧检测数量都很稳定就可以考虑用 TensorRT 进一步加速或者接入分选控制逻辑了。这个数据集我反复跑了三轮训练才得到一个信得过的模型权重。第一轮急于看结果随机划分训练集验证集mAP 高得吓人现场一测就废第二轮老老实实画框检查发现 12 张图的标注有漏框补标花了一个下午第三轮才进入正常调参节奏。从那以后我每次拿到新数据集都强制走一遍可视化检查和场景划分不查完不训练。这个习惯帮我避开了后面至少三次无效训练希望帮到你。本文还有配套的精品资源点击获取