简介面向目标检测与医学图像识别场景的痤疮检测数据集共915张标注图像采用Pascal VOC与YOLO双格式存储配套xml和txt标注文件适合希望直接训练YOLO系列、SSD、Faster R-CNN等模型的开发者或研究人员使用。类别仅含cuochuang一项标注框总计11807个由labelImg以矩形框方式绘制标注边界清晰可作为痤疮检测任务的训练集、验证集或迁移学习基础数据。压缩包共2000个文件以txt、xml标注文件及jpg图像为主整体大小约31.74MB目录结构直接简明便于按文件名快速定位标签。已有207人浏览学习。配套信息对标注规则与格式细节作了进一步说明下载后即可开始数据加载与模型实验适合刚接触目标检测数据格式的入门者也适合需要扩充痤疮样本的中级开发者。1. 解压前先搞懂痤疮检测数据集的VOC和YOLO格式分别是什么第一次拿到痤疮检测数据集VOCYOLO格式915张1类别.7z的人最容易犯的错是直奔 YOLO 的 labels 目录忽略同名的 VOC XML 标注文件。这个数据集本身解决的是一个非常具体的需求给目标检测模型提供一份围绕皮肤痤疮病灶的单类别训练数据。核心内容是 915 张皮肤影像每张图对应 VC 格式的 XML 标注同时给出 YOLO 格式的 TXT 标注类别只有一类即痤疮。VOC 格式是传统检测工具箱的标准输入YOLO 格式则是给 yolov5、yolov8 这类模型开箱即用的形态。它适合两类人一是刚接触目标检测、想跑通完整数据链路的学生二是做皮肤影像辅助分析的从业者用来做前置预研。但它不是临床诊断数据集没有严重程度分级不能直接拿去训练医疗级皮肤病变识别系统这一点先放心里后面所有操作都建立在这个边界上。2. 用 7z 解压并梳理目录915 张图片与两种标注的核对方法拿到压缩包第一步不是训练而是把文件和目录结构彻底搞清楚。7z 压缩格式在这类数据集里出现频率很高因为它在压缩率上比 zip 更占优势。但 7z 在 Windows 和 Linux 下的解压姿势不一样乱解容易出现“解到一半提示文件损坏”“目录嵌套多了一层”这类问题。下面先解决解压再解决目录核对。2.1 Linux 与 Windows 下解压 7z 文件p7zip 与命令行参数Linux 环境默认不带7z命令需要先安装 p7zip-full 包。Windows 上最常用的是 7-Zip但图形界面右键解压会丢失一些细节比如不保留目录结构。我更建议在命令行里统一处理这样后续做数据集梳理时命令可以直接复用。# Linux 安装 p7zip sudo apt install -y p7zip-full # 解压到指定目录保留完整目录结构 7z x acne_dataset.7z -o./acne_dataset -y # Windows 命令同理前提是 7z.exe 已加入 PATH 7z x acne_dataset.7z -o./acne_dataset -y-o参数指定输出目录注意这里没有空格写成-o./acne_dataset而不是-o ./acne_dataset这是 7z 命令容易踩的小坑。-y表示遇到同名文件时自动覆盖避免交互卡住。如果只是想看压缩包内部结构可以先执行7z l acne_dataset.7z列出文件树确认顶层是不是套了一层同名文件夹再决定解压到哪里。很多数据集打包时会把全部文件放在一个顶级目录里直接解压会产生acne_dataset/acne_dataset/images这类双层结构后续写 YAML 配置时路径就会变长提前发现能省不少事。解压完成后先验证完整性尤其是从网盘或某站下载的压缩包。运行7z t acne_dataset.7z会做一次完整 CRC 校验如果输出中有ERROR字样说明文件已经损坏重新下载比硬解压更快。2.2 核对 915 张图片与 VOC/XML 和 YOLO/TXT 的对应关系解压后不要急着进训练先把目录结构列一遍。一个规范的数据集顶层至少应该有images、annotations、labels三个平级目录或者已经按train/val分好子目录。前者代表原始格式和转换格式并存后者代表可以直接进 YOLO 训练。先用一段命令摸清家底。# 查看顶层目录结构 find . -maxdepth 2 -type d # 统计图片、XML、TXT 数量 ls images | wc -l ls annotations | wc -l ls labels | wc -l三个数字可能不是严格相等。常见场景是某些图片没有标注或者某张图的标注文件被标注软件导出成了空文件。如果annotations数量比images少说明存在未标注的负样本图如果labels数量比annotations少说明压缩包内置的 YOLO 转换不完整。这两种情况都要先处理而不是直接拿数字去拼一个训练集。最稳妥的做法是抽查同名文件是否一一对应。# 抽查一张图对应的标注文件 imgimages/000001.jpg base$(basename $img .jpg) ls annotations/$base.xml ls labels/$base.txt如果抽查发现 XML 和 TXT 都存在但内容对应的目标数量不一致就需要警惕这种“标注双轨制”在开源数据集里并不少见因为 VOC 转换 YOLO 的过程中可能存在漏掉小目标或过滤掉重叠框的情况。我一般会写一个脚本全量比对每个 XML 中的object数量和对应 TXT 文件的行数行数不一致的文件单独列出来。数量对得上再进入下一步确认类别名。3. VOC 转 YOLO 格式坐标归一化公式与批量转换脚本很多从网上下载的数据集虽然标题里写了 VOC 和 YOLO 两种格式但拿到手后你会发现 labels 目录是空的或者只有一部分 TXT。别慌这种情况再常见不过。VOC 转 YOLO 是目标检测数据准备阶段的必修课尤其是这种单类别数据集转换脚本写起来并不复杂但坐标系的转换逻辑必须一次到位。3.1 两种坐标体系VOC 的绝对坐标与 YOLO 的归一化中心坐标VOC 格式的标注文件是 XML框坐标用xmin、ymin、xmax、ymax四个像素值表示单位是原始图像像素。YOLO 格式的 TXT 每一行代表一个目标格式是class_id x_center y_center width height其中x_center、y_center、width、height都是归一化后的相对值范围在 0 到 1 之间。两种坐标体系的换算公式并不复杂。已知图像宽度img_w和高度img_h以及 VOC 框的四个角点YOLO 的中心点坐标和宽高分别是x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h这个公式看起来简单但坑在两边第一XML 里的宽高必须和实际图片尺寸一致否则归一化后坐标出错模型训练时框会整体偏移第二转换脚本里浮点数保留位数太少会导致小目标框精度丢失。我习惯保留六位小数这样对小框更友好。3.2 批量转换脚本XML 目录到 TXT 目录的完整命令下面这段脚本是我处理单类别 VOC 转 YOLO 时常用的模板。它遍历annotations目录下所有 XML解析每个目标框输出同名 TXT 到labels目录。import os import glob import xml.etree.ElementTree as ET # 只需要改这三个参数 voc_dir annotations # VOC XML 目录 out_dir labels # 输出 YOLO TXT 目录 class_map {acne: 0} # 类别名到 ID 的映射单类别固定为 0 if not os.path.exists(out_dir): os.makedirs(out_dir) for xml_file in glob.glob(os.path.join(voc_dir, *.xml)): tree ET.parse(xml_file) root tree.getroot() # 图片尺寸必须取 XML size 节点下的值不能自己猜 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: # 遇到未知类别直接跳过但打印日志方便排查 print(fskip unknown class: {name} in {xml_file}) continue cls_id class_map[name] bnd obj.find(bndbox) x1 float(bnd.find(xmin).text) y1 float(bnd.find(ymin).text) x2 float(bnd.find(xmax).text) y2 float(bnd.find(ymax).text) # 过滤非法框宽或高小于等于 0 的标注必然有问题 if x2 - x1 0 or y2 - y1 0: print(finvalid bbox: {xml_file}) continue # 核心归一化公式 cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 越界保护坐标 clip 到 [0, 1]避免 YOLO 增强阶段崩溃 cx max(0.0, min(1.0, cx)) cy max(0.0, min(1.0, cy)) w max(0.0, min(1.0, w)) h max(0.0, min(1.0, h)) lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # 输出 TXT 与 XML 同名 out_name os.path.splitext(os.path.basename(xml_file))[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))脚本逻辑分三段。第一段解析 XML 里的图片尺寸所有归一化计算都依赖这个值必须和图片真实尺寸一致。第二段遍历object节点提取类别名和框坐标这里用class_map做白名单过滤遇到未知类别直接跳过防止标注软件导出的无关类别污染数据集。第三段做归一化计算并写出 TXT写文件时每行只写一个目标。两个参数需要重点关注。class_map里类别名必须和 XML 中的name文本严格一致比如acne和Acne会被视为两个类别。clip操作是防呆设计正常情况下标框不会越界但皮肤影像里有些标注员会把框拉到图片边缘外一两像素不 clip 的话后期 YOLO 做 mosaic 增强时可能出现数组越界报错。转换完成后随机抽几个 TXT 文件打开看每行四个浮点数加一个整数数值范围应全部落在 0 到 1 之间。如果出现1.000001或者负数说明原标注越界过多这个 clip 操作兜住了问题。4. 用 YOLOv8 训练自己的数据集915 张单类的划分与参数设定数据集格式梳理完接下来进入正题跑一个像样的训练。标题里的1类别意味着这是一个单类目标检测任务只有一个acne类别。915 张图在深度学习里不算大数据集训练周期短显存占用低特别适合第一次接触低资源训练的从业者。但参数设置如果直接照搬公开教程里的大数据集方案很容易把模型训崩或过拟合。重点在于数据划分和关键超参数。4.1 dataset.yaml 与目录划分915 张数据拆 train/val/test 的正确姿势如果压缩包解压后已经带有train和val目录可以跳过手动划分。但更多时候拿到的是平级目录images放全部图片annotations和labels放全部标注。这种情况下必须先划分数据集而不是直接把全部 915 张图同时塞进训练和验证。我习惯按 8:1:1 划分成 train、val、test 三份也就是大约 730 张训练、90 张验证、90 张测试。单类别数据集的验证集不需要太大但测试集必须有否则后面判断模型真实表现时会少一个维度。划分脚本如下。import os import random import shutil random.seed(0) # 固定随机种子保证每次划分结果一致 all_images [f for f in os.listdir(images) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(all_images) n_val int(len(all_images) * 0.1) n_test int(len(all_images) * 0.1) train_list all_images[n_val n_test:] val_list all_images[:n_val] test_list all_images[n_val:n_val n_test] for split, files in [(train, train_list), (val, val_list), (test, test_list)]: img_out os.path.join(split, images) lbl_out os.path.join(split, labels) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for name in files: shutil.copy(os.path.join(images, name), os.path.join(img_out, name)) label_name os.path.splitext(name)[0] .txt label_src os.path.join(labels, label_name) if os.path.exists(label_src): shutil.copy(label_src, os.path.join(lbl_out, label_name))这段脚本做了三件事打乱图片列表、按比例截断、成对复制图片和对应 TXT 到train/images、train/labels等目录。注意它只复制存在的 TXT如果某张图没有标注文件图片会被复制过去但对应的 labels 目录里就没有同名 TXT。YOLO 会把这种图自动当作背景图参与训练对单类别任务来说反而有帮助可以让模型学会区分“有痘”和“没痘”。划分完成后构造acne.yaml配置文件。这是 YOLO 训练的核心输入之一路径写错直接报错。path: /mnt/data/acne_dataset # 数据集的绝对路径 train: images/train val: images/val test: images/test names: 0: acnepath必须是绝对路径不能写相对路径否则训练时 YOLO 在当前工作目录下找不到数据集。train和val相对path填写这里写的是images/train因为上面的划分脚本已经把图片放到了train/images、val/images子目录下。names里的0必须和 TXT 文件里的class_id对应TXT 中写的是0这里就得是0: acne漏掉这一行模型会在加载标签时直接报错。4.2 训练参数与 Loss 日志epochs、batch、imgsz 怎么定915 张图、单类别这个数据量的训练对算力要求很低。一张入门级显卡在 30 分钟内就能完成 100 个 epoch。训练命令我一般这样写yolo detect train \ dataacne.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ patience20 \ projectruns \ nameacne_baseline模型选择yolov8nnano 版本参数量最小。915 张单类别场景根本不需要yolov8m以上的模型模型参数越多过拟合风险越大。epochs120是经验值单类小数据集在这个范围内基本能收敛。patience20表示连续 20 个 epoch 验证集指标没有提升就停止训练防止浪费时间。batch16在 8GB 显存下安全如果显卡只有 4GB改成 8。imgsz640是 YOLOv8 默认输入尺寸这个数据集里的皮肤病灶通常比较小建议同时留一个imgsz768的实验作为对比分辨率提高对细节有利但显存压力更大。训练过程中不要只盯着 mAP要看 loss 曲线。train/box_loss、train/cls_loss、train/dfl_loss三组曲线如果持续下降说明模型还在学习如果验证集 loss 在某个 epoch 后反弹训练集 loss 仍在下降那就是典型过拟合。单类别数据集过拟合的诱因往往是背景太单调缓解手段是调大增强强度YOLOv8 的默认增强参数在小数据集上表现不错不需要额外改动。4.3 PyCharm 里跑训练解释器与工作目录的隐藏坑如果你是在 PyCharm 里启动训练最容易翻车的点不是代码而是终端位置和解释器。PyCharm 打开一个项目后默认的Run面板工作目录是项目根目录但如果你直接在项目终端里执行yolo命令它调用的可能是系统全局 Python而不是当前项目虚拟环境里的 ultralytics。最后表现为 ModuleNotFoundError。解决办法是在 PyCharm 的 Terminal 面板里先激活项目虚拟环境再执行训练命令# 先在 PyCharm Terminal 里确认解释器 which python python -c import ultralytics; print(ultralytics.__version__)输出正常再跑训练命令。另外注意yolo命令的project和name参数会生成runs/acne_baseline目录PyCharm 的文件树里可能不会自动刷新训练结束后手动刷新一下就能看到best.pt和混淆矩阵图。如果你在 Jetson AGX Orin 这类 ARM 设备上跑batch 优先压到 8imgsz降到 640 以下环境依赖需要 arm64 版本的 PyTorch 和 CUDA这一步配错了很难排查。5. 避坑小规模痤疮数据集从解压到训练的五个常见问题这一个章节全是踩坑记录。单类别小数据集的 pipeline 看似短每一段都有可能出现奇怪的失败。我按“现象、原因、解决”的方式整理了五条最高频的案例基本覆盖数据集解压、格式转换、训练启动三个阶段。5.1 7z 解压到一半提示 CRC 校验失败现象7z x执行到 70% 左右弹出CRC Failed个别文件无法解出继续执行后目录里多出大量不完整文件。原因压缩包在下载传输过程中损坏网盘下载最容易出现这类问题解压软件本身没有故障。解决先运行7z t acne_dataset.7z做完整性校验。如果输出行首大面积出现ERROR这个压缩包已经不可信重新下载。重点检查图片文件是否缺失因为图片文件体积大是校验失败的重灾区。用find images -type f | wc -l重新统计图片数量和标题里的 915 对不上就需要找数据发布者拿校验值。5.2 训练启动时报 Dataset not found 或 Image not found现象yolo detect train执行后立刻报错提示Dataset not found或者卡在读取图像时提示某张图片路径不存在。原因YAML 配置里path用的是相对路径但当前工作目录并不在数据集根目录上。另一个原因是路径或文件名里有中文字符、空格YOLO 对这类路径兼容性不好。解决把acne.yaml里的path改成绝对路径数据集整体拷贝到一个纯英文路径下比如/mnt/data/acne_dataset不要放在带中文的桌面路径。改完后先用yolo detect train dataacne.yaml epochs1 devicecpu跑一个单 epoch 冒烟测试确认数据加载环节通过再正式训练。5.3 VOC 转 YOLO 后 TXT 是空文件训练时报 all labels empty现象转换脚本跑完部分 TXT 文件大小是 0 字节训练时输出WARNING: all labels empty in ...或者 loss 曲线从开头就是 nan。原因XML 里的object/name文本和class_map中的键不一致比如大小写不同、类别名带了空格。脚本会把这些目标全部过滤掉写出的 TXT 就是空文件。解决在转换脚本里加一段类别名统计先跑一次把所有 XML 中的name文本打印出来再去对照class_map是否完整匹配。不要用可视化文件查看器去猜类别名直接看原始 XML 最准。改完class_map后重新转换确认空 TXT 占比低于 1%。5.4 mAP 接近 0.9 但小病灶依然漏检严重现象验证集 mAP50 很好看但逐张看预测结果小的痤疮病灶大量没框出来模型更偏爱中等尺寸的痘痘。原因915 张图像里小目标占比高但单类别标签让模型对尺度不敏感。mAP 是聚合指标小目标的漏检被大目标的正确预测稀释了。解决把imgsz从 640 提到 768输入分辨率提高后小目标对应的像素特征更充足。同时打开验证集预测可视化把conf0.1调低让模型多输出一些低置信度框观察是漏检还是定位不准。这一步能帮你判断该收集更多小目标数据还是通过提升分辨率就能解决。5.5 验证集可视化时框画到了图片外现象用yolo predict跑完推理可视化结果里出现大量出去的边界框框的一部分落在图片外。原因VOC 转 YOLO 脚本在归一化时没有对坐标做上下界保护原始 XML 里某些标注边缘超过了图片边界转换后数值大于 1。解决转换脚本里必须加clip操作把归一化后的坐标强制限制在[0, 1]这也是第 3 章那个脚本里写max(0.0, min(1.0, cx))的原因。已经转换完的数据集不用重新挑错直接把所有 TXT 读进来做一次数值范围检查超过范围的统一 clip再重训一次。6. 进阶验证技巧用负样本误检率判断这个数据集值不值得扩训练完成的指标只能说明模型在验证集上表现好不能说明它真的抓到了痤疮的本质特征。我要做的最后一步是负样本测试。找一批不含痤疮但纹理相似的皮肤影像比如毛囊炎、色素痣、皮肤油脂反光数量 50 到 100 张全部不标注直接跑已训练模型。统计误检框数量也就是模型在非痤疮图上产生的预测框。这个误检率比验证集 mAP 更能说明数据集本身的天花板。如果 100 张负样本里误检框超过 30 个说明训练集里缺少干净的背景负样本模型的“痤疮感”是靠背景纹理区分出来的不是靠病灶结构识别出来的。这时候继续调参意义不大更值得做的是收集一批无病灶皮肤图加入训练目录让 YOLO 学会产出置信度为 0 的结果。根据我的经验单类别检测数据集做完这一步才算真正闭环。有时高 mAP 带来的不是安心而是误导。我曾在一个皮肤影像预研项目里被高效指标骗过加上负样本测试后才发现模型会把毛囊角化误检成痤疮。后来补了三百多张负样本重新训练误检率才明显拉低。所以拿到这类数据集时不要急着把训练当作终点多花半小时做一次负面验证你会比盲目调参更接近真实效果。希望这条技巧对你也能派上用场。本文还有配套的精品资源点击获取