简介:面向计算机视觉目标检测方向的研究者与学习者,这套宫颈癌YOLOv5检测数据集已按标准YOLOv5目录结构整理,包含独立训练集与验证集,可直接用于目标检测模型训练,无需额外格式转换。数据按datasets目录划分训练集与验证集,训练集含816张图片及对应的816个txt标签,验证集含216张图片及对应的216个txt标签,统一使用单一类别cancer组织标注。图像均为640×640高分辨率RGB图片,病灶目标尺寸较小、边界框标注清晰且图像完整,尤其适合小目标检测相关算法的调试、消融实验对比与模型效果评估。资源共2000个文件,主要包含1083个txt标签文件、916张jpeg图像与1个可视化py脚本,压缩包约180.22MB;随包附带可视化脚本,无需修改即可直接运行,随机传入一张图片就能自动绘制边界框并保存至当前目录,便于快速查验标注质量。目前已有29人学习下载,适合需要直接开展YOLO系列模型训练与推理验证的中高级开发者使用。
1. 高质量宫颈癌YOLOv5检测数据集:一份可以直接喂给模型的标注资产
做宫颈癌辅助筛查相关的目标检测,最难的不是模型选型,而是数据。一张病理切片或TCT图像上,病变区域与正常组织交织,标注要由有经验的病理医生完成,普通标注工很难上手。所以当我看到「高质量宫颈癌YOLOv5检测数据集,包含完整训练与验证集、YOLO数据集」这个选题时,第一反应是:这解决了从业者最痛的环节。它把“从原始图像到可训练数据”之间最耗时、最容易出错的几步——标注、格式转换、训练集与验证集切分——全部提前做掉了,拿到手可以直接按YOLOv5的标准流程开工。
这个数据集适合三类人:第一次接触医学目标检测、想用现成数据跑通全流程的初学者;正在做宫颈癌辅助诊断方案、需要前期验证模型可行性的算法工程师;以及想评估YOLOv5在细胞学图像上到底能到多少精度的研究者。数据本身就是资产,但前提是你要会验收、会配置、会训练、会看指标,而不是拿到文件夹就开始敲命令。下面四件事,我建议按顺序做:先查数据、再训练、再排雷、最后用评估指标判断能不能落地。
2. 看清数据集家底:目录结构、YOLO标注格式与完整性校验
2.1 可训练数据资产的三个基本盘:目录、标签、映射表
一份能直接训练的YOLO格式数据集,基本盘是三件事:图片目录、标签目录、类别映射文件。常见做法是images目录下按train和val存放图片,labels目录下按同样的子目录结构存放同名txt文件,txt里每一行对应一个目标框。拿到的数据集只要长成这样,就说明已经把格式转换和训练验证集切分都替你做掉了。
我一般会先看一眼根目录是否有data.yaml或者类似的yaml文件,它声明了路径、类别数量和类别名字。下面是一个典型的YOLOv5数据集目录布局:
cervical_yolo/ ├── images │ ├── train │ │ ├── 001.jpg │ │ └── ... │ └── val │ ├── 101.jpg │ └── ... ├── labels │ ├── train │ │ ├── 001.txt │ │ └── ... │ └── val │ ├── 101.txt │ └── ... └── data.yaml注意一个细节:labels下的txt和images下的jpg必须保持同名,只是扩展名不同。YOLO训练时是按文件名前缀去匹配图片与标签的,前缀对不上,那一张图就等于没标注,会被静默跳过。很多训练结果虚低,第一步就栽在这里。
标签txt每一行的格式是固定的:类别ID、归一化后的中心点x坐标、中心点y坐标、归一化后的框宽、框高。比如下面这一行:
2 0.5248 0.4719 0.4920 0.6322含义是:类别ID为2,目标中心位于图片宽度的52.48%处、高度的47.19%处,目标宽度和高度分别占整幅图片的49.2%和63.22%。所有坐标都是0到1之间的浮点数,不需要去换算像素。要转成像素坐标,就是乘以图片的宽和高,这个转换在可视化或精确计算面积时经常会用到。
2.2 用30行脚本完成标签格式快检
拿到数据第一步不是训练,是写脚本检查完整性。这是踩过坑之后养成的习惯。一个数据集声称完整,不代表真的完整,可能是某一次转换脚本把一批标签漏掉了,或者某个txt里混入了空行、坐标越界。这类问题在训练阶段不会报错,只会让Loss曲线变得诡异。
下面这个脚本做两件事:检查图片是否有对应标签,检查标签内容是否合法。
import os import glob img_dir = 'images/train' label_dir = 'labels/train' imgs = sorted(glob.glob(os.path.join(img_dir, '*.[jp][pn]g'))) missing = 0 for img in imgs: stem = os.path.splitext(os.path.basename(img))[0] label = os.path.join(label_dir, stem + '.txt') if not os.path.exists(label): missing += 1 print('[missing]', label) print(f'total images: {len(imgs)}, missing labels: {missing}') bad_lines = 0 for label_path in glob.glob(os.path.join(label_dir, '*.txt')): with open(label_path, 'r') as f: lines = f.read().strip().splitlines() for line in lines: parts = list(map(float, line.split())) if len(parts) != 5: bad_lines += 1 print('[bad format]', label_path, line) continue cls, x, y, w, h = parts if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): bad_lines += 1 print('[out of range]', label_path, line) print(f'bad lines: {bad_lines}')这段逻辑很简单:先按扩展名匹配jpg和png图片,找同名txt;再逐行解析标签,检查是否有5个数值、坐标是否归一化在合法区间。参数上唯一可能需要改的是*.[jp][pn]g这个通配,如果数据集里有bmp格式,就把它扩进去。运行完后如果missing或bad_lines不是0,先用脚本修,坚决不要带着脏数据训练,这个时间省不得。对验证集目录也跑一遍,train和val都要干净。
2.3 把标注框可视化看一遍,别急着开训
格式检查通过只能说明文件齐全,不能说明标注框画得准。我建议训练前把所有验证集图片的标注框绘制出来,拼成一张大图人工过一遍。这一步能看到很多脚本查不出的事:比如标注框是套在病变核心区域,还是把一大片正常组织都框进去了。
import os import cv2 import glob img_dir = 'images/val' label_dir = 'labels/val' out_dir = 'debug_vis' os.makedirs(out_dir, exist_ok=True) for img_path in sorted(glob.glob(os.path.join(img_dir, '*.[jp][pn]g'))): stem = os.path.splitext(os.path.basename(img_path))[0] label_path = os.path.join(label_dir, stem + '.txt') img = cv2.imread(img_path) h, w = img.shape[:2] if not os.path.exists(label_path): continue with open(label_path, 'r') as f: for line in f.read().strip().splitlines(): parts = list(map(float, line.split())) if len(parts) != 5: continue cls, x, y, bw, bh = parts x1 = int((x - bw / 2) * w) y1 = int((y - bh / 2) * h) x2 = int((x + bw / 2) * w) y2 = int((y + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, str(int(cls)), (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) out_path = os.path.join(out_dir, stem + '.jpg') cv2.imwrite(out_path, img) print('saved', out_path)这里把归一化坐标乘回图像宽高,得到像素坐标之后用OpenCV画矩形和类别编号。主要参数是画框线宽2像素,文字放在框左上角外侧,避免遮挡目标区域。输出到debug_vis目录之后,挑30到50张图拼网格看,重点看两类问题:类别编号是否和实际病变区域一致,框是否贴合目标边界。这一步花10分钟,能省掉后面训练完才发现数据标注口径不对的后悔药。
3. 跑通YOLOv5训练:从data.yaml到命令行里的5个关键超参
3.1 编写data.yaml:路径、类别ID与names的一一对应
YOLOv5训练时通过--data参数指定数据配置文件,这个文件决定了模型去哪里读图片和标签。如果数据集没有附带data.yaml,这一步必须自己做。yaml内容不多,但每个字段都牵一发动全身。
path: /home/user/datasets/cervical_yolo train: images/train val: images/val nc: 3 names: 0: LSIL 1: HSIL 2: SCCpath建议写成绝对路径。train和val是相对于path的子目录路径,不要写成/home/user/datasets/cervical_yolo/images/train这样带完整前缀的写法,否则换机器或移动数据集目录时会全部失效。nc是类别总数,names是0到nc-1的类别列表。
这里最容易搞错的是类别ID的对齐。txt文件里第一个数字是类别ID,它是names列表的下标。也就是说,txt里出现0,代表LSIL;出现1,代表HSIL;出现2,代表SCC。如果数据集标注时类别编号从1开始,或者names顺序不对,训练出来的模型预测类别会整体错位,指标却不一定会低得很明显,属于比较难排查的问题。
提示:拿到数据后先统计所有txt里出现过的类别ID,确认最大值等于nc-1且无跳号,再开始训练。
3.2 启动训练:一个适合医学小目标的基准命令
数据验收没有问题之后,训练命令其实很固定。我一般用YOLOv5s作为起步模型,而不是直接上YOLOv5x。原因很简单:医学图像数据量通常不大,小模型更容易收敛,训练速度快,迭代成本低;等验证集mAP跑不动了再换大模型提升上限。
python train.py --img 640 --batch 16 --epochs 100 \ --data cervical.yaml --weights yolov5s.pt \ --cache ram --amp --patience 15 \ --hyp hyp.scratch-low.yaml每个参数仔细说一下。--img 640是输入分辨率,YOLOv5会把训练图片统一缩放到这个尺寸。对细胞学图像来说,640只是起步值,如果病灶区域很小,建议直接上960,这个后面会展开。--batch 16是要根据显存调的,显存不够就降到8,batch过小会影响BatchNorm统计量,所以8到32之间比较合适,不要为了显存硬压到2。--epochs 100是个相对稳妥的初始值,配合--patience 15做早停,连续15个epoch验证指标不上升就自动停止。
--weights yolov5s.pt是加载COCO预训练权重做迁移学习。虽然COCO没有医学图像类别,但骨干网络学到的纹理和边缘特征可以迁移到病理图像上,医学小数据集上效果比从头训练明显好。--amp开启混合精度训练,显存占用能低30%左右,精度损失极小。最后一个--hyp hyp.scratch-low.yaml是数据增强策略,这个文件降低了Mosaic与MixUp的增强强度,对医学图像很重要——过度增强可能生成组织纹理不真实的样本,让模型学偏。
3.3 读懂训练日志与loss曲线,让epochs不再靠感觉
训练过程中终端会持续输出类似下面的日志,不要只看有没有报错,要会看趋势:
Epoch 50/100: box_loss=0.042, cls_loss=0.011, dfl_loss=0.98, P=0.71, R=0.65, mAP@.5=0.73, mAP@.5:.95=0.41box_loss是框回归损失,数值越低说明预测框与标注框越贴合;cls_loss是分类损失,P和R分别是精度与召回率;mAP@.5是IoU阈值0.5下的平均精度,医学检测里它是个偏宽松的指标;mAP@.5:.95是COCO风格指标,对不同IoU阈值取平均,对定位精度的要求更苛刻,数值通常比mAP@.5低15到20个百分点,这个差距是正常的。
看日志时重点判断两件事:验证集的召回率是否在持续上涨,训练集损失和验证集损失的差距是否越拉越大。如果R长期在0.5以下而P很高,说明模型漏检严重。对宫颈癌筛查场景,漏检比误检更危险,后续调参优先保R。如果训练损失一路下降而验证损失在第40个epoch开始反弹,就是过拟合信号,早停或加大数据增强能缓解。
4. 避坑指南:训练宫颈癌YOLOv5时最常翻车的5个问题
4.1 标签与图片没对上号,训练集悄悄丢失样本
现象:训练日志里显示图片数比预期少很多,或者验证时某些类别的AP始终为0,但可视化发现标签文件明明存在。
原因:最常见的两种情况,一是文件名有前缀差异,比如图片叫001_a.jpg而标签叫001.jpg,YOLO按完全匹配的前缀找标签;二是某次切分脚本把部分子目录漏复制了,train图片比标签多出几百张。
解决:用前面2.2节的完整性校验脚本,先跑train再跑val,把missing标签的图片全部列出来。然后再写一个反向检查:找labels目录下没有对应图片的txt,如果有,说明标签是孤儿文件,直接移走。处理完后再跑一遍统计数据,确认图片数等于标签数再训练。这一步是免费的血泪经验,别跳过。
4.2 类别编号从1开始,模型把LSIL学成了HSIL
现象:训练一切正常,loss正常下降,mAP也还可以,但推理时模型输出的类别标签和医生标注完全对不上,低级别病变被识别成高级别病变。
原因:标注工具和转换脚本之间的类别编号约定不一致。很多标注工具内部从1开始计数,而YOLO要求从0开始。如果原始转换脚本没有把ID减1,所有类别就会整体偏移一位,模型学到的是错位的映射关系。
解决:训练前写一个统计脚本,把所有标签文件里的类别ID汇总成集合,看看实际有哪些值。如果ID最大值大于nc-1,或者ID有跳号,就对全部标签做一次批量修正。修正时保留一份备份,把txt里第一列的数字按照映射关系替换,再重新校验一遍分布。这个坑最阴的地方在于指标不一定会崩,因为模型确实学会了“把某类纹理分类成某个固定编号”,只是编号对应的名字是错的。
4.3 小病灶被下采样吃掉,漏检集中在最小尺寸类别
现象:结论是HSIL的AP比LSIL高很多,小尺寸目标的召回率显著偏低,模型原样输出一张图像里尺寸很小的核团时几乎全部漏掉。
原因:宫颈细胞学图像里早期病变的核心特征就是局部细胞团的形态变化,这些区域在整张图里可能只占几十乘几十个像素。把图缩放到640分辨率后,特征信息被下采样抹掉了,再强的网络也没法从模糊像素里找回细节。Mosaic增强把四张图拼到一起训练,又会进一步缩小每个目标的实际尺寸。
解决:把--img从640调到960甚至1280,对显存的要求会显著上升,batch对应降下来。如果显存撑不住,另一个方案是先把完整的大图切成小块patch,每个patch单独训练和推理,推理时再按位置拼回完整结果。切patch要带重叠区域,否则目标正好在切缝处时会被截断,后续检测困难。这个方向是典型的以算力换精度,但对小目标确实有效。
4.4 同一病例被拆进训练和验证,mAP虚高
现象:验证集mAP高达0.9以上,但把模型拿到新采集的病例上测试,效果明显变差,仿佛换了一批数据。
原因:医学图像通常按病例存储,同一病例的多张图像有强相似性。如果切分训练验证集时是按图片随机洗牌,而不是按病例分桶,同一个患者的部分图片会出现在训练集,另一部分出现在验证集。模型等于提前“见过”验证病人了,验证mAP虚高是必然的。
解决:拿到数据集先确认切分方式。如果数据集已经按病例隔离切分,可以直接使用;如果不是,重新按病例维度组织:一个患者的所有图像必须全部进训练集或全部进验证集,不允许跨集合。更严格的方案是同时按病例和数据来源做分层,保证不同医院或不同批次的样本在验证集中有覆盖,这样评估结果才对真实场景有参考价值。
4.5 标注边界过松,模型学会框住整个区域而不是病变核团
现象:模型得到的mAP不低,但输出的框明显比实际目标大一圈,IoU在0.5阈值下还能接受,一旦把评估阈值提高到0.75,mAP直接断崖式下跌。
原因:标注规范不统一。部分标注人员倾向于把整个上皮区域或整片可疑区都画进框里,而严格的标准应该只框住明确的病变核团。模型学习时接收到的是模糊边界,预测框自然随之放宽,定位精度上不去。
解决:在2.3可视化阶段就要发现这个问题。解决办法是统一标注边界规范:目标框必须紧贴病变核团外缘,不能包含周边正常组织;同一个类别的最小目标也要独立标注,不要合并成一个大框。如果数据集中已经存在大量松框,可以在预处理阶段计算每个框的宽高与对应目标实际轮廓的匹配度,挑出偏差大的样本重新标注或剔除。质量不足的标注宁可删掉一部分,也不要带进去训练。
5. 评估比训练更重要:mAP之外,还要看混淆矩阵与临床可用性
5.1 用val.py输出一套完整指标
训练完成后第一件事是回到命令行跑一次标准验证,而不是直接拿训练日志里的最大值当最终结论。YOLOv5自带val.py脚本,输出比训练过程更完整的结果。
python val.py --data cervical.yaml \ --weights runs/train/exp/weights/best.pt \ --conf-thres 0.25 --iou-thres 0.5 --img 960这里--conf-thres 0.25是置信度阈值,低于这个分数的预测框会被丢掉,调高它会提升精度但降低召回;--iou-thres 0.5是判断预测框与标注框是否匹配的IoU阈值,这是NMS阶段和mAP计算时使用的匹配标准。--img 960要与训练时的输入尺寸保持一致,否则评估结果和训练指标不可比。
运行结束后会输出一张results.png和一份confusion_matrix.png,保存到根目录或runs/val路径下。不要只看终端打印的mAP就完事,这是比较危险的习惯。
5.2 每类单独算AP,别用总体mAP掩盖短板
总体mAP是所有类别AP的均值,很容易被强类别带高。对于宫颈癌检测,LSIL、HSIL和SCC三个类别的临床意义完全不同,漏掉一个HSIL和漏掉一个SCC的后果不一样。至少要单独看每一类的AP和召回率。
| 指标 | 作用 | 这一类数据集的观察重点 |
|---|---|---|
| mAP@.5 | 整体精度 | 高于0.9时怀疑数据泄漏,需要排查验证集独立性 |
| mAP@.5:.95 | 定位精度 | 医学小目标通常比@.5低15%以上,属正常现象 |
| AP_LSIL | 低度病变检测 | 最容易与其他两类混淆,关注与HSIL的互混情况 |
| AP_HSIL | 高度病变检测 | 筛查场景里召回率比精度更重要,漏检不可接受 |
| AP_SCC | 鳞癌检测 | 样本量通常最小,AP波动大,看置信区间更稳 |
从混淆矩阵里重点看LSIL被预测成HSIL的比例,以及反向混淆。两者在细胞形态上有连续性,边界本来就不明确,如果混淆比例超过20%,就要考虑是否为标注时分类标准不一致导致的。可以用一个简单办法辅助判断:把混淆样本的图像和对应标注全部可视化,请有经验的医生或标注人员帮忙回看,区分到底是模型学错了还是标注本身标错。
5.3 针对宫颈癌筛查场景,怎么理解假阴性与假阳性
医学场景的评估逻辑和通用目标检测有本质差别。通用检测里,precision和recall是两个对等的指标,但在宫颈癌筛查里,假阴性意味着阳性病例被漏掉,患者可能错失早期干预窗口;假阳性虽然会带来不必要的复核,但至少还有医生兜底。所以调参方向要偏向召回率,必要时牺牲一些精度。
做法上,推理时把--conf-thres从0.25降到0.1或0.15,会捡回一批低置信度的可疑目标,代价是假阳性增加。这个阈值不是拍脑袋,要看验证集上recall曲线在什么阈值附近开始饱和。另一个做法是采用更严格的IoU评估阈值,比如把--iou-thres提到0.75再看定位精度,这样能反映出模型在真实切片上是否够用。最终给医生的输出应该带位置与置信度,把低置信度目标单独列一份“需复核”清单,比统一屏蔽掉更负责。
如果数据集附带验证集,就按规定使用;如果还想再严格一点,可以从中再留出一部分当盲测集,只做最后一轮最终评估,不要反复在同一个验证集上调参,否则验证集指标也会逐渐失真。评估到最后,问自己一句:这个模型敢不敢放进辅助诊断流程里跑一批新数据?如果不敢,前面的训练就还没收尾。
6. 继续挖精度:难例挖掘、增强策略与TTA推理的进阶路线
6.1 难例挖掘,把你的漏检喂回训练集
基础流程跑通后,最有效的提升手段不是换大模型,而是难例挖掘。用当前best.pt对训练集做一轮推理,把置信度低于0.2的预测框和漏检区域剪出来,人工复查这些样本的标签。如果发现漏标,补标后把这些图像加大采样权重重新混入训练。循环两轮,比盲目增加epochs的效果更明显。这个思路的本质是让模型把注意力放在它最不擅长的困难样本上。
6.2 TTA和patch推理,稳住小目标与边界目标
推理阶段可以开启TTA,在推理时对同一张图做多尺度缩放和翻转,再合并结果。召回率通常能提升两个点左右,代价是推理时长成倍增加。对显存不足或超大图像,切patch推理是更实用方案:将大图切成512或640的patch,每个patch重叠100像素,推理后按坐标合并再做NMS。这样可以保住小目标细节,也避免目标横跨切缝时被切断,是目前应对超高分辨率病理图像的常用手段。
我自己第一次训练宫颈癌检测模型时,只顾着盯mAP数字涨得高不高,结果模型交付给评估方试跑后,反馈是漏检太多,才发现自己忽略了recall和混淆矩阵,那一刻是比较懊悔的。后来每次迭代,我都强制自己先看单类AP与混淆矩阵再决定是否收工,毕竟筛查场景里漏一个病人,不只是指标低几个点的问题。这个习惯保住了后面几次项目的验收效果。希望帮到你。
本文还有配套的精品资源,点击获取