ARTICLE DETAIL

资讯详情

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

宫颈癌医学数据集训练YOLOV5:单类别目标检测全流程避坑指南

宫颈癌医学数据集训练YOLOV5:单类别目标检测全流程避坑指南 简介面向宫颈癌检测的YOLOV5标准格式医学数据集专为计算机视觉目标检测任务设计可直接用于训练与验证尤其适配小目标检测场景。压缩包共2000个文件包含1083个txt标注文件、916张大分辨率JPEG图像和1个可视化脚本资源包整体约614.59MB。数据按YOLOV5规范的train/val目录组织无需额外转换即可接入训练流程图像分辨率达到1000-4000像素病灶区域小且边界框标注清晰训练集816张、验证集216张统一为cancer单类别。附带的Python可视化脚本无需修改即可运行随机传入一张图片就能绘制边界框并保存到当前目录方便快速核验标注效果。已有738人学习/下载适合需要医学影像小目标检测数据集的开发者直接投入模型训练与实验对比。1. 宫颈癌检测的YOLOV5医学数据集为什么“1类别”反而是个坑拿到一个“医学数据集(YOLOV5目录格式)宫颈癌检测1类别包含训练集、验证集)”很多人的第一反应是这有什么可折腾的COCO都跑过80类1类不是分分钟的事。但真跑到医学影像数据集上才会意识到“单类别”三个字意味着完全不同的困难模式。这类数据集往往是医生手工标注的标注标准不统一、背景区域比例悬殊、病灶区域小且边界模糊。更关键的是目录结构虽然叫YOLOV5格式但有很多潜规则标签文件是否和图像严格对齐、坐标是否做过归一化、划分数据集时是否考虑过患者维度。这篇文章就顺着这条链路把单类别宫颈癌检测数据集从格式校验、训练调参到验证排错讲透给正在做医学影像检测的工程师一条可落地的路径。2. 解剖YOLOV5目录格式从原始宫颈图像到训练集/验证集的转换全流程对于宫颈癌检测原始数据通常来自医院病理科或妇科内窥镜拿到手的可能是VOC的XML、COCO的JSON甚至是医生直接在PPT上画的红圈这种情况只能手工二次标注。YOLOV5要的目录结构非常死板train/images存放图片train/labels存放对应的TXT标签验证集同理。这里第一个大坑就是“同名对应”关系一旦某个图片文件名和标签文件名对不上训练时那些图片会被悄悄跳过而你还在纳闷为什么Loss降得那么快。2.1 标准目录结构images与labels的强对应关系很多小伙伴习惯把所有图片丢进一个文件夹跑train.py这对YOLOV5来说是大忌因为YOLOV5在解析--data指定的yaml文件时是按images和labels两个文件夹去自动匹配的。匹配规则很简单图片名.jpg对应图片名.txt后缀随便但主文件名必须一致。如果你的原始数据是分患者的我建议按“按病例划分”而不是“按图片划分”来切分。这套逻辑能有效避免同患者图像同时出现在训练集和验证集里防止数据泄漏。import os import shutil import random # 不建议把所有图片都塞进一个文件夹再划分 # 建议先按患者ID或者病例ID归类 raw_root path/to/raw target_root cervical_cancer_yolo for split in (train, val): os.makedirs(os.path.join(target_root, split, images), exist_okTrue) os.makedirs(os.path.join(target_root, split, labels), exist_okTrue) # 假设raw_root下已经按 patient1/img001.jpg, patient1/img001.txt 放好了 all_cases [d for d in os.listdir(raw_root) if os.path.isdir(os.path.join(raw_root, d))] random.seed(2023) random.shuffle(all_cases) # 按病例数划分而不是按图片数划分 val_case_count max(1, int(len(all_cases) * 0.2)) val_cases set(all_cases[:val_case_count]) for case in all_cases: split val if case in val_cases else train case_dir os.path.join(raw_root, case) for img_name in os.listdir(case_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue label_name os.path.splitext(img_name)[0] .txt # 强调YOLOV5要求图片和label严格同名 # 如果标签不存在这条数据等于白标必须跳过 if not os.path.exists(os.path.join(case_dir, label_name)): continue shutil.copy(os.path.join(case_dir, img_name), os.path.join(target_root, split, images, img_name)) shutil.copy(os.path.join(case_dir, label_name), os.path.join(target_root, split, labels, label_name)) print(数据集划分完成记得检查train和val下的数量比例不要被单个病例图片数带偏)这段代码把按患者划分数据放在了第一位。因为宫颈癌数据集里同一个患者的病灶特征高度相似如果患者的某个帧在训练集另一个帧在验证集模型在验证集上的表现会有虚高这种数据泄漏是医学影像中的大忌。另外代码里做了一个默认跳过缺失标签的操作保证进到训练流程里的每一张图都有对应的TXT否则YOLOV5会在训练中途报错或者直接忽略导致训练和验证集对不上。2.2 转换关键一步坐标归一化与类别ID检查很多来自医院的XML标注长的是这种结构VOC格式的bndbox节点。但有个坑是有的XML里存的坐标是医生在原始大图上标的有的却经过了缩放如果直接拿图宽高去除坐标很容易超出0~1范围。我一般会写一个转换函数并且加两个保险第一把归一化之后小于0或大于1的坐标直接丢给异常处理第二用目标的面积做一个过滤面积太小的框大概率是标注噪声。import os import xml.etree.ElementTree as ET def convert_annotation(xml_path, img_w, img_h, class_id0): tree ET.parse(xml_path) root tree.getroot() objects root.findall(object) lines [] for obj in objects: # 医学数据集可能出现“极其激进”的标注比如整张图都是病灶 # 这种极端情况需要考虑是否是误标下面做个尺寸限制 bndbox obj.find(bndbox) xmin max(0, float(bndbox.find(xmin).text)) ymin max(0, float(bndbox.find(ymin).text)) xmax min(img_w, float(bndbox.find(xmax).text)) ymax min(img_h, float(bndbox.find(ymax).text)) if xmax xmin or ymax ymin: print(f跳过非法框: {xml_path}) continue bw xmax - xmin bh ymax - ymin # 过滤非常小的框比如小于10像素的多半是标注噪声 if bw 10 or bh 10: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w bw / img_w h bh / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines归一化的核心是必须使用图像的原始宽高因为YOLOV5在训练时会做随机缩放、裁剪等到马赛克增强时会把多张图拼接在一起如果归一化基准错了增强后框的位置会完全对不上。同时我在转换时顺手加了10像素的过滤阈值这在医学图像上非常管用。因为医疗标注常常伴随大量不仔细的点选10像素以下的框既无法提供有效特征还会扰乱损失计算。完成目录构建和坐标转换后YOLOV5需要的其实就只是一个数据集配置文件跟模型训练参数了。3. 修改模型配置与训练参数让YOLOV5在医学单类别数据上收敛很多人在这一步会直接执行python train.py --data cervical.yaml --weights yolov5s.pt但医学影像数据集的“脾气”和自然图像数据集完全不一样。宫颈癌检测需要在极小面积、极大背景干扰下找病灶训练参数调不好模型很容易在验证集上“眼瞎”。3.1 定义自己的data.yaml和模型yaml单类别配置要点数据集配置文件极其简单但有一个细节值得多说一句路径最好填绝对路径。我见过太多人填相对路径换一台电脑跑立刻报错。# cervical.yaml # 训练集图片路径 train: /data/cervical_cancer_yolo/train/images # 验证集图片路径 val: /data/cervical_cancer_yolo/val/images # 类别数1 nc: 1 # 类别名称这里强烈建议用英文中文在部分可视化工具里会变成乱码 names: 0: lesion模型配置文件如果是小数据量建议从yolov5s.yaml起步甚至直接修改yolov5s.yaml里的nc为1。数据集本身不大时直接上yolov5l或者yolov5x会带来两个问题一个是显存压力大另一个是模型容量太大医学数据量往往只有几百到几千张很容易把训练集的特征背下来然后在验证集上泛化崩溃。3.2 训练命令与关键超参数batch_size、img_size和epochs的取舍对于宫颈癌检测病灶通常只是图像的一小块区域如果使用默认的--img 640小病灶在特征图上的尺寸可能只有几个像素检测头基本学不到东西。我一般会先用--img 1280跑一版看看基线但代价是显存占用直接翻倍。如果显卡只是入门级的8G显存建议用--batch-size 4配合梯度累积。python train.py \ --data cervical.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch-size 4 \ --epochs 100 \ --patience 20 \ --name cervical_1280 \ --exist-ok这里最重要的一组参数是--img和--batch-size。--img决定了小目标能不能被模型看到--batch-size则直接影响显存和BN层统计。医学数据一般都比较少--patience要设置得比通用场景大一点比如20轮。如果设成10很容易在验证集损失波动时过早停掉错过最佳权重。另外针对医学单类别我建议直接给YOLOV5传一个自定义的超参文件核心是调低初始学习率、提高cls_loss权重。打开data/hyps/hyp.scratch-medical.yaml这样设置lr0: 0.005 # 初始学习率小数据集lr0一大就飞 lrf: 0.1 # 最后一轮学习率 warmup_epochs: 3.0 cls: 0.6 # 类别损失权重单类别也建议比默认0.5略高 cls_pw: 1.0 # 正负样本权重类别不平衡时调高cls_pw是关键宫颈癌数据集正样本区域少负样本区域多。提高这个值可以让模型更倾向于去“猜”有物体从而提升召回率代价是可能多一些误检这个在后面验证环节可以通过置信度阈值来压。整套超参数配合--hyp hyp.scratch-medical.yaml传入训练命令即可。如果你用的是YOLOV8或更新版本其实核心思路也一样只不过命令和配置文件结构稍有调整但“小数据集必须小学习率、大输入尺寸”这个原则是通用的。4. 训练与验证避坑解决宫颈癌检测数据集的常见翻车现场这部分内容是纯血泪经验。医学数据集不像通用数据集那么规整很多问题都是标注和预处理阶段埋下的雷到训练时才会炸出来。我整理了四个最常见的问题每一个都是“现象→原因→解决”的完整链路。4.1 避坑一显存溢出Out of Memory与尺寸选择现象运行train.py后刚开始加载数据就报CUDA out of memory进程被直接kill。原因--img 1280加上--batch-size 8在6G卡上基本是必炸的。图像尺寸越大特征图张量指数增长同时因为马赛克增强会一次性加载4张图显存峰值会更高。解决显存小就别硬扛--img 1280先回退到--img 640观察损失曲线。如果想强行上大图把--batch-size降到2同时开启--cache这样数据不会频繁地进行I/O虽然占内存但会快很多。也可以考虑用--multi-scale让模型在训练时自动在--img的0.5~1.5倍之间切换这样能在小显存条件下模拟大图输入。另外--workers 0在数据加载阶段能减少一部分显存峰值尤其是在Windows系统上。4.2 避坑二验证集mAP为0或极低现象训练和验证的损失都在下降但mAP0.5一直是0画出混淆矩阵发现所有的预测都被判成了背景。原因标签文件里的坐标是原始坐标但在预处理时用了压缩后的图片宽高做归一化导致中心点坐标偏移出0~1范围或者W、H变成负数模型在训练时根本找不到有效目标。还有一种常见情况是原始图片是灰度图但标注工具导出的TXT里类别ID写的是1而你的data.yaml里只有0导致所有标签被当成背景。解决在数据集目录里写个校验脚本把每个标签的坐标范围打印出来看一眼。import os for split in [train, val]: label_dir fcervical_cancer_yolo/{split}/labels for txt in os.listdir(label_dir): with open(os.path.join(label_dir, txt)) as f: for line in f: parts line.split() if len(parts) ! 5: print(f格式错误: {txt}) continue cls, x_c, y_c, w, h parts x_c, y_c, w, h float(x_c), float(y_c), float(w), float(h) if not (0 x_c 1 and 0 y_c 1 and 0 w 1 and 0 h 1): print(f非法坐标: {txt} - {line})如果真有非法坐标直接用我第2章里的转换函数重新生成一遍重点检查是不是用的原始图宽高。如果只是灰度图问题直接用cv2.imread(..., cv2.IMREAD_COLOR)强制转三通道存储。4.3 避坑三数据划分陷阱——同源图像泄漏现象验证集上mAP高达0.95但一放到真实临床上检测率却掉到0.6不到完全不是一回事。原因随机划分造成的同源数据泄漏。同一名患者的影像帧被同时切进了训练集和验证集模型“背”住了患者特征而不是病灶特征。这在视频或连续帧采集的医学数据里非常常见。解决一定要按患者ID来划分数据集。这一点我在第2章的代码里就做了。如果原始数据没有患者ID用文件夹名或文件名前缀提取否则就用图像感知哈希做去重。去重时注意医学图像往往只是亮度、对比度略有差异感知哈希本身对这类变化不敏感要配合结构相似性指数做一些预处理。4.4 避坑四标签文件为空或者格式错误现象训练结束发现标签数量统计里很多是0或者label可视化时框的位置和物体完全对不上。原因标注软件导出的TXT里有的行是0 0 0 0 0有的则是用逗号分隔YOLOV5只认空格。还有的标注工具会把类别名直接写进TXT比如lesion 0.5 0.5 0.1 0.1这会让YOLOV5解析时直接把坐标当成字符串训练直接中断或静默放弃。解决清洗标签的时候顺手做一次正则替换把逗号换成空格同时移除全0行。import os import re for split in [train, val]: label_dir fcervical_cancer_yolo/{split}/labels for txt in os.listdir(label_dir): path os.path.join(label_dir, txt) with open(path) as f: lines f.readlines() new_lines [] for line in lines: line line.replace(,, ).strip() line re.sub(r\s, , line) if not line: continue parts line.split() # 如果第一列不是数字大概率是类别名直接丢弃 if not parts[0].replace(., ).isdigit(): continue # 过滤全0的无效框 if all(float(x) 0 for x in parts[1:]): continue new_lines.append(line \n) with open(path, w) as f: f.writelines(new_lines)这种清洗脚本其实花不了几分钟但能省下一个晚上的Debug时间。每次转换完数据集我都会顺手跑一个全量的可视化脚本把标注框画在图上随机抽几十张看一眼确认框和病灶边缘贴合再开始训练。这个习惯帮我避掉过好几轮白训。5. 验证模型效果与部署准备用置信度阈值校准做医学目标检测模型训练完best.pt已经生成但医学场景里我们不能直接拿这个权重去上临床还有一个关键动作置信度阈值校准。先用带测试集的验证命令跑一遍完整指标python val.py --data cervical.yaml --weights runs/train/cervical_1280/weights/best.pt --img 1280 --conf-thres 0.1 --iou-thres 0.5 --augment注意这个--conf-thres 0.1我一般会刻意调低为的是把模型的“极限召回能力”试出来。如果低阈值下召回率都不到0.8那就别在部署阶段纠结阈值了回头去补数据或者调模型结构才是正路。如果低阈值召回率不错但精确率不堪入目那再通过提高--conf-thres到0.3、0.5来做取舍。医学场景里漏诊的代价往往比误诊高所以我会更看重召回率宁可多画几个框让医生去复核也不要漏掉真正的病灶。作为习惯我会把验证结果里的confusion_matrix.png单独拿出来看如果假阳性里确实带有一部分病灶中心但IoU不达标说明模型定位能力没问题是框不够准可以考虑用更高分辨率的输入或者调低NMS的--iou-thres做再次过滤。另外如果目标是部署到移动端或内窥镜设备记得在导出模型时使用--int8或者--dynamic做量化模拟因为浮点模型在推理时精度和速度往往不可兼得。我习惯把val.py输出的results.png留下来和上一次训练的结果做对比看一下每个类别的AP变化以及PR曲线下面积有没有实际提升。医学影像检测很忌讳只看最终mAP因为类别权重和样本数量会掩盖很多问题单类别场景下PR曲线和召回率才是真正能支撑临床决策的指标。这套流程走下来数据集格式、训练参数、阈值取舍都留下了记录下一次换一个新数据集照方抓药就能少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表