ARTICLE DETAIL

资讯详情

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

基于YOLOv8的火星月球陨石坑小样本检测实践

基于YOLOv8的火星月球陨石坑小样本检测实践 简介面向目标检测研究与计算机视觉实践的火星月球陨石坑检测数据集包含132张陨石坑图像统一标注为单一类别“keng”共1044个矩形框适合用于YOLO、SSD、Faster R-CNN等模型的训练与算法验证。数据同时提供Pascal VOC格式xml和YOLO格式txt两种标注可相互对应、直接使用免去格式转换步骤对初学者和科研人员都很友好。压缩包内共398个文件以jpg原图、xml标注和txt标注为主整体仅10.27MB轻量易下载标注由labelImg工具按统一矩形框规则完成框体类别命名规范便于训练前快速检查与筛选。目前已有243人浏览学习适合天文图像分析、遥感影像识别及目标检测方向的课程设计、毕业设计或课题预研使用对于入门者可直接将txt标注导入YOLO项目完成一次完整训练直观感受陨石坑检测流程。1. 火星月球陨石坑检测数据集132张图、1044个框够不够训YOLOv8这个数据集打包名里写着VCO其实指的是Pascal VOC格式解压后你会看到标准的VOC目录结构和YOLO格式的txt标注同时存在。132张火星月球表面影像1个类别keng也就是陨石坑总共1044个矩形框。这个量级对目标检测来说是小样本但正因为小反而能逼着你把数据增强、迁移学习、训练参数这些环节吃透。它适合三类人想跑通自定义YOLO训练流程的新手、做行星遥感目标识别的学生、以及需要用少量标注验证检测方案可行性的工程师。我用YOLOv8n在这个数据集上做过完整实验接下来的格式解析、数据划分、训练调参和验证技巧都是实际跑过之后沉淀下来的做法。2. 双格式标注解析VOC XML与YOLO txt的读取和一致性校验拿到压缩包第一步不是急着训练而是先把标注文件的结构看清楚。解压命令在Linux下要注意文件名里的中文和空格必须用引号包住7z x 火星月球陨石坑检测数据集VCOYOLO格式132张1类别.7z -o./crater_dataset cd ./crater_dataset find . -type f | head -20-o指定输出目录find列出前20个文件用于确认是否解压完整。这个数据集每个样本由三件套组成jpg原图、同名xml标注、同名txt标注。我在实际项目中还遇到过解压后文件损坏的情况所以建议顺手做一次哈希校验sha256sum 火星月球陨石坑检测数据集VCOYOLO格式132张1类别.7z记录下原始哈希值解压后再比对。如果校验不一致说明7z包在下载或传输过程中已经有字节位翻转直接训练会导致标签和图像错位。2.1 文件构成与命名关系下表是单个样本的文件对应关系文件示例内容firc_yunshi_27.jpg原始遥感影像裁剪图firc_yunshi_27.xmlPascal VOC格式矩形框标注firc_yunshi_27.txtYOLO格式归一化坐标标注命名规律是firc_yunshi_编号三个文件前缀完全一致这样才能被后续训练脚本自动匹配。标注工具是labelImg标注规则是对陨石坑画矩形外接框类别名统一写keng。这里有一个容易忽略的点labelImg导出VOC时会在xml里同时写入图片尺寸信息而YOLO txt只存归一化比例不存尺寸所以千万不能只拿txt去做其他格式的转换否则一旦图片被缩放框会全部错位。2.2 用Python解析VOC XML我一般会先写一个VOC解析函数把xml里的坐标和类别读出来import xml.etree.ElementTree as ET def parse_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) boxes [] for obj in root.iter(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) boxes.append((name, xmin, ymin, xmax, ymax)) return width, height, boxes w, h, boxes parse_voc(annotations/firc_yunshi_27.xml) print(w, h, boxes)这段代码拿到图像原始宽高以及每个目标框的绝对像素坐标坐标原点在左上角x向右递增y向下递增。医学影像和遥感影像的标注规范不同但voc格式是通用的。我这里用root.iter(object)而不是root.findall是因为某些标注工具会在object节点下多套一层iter能避免漏读取。2.3 解析YOLO txt的归一化坐标YOLO txt每行有五个字段class_id x_center y_center width height全部是相对图像宽高的比例。读取逻辑很简单def parse_yolo(txt_path): rows [] with open(txt_path, r) as f: for line in f: line line.strip() if not line: continue parts line.split() rows.append({ class_id: int(parts[0]), x_center: float(parts[1]), y_center: float(parts[2]), width: float(parts[3]), height: float(parts[4]) }) return rows注意这里width和height是归一化后的框宽、框高不是实际像素。如果jpg是1920x1080txt里写0.5、0.5、0.1、0.1对应实际中心点(960,540)、实际宽192、高108。由于陨石坑形状接近圆形宽高比接近1但有些坑因为拍摄角度和地形起伏会变成椭圆所以不能假设宽高相等。2.4 双格式一致性校验LabelImg的VOC转YOLO脚本经常出现坐标精度丢失比如把浮点转成int时边界溢出或者某个框在xml里有但在txt里丢了。我建议训练前做一次全量对比把xml里的绝对坐标转成归一化坐标后和txt逐字段比对import glob errors [] for xml_path in sorted(glob.glob(annotations/*.xml)): txt_path labels/ xml_path.split(/)[-1].replace(.xml, .txt) width, height, xml_boxes parse_voc(xml_path) txt_boxes parse_yolo(txt_path) if len(xml_boxes) ! len(txt_boxes): errors.append((xml_path, box count mismatch, len(xml_boxes), len(txt_boxes))) continue for box, tbox in zip(xml_boxes, txt_boxes): xmin, ymin, xmax, ymax box[1], box[2], box[3], box[4] x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height w_box (xmax - xmin) / width h_box (ymax - ymin) / height if (abs(x_center - tbox[x_center]) 1e-4 or abs(y_center - tbox[y_center]) 1e-4 or abs(w_box - tbox[width]) 1e-4 or abs(h_box - tbox[height]) 1e-4): errors.append((xml_path, coordinate mismatch)) print(error count:, len(errors))误差阈值设到1e-4是因为常见的转换脚本会把坐标格式化成%.6f。如果你的txt里出现了整型坐标比如0 500 300 200 150说明转换脚本没有归一化后面的训练一定会出现loss不降或预测框偏移的问题。3. 小样本划分与数据增强把132张陨石坑图用到极限只有132张图如果直接随机划分很容易让某一边的陨石坑框数严重失衡。比如验证集分到几张包含40个坑的图指标波动就会极大。我在类似的小样本遥感数据集上通常按框数而不是图片数来做分层采样。3.1 按框数比例划分训练集与验证集先统计每张图的框数然后按框数从多到少排序取前80%的图进入训练集剩余的进验证集import glob from collections import Counter samples [] for xml_path in sorted(glob.glob(annotations/*.xml)): width, height, boxes parse_voc(xml_path) samples.append((xml_path, len(boxes))) samples.sort(keylambda x: x[1], reverseTrue) train_ratio 0.8 train_num int(len(samples) * train_ratio) train_files [x[0] for x in samples[:train_num]] val_files [x[0] for x in samples[train_num:]] print(train images:, len(train_files), total boxes:, sum(x[1] for x in samples[:train_num])) print(val images:, len(val_files), total boxes:, sum(x[1] for x in samples[train_num:]))这样划分后训练集和验证集的框数比例接近80/20比随机切图要稳定得多。固定排序逻辑后不需要随机种子也能复现。这里有一个坑如果数据集的图来自不同探测器序列比如firc_yunshi_这段前缀暗示了来源批次最好按前缀做组划分避免同一个批次的相似图像同时出现在训练和验证中否则测出来是假高分。3.2 数据增强参数从翻转、HSV到马赛克YOLOv8的ultralytics仓库把数据增强封在了训练流程里不需要自己写离线增强代码。但参数怎么调对单类小样本影响很大。我常用的一组参数如下参数含义小样本建议值flipud随机上下翻转概率0.5fliplr随机左右翻转概率0.5hsv_h色调增强幅度0.02hsv_s饱和度增强幅度0.8mosaic马赛克增强概率0.8translate平移增强比例0.2scale缩放增强比例0.6火星月球陨石坑没有固定朝向上下翻转是安全的。hsv_h不要调太大因为遥感影像的光谱特征对模型识别坑壁阴影很重要颜色失真严重会让模型学到错误的纹理。mosaic增强能有效增加单张图的上下文多样性但在框数特别少时马赛克会拼接出大量空白区域。如果训练初期loss降不下去把mosaic降到0.5再观察。3.3 用脚本把VOC统一转成YOLO格式如果未来想加入更多类别或者需要从其他数据源补充图片手动用labelImg逐个导出太低效。我自己习惯写一个通用的VOC转YOLO脚本def voc_to_yolo(xml_path, class2id): width, height, boxes parse_voc(xml_path) lines [] for name, xmin, ymin, xmax, ymax in boxes: if name not in class2id: continue x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height lines.append( f{class2id[name]} {x_center:.6f} f{y_center:.6f} {box_w:.6f} {box_h:.6f} ) return lines class2id {keng: 0} for xml_path in train_files: txt_path labels/train/ xml_path.split(/)[-1].replace(.xml, .txt) lines voc_to_yolo(xml_path, class2id) with open(txt_path, w) as f: f.write(\n.join(lines))注意x_center:.6f这种格式化写法保留六位小数刚好和前面校验脚本的1e-4阈值对应。如果没有做格式化直接写原始浮点数最终写入txt的可能是17位小数读回时有微毫差不影响训练但会干扰严格的一致性校验。4. 用YOLOv8训练keng类别从data.yaml到loss曲线这个数据集只有1个类别用YOLOv8n就足够不需要上YOLOv8x。在普通消费级显卡上132张图100个epoch只需要几分钟非常适合做参数实验。4.1 编写data.yaml先把数据整理成ultralytics要求的目录结构crater_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── crater.yaml然后写crater.yamlpath: /workspace/crater_dataset train: images/train val: images/val nc: 1 names: 0: keng这里path建议写绝对路径否则在IDE或远程服务器上跑容易因为当前工作目录不同而报错找不到图片。train和val相对于path来解析。我实际踩过坑把labels文件夹写成了label结果ultralytics检查不到标注文件训练直接退出。yaml里的键名必须用labels而不是annotations。4.2 训练命令与关键参数在ultralytics的CLI下训练yolo detect train \ modelyolov8n.pt \ datacrater.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.005 \ device0 \ patience20 \ augmentTrue参数说明如下参数作用调参建议modelyolov8n.pt加载COCO预训练权重小样本首选n或sx会过拟合epochs100训练轮数100轮足够看patience触发imgsz640训练输入尺寸小坑多就试1280batch16批量大小显存允许就往上加lr00.005初始学习率少于50张图降到0.003patience20早停耐心值防止后期过拟合augmentTrue开启内置增强关闭可快速调试代码流程加载COCO预训练权重本质上是迁移学习。虽然COCO里没有陨石坑但预训练模型在前几层学到的是边缘、角点、纹理这些底层视觉特征对小样本任务非常关键。如果从随机权重开始训132张图根本学不出稳定的特征表示。4.3 损失函数与训练日志YOLOv8的损失函数由三个分量组成box_loss是CIoU边界框回归损失cls_loss是二分类交叉熵损失dfl_loss是Distribution Focal Loss用来细化框的坐标分布。训练日志长这样Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size 10/100 2.1G 1.104 0.876 1.233 38 640 20/100 2.1G 0.932 0.451 1.021 41 640看loss曲线时要注意如果box_loss持续下降但dfl_loss不降说明中心点定位已经不错但框的宽高还不够精确。陨石坑边界在光照下会产生阴影标注人员对阴影边缘的取舍会导致框线不一致这种噪声会明显体现在dfl_loss上。如果cls_loss降得非常快而box_loss很慢说明模型已经轻松区分前景和背景但框的回归还需要更长训练。训练结束后runs/detect/train/weights/下会生成best.pt和last.pt后面对验证集评估用的就是best.pt。5. mAP评估与过拟合控制在单类别小数据集上的实用调参单类检测任务没有类别间混淆mAP0.5是主要参考指标但也更容易被过拟合欺骗。表现为训练loss持续降低验证集mAP却停在某个值不再上涨甚至开始下降。5.1 用val命令生成指标和混淆矩阵yolo detect val \ modelruns/detect/train/weights/best.pt \ datacrater.yaml \ conf0.25 \ iou0.5conf是置信度阈值低于0.25会被丢掉iou是NMS去重阈值。评估结果里需要重点看两个数字mAP50和mAP50-95。对于小样本数据集mAP50的涨落比mAP50-95更大因为坐标的小偏差对mAP50影响小。如果你发现mAP50高但mAP50-95低说明框的定位精度不够优先检查dfl_loss和相关损失权重。5.2 单类别模型的边界框问题因为只有一个类别混淆矩阵只有前景和背景两类。此时最值得关注的是召回率。很多坑在图像里只占几十像素imgsz640时这些大图被缩放到640小坑特征已经模糊。我一般会用imgsz1280再训练一次对比。如果mAP50-95提升超过3个百分点说明原模型输在输入分辨率上不是模型能力问题。另外遥感影像的坑经常出现在暗色阴影区域置信度阈值设0.25可能漏检可以降到0.1专门看漏检情况。5.3 一个降低过拟合的技巧冻结主干微调132张图不能充分微调整个预训练网络冻结早期层能保留预训练模型学到的通用边缘和纹理特征只更新后面的检测头。用ultralytics的Python API可以这样控制from ultralytics import YOLO model YOLO(yolov8n.pt) model.train( datacrater.yaml, epochs100, freeze10, namefreeze_head )freeze10表示模型前10层参数不参与反向传播只更新后面与检测任务相关的层。冻结层数不是越大越好坑的纹理信息集中在中间层如果冻结到20层可能把陨石坑特有的阴影纹理也冻住了。建议分别试freeze5、freeze10、freeze15在验证集上选mAP最高的那个档位继续调。本文还有配套的精品资源点击获取
返回列表