☰
超声波肾脏结石检测:YOLOv5数据集训练与避坑指南
2026/10/1 6:18:33 网站建设 项目流程

简介:超声波肾脏结石检测YOLOv5数据集,涵盖训练集与验证集标注,面向医学影像目标检测开发者和研究者,适用于基于超声图像的肾脏与结石识别场景。压缩包为rar格式,约19.73MB,共2000个文件,其中包含1999个YOLO格式的txt标注文件与1个py可视化脚本,标注类别为结石和正常肾脏,并附有类别文本信息。数据集按照YOLOv5目录结构整理,训练集对应2564张超声图像的标注,验证集对应183张超声图像的标注,图像均为250x250分辨率的RGB超声图片,边界框标注清晰,无需额外格式转换即可用于目标检测模型的训练与验证。包内提供的show.py可视化脚本无需修改即可运行,传入任意一张图片即可绘制并保存边界框,便于快速检查标注质量。目前已有454人学习下载,适合需要现成医学超声检测数据集的入门与进阶用户。

1. 超声波肾脏结石检测:为什么这个数据集比通用目标检测集更难跑

做过超声影像的工程师都知道,肾脏结石检测这件事,难点压根不在模型结构,而在数据本身。B 超图像天生噪声大、边界模糊,结石在声像图里往往只是几毫米到十几毫米的强回声团,后面还拖着一道声影伪影,搞不好连医生都要反复扫查才能确认,想让模型第一次训练就抓住这个特征,靠通用 COCO 数据集那套思路基本行不通。这份 YOLOv5 超声波肾脏结石检测数据集就是冲着这个场景来的:两个类别、配好了训练集和验证集,标签直接是 YOLO 格式,下载解压之后不需要重新标注,改一下配置就能喂给 YOLOv5 训练。适合正在做医学图像目标检测落地的工程师、做超声辅助筛查相关课题的学生,以及想快速跑通 YOLOv5 完整流程但手上没有医学图像素材的人。

2. 目录结构与标注格式:先摸清数据集的底细再动手

拿到数据集的第一件事不是急着训练,而是把目录结构和标注格式核对一遍。YOLOv5 对数据摆放方式有固定约定,数据集的根目录组织结构通常会按照 images 和 labels 两部分展开,这两个目录各自再分 train 和 val。下面是一份典型的超声波肾脏结石数据集布局,绝大多数公开整理的 YOLO 数据集都遵循这个套路。

2.1 images 与 labels:训练集和验证集各就各位

kidney_stone_dataset/ ├── images/ │ ├── train/ │ │ ├── patient01_frame001.png │ │ ├── patient01_frame002.png │ │ └── ... │ └── val/ │ ├── patient09_frame001.png │ └── ... ├── labels/ │ ├── train/ │ │ ├── patient01_frame001.txt │ │ └── ... │ └── val/ │ └── ... └── data.yaml

这套结构里,images 和 labels 下方都带 train 与 val 子目录,一张 PNG 图像对应一个同名 TXT 标签文件。拿到数据集后,我一般先跑一个核对脚本,确认每张图都有对应标签、标签行数不等于零,免得训练到一半才发现某个文件名对不上。

2.2 两个类别到底指什么

标题里写的是“2 类别”,落到具体任务上,通常指的是 stone(结石)和 kidney(肾脏区域),前者是你要检测的目标,后者是器官上下文,帮助模型理解结石位于肾脏内部而不是别的脏器。不同数据集在类别定义上可能略有差异,常见的另一套定义是把“含结石肾脏”和“正常肾脏”作为两类,所以动手前一定要打开标签文件看一眼类别 ID 对应的实际含义。

# 查看一个标签文件的内容 cat labels/train/patient01_frame001.txt

一个典型文件内容长这样:

0 0.514843 0.323437 0.076563 0.051562 1 0.487500 0.543750 0.310938 0.265625

每一行的五个数字分别代表:类别 ID、归一化中心点 x、归一化中心点 y、归一化宽度、归一化高度。像素坐标都要除以图像宽高归一化到 0~1 区间,这是 YOLO 系列约定俗成的格式。如果看到某行数字大于 1 或者小于 0,说明标签在整理时出了问题,训练前必须清理,不然 loss 直接飞掉。

2.3 先做一次标签分布统计再决定训练策略

超声波图像里的结石通常是小目标,但到底小到什么程度,不能靠猜,要靠统计。我习惯在训练前写一段脚本,把所有训练集标签的框宽高和类别分布过一遍,生成柱状图,这个操作能帮你判断后面对 anchor 和输入尺寸该做什么调整。

import os import numpy as np label_dir = "labels/train" class_names = ["stone", "kidney"] class_counts = {} box_sizes = [] for file in os.listdir(label_dir): if not file.endswith(".txt"): continue with open(os.path.join(label_dir, file), "r") as f: lines = f.readlines() for line in lines: parts = line.strip().split() cls_id = int(parts[0]) w = float(parts[3]) h = float(parts[4]) class_counts[cls_id] = class_counts.get(cls_id, 0) + 1 box_sizes.append((w, h)) for cls_id, count in class_counts.items(): print(f"Class {cls_id} ({class_names[cls_id]}): {count} instances") box_sizes = np.array(box_sizes) print(f"Box width - mean: {box_sizes[:, 0].mean():.4f}, " f"min: {box_sizes[:, 0].min():.4f}, max: {box_sizes[:, 0].max():.4f}")

这段脚本做了三件事:按类别统计样本数量,计算所有标注框的归一化宽高,最后输出宽高的均值、最小值和最大值。输出结果直接决定后面的超参数方向。比如最小框宽只有 0.02 的话,意味着在 640×640 的输入下这个框大约只有 13 个像素,这种尺寸的结石必须靠高分辨率输入或者调整 anchor 才能抓住,按默认配置硬练大概率漏检。

3. 接入 YOLOv5 训练:data.yaml、超参数与第一次训练

数据集结构确认无误后,下一步就是把它接到 YOLOv5 的训练流程里。这一章把从编写 data.yaml 到跑通 train.py 的完整路径走一遍,重点说清楚哪些参数在医学超声场景下需要额外留意。

3.1 data.yaml:让 YOLOv5 认识你的数据集

YOLOv5 靠一个 YAML 文件描述数据集路径和类别信息,训练脚本不关心数据集放在哪,只认这个文件。针对这份肾脏结石数据集,data.yaml 的内容如下:

train: ../kidney_stone_dataset/images/train val: ../kidney_stone_dataset/images/val nc: 2 names: ["stone", "kidney"]

train 和 val 指向的是图像目录,不是标签目录,YOLOv5 会自动把路径里的 images 替换成 labels 去读取标签文件。如果解压后的目录名跟上面不一致,务必改成实际路径。nc 是类别数量,2 个类别就写 2;names 列表顺序必须和标签文件里的类别 ID 一一对应,tag 里 ID 为 0 的类对应 names 第一个元素,ID 为 1 对应第二个,顺序反了训练不会报错,但测试结果会完全错乱。

3.2 超参数怎么设:从 imgsz 到 anchor 的选择

超声波结石是小目标检测,输入分辨率直接决定模型能看到多小的结石。YOLOv5 默认 imgsz=640,对于一般场景够用,但在超声图像上,结石的像素尺寸往往只有十几个像素,我第一轮通常直接用 640 起步,先把基线跑出来,再考虑要不要提到 960 或 1280。

anchor 是另一个关键点。YOLOv5 支持自动学习 anchor,训练命令里加--noautoanchor会禁用这个功能,不加则会基于你的数据集重新聚类 anchor。对于这种框尺寸跨度大的医学数据集,建议第一次训练时先不加--noautoanchor,让 YOLOv5 在训练前用 k-means 对标签框做聚类,生成更适合结石尺度的初始 anchor。

python train.py \ --data data/kidney_stone.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --project runs/kidney_stone \ --name exp_baseline

命令里值得解释的是--weights yolov5s.pt。这里用的是 COCO 预训练权重做迁移学习,虽然 COCO 里没有超声图像,但浅层特征提取能力是可以迁移的,能让模型在较少训练数据下更快收敛。如果你的图量比较少,用 yolov5s 比 yolov5l 更稳,参数量小不容易过拟合。batch 大小主要看显存,16 这个值在 12GB 显存下配合 640 分辨率基本安全,显存紧张就降到 8,没必要硬撑。

3.3 训练过程的检查节点

训练跑起来之后,重点盯三个指标:train/box_loss是否在稳步下降、val/obj_loss是否有明显波动、以及每个 epoch 之后的验证集 mAP。超声图像噪声大,box_loss 在最初的 20 个 epoch 里振荡是正常的,不要看到曲线上下跳就急着调参。

# 训练结束后查看结果 ls runs/kidney_stone/exp_baseline/

训练完成后,这个目录下会生成 weights 文件夹,里面是 best.pt 和 last.pt,以及一系列结果曲线图。best.pt 是验证集表现最好的权重,后续推理优先用这个。如果训练到一半发现 loss 不降,先别调模型,回去检查标签文件里有没有异常的框——比如宽度为 0 或者超出图像边界的标注,这类脏数据在医学数据集里相当常见。

4. 针对超声图像的预处理与增强策略:灰度一致性、mosaic 与尺度取舍

超声波图像和自然图像在特征分布上有本质差别,直接把 YOLOv5 默认的数据增强套上去,短期看不出问题,等模型上线面对真实超声设备时就会露馅。这一章说三个必须调整的点。

4.1 灰度图的通道处理:别让模型学到伪彩色特征

超声原始图像是单通道灰度图,数据集里存储的 PNG 可能是三通道的,也可能是单通道的。如果训练集里混了两种通道数,模型会学到“图像的 RGB 通道分布”这种无关特征,换一台超声设备就失效。最简单的做法是统一转成三通道灰度,再做归一化。

import cv2 import os def normalize_grayscale(src_dir, dst_dir): os.makedirs(dst_dir, exist_ok=True) for filename in os.listdir(src_dir): if not filename.lower().endswith(('.png', '.jpg', '.jpeg')): continue img = cv2.imread(os.path.join(src_dir, filename), cv2.IMREAD_GRAYSCALE) # 将单通道灰度图复制为三通道,保持维度兼容 img_3c = cv2.merge([img, img, img]) cv2.imwrite(os.path.join(dst_dir, filename), img_3c) print(f"Processed: {filename} -> shape {img_3c.shape}") # 示例:对训练集做灰度归一化 normalize_grayscale("kidney_stone_dataset/images/train", "kidney_stone_dataset/images/train_3c")

这段代码把图像按灰度读取,再复制成三通道,明确告诉模型“颜色信息不存在,三个通道只是兼容性填充”。对超声数据来说,色调和饱和度没有物理意义,而灰度图像的纹理和亮度梯度才是关键特征,与其依赖 YOLOv5 自带的 HSV 增强引入无意义的颜色扰动,不如从源头统一通道数。

4.2 mosaic 增强要降低概率:肝脏能拼,结石不能切

YOLOv5 默认开启 mosaic 数据增强,每张训练图由四张图拼接而成。这个策略对自然场景很有效,能增加目标上下文多样性,但放到超声肾脏结石检测上反而有害:结石的位置信息强烈依赖器官解剖结构,mosaic 把四张超声图拼接后,一个肾脏的上半截和另一个肾脏的下半截拼在一起,模型学到的解剖上下文是扭曲的。我在训练这类医学小目标数据集时,会单独降 mosaic 概率,让模型在多数 epoch 里看到完整的原始图像。

# hyp.kidney_stone.yaml 片段 mosaic: 0.3 hsv_h: 0.0 hsv_s: 0.0 hsv_v: 0.0 fliplr: 0.5

上面这份超参数文件里,mosaic 从默认的 1.0 降到 0.3,同时把三个 HSV 增强全部置零。fliplr 的水平翻转可以保留,因为肾脏左右对称,翻转不破坏语义。垂直翻转不建议开,超声扫查方向固定,上下翻转后的图像在真实临床场景中不会出现,开了只会增加无效的分布偏移。

4.3 输入分辨率的选择:640 起步,1280 看显存

超声波结石检测对输入分辨率很敏感,因为结石是典型的强回声小目标。640 分辨率下,一个直径 8mm 的结石如果占图像宽度 1/20,对应的像素宽度只有 32 像素,属于中等偏小目标,YOLOv5s 勉强能处理;如果你发现统计脚本算出的框均宽低于 0.03,也就是 640 下不足 20 像素,就要把 imgsz 提到 960 或 1280。

分辨率提升带来两个问题:显存占用翻倍,训练速度明显下降。一个折中做法是先用 640 把迁移学习做扎实,再用 960 微调 20 到 30 个 epoch,这样既稳住了收敛曲线,又保住了小目标的细节信息。微调阶段记得把学习率降低 10 倍,否则容易震荡。

5. 避坑与常见问题:数据泄漏、标签漂移、mAP 虚高这几道坎

医学图像数据集训练,翻车往往不在模型,而在数据本身。下面这几条是我反复踩过的坑,每一条都按现象、原因、解决三步说明,训练前对照着排查一遍能省很多无谓的时间。

5.1 验证集 mAP 高达 0.95,换一台超声设备直接失灵

现象:训练集的验证 mAP 接近 0.9,模型在测试集上表现惊艳,可一旦在真实超声设备的新扫查画面上跑,漏检和误报同时飙升。

原因:超声数据集的典型问题是同一病人的连续帧同时混入了训练集和验证集。扫查时保存的几十帧图像只有轻微的运动差异,在模型眼里几乎是同一张图,这等于把测试答案提前塞进了训练集,属于数据泄漏。

解决:检查数据集中是否存在同一病人编号的图像同时出现在 train 和 val 目录。我一般会写一个脚本,从文件名提取病人编号,统计编号分布,发现重叠就手动把整个病人的图像整体挪到训练集或验证集,保证验证集中不会出现训练集见过的病人。医学数据按病人划分而不是按帧划分,这条规则要刻在脑子里。

5.2 训练到一半 loss 出现 NaN,loss 直接崩溃

现象:前几十个 epoch 训练正常,突然某个 epoch 的 box_loss 跳成 NaN,之后指标全部归零。

原因:超声图像中偶尔会有极端标注——比如标注框宽度为 0、或者框超出图像边界。YOLOv5 在计算 GIoU loss 时遇到这种非法框,梯度就会变成 NaN。另一个高发原因是某个标签文件里混入了类别 ID 超出范围的数字,比如类别数是 2,标签里却出现了 ID 5。

解决:训练前强制清一遍标签。检查每个 TXT 文件中坐标是否在 0~1 之间、宽高是否大于 0、类别 ID 是否小于 nc。遇到异常行直接删除并记录文件名,训练结束后可以人工复查这些被清洗掉的样本。

def clean_labels(label_dir, num_classes=2): issue_count = 0 for filename in os.listdir(label_dir): if not filename.endswith(".txt"): continue filepath = os.path.join(label_dir, filename) with open(filepath, "r") as f: lines = f.readlines() valid_lines = [] for line in lines: parts = line.strip().split() cls_id = int(float(parts[0])) cx, cy, w, h = map(float, parts[1:]) if cls_id >= num_classes or w <= 0 or h <= 0: issue_count += 1 continue if not (0 <= cx <= 1 and 0 <= cy <= 1): issue_count += 1 continue valid_lines.append(line.strip()) with open(filepath, "w") as f: f.write("\n".join(valid_lines)) print(f"Cleaned {issue_count} invalid annotations in {label_dir}") clean_labels("labels/train")

这个脚本会自动剔除非法标注行,保留有效数据。清洗后再跑同样的训练,NaN 问题基本不会出现,标签的脏数据是超声数据集里最常见也最容易忽略的根因。

5.3 验证集 mAP 不低,Recall 却只有 0.5 左右

现象:训练结束打印的 mAP@0.5 有 0.85,看起来能用,但查看 Recall 指标只有 0.5 上下,大量结石样本根本没被模型召回。

原因:mAP 的数值同时受 Precision 和 Recall 影响,如果类别不平衡——比如 kidney 类样本数量远多于 stone 类——模型会在多数类上学得很好,少数类则倾向于保守预测,宁可漏掉也不敢错检,导致 Recall 偏低。

解决:训练时给少数类加大 loss 权重,或者用过采样策略。在 YOLOv5 中可以直接修改损失函数的类别权重参数,让 stone 类的正样本贡献更大的梯度。更省事的做法是先用当前模型跑一遍验证集,把漏检的图像挑出来看,如果漏检集中在小尺寸结石上,优先提升输入分辨率而不是调权重。

5.4 显存不足:OOM 反复出现,batch 降到 4 还是崩溃

现象:训练命令执行后爆出 CUDA out of memory,把 batch 降到 4 依然报错。

原因:超声图像分辨率偏高,如果数据集原图是 1024×1024 而你没有在训练时强制 resize,YOLOv5 会按--img参数缩放,这个一般不占显存。真正吃显存的是默认开启的 cache 选项和 mosaic 拼接。另外,如果同时跑着 TensorBoard 或者其他模型,显存分配也可能不足。

解决:训练命令加--cache改成不缓存,或者直接不加。检查后台是否有其他进程占用 GPU,nvidia-smi看一下显存分配情况。如果确认没有别的进程抢显存,就把 batch 设为 8,同时把--workers降到 4,减少数据加载线程的内存开销。

6. 验证技巧:用混淆矩阵和 PR 曲线决定阈值

训练结束不代表项目结束,医学图像模型上线的关键在于找到合适的置信度阈值,而这个阈值不应该拍脑袋定。跑完验证集后,YOLOv5 会在结果目录下生成 confusion_matrix.png 和 PR 曲线图,这两张图就是定阈值的依据。

python val.py \ --data data/kidney_stone.yaml \ --weights runs/kidney_stone/exp_baseline/weights/best.pt \ --img 640 \ --conf 0.25

val.py 会遍历验证集,按置信度从高到低排列预测结果,得到完整 PR 曲线。我一般先看一下类别各自的 Recall 在什么置信度下开始急剧下降,然后选择曲线膝盖位置对应的 conf 阈值——既能保证不漏检,又不会让误报满天飞。对于超声结石检测,我的习惯是把置信度阈值压到 0.15 到 0.2,宁可多几个人工复核的框,也不放过小结石,医学场景漏检的代价远大于多余框的代价。

定完阈值后再做一次批量可视化推理,把预测框画到原图上,几张图拼成一张大图。超声图像灰度背景下的可视化效果很直观,框能叠在强回声团上,说明模型确实学到了声学特征,而不是在瞎猜。我过去有一个项目就是靠这批可视化图说服医生复核标注数据的——把结石的框和真实声影位置比一比,哪些标注偏了,哪些框压在伪影上,一眼就能看出来。

从那以后,我拿到任何超声类数据集都会强制走一遍:按病人划分训练验证集、清洗标签坐标、统计框分布、可视化抽查预测结果,四步做完才允许自己动训练命令。这套流程救过我很多次,希望也能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询