简介:面向目标检测与医学影像识别学习者的宫颈癌YOLOv5检测数据集,定位为可直接投入模型训练与验证的YOLO格式数据包。数据按YOLOv5标准文件夹保存,cancer单一类别,训练集816张、验证集216张,图像为640×640 RGB大分辨率图片,病灶目标偏小,适合小目标检测、医疗影像筛查等场景。包内共2000个文件,以txt标注文件、jpeg图像和1个Python可视化脚本为主,7z压缩包约180.22MB;txt对应边界框标签,jpeg为原始病灶图像,可视化脚本无需修改即可随机抽图绘制边界框并输出结果。已有29人学习浏览。下载后无需额外标注或格式转换,可直接划分训练/验证数据进行YOLOv5训练。
1. 宫颈癌YOLOv5检测数据集:为什么它值得直接拿来训练
做医疗影像目标检测的开发者大多卡在同一个位置:模型结构早就熟了,YOLOv5的权重也能跑通公开数据集,但一换到宫颈癌筛查场景,数据就找不到。要么是公开数据带着水印和重复切片,要么只有几百张图根本不敢拿来训练,要么是标注格式混乱到要自己写一周转换脚本。这还只是数据层面,到了训练阶段还有类别不平衡、样本重复、标注框过小这些暗坑。标题里这套宫颈癌YOLOv5检测数据集,解决的正是这一段从「有idea」到「有模型」之间最耗时的问题:训练集和验证集齐整、YOLO格式规整、标注信息完整。
我按「先看数据组成 → 再讲训练前调整 → 然后跑通YOLOv5训练与验证 → 最后聊排错和进阶验证」的顺序来拆这个数据集怎么用。适合两类人:一是做宫颈细胞学或病理切片目标检测的研究生和算法工程师,想省掉数据预处理的时间直接跑基线;二是医疗AI公司里负责数据管线的工程师,需要一份结构规范的YOLO数据集来评估标注质量和模型可行性。读完你可以直接按步骤把训练跑起来,也能避掉我在类似数据集上踩过的坑。
2. 数据集结构拆解:训练、验证、标签、类别一一对应
2.1 目录结构与YOLO格式的对应关系
拿到数据集第一件事不是解压就跑,而是先看清楚目录结构。这个数据集的根目录一般长这样:
cervical_yolo/ ├── images/ │ ├── train/ # 训练集图像,通常占 80% 左右 │ └── val/ # 验证集图像,通常占 20% 左右 ├── labels/ │ ├── train/ # 与 images/train 一一对应的标签文件 │ └── val/ # 与 images/val 一一对应的标签文件 ├── classes.txt # 类别清单 └── dataset.yaml # YOLOv5 训练所需配置文件这里最关键的是 labels 目录和 images 目录必须同名同数量。每张img_001.jpg必须对应一个img_001.txt。缺一个,训练时 YOLOv5 会直接跳过图像,而且只在日志里出现一行警告,不仔细看根本发现不了。我第一次拿这类数据集训练时,就是没核对对应关系,训练完看 mAP 只有 0.1,排查半天才发现训练集里有几张图没标签被静默跳过了。
每个 txt 文件内容是 YOLO 格式的检测标注,一行代表一个目标框:
class_id x_center y_center width height四个数值都是相对于图像宽高的比例值,范围在 0 到 1 之间。比如某一行的内容是1 0.53125 0.46875 0.06250 0.05729,表示类别 id 为 1 的目标,中心点在图像横向 53.1%、纵向 46.9% 的位置,框宽为图像宽度的 6.25%,框高为图像高度的 5.73%。这种格式的好处是与图像分辨率解耦,换分辨率不用重新标注,缺点是对坐标精度敏感,转格式时小数点截断就会影响 IoU 计算,后面讲踩坑会细说。
2.2 classes.txt 里的类别定义与类别平衡评估
categories 文件的命名会因为标注习惯不同略有差异,有的叫classes.txt,有的叫labels.txt或者class_names.txt,内容都是一行一个类名。宫颈癌检测数据集常见的类别定义有按细胞类型分的(如normal、ascus、lsil、hsil),也有按病灶区域分的(如normal、lesion、cancer),本文这套数据集具体怎么定义,以解压后看到的classes.txt为准。
查看类别信息后,要顺手统计一下每个类别的样本数量。可以用下面的 Python 脚本:
import os from collections import Counter def count_classes(labels_dir, classes_file): with open(classes_file, 'r', encoding='utf-8') as f: class_names = [line.strip() for line in f.readlines()] counter = Counter() total_boxes = 0 for file in os.listdir(labels_dir): if not file.endswith('.txt'): continue with open(os.path.join(labels_dir, file), 'r', encoding='utf-8') as f: for line in f: parts = line.strip().split() if len(parts) >= 5: cls_id = int(parts[0]) counter[cls_id] += 1 total_boxes += 1 print("总标注框数:", total_boxes) for cls_id in range(len(class_names)): print(f"{class_names[cls_id]} (id={cls_id}): {counter[cls_id]} 个框") # 输出每张图的平均框数,判断标注密度 img_count = len([f for f in os.listdir(labels_dir) if f.endswith('.txt')]) print(f"平均每图框数: {total_boxes / img_count:.2f}") count_classes("./labels/train", "./classes.txt")这个脚本是「拿到 YOLO 数据集先做体检」的第一步。输出的类别分布有几个值得注意的指标:最高类和最低类的数量差距是否在一个数量级以上,决定训练时要不要加 class weights;平均每图框数低于 1 说明大部分图没有目标或标注稀疏,模型容易学成背景检测器;标注框总数去重后是否合理,可以去重一次,看看重复框占比,这在前面我用过的类似医疗数据集中是一个高频问题。
2.3 验证集的标注覆盖度与训练-验证泄露风险
验证集最容易出问题的地方,不是数量不够,而是覆盖度和独立性。理想的验证集应该覆盖所有类别,并且在图像层面与训练集完全隔离——同一患者的不同视野图不能同时出现在训练和验证里。但这个数据集是否在患者级做了划分,从目录结构上看不出来,需要自己验证。
一个实用的做法是检查验证集的图像哈希是否与训练集有重合:
import hashlib import os def file_hash(filepath): h = hashlib.md5() with open(filepath, 'rb') as f: for chunk in iter(lambda: f.read(8192), b''): h.update(chunk) return h.hexdigest() train_imgs = [os.path.join("./images/train", f) for f in os.listdir("./images/train")] val_imgs = [os.path.join("./images/val", f) for f in os.listdir("./images/val")] train_hashes = set(file_hash(f) for f in train_imgs) overlap = [f for f in val_imgs if file_hash(f) in train_hashes] print(f"训练-验证图像重合数量: {len(overlap)}")同样,要对标注框做内容级检查,比如检查验证集的标注框大小分布。医疗影像目标检测中,标注框过小是常态,但验证集里如果大量框的宽高都小于 0.05,AP 计算时会被当作无效结果。这两步检查最好在训练前做完,等模型训完再发现验证集有问题,等于白跑一轮。
3. 训练前的数据调整:三个绕不开的准备工作
3.1 坐标格式校验与损坏图像处理
YOLO 格式的标注文件是纯文本,任何一个字段出错,YOLOv5 训练时要么报错,要么把这个框忽略掉。最常见的问题有三类:坐标越界(大于 1 或小于 0)、坐标顺序错乱(xyxy 格式混入 xywh)、浮点精度过低。校验脚本我会这么写:
import os def validate_label_file(label_path, img_width, img_height): errors = [] with open(label_path, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): parts = line.strip().split() if len(parts) != 5: errors.append(f"第{line_num}行字段数不为5: {line}") continue try: cls_id = int(parts[0]) x_center = float(parts[1]) y_center = float(parts[2]) w = float(parts[3]) h = float(parts[4]) except ValueError: errors.append(f"第{line_num}行存在非数值: {line}") continue # 类别 id 越界 if cls_id < 0: errors.append(f"第{line_num}行类别id为负: {line}") # 坐标范围检查 if not (0 <= x_center <= 1 and 0 <= y_center <= 1): errors.append(f"第{line_num}行中心坐标越界: {line}") # 宽高检查 if w <= 0 or h <= 0 or x_center - w/2 < 0 or x_center + w/2 > 1 or y_center - h/2 < 0 or y_center + h/2 > 1: errors.append(f"第{line_num}行框超出图像边界: {line}") return errors校验完成后,还要检查图像文件本身能否被正常读取。用 OpenCV 或 PIL 批量读一遍,重点看有没有损坏的 JPEG、截断的 PNG、灰度图混入彩色图、EXIF 旋转信息不一致。这些在公开数据里概率不大,但医疗影像导出过程中常见。某个开放宫颈数据集翻车过,因为部分图像是 16 位深度的 TIF 混在 JPEG 里,YOLOv5 默认的cv2.imread会把 16 位图读成 8 位,信息直接丢掉,部分细粒度特征就没了。
3.2 数据增强策略选择与医学图像的约束
YOLOv5 自带增强管线,hyp.scratch.yaml里hsv_h、hsv_s、hsv_v控制颜色增强,translate、scale、fliplr控制几何增强。但医学图像的增强策略和自然图像有本质区别,不能直接用默认值。
对于宫颈癌筛查数据,我的建议是:颜色增强幅度降低到默认的一半,hsv_h从 0.0138 降到 0、hsv_s从 0.678 降到 0.3 左右、hsv_v从 0.36 降到 0.2。原因在于宫颈细胞学的染色方案(巴氏染色、液基薄层制片)有标准化的颜色范围,过度增强颜色会让模型学到错误的染色特征;几何增强里的fliplr=0.5可以保留(左右翻转对细胞形态没有语义影响),但scale=0.5不要开太大,因为很小的标注框经过大尺度缩放会变成几个像素甚至无效框。
旋转增强是另一个需要小心的点。病理切片的拍摄角度随机,适度旋转的泛化收益在 1 到 2 个百分点之间,但超过 30 度的旋转会改变细胞核的相对排列方向,如果类别定义中包含方向相关的形态学特征,旋转就会制造伪样本。用hyp.scratch.yaml时,把degrees设置在 15 以内比较合理。
3.3 数据集的 train-val 划分合理性复核
标题说包含完整训练与验证集,但「完整」不等于「合理」。拿到的划分比例通常是 8:2,需要确认两点:一是验证集的困难样本占比不能太低,如果验证集全是典型样本,训练时 mAP 虚高,换到真实场景就掉点;二是验证集里不能有训练集图像的缩放、裁剪版本。
实际操作中我一般会统计验证集里标注框宽高的 P50 和 P10 分位,判断它是否覆盖了小目标区间:
import os import numpy as np def get_box_percentiles(labels_dir): sizes = [] for f in os.listdir(labels_dir): if not f.endswith('.txt'): continue with open(os.path.join(labels_dir, f)) as fh: for line in fh: parts = line.strip().split() if len(parts) < 5: continue w = float(parts[3]) h = float(parts[4]) # 取框尺寸为边长 sizes.append((w + h) / 2) arr = np.array(sizes) return np.percentile(arr, [10, 50, 90]) train_pct = get_box_percentiles("./labels/train") val_pct = get_box_percentiles("./labels/val") print(f"训练集框边长分位 P10/P50/P90: {train_pct}") print(f"验证集框边长分位 P10/P50/P90: {val_pct}")如果两者的尺度分布差异过大,比如训练集 P10 超过 0.1 而验证集 P10 在 0.03,说明验证集的困难程度高于训练集,训练时 loss 会降得不错但 mAP 上不去。这种时候不是模型的问题,是数据集划分的问题,重新划分或者换一个按尺度分层抽样的划分策略才靠谱。
4. 用 YOLOv5 训练宫颈癌检测模型:完整命令与参数
4.1 环境准备与数据集配置文件的改写
YOLOv5 对硬件的要求不算苛刻,一张 8GB 显存的显卡就能训练小尺寸模型,但训练宫颈癌这类细节纹理数据集,图像分辨率一旦超过 640,显存会迅速吃紧。训练前先把数据集配置文件写好:
# cervical_dataset.yaml train: ./cervical_yolo/images/train val: ./cervical_yolo/images/val # test: 如果提供了测试集可以加上 nc: 4 # 修改为数据集实际的类别数 names: [ 'normal', 'ascus', 'lsil', 'hsil' ] # 以 classes.txt 内容为准,顺序必须一致这里的train和val推荐写绝对路径,避免相对路径在不同的工作目录下解析失败。nc必须与classes.txt的行数严格相等,names的顺序就是类别 id 的映射顺序,从 0 开始。很多数据集的 classes.txt 顺序和训练时的 id 不匹配,如果拿classes.txt里第 0 行是 normal,就要确保names数组里第 0 个也是 normal,不一致会导致评估时混淆矩阵完全错位。
4.2 训练启动命令与关键参数拆解
YOLOv5 的训练入口是train.py,启动命令我一般按这个模板:
python train.py \ --data cervical_dataset.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache ram \ --name cervical_yolo_run \ --project ./runs \ --exist-ok \ --patience 20每条命令的参数在这个场景下的考虑是:
--weights yolov5s.pt用 COCO 预训练权重做迁移学习,比随机初始化收敛快很多,医学图像和自然图像虽然在特征层面差异大,但底层边缘、纹理滤波器的共享还是有效的。如果显存够,可以用yolov5m.pt换一点精度;显存只有 6GB 的卡老老实实用yolov5s和--img 640。
--img 640是输入分辨率。原始数据集如果是 1920×1080 级别的视野图,640 能保留主要特征但会丢失细粒度细胞核细节。建议先用 640 跑通基线,然后用--img 800或者--img 1024做对比实验,看 mAP 提升幅度是否值得显存开销。
--batch 16在 8GB 显存上属于安全值。batch size 如果太小(小于 8),BatchNorm 的统计量会不稳定,在医疗数据这种类别不平衡的场景里会让少数类的梯度方向噪声变大。
--patience 20是早停参数,超过 20 个 epoch 验证集没有提升就自动结束。训练流程默认会保存 best.pt 和 last.pt,最后用 best.pt 做验证。
4.3 多种模型规模的效果对比与选择建议
不要只训一个模型就交差。同样是这个数据集,yolov5s和yolov5l在精度上的差距通常有 3 到 7 个 mAP 点,但推理速度会相差 2 到 3 倍。如果场景是离线分析病理切片,用大模型换精度是划算的;如果是实时辅助筛查,速度才是刚需。
以「先 s 后 l」的顺序做对比训练是比较常见的做法:
# 小模型快速验证数据质量 python train.py --data cervical_dataset.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 60 --name cerv_s_baseline # 大模型精调 python train.py --data cervical_dataset.yaml --weights yolov5l.pt --img 640 --batch 8 --epochs 100 --name cerv_l_finetune--batch 8是因为大模型显存占用翻了一倍以上。训练完成后对比两个模型的results.csv里验证集的mAP_0.5和mAP_0.5:0.95,如果 s 模型已经达到业务阈值,就没必要上 l。我见过不少团队把小模型训出 0.6 的 mAP 后直接上线,实际效果也不错,因为宫颈癌检测场景里召回率比精确率更关键——漏掉一个可疑细胞比多标几个正常区域严重得多。
4.4 训练日志观察:loss 曲线和 mAP 曲线的正常形态
训练过程中通过runs/cervical_yolo_run/results.csv观察指标,主要看 box_loss 和 mAP 的关系。正常的形态是:前 10 个 epoch 内 box_loss 快速下降,mAP_0.5 从 0.1 爬到 0.3 左右;20 到 30 个 epoch 之间 mAP 开始进入平台期;之后即使 mAP_0.5 不再涨,mAP_0.5:0.95 也还会有缓慢上升,说明边界框回归还在优化。
如果出现 mAP 在某个 epoch 突然掉到接近 0 然后恢复,通常是训练时 batch size 太小遇到梯度爆炸,或者学习率在 cosine 调度后期降得过快。用--cos-lr默认开启的情况下,把--lr0 0.01改为0.005可以缓解。如果 box_loss 降得很快但 mAP 始终在 0.2 以下,大概率是标注框与真实目标不匹配——有的框可能标在了背景区域,有的漏标了同一目标,这类问题不是超参能解决的,要回数据。
5. 训练与验证中的避坑指南:五条血泪经验
5.1 图像尺寸必须统一,否则锚框计算失真
现象:训练时损失很低,但验证时 mAP 忽高忽低,同一份权重多次验证结果波动超过 3 个点。
原因:数据集里混有不同分辨率的图像,YOLOv5 虽然会自动缩放,但如果原图比例差异大,比如 4:3 和 16:9 混着来,letterbox 填充区域比例不同,锚框匹配时背景占比变化导致预测置信度不稳定。
解决:训练前把图像统一缩放到相同长宽比的安全做法是预处理后再训练,用--img参数固定输入以外,还要确保dataset.yaml的train路径指向的图像本身比例接近。如果批次内图像长宽比跨度大,把rect=True加进启动命令,让 YOLOv5 按批次内最长边做矩形训练,尽量减少填充。
5.2 标注框过小导致的数值不稳定
现象:训练中obj_loss降不下去,始终在 0.04 以上震荡,输出层的小目标分支权重一直偏高。
原因:宫颈细胞图像里存在大量小于 16×16 的标注框(相对 640 输入),这些框在特征金字塔的浅层分支上对应的特征图区域只有 1×1 左右,负样本梯度占比过大。
解决:我在这类数据集上习惯的做法是检查所有标注框的像素尺寸分布,如果 P10 分位的框边长低于 8 像素,优先用--img 1024提高输入分辨率。如果显存不够,就把--multi-scale打开,让模型周期性切换 0.5 到 1.5 倍缩放,等效提升小目标分辨率覆盖。另外在hyp.scratch.yaml的box损失权重从 0.05 降到 0.03,能让模型更多关注类别和置信度预测,小目标框回归压力降低。
5.3 类别不平衡导致验证集精度虚高
现象:总的 mAP_0.5 超过 0.85,但每类单独看,少数类(比如 hsil 阳性细胞)的 AP 只有 0.1 附近。
原因:多数类样本占比超过 90%,模型整体精度被多数类拉高,少数类几乎没学到有效特征。验证时如果验证集的类别分布和训练集一样偏斜,总 mAP 会非常具有欺骗性。
解决:不要在验证集上只看总 mAP,必须用--save-conf输出置信度文件后逐类计算 PR 曲线。我在这类不平衡数据上的建议是,对少数类做离线复制增强,或者改用--cls参数提高类别损失权重(一般从默认 0.5 提到 0.8 到 1.0)。也可以尝试给少数类单独做模型蒸馏,用多数类模型作为 teacher,在特征层面指导学生模型学习少数类的表征。
5.4 训练集和验证集泄露的隐蔽形式
现象:训练时 mAP 达到 0.95 以上,但换到任何真实外部测试集都断崖式掉到 0.4 以下,且人工抽查预测结果时发现模型对同一视野的不同截图输出完全不同的检测框。
原因:数据划分时没有按患者或病例做拼接查重,导致同一个病例的不同视野图分散在训练和验证集里,模型实际是记住了病例特征而不是检测语义。
解决:唯一可靠的做法是自己重新划分一个患者粒度的验证集。正常来说拿到数据集后先做图级查重,再做患者级查重比较稳妥,具体做法是统计图像的文件名前缀,如果前缀包含患者编号,就按编号分组后重新按组划分 train/val。如果这个数据集的文件名是随机哈希,无法从文件名判断患者归属,那就只能从图像内容层面做感知哈希去重,把相似度超过 0.95 的图像对标记出来,确保训练集和验证集不跨组。
5.5 验证阶段设备差异导致的 NaN
现象:训练好的模型在本地 CUDA 上推理正常,部署到 CPU 机器或另一块显卡时报 NaN 或输出空的检测结果。
原因:权重文件本身没问题,出问题的是模型结构里使用了不兼容的某些算子,或者验证机器上 OpenCV 版本太旧,对某些格式的 JPEG 解码丢数据。
解决:先固定验证环境,用同一个 Docker 镜像或 conda 环境。我在类似项目里遇到过cv2.imread在两台机器上返回 None 的情况,换成PIL.Image.open做验证时读图,再转成 BGR 数组给模型推理,能避免大部分坑。如果遇到了全图推理为 NaN,大概率是精度溢出,把输入图先转成float32并做np.clip到 0-255 范围再归一化。
6. 从训练到部署:验证数据集的真实泛化能力和后续提点技巧
训练完之后,验证集的 mAP 只能说明拟合程度,真实泛化能力要看外部验证。我养成了一个习惯:拿这个数据集训出的模型,再去找一份与此数据集来源不同但标注目标语义相近的宫颈细胞图像数据集,跑一遍推理,统计检测框的类别置信度分布和误检类型。如果外部数据集的精度只比验证集低 5 个点以内,说明模型学到了语义特征而不是背景特征;如果掉点超过 15 个点,大概率是验证集与训练集存在隐藏的相关性,要回到第 5 章的方法重新划分数据。
另一个实用的验证方法是做 CAM 可视化。YOLOv5 虽然不像分类网络那样直接输出热力图,但可以对检测分支的高层特征图做梯度加权,查看类别激活区域是否集中在细胞核周围。这个方法不需要额外的库,用 PyTorch 的 hooks 就能实现。如果激活区域覆盖到整个细胞质而不是细胞核,说明模型的注意力被背景染色特征干扰了,这时候需要检查增强策略里的色彩扰动是否导致了颜色混淆。
更进一步的提点方向有两个。第一是使用 YOLOv5 的--evolve模式做超参数搜索。以这段数据的训练成本看,单次训练几十个 epoch 在单卡上可以接受,用进化算法跑两轮约 50 次训练,重点搜索lr0、lrf、box、cls几个参数组合,通常能比默认超参提升 2 到 4 个 mAP_0.5:0.95。第二是针对宫颈癌检测的特定类别关系做后处理,比如 hsil 和 lsil 在形态上存在连续性,模型容易把同一细胞在帧间判别为不同类别,可以在输出层之后加一个时序或空间平滑模块,利用相邻检出框的类别概率做投票修正。
最后是一个我踩过很多次的小教训:不要在同一个数据集上反复迭代模型,而是一次性把数据质量检查做扎实,再开始调模型。宫颈癌检测这种类别边界模糊的任务,数据标注偏差对模型上限的影响远超模型结构的选择,花三天清洗数据,比花三周试网络结构有效得多。希望这篇笔记能帮你把时间花在真正有价值的地方。
本文还有配套的精品资源,点击获取