简介:面向医学影像与深度学习研究者的X光片肺病数据集,包含800张原始胸部X光图片,并基于YOLOv8完成标注,覆盖细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎五类典型检测场景,可用于疾病自动筛查、模型训练与多标签分类研究。压缩包共1601个文件,包括800个jpg原图、800个txt标注文件和1个yaml数据集配置,整体仅25.51MB,轻量便于分享与快速部署。其中txt文件采用YOLO格式保存目标边界框坐标,yaml文件包含类别名称等配置信息,配合YOLOv8框架可直接运行训练脚本。该数据集尤其适合医学影像入门者、算法开发者和高校师生,作为基准数据检验模型在肺炎、新冠等呼吸系统疾病上的识别效果。目前已有419人学习或下载,资源附带的原图与标注一一对应,省去人工标注成本,是快速上手医学目标检测任务的实用资料。
1. 医疗影像检测为什么值得自己训练:一个 YOLOv8 标注的肺部 X 光数据集
肺病 X 光片检测方向,大部分人第一步就卡在数据上。公开数据集要么只做了图像分类级别的标注,要么格式混乱没法直接喂给 YOLOv8 训练。这份数据集一共 800 张原始胸片,已经用 YOLOv8 标注好了五个类别:细菌性肺炎、新冠病毒、正常肺、结核和病毒性肺炎,是直接从 Roboflow 生态导出的标准格式,解压后整理一下目录结构,就能跑yolo detect train。它特别适合两类人:一是做毕业设计需要真实医疗影像数据、但没法从医院拿到脱敏片子的学生;二是想用 YOLOv8 训练自己的数据集、又不想从零开始标数据的工程师。下面从数据集格式拆解开始,一路讲到训练参数、排查方法和部署导出,按这个流程走一遍,你手里的就是这个文件的完整落地路径。
2. 拆解数据集结构:800 张 X 光片、五个类别,以及 Roboflow 导出格式里的隐藏信息
拿到压缩包先别急着解压跑训练,花五分钟把文件结构看清楚,后面能少踩好几个坑。这一章我们从文件名反推数据血缘,再剖析 YOLOv8 标签文件的内容,最后写一个数据体检脚本,确认这份数据集能不能直接用。
2.1 文件名里的信息量:Roboflow 血缘与数据集切分
解压后随便打开一个文件看名字,比如1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.jpg。最后的.rf.加上一串十六进制哈希,是 Roboflow 平台导出时留下的标记,说明这份数据经历过平台上的预处理、去重和格式转换。420是原始图片在某个上游数据集里的编号,而_jpg则提示这张图在导出时才被统一转成了 JPG 格式——原始文件可能是 PNG 或者其他格式,像素尺寸也可能不一样。
同一批文件里,你会发现Normal_test-3-_jpeg.rf.xxx.jpg、Normal_val-1-_jpeg.rf.xxx.jpg这样的命名混合出现。test和val这两个词直接嵌在文件名里,说明 Roboflow 在导出时已经按比例切分过数据集。解压后大概率会看到train、valid、test三个目录,也可能是一个扁平的目录但文件名里带着切分标记。前者直接能用,后者需要你手动把文件挪到对应的子目录里去。
另外注意扩展名的不统一:部分文件名里写的是_jpg.rf.,部分写的是_jpeg.rf.,但最终落地都是.jpg。这意味着原图经历了从jpeg到jpg的格式归一化,部分图像的元数据(比如 DPI 信息)已经丢失,不影响目标检测,但如果你后期想做像素级别的分类任务(比如分割),需要重新确认图像分辨率是否一致。
# 解压后先看一级目录结构 tree -L 2 lung_dataset如果输出结果是扁平的,没有train、valid、test子目录,那就需要自己按文件名里的关键字归位。常见做法是写一个简单的 Shell 脚本,把含_test_的文件挪到test/images,含_val_挪到valid/images,其余放train/images。文件名里的血缘信息在这一步帮你省了手工分类的工作量。
2.2 YOLOv8 标签文件:五个类别 ID 与归一化坐标
每一张.jpg图片都有一个同名的.txt文件作为标签,放在对应的labels目录下。YOLOv8 的标签格式是每行一个目标框,五个字段分别是:
类别ID 归一化中心点x 归一化中心点y 归一化框宽 归一化框高比如一个标签文件的内容是:
0 0.4281 0.3571 0.0934 0.1128 2 0.7135 0.6832 0.0877 0.0945第一行的0代表细菌性肺炎,第二行的2代表正常肺。坐标值全部做了归一化,范围在 0 到 1 之间,直接除以了图像宽高,所以无论原图是 512×512 还是 1024×1024,标签都能复用。这就是 YOLO 格式的核心优势——不依赖绝对像素值,换分辨率不用重新标。
实际打开几个标签文件你会发现,大部分文件只有一行,少数文件有两行甚至三行。一张胸片里同时出现多个异常区域是正常的,比如结核病灶和胸腔积液可能同时存在。但这里有一个医疗影像特有的现象:标注框通常比肉眼看到的病灶范围大一圈。原因是肺病病灶边界模糊,标注者倾向于把不确定的区域也包进来,确保不漏检。这会直接导致训练出来的模型预测框偏大,和自然场景目标检测里那种严丝合缝的框有明显区别。如果你拿 COCO 数据集上训练出来的心理预期来评估这个模型,会觉得框得不精确,但在医疗影像场景里这恰恰是合理行为。
2.3 先做一次数据体检:类别分布、空标签和异常尺寸
正式训练之前我建议先跑一遍数据体检脚本,花两分钟把家底盘清楚。下面是基于统计脚本:
import os from collections import Counter label_dir = "lung_dataset/train/labels" image_dir = "lung_dataset/train/images" label_count = 0 empty_labels = [] box_counter = Counter() for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue label_count += 1 fpath = os.path.join(label_dir, fname) with open(fpath, "r") as f: lines = [line.strip() for line in f.readlines() if line.strip()] if not lines: empty_labels.append(fname) continue for line in lines: cls_id = line.split()[0] box_counter[cls_id] += 1 print(f"总标签文件数: {label_count}") print(f"空标签文件数: {len(empty_labels)}") print("各类别目标框数量:") for cls_id, cnt in box_counter.most_common(): print(f" 类别 {cls_id}: {cnt} 个框") class_names = { "0": "bacterial-pneumonia", "1": "covid", "2": "normal", "3": "tuberculosis", "4": "viral-pneumonia", } print("\n类别ID对照:") for cls_id in sorted(class_names.keys()): print(f" {cls_id}: {class_names[cls_id]}")这个脚本做了什么:遍历train/labels目录下所有.txt文件,统计标签文件数量,检查空标签(没有任何目标框的样本),再按类别 ID 统计目标框总数。跑完你会对数据集有一个量化认知——比如正常肺样本是不是远多于结核样本,哪一类目标框特别少。
跑出来的分布通常不会均匀。医疗影像数据集天然存在类别不均衡,结核和细菌性肺炎的样本量往往只有正常肺的一半甚至更少。这不一定是坏事,但你要在训练阶段提前知道,然后用class_weights或数据增强去平衡。空标签文件如果数量多,说明部分正常胸片没有标注任何区域,这类样本在训练时只贡献背景损失,会让模型倾向于输出更少的框。如果空标签超过 5%,建议剔除。
最后补充一个容易忽略的检查点:验证图片和标签文件是否一一对应。YOLOv8 遇到缺标签的图片不会报错,只会默默忽略,导致实际参与训练的图片数比你预想的少。常见做法是用下面这段代码快速核对:
for f in lung_dataset/train/images/*.jpg; do base=$(basename "$f" .jpg) if [ ! -f "lung_dataset/train/labels/$base.txt" ]; then echo "缺少标签: $base" fi done把三处路径替换成你自己的目录,跑一遍没输出就说明配对完整。这一步做完,数据集侧的工作才算真正结束。
3. 搭建 YOLOv8 并训练:从 CPU 跑通的 Ubuntu 环境到一套可复现的训练参数
环境搭建和训练是整个流程里最繁琐但最不容易出错的环节。这章我会一边讲环境一边讲参数,照着抄基本能跑通。重点会放在 YOLOv8 的配置上,因为医学影像数据集的训练参数和自然场景差别很大,照抄 COCO 的标准配置会后果惨淡。
3.1 环境准备:Python 虚拟环境、torch 与 ultralytics 的版本搭配
YOLOv8 的安装非常透明,核心就是ultralytics这个 Python 包。但强烈建议不要直接装到系统 Python 里,因为后续训练过程中你可能需要切换 CUDA 版本、重装 torch,没有虚拟环境的话这一通操作会搞乱系统依赖。
# 以 Ubuntu 20.04 为例,先装虚拟环境 sudo apt update && sudo apt install python3-venv python3-pip -y python3 -m venv ~/yolo_env source ~/yolo_env/bin/activate pip install --upgrade pip # 没有 NVIDIA GPU 的情况下,安装 CPU 版 torch pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 YOLOv8 本体 pip install ultralyticsCPU 版 torch 的体积比 CUDA 版小不少,安装速度快,且不影响后续代码逻辑。如果你的机器有 NVIDIA 显卡,把--index-url那一行去掉即可,默认装的就是 CUDA 版。
安装完验证一下:
python -c "import ultralytics; print(ultralytics.__version__)"能输出版本号就说明环境没问题。CPU 训练 800 张图,YOLOv8n 大约需要 2 到 4 小时跑完 100 个 epoch,属于可以接受的范畴。有 GPU 的话时间直接降到几分钟。
3.2 编写 data.yaml:路径、类别名与 train / val / test 的对照关系
训练前需要一份data.yaml告诉 YOLOv8 数据在哪里、有几个类别。这份文件是整个流程里最容易出错的地方——路径写错、类别对不上,训练时不会报错,只会默默用错数据。
# lung_dataset.yaml path: /home/user/lung_dataset # 数据集的绝对路径,不要用相对路径 train: train/images # 相对于 path 的训练图片目录 val: valid/images # 验证集目录,注意是 valid 不是 val,按实际目录名来 test: test/images # 测试集目录,可选 nc: 5 names: 0: bacterial-pneumonia 1: covid 2: normal 3: tuberculosis 4: viral-pneumonia几处定义需要特别注意。path建议写绝对路径。YOLOv8 虽然支持相对路径,但如果你在不同目录下启动训练,相对路径解析会乱掉,直接导致找不到图片然后报错。val这一行要看解压出来的目录名是valid还是val,Roboflow 导出时默认叫valid,但有些人会手动改成val,不对应就直接翻车。
names的 ID 顺序要和标签文件里的数字严格对应。如果标签文件中类别 ID 3实际是结核,但names里第 4 个写成了 viral-pneumonia,模型不会报错,但你的混淆矩阵和预测结果全会错位。最稳妥的方式是打开几个标签文件,对照实际类别名人工确认一遍,别只看下载页面提供的说明。
3.3 第一次训练跑通:模型选择、epoch、batch 和 freeze 参数说明
环境就绪、data.yaml写好后,训练命令比想象中简单。核心就一行:
yolo detect train \ data=/home/user/lung_dataset.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ patience=40 \ lr0=0.001 \ freeze=10我来逐项拆解这些参数的实际意义。model=yolov8n.pt用的是 COCO 预训练权重。有人会觉得医学影像和 COCO 场景差距太大,预训练权重没用,实际不然——底层边缘、纹理、对比度这些基础特征在两个领域是通用的,迁移学习依然能显著加速收敛,特别是在只有 800 张图这种小数据量场景下,从零训练几乎必挂。
freeze=10冻结主干网络前 10 层。YOLOv8n 的总层数大约 60 多层,前 10 层学到的是最通用的底层特征,冻结它们可以大幅减少可训练参数量,防止在小数据集上过拟合。这个参数在数据量不足 2000 张时强烈建议开启。
batch=16是显存不足时的最常用值。imgsz=640是默认输入分辨率,如果你的胸片原始分辨率是 1024×1024,可以试imgsz=1024,小病灶检测效果会有可见提升,但显存占用和训练时长会翻好几倍。CPU 环境下建议保持 640 不变。
patience=40表示连续 40 个 epoch 没有提升就早停。800 张图训练 150 个 epoch 通常在第 80 到 100 个 epoch 之间达到最优,早停机制帮你省掉后面的无效计算。
训练启动后终端会实时打印box_loss、cls_loss、dfl_loss和mAP50等指标。医学影像数据要重点关注cls_loss,因为 COVID 和病毒性肺炎在 X 光片上的视觉表现极其相似,类别预测难度远高于普通目标检测。如果看到cls_loss一直降不下来,标注质量的怀疑优先级要大过模型结构的怀疑。
训练正常完成后,最优权重在runs/detect/train/weights/best.pt。下一步就是进到排查环节——拿到一份看起来不错、实际用起来到处漏检的模型,可能比训练失败更让人头疼。
4. 训练医学影像的避坑排查:五个最容易翻车的点与对应解决方案
数据和环境就绪后,训练过程本身并不复杂,复杂的是模型效果不符合预期时你不知道该改哪里。这一章是我实际跑这个数据集时遇到的五个最典型的坑,从现象、原因和解决三个层面展开,照着排查能省下大量重试时间。
4.1 现象:loss 不断下降,但 mAP 纹丝不动
训练日志里box_loss和cls_loss都在稳步下降,看着很健康,但输出到终端的mAP50一直在 0.2 附近徘徊,怎么都上不去。这种现象通常有两个原因。
第一个原因是数据泄露:train和valid目录里混入了来自同一患者的图片,验证集不再独立,模型在验证集上的表现就没有代表性。Roboflow 导出时通常会做去重,但如果你自己补充过数据,这一步很容易漏。
第二个原因是学习率过高导致损失函数在局部震荡。cls_loss降但 mAP 不涨,往往就是优化过程出了问题。
解决步骤是先把data.yaml里path逐级检查,确认 train 和 valid 图片没有重复文件名;然后把学习率调低一档再跑一次,比如从lr0=0.001降到lr0=0.0005,同时把batch减半以稳定梯度。医疗影像的标注噪声比自然场景大,学习率稍微低一点能显著提升稳定收敛的概率。
4.2 现象:某一个类别的精确率或召回率直接是 0
验证结果里显示细菌性肺炎的recall=0,或者结核这个类别的框一个都没预测出来。真实原因通常是两个:一是这个类别的样本数太少,比如只有 20 张图,模型把它当成了背景;二是标签文件里这个类别的 ID 写错,导致类别名匹配不上。
解决办法是看数据体检脚本的输出——我在第 2 章提过,事先统计类别分布省事多了。如果样本数确实太少,优先做针对性的数据增强:旋转 30 度、水平翻转、随机缩放 0.8 到 1.2 倍,让这个小类别的样本量翻三到五倍再训练。另外在训练命令里加class_weights参数,YOLOv8 支持为少数类分配更高的损失权重,这比重复复制样本更干净。
4.3 现象:验证集 mAP 0.85,但真实图片漏检严重
模型在验证集上的指标非常漂亮,你拿几张没用过的胸片去测,却出现了整块病灶区域完全漏检。这个坑的根源是数据分布不一致。验证集和训练集来自同一个 Roboflow 导出流程,成像风格、拍摄设备、体位朝向都高度一致,模型只需要记住这种特定风格就能考高分。但真实世界的胸片可能来自不同医院的不同设备,灰度范围、对比度、正位侧位都不同,模型没见过这种分布,掉链子完全正常。
解决思路不是盲目加数据,而是结构化地收集目标场景的图片,按一定比例补充进训练集。如果你拿到的数据是正位胸片,但实际部署场景有侧位片,一定要单独收集一批侧位片加进去,否则再怎么调参数也补不上这个分布差异。
4.4 现象:显存不够,batch 只能开到 2
训练时报CUDA out of memory,把batch降到 2 才能跑,但 batch 太小导致梯度噪声大,loss 收敛得很慢。如果机器只有 4GB 或 6GB 显存,这种问题是必然的。
这时的应对顺序是:先用混合精度训练,把amp=True打开,这是最无损的优化;然后把imgsz从 640 降到 512,显存占用大约降一半,代价是 mAP 掉 1 到 3 个百分点;最后换yolov8n——它是 YOLOv8 系列里显存占用最低的模型,适合在低显存环境挖出可用的基线结果。
如果以上还扛不住,最后一个兜底方案是cache=False。默认 YOLOv8 会把图片缓存进显存加速训练,关掉后每轮从磁盘读图,训练时间变长但显存占用大幅下降。
4.5 现象:训练中段 loss 变成 NaN
训练到第 30 个 epoch,loss 突然打印成nan,然后整个训练直接终止。最常见的原因是学习率过大导致梯度爆炸,其次是标签文件里有异常数据,比如框的坐标超出 0 到 1 范围,或者标注框宽高为负数。
排查方法很直接:第一步检查所有标签文件,把坐标不在[0,1]区间的行挑出来删掉;第二步换优化器,YOLOv8 默认是AdamW,换成SGD配合lr0=0.001通常能解决不稳定的梯度问题。医学影像数据里偶尔混入一个带 NaN 坐标的标签文件并不罕见,这五个坑几乎覆盖了训练阶段的大部分翻车场景。
5. 评估与调优:用 mAP 和混淆矩阵判断模型是否真的能用
训练出的模型到底能不能用,光看训练日志里的数字不算数。这一章讲如何用验证集和测试集做系统性评估,以及在医疗影像场景里怎么调优才有意义。
5.1 跑一遍 val:混淆矩阵和每个类别的 PR 值
训练完成后,第一件事是跑验证集评估,YOLOv8 提供了一行命令:
yolo detect val \ model=runs/detect/train/weights/best.pt \ data=/home/user/lung_dataset.yaml执行完会生成一组结果文件,放在runs/detect/val/下,包括confusion_matrix.png、PR_curve.png、labels.jpg等。
混淆矩阵是评估医疗影像模型最重要的图。上面除了五个类别外,还会出现background一列。看的时候重点关注意外混淆:COVID 和病毒性肺炎在 X 光片上视觉特征高度接近,模型把 COVID 错分成 viral-pneumonia 的比例高不高,结核有没有被当成正常肺。这些错误类别的倾向性,比整体 mAP 数值更能反映模型在真实场景里的可靠性。
另外要留意labels.jpg,它展示的是训练数据的目标框分布情况。看框的尺寸分布是否符合直觉:医疗影像里的病灶框通常占整图的 5% 到 20%,如果存在大量明显小于这个比例的标注框,检查是不是误标了微小噪点。
5.2 医疗场景里,Recall 优先于 Precision 的参数取舍
自然场景目标检测追求 precision 和 recall 的平衡,医疗筛查场景则不同——漏诊一个阳性病例的代价远高于误报一个正常样本。所以评估模型时,优先级应该是 recall 高于 precision。
YOLOv8 在推理阶段提供两个核心控制参数:conf(置信度阈值)和iou(非极大值抑制的 IoU 阈值)。提高 recall 的常规做法是降低conf:
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="test_images/", conf=0.15, # 默认是 0.25,医疗场景可降到 0.1~0.15 iou=0.5, # NMS 的 IoU 阈值,低于此值的重叠框会被合并 save=True, )conf=0.15会让模型输出更多的低置信度预测框,recall 提升但 precision 下降。实际使用时可以把推理结果全部导出,逐张看被误报的样本是哪些,借此摸清模型的真实能力边界。调阈值没有标准答案,完全取决于你的业务对误报和漏诊的容忍度。
5.3 调优的三个方向:增强策略、模型大小和类别权重
在排除了数据问题之后,提升模型效果通常从三个方向下手。
方向一是调整增强策略。YOLOv8 默认开启mosaic=10,这个增强会把四张图拼成一张新的训练图,对小目标检测提升明显,但用在医学影像上反而可能有害——拼接产生的图像边界会干扰病灶区域的上下文语义,模型学到的是拼贴结构而不是病灶特征。我的建议是调低mosaic的启用概率,或者先用默认策略跑一版,再关掉对比效果。
方向二是换更大的模型。yolov8n是最小的版本,参数量约 320 万;换成yolov8s(约 1100 万参数)或yolov8m(约 2500 万参数),对微小病灶的敏感度通常会有可见提升。800 张图用yolov8m偏大,存在过拟合风险,但在freeze=10和早停的配合下仍值得一试。
方向三是用类别权重。如果体检脚本发现某个类别框数明显少于平均值,可以在训练命令里加上样本权重,让模型对少数类投入更多注意力。YOLOv8 的训练参数class_weights接收一个字典或列表,按五类数据分布按比例给权重即可。
6. 让模型走出笔记本:ONNX 导出、CPU 推理和数据集迭代方向
训练评估完成,模型能用了之后,下一个需求往往是从笔记本走到真实环境里。YOLOv8 支持导出多种推理格式,ONNX 是跨平台迁移最省事的选择。
6.1 导出 ONNX 并用 onnxruntime 做 CPU 推理
导出命令一行:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 opset=12生成的best.onnx文件不依赖 PyTorch,可以在纯 CPU 环境用onnxruntime跑推理:
import onnxruntime as ort import cv2 import numpy as np session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name image = cv2.imread("test_images/covid_sample.jpg") image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) resized = cv2.resize(image_rgb, (640, 640)) input_tensor = resized.astype(np.float32) / 255.0 input_tensor = np.transpose(input_tensor, (2, 0, 1))[None] outputs = session.run(None, {input_name: input_tensor}) # outputs[0] 的 shape 是 (1, 84, 8400),前 4 个值是中心点坐标和宽高,后 80 个是对应类别的置信度输出格式需要解释一下:YOLOv8 的 ONNX 输出是一个(1, 84, 8400)的张量,其中8400是不同尺度特征图上的候选框总数,84由 4 个边框坐标加 80 个 COCO 类别置信度组成。因为你用的是自己的五类数据集,最后一个维度实际是4 + 5 = 9,这里需要按自己的类别数调整后续解析逻辑,不能照抄 COCO 的 80 类解析代码。
如果你的部署目标是边缘设备,比如 RK3588 这类开发板,ONNX 模型还需要进一步转换。各家的转换工具链不同,但核心路径一致:先将 PyTorch 权重导出为 ONNX,再用设备厂商提供的工具优化为板端推理格式。这一步踩坑率高,建议先在 CPU 上用 ONNX Runtime 跑通推理,确认精度损失可接受,再进入板端适配。
6.2 下一轮数据集怎么补:优先补充混淆严重的类别和目标形态
模型第一版落地后,如果效果不达预期,最有效的提升方式永远是有针对性地补数据。优先补两类:一类是混淆矩阵里互相误判严重的类别,比如 covid 和 viral-pneumonia 之间的错分样本;另一类是当前模型漏检的目标形态,比如小尺寸病灶、低对比度区域、被肋骨遮挡的阴影。
从那以后,我每拿到一批医疗影像数据,都会在训练之前强制自己走一遍完整流程:先统计类别分布,再检查空标签和文件配对,最后确认混淆矩阵再决定下一步怎么做。这个流程耗时不到 20 分钟,但能省下后面至少一整天的无效调参时间。希望这次的拆解能帮到你。
本文还有配套的精品资源,点击获取