ARTICLE DETAIL

资讯详情

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

小样本训练YOLO皮肤病灶检测:数据组织、损失曲线与标签校验

小样本训练YOLO皮肤病灶检测:数据组织、损失曲线与标签校验 简介一份用于yolo系列算法训练的皮肤疾病目标检测数据集涵盖白癜风、皮炎、黄褐斑三类病变样本已完成训练/验证划分适合目标检测入门及皮肤影像AI实验。压缩包共334个文件包含111张jpg原图、111个txt标签、111个xml标签及1个data.yaml配置文件txt采用yolo格式class、x_center、y_center、width、heightxml为VOC格式两类标签分别存放可直接适配yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等常见框架。资源包仅4MB轻量易下载配置文件中已写好类别与路径省去手动整理标签的步骤。目前已有158人学习下载。对于需要专用皮肤疾病数据集做模型对比或快速验证的开发者这份数据能帮助节省标注时间集中精力调整模型结构与训练参数。1. 111张皮肤病灶图凭什么敢训YOLO检测模型皮肤疾病检测里最不缺的是“类间相似”白癜风与浅色皮炎的边界在RGB空间里几乎粘连黄褐斑又经常和晒斑重叠。用目标检测去框三类病灶能拿到的高质量标注图往往只有百张级别这个zip就是典型111张图像、三类标签、同时给出YOLO和VOC双格式并且已经划分好训练与验证集。小样本直接训YOLO通常几十个epoch就出现过拟合但配合足够细的迁移学习、数据增强和损失函数调参这种规模的数据完全可以产出可用于课题对比和算法预研的检测权重。对需要用YOLO系列算法快速验证皮肤影像思路的人来说这份数据集的价值不在数量而在它已经把标签格式、目录划分和训练入口都备好了。接下来的篇幅我会从数据组织讲到训练曲线再落到一个能帮你少跑冤枉路的标签体检脚本。2. 先拆zip再看标签txt、xml与data.yaml的数据组织2.1 目录结构与data.yaml是配置中枢拿到zip之后不要直接丢给训练命令。解压后第一件事是看根目录结构这类资源常见做法是images目录放训练/验证/测试图像labels目录放对应的yolo格式txt文件另一个目录放voc格式xml根目录再放data.yaml。以这份数据集为例data.yaml承担了YOLO系列算法所有训练入口的配置yolov5、yolov7、yolov8、yolov9、yolov10、yolo11都能通过它指定路径、类别数和类别名。# data.yaml 示例具体路径以解压目录为准 path: ./skin-disease train: images/train val: images/val test: images/test nc: 3 names: 0: vitiligo 1: dermatitis 2: melasma这段yaml里有几个地方需要重点核对path可以直接写相对路径也可以写绝对路径训练时如果提示找不到图像80%的问题出在这里names的顺序决定了txt标签第一列的类别索引不能与后续分类层里的类别ID错位。如果只用yolov5可以改成train: ../skin-disease/images/train这种写法但yolov8及之后的版本更建议直接使用这份文件不需要另写解析代码。2.2 YOLO txt与VOC XML的双格式对应关系同一个标注信息用两种格式表达是为了兼容不同训练管线。YOLO txt每一行是五列类别索引、中心点x、中心点y、宽度、高度其中坐标都是相对于图像宽高的比例值范围在0到1之间。VOC XML则用绝对像素坐标保存物体框锚点在左上角。要在同一条YOLO目标检测流程里跑通必须明确这两种格式的换算关系。标签属性YOLO txt字段VOC XML字段是否需要归一化类别第一列int从0开始object/namestring否框中心x_center, y_center(xminxmax)/2, (yminymax)/2YOLO归一化VOC为像素框宽高width, heightxmax-xmin, ymax-yminYOLO归一化VOC为像素如果资源包里只有其中一种格式可以用下面这个函数把VOC转成YOLOimport xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() classes [vitiligo, dermatitis, melasma] # 顺序必须与data.yaml一致 lines [] for obj in root.findall(object): name obj.findtext(name) if name not in classes: print(f[SKIP] {xml_path}: 未识别的类别 {name}) continue bbox obj.find(bndbox) xmin float(bbox.findtext(xmin)) ymin float(bbox.findtext(ymin)) xmax float(bbox.findtext(xmax)) ymax float(bbox.findtext(ymax)) xc (xmin xmax) / 2 / img_w yc (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{classes.index(name)} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) return lines这个函数做的事情是解析XML里每个object把bndbox的左上右下像素坐标换算成归一化中心点和宽高再拼接成YOLO格式的一行。注意classes列表的顺序必须和data.yaml里的names一致否则标签会“串类”一旦开训后才发现mAP再好看也是无效指标。实际资源包已经提供了现成的txt一般不需要再转换但理解这段逻辑对接下来调试数据增强和损失函数很有用。2.3 先统计类别样本量再从XML校验皮肤病数据集最常见的坑是类别极度不平衡某些类别只有十几张图另一类占一半。因为VOC和YOLO格式的坐标含义不同训练前最好分别看一眼各类框的数量。cut -d -f1 labels/train/*.txt | sort | uniq -c这行命令的作用是读取训练集所有txt的首列类别索引统计每个类别出现的框数量如果发现某个类别框数远小于其他类别就需要在下文的数据增强里有针对性地补样本。另一种做法是用XML标注统计因为XML里保存的是类名而不是数字更容易对照data.yaml中的namesimport xml.etree.ElementTree as ET from pathlib import Path xml_dir Path(annotations) class_stat {} for xml_file in xml_dir.rglob(*.xml): root ET.parse(xml_file).getroot() for obj in root.findall(object): name obj.findtext(name) class_stat[name] class_stat.get(name, 0) 1 print(class_stat)如果输出结果里某个类别的框数是0不一定是图像里没有病灶也可能是标注文件的class文本和data.yaml不一致需要先修数据而不是改names。这种校验动作放在训练之前成本只有几分钟却能把后续一系列诡异的损失曲线问题挡在前面。3. 用YOLOv8训练自己的皮肤疾病数据集命令、参数与增强3.1 为什么从YOLOv8n起步YOLO系列算法发展到yolov8、yolov9、yolo11骨干网络和损失函数各有侧重但在小数据集上模型容量永远是第一约束。111张图像、三类病灶直接上yolov8x或者yolov9c大概率会在训练集上拟合得过分漂亮验证集mAP却掉下去。常见做法是从yolov8n这类轻量权重起步用COCO预训练参数做迁移学习把网络对通用物体边缘、纹理的把控迁移到皮肤病灶上。yolov8n参数量约3.2M对显存要求也低批量大小可以给到16甚至32反向传播的梯度更稳定。至于yolov7、yolov10和yolo11训练命令和data.yaml入口几乎一致只是权重文件名和部分超参略有差异。3.2 训练命令与参数含义确认data.yaml无误后在项目根目录执行下面的命令即可开始训练。这个命令适配ultralytics框架的yolov8yolov5需要把yolo detect train换成train.py的方式yolov9/yolo11则保持detect train的语法。yolo detect train \ data./skin-disease/data.yaml \ modelyolov8n.pt \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectruns \ nameskin_disease这里的每个参数都对着小样本场景做了取舍epochs设100而不是300是让早期验证集loss变化趋势更快暴露patience20表示20个epoch内验证指标不再提升就提前停止避免后50轮纯过拟合imgsz640维持YOLOv8默认输入尺寸不降分辨率因为皮肤病灶边界本来就模糊再缩小会丢失纹理细节batch16在单张消费级显卡上比较从容显存不够可以先降到8但要注意batch过小会让BN统计量抖动必要时把mosaic关闭。modelyolov8n.pt表示加载COCO预训练权重如果不加这个文件网络从随机初始化开始111张图训练几百轮也难以收敛。参数示例值作用data./skin-disease/data.yaml训练入口负责加载路径和类别名modelyolov8n.pt预训练权重迁移学习的起点epochs100总训练轮数小数据偏保守batch16批大小显存不足降到8imgsz640输入边长512可作为正则尝试patience20验证指标连续20轮不提升则早停projectruns输出根目录nameskin_disease本次实验的命名3.3 针对111张图的数据增强配置训练命令只决定流程真正决定小样本上限的是数据增强。YOLOv8默认开启的马赛克、HSV扰动等策略在显著病灶上通常有效但皮肤病图像的颜色是重要诊断线索不能把色调破坏得太厉害。我一般会调整下面几个增强项# 对应ultralytics默认配置里的增强段 hsv_h: 0.01 # 色调扰动收窄避免黄褐斑颜色失真 hsv_s: 0.5 # 饱和度扰动保留一定幅度 hsv_v: 0.3 # 明度扰动适中 degrees: 5.0 # 旋转角度收窄皮肤病灶没有强方向性 scale: 0.5 # 缩放保持默认 fliplr: 0.5 # 水平翻转 mosaic: 0.6 # 马赛克概率降低到0.6避免过多遮挡 copy_paste: 0.2 # 复制粘贴增强缓解少样本类别这段配置对应ultralytics默认训练配置里的增强段。如果不想单独维护yaml直接在训练命令末尾追加同名参数也可以覆盖默认值效果一致。hsv_h调小到0.01是防止白癜风区域的边界颜色被随机扰动推入皮炎区间mosaic降到0.6是为了不让大量拼接框在batch里占据主导copy_paste在YOLOv8里可以把某个病灶实例粘到另一张图的背景区域尤其适合黄褐斑这类目标框占比小的类别。需要说明的是copy_paste的增强强度对检测稳定性影响很大如果训练中看到box_loss持续震荡应当把它调回0.1或直接关闭并观察验证集mAP的变化。也见过有人把fliplr改到0.8但皮肤病灶普遍存在左右对称性实际用0.5和0.8差别不大反而可能让中心坐标的拟合负担变大。4. 小样本训练的验收损失函数分解、过拟合判断与验证流程4.1 从results.csv分解YOLO损失函数训练结束后runs/skin_disease目录下会留下weights、events文件和results.csv。很多人在等训练结束时才看终端输出忽略了这个csv它把每个epoch的损失函数分量都记录下来了。YOLOv8的损失由三部分组成box_loss边界框回归误差、cls_loss类别分类误差、dfl_loss分布焦点损失用于框的精细回归。对皮肤疾病检测而言三者权重不同小样本下它们的走势也不相同。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/skin_disease/results.csv, skipinitialspaceTrue) cols [train/box_loss, val/box_loss, train/cls_loss, val/cls_loss, train/dfl_loss, val/dfl_loss] df[cols].iloc[5:].plot(figsize(10, 6)) plt.tight_layout() plt.savefig(loss_component.png, dpi150)这里用skipinitialspaceTrue是因为ultralytics导出的csv列名头部有空格跳过前5行是为了避开初始震荡区间让训练和验证曲线的对比更清楚。如果val/box_loss持续下降而val/cls_loss在第40轮后明显回升说明分类层在过拟合类别频率高的那一类如果dfl_loss震荡大则需要回去调整数据增强中的copy_paste强度。4.2 用验证集曲线拦下过拟合小样本数据集评价不能只看mAP。真正有效的时间点是验证损失开始上升的epoch而不是测试集精度最高的epoch。一个可用的判断标准是当val/box_loss连续5个epoch高于历史最低值同时train/box_loss仍在下降说明模型开始记忆训练集中特定病灶的纹理背景这时候即使mAP50还在微涨权重也不是可靠的。典型现象是黄褐斑类别因为样本少被模型当成皮炎背景的一部分precision和recall中某一项会突然掉点。如果出现这类过拟合最直接的做法是回到第3章的命令里把epochs从100降到60并把patience从20改为10。另一个更有效的方向是缩小输入尺寸把imgsz从640降到512这相当于给网络加了空间上的正则虽然会略微损失小目标的定位精度但对百张级别的数据集通常利大于弊。注意不要更换成yolov8x这类大模型模型变深后过拟合会更早。4.3 验证命令与结果指标训练结束后的验证流程应当独立于训练命令同一份权重需要在相同的data.yaml上重新跑一遍避免框架训练过程中夹带的缓存影响指标。yolo detect val data./skin-disease/data.yaml \ modelruns/skin_disease/weights/best.pt \ splitval \ batch16 \ imgsz640splitval用于显式指定验证集防止框架默认把训练集或测试集混进来。输出会包含precision、recall、mAP50和mAP50-95其中mAP50-95对框的定位质量更敏感在皮肤疾病这类目标较小、边界模糊的任务里mAP50如果明显高于mAP50-95说明模型框出了目标但边界不紧后续可以在推理阶段降低conf阈值、调整NMS的iou阈值看能不能让mAP50-95涨回来。指标含义在小样本皮肤数据集上的关注点precision预测框中真正的病灶占比黄褐斑误检率是否过高recall真实病灶被召回比例皮炎小病灶是否被漏检mAP50IoU阈值0.5时的平均精度模型是否大致框对位置mAP50-95平均IoU阈值0.5:0.95边界框贴合程度5. 训练前的一层保险用校验脚本保证标签不拖后腿5.1 一个覆盖四类异常的YOLO标签检查脚本百张图手工标注最容易出问题的不是模型参数而是标签文件里的归一化坐标。YOLO格式的框坐标是比例值标注工具在图像边缘处往往会把x_center或y_center算得超出[0,1]还有可能某张图对应的txt文件为空训练时网络会收到负样本信息把背景学成病灶。这里给一个可以直接跑的检查脚本配合前文的目录结构使用。from pathlib import Path import sys label_dir Path(sys.argv[1] if len(sys.argv) 1 else labels/train) num_classes 3 for txt in sorted(label_dir.glob(*.txt)): lines txt.read_text().strip().splitlines() if not lines: print(f[EMPTY] {txt.name}: 没有任何标签) continue for idx, line in enumerate(lines, 1): parts line.split() if len(parts) ! 5: print(f[FORMAT] {txt.name}: 第{idx}行列数为{len(parts)}) continue cls, cx, cy, w, h parts cls int(cls); cx float(cx); cy float(cy) w float(w); h float(h) if cls num_classes: print(f[CLASS] {txt.name}: 类别索引{cls}越界) if not (0 cx 1 and 0 cy 1): print(f[CENTER] {txt.name}: 中心点越界 ({cx}, {cy})) if w 0 or h 0 or cx w / 2 1 or cy h / 2 1: print(f[BOUND] {txt.name}: 框超出图像范围 {parts[1:]})这个脚本按行读取YOLO标签依次检查四类异常空文件、列数不等于5、类别索引超界、归一化坐标或宽高不合理。运行方式是在终端执行python check_labels.py labels/train如果某个txt报[BOUND]说明标注工具生成框时把边缘点算溢出了可以先看看对应XML里的xmax是否超过图像宽度再做修正。特别说明一点很多训练框架会对越界框自动裁剪到图像边界因此小范围越界不会导致报错但会让框中心偏移体现在mAP50-95的低分上这也是为什么需要在训练前就把它挑出来。对于[EMPTY]文件通常是把没有病灶的图像也放进来了可以直接移出训练集不用删除图像文件。本文还有配套的精品资源点击获取
返回列表