简介:这份X光片肺病数据集面向计算机视觉与医学影像入门开发者,覆盖细菌性肺炎、新冠病毒、正常肺、结核和病毒性肺炎五个常见类别,适合用于目标检测、图像分类和迁移学习实验。全部800张原始胸片已按YOLOv7格式完成标注,每张图片对应一个txt标签文件,另含1个yaml配置文件,可直接送入YOLOv7系列模型进行训练、验证与推理。压缩包共1601个文件,包含800张jpg图片、800个txt标注和1个yaml,整体大小25.51MB,文件命名包含类别与来源信息,便于划分训练集、验证集或按类别筛选。对希望快速接触医学影像AI的开发者而言,这份资源省去了繁琐的标注环节,也提供了现成的数据组织方式,可更专注于模型调参、指标评估和检测效果优化。目前已有410人浏览学习,适合作为入门级肺炎检测项目的实验数据。
1. 为什么说YOLOv7标注的800张X光片肺病数据集能直接拿来训练
做过医疗影像检测的人应该都体会过那种焦躁感:模型架构改了三轮,loss死活不降,最后排查发现是标注文件的问题,浪费时间比训练还多。这份X光片肺病数据集,800张原始图片已经用YOLOv7格式标记完成,覆盖细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎五个类别,看到的第一反应是“省了大事”。它把最耗人力的标注环节直接跳过了,你拿到手就是图片加txt标签,从零到能跑通YOLOv7训练大概只需要半天时间。适合两类人:一类是想快速验证检测方案的从业者,另一类是刚上手YOLO系列、需要一份干净数据练手的新手。后者尤其划得来,因为踩坑的时候你能确定问题不在数据上。
2. 数据集结构:从Roboflow文件名反推标注规则与类别体系
先别急着把压缩包拖进训练脚本,花两分钟搞清楚它的内部结构,后面做训练配置会顺利得多。这类从标注平台导出的数据集,文件组织方式通常会保留平台痕迹,文件名往往比目录更能说明问题。
2.1 文件名里藏着什么:前缀类别、Roboflow版本号、图片ID
先看文件名,比如1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.jpg,拆开是这样的:
1_是编号前缀,可能是人工批次或平台内部排序。coronavirus-420是这张图对应的类别原始标签。_jpg表示原始图片是 JPG 格式。.rf.后面跟着一长串十六进制字符,这是 Roboflow 导出时自动生成的图片 ID,用来在平台上定位原图。
这套命名规则意味着两件事:第一,类别信息直接写在文件名里,即便不看标签文件也知道大概分布;第二,.rf.后缀在训练时没有实际作用,因为 YOLOv7 读取的是images/和labels/两个目录下的同名文件对,文件名只是用来关联图与标签的索引。平时训练环境里,直接用短文件名即可,长 ID 不会影响结果,只是复制路径时候容易出错。
2.2 一张图对应一个txt:读标签并检查标注格式
YOLOv7 格式的标签文件是纯文本,每行代表一个目标框,结构是class_id x_center y_center width height,前四个数值都归一化到 0 到 1 之间。拿到资源后,我先写一个五行的检查脚本,确认标签坐标没有异常:
img_path = "1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.jpg" label_path = img_path.replace(".jpg", ".txt") with open(label_path, "r", encoding="utf-8") as f: for line in f: cls_id, x_center, y_center, width, height = map(float, line.strip().split()) print(f"类别id: {int(cls_id)}") print(f"中心点: ({x_center:.4f}, {y_center:.4f})") print(f"框宽高: ({width:.4f}, {height:.4f})")这段脚本的原理不复杂:先把.jpg替换成.txt,找到对应的标签文件,再按空格切分每一行,把前五个字段转成浮点数。如果某个数值大于 1 或小于 0,说明坐标没有归一化,训练时会直接报错,甚至悄悄学到错误的特征空间。正常情况每一行应该到 0.9 以下,因为卫生影像里肺区通常不会顶着图边缘。
2.3 数据划分:导出可能已隐含划分,但建议自己重分
观察文件名,像Normal_test-1-、Normal_val-27-这种后缀,说明平台导出时已经做过一部分 train/val/test 划分。但这不代表直接就能用:一是划分比例未必匹配你的需求,二是如果只用平台默认划分,验证集里容易混进语义相似的图像,导致评估分数虚高。我一般会把所有图片和标签打散,按 8:1:1 重新分成 train、val、test,保证五个类别在每个集合里的比例接近整体比例,再记录每类的样本数。
3. 五大类别的语义边界:X光片上的纹理特征与标注倾向
检测任务和分类任务有区别,分类只需要判断“这张图里有什么”,检测还要知道“目标框落在哪里”。所以训练前得清楚每个类别在 X 光片上呈现什么形态,尤其是那些容易混淆的类别,这决定了你在后处理阶段要不要调低阈值、要不要额外加分类器。
3.1 细菌性肺炎与病毒性肺炎:实变和磨玻璃影的落脚点不同
细菌性肺炎在 X 光片上多表现为局灶性实变,密度较高,边界相对清晰,通常靠近肺叶的某一叶或肺段,甚至能看到支气管充气征。病毒性肺炎则多为间质性改变,出现磨玻璃样影,密度比实变低,边界模糊,常分布在外周或双侧。如果数据集标注得足够细,这两类的目标框位置往往有明显区别:实变区域更集中,框尺寸偏大;磨玻璃影分散,框偏小。训练时如果发现模型频繁把两类框搞混,我一般会在后处理阶段单独给这两类设置更高的置信度阈值,而不是用统一的 conf_thresh。
3.2 结核与新冠:空洞性病变和磨玻璃影的核心差异
结核病灶在胸片上常见于上肺野,表现为斑片状、结节状影,严重时会有空洞,空洞壁通常是实性的,中央是低密度区。新冠早期则以外周带磨玻璃影为主,多分布在双下肺,密度均匀,没有空洞。这两类的特征区别反而比细菌性 vs 病毒性肺炎更明显。真正容易出问题的地方在于:当结核病灶较小、没有形成空洞时,它和有磨玻璃影的新冠在外观上很接近,尤其在 640x640 分辨率下,像素特征几乎一致。我在实际项目中遇到过这种情况,解决思路是保留原始图像尺寸做一次额外的分类分支,或者在推理时直接看目标框的大小和位置分布来辅助判断。
3.3 先数清楚每个类别的样本量,再做训练决策
标注文件里的文件名前缀只能给人印象,不能精确计数。我建议写一个统计脚本,把五个类别在全部标签中的出现次数拉出来,这直接决定了要不要做数据增强加权、要不要调修改损失函数里的类别权重。可以这样写:
import os label_dir = "labels" class_count = {} for file in os.listdir(label_dir): if not file.endswith(".txt"): continue path = os.path.join(label_dir, file) with open(path, "r", encoding="utf-8") as f: for line in f: cls_id = line.split()[0] class_count[cls_id] = class_count.get(cls_id, 0) + 1 print(class_count)统计结果如果看到某一类占比不足 5%,那它在 YOLOv7 里的 baseline 性能大概率很差。受医学影像获取成本限制,这是常态,不用慌,重点在于后续设置训练策略时,要针对少数类多做一次离线增强,比如把某个类别的图片做左右翻转、加入随机亮度和对比度扰动。不要指望模型自己学会“少数类优先”。
4. 复现训练:YOLOv7从数据划分、配置到参数优化
拿到数据集最终目的是跑通检测流程。这里按我实际操作时的顺序走一遍,包含数据划分脚本、YAML 配置和训练命令,每一步都备注了可调整的参数。
4.1 准备目录结构:先建 images 和 labels 的同名配对
YOLOv7 的训练脚本默认按照image和label文件路径配对,不需要把标签作为额外输入参数,只要保证文件名一致。常见做法是先创建三个子目录,保存划分后的图片和标签:
import os import shutil from sklearn.model_selection import train_test_split image_files = [f for f in os.listdir("images") if f.endswith(".jpg")] label_files = [f.replace(".jpg", ".txt") for f in image_files] train_idx, test_idx = train_test_split( range(len(image_files)), test_size=0.2, random_state=42, stratify=... ) train_idx, val_idx = train_test_split( train_idx, test_size=0.1, random_state=42 )这段代码用的是最常规的做法:先把 20% 划为测试集,再在剩余 80% 里抽出 10% 作为验证集。stratify参数需要传一个类别分布数组,代码里省略了,实际使用时要先从标签文件里解析出每个图片的主类别。千万别省略分层采样,否则可能出现验证集里恰好没有结核样本的情况,模型在验证集上的召回率会失真。
4.2 配置 lung.yaml:类别列表、路径和训练参数
YOLOv7 的data参数指向一个 YAML 文件,里面的核心内容是类名列表和数据集路径。新建lung.yaml:
train: ./data/xray_dataset/train/images val: ./data/xray_dataset/val/images test: ./data/xray_dataset/test/images nc: 5 names: 0: bacterial_pneumonia 1: coronavirus 2: normal 3: tuberculosis 4: viral_pneumonia注意一个容易忽略的点:names的 id 顺序必须和标签文件里第一列的数值完全对应。这份数据集可能来自 Roboflow,Roboflow 导出的classes.txt会给出每个类别 id 对应的名称,一定要把那个文件打开核对一遍。类别顺序错乱是新手最容易犯的错,表面上训练能跑,实际上模型从头到尾都在学错误映射。
4.3 训练命令与关键参数:先跑 20 个 epoch 观察趋势
直接跑完整训练不现实,我习惯先用小 epoch 数验证整个流程是否通顺:
python train.py \ --data lung.yaml \ --cfg cfg/training/yolov7.yaml \ --weights yolov7.pt \ --batch-size 16 \ --img-size 640 \ --epochs 20 \ --name lung_xray_test \ --workers 4--cfg是模型结构文件,如果数据集目标尺寸跨度大,可以换成yolov7-tiny.yaml跑一个快速测试;--weights使用预训练权重时,YOLOv7 会自动跳过类别数不同的输出层,自动适配nc=5,不需要手动修改权重里的 head 部分;--img-size设 640 是综合了显存和检测精度的折中,X 光片原图分辨率通常超过 1000px,但直接上 1280 显存占用会翻倍,所以先用 640 验证。
跑完 20 个 epoch,观察results.txt里的 mAP50 和 mAP50-95 两个指标。如果 mAP50 能到 80% 以上,说明数据准备没问题,可以加大 epoch 继续训练;如果 mAP50 低于 50%,优先怀疑标注框有错位,而不是调模型结构。
5. 避坑指南:五个让YOLOv7训练翻车的细节与排查
这部分是实际用过这份数据集之后总结的排查清单,覆盖从数据到训练再到推理的常见问题。每一条都是“先看现象,再找原因,最后给解决路径”的顺序,照着走能省下大量试错时间。
5.1 训练 loss 一开始就在高位且梯度消失,先查坐标归一化
现象:训练前几个 epoch loss 卡在 0.2 左右不动,之后缓慢下降但 mAP 几乎为 0。
原因:标签 txt 中出现了大于 1 的坐标值或宽高像素值。典型场景是某些标注工具把框宽高写成了像素绝对值,YOLOv7 不会主动屏蔽这类错误,而是把它当作极值特征去学习。
解决:写脚本遍历所有标签,找出坐标值不在 0 到 1 范围内的行,统一除以图片宽高后重新覆盖。如果整个文件大部分数据都有问题,最稳妥的补救是重新导出数据集的标注格式,确认导出时勾选了“Normalize bounding boxes”。
5.2 训练过程正常,但 val 损失在后期不断波动
现象:前 30 个 epoch 平稳,第 30 个 epoch 之后 val loss 出现周期性锯齿状波动,每个 cycle 结尾突然升高。
原因:大概率是 batch size 太小,比如设为 8,每次迭代的梯度方向偏差过大,后期模型进入震荡区。X 光片背景比较单一,特征差异不像自然图像那么显著,小 batch 的梯度噪声会被放大。
解决:把--batch-size提到 16 或 32,同时检查是否开启了--cache-images把图像预加载到内存,这样能大幅缩短 epoch 时间,让 val loss 曲线更平滑。
5.3 正常肺被大量误报成肺炎,问题不在网络而在阈值
现象:验证集 mAP 不错,但是推理时正常肺图片经常输出一个假的肺炎框,置信度还不低。
原因:正常肺的样本虽然数量够,但边缘纹理和细菌性肺炎的磨玻璃影有相似之处,模型在低置信度区间对这两类的区分度不够。YOLOv7 默认的conf_thres=0.25对医疗场景偏低。
解决:调高--conf-thres到 0.4 以上,必要时对normal类单独做一个二次分类,直接把所有 detected boxes 抠出来交给一个小型 CNN 做二分类。不要指望目标检测模型自己解决所有语义混淆问题,尤其在数据有限时。
5.4 anchors 与目标尺寸不匹配导致小目标召回率为零
现象:确认标注没问题后,验证集上 tuberculosis 类别的 recall 始终为零,其它类别表现正常。
原因:X 光片的结核病灶往往是小结节或空洞,目标框可能只有 30x30 像素,而默认 anchors 里最小尺度是 10x13,在 640x640 分辨率下对应多个像素感受野,对这类小目标不够敏感。
解决:训练前运行tools/kmeans.py重新计算 anchors,并将输出的新 anchors 写回cfg/training/yolov7.yaml。具体做法是导入全部标签的宽高,用 k-means 聚类生成 9 组先验框,然后把anchors:后面的数值替换掉。
5.5 数据集稀疏类别少,用了类别权重但 loss 反而上升
现象:设置 loss 中类别权重后,少数类 recall 略有提升,但整体 mAP 下降,多数类被误判成少数类。
原因:类别权重是全局标量,对所有样本统一放大少数类梯度,导致模型强行把边界样本往少数类拉,反而破坏了原本清晰的分类边界。
解决:不用全局权重,改用离线增强策略。例如对少数类样本做 Mosaic 增强时提高参与概率,或者直接复制粘贴少数类目标框到空白区域生成新样本。这种做法让模型在位置和纹理两个维度都见到更多少数类,效果比全局加权更稳定。
6. 迁移学习微调的具体技巧:冻结骨干网络与学习率策略
如果你跑通了一次完整训练,接下来想压榨精度,最快的方式是微调而不是从头训练。这个数据集的五个类别在 X 光片上的纹理差异,并不比 COCO 数据集里的八十个类别更复杂,所以模型早期层次学到的边缘、纹理、对比度特征完全可以直接迁移。常见做法是加载yolov7.pt预训练权重,然后把骨干网络全部冻结,只训练检测头和颈部。第一次冻结训练用 5e-4 的学习率跑 10 个 epoch,等 head 收敛后把骨干解冻,再用 1e-4 跑 20 个 epoch 做全量微调。冻结可以这样实现:修改train.py内部的freeze参数,或者直接在配置里指定--freeze backbone,具体看版本支持情况。
微调时容易踩的一个玄学点,是学习率调度器的选择。YOLOv7 默认的余弦退火调度在处理医疗图像时经常出现最后几个 epoch 掉点,原因是医疗图像对比度低、背景干净,梯度本身比较大,余弦退火在尾部把学习率压得过低,模型来不及从特定噪声中恢复。换用固定学习率加ReduceLROnPlateau,在验证集 mAP 不再提升时手动降一半,反而更容易砍出高精度。
最后说一个我自己的习惯:每次做完数据增强或调参,都会在同一个验证集上全量跑一次预测,把预测结果的框和原图打印成网格图,左边放 ground truth,右边放 prediction。不要只看 mAP 数值,你要看到模型在哪些图上瞎猜,哪些框是“绕过去”的侥幸正确。不然一个 mAP 从 0.7 涨到 0.75 的改动,你根本不知道它到底改变了什么行为。从那以后,我每次调参都强制走一遍可视化验证,数据、模型、阈值三个环节各盯一遍,才敢说这次训练是靠谱的。希望帮到你。
本文还有配套的精品资源,点击获取