简介:面向煤矿智能分选与视觉检测场景的YOLOv8格式识别数据集,围绕煤炭、煤矸石、高岭石三类目标提供现场采集的原始图像与标注文件,适合目标检测初学者、算法工程师以及煤矿智能化项目团队用于模型训练、验证和算法调研。数据包共205个文件,包含102张现场jpg图片、102个对应的txt标签文件,以及1个yaml类别配置文件,整体压缩后仅2.26MB,轻量易用,可在本地快速跑通YOLOv8训练流程,不必等待大体积数据集下载。目前已有459人学习浏览,属于典型的小样本真实场景数据集。透过内容预览可见图片命名保留了原始采集编号与标注来源,便于按批次筛选和检查标注质量;将yaml配置与txt标注直接放入YOLOv8工程即可开始训练,也可结合数据增强扩展样本规模,用于验证小样本条件下煤矸石检测的可行性。这份资源适合需要快速搭建煤矸石识别原型或进行预实验的开发者参考使用。
1. 煤矸石识别数据集:为什么这 102 张现场图比几千张网图更值得用
做选煤厂视觉分选或者矸石山监测的算法工程师,大概率都经历过这种尴尬:跑通的模型在演示视频里一切正常,一到现场换条皮带、换个光照就掉点。煤矸石识别数据集这个项目,正好卡在这个痛点上——它不是从网上爬的合成图,而是 102 张现场采集的原始图片,已经按 yolov8 格式标注好煤炭、煤矸石、高岭石三类目标,拿来就能跑训练流程。102 张听起来很少,但现场图的价值在于背景、光照、粉尘干扰都是真实的,做迁移学习的底子比几千张干净网图扎实。适合两类人:一类是刚接触 yolov8、想用一套真实数据走通完整流程的开发者,另一类是需要快速验证「厂区视觉分选到底可不可行」的预研工程师。如果你是冲着「下载即得工业级模型」来的,建议先看完后面几章——小样本数据集的坑,一个都不会少。
2. 看懂这套 yolov8 格式标注:三类目标的视觉边界与文件结构
2.1 煤炭、煤矸石、高岭石,现场视觉上怎么区分
拿到数据集先别急着训练,先搞清楚这三类目标在图像里到底长什么样。这里的分类不是矿物学意义上的严格区分,而是视觉分选意义上的定义——模型学的是颜色、纹理、形状这些表观特征,不是化学成分。
煤炭在可见光下表面呈黑色到深黑褐色,块状,断口处有贝壳状纹理,新鲜断面有光泽,整体形状相对规则。煤矸石的颜色跨度很大,从灰黑色到灰白色都有,质地更硬,边缘更尖锐,表面粗糙,没有煤炭那种油润光泽。高岭石则是灰白到白色,细粒状,容易破碎,表面有细腻的粉感,和浅色煤矸石的区分点是颗粒更细、颜色更白更均匀。
听起来边界清晰?现场图不是实验室图。暗色煤矸石在阴影里和煤炭非常接近,白色高岭石在强光下会和浅色矸石混在一起。所以看标注的时候要注意标注方对边界是怎么定的——这决定了你自己补充数据时能不能保持一致。一般现场采集数据的标注规范会写「以人工分选结果为准」,意思是标注员是见过实物或者跟着选煤厂工人确认过的,不是纯看图猜的。
2.2 yolov8 格式标注的文件结构:一张图对应一个 txt
yolov8 格式的标注核心就是 YOLO 的 txt 标注文件:每个 txt 文件名和对应的图片名一致,放在 labels 目录下,图片放在 images 目录下。txt 里的每一行代表一个目标框,五个值分别是:类别 id、归一化中心点 x、归一化中心点 y、归一化宽、归一化高。
比如一张 1920x1080 的现场图里有个煤矸石目标,框的左上角是 (960, 300),右下角是 (1200, 600),那么标注行就是1 0.5625 0.4167 0.125 0.2778。类别 id 从 0 开始,常见的映射是 0=煤炭、1=煤矸石、2=高岭石,但不同数据集的映射可能不一样,训练前先确认自己的 data.yaml 里的类别顺序,不然会出「模型把煤认得特别准,因为标注全串了」这种低级翻车。
拿到数据之后我一般第一步不是训练,而是把标注文件全部读一遍,确认边界框有没有越界、宽高有没有变负数、类别 id 有没有超出数量。写个脚本几分钟就能跑完,省得后面训练时冒出一堆 nan loss 还不知道为什么。
import os from pathlib import Path label_dir = Path("datasets/coal_gangue/labels/train") img_w, img_h = 1920, 1080 # 按数据集的原始分辨率填 problems = [] for txt_path in label_dir.glob("*.txt"): for line_idx, line in enumerate(txt_path.read_text().strip().splitlines()): parts = line.split() if len(parts) != 5: problems.append(f"{txt_path.name}:{line_idx} 字段数不对") continue cls_id, cx, cy, w, h = parts cx, cy, w, h = float(cx), float(cy), float(w), float(h) if not (0 <= int(cls_id) <= 2): problems.append(f"{txt_path.name}:{line_idx} 类别越界 {cls_id}") if w <= 0 or h <= 0 or w > 1 or h > 1: problems.append(f"{txt_path.name}:{line_idx} 框尺寸异常") if not (0 <= cx <= 1 and 0 <= cy <= 1): problems.append(f"{txt_path.name}:{line_idx} 中心点越界") print(f"检查 {len(list(label_dir.glob('*.txt')))} 个文件," f"发现 {len(problems)} 个问题") for p in problems[:20]: print(p)代码逻辑很直接:遍历标签目录下每个 txt,按行解析五个字段,逐一校验类别 id 范围、框宽高、归一化坐标。归一化坐标理论上必须在 0 到 1 之间,如果源标注是基于某个裁切过的图片做的但忘了同步更新,这里就能发现。
这个脚本跑完没问题,再往下走。
2.3 数据体检:类别分布、目标尺度和框密度
102 张图不是均匀分布三类目标的。矸石山场景里煤矸石可能占了大部分,高岭石可能只有十几张图里有。这种类别不平衡如果不提前知道,训练时会发现高岭石的 mAP 一直上不去,不是你参数调得不好,是从根上数据就偏。
我一般会写一个统计脚本,把所有标注的类别数量、框的宽高分布、单张图的目标数量都压出来。目标尺度尤其重要——如果绝大多数框的宽高都在整张图的 5% 以下,那么训练时 imgsz 设置成 640 可能不够用,得考虑提到 1280,或者接受小目标漏检的现实。
import numpy as np from pathlib import Path label_dir = Path("datasets/coal_gangue/labels/train") stats = {0: [], 1: [], 2: []} # cls_id -> 框的相对面积列表 for txt_path in label_dir.glob("*.txt"): for line in txt_path.read_text().strip().splitlines(): parts = line.split() cls_id = int(parts[0]) w, h = float(parts[3]), float(parts[4]) stats[cls_id].append(w * h) # 归一化面积 for cls_id, areas in stats.items(): areas = np.array(areas) print(f"类别 {cls_id}: 框数 {len(areas)}, " f"归一化面积 中位数 {np.median(areas):.5f}, " f"最小 {areas.min():.5f}, 最大 {areas.max():.5f}")参数和逻辑说明:这里统计的是每个目标框相对整图面积的占比,0.01 就代表框只占图的 1%,属于典型的小目标。对煤矸石分选场景,目标往往贴在皮带上、互相遮挡,框面积普遍偏大但重叠严重,所以这个指标能直接告诉你模型需要多大的特征分辨率才能「看清」目标。
数据体检做完,心里有底了,再进训练环节。
3. 用 yolov8 跑通三类目标检测:数据集组织、训练参数与增强策略
3.1 数据集目录组织与 data.yaml 配置
yolov8 官方仓库对数据集目录的约定是 images 和 labels 分开,训练集、验证集可以按子目录分,也可以用 txt 文件列举图片路径。102 张图本来就少,不建议再按 80/20 硬切——后面避坑章节会细说。这里先按最标准的目录结构摆好。
datasets/ coal_gangue/ images/ train/ img_001.jpg img_002.jpg ... val/ img_091.jpg ... labels/ train/ img_001.txt ... val/ img_091.txt ...对应的 data.yaml 内容:
path: datasets/coal_gangue train: images/train val: images/val nc: 3 names: 0: coal 1: gangue 2: kaolinite注意path建议用相对路径,或者直接用绝对路径,别用../这种相对写法依赖当前工作目录——换台机器跑直接报错找不到数据集,我踩过这种坑。names的顺序必须和标注 txt 里的 class id 一致,之前提过。
3.2 训练参数设多少:102 张小数据集的调参逻辑
大多数人第一次跑 yolov8 都是直接敲yolo detect train data=... model=yolov8n.pt epochs=100,对着默认参数开训。默认参数是大数据集调出来的,小数据集上照搬会出问题。先说结论,再解释为什么。
我一般用这个组合:
yolo detect train \ model=yolov8n.pt \ data=datasets/coal_gangue/data.yaml \ epochs=300 \ batch=16 \ imgsz=640 \ workers=4 \ optimizer=SGD \ lr0=0.01 \ mosaic=0.5 \ close_mosaic=20 \ patience=50 \ device=0 \ project=runs/detect \ name=coal_gangue_102参数说明:yolov8n是 nano 版本,参数最少、最快,小数据集上先用小模型把流程跑通,别一上来就yolov8x——102 张图喂大模型,100% 过拟合。epochs=300看起来多,但配合patience=50(50 个 epoch 验证集指标不涨就提前停)实际不会跑满,这是用小数据集训练的关键。mosaic=0.5是 mosaic 增强启用概率,前 100 个 epoch 有一半的 batch 会做 mosaic 拼接,后 20 个 epoch 完全关闭(close_mosaic=20),让模型在正常分布上微调收敛。SGD + lr0=0.01是小数据集上最稳的组合,Adam 系收敛快但容易在小样本下跑飞。
batch=16这个值要看显卡显存,1660Ti 6G 跑 640 分辨率、nano 模型,16 是安全的,8G 以上显存可以加到 32。如果显存紧张,优先调小 batch 而不是调小 imgsz——图片分辨率直接决定小目标能不能被检测到。
3.3 训练过程怎么看:损失、验证指标与过拟合信号
训练启动之后不要干等。打开runs/detect/coal_gangue_102/目录下的results.png,里面画了 train/val 的 box loss、cls loss 和 mAP 曲线。前 50 个 epoch 里 val loss 下降是正常的,如果 train loss 一直在降但 val loss 在 100 epoch 后开始反弹,说明已经过拟合了,这时候看weights/last.pt和weights/best.pt的差距——两个文件差很多,说明最佳模型出现在训练中段,后面全在硬背训练集。
from ultralytics import YOLO # 训练完成后加载 best.pt,跑验证集并输出每类指标 model = YOLO("runs/detect/coal_gangue_102/weights/best.pt") results = model.val( data="datasets/coal_gangue/data.yaml", split="val", imgsz=640, conf=0.25, iou=0.5, )代码逻辑:model.val()用的就是 data.yaml 里指定的 val 集。conf=0.25是置信度阈值,iou=0.5是 NMS 的 IoU 阈值,这两个只是推理参数,不影响已经训练好的权重。输出的results.results_dict里有mAP50和mAP50-95,但更要看 per-class 的 AP——如果 coal 的 AP 有 0.8、gangue 只有 0.3,那问题不在整体训练,在数据或标注本身,得回到第 2 章的统计结果去查。
到这里,训练闭环已经通了。但 102 张图的数据集,光会跑通不够,还得知道哪些地方会翻车。
4. 小样本目标检测必踩的 5 个坑:现象、原因与解法
4.1 高岭石类别 AP 极低:类别不平衡比你想象的更严重
现象:训完看 per-class AP,coal 和 gangue 都能到 0.7 以上,kaolinite 只有 0.1 甚至更低,换任何增强参数都救不回来。
原因:102 张图里高岭石只在十几张中出现,且每张只有一两个目标。yolov8 的损失函数里类别损失按样本数量加权,占比小的类别梯度贡献微弱,模型倾向于把所有目标都预测成占多数的类别。
解决:先统计各类别框的数量(用第 2.3 节的脚本),如果差距超过 5 倍,考虑对高岭石图片做离线增强——复制高岭石的标注并加载到背景图上生成额外样本,注意别让同一张图的增强版本同时出现在训练集和验证集。另一个做法是提高cls_pw这个类别的正样本权重,但这种操作要小心调过头导致其它类别 AP 下跌。
4.2 标注边界争议:煤炭和暗色煤矸石肉眼不可分
现象:训练 loss 正常下降,但 val 的混淆矩阵里 coal 和 gangue 互相错认的比例特别高。
原因:在弱光或粉尘遮挡下,暗色煤矸石和煤炭在视觉上本来就难分。如果标注规范不明确,不同标注员甚至同一个标注员前后标准不一致,模型学到的是一个矛盾的边界,自然没法稳定区分。
解决:回到标注数据上,把预测错误最多的图片抽出来,看模型哪些框预测成了另一类。如果连人都很难通过视觉确认,就去找现场工人核对样本——他们用手掂一下、敲一下就能判断。确认错的标注后修正 txt 里的 class id,这个工作没有捷径,属于数据集质量的必经投入。
4.3 mosaic 增强在小数据集上的虚影问题
现象:训练 loss 收敛很快,但模型对紧挨在一起的目标会漏检或者框偏。
原因:mosaic 把 4 张图拼接在一起训练,目标是模拟目标密集的场景。但数据集只有 102 张,拼接后大量目标被裁切,产生大量只有半个目标、没有完整特征的「虚影标注」。模型学会了「半截目标也能是目标」,实际推理时反而对完整目标犹豫。
解决:把小数据集下的mosaic=0或者0.25,同时把close_mosaic保留在最后 20 个 epoch。如果场景里本身就有密集遮挡,需要模拟密集目标,可以用copy_paste增强——把一个图里的目标实例复制到另一张图里,但这是高级技巧,先用关闭 mosaic 来治虚影问题。
4.4 切分验证集随缘:高岭石全进了训练集或验证集
现象:训练完 val 指标很好看,但实际部署一测,高岭石一个都检不出来。
原因:做数据集切分的时候用了随机划分,而高岭石图片只有十几张,随机划分时可能全部落进训练集,验证集里一张高岭石都没有,val 指标自然好看但失去了意义。
解决:切分之前先按类别分布做分层采样,最粗暴的做法是手动确认验证集里每类都有样本,且占比和整体分布接近。我通常的做法是:先把包含高岭石的图片挑出来单独编号,抽 15% 到 20% 进验证集,剩下的其它类别再随机补。这是数据切分里最不值得省的时间,省了后面全在返工。
4.5 光照和角度变化导致泛化崩塌
现象:模型在数据集的图片上表现不错,拿到现场新拍的图,同样的目标检不出来或者大量误检。
原因:102 张图很可能来自同一天、同一个机位、同一种光照条件。yolov8 会在背景纹理上过拟合——模型可能记住了「左上角那团阴影」而不是「黑色的块状物体」。
解决:训练前期的数据增强里把光照相关项拉高:hsv_h=0.05、hsv_s=0.5、hsv_v=0.5,同时适度加degrees=5(小幅旋转)和translate=0.1(小幅平移)。更重要的是采集策略:如果可能,再补几批不同时间段的现场照片,哪怕每批只有 30 张,对泛化能力的提升比增加 100 张同条件图片都明显。
5. 从训练到能用:验证指标的正确打开方式与部署到边缘设备的路径
训练结束、坑也踩完一轮之后,最后一步是把这个模型真正用起来。先说实话:在 102 张图上训练出来的模型,验证集 mAP 到 0.8 以上只能说明「这个数据集上还行」,离「选煤厂可靠运行」还有距离。真正决定能不能用的,是混淆矩阵和实际场景的抽测。看混淆矩阵的时候重点看 off-diagonal——coal 被预测成 gangue 的比例超过 5% 就要警觉,矸石混入煤炭和煤炭混入矸石的经济后果完全不同,分选场景里错分率比 mAP 更重要。
部署到边缘设备的话,rk3588 是当前性价比很高也常被点名的选择。yolov8n 训练完的权重是 PyTorch 格式,先导出 ONNX。
yolo export model=runs/detect/coal_gangue_102/weights/best.pt format=onnx opset=12 imgsz=640导出后用onnx2rknn工具转成 RKNN 格式,这一步需要在 x86 机器上装 rknn-toolkit2,转换完成后在板端用 rknn-toolkit-lite2 做推理。实际部署时注意三个细节:一是 RKNN 转换时量化方式选hybrid而不是全int8,小模型全量化掉点很明显;二是板端推理时预处理不要做太多归一化操作,RKNN 输入对齐好数据集的预处理逻辑;三是输入分辨率尽量和训练一致,rk3588 用imgsz=640跑 nano 模型可以实时,提到 1280 会掉帧严重。
最后聊一点实战习惯。我拿到任何小样本检测任务,都不会只用一轮训练就完事——训练完先让模型推理全部 102 张图,把置信度低但肉眼确定的目标找出来,修正标注或者补充标注,然后再训一轮。这个过程叫自举,一轮下来数据质量会有实质提升。
另外一个小技巧:训练时给同一份数据准备两个随机种子跑两遍,对比 loss 曲线。如果两次训练的最佳 mAP 差异超过 0.1,说明当前数据量下模型强烈依赖随机初始化,再多的调参都治标不治本,优先加数据而不是换模型结构。
最后是一点教训:一开始做类似数据集的时候,我迷信「模型越大越好」,直接上 yolov8x,结果 100 多张图训出来的模型过拟合到惨不忍睹。后来换 yolov8n 先把流程和数据质量摸清楚,效果反而翻倍。小样本场景,先小模型、强增强、盯混淆矩阵,这条路走得通。希望帮到你。
本文还有配套的精品资源,点击获取