资讯动态

YOLO岩石矿物检测数据集解析与训练实战指南

发布时间:2026/10/5 11:29:36 来源:尧图企业网站定制
简介面向岩石矿物识别与目标检测研究的一份YOLO格式数据集专供地质学者、矿业工程师及计算机视觉开发者用于矿物质检测模型的训练与评估。压缩包内共2000个文件含861张岩石表面JPG图像和1138个配套txt标签以及1个用于可视化检测框的Python脚本整体大小约59.08MB数据已按训练集、验证集划分并完成数据增广可直接用于YOLO全系列网络训练。类别覆盖石英、斑铜矿、黄铁矿等8种矿物class文件提供了清晰的类别映射方便直接训练与调参show脚本可在图上绘制边界框便于直观检查标注质量与模型输出。目前已有455人浏览学习。整体资料既包含图像标注又带有可视化调试工具适合刚接触YOLO目标检测的入门者快速上手也可为矿物识别科研项目提供可复用的数据集基础。1. 岩石表面矿物质检测1000 张标注图能不能撑起一个 YOLO 项目野外勘探拍回来的岩石照片最大的瓶颈往往不是算法而是手里有没有一份能直接喂给模型的标注数据。这份岩石表面矿物质检测数据集正好绕开了最磨人的数据整理环节超过 1000 张图片、每张都带目标检测标签按 YOLO 的 txt 格式存放直接复制到训练框架就能跑。数据集覆盖 8 个矿物类别包含石英、斑铜矿、黄铁矿等已经拆好训练集和验证集并做了一轮数据增广资源里还附了 show 脚本能把 bounding box 画回原图做质量检查。适合矿物自动识别、深度学习课设以及第一次想完整跑通 YOLO 训练-验证-推理链路的人。别指望文件拉到就能拿高分样本不均衡的问题依然存在——它的价值在于把从零开始标数据直接变成从数据开始调模型。2. 数据集解剖YOLO 文件名、类别映射与标签格式的底层规律拿到数据集先别急着解压训练花十分钟把目录结构和标签文件读清楚后面能省下大量排查时间。这段主要拆解三个东西文件名透露的信息、class 文件和 txt 标签的格式。2.1 文件名结构前缀编号、.rf 标记与 hash 后缀从项目原始文件可以看出图片命名是这种形式0011_jpg.rf.7a7655516b6ade13a199ad135383cba7.jpg。这个文件名由三部分组成0011_jpg原始采集编号jpg表示原图是 JPG 格式.rfRoboflow 平台导出时的标记说明这份数据集的中转来源7a7655516b6ade13a199ad135383cba7随机 hash用于区分同一张原图增广出来的不同版本。老手看文件名就能判断一件事如果同一前缀0011_jpg下面有多个不同 hash 的图片说明原始图被做了离线增广这几张图会同时出现在目录里。这个信息很重要——它直接关系到后面统计“真实样本量”时会不会把增广副本误算成独立样本也关系到验证集是否可能混入训练图。标注文件名和图片名一一对应只是把.jpg换成.txt。比如图片叫0011_jpg.rf.xxx.jpg对应的 label 就是0011_jpg.rf.xxx.txt。在 YOLO 训练框架里图片和标签的对应关系就是靠这个同名规则建立的目录摆错或者改名都会导致跑训练时大量报错 “image without label”。2.2 class.txt8 个矿物类别的 id 映射数据集一共有 8 个类别石英、斑铜矿、黄铁矿及其他矿物具体名称必须以资源里的 class 类别文本文件为准因为类别在导出时排过序顺序决定了每个分类编号。下面是 class.txt 在训练里被引用的方式示意以实际文件内容为准class_id类别名称示例0石英1斑铜矿2黄铁矿3类别44类别55类别66类别77类别8class_id 是从 0 开始计的不是从 1 开始。这个看似不起眼的细节是目标检测里最常见的越界错误来源如果训练框架里配了nc8但标签里出现了整数 8说明某个标注被当成了第九类训练时这个框会直接被忽略或者报错。调节类别顺序时有一个原则不要手动重排 class.txt 里的行。一旦重排所有标签文件里的 class_id 都需要跟着改label 里的 0 会变成另一个类别的 0训练出来的模型会“指鹿为马”。2.3 标签 txt 的 cxcywh 格式从像素框到归一化坐标YOLO 格式的标签每行只有五个数字比如2 0.483921 0.554688 0.191633 0.241797含义从左到右依次是类别 id、目标中心点 x 坐标相对图像宽、目标中心点 y 坐标相对图像高、目标宽度相对图像宽、目标高度相对图像高。全部取值范围在 0 到 1 之间这就是“归一化”。如果要从普通目标检测数据集的像素坐标转成 YOLO 格式常见做法是写一段小脚本import os def convert(size, box): 把像素坐标转换成一行的 YOLO 归一化标注 size: (图像宽, 图像高) box: (x_min, y_min, x_max, y_max) 像素坐标 返回: [class_id, cx, cy, w, h]h 和 w 均已归一化到 0~1 dw 1.0 / size[0] dh 1.0 / size[1] cx (box[0] box[1]) / 2.0 cy (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return [round(cx * dw, 6), round(cy * dh, 6), round(w * dw, 6), round(h * dh, 6)] # 使用示例图片宽高为 (1920, 1080)框的像素范围如下 box [565, 643, 912, 1040] # x_min, x_max, y_min, y_max norm convert((1920, 1080), box) print(norm) # [0.419, 0.753, 0.181, 0.368]逻辑说明convert函数先把中心点和宽高的像素值算出来再除以图片宽高做归一化。之所以要 round 到 6 位小数是因为 YOLO 官方和 Ultralytics 读标签时按浮点解析精度保留太多反而没有意义。需要注意的是输入的box顺序如果是x_min, x_max, y_min, y_max那么box[0]和box[1]是 x 方向范围box[3]和box[2]才是 y 方向范围这个顺序写反框就会上下颠倒。数据集里的标签已经是这个格式不需要重新转换但读懂换算关系对后面写校验脚本非常有用。2.4 train/val 划分验证集为什么不能随便合并这份数据集已经划分好训练集和验证集各自的图片和标签都放在对应目录下。划分的目的一是训练中每轮结束后评估 mAP二是判断模型是否过拟合。常见的错误做法是为了“让指标好看”把验证集也并回训练集重新训练最后报告一个虚高的 mAP。在真实项目里这是典型的 trick但在学习或交付评测时属于数据泄露验证集如果参与了训练模型的泛化能力就无法被真实评估换一批实拍图立刻露馅。正确的划分习惯是确认资源自带的 train/val 划分不被改动如果你需要测试集再从 train 里按比例抽一部分比如 10%并且保证抽取的图片不来自同一批增广副本。抽取时我一般按文件前缀分组做分层抽样而不是随机逐张抽——逐张随机抽容易让同一原始场景的两个增广版本一个进训练、一个进测试宽高比相似的样本会造成测试指标虚高。3. 训练前的落地准备目录规约、标签校验与可视化核对为什么总有人训练时“玄学掉点”十次里有八次是数据入口的问题。先把目录按常用框架的约定摆好再用脚本做一轮格式体检最后把标注画回图片上人眼过一遍这三步比调参管用得多。3.1 目录规约按 Ultralytics 的习惯摆放目前主流 YOLO 训练框架包括 ultralytics约定数据集的目录结构是datasets/ └── rock_surface/ ├── images/ │ ├── train/ │ │ ├── 0011_jpg.rf.xxx.jpg │ │ └── ... │ └── val/ │ ├── 0011_jpg.rf.yyy.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0011_jpg.rf.xxx.txt │ │ └── ... │ └── val/ │ ├── 0011_jpg.rf.yyy.txt │ └── ... └── class.txt逻辑说明images 和 labels 是两个同级目录train/val 在两侧保持同名同结构训练框架通过 images/train 找到图片再把images替换成labels找到对应标签。class.txt 放在数据集根目录方便后面生成训练 yaml 时直接读取类别名。在这里面有一个容易忽略的点资源如果默认是平铺结构所有图片和标签都在一个目录里你需要自己移动文件生成上面的树形结构。移动时不要用图形界面拖拽用脚本按train.txt / val.txt的清单去分发没有任何清单的话按文件名 hash 随机分成 90/10 也可行但最好先用资源里自带的划分。3.2 标签体检一个脚本查越界、查空框、查错类模型训练前我有一个写了很久没删过的体检脚本——它是针对 YOLO 格式的“最后一层防线”。脚本做三件事检查类别 id 是否越界、检查归一化坐标是否超出 0~1、检查是否存在完全为空的标签文件。import os from collections import Counter labels_dir rock_surface/labels/train # 按实际路径修改 num_classes 8 # 与 class.txt 行数一致 class_counter Counter() bad_files [] for fname in os.listdir(labels_dir): if not fname.endswith(.txt): continue path os.path.join(labels_dir, fname) with open(path, r, encodingutf-8) as f: lines [line.strip() for line in f if line.strip()] if not lines: bad_files.append((fname, empty_label)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append((fname, field_count_error)) continue cid int(parts[0]) cx, cy, w, h map(float, parts[1:]) if cid num_classes: bad_files.append((fname, fclass_id_{cid}_out_of_range)) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): bad_files.append((fname, normalized_range_error)) class_counter[cid] 1 print(各类别框数量:, class_counter) print(异常文件数量:, len(bad_files)) for item in bad_files[:20]: print(item)逻辑说明这个脚本不依赖任何第三方库用os.listdir遍历标签目录按行解析每个标注。parts的长度必须为 5否则说明行里少了类别或者坐标类别 id 越界、归一化坐标超范围都会被收集到bad_files列表里最后一次性列出来。class_counter同时给你一份各类别的框数量分布——这一步能提前暴露出样本不均衡程度比如某个类别只有十几个框那后面训练时必须特殊处理。参数说明num_classes必须和 class.txt 的实际行数一致不能凭印象写 8 就完事如果 class.txt 里有注释行或者空行先去掉再数。脚本输出里的“异常文件数量”为零才算是过了第一关。3.3 可视化核对把 box 画回图上看一眼数据集的 show 脚本就是干这件事的读取图片和对应 txt把每个标注框画到图上。如果你不想依赖资源里自带脚本下面这段代码可以直接复现同样的效果import cv2 import os image_dir rock_surface/images/train label_dir rock_surface/labels/train out_dir visual_check os.makedirs(out_dir, exist_okTrue) for fname in sorted(os.listdir(image_dir))[:50]: # 只看前 50 张 if not fname.endswith((.jpg, .jpeg, .png)): continue img cv2.imread(os.path.join(image_dir, fname)) h, w img.shape[:2] txt_path os.path.join(label_dir, fname.rsplit(., 1)[0] .txt) if not os.path.exists(txt_path): continue with open(txt_path) as f: lines [line for line in f if line.strip()] for line in lines: parts line.split() if len(parts) 5: continue cx, cy, bw, bh map(float, parts[1:5]) # 把归一化坐标还原成像素坐标用于画框 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, parts[0], (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(os.path.join(out_dir, fname), img) print(可视化结果已保存到, out_dir)逻辑说明代码核心是坐标还原——归一化坐标乘以图像的宽和高就能得到像素坐标系下的框角点。x1, y1是左上角x2, y2是右下角parts[0]是类别 id画在框上方方便肉眼核对。打印保存在visual_check目录里而不是用cv2.imshow是为了避免服务器环境没有显示界面时报错。参数说明[:50]控制检查数量第一次建议肉眼过 50 张就够判断标注质量fname.rsplit(., 1)[0]是按最后一个点把后缀去掉兼容多重.jpg文件名比如.rf.xxx.jpg。如果画出的框和矿物实际边界对不齐比如框沿包含了大量背景这类样本会在训练中持续给模型传递噪声逐张剔除比硬着头皮训练更划算。3.4 离线增广与在线增强的边界这份数据集做过离线增广目录里那些同前缀不同 hash 的文件就是结果。离线增广的好处是标注已经同步生成好训练时零成本使用风险在于如果增广参数开得太猛比如旋转 90 度、加大量噪声样本分布会偏离真实拍摄场景。而当前 YOLO 训练框架自带的在线增强Mosaic、HSV 抖动等是在训练读取图片时动态执行的每个 epoch 生成的图都不同可以理解为“无限数据”。对小样本数据集通常建议同时保留两路增强——数据集自带的离线增广保证数量框架的在线增强保证多样性。如果你发现训练出来的模型误检率偏高优先检查是不是训练时把在线增强参数调到过高比如hsv_h0.05是常规值调到0.5会让岩石颜色失真模型学到的全是色块特征而不是矿物本身的纹理边界。4. 训练与验证用 YOLO 吃下这份数据集的关键细节数据体检完了接下来就是把数据喂给 YOLO 系列网络。这里以 YOLO 常用训练框架为例流程对 v8、v11 等版本通用因为数据格式是同一套。4.1 写一个 data.yaml数据集入口文件YOLO 训练框架读数据依赖一个 yaml 文件。把下面内容放到rock_surface.yaml# 数据集的 yaml 入口文件 path: /your/absolute/path/datasets/rock_surface # 数据集根目录 train: images/train # 相对 path 的训练图片目录 val: images/val # 相对 path 的验证图片目录 nc: 8 # 类别个数和 class.txt 行数一致 names: 0: quartz 1: bornite 2: pyrite 3: mineral_4 4: mineral_5 5: mineral_6 6: mineral_7 7: mineral_8逻辑说明train和val写成相对路径path写绝对路径。这样框架解析时会自动到path下找images/train并类推到labels/train。names里的类别名不需要和 class.txt 完全一致但顺序必须一致——0这一行的语义是“所有标签里 class_id 为 0 的框”如果你在 names 里把 0 写成黄铁矿训练和结果图上的标签都会跟着错。参数说明nc错了影响最大。写成 7 会让最后一个类别的样本在训练时被当作背景写成 9 会让模型为了凑数而额外收敛一个空类。建议写完后用前一章的脚本再输出一次最大类别 id对照确认。path里不要用 Windows 盘符加反斜杠的写法统一用正斜杠。4.2 从预训练权重开始迁移学习的意义在这份数据集上从零随机初始化权重训练1000 张图完全不够用。原因很直接目标检测网络前面的卷积层学的是边缘、纹理、色块这些底层特征在大规模数据集上已经被学透了随机初始化意味着要拿这几百张岩石图重新学一遍底层特征mAP50 大概率卡在 0.3 上下。常见做法是加载官方预训练权重继续训练。启动命令如下# 在数据集根目录执行model 可以根据显存换成 yolov8n / yolov8s / yolov8m yolo train modelyolov8s.pt datarock_surface.yaml imgsz640 batch16 epochs100 device0 projectruns namerock_surface_v1逻辑说明modelyolov8s.pt会先加载预训练权重然后模型结构调整输出层类别数时最后一层会重新初始化其余层保留预训练参数继续微调。对这份 8 类岩石数据集yolov8s 是性价比比较稳的选择——n 太小特征提取能力捉急m 以上对显存要求高而且当前样本量不足以支撑大模型的容量。参数说明imgsz640是训练输入分辨率岩石表面目标如果整体偏小可以调到 768 或 896 提高小目标检出率但显存占用按平方上涨batch16在 12GB 显存左右跑得动 s 模型如果 OOM 就先减半 batchdevice0指定第一块 GPU省得 CPU 硬撑。epochs100是常规值如果时间紧可以先 50 轮看趋势再决定是否续跑。4.3 训练日志损失、精度、召回率怎么读框架训练过程中会在控制台持续输出指标也会在runs/rock_surface_v1目录写下 results.csv。关键列名和判断标准指标含义这份数据的经验值box_loss / cls_loss边界框回归损失和分类损失持续下降末尾 10 轮起伏在 0.01 内precision (P)预测框里真正是矿物的比例0.7 以上算健康recall (R)真矿物被检出来的比例0.8 左右说明漏检可控mAP50IoU 阈值 0.5 的平均精度0.8 以上可以开始部署测试mAP50-95更严格的综合精度比 mAP50 低 0.15-0.3 正常读日志时最容易犯的错误是只盯 mAP50。矿物检测场景里漏检一块矿石比误检一块背景更致命所以我会优先看 recall 和 mAP50-95。如果 mAP50 很高但 recall 很低说明模型“宁缺毋滥”要么把置信度阈值调低要么往样本少的类别上补数据。另外要注意 results.csv 里带有 fitness 列它是综合 mAP 的加权版本框架选最优权重时用它排序不是拿 mAP50 单列排序。训练结束后优先用best.pt而不是last.pt这个选择直接影响推理效果。4.4 验证与第一次推理训练完先用 val 集验证一次# 验证集评估自动输出 mAP 指标 yolo val modelruns/rock_surface_v1/weights/best.pt datarock_surface.yaml imgsz640 batch16再看一张实际图片效果from ultralytics import YOLO # 加载训练好的权重对单张图片推理并保存结果 model YOLO(runs/rock_surface_v1/weights/best.pt) results model.predict( sourcetest_samples/0011_jpg.rf.xxx.jpg, # 你自己挑一张没参与训练的图 conf0.25, # 置信度阈值 saveTrue, save_txtTrue, # 同时保存标签到 runs/detect/predict/labels ) print(results[0].boxes.cls) # 打印这张图检出的类别 id逻辑说明推理时如果模型表现远不如验证集先看是不是输入尺寸不一致训练时是 640推理时也要保持 640框架会自动 letterbox。save_txtTrue会把推理结果写成 YOLO 格式标签这一步对后续做结果分析比如统计漏检类别特别有用。参数说明conf0.25是常见起始值如果发现矿物漏检降到 0.1 看召回率上升多少代价是误检增多如果误检太多升到 0.5。第一次推理建议选一张 val 集里的图、一张完全没参与训练的实拍图两张对比着看。5. 避坑记录这份岩石矿物数据集最容易翻车的五个位置下面五条是我拿到类似数据集后真实踩过的坑按发生频率排序。每一条都按现象到原因到解决给出适合先扫一遍再回去改配置。5.1 类别编号错位模型把石英框认成了黄铁矿现象验证集 mAP 看起来很高但可视化输出里同一张图的同一个框类别标签明显张冠李戴石英矿物的框上面写着黄铁矿。原因数据集的 class.txt 顺序和你在 data.yaml 里手写的 names 顺序不一致。标签文件里 class_id0 永远对应 names 里的第 0 项你要是把 names 数组按自己的直觉排模型学到的语义就和显示对不上。这是最隐蔽的翻车点因为 mAP 数值完全不受影响只有可视化时才发现。解决直接用 class.txt 的内容生成 names不要手抄。写个小脚本按行读取 class.txt 生成 yaml 的 names 字段顺序保持原样改任何一行都等于改全部标签。训练前再用可视化脚本画 20 张图人眼扫一遍类别名对不对。5.2 标签坐标越界框画到图像外面去现象训练日志没有报错但可视化时发现个别框左下角超出图像范围或者某个框宽度超过图片本身。原因原始标注可能有少数边界框包含了岩石边缘之外的区域或者是离线增广尤其是翻转和平移时没有把越界部分裁剪干净。YOLO 训练时一般会忽略 IoU 计算中超出边界的部分但不会帮你纠正这些脏框持续传递噪声。解决跑第 3 章的体检脚本把越界样本挑出来对越界幅度小的框做裁剪把坐标 clamp 到 0~1对越界幅度大的直接删掉这条标注。记住删除标注不是删除图片图片可能还有别的有效目标。5.3 类别不均衡少数类怎么都检不出现象训练 100 轮mAP50 在 0.8 附近但单独看某一个类别的 AP 只有 0.1检查发现这个类别在训练集里只有几十个框。原因岩石数据集的自然分布里不同矿物的出现频率差异很大。模型为了把整体 loss 降下来会把容量分配给样本多的类少样本类几乎被忽略。解决三件事按顺序做。第一用体检脚本统计每个类别的框数量确认不均衡比例第二对少样本类别做针对性离线增强比如水平翻转、小角度旋转、亮度抖动只做数据复制不改变语义第三训练时对少样本类调低置信度阈值或者设置自定义类别损失权重。框架支持传入按类别顺序的损失加权参数给样本少的类加大权重模型会更加注意这些类别。5.4 验证集指标虚高一换实拍图立刻掉点现象验证集上 mAP50 连续几个 epoch 稳步上涨最后停在 0.9 左右把权重拿到露天矿区实拍照片上一测召回率掉到一半都不到。原因最常见的两个原因都在标注环节。一是验证集和训练集来自同一批增广副本同一个原始场景的轻微变化版本分别进了 train 和 val模型记住了场景而不是矿物本身二是目录划分时用了随机逐张划分导致相似图像被拆到两侧。解决检查目录里是否出现了“同名前缀不同 hash”的文件分跨 train/val如果有必须重新画划分边界。正确做法是按原始图片前缀分组划分同一个原始场景的所有增广副本必须待在同一个集合里。之后再做一次可视化把验证集的图片挑几张看确认训练和验证图片在背景纹理上没有明显重叠。5.5 小目标全漏岩石纹理里的矿物颗粒检不出来现象召回率低特别是远距离拍摄的岩石表面矿物颗粒占图面积不到 2%模型一个也检不出。原因训练分辨率 640 时小目标在特征图上的尺寸小于一个网格特征图下采样后信息基本丢失此外如果标签里小目标比例很高通用目标检测损失函数也会倾向于忽略 IoU 小的框。解决优先把 imgsz 从 640 提到 896 或 1024显存不够就减 batch 或换更大显存训练时打开close_mosaic10让最后 10 轮关掉 Mosaic 增强避免小目标被进一步稀释推理时把 conf 降到 0.1 看看召回变化。如果还不行就需要考虑切图检测把大图切成多个 640 瓦片分别推理再合并结果——这是岩石矿物这类高分辨率场景下的常见工程做法。6. 进阶技巧把这份数据集的剩余价值榨干的三件小事到这里模型能跑了但 1000 张图的数据集远没有用完。以下几件事能让你从同一份资源里多榨出几个点的 mAP而且每一步都不复杂。6.1 二次迁移先矿物后矿区第一次用官方预训练权重在这 1000 张图上训练得到 best.pt。到了新项目别从零来用这份 best.pt 作为起点继续在新矿区少量图上 finetune因为它已经见过石英、黄铁矿这些底层纹理再学习新露头只需要少量样本。实际项目里我把 100 张新图配 300 轮微调mAP 就能追平从零开始训 1000 张的效果。6.2 验证时开 TTA用多尺度推理换召回训练完做最终评估时框架的 TTATest-Time Augmentation会同时用多尺度和水平翻转推理再把结果合并。对岩石这种纹理密集的场景TTA 对 recall 的提升经常有 2~3 个点。代价是推理时间翻倍适合离线跑结果不适合实时检测。from ultralytics import YOLO model YOLO(runs/rock_surface_v1/weights/best.pt) # 开启 TTA 验证多尺度 翻转后融合结果 metrics model.val(ttaTrue) print(metrics.box.map50, metrics.box.map) # mAP50 与 mAP50-956.3 检测后处理形态学开运算滤孤立误检低置信度阈值在高分辨率岩石图上容易带出零星孤立的误检框。我一般先把 conf 压到 0.1 保证召回再对输出掩码做形态学开运算将误检的孤立点滤掉这是不重新训练的前提下在工程端把精确率拉回来的常用手段。import cv2 import numpy as np # mask_vis 是模型输出的二值化前景掩码0/255 kernel np.ones((5, 5), np.uint8) # 开运算先腐蚀去掉孤立噪声再膨胀恢复目标主体 cleaned cv2.morphologyEx(mask_vis, cv2.MORPH_OPEN, kernel, iterations2)从那以后我每次拿到新数据集第一件事都是先跑一遍可视化脚本和统计脚本这两样不过关我不会把任何一张图喂进训练轮。这个习惯帮我躲过了至少三次类别错位和增广样本混入验证集的问题希望你也能少走这几段弯路。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑