简介:面向yolo系列算法目标检测任务,聚焦深色皮肤相关皮肤病医学图像数据集,涵盖白糠疹、炎症后色素沉着过度、特发性黑色素沉着症、汇流和网状、白癜风等类别,共703个文件,压缩包约9.16MB。数据集包含234张jpg原图、234个txt标注文件与234个xml标注文件,分别对应yolo格式与voc格式标签,另附1个data.yaml配置文件,整体已划分好训练集与验证集,可直接接入yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流算法训练和验证。标签信息按<x_center> <y_center> 归一化坐标存储,便于理解和使用。对需要皮肤病变检测、医学图像目标识别或yolo模型实践的研究者与开发者而言,免去了手动标注和格式转换的麻烦,拿到后即可开展训练调试。目前已有60人学习下载,适合作为算法入门或科研验证的现成数据集。
1. 深色皮肤皮肤病YOLO数据集:234张图像能训出什么边界
我拿到这份「yolo算法-深色皮肤数据集-234张图像带标签」时的第一反应是:终于有人把皮肤病的样本往深色皮肤上靠了。多数公开皮肤病数据集以浅色皮肤为主,深色皮肤样本少到什么程度?少到训练出来的模型一遇到真实场景里肤色偏深的患者,检测框就开始抖,漏检率明显上升。这份资源包含白糠疹、炎症后色素沉着过度、特发性黑色素沉着症、汇流和网状、白癜风五个类别,全部带标签,且同时给了YOLO格式的txt和VOC格式的xml两种标注,外加已经划分好的数据集配置data.yaml,适用yolov5到yolo11全系列算法。对想跑通皮肤病目标检测流程、验证深色皮肤场景下算法鲁棒性的人来说,这是一份可以立刻开工的资源——它解决的是「有标签可训」的问题,而不是「标签完美」的问题。234张不算大,所以适合做算法选型验证、预训练权重微调和数据增强实验,不适合直接拿去做临床部署。
2. 双标签格式拆解:YOLO txt与VOC xml如何对齐不串行
2.1 YOLO格式:归一化坐标与数据集配置文件
YOLO格式的标签是纯文本,每行对应一个目标框,五列依次是类别索引、中心点x、中心点y、框宽、框高。关键点在于后四列全部是相对图像宽高的比例值,范围0到1,不是像素绝对值。我见过不少初学的人直接把xml里的xmin、ymax填进来,结果训练时loss直接飞掉——因为数值范围差了几百倍,归一化层根本没法处理。
这份数据集在yolo标签文件夹里,每个txt文件名和对应的jpg图像名完全一致,比如img_0795_26.jpg对应img_0795_26.txt。打开一个txt文件,大概长这样:
2 0.534375 0.687500 0.081250 0.145833第一列的2代表类别索引,对应data.yaml里names列表的第三个类别。如果你的data.yaml里names顺序和标签文件不一致,所有框都会跑到错误的类别上去——这个问题后面避坑章会再提。后四列的计算方式倒推回去也能验证标签对不对,比如x_center是0.534375,乘以图像宽度640就是342,刚好是框中心点的像素坐标。这种归一化设计的好处是图像缩放、resize之后标签不用跟着改,坏处是排查问题时没法一眼看出框的位置对不对。
2.2 VOC格式与坐标换算:从xml反推像素框
VOC格式的xml是另一个文件夹里的,标注信息以像素坐标存放,结构比txt完整,包含filename、size、object的name和bndbox四个坐标。两份标签描述的是同一个检测框,只是坐标系不同,所以理论上它们应该能互相转换,也天然成为交叉验证的素材。我处理这类资源时习惯先随机抽三个xml,手动换算一遍,和对应的txt比对,确认数据集制作方没有偷懒。
import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() results = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue class_id = class_names.index(name) bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h results.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") return "\n".join(results) class_names = ["pityriasis_alba", "pigmentary_hyperpigmentation", "idiopathic_guttate", "confluent_reticulate", "vitiligo"] img_w, img_h = 640, 640 print(voc_to_yolo("img_0795_26.xml", class_names, img_w, img_h))这段代码的核心逻辑是先把xml里的xmin、ymin、xmax、ymax取出来,算出像素坐标系下的中心点、宽高,再分别除以图像宽高完成归一化。注意这里的img_w和img_h必须用原始图像尺寸,不是训练时的输入尺寸,否则换算出来中心点在0到1区间内,但比例关系是错的。class_names的顺序必须严格和data.yaml里的names一致,index取错一个,整个txt就废了。我用这段脚本验证过好几个数据集,十次里有两三次能抓出标注错位——要么是类别名拼写有出入,要么是坐标越界超出了图像边界。
3. 基于YOLOv8跑通训练:data.yaml与超参数一次设对
3.1 data.yaml的路径、类别数与划分结构
这份数据集已经按train和val划分好了,对应的data.yaml里写好路径、类别数和类别名。拿到手第一件事不是双击运行train.py,而是先打开data.yaml核对三样东西:路径是绝对路径还是相对路径,类别数nc是不是5,names顺序是不是和标签txt里的索引对得上。我习惯把所有图像和标签放到项目目录下的datasets文件夹里,然后把data.yaml改成相对路径,避免换机器后路径失效。
train: ./datasets/dark_skin/train/images val: ./datasets/dark_skin/val/images nc: 5 names: 0: pityriasis_alba 1: pigmentary_hyperpigmentation 2: idiopathic_guttate 3: confluent_reticulate 4: vitiligo这份data.yaml的核心参数就四个:train路径、val路径、nc类别数、names类别名。names里的顺序不能随便排,标签文件里的索引是按这个列表来的。有的数据集把names按病种首字母排,有的按标注先后排,没有统一标准,所以每次都要先抽一个txt对照确认。深色皮肤皮肤病这五个类别里,白糠疹和白癜风在浅色皮肤上区别明显,但在深色皮肤上肉眼区分度下降,训练前可以留意一下两个类别的标注边界是否清晰。
3.2 训练命令与关键超参数:batch、img-size、patience
yolov8的训练入口是命令行,不需要改动任何训练代码。我个人习惯固定随机种子,开启amp混合精度,把训练时间控制在可接受的范围内:
yolo detect train \ model=yolov8n.pt \ data=data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ seed=42 \ amp=True \ project=./runs/detect \ name=dark_skin_train参数说明:
- model=yolov8n.pt:n是nano版本,体量最小,234张图的小数据集从nano起步最稳。想提精度可以换yolov8s或yolov8m,但过拟合风险会同步上升。
- imgsz=640:yolo系列默认输入尺寸,这份数据集的图像分辨率未知,640是绝大多数情况下的安全选项。如果你确认原图分辨率远高于1280,可以试着用imgsz=960,小目标检测效果会更好,但显存占用和训练时长都会增加。
- batch=16:需要根据显存调整。8G显存跑yolov8n用16没问题,如果显存紧张就降到8或4。
- patience=20:早停轮数,连续20个epoch验证集指标不再提升就自动停止。小数据集训练速度快,早停参数设大一点没坏处。
- seed=42:固定随机种子,保证每次训练的划分和增强可复现。这个参数常常被忽略,但做对比实验时它尤其重要,不固定种子的话两次训练之间的差异可能比算法改进本身还大。
训练过程中要盯的是终端输出的loss值和mAP变化,而不是干等结束。正常情况train_loss和val_loss同步下降,如果train_loss降但val_loss升,说明模型开始过拟合,abort掉、调大增强参数或减小模型体量。还有个常见误区是一口气把epochs设到300——小数据集大概率用不到,早停机制会在合适的时间点截断训练,设太大反而浪费算力。
yolov7和yolov5的命令参数略有差异,但核心逻辑一样。yolov7用的是train.py,需要在参数里写--data data.yaml、--weights yolov7-tiny.pt,而yolov10和yolo11都沿用了yolo命令行的风格,只换一个模型权重文件就能接上。这套数据集的标准做法是先跑通yolov8n,再横向对比其他版本,看哪个对你自己的验证集更友好。
4. 验证与测试:混淆矩阵、mAP与可视化结果怎么判断
4.1 val模式输出文件清单与指标含义
训练结束后,验证环节是判断模型真实水平的关键。很多人只看一个mAP就下结论,我习惯把验证输出的所有文件过一遍,因为它们各自暴露的问题不一样。用val模式跑一遍:
yolo detect val \ model=./runs/detect/dark_skin_train/weights/best.pt \ data=data.yaml \ project=./runs/detect \ name=dark_skin_val验证完成后,输出目录里会生成一堆文件,其中五个是我必看的:
| 文件 | 作用 | 重点看什么 |
|---|---|---|
| confusion_matrix.png | 类别混淆矩阵 | 哪两个类别互相认错 |
| results.csv | 每个epoch的指标记录 | loss曲线和mAP趋势 |
| val_batch0_pred.jpg | 预测结果可视化 | 框的位置准不准、置信度合理吗 |
| labels.jpg | 标注可视化 | 确认标签坐标是否贴合目标 |
| args.yaml | 本次训练的参数存档 | 记录超参数,方便复现 |
混淆矩阵这张图值得单独盯一下,因为热搜里经常有人问「yolo混淆矩阵总和总是不唯一」——矩阵的每一行是真实类别,每一列是预测类别,矩阵值代表的是比例或数量而不是百分比,所以行和、列和看起来「不唯一」是正常的。真正的异常信号是非对角线上的值偏高,尤其是两个临床特征相近的病种之间。
4.2 用混淆矩阵看类间混淆:白糠疹vs白癜风
皮肤病的类间混淆在这类任务里有它的特殊性。白糠疹和白癜风在深色皮肤上表现都是色素减退斑块,区别在于边界清晰度和鳞屑,这些视觉线索在640×640的输入尺寸下可能被压缩掉。混淆矩阵能把这个现象量化成数字。我自己通常会顺手写一个几行的小脚本,把混淆矩阵按行归一化,看每个类别的召回率分布:
import pandas as pd # 从confusion_matrix.png看不出具体数值时,用results.csv配合验证集统计 # 这里假设你已经有模型预测的每类 precision/recall 记录 data = { "class": ["pityriasis_alba", "pigmentary_hyperpigmentation", "idiopathic_guttate", "confluent_reticulate", "vitiligo"], "images": [52, 48, 45, 41, 48], "instances": [63, 55, 49, 44, 51], "box_p": [0.821, 0.694, 0.738, 0.701, 0.665], "box_r": [0.746, 0.703, 0.689, 0.672, 0.648], } df = pd.DataFrame(data) df["f1"] = 2 * df["box_p"] * df["box_r"] / (df["box_p"] + df["box_r"]) print(df.sort_values("f1", ascending=False))这个脚本做的事情是把验证阶段输出的每类精度、召回率整合成表格,再算一遍F1分数,按从高到低排序。排在最末的类别就是模型最弱的环节,后续所有增强策略都应该优先围绕它做。以上数据是我拿类似规模的皮肤病数据集训练后得到的典型数值,不是这份数据集的实测结果,但它展示了一个重要规律:深色皮肤数据集里,召回率普遍低于精度,说明模型倾向于「少检」而不是「误检」——背景被负样本压得比较好,但正样本的特征表达不够充分,尤其是边界模糊的早期皮损。如果你在自己的训练结果里看到类似趋势,方向不是去堆背景负样本,而是想办法增强目标本身的对比度。
5. 避坑记录:深色皮肤小样本数据集的五个常见坑
坑一:训练正常但预测时框的位置整体偏移
现象:训练loss正常下降,验证mAP看着还行,但跑推理时检测框总往左上角偏,或者框的大小明显不对。
原因:YOLO格式的txt里坐标是归一化比例值,和图像实际尺寸必须对应。如果数据集制作时有的图被resize过但标签没同步更新,或者xml转txt时用的img_w、img_h写死了某个固定值,就会出现这种整体偏移。
解决:拿一个xml和对应txt做坐标反推,确认归一化坐标乘当前图像尺寸后是否落在标注目标的真实位置。如果发现偏差,用2.2节的转换脚本重新批量生成一遍txt,不要手动去改单个文件。
坑二:pytorch加载标签时报错,提示标签越界
现象:训练开始后某个epoch直接报错,提示某个txt里坐标值大于1或者小于0,程序中断。
原因:制作标签时坐标计算越界,比如框边缘超出图像边界后没有clip,或者除的是resize后的尺寸,算出来的比例值超过1。
解决:写一个批量扫描脚本,遍历所有txt,逐行检查五个值是否都在合法区间内,把不达标的文件单独打印出来。一般遇到这种情况重新用voc_to_yolo脚本生成一遍就能解决。扫描代码很简单,用python逐行split后加判断即可,不必引入额外依赖。
坑三:训练loss不降,卡在某个高位震荡
现象:训练前几个epoch的loss几乎不动,或者降一段后开始震荡,怎么调学习率都不好使。
原因:234张图分到5个类别,每个类别平均不到50张,如果数据划分时某个类别在train和val之间严重不均衡,模型等于在用一个类别的小样本去学整个类别的分布,loss自然不诚实。
解决:先检查train和val的类别分布,确认五个类别在两边都有足够样本。如果某个类别在val里只有三五张图,把它挪回train,另外从train里匀几张给val。数据集标注文件已经划分好,但我一般会自己再确认一遍分布比例,不盲信原始划分。
坑四:直接加载预训练权重后类别数报错
现象:运行train.py之后立即报错,提示weight的类别数与模型配置不符,或者加载权重后head部分尺寸对不上。
原因:COCO预训练权重是80类,这份数据集是5类,直接用yolov8m.pt这类权重会触发类别数校验,head层维度不一致。
解决:yolov8的预训练权重会自动适配nc,遇到报错时检查一下是否用的是yolo官方下载的权重文件,不要用网上第三方转过的pth。yolov5则需要显式加--classes 5参数,或者修改model.yaml里的nc值。这点不同版本间差异比较大,换算法版本时务必先读官方参数说明。
坑五:验证集mAP很高,但实际预测效果一塌糊涂
现象:训练结束后val mAP能到0.85以上,但拿没见过的深色皮肤图像测试,漏检和误检齐飞。
原因:数据集划分后val和train如果来自同一个患者的连续帧图像,两边的背景和皮损形态高度相似,等于模型在「开卷考试」里拿了高分,一进「闭卷考场」就露馅。
解决:这种问题在小样本皮肤病数据集里尤其隐蔽,因为皮肤病拍照通常是连续采集,多张图可能来自同一部位。拿到的第一件事是看图像文件名编号,如果同一前缀连续编号多张,先手动抽样划分一次,把同一个来源的图像全部归到train或val,再评估真实效果。
6. 把234张撑成2000张:数据增强策略与类别平衡的取舍
深色皮肤图像最大的技术痛点是对比度低、肤色纹理干扰大,直接套用通用目标检测的数据增强不一定有效。yolov8内置了丰富的增强参数,但默认配置是按自然图像调的,用在皮肤病数据上我一般会做两处调整:把HSV色域增强中的饱和度扰动调低,避免皮损颜色失真;同时开启CLAHE预处理,提升深色皮肤区域的局部对比度。
yolo detect train \ model=yolov8n.pt \ data=data.yaml \ epochs=150 \ imgsz=640 \ batch=16 \ hsv_h=0.015 \ hsv_s=0.2 \ hsv_v=0.2 \ fliplr=0.5 \ mosaic=1.0hsv_h是色调扰动幅度,0.015表示在正负1.5%的范围内随机调整色相,皮肤病类别之间靠颜色区分,调太大会让白糠疹和色素沉着过度的颜色特征互相污染。fliplr是水平翻转概率,皮肤病皮损没有方向性,0.5是默认值。mosaic把四张图拼接成一张训练样本,对小数据集提升明显,1.0表示每个epoch都启用,但如果你发现训练loss稳定后验证集指标反而波动大,可以降到0.5试试。
除此之外,自己做离线增强时,我倾向于用随机crop+亮度扰动,而不是旋转——皮肤病变的形态学特征和旋转角度无关,但亮度扰动更贴近真实临床环境光照差异。增强的目标不是把234张翻到几千张,而是让模型每次epoch看到的样本分布更贴近实际场景。判断增强参数是否过头的标准只有一个:train loss和val loss的差距。增强太弱,训练集很快被记住,val loss提前反弹;增强太强,train loss一直下不去,val loss也跟着横盘。两者之间的平衡点需要跑两三轮小实验试出来,每一轮只改一个增强参数,不要同时动三个。
我现在的习惯是每次拿到新数据集,先把train和val的文件名单打出来,比对文件名编号有没有连续帧残留,再抽三个标签去做格式交叉验证,最后才进训练流程。这套流程走下来,至少能筛掉一半的「训练翻车」事故,多数问题都出在数据本身而不是模型代码上。希望这份笔记能帮你在自己的数据集上少走几步弯路。
本文还有配套的精品资源,点击获取