简介:本资源为面向YOLO系列目标检测算法的深色皮肤病变图像数据集,适用于医学图像识别、皮肤病辅助诊断等方向的算法训练与验证,适合具备一定深度学习基础的目标检测学习者与研究人员使用。压缩包共703个文件,包含234张jpg图像、234个txt标签、234个xml标签及1个data.yaml配置文件,整体约9.16MB,已按训练与测试需求划分完毕,可直接投入模型训练。标签覆盖白糠疹、炎症后色素沉着过度、特发性黑色素沉着症、汇流和网状、白癜风等类别,同时提供YOLO格式与VOC格式两套标注,兼容yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流版本。YOLO格式采用归一化中心点与宽高坐标,便于直接读取;VOC格式则方便与其他框架对接。目前已有60人学习下载,可作为深色皮肤病变检测的基线数据,帮助读者快速搭建训练流程、验证模型效果并开展类别对比实验。
1. 深色皮肤病变检测:234 张图像的数据集到底能不能撑起一个 YOLO 模型
深色皮肤上的色素性病变,长期是皮肤影像 AI 里被忽视的一块。白糠疹、炎症后色素沉着过度、特发性黑色素沉着症、汇流和网状型色素沉着、白癜风,这几类在 Fitzpatrick IV–VI 型皮肤上表现出的颜色对比度低、边界模糊、与正常皮肤色差小,用常规皮肤数据集训出来的 YOLO 模型往往直接翻车。这个标题指向的就是一个专门针对深色皮肤、234 张图像带标签、覆盖上述五类病变的目标检测数据集,配合 YOLO 算法做检测与分类。它解决的核心问题是:在公开皮肤数据集几乎全是浅色皮肤样本的现状下,给深色皮肤病变检测提供一个可用的起点。适合谁?做医学影像目标检测的算法工程师、皮肤科 AI 辅助诊断方向的研究生、以及想用 YOLO 在垂直小数据集上跑通全流程的从业者。234 张不算多,但足够把数据管线、标注格式、训练策略和评估方法完整走一遍。
2. 从压缩包到 YOLO 可训练格式:数据管线的搭建与验证
2.1 先搞清楚压缩包里有什么,再决定怎么拆
拿到一个带标签的图像数据集压缩包,第一件事不是急着解压跑训练,而是把目录结构和标注格式摸清楚。皮肤病变数据集常见的标注格式有三种:PASCAL VOC 的 XML、COCO 的 JSON、以及 YOLO 的 TXT。标题里写的是「带标签」,但没说是哪种,所以需要先做格式探测。
解压后先看目录树:
# 查看压缩包内容,不急着全解 unzip -l skin_lesion_234.zip | head -50 # 解压到工作目录 mkdir -p ~/datasets/skin_lesion unzip skin_lesion_234.zip -d ~/datasets/skin_lesion # 看目录结构 find ~/datasets/skin_lesion -maxdepth 3 -type d如果看到Annotations/和JPEGImages/两个目录,基本可以判定是 VOC 格式;如果看到labels/和images/且 labels 里是.txt,那就是 YOLO 格式;如果只有一个annotations.json,那是 COCO。
# 统计图像数量和标注文件数量 find ~/datasets/skin_lesion -name "*.jpg" -o -name "*.png" | wc -l find ~/datasets/skin_lesion -name "*.xml" -o -name "*.txt" -o -name "*.json" | wc -l # 看一个标注文件长什么样 head -30 ~/datasets/skin_lesion/Annotations/$(ls ~/datasets/skin_lesion/Annotations | head -1)这一步的逻辑说明:图像数量和标注文件数量必须对得上,234 张图像就应该有 234 个标注文件。如果对不上,说明有图像没标或者标注文件命名和图像不对应,这是小数据集最常见的坑。参数上,-maxdepth 3是为了避免目录太深刷屏,head -50是先看个大概。
2.2 VOC 转 YOLO:转换脚本与四个边界坑
如果标注是 VOC XML 格式,需要转成 YOLO 的归一化 TXT。YOLO 格式每行是class_id x_center y_center width height,全部归一化到 0–1。转换脚本如下:
import os import xml.etree.ElementTree as ET from pathlib import Path # 类别映射:按你的数据集实际类别顺序改 CLASS_MAP = { "pityriasis_alba": 0, # 白糠疹 "postinflammatory_hyperpigmentation": 1, # 炎症后色素沉着过度 "idiopathic_guttate_hypomelanosis": 2, # 特发性黑色素沉着症 "confluent_reticulate_papillomatosis": 3, # 汇流和网状 "vitiligo": 4 # 白癜风 } def voc_to_yolo(xml_path, img_w, img_h, out_path): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text.strip() if cls_name not in CLASS_MAP: continue cls_id = CLASS_MAP[cls_name] bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 边界裁剪:防止标注越界导致归一化后为负或大于1 xmin = max(0, min(xmin, img_w)) xmax = max(0, min(xmax, img_w)) ymin = max(0, min(ymin, img_h)) ymax = max(0, min(ymax, img_h)) if xmax <= xmin or ymax <= ymin: continue # 跳过无效框 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines))逻辑说明:这个脚本做了三件关键事。第一,类别映射用字典硬编码,避免顺序错乱;第二,边界裁剪把标注框限制在图像范围内,因为小数据集手工标注经常出现框超出图像边界的情况;第三,跳过无效框,防止宽高为负或零。参数上,img_w和img_h需要从图像实际尺寸读取,不能硬编码,因为皮肤图像可能来自不同设备,分辨率不统一。
四个边界坑:一是标注框坐标是浮点数但 XML 里可能写成整数,float()转换不能省;二是类别名大小写和空格,strip()必须加;三是有些 XML 里object节点可能没有bndbox,需要 try-except 包住;四是归一化后坐标必须保留足够小数位,.6f是经验值,太少会导致小目标框精度丢失。
2.3 划分训练集验证集:234 张怎么分才不浪费
234 张图像,按 8:2 分是 187 训练、47 验证。但皮肤病变类别不平衡,如果随机分,可能出现某个类别在验证集里一张都没有。正确做法是按类别分层抽样:
import random from collections import defaultdict from pathlib import Path def stratified_split(label_dir, img_dir, val_ratio=0.2, seed=42): random.seed(seed) # 按主类别分组 cls_to_files = defaultdict(list) for lbl in Path(label_dir).glob("*.txt"): with open(lbl) as f: lines = f.readlines() if not lines: continue # 取第一个框的类别作为该图主类别 main_cls = lines[0].split()[0] cls_to_files[main_cls].append(lbl.stem) train, val = [], [] for cls, files in cls_to_files.items(): random.shuffle(files) n_val = max(1, int(len(files) * val_ratio)) # 每类至少1张进验证 val.extend(files[:n_val]) train.extend(files[n_val:]) return train, val逻辑说明:按主类别分组后,每个类别至少抽 1 张进验证集,保证验证集覆盖所有类别。seed=42是为了可复现。参数上,val_ratio=0.2是 234 张这种量级的常用值,如果类别特别少可以调到 0.15。
提示:划分完之后一定要打印每个类别的训练/验证数量分布,确认没有类别在验证集里为零。
3. YOLO 训练配置:小数据集上的参数怎么调才不玄学
3.1 选 YOLOv8 还是 YOLOv5:小数据集的选型理由
234 张图像属于典型的小数据集。YOLOv8 和 YOLOv5 都能跑,但选型要看几个实际因素。YOLOv8 的 anchor-free 设计在小数据集上收敛更快,因为不需要聚类 anchor 框;YOLOv5 的 anchor-based 设计在标注框尺寸分布集中时表现更稳。皮肤病变的标注框通常比较集中(病变区域占图像比例中等),两者差距不大。
我一般会选 YOLOv8n 或 YOLOv8s,原因是:第一,n 和 s 的参数量小,234 张图像用大模型必然过拟合;第二,YOLOv8 的 CLI 和 Python API 更统一,调试方便;第三,预训练权重在 COCO 上训过,迁移到皮肤图像虽然域差异大,但底层纹理特征仍有复用价值。
# 安装 ultralytics pip install ultralytics # 验证安装 yolo checks3.2 data.yaml 的写法与三个必调参数
YOLO 训练需要一个data.yaml描述数据集路径和类别:
path: /home/user/datasets/skin_lesion_yolo train: images/train val: images/val nc: 5 names: 0: pityriasis_alba 1: postinflammatory_hyperpigmentation 2: idiopathic_guttate_hypomelanosis 3: confluent_reticulate_papillomatosis 4: vitiligo三个必调参数:
| 参数 | 推荐值 | 理由 |
|---|---|---|
| imgsz | 640 | 皮肤图像分辨率通常够,再大显存吃不消 |
| batch | 8 或 16 | 234 张图,batch 太大一个 epoch 只有十几步 |
| epochs | 100–150 | 小数据集需要多轮但必须配合早停 |
训练命令:
yolo detect train \ data=/home/user/datasets/skin_lesion_yolo/data.yaml \ model=yolov8n.pt \ imgsz=640 \ batch=8 \ epochs=150 \ patience=30 \ lr0=0.001 \ lrf=0.01 \ warmup_epochs=5 \ augment=True \ mosaic=0.5 \ mixup=0.1 \ project=runs/skin_lesion \ name=exp1逻辑说明:patience=30是早停,验证集 loss 30 轮不降就停,防止过拟合。lr0=0.001比默认的 0.01 小,因为小数据集梯度噪声大。mosaic=0.5降低默认的 1.0,因为皮肤病变图像拼接后可能产生不真实的病变组合。mixup=0.1轻微混合,增加泛化。
3.3 数据增强在皮肤图像上的边界:哪些能用哪些会翻车
皮肤病变检测的数据增强有特殊性。颜色抖动(hsv_h、hsv_s、hsv_v)要慎用,因为深色皮肤和浅色皮肤的色差本身就是分类依据,过度抖动会让模型学到错误的颜色关联。翻转(flipud、fliplr)可以用,皮肤病变没有方向性。旋转(degrees)可以用小角度,大角度旋转会产生不真实的病变形态。
# 保守增强配置 hsv_h=0.01 hsv_s=0.3 hsv_v=0.3 \ degrees=10.0 translate=0.1 scale=0.3 \ shear=2.0 perspective=0.0 \ flipud=0.5 fliplr=0.5逻辑说明:hsv_h=0.01几乎不动色调,因为色调变化会改变病变颜色。hsv_s=0.3和hsv_v=0.3适度调整饱和度和亮度,模拟不同光照。degrees=10.0小角度旋转。perspective=0.0关闭透视变换,因为皮肤是平面拍摄,透视变换不真实。
注意:如果验证集 mAP 在训练前期就很高但后期下降,大概率是增强过强导致模型学到了增强后的伪特征,先把 mosaic 和 mixup 关掉再试。
4. 训练过程排查:loss 不降、mAP 不动、过拟合怎么定位
4.1 loss 曲线读法:box_loss、cls_loss、dfl_loss 分别说明什么
YOLOv8 训练输出三个 loss:box_loss(边界框回归)、cls_loss(分类)、dfl_loss(分布焦点损失)。正常情况三者都下降。如果 box_loss 降但 cls_loss 不降,说明模型能定位但分不清类别,通常是类别不平衡或标注类别错误。如果 cls_loss 降但 box_loss 不降,说明分类对了但框不准,通常是标注框质量差。
# 训练完看结果 cat runs/skin_lesion/exp1/results.csv | head -5 # 用 tensorboard 看曲线 tensorboard --logdir runs/skin_lesion4.2 验证集 mAP 卡在 0.3 上不去:五个排查方向
234 张图像训 YOLO,mAP@0.5 能到 0.5–0.7 就算不错。如果卡在 0.3 以下,按顺序排查:
第一,标注质量。打开几张验证集图像,把预测框和标注框叠在一起看,如果标注框本身就不准,模型学不到正确边界。第二,类别定义。白糠疹和白癜风在深色皮肤上视觉相似,如果标注时混淆,模型必然分不清。第三,图像分辨率。如果原图是 2000×3000 但训练时缩到 640,小病变可能只剩几十个像素。第四,预训练权重。试试从 COCO 预训练换成在更大皮肤数据集上预训练的权重。第五,学习率。lr0 太大导致震荡,太小导致收敛慢,用 lr finder 找一下。
# 用 YOLO 自带的 lr finder yolo detect train data=data.yaml model=yolov8n.pt epochs=10 lr0=0.01 lrf=0.01 warmup_epochs=0 # 看 runs/.../lr_finder.png4.3 过拟合的早期信号与三种正则化手段
234 张图像训 150 轮,过拟合几乎必然。早期信号:训练 loss 持续降但验证 loss 开始升,或者验证 mAP 在某个 epoch 后不再涨。三种手段:第一,早停 patience 设小一点,20–30 轮;第二,weight_decay 从默认 0.0005 加到 0.001;第三,dropout 在 YOLOv8 里通过dropout参数控制,但 YOLOv8n 本身没有 dropout 层,需要用 YOLOv8s 并手动改模型配置。
# 加 weight_decay 和 label_smoothing yolo detect train ... weight_decay=0.001 label_smoothing=0.1逻辑说明:label_smoothing=0.1让分类标签不再是硬 0/1,而是 0.1/0.9,防止模型对训练样本过度自信。weight_decay=0.001增大 L2 正则化强度。
5. 避坑与常见问题:小数据集训 YOLO 的血泪经验
5.1 现象:训练时 mAP 很高,推理时框全错
原因:验证集和训练集划分时没有按类别分层,验证集里某个类别只有一两张,模型在这两张上过拟合,mAP 虚高。解决:用 2.3 节的分层抽样重新划分,确保每个类别在验证集里至少有 3–5 张。
5.2 现象:模型把白糠疹和白癜风混为一谈
原因:这两类在深色皮肤上都是色素减退,视觉特征高度重叠,234 张图像里如果两类样本数量差异大,模型会偏向多数类。解决:检查两类样本数量,如果差异超过 3:1,对少数类做过采样或加 class_weight。YOLOv8 不直接支持 class_weight,但可以通过复制少数类图像到训练集实现。
5.3 现象:训练到 50 轮后验证 loss 突然飙升
原因:学习率在训练后期没有正确衰减,或者 mosaic 增强在后期产生了不真实的图像组合。解决:确认lrf=0.01让学习率从 lr0 线性降到 lr0×0.01,同时在最后 20 轮关闭 mosaic:close_mosaic=20。
5.4 现象:推理时同一张图每次框的位置都不一样
原因:YOLO 推理时有 NMS(非极大值抑制),如果conf阈值设得太低,大量低置信度框参与 NMS,结果不稳定。解决:推理时conf=0.25起步,根据验证集 PR 曲线调整。另外确认augment=False,推理时不要开 TTA。
yolo detect predict model=runs/skin_lesion/exp1/weights/best.pt \ source=test_images/ conf=0.25 iou=0.45 augment=False5.5 现象:换一台机器训练,结果完全不一样
原因:随机种子没固定,或者 CUDA 版本和 cuDNN 版本不同导致浮点运算差异。解决:训练时加seed=42 deterministic=True,但注意 deterministic 会降低训练速度。跨机器复现时,记录 CUDA、cuDNN、PyTorch 版本,尽量保持一致。
6. 小数据集的进阶技巧:用 234 张图像榨出可用模型
234 张图像训 YOLO,如果只跑一遍就结束,大概率得到一个过拟合的模型。进阶做法是:第一,用交叉验证代替单次划分,5 折交叉验证能更可靠地评估模型真实性能;第二,用伪标签(pseudo-labeling),先用训练好的模型在未标注皮肤图像上推理,把高置信度预测作为伪标签加入训练集;第三,用 CutMix 和 CopyPaste 增强,把病变区域复制粘贴到正常皮肤区域,生成更多训练样本。
# 5 折交叉验证的划分逻辑 from sklearn.model_selection import KFold import numpy as np all_files = list(range(234)) kf = KFold(n_splits=5, shuffle=True, random_state=42) for fold, (train_idx, val_idx) in enumerate(kf.split(all_files)): print(f"Fold {fold}: train={len(train_idx)}, val={len(val_idx)}") # 按索引生成对应的 data.yaml 并训练逻辑说明:5 折交叉验证把 234 张分成 5 份,每次用 4 份训练 1 份验证,最终报告 5 次验证 mAP 的均值和标准差。标准差大说明模型对数据划分敏感,需要更多数据或更强正则化。
验证方法上,除了 mAP,还要看每个类别的 AP。如果某个类别 AP 特别低,单独把该类别的验证图像拿出来看预测结果,判断是标注问题还是模型问题。我一般会做一个混淆矩阵,看类别之间的误判方向。
# YOLOv8 自带混淆矩阵输出 yolo detect val model=best.pt data=data.yaml plots=True # 结果在 runs/.../confusion_matrix.png一个具体技巧:如果白癜风和白糠疹混淆严重,可以在推理后加一个后处理规则,根据病变区域的色素脱失程度(用图像灰度直方图判断)做二次分类。这个规则不需要训练,纯图像处理就能实现,能显著降低这两类的误判率。
我自己做小数据集检测的习惯是:先跑一个 baseline,不管效果多差,把完整管线跑通;然后花 70% 的时间在数据清洗和标注校验上,而不是调参;最后用交叉验证确认模型不是靠运气。234 张图像不多,但足够验证一个方向值不值得投入更多数据。希望帮到你。
本文还有配套的精品资源,点击获取