简介面向医学影像与深度学习学习者这份X光片肺病数据集提供了800张原始图片及配套的YOLOv8格式标注可支持细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎五类目标的检测与分类任务。数据来自公开来源并经统一整理含训练/验证/测试划分的命名文件适合图像分类、目标检测项目实践及模型效果对比。压缩包共1601个文件核心构成为800个jpg原图、800个txt标注文件及1个yaml配置文件整体大小25.51MB目录结构简洁便于直接接入YOLOv8训练流程。目前已有419人学习下载。通过该数据集可快速获得带标注的医疗X光图像样本省去自行采集与标注的时间也能帮助入门者理解目标检测标注规范或作为论文实验、课程设计的补充数据支撑。1. 800张X光片、五分类、YOLOv8标签这个肺病数据集到底能干什么拿到这个「X光片肺病数据集800张原始图片使用yolov8标记」压缩包很多人的第一反应是直接拖进训练脚本跑起来。我劝你先别急——这个数据集的真正价值不在「数据集」三个字而在「yolov8标记」这五个字。它不是一个单纯的分类文件夹而是一套已经用检测框标注好的YOLO格式数据你能直接喂给yolov8做目标检测训练最终模型能在胸片上框出病灶位置并区分细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎五类结果。对个人开发者、研究生和刚接触医学影像落地的人来说它解决了「想训医疗模型但找不到干净标注数据」的痛点800张的体量也刚好卡在「够训练」与「不过重」之间一张消费级显卡就能跑完整个流程。这篇文章会把这套数据从解压、核对、训练到验证的完整路径讲清楚包括那些让你血泪教训的坑。2. 数据集拆包与目录核对先把800张图的YOLO格式对齐2.1 解压后的标准目录结构长什么样YOLO系列训练对数据目录的要求非常固定不管你是yolov5还是yolov8底层吃的都是同一套「图片 同名TXT标签」的约定。下载下来的压缩包解压后我一般习惯先看一眼目录层级而不是急着搜索训练入口。常见做法是压缩包内有一层外层文件夹里面再分images和labels两个子目录images里放800张JPG或PNG原图labels里放同名TXT文件。TXT文件名与图片名必须完全一致只是扩展名不同这是YOLO最核心的匹配逻辑。unzip X光片肺病数据集.zip -d ./lung_xray cd ./lung_xray find . -maxdepth 2 -type d | sort如果find列出的目录里没有images和labels而是平铺了一堆图片和TXT也不用慌后面我会给一个自动归类的脚本。先执行这条命令确认原始结构再决定手工整理还是脚本整理。很多人在这一步图省事直接把TXT和图片混一起就配路径最后训练时yolo报「no labels found」回头检查才发现是目录映射写错了白白浪费时间。2.2 用脚本核对图片与标签是否一一对应确认目录后先跑一个完整性和配对检查。这个步骤能过滤掉「坏图、缺标签、标签文件为空、类别ID越界」四类最常见的数据脏问题。800张图不算大用Python标准库加Pillow就能扫完不需要上重型框架。import os from pathlib import Path from PIL import Image img_dir Path(images) label_dir Path(labels) # 支持常见扩展名避免漏掉格式不统一的图 imgs sorted(img_dir.glob(*.jpg)) sorted(img_dir.glob(*.png)) sorted(img_dir.glob(*.jpeg)) broken [] # 打不开的图片 no_label [] # 没有对应TXT的图片 empty_label [] # TXT存在但内容为空 bad_id [] # 类别ID不在0-4范围内 for img in imgs: try: with Image.open(img) as im: im.verify() except Exception: broken.append(img.name) continue label label_dir / (img.stem .txt) if not label.exists(): no_label.append(img.name) continue lines [line.strip() for line in label.read_text().splitlines() if line.strip()] if not lines: empty_label.append(img.name) continue for line in lines: cls int(line.split()[0]) if cls 0 or cls 4: bad_id.append(f{img.name}:{cls}) print(f总图片数: {len(imgs)}) print(f损坏图片: {broken}) print(f缺标签: {no_label}) print(f空标签: {empty_label}) print(f类别越界: {bad_id})这段脚本的逻辑分三层先验证图片本身能不能正常打开排除下载损坏或格式伪装问题再检查标签文件是否存在labels/xxx.jpg.txt和labels/xxx.png.txt这类命名错误在这里就会现出原形最后解析标签内容把类别ID超出0-4的行拎出来因为本数据集只定义了五个类ID写成5或-1的说明标注时出了错。校验通过后你才敢把数据交给训练流程否则训练到一半loss异常下不来排查起来极其痛苦。2.3 打开一个标签文件理解归一化坐标的含义YOLOv8标签文件里每一行代表一个目标框格式固定为class_id center_x center_y width height其中四个坐标值全部除以图片宽高做了归一化范围在0到1之间。拿到数据后我习惯用cat直接看一个样本标签再对照原图人工确认框的位置是否合理。cat labels/000001.txt | head -5输出大概是这样的形式0 0.5203 0.3841 0.1562 0.2235。含义是类别ID为0对应细菌性肺炎目标框中心点位于图片宽度的52%、高度的38%附近框宽约图片宽度的15.6%框高约图片高度的22.4%。坐标值全部是小数这是YOLO训练时要求的如果你的数据集里出现大于1的坐标或没有归一化的像素值基本上可以断定标注工具导出格式选错了。对于医学X光片还有一个肉眼判断的要点胸片里肺野通常位于画面中上部病灶框如果大面积落在图像四角或贴近边缘大概率是标注框画大了或者画偏了。这一步不要跳过因为YOLO对标注框的质量极其敏感框大了模型学到的是「一块区域」框小了模型学到的是「半个病灶」这两种情况都会直接体现在最终mAP上。看三五个样本你就能对这个数据集的标注风格心中有数。3. 训练配置与启动写好data.yaml第一条训练命令才能跑通3.1 划分train/val并生成YOLOv8数据集描述文件标签全部核对完之后下一步是做训练集和验证集的划分。医学影像小数据集有个特殊性同一个患者的X光片往往有多张不同视角或不同时间点的拍摄如果划分时不管来源随机切训练集和验证集里可能出现同一患者的多张片子导致验证指标虚高。稳妥的做法是尽量把划分比例控制在8:2并且按图片文件名前缀做人工去重保证同一ID的病历只出现在一个集合里。import random import shutil from pathlib import Path random.seed(42) img_dir Path(images) label_dir Path(labels) all_imgs sorted(img_dir.glob(*.jpg)) sorted(img_dir.glob(*.png)) random.shuffle(all_imgs) train_ratio 0.8 split int(len(all_imgs) * train_ratio) train_imgs all_imgs[:split] val_imgs all_imgs[split:] for split_name, imgs in [(train, train_imgs), (val, val_imgs)]: out_img Path(fyolo_{split_name}/images) out_lbl Path(fyolo_{split_name}/labels) out_img.mkdir(parentsTrue, exist_okTrue) out_lbl.mkdir(parentsTrue, exist_okTrue) for img in imgs: shutil.copy(img, out_img / img.name) label label_dir / (img.stem .txt) if label.exists(): shutil.copy(label, out_lbl / label.name) else: print(f警告: {img.name} 缺标签已跳过)这段脚本会在当前目录下生成yolo_train和yolo_val两个文件夹内部各自包含images和labels子目录正好是YOLOv8要求的布局。复制而不是移动文件保留原始数据集不受破坏是处理数据的一贯习惯万一划分出问题还能重新来过。随机种子固定为42保证每次运行划分结果一致这对后面复现指标非常重要。3.2 编写data.yaml五类目标的名字一个都不能错YOLOv8通过一个YAML文件读取数据路径、类别数和类别名称。它的格式比yolov5更严格path字段必须写清楚train和val都是相对path的路径不能随意乱指。# lung_xray.yaml path: ./lung_xray # 数据集根目录相对当前工作目录 train: yolo_train/images # 训练图片目录 val: yolo_val/images # 验证图片目录 nc: 5 # 类别总数 names: 0: bacterial_pneumonia # 细菌性肺炎 1: covid # 新冠病毒 2: normal # 正常肺 3: tuberculosis # 结核 4: viral_pneumonia # 病毒性肺炎注意类别顺序必须和标签文件里数字ID一一对应。本数据集标明的五个类别是细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎你不需要按字母排序或按疾病严重程度重排保持和原始标注一致就好。如果这里ID对不上模型训练不会报错但预测时会把「结核」的框显示成「正常肺」属于灾难级错误。存放位置我习惯放在数据集根目录里和yolo_train同级训练命令用相对路径引用整个项目移动位置也能跑。3.3 跑通第一个epoch最小可用训练命令与日志解读环境有yolov8之后训练命令本身不复杂关键是把几个核心参数一次给对。先用最小配置跑起来确认数据流没有断裂再谈调参。yolo detect train \ modelyolov8s.pt \ datalung_xray.yaml \ epochs50 \ imgsz640 \ batch8 \ projectruns/lung_ft \ namebase \ device0挑几个参数说明。modelyolov8s.pt表示加载COCO预训练权重做迁移学习医学影像和自然图像相差很大但骨干网络提取纹理边缘的能力可以迁移比从零训练收敛快得多imgsz640是训练输入分辨率对X光片来说后面可以提高到1280但第一轮先跑默认值batch8是给8GB显存显卡留的余量你的卡如果是GTX 1660 Ti这类6GB显存的型号建议改成4。训练启动后重点看前几个step的日志GFLOPs、Box PR这些指标出现且数值不为0说明数据被正确读取了如果日志直接报AssertionError: Label shape或者卡在All labels are empty回头查数据和配置别怀疑显卡。训练过程中另一个要盯的是损失曲线。YOLOv8每个epoch结束会输出box_loss、cls_loss和dfl_loss正常情况下三者呈整体下降趋势。如果loss在头几个epoch不降反升优先怀疑学习率设置出了偏差如果loss降得飞快但验证集mAP几乎没有那基本可以判断标注质量有问题回到第2章重新做数据检查。4. 训练参数怎么调小样本医学影像的batch、epoch与增强取舍4.1 imgsz是第一个要动的参数640还是1280X光片和自然图像最大的区别在于病灶区域占整图的比例很小一个早期结核空洞可能只有几十个像素。训练分辨率直接决定了这些小目标的表征上限imgsz640时一个小病灶在特征图里可能只剩一两个格子检测头能拿到的信息极其有限。我的习惯是把imgsz提到960或1280再训一轮和imgsz640的baseline对比。代价是显存占用近似平方增长imgsz1280时batch8在16GB显卡上都会紧张。如果显存不够有两个变通方案一是降低batch到2或4配合梯度累积保持有效批量二是用yolov8n替代yolov8s做分辨率测试先验证高分辨率有没有收益再回退到更宽的模型。不要一上来就同时拉高分辩率和模型容量那样翻车了都定位不到是谁的问题。4.2 小数据集的epoch设置早停不是玄学是有据可查800张图的数据量对检测任务来说属于小样本训练很容易在某个节点开始过拟合具体表现是训练loss持续下降、验证集mAP却开始回落。YOLOv8提供了patience参数做早停默认值一般是100个epoch对医学小数据集来说太宽容了建议压到20到30。yolo detect train \ modelyolov8s.pt \ datalung_xray.yaml \ epochs200 \ imgsz960 \ batch8 \ patience25 \ projectruns/lung_ft \ nameimgsz960 \ device0epochs200是上限值patience25表示验证指标连续25个epoch没有提升就自动终止。终止后模型会自动保存最后一版权重但实际我们应该用的是best.pt它在训练过程中持续追踪验证集表现最优的那个checkpoint。训练结束后看runs/lung_ft/imgsz960/下的results.png上面有loss和mAP的曲线图能直观看到早停触发的位置是否合理。如果你的曲线在patience耗尽时还在稳步上升说明25太小如果早在20轮就出现mAP高点然后一路下滑说明模型容量撑不住数据量考虑增强正则化而不是继续加epoch。4.3 batch与优化器选择小数据集上的稳定性优先batch大小在小样本训练里影响的不只是显存它还决定了Batch Normalization层的统计量是否稳定。800张图按8:2划分后训练集只有640张batch16意味着每个epoch只有40次迭代BN层的均值和方差估计在高方差环境下很容易波动导致验证集指标跳来跳去。我在这类数据量级上的顺序是先用batch8 SGD跑一版baseline再试batch16 AdamW对比两者验证集的稳定性。SGD在小数据上收敛慢但泛化通常更稳AdamW收敛快却更容易记住训练集噪声。如果你是第一次跑这个数据集别在优化器上纠结太久选SGD跑通流程记录指标后面有余力再换。weight_decay默认0.0005可以不动但如果base模型出现明显过拟合上调到0.001是常见做法。4.4 训练参数速查表参数默认值本数据集的建议调整理由imgsz640640 → 960 → 1280病灶小目标需要更高分辨率epochs100150-200配合早停小数据需要提前量patience10020-30防过拟合batch164-8视显存BN统计稳定性优先optimizerautoSGD首选小数据泛化更稳weight_decay0.00050.0005-0.001过拟合时上调close_mosaic1030-40末段关掉拼接增强pretrainedTrue保持True迁移学习收益明显close_mosaic值得单独说一下。YOLOv8默认在前10个epoch关闭马赛克增强因为马赛克拼接生成的图片和真实分布差距过大最后阶段需要让模型适应真实分布。医学影像中病灶边界对诊断极其关键马赛克增强会把不同X光片的肺野拼接在一起制造出大量生理结构不合理的训练样本所以我建议把它加到30甚至40让模型在最后三分之一的训练周期里完全回到真实图像分布。5. 避坑医学小数据集训练最容易翻车的5个地方5.1 同一患者的片子同时进了训练集和验证集mAP虚高到不真实现象验证集mAP高达0.95loss曲线也完美但拿去跑一张新X光片预测结果惨不忍睹漏检误检一堆。原因X光片数据集经常包含同一患者不同角度、不同时间点的多张影像。随机划分时这些高度相似的片子大概率被拆到两个集合里模型在训练阶段见过几乎一样的图像验证时等于开卷考试指标自然虚高。解决划分数据集前先看文件名是否包含患者ID或病例编号同一ID的图片归并后整体划分。我的习惯是按患者粒度划分保证同一个ID的所有片子全部进训练集或全部进验证集虽然验证集图片数会打完折但指标才是真实水平的反映。5.2 马赛克增强导致小病灶被拼掉验证集漏检激增现象训练loss正常下降但验证集上小病灶的recall很低尤其是早期结核和病毒性肺炎这类病灶不明显的类别。可视化预测结果发现模型对单张完整胸片的小区域完全无感。原因马赛克增强把四张图缩放到四分之一然后拼成一张原本就小的病灶被缩小到几乎不可辨认的程度检测头学习了大量无效特征。简单地说增强手段把数据人为变难了但这个难度不是模型该面对的。解决除了提高close_mosaic还可以直接调低mosaic增强概率。YOLOv8超参数配置里可以通过加载hyp.yaml修改把mosaic从默认的1.0降到0.5保留一定的拼接多样性但不至于摧毁小目标。对医学影像我的经验是mosaic可有可无关闭后验证集mAP经常反而上涨。5.3 类别不均衡正常肺样本占比过高少数类直接被无视现象正常肺类别有400张细菌性肺炎只有50张。训练后正常肺的AP很高其余四类的AP低得离谱尤其是数据集里占比最小的病毒性肺炎几乎完全不被模型识别。原因YOLO的损失函数是按样本量天然加权的多数类在训练中主导梯度方向少数类的学习信号被淹没。解决先别急着改损失函数。第一件事是统计每类目标的框数量画出类别分布直方图。如果差异没有超过10倍优先尝试数据增强层面解决比如对少数类做翻转、亮度扰动、随机裁剪扩充如果差异超过10倍给少数类对应的损失权重乘一个系数YOLOv8的cls参数可以调但这属于后手操作。更简单的做法是确认数据集的类别定义有没有套错很多「类别不均衡」其实是标注阶段把两个类合并成了一个类。5.4 标签框标注过宽把整个肺野框了进去现象训练loss很低但可视化预测结果时发现预测框喜欢把整个肺部区域框住而不是紧贴病灶边界框的宽高比看起来异常统一。原因标注者为了省事把病灶周围的正常组织也包进去了或者用的是「框整片阴影」的策略而不是「框病灶核心区」的策略。YOLO的回归目标完全由标注决定你给的框多大模型就学多大。解决这就是我一直强调在训练前要人工看标签的原因。用OpenCV把标注框画到原图上抽查20张看标注风格是否统一框和病灶边缘的贴合度如何。如果发现标注确实偏宽有两个选择一是重新标注成本高二是用脚本把框向内收缩5%到10%通过统一缩框算法修正标注风格。第二种方法虽然粗暴但在标注一致性差的数据集上效果意外地好。5.5 训练中断后从头再来白白浪费十几个小时现象训练到40个epoch断电或显存溢出导致进程退出。重启后直接重新敲yolo detect train结果从头开始训练之前十几个小时全是白费。原因YOLOv8的恢复机制是显式的训练过程中会在runs/lung_ft/base/weights/下定期保存last.pt但重启训练时如果你不指明用这个权重它会默认加载预训练权重重新开始。解决中断后不要慌用一条命令就能接上进度。yolo detect train resume modelruns/lung_ft/base/weights/last.ptresume参数会自动读取训练状态文件恢复当时的epoch、优化器状态和学习率调度位置。前提是中断前目录结构没被动过数据路径也别改否则会报路径找不到。养成训练前确认磁盘空间足够、显存没有其他任务占用的习惯能从源头上减少中断。6. 训练完怎么验证conf阈值、混淆矩阵与漏检复盘模型跑完别急着打包交付。先跑一次官方验证脚本拿到结构化指标再单独做推理可视化两边结合才能判断模型能不能用。yolo detect val modelruns/lung_ft/base/weights/best.pt datalung_xray.yaml imgsz960val会自动输出每个类别的Precision、Recall和mAP50-95同时保存混淆矩阵图。看结果时我的习惯是先看对角线对角线上数字越接近1越好再看「正常肺」被误判成「细菌性肺炎」的交叉位置这种误判在临床上最不能接受。如果交叉项明显回到训练端提高对应类别的权重或者增加该类样本。单张推理验证做最后的把关。写一段脚本把预测框画出来人工过一遍视觉效果这一步能发现指标看不出的问题比如定位偏移、重复框、置信度异常低却预测正确的框。from ultralytics import YOLO import cv2 model YOLO(runs/lung_ft/base/weights/best.pt) img cv2.imread(sample_xray.jpg) results model.predict(img, conf0.4, iou0.5, imgsz960) for r in results: for box in r.boxes: cls int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 [round(v) for v in box.xyxy[0].tolist()] label f{r.names[cls]} {conf:.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(result.jpg, img)conf0.4是置信度阈值调低会召回更多病灶但同时带来更多误检调高则反之。我会把验证集上的20张图分别用0.3、0.4、0.5跑一遍挑出视觉上漏检最少又不会满屏乱框的那个阈值。iou0.5是用来去重相邻框的NMS阈值如果模型对同一病灶输出多个重叠框适当降低这个值能减少重复输出。做完这一套流程模型能不能用你自己心里就有数了。mAP只是一个数字真正决定模型能否投入使用的是它在陌生X光片上的表现。我现在拿到任何新数据集都会强制自己先跑一版baseline和一组数据质量检查再做优化这两步看着浪费时间却省掉了无数个在玄学调参里打转的夜晚。希望这套流程对你有用能帮你把这个800张图的数据集跑出可信的结果。本文还有配套的精品资源点击获取