简介:痤疮检测数据集面向皮肤检测、医学图像识别方向的算法学习者和模型训练者,提供一套可直接用于目标检测任务的标注数据,帮助解决痤疮区域定位与识别中样本获取难、标注成本高的问题。资源包共约2000个文件,包含915张jpg图片、915个VOC格式xml标注文件、915个YOLO格式txt标注文件及少量说明文件,压缩包大小约31.74MB,同时兼容Pascal VOC与YOLO两种主流训练流程,无需额外格式转换即可投入实验。标注类别为cuochuang单类,采用labelImg工具绘制矩形框,累计标注框数达11807个,平均每张图约12.9个目标,标注密度较高,适合小目标密集场景下的检测模型训练与验证。目前已有206人学习下载,可作为课程设计、毕业设计或医学图像检测课题的基础数据,便于快速搭建训练管线、对比不同检测算法效果,并在此基础上进行数据增强与模型调优。
1. 915张痤疮检测数据集:VOC与YOLO双格式到底怎么选、怎么用
手上拿到一个标注好的痤疮检测数据集,915张图、1个类别,压缩包名字里写着「VOC+YOLO格式」。很多人第一反应是解压、找train、直接喂给YOLOv8跑训练,结果要么路径报错,要么标签读不进去,要么训练几轮loss不降。问题往往不在模型,而在没搞清楚VOC和YOLO这两套标注体系到底差在哪、什么时候该用哪套、转换时哪些字段会丢。这个数据集规模不大,属于典型的小样本单类别检测任务,适合做皮肤病灶区域的定位验证、算法原型快速迭代,也适合刚接触目标检测的从业者拿来跑通全流程。它解决的核心诉求很明确:让你不用自己从零标注,就能把一份现成的痤疮图像数据接进YOLO训练管线。适合人群包括做医疗辅助诊断原型的算法工程师、需要快速验证检测方案的学生,以及想拿真实小数据集练手YOLO训练与评估的开发者。下面按「先认清格式、再动手转换、最后训练排错」的顺序拆开讲。
2. VOC与YOLO标注格式的差异与选型判断
2.1 两套格式的文件结构对比
VOC格式的核心是每张图对应一个XML文件,文件名与图片同名,放在Annotations目录下。XML里记录图片尺寸、目标类别名、边界框的xmin、ymin、xmax、ymax四个坐标值。YOLO格式则是每张图对应一个txt文件,放在labels目录下,每行表示一个目标,格式为「类别索引 中心x 中心y 宽 高」,且这四个值都是相对于图片宽高的归一化数值,范围在0到1之间。
这个数据集标注为1个类别,意味着YOLO的txt里每行开头都是0。VOC的XML里则会出现具体的类别名称,比如acne或lesion,具体名称取决于标注者定义。两套格式最本质的区别在于坐标表达方式:VOC用绝对像素值,YOLO用归一化相对值。这个差异直接决定了转换时不能简单复制数值,必须做归一化计算。
| 对比项 | VOC格式 | YOLO格式 |
|---|---|---|
| 标注文件 | XML | TXT |
| 坐标类型 | 绝对像素 | 归一化0-1 |
| 类别表达 | 字符串名称 | 整数索引 |
| 目录结构 | Annotations/JPEGImages | images/labels |
| 多目标 | 多个object节点 | 多行文本 |
2.2 什么场景选VOC、什么场景选YOLO
选VOC的典型场景是:你需要用LabelImg继续修改标注、要把数据接入MMDetection或Detectron2这类框架、或者需要保留类别名称的可读性做人工复核。VOC的XML结构自描述性强,打开就能看懂每个框对应什么类别、图片多大,适合标注阶段和跨框架迁移。
选YOLO的场景更直接:你要用Ultralytics系的YOLOv5、YOLOv8、YOLOv11训练,这些框架的数据加载器默认读YOLO格式的txt。用VOC格式虽然也能通过配置转换,但多一层中间环节就多一个出错点。对于915张这种小规模数据集,我一般建议直接转成YOLO格式,把目录结构一次性摆对,后面训练脚本不用改。
注意:这个数据集同时提供VOC和YOLO两套格式,不代表两套内容一定完全一致。常见情况是YOLO格式由VOC转换而来,转换脚本如果有bug,可能出现框偏移或漏标。拿到后先用下面第4章的校验脚本比对两套格式的框数量是否一致。
2.3 单类别数据集的标签索引陷阱
1个类别听起来简单,但恰恰容易翻车。YOLO的类别索引从0开始,所以唯一类别就是0。问题出在有些转换脚本会写成从1开始,或者VOC的类别名映射表里多写了一个背景类,导致训练时类别数配置成2,模型输出维度对不上,训练直接报错或loss异常。
判断方法很直接:打开任意一个YOLO的txt文件,看每行第一个数字。如果全是0,说明索引正确;如果出现1,说明转换时类别映射偏了。同时检查data.yaml里的nc字段,单类别必须写nc: 1,names列表里只放一个类别名。这两处必须一致,否则训练时会出现「标签类别超出范围」的报错。
3. 从VOC到YOLO的转换脚本与目录组织
3.1 转换脚本的完整实现
下面这个脚本读取VOC的XML,输出YOLO格式的txt,同时完成坐标归一化。脚本假设图片是jpg格式,XML在Annotations目录,输出到labels目录。
import os import xml.etree.ElementTree as ET # 类别映射:根据实际XML里的类别名修改 class_map = {"acne": 0} # 单类别,索引为0 def convert_bbox(size, box): """将VOC的xmin,ymin,xmax,ymax转为YOLO的center_x,center_y,w,h(归一化)""" dw = 1.0 / size[0] dh = 1.0 / size[1] x_center = (box[0] + box[1]) / 2.0 y_center = (box[2] + box[3]) / 2.0 w = box[1] - box[0] h = box[3] - box[2] return (x_center * dw, y_center * dh, w * dw, h * dh) def convert_annotation(xml_path, out_dir): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) img_name = os.path.splitext(os.path.basename(xml_path))[0] out_path = os.path.join(out_dir, img_name + ".txt") with open(out_path, "w") as f: for obj in root.iter("object"): cls_name = obj.find("name").text if cls_name not in class_map: continue cls_id = class_map[cls_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) bb = convert_bbox((w, h), (xmin, xmax, ymin, ymax)) f.write(f"{cls_id} {' '.join([f'{v:.6f}' for v in bb])}\n") if __name__ == "__main__": xml_dir = "Annotations" out_dir = "labels" os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if xml_file.endswith(".xml"): convert_annotation(os.path.join(xml_dir, xml_file), out_dir)逻辑说明:convert_bbox函数接收图片宽高和VOC的四个坐标,先算中心点,再除以宽高做归一化。注意传入顺序是(xmin, xmax, ymin, ymax),因为函数内部按这个顺序取box[0]到box[3]。convert_annotation遍历XML里所有object节点,按class_map过滤并取索引,最后格式化成6位小数写入txt。参数方面,class_map必须和XML里的name字段完全匹配,大小写敏感;如果XML里类别名是Acne而映射表写acne,会全部跳过导致空标签。
3.2 训练目录的摆放规范
YOLOv8训练时对目录结构有约定,推荐按下面这样组织:
dataset/ ├── images/ │ ├── train/ # 训练图片,约732张 │ └── val/ # 验证图片,约183张 ├── labels/ │ ├── train/ # 对应训练标签txt │ └── val/ # 对应验证标签txt └── data.yaml划分比例按8:2比较常见,915张大约是732训练、183验证。图片和标签必须同名,只是扩展名不同。如果图片是jpg,标签是txt,放在images和labels两个平行目录下,YOLO会自动按路径替换找到标签。不要把所有图片堆在一个目录再在yaml里写同一个路径,那样验证集和训练集会重叠,评估指标虚高。
data.yaml的内容如下:
path: ./dataset train: images/train val: images/val nc: 1 names: ["acne"]path是数据集根目录,train和val是相对path的路径。nc必须等于names列表长度,单类别就是1。names里的名称只用于显示,不影响训练,但建议和VOC里的类别名保持一致,方便对照。
3.3 转换后的快速自检
转换完不要直接开训,先跑一遍数量核对。VOC的XML里object节点总数应该等于YOLO所有txt的行数总和。下面这段脚本做这个比对:
import os import xml.etree.ElementTree as ET def count_voc_objects(xml_dir): total = 0 for f in os.listdir(xml_dir): if f.endswith(".xml"): tree = ET.parse(os.path.join(xml_dir, f)) total += len(tree.getroot().findall("object")) return total def count_yolo_lines(label_dir): total = 0 for f in os.listdir(label_dir): if f.endswith(".txt"): with open(os.path.join(label_dir, f)) as fh: total += len([l for l in fh if l.strip()]) return total voc_total = count_voc_objects("Annotations") yolo_total = count_yolo_lines("labels") print(f"VOC目标数: {voc_total}, YOLO目标数: {yolo_total}") assert voc_total == yolo_total, "数量不一致,检查转换脚本或类别映射"如果两个数字不等,优先查class_map是否漏了类别名,其次查XML里是否有坐标为0或超出图片范围的异常框。坐标异常会导致归一化后数值不在0到1之间,虽然脚本仍会写入,但训练时这些框会被忽略或产生梯度异常。
4. 用YOLOv8训练痤疮检测模型的完整命令与参数
4.1 环境准备与预训练权重选择
训练前先确认环境。Ultralytics的安装命令是pip install ultralytics,它会自动装好torch和torchvision。如果机器有CUDA,装完后用python -c "import torch; print(torch.cuda.is_available())"确认返回True。没有GPU也能跑,但915张图在CPU上训练会非常慢,建议至少用一张显存8G以上的卡。
预训练权重方面,YOLOv8提供n、s、m、l、x五个尺度。单类别小数据集用yolov8n.pt或yolov8s.pt就够了,模型太大反而容易过拟合。权重文件在首次训练时会自动下载,也可以提前下好放到当前目录。注意不要用分类任务的权重,检测任务必须用带检测头的预训练模型。
4.2 训练命令与关键参数解释
yolo detect train \ data=./dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/acne \ name=exp1逐项说明:data指向yaml配置文件;model指定预训练权重;epochs是训练轮数,小数据集100轮通常够,配合patience=20表示20轮验证指标不提升就早停;imgsz=640是输入分辨率,痤疮病灶如果比较小,可以提到768或896,但显存占用会增加;batch=16根据显存调整,显存不够就降到8或4;lr0是初始学习率,0.01是YOLOv8的默认值,小数据集可以降到0.005减少震荡;project和name决定输出目录,训练日志、权重、混淆矩阵都会存在runs/acne/exp1下。
训练过程中重点看三个指标:box_loss持续下降说明框回归在收敛;mAP50在验证集上逐步上升说明检测能力在提升;如果box_loss下降但mAP50不动,大概率是过拟合或验证集标签有问题。单类别任务的混淆矩阵只有两类(背景和acne),如果矩阵里acne被大量预测为背景,说明模型没学到病灶特征,需要检查标注框是否准确。
4.3 训练结果解读与推理验证
训练结束后,runs/acne/exp1/weights目录下会有best.pt和last.pt。best.pt是验证指标最好的权重,推理时优先用它。推理命令:
yolo detect predict \ model=runs/acne/exp1/weights/best.pt \ source=./dataset/images/val \ conf=0.25 \ save=Trueconf=0.25是置信度阈值,低于这个值的框不显示。痤疮检测如果漏检多,可以降到0.1看更多候选框;如果误检多,提到0.4以上。推理结果默认存在runs/detect/predict下,图片上会画出框和置信度。拿几张结果图对照原图看,重点看小病灶有没有被漏掉、密集区域有没有框重叠。
提示:915张图训练出来的模型泛化能力有限,换一批不同来源的痤疮图像,指标大概率会下降。这个数据集更适合做流程验证和原型演示,不要直接当成临床级模型使用。
5. 训练痤疮检测模型时最容易踩的五个坑
5.1 标签文件与图片不对应导致空标签
现象:训练启动后loss一直是nan或0,或者日志里提示「no labels found」。原因通常是labels目录下的txt文件名和images下的图片名不一致,比如图片是IMG_001.jpg而标签是img_001.txt,大小写差异导致匹配失败。另一种情况是转换脚本输出的txt全部为空,因为class_map里的类别名和XML里的name不匹配,所有object都被continue跳过了。解决办法:用第3.3节的脚本核对数量,如果YOLO总行数为0,检查XML里的实际类别名,把class_map的键改成完全一致的值。
5.2 坐标归一化时宽高传反
现象:训练能跑,但框的位置明显偏移,比如框集中在图片左上角或尺寸异常。原因是convert_bbox函数里把宽高传反了,或者VOC的xmin、ymin顺序和函数参数顺序不一致。VOC的bndbox子节点顺序是xmin、ymin、xmax、ymax,但有些标注工具输出的顺序可能不同。解决办法:打开一个XML确认子节点顺序,再对照转换脚本的参数顺序。归一化后的中心点x应该在0到1之间,如果出现大于1的值,说明除以的宽高不对。
5.3 训练集和验证集图片重叠
现象:验证集mAP50异常高,接近0.95以上,但换一批新图推理效果很差。原因是划分时没有去重,同一张图既在train又在val里,模型相当于在验证集上「见过答案」。解决办法:划分前先对所有图片名做集合运算,确保train和val的交集为空。如果数据集本身有同一病灶的连拍图,建议按病灶ID分组划分,而不是按图片随机划分,否则同一病灶的不同角度图会分到两边,造成信息泄漏。
5.4 类别数配置错误导致输出维度不匹配
现象:训练时报错「IndexError: Target X is out of bounds」或「AssertionError: nc mismatch」。原因是data.yaml里nc写成2或更多,而标签里只有类别0。YOLO的检测头输出维度由nc决定,nc=2时模型预测两个类别,但标签只有0,计算loss时索引越界。解决办法:确认data.yaml里nc: 1,names列表只有一个元素。同时检查是否有转换脚本在类别索引上加了1,导致标签里出现1。
5.5 小目标病灶在640分辨率下丢失
现象:训练指标尚可,但推理时小颗痤疮漏检严重。原因是痤疮病灶在原图中可能只占几十个像素,缩放到640后信息进一步丢失。解决办法:把imgsz提高到896或1024,同时batch相应减小。另一种做法是在数据加载时不做过度缩放,保持原图比例做letterbox填充。如果显存不够,可以改用yolov8s并开启多尺度训练,让模型适应不同尺度的小目标。
6. 小样本单类别检测的进阶技巧:从915张里榨出更多信息
915张图、1个类别,数据量确实不大。想让模型表现更稳,有几个我实际用过的手段。第一个是离线数据增强:在训练前用albumentations对训练集做翻转、旋转、亮度调整、高斯噪声,把训练集扩到3000张左右。注意增强只对训练集做,验证集保持原样,否则评估指标不可信。第二个是 mosaic 和 mixup 的开关控制:YOLOv8默认开启mosaic,对小数据集有帮助,但如果病灶本身形态固定,mosaic可能引入不自然的拼接,可以在最后20轮关掉mosaic,让模型在真实分布上微调。
第三个技巧是冻结 backbone 做 warmup。前10轮把backbone的学习率设得很低或直接冻结,只训练检测头,让模型先适应单类别的输出分布,再解冻全网络微调。命令上可以通过freeze参数控制:
yolo detect train \ data=./dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=896 \ batch=8 \ freeze=10 \ lr0=0.005 \ patience=20freeze=10表示冻结前10层,具体层数可以用model.model查看。这个做法在数据量小于1000时通常能提升2到5个点的mAP50。
验证阶段除了看mAP,建议手动抽20张验证图做混淆矩阵的逐图核对。YOLO自带的混淆矩阵是汇总的,单类别任务里如果背景类被大量误判为acne,矩阵上表现为背景行acne列数值高,这时候要回去看标注是不是把非病灶区域也框进去了。我自己的习惯是每次训练完先看混淆矩阵,再看PR曲线,最后才看mAP数字。PR曲线能看出在不同置信度下的查准查全平衡,比单一mAP更能反映模型在业务阈值下的表现。
最后说一个习惯:每次改完数据或参数,把data.yaml、训练命令、关键指标记在一个文本文件里,和权重放在一起。小数据集实验迭代快,不记录的话两周后就忘了哪组参数对应哪个结果。这个数据集本身不大,但把流程跑通、把每个坑踩明白,换成其他单类别检测任务也能直接复用。希望帮到你。
本文还有配套的精品资源,点击获取