痤疮图像识别YOLOv8训练全流程:数据集规范、标注校验与调优
发布时间:2026/9/27 21:03:29来源:尧图网络
简介面向YOLOv8目标检测任务构建的痤疮图像识别数据集适用于医学图像分析、皮肤病变检测等教学与科研场景。资源共2000个文件体积仅39.37MB其中738张jpg痤疮图像与927个txt标注文件一一对应另有yaml配置、pt预训练权重、Python脚本及说明文档可支撑从数据准备到模型评估的完整流程。已有75人学习下载。标注遵循YOLOv8格式每个目标包含定位框与整数类别标识ignore.txt可用于排除特定样本训练时可灵活控制数据规模。借助数据增强与标准化处理用户可复现痤疮检测模型的训练与调优并进一步集成至医疗辅助诊断系统提升面部痤疮识别的效率与准确度。1. 痤疮图像识别这件事卡脖子的往往不是模型而是数据集和标注规范做痤疮图像识别很多人第一反应是换个更强的YOLOv8框架或者去调训练参数结果训练一跑起来就翻车loss曲线乱跳、mAP常年停在零点几、验证集里的小痤疮一个都检不出来。我拆过几份医疗影像类数据集后有一个很直接的感受——这类项目能不能落地八成取决于数据集本身的类别定义是否清晰、标注规范是否严格、标注文件和YOLOv8要求的格式是否对齐模型结构反而是最不用操心的部分。这份痤疮图像识别数据集正好是一套可以直接拿去训练的资源图像已经按检测任务整理好同时附带一套标注规范文档用来约束边界框怎么打、类别怎么分、哪些图要剔除。适合正在做皮肤图像识别研究、准备用YOLOv8训练自己的数据集以及需要一套可复现标注流程的从业者。2. 数据集拆解类别体系、标注规范与文件组织拿到一份数据集我的习惯是先看它的类别和标注文档而不是直接解压图片跑训练。因为YOLO格式标注一旦和你自己的类别顺序对不上训练时所有损失都能正常计算但模型学到的完全是错误映射这种问题最隐蔽。2.1 痤疮类别怎么定义从临床可见特征到检测类别这份数据集的类别体系不是随便拍的它参考了皮肤影像分析里常用的病变形态划分把痤疮相关区域分成四类可检测目标。目标检测场景里我们不写诊断结论只按肉眼可见的形态特征区分这样标注员不需要医学背景也能稳定执行。类别id病变类型常见可见特征标注原则0粉刺黑头/白头毛发开口处颗粒状凸起黑头中心有暗色点只框明显凸起区域不框泛红皮肤1丘疹直径较小、红色实性凸起边界清晰框整体凸起范围包括发红边缘2脓疱顶部有白色或黄色脓点基底泛红框整个脓疱区域中心脓点必须框入3结节/囊肿较大且硬凸起明显可能肤色发暗一个框中只包含一个独立结节粘连区域建议剔除这套类别映射和标注原则在数据集里以标注规范文档的形式给出训练前你需要确认自己转换脚本中的 class id 与这份表格完全一致。常见错误是有人把“粉刺”排在第一个就把 id 记为 0但实际数据集的 txt 里 id 排列可能完全不同所以要先抽一个 txt 文件核对。每个目标的边界框宽度在整张图占比通常在 1% 到 8% 之间属于典型的小目标检测场景。这个先记住后面选 imgsz 和评估指标时都会用到。2.2 标注文件字段解读class、归一化坐标与边界框约定数据集里的标注全部采用 YOLO 格式每个图片对应一个同名.txt文件每行对应一个目标框共五个字段class_id x_center y_center box_width box_height前面类别 id 对应 2.1 的表格后面四个值全部是归一化浮点数范围在 0 到 1 之间。归一化坐标不是绝对像素用 0.5 表示中心点在图像正中间用 0.1 的宽度表示这个框的宽度占整张图宽度的 10%。举个例子如果一张 1920×1080 的图片里某个丘疹区域的绝对中心点在 (960, 540)宽 120 像素高 90 像素那 txt 里这一行应该写成1 0.5000 0.5000 0.0625 0.0833x_center 和 y_center 是归一化后的中心点坐标不是左上角坐标这跟 COCO 数据集用的 xywh 格式最明显的差异就是中心点坐标。标注规范里还明确了三个边界规则框与病变区域边缘贴合不超过 3 个像素一个框内混入多个独立病变的直接删除该标注不硬圈图像边缘被截断且截断面积超过 20% 的目标不标注。这些规则直接影响最终训练样本质量拿到数据集后最好抽十张原始大图与标注叠层对比一下。2.3 图像采集规格与筛选标准分辨率、光照与去重会额外看一下数据集的采集筛选条件这决定了模型在实际场景里的泛化能力。这套资源的图像以面部可见光照片为主覆盖不同肤色、室内外光照和拍摄角度原始图片分辨率普遍在 1080p 以上这是为下采样到 640 或 800 做训练留出余量的。原始数据经过一轮筛选剔除对象包括严重虚焦的人脸照片、多次压缩产生明显马赛克或条带的图片、带第三方水印覆盖病变区域的图片以及重复帧。这里特别提醒一点如果这套数据集是通过视频抽帧得到的里面很可能存在连续帧近似重复的画面训练前最好按感知哈希去重否则验证集里会出现来自同一段视频的图片mAP 虚高得厉害。关于匿名化标注规范里也做了要求图像只保留面部皮肤区域眼部和可识别身份的特征基本被裁掉所有文件名用编号代替不包含任何个人信息。这也是后面你在自己设备上继续扩展数据时要遵守的习惯。3. 把数据集喂给 YOLOv8环境搭建、目录组织与训练参数数据集本身整理得再干净交付到 YOLOv8 手里仍然需要过一遍组织流程。这一部分我按自己处理数据集的经验把从环境准备、目录搭建到跑通训练命令的全流程列出来每一步都是可抄作业的。3.1 环境准备与版本组合CPU 能跑通GTX1660Ti 能训练在 Ubuntu 20.04 上搭建 YOLOv8 环境CPU 版本完全够用来做数据处理和模型推理验证。我这里给出的是一种最稳的组合Python 3.10 以上版本官方维护的 PyTorch CPU 包配合 ultralytics 库。# 创建独立虚拟环境避免污染系统 Python python3 -m venv yolo_env source yolo_env/bin/activate # 安装 CPU 版 PyTorch不带 CUDA 加速 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 YOLOv8 官方库 pip install ultralyticsCPU 包体积小且不存在 CUDA 版本不匹配的问题我第一次就在没有 GPU 的服务器上用这个组合完成数据格式校验和单张图片推理。注意--index-url参数指定了 CPU 版源不要省略否则默认装的是带 CUDA 的版本运行时会报 CUDA 不可用。如果你手里是 GTX1660Ti 这类 6GB 显存的卡训练这套痤疮数据集可以跑batch8配合imgsz640显存占用在 4GB 到 5GB 之间。再往上提分辨率就要注意显存警告。装 GPU 版时先确认驱动支持 CUDA然后装torch对应版本不再赘述。3.2 目录结构组织与标签可视化校验YOLOv8 对数据集目录结构有约定最省事的方式是直接把数据集整理成以下形态。这个结构也是官方文档里推荐的 images/labels 并列布局datasets/acne/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamlcd ~/datasets mkdir -p acne/{images/{train,val,test},labels/{train,val,test}} # 按 8:1:1 的划分把原始图片和同名 txt 分别拷贝到对应目录 # 注意labels 目录里每张图只允许存在一个同名 txt多一个都不行这里有个最容易被忽略的规则images/train里的0001.jpg必须要在labels/train里找到对应0001.txt查了 txt 但没查到图片、或者有多个同名文件训练阶段都会直接报无标签错误。组织完目录后我一般会先做一次可视化校验把一张大图和它的 txt 标注画在一起看看。用 OpenCV 读标注不太方便读回浮点坐标再转回像素这里给个简单的脚本思路遍历labels/val下任意一个 txt把五字段解析后乘回图像宽高用cv2.rectangle画框人眼确认框是否紧贴病变区域。这一步 5 分钟能完成能尽早发现坐标归一化方向是否搞反了。3.3 训练集/验证集/测试集划分脚本固定种子统计类别分布这一步我直接写脚本完成划分而不是手动拖文件。划分有两个硬性要求按文件名对齐移动固定随机种子保证每次划分结果一致。下面这个脚本同时做了类别分布统计划分后可确认四个类别的框数量没有某个类别只有几十个框的极端情况。import os import random from collections import Counter from shutil import copy2 IMAGE_DIR acne/images LABEL_DIR acne/labels TRAIN_RATIO, VAL_RATIO 0.8, 0.1 SPLIT_SEED 42 random.seed(SPLIT_SEED) file_stems [] for img_name in os.listdir(IMAGE_DIR): stem os.path.splitext(img_name)[0] if os.path.exists(os.path.join(LABEL_DIR, stem .txt)): file_stems.append(stem) random.shuffle(file_stems) n_train int(len(file_stems) * TRAIN_RATIO) n_val int(len(file_stems) * VAL_RATIO) splits { train: file_stems[:n_train], val: file_stems[n_train:n_train n_val], test: file_stems[n_train n_val:] } for split, stems in splits.items(): for stem in stems: copy2(os.path.join(IMAGE_DIR, stem .jpg), facne/images/{split}/{stem}.jpg) copy2(os.path.join(LABEL_DIR, stem .txt), facne/labels/{split}/{stem}.txt) print(f{split}: {len(stems)} samples) # 类别分布统计确认每类框数量不会过于失衡 class_counter Counter() for split in [train, val]: for stem in splits[split]: with open(os.path.join(LABEL_DIR, stem .txt)) as f: for line in f: cls_id int(line.strip().split()[0]) class_counter[cls_id] 1 print(class_counter)脚本里的SPLIT_SEED是随机种子固定为 42 可以保证同样的数据每次划分结果完全一致方便复现实验。n_train和n_val用比例计算余下的样本全部进测试集。类别统计这一步非常关键如果某个类别只占全部标注框的 3% 以下后续训练时可以考虑单独对那张图做过采样而不是直接开训。3.4 data.yaml 与训练命令关键参数含义数据目录就绪后写data.yaml让 YOLOv8 能定位到图片和标注。文件路径建议写绝对路径在服务器上折腾相对路径经常因为当前工作目录不对而找不到数据浪费时间。# data.yaml path: /home/yourname/datasets/acne train: images/train val: images/val test: images/test nc: 4 names: 0: comedone 1: papule 2: pustule 3: nodulenc: 4和names列表必须与标注 txt 里的 class id 一一对应顺序绝对不能乱这一点怎么强调都不为过。YOLOv8 会优先使用names判断类别数如果你的数据集在转换时 id 映射已经错位nc写再多也是错的。训练命令我按 GTX1660Ti 显存配置给一套可直接跑的参数组合yolo train \ data/home/yourname/datasets/acne/data.yaml \ modelyolov8n.pt \ epochs300 \ batch8 \ imgsz640 \ patience40 \ lr00.01 \ device0这套参数配合 2000 张以上的训练图通常 200 个 epoch 内能收敛。如果数据集规模小到 500 张以下建议把epochs提高到 600 并开启更强的数据增强或者直接换用预训练权重yolov8n.pt做迁移学习只在最后几层微调。patience40表示连续 40 轮验证损失不下降就提前停止实际训练中能省掉大量无效时间。训练完成后YOLOv8 会在runs/detect/train/目录下输出results.csv和每一轮的验证指标用这段脚本可以直接画损失函数曲线图方便观察是否存在过拟合python -c import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.plot(df[train/box_loss], labeltrain box_loss) plt.plot(df[val/box_loss], labelval box_loss) plt.legend(); plt.savefig(loss_curve.png, dpi200) 4. YOLOv8 训练痤疮数据的四个常见问题现象、原因与排查训练这类皮肤影像数据我发现翻车点高度集中在那几处而且每个问题都有一套固定排查路径。下面是四条踩坑记录按实际排查顺序排好。4.1 loss 曲线不降反升学习率与异常图双重排查现象训练到第 10 个 epoch 时loss 曲线不再下降验证集上的 box_loss 反而一路走高最终训练出来的模型在测试图上面框全部漂移。原因学习率lr0对这套小规模数据偏大加上少数包含过曝区域的图像在增强后被放大成极端样本产生了异常梯度直接把权重拉到了坏点。解决把lr0降到 0.001并给优化器增加两轮 warmup。同时遍历一遍训练集用 Python 统计每张图的像素均值与方差把全黑、过曝这类异常图直接剔除或归入忽略列表。第二次训练时 loss 在前 20 个 epoch 稳步下降最终 mAP50 回到正常水平。4.2 All labels empty 报错文件名与扩展名的隐藏错位现象训练命令一启动就提示All labels empty in /.../labels/train但目录里明明有几百个 txt 文件。原因目录结构没问题但图片文件名是.jpeg后缀而 txt 是对应的.jpg名另外有部分图片是直接从微信原图导出的文件名里带了(1)这类括号字符YOLOv8 读路径时直接解析失败。解决先做一轮文件名规范化统一改成小写.jpg再用脚本把images与labels两侧的文件名做差集比对输出所有不匹配项。写一个简单的find命令确认文件实际后缀find . -name *.jpeg -o -name *.jpg | head -20从那以后每次处理新数据集我都先把文件名批量重命名这步跑完再往下走。4.3 训练能跑但 mAP 接近零class id 映射错位现象训练全程无报错loss 也在正常下降但验证集的混淆矩阵中所有类别都跑到背景类上mAP50 只有 0.05 左右。原因标注阶段用的是 Labelme导出时工具按类别名首字母排序自动分配 id而我写转换脚本时按自己习惯的顺序枚举两边的 id 映射完全错位。比如comedone在 Labelme 里被分配为 id 0但转换脚本里comedone对应 id 2于是一个粉刺框被强行包装成结节框训练。解决转换脚本里不要手写列表顺序而是直接读取数据集的 classes 定义文件构建字符串到 id 的映射字典。训练前打印每个 class id 的框数量分布眼见为实。检查一行标注是否合法也能用类似脚本快速扫描awk { if ($1 0 || $1 3) print invalid class:, $0 } $(find . -name *.txt)4.4 小目标痤疮漏检分辨率与增强策略的取舍现象验证集照片上大的结节都能正确框出但散在的小丘疹和粉刺几乎全部漏检召回率明显偏低。原因痤疮病变在 1920×1080 原图上平均只有几十个像素宽缩放到 640 训练分辨率后目标面积直接减半小目标信息大部分丢失。解决两件事同时做。第一把imgsz从 640 提到 800显存不够时保持batch6第二关闭对这张数据集副作用明显的部分增强策略尤其是mosaic0.5会让小目标被切割后更难以学习。改用针对性做法在原图上切片训练把一张 1920 宽的大图切成 4 个 640 的 patch 作为独立样本小目标在 patch 中的像素占比立刻翻 4 倍。这种做法能在不改模型结构的情况下显著提升小目标召回。5. 从 best.pt 回到标注验证指标驱动的修正闭环训练收敛后直接看mAP50一个数是不充分的。我的验证习惯是先用官方验证命令拿到分项指标再做一轮人工复查最后把问题反馈回标注规范形成闭环。第一步跑测试集验证命令如下yolo val modelruns/detect/train/weights/best.pt \ data/home/yourname/datasets/acne/data.yaml \ batch1 \ imgsz800验证完马上打开runs/detect/val/confusion_matrix.png和results.csv。混淆矩阵里如果某一类对角线数值明显低于其他类说明该类标注数量不足或标注边界不一致。results.csv里按目标尺寸分组的指标小/中/大目标 AP能直接指出问题到底出在小目标还是大目标上。第二步做可视化复查我用这段脚本批量输出低于置信度阈值的预测结果人工挑出漏检和误检样本from ultralytics import YOLO import glob model YOLO(runs/detect/train/weights/best.pt) for img_path in glob.glob(datasets/acne/images/test/*.jpg)[:50]: results model.predict(img_path, conf0.25, imgsz800) detections results[0].boxes if detections is not None and len(detections) 0: # 打印置信度处于 0.25~0.45 之间的“边缘预测” for i, conf in enumerate(detections.conf): if 0.25 conf 0.45: print(f{img_path} class{int(detections.cls[i])} conf{conf:.2f})这些低置信度预测往往对应标注规范里没有覆盖的“灰色地带”比如被头发遮挡一半的丘疹、光照极暗处的粉刺。我会把这类样本单独收集到 hard_examples 目录要么补标注进数据集要么在规范文档里明确“遮挡超过 50% 不标”避免标注员再产生分歧。从那以后我每次拿到新数据集第一件事不是开训练而是先跑一遍格式校验脚本确认 class id 映射和文件对齐都没问题再开始跑 baseline。因为翻车后排查的时间总是训练本身的好几倍。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网