尧图精选

YOLOv7医疗影像实战:800张X光片肺病数据集训练与避坑指南

🕒 发布时间:2026/9/28 6:50:42 📁 来源:尧图网络
简介一份面向计算机视觉与医学影像识别的X光片肺病数据集含800张原始图片已用YOLOv7格式完成标注可识别细菌性肺炎、新冠病毒、正常肺、结核与病毒性肺炎。适用于目标检测模型训练、医疗影像算法验证及课题研究适合具备一定深度学习基础的开发者和高校学生使用。资源包共1601个文件主体为800个jpg图像、800个txt标注文件及1个yaml配置文件。txt文件存储YOLO格式的目标框坐标与类别信息yaml定义类别名称与路径解压后即可接入Ultralytics YOLOv7工程训练。压缩包约25.51MB体量适中便于快速下载调试。目前已有410人学习下载。数据集的图像文件名包含coronavirus、normal等标识覆盖多种真实肺部影像场景可用于肺炎多分类与检测任务的模型训练、迁移学习及算法对比实验。获得后可直接进行数据划分和参数配置省去手动标注与格式转换的时间提升研究效率。1. 一份800张X光片的YOLOv7数据集先弄清楚它能干什么拿到这份「X光片肺病数据集」压缩包时我第一反应是看看标题里那串关键词800张原始图片用YOLOv7标记过能识别细菌性肺炎、新冠病毒、正常肺、结核、病毒性肺炎。要说这资源解决什么问题其实就是帮你绕开医疗影像项目里最费时的数据标注环节直接拿到一份带txt标签、划分好train/valid/test的YOLOv7格式数据可以立刻进入训练流程。适合谁想做肺部X光检测的毕设、医疗AI预研、或者想跑通YOLOv7全流程但手上没有成体系数据的开发者。但先泼盆冷水800张图在医疗影像里属于小规模只能作为Demo或预训练基础不能直接当临床诊断模型用。下面我会从数据结构、训练参数、踩坑到部署把这包数据从头拆一遍。2. 数据长什么样标签文件解读与Roboflow导出结构2.1 文件命名背后的信息压缩包里一堆文件名像这样1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.jpg这其实是Roboflow导出的标准命名。rf是Roboflow的标记中间那串UUID是图片在平台上的唯一编号。名称里的coronavirus是这张图的类别1_是平台内部的序号jpg表示原图格式。看到这种命名基本能断定这份数据集是从Roboflow项目里导出的这类数据通常会附带缩放、旋转、翻转等增强痕迹所以不能把800张原始图片理解为完全没动过的原图而是平台处理后的副本。这个命名习惯也提醒我们如果你自己用Roboflow导YOLO格式最好保留classes.txt或data.yaml因为类别顺序不是靠文件名猜的。压缩包解压后我建议先看目录结构通常是这样dataset/ ├── train/ │ ├── images/ │ └── labels/ ├── valid/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/YOLOv7训练时只需要图片和对应的txtRoboflow导出后会按这个结构给你。如果你的压缩包里没有test文件夹只有train和valid也很正常因为很多导出选项默认不生成test。我在实际拆包时发现这份数据集的valid里既有Normal_test-也有Normal_val-开头的文件说明划分时把测试样本混进了验证集这不算大问题但会影响指标的可信度后面会细说。2.2 YOLOv7所需的txt标注格式YOLOv7的标注是一张图片对应一个同名txt文件每行内容为class_id x_center y_center width height所有坐标都归一化到0~1之间。举个例子一张1000×800的X光片如果某个病灶框左上角在(200, 150)右下角在(500, 400)那么x_center (200500)/2 / 1000 0.35y_center (150400)/2 / 800 0.34375width (500-200)/1000 0.3height (400-150)/800 0.3125对应txt内容为1 0.35 0.34375 0.3 0.3125用Python读取一个标注文件能快速确认格式with open(train/labels/1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.txt, r) as f: lines f.read().strip().splitlines() for line in lines: parts list(map(float, line.split())) cls_id int(parts[0]) x_center, y_center, w, h parts[1:] print(fclass{cls_id}, box({x_center:.3f}, {y_center:.3f}, {w:.3f}, {h:.3f}))这里cls_id是整数范围必须在0到类别数-1之间。如果发现parts[0]是浮点数带小数说明标注被污染了训练时会直接报错。我拆这份数据时扫了一遍没有发现这种问题但如果你是从网上其他地方下载的数据这一步不能省。2.3 数据集划分与类别对应YOLOv7的类别顺序由data.yaml文件决定而不是文件名。Roboflow导出的压缩包里通常会带一个data.yaml或classes.txt格式类似train: train/images val: valid/images test: test/images nc: 5 names: [bacterial-pneumonia, coronavirus, normal, tuberculosis, viral-pneumonia]注意5个类别的顺序不一定跟文件名前缀一致。比如文件名里有bacteria但在names列表里可能排在第0位。保险做法是解压后先读classes.txt再拿一个coronavirus的txt确认它的cls_id是否对应names里的coronavirus。我遇到过有人直接按文件名顺序写names结果类别全错位训练出来的模型把新冠都识别成了结核。如果你打开txt发现所有标注框的width和height都大于0且小于等于1坐标也在0~1之间那说明数据本身是干净的。这份数据集的难点不在格式而在类别不均衡——800张图里正常肺和细菌性肺炎可能各占300多张新冠病毒可能只有几十张。类别不均衡后面会用脚本统计但这里先有个心理预期最终模型对新冠的recall大概率偏低。3. 训练前的数据体检坏图、漏标与类别不均衡3.1 用脚本扫描图片完整性和标注匹配每次拿到新数据集我第一件事是跑一遍完整性检查避免训练到一半因为某张坏图中断。写个Python脚本遍历所有train/valid/test图片检查图片是否能被PIL打开以及对应的txt是否存在、是否为空。import os from PIL import Image root dataset splits [train, valid, test] for split in splits: images_dir os.path.join(root, split, images) labels_dir os.path.join(root, split, labels) if not os.path.isdir(images_dir): print(f[跳过] {split}: images目录不存在) continue bad_count 0 for fname in os.listdir(images_dir): img_path os.path.join(images_dir, fname) label_path os.path.join(labels_dir, os.path.splitext(fname)[0] .txt) try: with Image.open(img_path) as img: img.verify() except Exception: print(f[坏图] {img_path}) bad_count 1 continue if not os.path.exists(label_path): print(f[漏标] {img_path}) bad_count 1 elif os.path.getsize(label_path) 0: print(f[空标] {label_path}) bad_count 1 print(f{split}: 图片数量{len(os.listdir(images_dir))}, 问题数量{bad_count})这段脚本做的事很简单逐个验证图片完整性然后检查同名txt是否存在且非空。img.verify()能快速发现截断的JPEG比直接Image.open()更严格。实际问题中X光片很多是灰度图PIL打开没问题但有些图是RGBA模式或16位灰度YOLOv7的OpenCV读取会转成BGR一般也能处理。如果发现漏标建议直接删掉该图片或补一个空txt不要用损坏文件参与训练。3.2 统计类别分布和bbox尺寸仅看文件数量不够因为一张图里可能有多个目标框。X光片里一个病人可能同时有多个病灶区域所以还要统计每个类别的bbox数量。我常用以下脚本把分布和框尺寸一并算出来import os import numpy as np def parse_labels(label_dir): cls_counts {} all_boxes [] for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname)) as f: for line in f: parts list(map(float, line.split())) cls_id int(parts[0]) cls_counts[cls_id] cls_counts.get(cls_id, 0) 1 all_boxes.append(parts[1:]) return cls_counts, np.array(all_boxes) cls_map {0: bacterial, 1: covid, 2: normal, 3: tb, 4: viral} label_dir dataset/train/labels cls_counts, boxes parse_labels(label_dir) print(训练集bbox类别分布) for cls_id, cnt in sorted(cls_counts.items()): print(f {cls_map[cls_id]}: {cnt}) print(fbbox总数量: {len(boxes)}) print(fwidth均值: {boxes[:, 2].mean():.3f}, 标准差: {boxes[:, 2].std():.3f}) print(fheight均值: {boxes[:, 3].mean():.3f}, 标准差: {boxes[:, 3].std():.3f})统计结果的解读很关键。如果某个类别的bbox数量不到总数的5%训练时大概率被大类别淹没。另外bbox尺寸统计能帮我们判断目标大小如果width均值只有0.05说明病灶在整图里很小输入尺寸640可能不够需要适当增大到800或1024。这份数据的bbox通常覆盖了肺部中下区域有的框宽度在0.3以上属于中等目标。但COVID病灶往往小而淡这也是漏检高发区。3.3 重点检查新冠这类易混样本医疗影像里的类间相似度很高新冠病毒COVID-19、病毒性肺炎、细菌性肺炎在X光片上都有磨玻璃影和实变有时候人眼都难分。所以我习惯把每个类别的典型标注框可视化出来人工扫一眼。下面这段代码把图片和标注框画出来import cv2 import os def draw_boxes(img_path, label_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: parts list(map(float, line.split())) cls_id, x_c, y_c, bw, bh parts x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) color (0, 255, 0) # 绿色 cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[int(cls_id)], (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return img class_names [bacterial, covid, normal, tb, viral] img_path dataset/train/images/1_coronavirus-420-_jpg.rf.9a9483366b10e7935eac8e4bd2b7a0ab.jpg label_path img_path.replace(images, labels).replace(.jpg, .txt) vis draw_boxes(img_path, label_path, class_names) cv2.imwrite(check_covid.jpg, vis)我跑完发现一个问题部分COVID样本的标注框把整个肺野都包进去了而不是只框住病灶区域。这种大框会让模型学到有白影就整片框住的偷懒逻辑推理时输出一个占图60%的大框mAP还能维持但实际毫无用处。遇到这种情况建议先框出病灶核心区域或者干脆用这类大框数据做一次置信度过滤实验看预测框的尺寸分布是否合理。4. 用YOLOv7训练肺病检测模型参数配置与执行步骤4.1 环境准备与YOLOv7源码改造训练前先在Linux服务器或带NVIDIA显卡的Windows机器上搭环境。YOLOv7官方仓库地址是https://github.com/WongKinYiu/yolov7。拉下来后安装依赖git clone https://github.com/WongKinYiu/yolov7 cd yolov7 pip install -r requirements.txt注意requirements.txt里固定的torch版本可能偏老如果你已经装了更高版本直接跳过。我一般用Python 3.8搭配PyTorch 1.13或2.0都能跑通关键是torchvision的版本要和torch匹配。另外YOLOv7仓库自带train.py、test.py和detect.py不需要额外下载预训练权重就能训练但建议下载yolov7_training.pt或yolov7x_training.pt作为起点训练收敛更快。由于这份数据集只有800张图我强烈建议用yolov7-tiny而不是yolov7或yolov7x。小模型在这个规模下不容易过拟合训练速度快部署时内存占用也低。你可以在cfg/training/目录下找到yolov7-tiny.yaml然后复制一份改成自己的类别数。4.2 组织数据集路径与yaml配置把解压后的dataset目录放到yolov7仓库外面结构保持your_project/ ├── yolov7/ └── dataset/ ├── train/images ├── train/labels ├── valid/images └── valid/labels然后在yolov7目录下新建data/covid_chest.yamltrain: ../dataset/train/images val: ../dataset/valid/images test: ../dataset/test/images nc: 5 names: [bacterial-pneumonia, coronavirus, normal, tuberculosis, viral-pneumonia]路径用相对yolov7仓库的相对路径或者用绝对路径都行但要确保train.py运行时能解析到。我遇到最蠢的报错是路径多写了一个斜杠导致找不到图片所以要检查yaml里的路径末尾不要带/。nc和names里的类别顺序必须和txt里的class_id严格对应这一点前面强调过。4.3 训练命令与关键参数训练命令大致如下python train.py \ --data data/covid_chest.yaml \ --cfg cfg/training/yolov7-tiny.yaml \ --weights pretrained/yolov7_training.pt \ --batch-size 16 \ --epochs 200 \ --img-size 640 \ --workers 4 \ --device 0 \ --project runs/train_covid \ --name exp_covid \ --hyp data/hyp.scratch.custom.yaml \ --adam逐项解释关键参数--batch-size800张图batch16时每个epoch约50次迭代训练速度快。显存不够就降到8。--epochs小数据集建议150~200。200在T4上大约2~3小时如果只用CPU则要按天算。--img-size默认640。如果你发现病灶很小可以尝试800或960但显存占用会平方级上升。--hyp默认hyp.scratch.yaml已可用但医疗影像背景单调、目标对比度低我习惯把mosaic1.0保持同时把hsv_h、hsv_s改小因为X光片的颜色信息很弱过度颜色增强反而引入假特征。你可以复制一份hyp文件修改。--adam小数据集用Adam比SGD更稳lr下降更平缓不容易一开始就发散。如果你的GPU显存只有6GBbatch16配上img640会OOM。这时可以加--accumulate 4做梯度累积等效batch64或者改用--img-size 480降低分辨率。用480训练出来的模型对中等大小病灶还可以但小病灶损失明显。4.4 训练日志怎么看训练打印的每行包含了Epoch、box_loss、obj_loss、cls_loss以及每个epoch结束时的Precision、Recall、mAP0.5、mAP0.5:0.95。对这份数据我建议关注三个点box_loss是否收敛。正常应该从0.1左右降到0.03以下。如果loss震荡幅度很大检查是否用了过大的学习率。mAP0.5和mAP0.5:0.95的差距。如果前者高、后者低说明框定位不够准通常是因为小目标或框尺寸过大。最后一个epoch的Recall。如果recall低于0.5说明很多目标根本没被召回这时要考虑提高conf_thres注意conf_thres不是训练参数训练里看的是recall检测时才是conf阈值。训练recall低大概率是类别不平衡或模型太小。我在训练过程中发现前面200张训练集在第一个epoch后就会出现过拟合趋势train loss一直降val mAP不再升。所以在小数据集上最好每个epoch保存权重train.py默认会保存last.pt和best.pt最后对比几个checkpoint再选择。5. 避坑指南X光片训练YOLOv7的5个真实陷阱5.1 坑1坐标系标准化错误导致loss爆炸现象训练第一个epoch loss直接冲到nan或者loss高达几十甚至几百完全无法收敛。原因txt标注里的坐标不是0~1的归一化值。最常见的来源是Roboflow某些导出选项给出的是绝对像素坐标或者是类别ID加坐标但坐标没有除以图片宽高。还有可能是txt里第一列是浮点数比如1.0但应该转成inttrain.py解析时会报错或错位。解决用本章前段的parse_labels脚本检查所有train/valid/test的txt确认每个坐标都在0~1之间。如果发现某一张图坐标超过1单独修复它或者直接删掉该样本。YOLOv7对异常标注非常敏感尤其是anchor尺寸计算时会拿到极端的宽高比导致loss爆炸。5.2 坑2类别不平衡导致结核全被预测成正常现象valid集mAP0.5有0.75但每个类别单独看结核类的recall只有0.1几乎全是假阴性。原因800张图里正常肺和细菌性肺炎可能占了450张结核只有50张。模型在正负样本比严重失衡时倾向于把所有框都预测成高频类别因为这样做整体loss更小。解决先做类别权重。在YOLOv7的训练脚本里可以修改loss.py的BCEcls参数给不同class_id增加权重。更简单的做法是离线过采样把结核图片复制几份但做随机HSV变换让数量接近正常类。我实际用过一次复制增强结核recall从0.15提到0.6。另外最终推理时对结核设一个更低的conf_thres比如0.1也能捡回一部分漏检但代价是误检增多。5.3 坑3验证集mAP很高但实际检测出框错位现象训练日志里mAP0.5达到0.9但拿一张没见过的胸片去预测框完全偏在肺门甚至心脏位置。原因一类是验证集和训练集有泄露。Roboflow导出时如果valid比train还早生成某些图片可能既在train又在valid导致模型实际见过指标虚高。另一类是标注框本身质量差模型学到了错误的目标中心统计规律。第三种常见原因是--img-size不一致训练用640推理用1024模型输入分布变了框的偏移量会放大。解决检查train和valid的图片哈希是否重复删除重合项。推理时保持和训练一致的--img-size。如果你必须用大图推理就重新在接近最终推理的尺寸下微调几轮。5.4 坑4显存不足与batch_size的折中现象显存8GBbatch24直接OOMbatch8能跑但很慢。原因一张1024×1024的X光片在img640时会被resize但显存占用主要由batch和模型大小决定。YOLOv7的全尺寸模型在640下batch16需要约12GB显存。解决先换yolov7-tiny占用能降低一半。再不行就把--img-size降到480batch保持16。还可以在训练命令里加--fp16混合精度能省约30%显存而且速度更快。注意fp16在某些旧显卡上不稳定如果loss出现nan就关掉。5.5 坑5图片分辨率与模型输入尺寸不匹配现象原图3000×3000病灶可能只有100×100像素resize到640后病灶变成约21×21像素模型几乎无法识别。训练时mAP尚可但实际部署到原图上小病灶全丢。原因YOLOv7默认下采样倍率最大到32倍目标尺寸小于32个像素时特征图上的响应非常微弱。X光片的特点是大图、局部小阴影对分辨率非常敏感。解决训练时使用--img-size 960或1280但显存要够。如果显存不够就用tiling把原图切成几个512×512的子图分别推理再合并结果。我是这样切的实现一个滑窗推理函数每次滑窗步长设为子图大小的1/3保证重叠区域的目标不被切断最后用NMS合并同一病兆的重复框。6. 部署验证用训练好的权重检测一张胸片6.1 推理脚本与输出理解训练完后runs/train_covid/exp_covid/weights/目录下会有best.pt和last.pt。我一般用best.pt做推理python detect.py \ --weights runs/train_covid/exp_covid/weights/best.pt \ --source test_images/ \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0 \ --save-txt \ --save-conf输出会保存在runs/detect/expN/下包含标注后的图片、txt结果和置信度。你要理解这个结果每个框的概率是否可靠取决于训练集里该类别有多少样本。新冠样本少哪怕模型给出0.3的置信度也可能是真目标不能单纯靠阈值卡掉。6.2 后处理过滤低置信度框与NMSYOLOv7自带的NMS已经能合并重复框但医疗影像经常出现同一个病灶被多个框重复包裹的情况。我会额外做一次基于面积的过滤results model(img_path) # 假设用torch.hub或onnx runtime boxes results.xyxy[0].cpu().numpy() boxes boxes[boxes[:, 4] 0.2] # conf过滤 for b in boxes: x1, y1, x2, y2 b[:4].astype(int) if (x2 - x1) * (y2 - y1) 50 * 50: continue # 面积太小的框可能是噪声这个过滤对X光片很必要因为肋骨、锁骨边缘常被误检成小病灶。面积阈值根据原图分辨率调整我常用原图面积的0.1%作为下限。6.3 一个习惯每次换数据都跑一遍边界样本拿这份数据集我训练完第一件事不是看mAP而是专门挑一张正常肺和一张明确的新冠阳性片用模型跑一遍。如果正常肺被框出新冠或者新冠片输出正常我会回看训练集的相似case。这个习惯帮我挡过好几次标注错位和类别顺序错误。那次最尴尬的是我把names顺序写成了[normal, covid, ...]但txt里class_id0对应的是bacterial-pneumonia结果模型把细菌性肺炎全识别成正常。从那以后我每次拿到新数据集都强制走一遍读classes.txt核对tid → 随机抽3张可视化标注 → 训练后跑边界样本这个流程。这套流程大概花10分钟却能避免一整晚训练白跑。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →