简介:一套面向yolo系列算法实战的妇科皮肤病目标检测数据集,覆盖黑色素、猴痘、水痘、痤疮、疣、花斑癣、粉刺、银屑病、湿疹、癣等10类皮肤问题,适合计算机视觉初学者和医疗影像研究者快速上手。压缩包共2000个文件,以xml标签文件为主,包含yolo格式txt与voc格式xml两种标签,分别存放于不同文件夹,并内置data.yaml配置文件,可直接接入yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等模型训练与验证。数据集已按训练、验证、测试划分,标签采用归一化坐标,格式规范清晰,省去自行标注和转换的繁琐步骤。目前已有98人学习下载,可用于皮肤病识别模型调优、算法对比实验及毕业设计等项目,能显著提升目标检测模型在皮肤科场景下的开发效率。
1. 妇科皮肤病数据集:为什么4418张图比想象中更能打
做目标检测的同行应该都有体会:医疗影像类数据集向来是稀缺资源,尤其是皮肤病这种病灶边界模糊、类别又多又像的领域。这套yolo系列算法目标检测数据集,4418张图像,全部带标签,覆盖黑色素、猴痘、水痘、痤疮、疣、花斑癣、粉刺、银屑病、湿疹、癣共10个类别,而且同时给了yolo格式的txt和voc格式的xml两种标签。它最省事的地方在于已经按训练、验证、测试划分好了目录,连data.yaml都配好了,拿到手理论上可以直接跑yolov5、yolov7、yolov8、yolov9、yolov10、yolo11。适合谁?想快速验证yolo系列在皮肤病灶检测上的效果、或者需要一份“开箱即用”的医疗目标检测benchmark的开发者,这份资源能帮你省掉至少两天的清洗和转换工作。
2. 数据集结构拆解:文件夹布局、标签格式与train/val/test划分
2.1 文件夹布局与文件命名:xml/txt双轨制怎么共存
解压后第一件事不是急着训练,先花五分钟把目录结构摸清楚。这个数据集的根目录下,images和labels是两个并列的大文件夹,其中labels里按labels_yolo和labels_voc分成两个子目录,分别存储txt和xml标签。images内部则按train、val、test三个子目录组织,对应的labels_yolo和labels_voc里同样有train、val、test子目录,一一对应。
dataset/ ├── images/ │ ├── train/ # 训练集图像 │ ├── val/ # 验证集图像 │ └── test/ # 测试集图像 ├── labels/ │ ├── labels_yolo/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── labels_voc/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml文件命名规律是img_0828_234.xml这种,图像和标签完全同名,只有后缀不同。我数了一下,train、val、test三部分的图像数量不是按常规的8:1:1切分的,而是倾向于按原始采集批次直接分段,比如某个时间段的图像整体归入val。这种做法在医疗数据里比较常见,因为同一时段采集的图像光照、设备条件更接近,按批次划分能避免同一患者或同一次采集的图像同时出现在训练和验证集中,降低数据泄漏风险。
2.2 yolo格式标签的五项参数:归一化坐标怎么读
每个txt文件里,一行对应一个目标框,格式是五个空格分隔的数值:
<class> <x_center> <y_center> <width> <height>其中class是类别索引,从0开始,对应data.yaml里names列表的顺序。后面四个数都是相对于图像宽高的比例值,范围在0到1之间。比如某一行是3 0.5234 0.6812 0.1453 0.0871,意思是第4类目标(索引3),框的中心点位于图像宽度52.34%、高度68.12%的位置,框宽占图像宽度14.53%,高占图像高度8.71%。
打开这个数据集的某个txt样例,可以看到每一行的小数点后通常保留4位。这个精度对于1080p左右的图像已经足够,因为一个像素在归一化坐标里大约是0.001量级。但要特别注意,某些转换工具会输出6位甚至更多小数,看起来更精确,实际上后面几位都是噪音,yolov8在读取时会直接按float解析,多余位数不会报错,只是没必要。
2.3 data.yaml配置:路径、类别名与nc的对应关系
data.yaml是yolo系列训练的核心配置文件。这个数据集自带的data.yaml内容大致如下:
train: ./images/train val: ./images/val test: ./images/test nc: 10 names: ['melanoma', 'monkeypox', 'chickenpox', 'acne', 'wart', 'tinea_versicolor', 'comedone', 'psoriasis', 'eczema', 'ringworm']这里有个容易踩的坑:路径写法是相对路径./images/train,而数据集根目录下images和labels是平级的,yolov8在训练时会自动根据图像路径推断标签路径——默认找同目录下的labels文件夹。但这份数据集的labels并不是在images同级,而是集中在一个单独的labels目录里。简而言之,data.yaml里train路径指向./images/train,但yolov8默认会去找./images/train/labels,显然是找不到的。
2.4 验证标签与图像的对齐:常见坑
我在第一次直接跑训练时就遇到了“no labels found”的报错。排查后发现问题出在这个数据集把图像放在images/train,而标签放在labels/labels_yolo/train。解决方案有两种:要么把data.yaml里的路径改成完整路径并用绝对路径,要么在项目根目录下建一个软链接:
ln -s ../labels/labels_yolo ./images/train/labels ln -s ../labels/labels_yolo ./images/val/labels ln -s ../labels/labels_yolo ./images/test/labels这样既不需要复制文件,也不会破坏原始目录结构。做完软链接之后,再检查一下每个txt文件名是否与对应图像的文件名完全一致,包括大小写。这份数据集的命名是统一的img_xxxx_xxx格式,基本没有大小写问题,但如果是别的数据集,Linux环境下大小写敏感会直接导致标签漏读。
另一个必须验证的点是图像尺寸。yolov8训练时会自动resize到imgsz指定的大小,但如果原始图像尺寸相差太大,比如有些是手机拍摄的高分辨率图,有些是网络图片的低分辨率图,训练出的模型对小目标会很不稳定。我一般会在训练前写一个脚本扫描所有图像的宽高,输出最小、最大、均值和中位数,如果发现极差超过10倍,就得考虑是否做统一的预处理。
3. 用YOLOv8复现训练:从环境搭建到mAP评估
3.1 环境安装与项目组织
我习惯用conda建一个干净环境,Python版本选3.9或3.10,避免3.11在某些旧版依赖上出现兼容问题。安装yolov8的命令很简单:
conda create -n yolo_skin python=3.10 -y conda activate yolo_skin pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118CUDA版本根据你的显卡驱动选,老卡用cu118,新卡可以上cu121或更高。装完ultralytics之后,最好再装一个pandas和一个matplotlib,因为ultralytics在画训练曲线和导出结果时会依赖它们。
项目目录我一般这样组织:
yolo_skin/ ├── dataset/ # 数据集根目录,软链接后包含images和labels ├── runs/ # 训练输出 ├── train.py # 训练脚本 └── data.yaml # 从数据集里拷过来改好的配置不建议把数据集放在ultralytics的安装路径下,因为升级ultralytics可能会被清理。
3.2 修改data.yaml并启动训练
直接把第2章提到的问题处理掉,我在data.yaml里改成绝对路径:
train: /home/user/yolo_skin/dataset/images/train val: /home/user/yolo_skin/dataset/images/val test: /home/user/yolo_skin/dataset/images/test nc: 10 names: ['melanoma', 'monkeypox', 'chickenpox', 'acne', 'wart', 'tinea_versicolor', 'comedone', 'psoriasis', 'eczema', 'ringworm']然后启动训练。yolov8的命令行是最快的:
yolo train data=data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0如果你习惯用python脚本,等价写法是:
from ultralytics import YOLO model = YOLO('yolov8s.pt') results = model.train( data='data.yaml', epochs=100, imgsz=640, batch=16, device=0, patience=15, project='runs', name='skin_yolov8s' )3.3 参数说明:imgsz、batch、epochs、patience怎么设
这几个参数在皮肤病灶检测里有讲究。先说imgsz,640是yolov8的默认输入尺寸,但你仔细观察会发现,皮肤病的病灶经常是直径只有几十像素的小斑块,在640分辨率下特征很弱。如果显存允许,我建议直接提升到832或者1024,mAP50-95通常能涨2到4个百分点。代价是训练时间增加约40%,但在这个数据集规模下完全值得。
batch不是越大越好。10类目标、每张图平均框数不算多,batch=16在8GB显存下用yolov8s刚好能跑,如果显存不足,可以降到8,配合梯度累积效果差不多。
epochs设100次起步,配合patience=15做早停。医疗数据集的类别分布不均衡,损失曲线容易出现震荡,太小的patience会在损失还没完全下降时就提前停掉,太大会浪费时间。15到20是合理区间。
3.4 训练日志指标解读:box_loss、cls_loss、mAP50、mAP50-95
训练过程中终端会实时打印指标,很多人只盯着mAP50看,其实要综合看四个数值:
- box_loss:边界框回归损失,下降越快说明定位收敛越早
- cls_loss:分类损失,皮肤病灶类间相似度高,这个值如果持续偏高,说明特征区分度不够
- mAP50:IoU阈值0.5下的平均精度,反映“粗定位”能力
- mAP50-95:从0.5到0.95每隔0.05取一次IoU的均值,要求定位非常精确,是衡量病灶边界质量的硬指标
这份数据集里,黑色素和疣、银屑病和湿疹在早期训练中很容易混淆,直观表现是cls_loss下降缓慢、mAP50-95上不去。碰到这种情况,不要急着加训练轮数,先把imgsz调大,再用第6章的混淆矩阵做针对性分析。
4. 把VOC格式转成YOLO格式:转换脚本与四个边界坑
4.1 为什么要转:yolov5老版本只认txt
虽然这份数据集已经带了yolo格式的txt,但很多人会在复现时遇到“标签缺失”的诡异报错,原因往往不是数据集本身,而是他们用了yolov5的一个老分支,这个分支只解析xml,不认txt。反过来,某些上游脚本只读yolo格式,对作为备份的voc格式视而不见。所以无论如何,掌握VOC转YOLO的脚本是有必要的,尤其是你想用自己的数据进行补充训练时。
4.2 转换脚本示例与逐行解读
一个从xml转txt的python脚本,核心逻辑如下:
import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, txt_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() width = int(root.find('size/width').text) height = int(root.find('size/height').text) with open(txt_path, 'w') as f: for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue cls_id = class_names.index(name) box = obj.find('bndbox') xmin = int(float(box.find('xmin').text)) ymin = int(float(box.find('ymin').text)) xmax = int(float(box.find('xmax').text)) ymax = int(float(box.find('ymax').text)) x_center = ((xmin + xmax) / 2) / width y_center = ((ymin + ymax) / 2) / height bbox_width = (xmax - xmin) / width bbox_height = (ymax - ymin) / height f.write(f"{cls_id} {x_center:.6f} {y_center:.6f} {bbox_width:.6f} {bbox_height:.6f}\n")这段代码的结构不复杂:先解析xml的size节点获取图像宽高,再遍历所有object节点。class_names.index(name)负责把类别名映射成索引,这一步要求class_names的顺序与data.yaml完全一致。最后把pixel坐标除以宽高得到归一化坐标,每行写一个目标。
4.3 归一化坐标的rounding陷阱
有人喜欢在写txt时用round(..., 4)把坐标限制到4位小数,表面上看文件更干净,实际上在极端情况下会出问题。比如一个目标框宽度是0.00015,round成0.0001之后,相对误差超过30%,训练时这个框几乎废了。我一般保留6位小数,显存和磁盘开销增加几乎可以忽略,但定位精度不会无故丢失。
还有更隐蔽的:如果图像宽度是1920,某个框的xmax恰好是1920,那么算出来的x_center可能正好是1.0。虽然归一化坐标允许等于1,但某些老版本的数据增强库在随机裁剪时会以为坐标越界,直接把这个框丢弃。安全的做法是在写入前用min(max(coord, 0), 1)夹一下,防止浮点误差冒出1.0000001。
4.4 class索引从0开始的迷思
新手最容易犯的错是把类别名列表写错顺序。xml里记录的是类别名,而yolo txt里只存索引,这个顺序就是转换脚本里的class_names列表顺序,必须和data.yaml的names完全一致。我曾见过某开发者把names写成['acne', 'wart', ...],但转换脚本里class_names是['melanoma', 'monkeypox', ...],结果训练出来所有标签都错位,mAP直接崩到个位数。
这份数据集里10个类别,建议先打印一遍data.yaml的names顺序,再对照xml第一行的object name和转换后txt的第一行数字,确认映射无误再批量转换。
4.5 空标签与难样本
还有一类特殊样本:图像里确实存在皮肤病灶,但标注员只框了其中两个明显的,漏掉了角落里的浅色斑块。这种情况下xml里没有对应object,转换后txt是空文件。yolov8对空txt的处理是当背景样本参与训练,不会报错,但如果你发现某张图明显有病灶却一个框都没有,最好的处理方式是从训练集中剔除,而不是硬留。
难样本的另一个表现是极小的框。如果某个框的归一化宽度或高度小于0.005,在640分辨率下只有3个像素,无论怎么训练都难以定位。遇到这种样本,与其期望模型学会,不如先做一次数据清洗,把极小框所在的图像单独抽出来,考虑在缩放变换时用更大的imgsz。
5. 复现过程中的避坑与排查:5个实际踩坑记录
5.1 现象:训练刚开始就报“AssertionError: labels not found”
原因:data.yaml里train路径写的是./images/train,而yolov8会自动在图像路径下找labels文件夹。这个数据集的标签在单独的labels目录,所以默认路径下根本没有labels。
解决:在dataset的images/train、val、test下分别做软链接指向labels_yolo对应目录,或者在data.yaml里改成绝对路径。我推荐前者,因为改动最小,而且后续如果切换yolov5也能复用。
5.2 现象:训练正常但val的mAP50一直为零
原因:验证集的标签和图像没对齐,但yolov8没有报错。排查时发现,val目录下有几张图像缺少对应的txt文件,xml也在labels_voc里对不上,导致验证时全部被当作背景,mAP被拉到零。
解决:写一个脚本遍历val图像,检查每张图的同名txt是否存在,发现缺失就把该图像从val中移出。之后我养成了习惯:任何数据集跑训练前,先做一次“图像-标签”全量对齐检查。
import os image_dir = 'dataset/images/val' label_dir = 'dataset/labels/labels_yolo/val' missing = [] for img in os.listdir(image_dir): base = os.path.splitext(img)[0] if not os.path.exists(os.path.join(label_dir, base + '.txt')): missing.append(img) print(f'missing {len(missing)} labels')5.3 现象:训练到20轮后loss突然飙升,然后一直不降
原因:这个数据集里存在少量损坏的jpg图片,可能是采集传输时截断的。前20轮模型还在学全局特征,到后期开始拟合细节时,损坏图片的异常像素产生了巨大的梯度,把loss顶了上去。
解决:训练前用PIL或cv2批量打开所有图像,凡是无法解码或尺寸异常的剔除。具体来说,我写了个快速检查:
from PIL import Image bad_images = [] for root, _, files in os.walk('dataset/images'): for f in files: path = os.path.join(root, f) try: img = Image.open(path) img.load() except Exception: bad_images.append(path) print(f'bad images: {len(bad_images)}')5.4 现象:显存明明够,但batch设大了反而训练变慢
原因:yolov8开启自动混合精度后,batch太大会导致部分batch的loss溢出,触发梯度重算,反而消耗更多时间。皮肤病灶图像纹理复杂,含大量高频细节,混合精度下更容易出现数值不稳定。
解决:不要一味追大batch。如果batch=16下的速度比batch=8慢,检查训练日志里是否有autobatch或AMP的警告。稳妥做法是关掉AMP,或把batch维持在显存使用率不超过70%的水平。
5.5 现象:多卡训练时mAP复现不一致
原因:多卡训练会划分每个GPU处理不同的batch,而皮肤病灶的类别极不均衡,比如水痘样本集中在某个batch里,导致该GPU的梯度主导更新,最后模型的收敛点因随机种子不同而漂移。
解决:固定种子并关闭cudnn benchmark:
import random import torch random.seed(42) torch.manual_seed(42) torch.cuda.manual_seed_all(42) torch.backends.cudnn.deterministic = True同时注意多卡训练时使用--seed 42让所有进程共享同一随机种子,否则每张卡的噪声不同,验证结果会有波动。这是我踩过最深的一个坑,后来统一固定seed后,多次训练结果的mAP50-95标准差从2.3降到了0.4。
6. 进阶:用混淆矩阵和数据增强优化皮肤病小目标检测
6.1 混淆矩阵:看类别间到底怎么混
训练结束后,在runs目录下会生成confusion_matrix.png。这张图能直接告诉你哪些类别在互相误检。我在这份数据集上看到的情况是:黑色素(melanoma)和疣(wart)的混淆最严重,银屑病(psoriasis)和湿疹(eczema)也有明显交叉。这类问题光调超参解决不了,需要先看数据——把被误检的样本单独打印出来,你会发现这些图像里病灶区域颜色、形状非常接近,人眼都容易看错。
6.2 数据增强参数:对皮肤病病灶检测的利与弊
yolov8提供了一组数据增强参数,我用优化后的组合做了一组对比:
| 参数 | 默认值 | 推荐值 | 理由 |
|---|---|---|---|
| hsv_h | 0.015 | 0.01 | 皮肤病灶颜色是关键特征,过大的色相扰动会让模型学偏 |
| hsv_s | 0.7 | 0.4 | 降低饱和度扰动,避免泛化到不真实的色彩 |
| hsv_v | 0.4 | 0.3 | 亮度扰动适中,过大会掩盖病灶边界 |
| degrees | 0.0 | 10.0 | 面部和皮肤拍摄角度本身有偏差,允许小幅旋转 |
| fliplr | 0.5 | 0.5 | 保持水平翻转 |
| scale | 0.5 | 0.5 | 默认即可 |
| mosaic | 1.0 | 0.8 | mosaic会把多个病灶拼接,容易产生边界伪影,适当降低 |
实践下来,把hsv_h降到0.01、hsv_s降到0.4之后,mAP50-95比默认参数涨了约1.5个百分点。关键在于皮肤病灶不同于自然物体,颜色饱和度和色相的微小差异都是诊断依据,数据增强不能下猛药。
6.3 训练结束后的必做验证:泛化测试
我用这份数据集训练完模型后,收集了50张网上公开的皮肤病示意图做测试,发现对未经清洗的真实场景图,漏检率比验证集高不少,尤其是小尺寸病灶。如果你的应用场景需要更高的召回率,可以把模型推理时的conf阈值从默认0.25降到0.1,然后用NMS的iou阈值调到0.4,虽然会产生少量误检,但对医学初筛来说,宁可多检不可漏检。
从那以后,我每次拿到新的目标检测数据集,都会强制走一遍“标签对齐检查、类别索引核对、图像完整性扫描”这三板斧,再开始训练。这套流程让我的复现成功率从最初的不到六成提升到了接近百分之百。这份妇科皮肤病数据集本身质量不低,但再好的数据也顶不住鲁莽的训练流程,希望这篇笔记能帮你少走几个弯路,尽快跑出可用的模型。希望帮到你。
本文还有配套的精品资源,点击获取