news 2026/9/27 21:03:26

痤疮图像识别YOLOv8训练全流程:数据集规范、标注校验与调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
痤疮图像识别YOLOv8训练全流程:数据集规范、标注校验与调优

简介:面向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.0833

x_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 ultralytics

CPU 包体积小且不存在 CUDA 版本不匹配的问题,我第一次就在没有 GPU 的服务器上用这个组合完成数据格式校验和单张图片推理。注意--index-url参数指定了 CPU 版源,不要省略,否则默认装的是带 CUDA 的版本,运行时会报 CUDA 不可用。

如果你手里是 GTX1660Ti 这类 6GB 显存的卡,训练这套痤疮数据集可以跑batch=8配合imgsz=640,显存占用在 4GB 到 5GB 之间。再往上提分辨率就要注意显存警告。装 GPU 版时先确认驱动支持 CUDA,然后装torch对应版本,不再赘述。

3.2 目录结构组织与标签可视化校验

YOLOv8 对数据集目录结构有约定,最省事的方式是直接把数据集整理成以下形态。这个结构也是官方文档里推荐的 images/labels 并列布局:

datasets/acne/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml
cd ~/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"), f"acne/images/{split}/{stem}.jpg") copy2(os.path.join(LABEL_DIR, stem + ".txt"), f"acne/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: nodule

nc: 4和names列表必须与标注 txt 里的 class id 一一对应,顺序绝对不能乱,这一点怎么强调都不为过。YOLOv8 会优先使用names判断类别数,如果你的数据集在转换时 id 映射已经错位,nc写再多也是错的。

训练命令我按 GTX1660Ti 显存配置给一套可直接跑的参数组合:

yolo train \ data=/home/yourname/datasets/acne/data.yaml \ model=yolov8n.pt \ epochs=300 \ batch=8 \ imgsz=640 \ patience=40 \ lr0=0.01 \ device=0

这套参数配合 2000 张以上的训练图,通常 200 个 epoch 内能收敛。如果数据集规模小到 500 张以下,建议把epochs提高到 600 并开启更强的数据增强,或者直接换用预训练权重yolov8n.pt做迁移学习,只在最后几层微调。patience=40表示连续 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'], label='train box_loss') plt.plot(df['val/box_loss'], label='val box_loss') plt.legend(); plt.savefig('loss_curve.png', dpi=200) "

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,显存不够时保持batch=6;第二,关闭对这张数据集副作用明显的部分增强策略,尤其是mosaic=0.5会让小目标被切割后更难以学习。改用针对性做法:在原图上切片训练,把一张 1920 宽的大图切成 4 个 640 的 patch 作为独立样本,小目标在 patch 中的像素占比立刻翻 4 倍。这种做法能在不改模型结构的情况下显著提升小目标召回。

5. 从 best.pt 回到标注:验证指标驱动的修正闭环

训练收敛后,直接看mAP50一个数是不充分的。我的验证习惯是先用官方验证命令拿到分项指标,再做一轮人工复查,最后把问题反馈回标注规范,形成闭环。

第一步跑测试集验证,命令如下:

yolo val model=runs/detect/train/weights/best.pt \ data=/home/yourname/datasets/acne/data.yaml \ batch=1 \ imgsz=800

验证完马上打开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, conf=0.25, imgsz=800) 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。因为翻车后排查的时间总是训练本身的好几倍。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 21:03:15

TSM6.3+Mhvtl+Oracle/DB2 Linux备份恢复实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 21:02:04

5G铁路通信组网实践:从关键技术选型到现场验收避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 20:57:00

Android中SQLite使用:从建库到TaoToken配置的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 20:54:01

Grix 移动端与桌面端 DeepSeek Harness 配置 TaoToken 统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/27 20:52:30

DeepSeek量化回测全流程:因子评估、LoRA微调与部署优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华