十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLO皮肤疾病检测实战:111张小样本训练与标签优化指南

YOLO皮肤疾病检测实战:111张小样本训练与标签优化指南 简介面向yolo系列算法目标检测训练需求提供一份皮肤疾病图像数据集涵盖白癜风、皮炎、黄褐斑三类病灶。数据集中111张jpg原图均配有对应标注既包含yolo格式txt文件也包含VOC格式xml文件标签坐标已归一化可直接用于yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流版本。资源已划分训练集与验证集并附带data.yaml配置方便快速开展模型训练与测试。压缩包共334个文件以图片、两类标签文件及配置文件为主整体大小仅4MB结构简洁、易于搬运。已有158人学习下载适合正在做皮肤影像识别、医疗目标检测项目或希望快速上手yolo流程的开发者使用。可帮助省去数据采集与格式转换环节直接聚焦模型调参与效果验证。1. 111张图像加YOLO标签的数据集先别急着训练标签质量决定上限yolo算法-皮肤疾病数据集-111张图像带标签-白癜风-皮炎-黄褐斑.zip这个压缩包名称已经点出了训练链路的三个关键YOLO算法、皮肤疾病图像、111张带标签样本。111张不算大但也没到不能训练的程度真正决定模型上限的是标签文件里每行坐标准不准、类别ID对不对齐、框有没有盖住病变区。解压后我建议先在代码层把标签读一遍再谈配置和调参。这篇文章从数据集本身讲起依次说清YOLO标签格式怎么核对、dataset.yaml怎么配、小样本训练参数怎么设以及迁移学习和两阶段微调这两件最值得做的事。适合刚接触目标检测的开发者也想给做过检测但没碰过医疗图像的工程师一点参考。2. 数据集预检YOLO标签结构、类别分布与框尺寸统计2.1 解压后先确认目录结构和文件对齐关系拿到zip包第一件事不是写训练脚本而是把压缩包完整解压用find命令看一遍目录。常见打包方式是 images 和 labels 两个平行目录同一张图像的标签通过同名不同后缀对应例如 IMG_001.jpg 对应 IMG_001.txt。如果发现 labels 目录里既有txt又有xml说明数据格式混过需要先统一。unzip yolo算法-皮肤疾病数据集-111张图像带标签-白癜风-皮炎-黄褐斑.zip -d skin-dataset cd skin-dataset find . -maxdepth 2 -type f | sort | head -40 find . -name *.txt | wc -l find . \( -name *.jpg -o -name *.png \) | wc -l第一条命令把压缩包解压到 skin-dataset 目录第二条按文件名排序展示前40个文件用来快速确认目录层级。第三条和第四条分别统计标签和图像数量两边应当一致。如果txt数量比图像少优先排查空标签文件如果多多半是存在重复或无效标注。我见过数据集把标签文件名多写一个空格或者把jpg写成jpeg导致训练时出现image not found。检查对齐关系最直接的办法是遍历图像扩展名换成.txt后判断文件是否存在。from pathlib import Path img_dir Path(images) label_dir Path(labels) missing [] for img_path in sorted(img_dir.iterdir()): if img_path.suffix.lower() not in (.jpg, .jpeg, .png): continue label_path label_dir / (img_path.stem .txt) if not label_path.exists(): missing.append(img_path.name) print(f缺失标签的图像: {len(missing)}) for name in missing[:20]: print(name)这段脚本遍历images目录按相同主文件名去labels目录找同名txt找不到就记录下来。111张图像的规模下缺失数应为0如果超过5%建议先找数据提供方确认不要直接删图硬训。2.2 统计三个类别的目标框数量与框尺寸分布文件对齐之后看每一行标签的内容分布。YOLO检测格式一行是“类别ID 中心点x 中心点y 框宽 框高”五个字段全部是相对图像宽高的归一化坐标取值在0到1之间。注意不是xmin ymin xmax ymax第一次接触YOLO格式的人很容易在这里踩坑。from collections import Counter from pathlib import Path label_dir Path(labels) cls_counter Counter() w_list [] h_list [] invalid_lines [] for label_file in sorted(label_dir.glob(*.txt)): for line in label_file.read_text(encodingutf-8).strip().splitlines(): parts line.split() if len(parts) ! 5: invalid_lines.append((label_file.name, line)) continue try: cls_id, x_c, y_c, w, h map(float, parts) except ValueError: invalid_lines.append((label_file.name, line)) continue cls_counter[int(cls_id)] 1 w_list.append(w) h_list.append(h) print(类别ID - 目标框个数) for cls_id in sorted(cls_counter): print(f {cls_id}: {cls_counter[cls_id]}) print(f无效行: {len(invalid_lines)}) for fname, line in invalid_lines[:10]: print(f {fname}: {line})这段代码统计每个类别ID对应的框总数同时把字段数量不等于5或数值转换失败的行挑出来。框总数除以图像数可以算平均每图框数低于1说明大量图像没正样本模型容易整体偏向背景高于3说明一张图里有多个离散病变区域模型需要学会区分同一病灶上的多个框。框尺寸统计同样关键。把w_list和h_list的中位数算出来如果中位数小于0.1说明大多数目标框只占图像面积1%左右属于典型小目标问题。皮肤病检测里白癜风白斑和黄褐斑区域经常只占皮肤表面一小块目标框小意味着YOLO深层特征图上的响应很弱后面训练时要么配合更大的imgsz要么用patch推理。2.3 归一化坐标转像素框的可视化验证当标签里出现大于1的数值不要直接认定数据坏了。有一种常见情况是数据从LabelImg导出的XML或JSON转成YOLO txt时转换脚本没有归一化保留了绝对像素坐标。判断方法很简单取一行坐标乘以图像真实宽高。from PIL import Image from pathlib import Path img_path Path(images) / demo.jpg label_path Path(labels) / demo.txt w_img, h_img Image.open(img_path).size lines label_path.read_text().strip().splitlines() cls_id, x_c, y_c, w, h map(float, lines[0].split()) abs_xmin int((x_c - w / 2) * w_img) abs_ymin int((y_c - h / 2) * h_img) abs_w int(w * w_img) abs_h int(h * h_img) print(f图像尺寸: {w_img} x {h_img}) print(f像素框: [{abs_xmin}, {abs_ymin}, {abs_w}, {abs_h}])将结果换算成像素坐标后必须对照原图判断框是否覆盖病变区域。这一步建议抽样5到10张图人工过目不要只信数值。用OpenCV画框最直接import cv2 img cv2.imread(str(img_path)) h_img, w_img img.shape[:2] cls_id, x_c, y_c, w, h map(float, lines[0].split()) x1 int((x_c - w / 2) * w_img) y1 int((y_c - h / 2) * h_img) x2 int((x_c w / 2) * w_img) y2 int((y_c h / 2) * h_img) color (0, 0, 255) # BGR cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(check_demo.jpg, img)画框后重点看两点框是否把病变边缘完全包住以及框内是否混入大量正常皮肤。黄褐斑这类边界模糊的病种标注员很容易把框画大一圈模型训练时学到“框内包含外围正常皮肤”的错误模式推理时也会把框扩大。2.4 标签质量常见问题与处理对照表问题类型现象处理方式坐标未归一化标签行出现大于1的数值确认转换工具写脚本统一转成归一化坐标类别ID错位标签出现类别3但数据集只有0、1、2检查标注工具的class顺序修正ID映射空标签文件txt存在但0字节对照图像确认是否确实无病变再决定保留或删除框画太大病变边缘只占框内面积一半用可视化脚本逐张检查重新标注受影响图像这一章做的预检不会花费太多时间但能省掉后面训练时排查数据问题的半天时间。111张图像的数据集经不起标注错误带来的噪声消耗标签干净比模型选型更重要。3. 用YOLO在111张图像上训练目录重排、dataset.yaml与关键超参3.1 把数据集整理成YOLO要求的目录布局YOLOv5和YOLOv8对目录要求差异不大本质是images和labels平级再按train和val分开。我这里采用ultralytics推荐的写法在 skin-dataset 下建 images/train、images/val、labels/train、labels/val 四个子目录。cd skin-dataset mkdir -p images/train images/val labels/train labels/val原包如果本来就是images和labels两个平级目录不要直接拿它们训练需要先划一部分图像做验证集同时迁走对应标签。划分原则是同一张图像和它的txt必须同时进入train或val如果数据里同一个患者有多张照片要按患者维度分组不能把同一个人的不同照片同时分到train和val。皮肤图像通常来源于病历系统同一个人不同角度、不同光照下纹理差异很大模型很容易靠记忆患者身份而不是病变特征来识别这个问题在医学图像里被称作身份泄露。划分脚本我用固定随机种子保证复现import random from pathlib import Path import shutil random.seed(42) img_files list(Path(images).glob(*.jpg)) list(Path(images).glob(*.png)) val_ratio 0.2 random.shuffle(img_files) val_count int(len(img_files) * val_ratio) for i, img_path in enumerate(img_files): label_path Path(labels) / (img_path.stem .txt) dest_img_dir Path(images/val if i val_count else images/train) dest_label_dir Path(labels/val if i val_count else labels/train) shutil.copy2(img_path, dest_img_dir / img_path.name) if label_path.exists(): shutil.copy2(label_path, dest_label_dir / label_path.name)如果数据已经自带了官方划分就不要重复切分直接用官方给的目录。随机划分时校验一下每类在train和val里都有样本避免出现验证集只有两类、另一类全在训练集里的情况。3.2 配置dataset.yaml类别名与ID映射要按标签统计结果写YOLO数据集入口是一个data配置文件。在 skin-dataset 目录下新建 skin.yaml# skin-dataset/skin.yaml path: /data/skin-dataset train: images/train val: images/val names: 0: vitiligo # 白癜风 1: dermatitis # 皮炎 2: melasma # 黄褐斑这里最容易犯的错是names索引和标签文件对不上。写yaml之前先回看第二章统计输出的类别ID分布确认实际存在的ID是0、1、2。如果统计数据里只有0和2没有1说明缺少类别1的训练样本或者标注ID写错了。names里的名称不影响训练本身只影响推理结果里打印的类名但不建议写错后面分析混淆矩阵时会误导判断。path字段建议写成绝对路径。yaml里用相对路径时执行yolo命令的工作目录一变路径解析就不同排查起来很烦。改成 /data/skin-dataset 后不管在哪个目录执行命令最终指向的都是同一份数据集。3.3 第一次训练命令yolov8n加小数据集参数列表数据集规模小模型规模就不要选大。YOLOv8的n、s、m、l、x五个规格里111张图像最合适的是yolov8n参数最少、速度最快、最不容易过拟合。m以上模型在小数据上几乎必然过拟合而且训练时间成倍增长。yolo detect train \ data /data/skin-dataset/skin.yaml \ model yolov8n.pt \ imgsz 640 \ epochs 100 \ batch 8 \ patience 15 \ seed 0 \ cacheTrue \ optimizerAdamW \ lr0 0.001 \ mosaic 0.5 \ degrees 5 \ scale 0.3 \ hsv_h 0.0imgsz 640是目标检测的通用默认值。如果原始图像分辨率高且病变区域很小白斑或黄褐斑区域在图像里只占几十个像素640下网络几乎学不到细节这时候可以等第一轮跑完再考虑放大imgsz。epochs设100配合patience 15实现早停。111张图像去掉验证集后训练样本不足90张通常前20个epoch就能在验证集上到达平台期后面继续训练只会加重过拟合。batch在小显存卡上设8比较稳妥。显存充足时提到16训练更稳但不要为了加速把batch拉到32训练集样本量太少一个epoch只更新两三次参数损失曲线会来回震荡。optimizer选择AdamW配合较低初始学习率对检测头收敛比较友好尤其是使用预训练权重做迁移学习时。几个需要单独说明的增强参数mosaic 0.5表示一半概率使用马赛克增强把4张图拼成一张。这是小目标检测的常用增强但医学场景有一个副作用把4个不同病人的皮肤拼在一起模型可能学到“拼接缝附近出现伪边缘”的特征所以从默认的1.0降到0.5更合适。degrees 5把随机旋转限制在正负5度。皮肤病变没有方向性旋转不会改变病变性质但过大的旋转会产生大量背景填充迫使模型去学旋转后的边框变换对小数据集不划算。hsv_h 0.0表示关闭色调扰动。皮肤颜色是三种疾病最重要的区分特征之一色调扰动会拉近不同病变的颜色分布损害判别力。饱和度扰动hsv_s可以保留一个较小的值我习惯给到0.2左右模拟不同光照条件。超参数推荐值说明imgsz640病变目标小于30像素时再考虑960或1280batch8-16显存允许优先16epochs100配patience早停防止过拟合patience15验证集指标连续15轮不提升就停止mosaic0.5不要用默认1.0避免拼接伪影degrees5小角度旋转足够不做过大几何扰动hsv_h0.0保留皮肤颜色特征lr00.001迁移学习下较低学习率更稳3.4 训练输出先看哪张图训练结束后runs/detect/train/ 下会生成 weights/best.pt、weights/last.pt、results.csv、confusion_matrix.png、results.png 等文件。先不要急着看mAP打开 results.png 看训练损失和验证损失两条曲线。如果训练损失持续下降、验证损失在第20个epoch后反弹说明过拟合下一步调整方向是增强策略而不是增加epoch。如果两条曲线都在高位降不下去要么学习率偏大要么标签存在系统性错误回到第二章重新检查。用第一轮baseline的目的不是拿一个可用模型而是验证整个数据链路是通的标签格式没问题、目录路径没错、训练能正常收敛。在这个基础上再去谈针对皮肤小数据集的增强和微调。4. 评估皮肤病目标检测模型mAP曲线、混淆矩阵与预测可视化4.1 用验证集执行完整评估命令训练完成后对验证集执行评估yolo detect val \ model /data/skin-dataset/runs/detect/train/weights/best.pt \ data /data/skin-dataset/skin.yaml \ imgsz 640 \ conf 0.001 \ iou 0.6 \ save_jsonTrueconf设置为0.001是为了让模型尽可能输出所有候选框给全精度召回空间。如果直接用默认的0.25大量低置信度真阳框会被过滤后续分析会漏掉重要信息。iou 0.6是NMS阈值重叠度高于0.6的检测框会被当作重复预测抑制训练和评估保持相同的iou口径结果才有可比性。评估结束后查看 results.csv重点关注每类别的 mAP50 和 mAP50-95。验证集只有20张左右图像每类比指标波动会比较大所以要把评估结果和混淆矩阵放在一起看。4.2 混淆矩阵里的典型皮肤误判模式训练目录下的 confusion_matrix.png 会显示每类相互混淆情况。皮肤检测场景里最典型的误判模式有两种一类是白斑病被预测成皮炎另一类是皮炎被预测成背景。前者常见于边界不清晰、部分白斑区域已经出现色素沉着的病变框内特征同时包含两种病变纹理后者常见于光照不均匀或图像模糊的情况模型没有捕获到皮炎区域的纹理差异。出现这两种模式时不要急着调模型先回到标签层面找原因。黄褐斑常分布在脸颊两侧边界模糊标注框如果画得过大把正常皮肤也包进去模型学到的就是“包含正常皮肤的框也是正样本”推理时自然会把框扩大。修正办法是收紧边界框让框边缘贴近病变可见边界。现象重点观察指标典型原因某一类mAP显著低于其他类每类AP曲线该类训练样本过少或标注框不齐混淆矩阵里类间互相混淆归一化混淆矩阵两类病变视觉特征接近或类别ID错标某类只有低置信度预测命中F1-confidence曲线模型对该类特征学习不充分4.3 预测框和标注框对比的可视化脚本只看数字不够把预测框和标注框画在同一张图上视觉上对比最直接。我通常写一个Python脚本绿色画标注框红色画预测框from pathlib import Path import cv2 def draw_yolo_boxes(image, label_path, color, thickness2): h_img, w_img image.shape[:2] if not Path(label_path).exists(): return image for line in open(label_path): parts line.strip().split() if len(parts) ! 5: continue cls_id, x_c, y_c, w, h map(float, parts) x1 int((x_c - w / 2) * w_img) y1 int((y_c - h / 2) * h_img) x2 int((x_c w / 2) * w_img) y2 int((y_c h / 2) * h_img) cv2.rectangle(image, (x1, y1), (x2, y2), color, thickness) return image img cv2.imread(val_sample.jpg) img draw_yolo_boxes(img, labels/val/val_sample.txt, (0, 255, 0)) img draw_yolo_boxes(img, pred/val_sample.txt, (0, 0, 255)) cv2.imwrite(compare.jpg, img)画出对比图后重点看三种情况预测框和标注框高度重合但位置偏移说明边框回归不够精确预测框明显小于标注框说明模型只学到了病变中心区域标注框没有覆盖但预测框高置信度输出说明存在漏标注问题。第三种情况如果在多张图出现应该补标注而不是改模型。5. 小样本提升两阶段微调、patch推理和标签闭环修正5.1 两阶段微调冻结主干训练头部再解冻全层低学习率第一次baseline验证链路后下一步用两阶段微调替代单次训练。第一段只更新检测头冻结主干低层特征yolo detect train \ data /data/skin-dataset/skin.yaml \ model yolov8n.pt \ imgsz 640 \ epochs 60 \ batch 8 \ freeze10 \ lr0 0.001 \ mosaic 0.3 \ patience 20freeze10在YOLOv8里表示冻结前10层这些层学到的是基础纹理和边缘特征与皮肤镜图像恰好能共用。冻结后模型只更新检测头参数不容易破坏预训练特征。第二阶段从第一阶段的best.pt继续训练解冻全部层yolo detect train \ data /data/skin-dataset/skin.yaml \ model /data/skin-dataset/runs/detect/train/weights/best.pt \ imgsz 640 \ epochs 60 \ batch 8 \ freeze0 \ lr0 0.0002 \ mosaic 0.3 \ patience 20第二阶段学习率降到0.0002让主干在皮肤数据上做微小调整。同样的两阶段做法在遥感图像目标检测里也很常见因为遥感标注同样是人工画框、样本量受限。小数据集上这个策略比直接全量训练稳定得多。5.2 病变目标过小时的patch推理皮肤病图像常常是相机近距离拍摄的高分辨率图整图缩放到640后小的白斑区域只剩几个像素。推理时使用patch切分比直接放大imgsz更灵活把一张大图切成多个640x640的patch分别推理后再合并结果。切分时保留一定重叠避免病变正好落在patch边缘被截断。import cv2 img cv2.imread(derm.jpg) H, W img.shape[:2] patch 640 stride int(patch * 0.8) for y in range(0, H - patch 1, stride): for x in range(0, W - patch 1, stride): crop img[y:y patch, x:x patch] cv2.imwrite(fpatch_{x}_{y}.jpg, crop)合并预测时同一个目标被多个patch检测到是很正常的对重叠区域的检测框做一次NMS即可。patch推理的代价是速度下降但替换来的是对小目标召回率明显提升适合医学影像定位类任务。5.3 把误检结果送回标注修正流程小数据集训练过程中模型会暴露标注问题。把验证集预测结果里置信度低于0.25却被模型输出的区域整理出来人工对照原图检查重点确认是否属于漏标注的正常病变区域。如果漏得多用LabelMe这类标注工具补框重新整理标签后继续训练。皮肤图像标注时病变边界模糊的类别建议多人分别标一次再合并框取交集能有效减小边界不确定性。版权与隐私方面皮肤疾病图像属于医疗数据训练时不要直接放在公网服务器数据划分脚本、训练参数和评估结果建议用一个固定目录归档方便复现审计。把conf、iou和imgsz这一组推理参数固定在配置文件里后续每次评估和推理都使用同一套取值确保线上结果和验证集指标口径一致。本文还有配套的精品资源点击获取
返回列表