新闻详情

新闻详情

首页 / 资讯中心 / 详情

111张皮肤镜图像跑YOLO:小样本皮肤病检测实战与避坑指南

发布时间:2026/10/1 5:02:33来源:尧图网络
111张皮肤镜图像跑YOLO:小样本皮肤病检测实战与避坑指南
简介本资源为面向皮肤疾病检测的YOLO系列目标检测数据集适用于白癜风、皮炎、黄褐斑三类皮肤病变的识别任务可服务于医学图像分析方向的算法工程师、研究生及竞赛选手帮助解决皮肤病灶数据稀缺、标注格式不统一的问题。压缩包共334个文件包含111张jpg图像、111个txt标签、111个xml标签及1个data.yaml配置文件整体约4MB同时提供YOLO格式与VOC格式两套标注便于直接接入训练与验证流程。YOLO格式采用归一化中心点与宽高比例类别索引从0开始兼容yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法。数据集已完成训练集与验证集划分读者可快速搭建皮肤病变检测基线模型用于算法对比、迁移学习或医学辅助诊断研究。目前已有160人学习下载适合需要快速验证模型效果的中小规模实验场景。1. 111 张皮肤镜图像跑 YOLO小样本皮肤病检测到底能不能落地手里只有 111 张带标签的皮肤镜图像要检测白癜风、皮炎、黄褐斑三类皮损第一反应多半是「这点数据训 YOLO 不是开玩笑吗」。我一开始也这么想但真把流程跑通之后发现小样本皮肤疾病数据集的价值不在于刷多高的 mAP而在于验证一条从标注格式转换、预训练权重微调、到推理部署的完整链路能不能走通。这个标题对应的场景非常具体一个 zip 包里装着 111 张图像和对应标签类别是白癜风、皮炎、黄褐斑目标是训练一个能定位并分类皮损的 YOLO 模型。适合谁看手上有一批医学图像、想快速验证 YOLO 可行性的算法工程师以及需要做皮肤疾病辅助筛查原型的开发者。111 张图确实少但少不代表不能做关键在于你怎么用预训练模型、怎么做数据增强、怎么设阈值。下面把我踩过的路一步步拆开讲。2. 先搞清楚 111 张图能喂出什么YOLO 检测皮肤疾病的原理与选型2.1 为什么皮肤疾病检测适合用 YOLO 而不是分类网络皮肤疾病图像的传统做法是分类网络输入一张图输出一个类别。但临床上更关心的是「病灶在哪、有多大、边界在哪」纯分类给不了位置信息。YOLO 作为单阶段检测器一次前向就输出边界框和类别推理速度快适合做成实时辅助工具。白癜风表现为色素脱失斑边界相对清晰皮炎往往成片分布、边界模糊黄褐斑多对称分布在面颊颜色均匀。这三类的形态差异意味着模型需要同时学到颜色特征和纹理特征YOLO 的多尺度特征金字塔刚好能兼顾。另一个现实原因是数据量。111 张图做分类每类平均 37 张分类网络很容易过拟合到背景颜色。而检测任务里每张图可以贡献多个目标框等效样本量比分类大。加上 YOLO 系列有成熟的预训练权重COCO 上训过的 backbone 已经具备通用纹理和边缘提取能力微调时只需要调整检测头小样本下反而比从零训分类网络稳。选型上我一般会从 YOLOv8n 或 YOLOv8s 起步。n 版本参数量小111 张图不容易过拟合s 版本精度略高但需要更多增强。如果显存紧张YOLOv8n 在 640 分辨率下单卡 8GB 就能跑。不要一上来就上 YOLOv8x小数据集上大模型只会更快过拟合验证集 loss 会给你脸色看。2.2 数据集结构拆解111 张图带标签到底长什么样拿到 zip 包后先别急着解压训练第一步是确认目录结构和标签格式。常见的皮肤疾病数据集有两种组织方式一种是 images/ 和 labels/ 平行目录标签是 YOLO txt 格式另一种是 VOC 的 XML 格式。从标题「带标签」来看大概率是已经标注好的边界框。先跑一段脚本统计类别分布和框的数量这决定了后面怎么分训练集。import os import glob from collections import Counter # 假设解压后目录为 skin_dataset/images 和 skin_dataset/labels img_dir skin_dataset/images label_dir skin_dataset/labels img_files glob.glob(os.path.join(img_dir, *.jpg)) glob.glob(os.path.join(img_dir, *.png)) print(f图像总数: {len(img_files)}) class_counter Counter() box_per_img [] for img_path in img_files: base os.path.splitext(os.path.basename(img_path))[0] label_path os.path.join(label_dir, base .txt) if not os.path.exists(label_path): print(f缺失标签: {base}) continue with open(label_path, r) as f: lines [l.strip() for l in f if l.strip()] box_per_img.append(len(lines)) for line in lines: cls_id int(line.split()[0]) class_counter[cls_id] 1 print(类别分布:, dict(class_counter)) print(f平均每图框数: {sum(box_per_img)/len(box_per_img):.2f}) print(f无框图像数: {box_per_img.count(0)})这段脚本做三件事统计图像总数、统计每个类别的框数量、统计每张图的平均框数。参数上class_counter 的 key 是类别 id你需要对照 data.yaml 里的 names 顺序确认 0/1/2 分别对应白癜风、皮炎还是黄褐斑。如果发现某个类别框数少于 20后面增强策略要单独给它加权。无框图像要单独处理不能直接丢进训练集否则 YOLO 会把它当负样本小数据集下负样本比例过高会拉低召回。统计完你会对数据有个底111 张图如果平均每图 1.5 个框总框数约 166 个分到三类每类 50 个左右。这个量级做检测训练集至少留 80 张验证集 20 张测试集 11 张。别做三折交叉验证111 张图折完每折更少指标波动大到没有参考意义。2.3 预训练权重怎么选从 COCO 到皮肤镜的迁移逻辑YOLO 预训练模型下载下来后直接 load 到模型里但要注意两个细节。第一COCO 的 80 类里没有皮肤疾病类别所以检测头的分类分支需要重新初始化backbone 和 neck 可以冻结几轮再解冻。第二皮肤镜图像和 COCO 的自然图像分布差异大颜色偏红、对比度低如果直接全量微调前几轮梯度会很大容易把预训练特征冲掉。我一般会分两阶段前 10 个 epoch 冻结 backbone只训检测头学习率设 0.001后 40 个 epoch 解冻全部学习率降到 0.0001。这样 backbone 的通用特征先适配皮肤镜的色调再整体微调。如果显存够batch size 设 8 或 16不够就设 4 并开梯度累积。YOLOv8 的默认增强里 mosaic 和 mixup 对小数据集很关键但皮肤镜图像做 mixup 要小心两张不同病灶叠在一起可能产生不存在的病理特征我通常把 mixup 关掉只留 mosaic 和随机翻转。3. 从 zip 到可训练标签转换、data.yaml 与训练命令3.1 标签格式转换VOC XML 转 YOLO txt 的脚本与边界处理如果标签是 XML 格式需要转成 YOLO 的归一化 txt。转换的核心是读取 XML 里的 bndbox除以图像宽高得到归一化坐标。这里有个容易翻车的点XML 里的 xmin/ymin/xmax/ymax 是左上角和右下角YOLO 要的是中心点 x,y 和宽高 w,h顺序别搞反。import xml.etree.ElementTree as ET import os from PIL import Image # 类别映射根据实际 data.yaml 调整 class_map {vitiligo: 0, dermatitis: 1, chloasma: 2} def convert_voc_to_yolo(xml_path, img_path, out_path): tree ET.parse(xml_path) root tree.getroot() img Image.open(img_path) w, h img.size lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip().lower() if cls_name not in class_map: continue cls_id class_map[cls_name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪防止标注超出图像 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) # 批量转换 xml_dir skin_dataset/annotations img_dir skin_dataset/images out_dir skin_dataset/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue base os.path.splitext(xml_file)[0] img_path os.path.join(img_dir, base .jpg) if not os.path.exists(img_path): img_path os.path.join(img_dir, base .png) convert_voc_to_yolo(os.path.join(xml_dir, xml_file), img_path, os.path.join(out_dir, base .txt))参数说明class_map 必须和 data.yaml 的 names 顺序一致否则训练时类别全错。边界裁剪那四行是血泪经验有些标注框会超出图像边界不裁剪的话归一化坐标会大于 1YOLO 训练时直接报错。cx/cy/bw/bh 保留 6 位小数足够再多没必要。转换完随机抽 5 张图用可视化脚本画框检查确认框的位置和类别都对。3.2 data.yaml 配置与训练集验证集划分YOLO 训练依赖 data.yaml 指定路径和类别。111 张图我一般按 8:1:1 分训练 88 张、验证 11 张、测试 12 张。划分时要注意类别均衡不能随机分完发现验证集里全是皮炎。写个分层抽样的脚本按主类别分组再抽。import os import random import shutil from collections import defaultdict random.seed(42) img_dir skin_dataset/images label_dir skin_dataset/labels out_base skin_dataset_split # 按每张图的主类别分组 groups defaultdict(list) for img_file in os.listdir(img_dir): base os.path.splitext(img_file)[0] label_path os.path.join(label_dir, base .txt) if not os.path.exists(label_path): continue with open(label_path) as f: lines [l.strip() for l in f if l.strip()] if not lines: continue main_cls lines[0].split()[0] groups[main_cls].append(base) for split in [train, val, test]: os.makedirs(os.path.join(out_base, images, split), exist_okTrue) os.makedirs(os.path.join(out_base, labels, split), exist_okTrue) for cls_id, bases in groups.items(): random.shuffle(bases) n len(bases) n_train int(n * 0.8) n_val int(n * 0.1) splits { train: bases[:n_train], val: bases[n_train:n_train n_val], test: bases[n_train n_val:] } for split, items in splits.items(): for base in items: for ext in [.jpg, .png]: src_img os.path.join(img_dir, base ext) if os.path.exists(src_img): shutil.copy(src_img, os.path.join(out_base, images, split, base ext)) break shutil.copy(os.path.join(label_dir, base .txt), os.path.join(out_base, labels, split, base .txt))划分完写 data.yamlpath: /absolute/path/to/skin_dataset_split train: images/train val: images/val test: images/test names: 0: vitiligo 1: dermatitis 2: chloasma注意 path 要写绝对路径YOLO 对相对路径的解析有时会出玄学问题。names 的顺序和转换脚本里的 class_map 必须完全一致这是最容易翻车的地方类别错位训练出来的模型会把白癜风认成黄褐斑。3.3 训练命令与关键参数111 张图的超参怎么设环境装好 ultralytics 后训练命令本身不复杂关键是参数。111 张图我一般跑 100 个 epochbatch 设 8imgsz 设 640。学习率用默认的 0.01 对微调来说偏大改成 0.001cos 衰减。mosaic 概率保持 1.0但最后 10 个 epoch 关掉 mosaic让模型在真实分布上收敛。yolo detect train \ dataskin_dataset_split/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch8 \ lr00.001 \ lrf0.01 \ cos_lrTrue \ mosaic1.0 \ close_mosaic10 \ mixup0.0 \ degrees15.0 \ translate0.1 \ scale0.3 \ fliplr0.5 \ flipud0.2 \ patience30 \ projectruns/skin \ nameexp1参数逐个说lr0 是初始学习率小数据集必须降close_mosaic10 表示最后 10 轮关闭 mosaic这个对验证指标提升明显mixup0.0 是因为医学图像叠加会产生伪影degrees15 做小角度旋转皮肤镜图像旋转后病理特征不变flipud0.2 允许少量上下翻转但不要设太高黄褐斑的对称分布有方向性。patience30 表示 30 轮验证指标不升就早停111 张图很容易过拟合早停能省时间。训练过程中盯两个指标val/box_loss 和 metrics/mAP50。如果 box_loss 震荡不降多半是学习率太大或标注框有问题如果 mAP50 在 20 轮后就平了说明模型容量不够或增强太弱。111 张图正常能到 mAP50 0.6 到 0.75 之间低于 0.5 要回头查标签。4. 小样本训练的避坑清单从 BN 崩溃到类别错位4.1 现象训练几个 epoch 后 loss 变 NaNBN 层崩溃原因batch size 太小YOLOv8 的 BN 层在 batch4 时统计量不稳定加上皮肤镜图像颜色分布集中梯度爆炸直接把 BN 的 running_mean 冲成 NaN。这是小数据集训练最常见的翻车方式。解决把 batch 提到 8 以上显存不够就开 accumulate2 模拟大 batch。或者在 model 里把 BN 换成 GN但 YOLOv8 改起来麻烦优先调 batch。另外学习率从 0.001 降到 0.0005 也能缓解。4.2 现象验证集 mAP 很高但推理时框全偏了原因验证集和训练集划分时没有按类别分层验证集里恰好都是边界清晰的样本模型没学到模糊边界。或者标签转换时坐标归一化用错了图像尺寸训练时 YOLO 会按自己的 letterbox 缩放如果原始标注的宽高和实际图像不一致框就会系统性偏移。解决重新做分层划分确保验证集覆盖三类和不同边界清晰度。用可视化脚本把预测框画到原图上和标注框对比如果整体偏移一个固定方向就是归一化尺寸问题检查转换脚本里的 w,h 是不是 PIL 读出来的真实尺寸。4.3 现象某一类召回率极低几乎检测不到原因类别不平衡。111 张图里如果黄褐斑只有 15 张模型会偏向多数类。另外 YOLO 默认的类别损失没有加权小类别梯度被淹没。解决在 data.yaml 里加类别权重或者用 copy-paste 增强给小类别造样本。更简单的办法是过采样训练时对小类别图像重复采样。YOLOv8 支持通过设置 fraction 或自定义 sampler但最直接的是把黄褐斑图像复制几份到训练集注意验证集不要复制。4.4 现象训练时提示「No labels found」原因YOLO 找标签的路径逻辑是 images 同级目录下的 labels且文件名必须和图像一一对应。如果 zip 解压后标签在另一个文件夹或者文件名有空格、中文就会找不到。解决统一重命名图像和标签都用英文加数字比如 img_001.jpg 和 img_001.txt。目录结构严格按 images/train 和 labels/train 平行。跑一遍前面 2.2 的统计脚本确认每个图像都有对应标签。4.5 现象推理时同一张图重复检测出多个框原因NMS 的 iou 阈值设太高或者模型对模糊边界输出了多个高置信度框。皮肤疾病病灶边界模糊模型不确定时会在附近撒多个框。解决推理时把 iou 从默认 0.7 降到 0.5conf 从 0.25 提到 0.4。如果还重复检查训练时有没有开 multi_label关掉它。另外黄褐斑成片分布时多个真实目标靠得近NMS 阈值太低会误删需要按类别调这个只能靠验证集试。5. 111 张图之后把 mAP 从 0.6 推到 0.75 的三个技巧第一个技巧是 TTA。推理时对图像做水平翻转和不同尺度缩放把多次预测融合。YOLOv8 的 predict 支持 augmentTrue开启后 mAP 通常能涨 2 到 4 个点。代价是推理速度慢三倍但 111 张图的验证集跑一次也就几秒值得开。yolo detect predict \ modelruns/skin/exp1/weights/best.pt \ sourceskin_dataset_split/images/test \ imgsz640 \ conf0.4 \ iou0.5 \ augmentTrue \ save_txtTrue \ save_confTrueaugmentTrue 开启 TTAsave_txt 把预测结果存成 txt 方便和标注对比算指标。conf 和 iou 按 4.5 的建议设。跑完用脚本算每类的 precision 和 recall如果某一类 recall 特别低单独把那一类的验证图抽出来看预测框。第二个技巧是伪标签。把测试集和未标注的皮肤镜图像用当前模型推理挑置信度高于 0.7 的框作为伪标签加入训练集再训一轮。111 张图扩到 200 张左右mAP 通常能再涨 3 到 5 个点。但伪标签有风险置信度阈值设低了会把错误框学进去我一般设 0.7 以上且只取模型预测和真实标注 IoU 大于 0.5 的。第三个技巧是调整输入分辨率。皮肤镜图像病灶通常占画面比例大640 可能不够。试试 imgsz800 或 1024小目标召回会提升。但 111 张图下高分辨率更容易过拟合需要配合更强的增强。我一般先跑 640 拿到 baseline再跑 800 对比如果验证集 mAP 涨了但测试集没涨说明过拟合退回 640。最后说个习惯每次改完参数把 data.yaml、训练命令、验证集指标记到一个文本文件里。111 张图的实验波动大不记录的话两周后你根本想不起来哪个参数组合跑出了最好的结果。我吃过这个亏重跑了一遍所有实验浪费了一整天。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

马德拉岛旅行全攻略:丰沙尔玩法与levada徒步路线解析 2026/10/1 5:59:44

马德拉岛旅行全攻略:丰沙尔玩法与levada徒步路线解析

提到Madeira这个名字,不同的人会有三种反应:酒徒条件反射地想起那种琥珀色强化葡萄酒,配雪茄和奶酪刚刚好;英国人想到从小在茶点里吃到的黄油蛋糕,也叫Madeira Cake;而真正做过功课的人会告诉你——Madeira…

阅读更多 →
一张RTX 3090从零预训练LLM到领域适配实战指南 2026/10/1 5:59:44

一张RTX 3090从零预训练LLM到领域适配实战指南

想自己从头训一个 LLM,很多人第一反应是"这得多少张 A100 才玩得起"。我一开始也这么想,直到真正把整条链路跑通一遍才发现:个人开发者完全可以用一张 RTX 3090,从零预训练一个小规模语言模型,再通过领域数据…

阅读更多 →
马德拉岛旅行全攻略:徒步路线、马德拉酒与丰沙尔玩法 2026/10/1 5:59:31

马德拉岛旅行全攻略:徒步路线、马德拉酒与丰沙尔玩法

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

阅读更多 →
马德拉岛旅行指南:徒步路线、自驾环岛与四季玩法全解析 2026/10/1 5:59:18

马德拉岛旅行指南:徒步路线、自驾环岛与四季玩法全解析

只在搜资料时看到过“Madeira”这块拼写,绝大多数人都下意识问一句:这不是酒吗?没错,马德拉酒很有名,但Madeira首先是葡萄牙在大西洋中的一片群岛,距离摩洛哥海岸大约600公里,离里斯本飞行约1小…

阅读更多 →
AI Agent产品设计核心决策:从边界定义到架构落地 2026/10/1 5:59:18

AI Agent产品设计核心决策:从边界定义到架构落地

刚入行做 Agent 产品的人,最爱问的一个问题往往是:"现在最火的 Agent 框架是哪个?我该学 LangGraph 还是 AutoGen?" 每次听到这种问题,我都想把人拉回来:你连自己要做的 Agent 解决什么问题、边界…

阅读更多 →
AgentScope 2.0体验:RAG as Service如何重塑多智能体协作 2026/10/1 5:59:11

AgentScope 2.0体验:RAG as Service如何重塑多智能体协作

前阵子刷到AgentScope更新的消息,起初我没太当回事。毕竟多智能体框架这两年冒出来不少,个个都说自己编排能力强、扩展性好,真上手才知道怎么回事。但把AgentScope 2.0完整跑通一遍之后,我改变了判断,这确实是我目前愿…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉