ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

糖尿病肾病病理切片目标检测:VOC+YOLO双格式数据集与训练避坑指南

糖尿病肾病病理切片目标检测:VOC+YOLO双格式数据集与训练避坑指南 简介一套面向糖尿病肾病检测的数据集适用于医学影像目标检测模型训练与算法验证尤其适合研究YOLO、Faster R-CNN等常见检测框架的开发者使用。数据已按Pascal VOC与YOLO两种格式组织标注类别包括mild-DR、moderate-DR、normal、proliferation-DR、severe-DR共5类覆盖从正常到严重增殖期的病变等级便于开展分类与检测实验。资源包共2000个文件以1999个xml标注文件为主另含1个txt使用说明整体压缩包大小约44.31MB下载解压后即可查看目录结构与标注内容。已有139人学习使用适合医学图像处理方向的学生、算法工程师在模型训练、数据增强、类别分布分析等环节直接取用。整理者按照统一命名规则组织文件可显著减少数据清洗和格式转换成本帮助使用者将更多精力聚焦于模型调优与评估。1. 糖尿病肾病检测数据集4122 张双格式标注先验证数据再决定怎么训做糖尿病肾病病理切片检测的人都知道标注比模型更稀缺。拿到一个VOCYOLO双格式的4122张五分类数据集第一反应不该是直接开训而是先确认压缩包里的图片和标注没有在整理过程中被折腾坏。VOC格式适合做通用目标检测与二次标注YOLO格式是yolo系列训练和推理工具链最顺手的一环这个数据集同时给两套等于省掉了耗时的格式转换环节。适合有病理切片、显微图像检测需求愿意花一晚上把数据链路跑通再投入训练的人。下一步先把 .7z 解压、核对文件数再谈训练。2. 先解开 .7z 再说别的Linux/PyCharm 两种解压路径与数据完整性校验2.1 Linux 下解压 7zp7zip 安装与破坏性参数说明7z 不是 Linux 标配解压器很多服务器上没装直接跑7z x会先报 command not found。Debian/Ubuntu 装 p7zip-fullCentOS/RHEL 装 p7zip 加 p7zip-plugins后者不含 plugins 连 .7z 都解不了。# Debian/Ubuntu sudo apt update sudo apt install -y p7zip-full # CentOS/RHEL 7/8/9 sudo yum install -y p7zip p7zip-plugins装完用7z i确认版本和格式支持能列出 7z 说明插件加载正常。# 先做完整性测试不实际解压 7z t 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z # 解压到指定目录/data/dkd 需预先创建 7z x 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z -o/data/dkd -y参数说明x是完整解压并保留目录结构e会把所有文件摊平到单目录这种标注数据集千万别用e目录层级一旦丢了后面找 train/val 划分和标签对应关系会很难受-o指定输出目录-o与路径之间不能有空格-y跳过所有确认提示。解压后先看顶层目录ls /data/dkd常见的包内布局是images/、Annotations/VOC 的 xml和labels/YOLO 的 txt也可能把整个 VOC 目录套在一层子文件夹里。别急着写训练脚本先用一条命令统计各类文件数量判断目录结构与预期是否一致find /data/dkd -type f | sed s/.*\.// | sort | uniq -c# 如果包内文件是 GBK 编码的中文名Linux 下可能乱码 # 先只查 jpg/xml/txt 三类乱码文件一般不会影响训练 find /data/dkd \( -name *.jpg -o -name *.xml -o -name *.txt \) | wc -l提示包内文件名如果是中文且压缩时用了非 UTF-8 编码Linux 解压后可能显示乱码。处理方法是用convmv转码文件名或者解压后直接按扩展名和内容匹配不要依赖文件名里的中文做逻辑判断。2.2 PyCharm 里添加 7zWindows/macOS 本地预览与抽查在 PyCharm 里直接双击 .7z 只会看到二进制内容没法展开预览。想快速抽查几张图和标签常见做法是装 7-Zip 并把它加入系统 PATH然后在 PyCharm 的 Terminal 里直接调用 7z 命令省得每次都在图形界面里拖来拖去。# Windows 下 7-Zip 安装目录加入 PATH 后 7z t DKD.7z 7z x DKD.7z -oC:\datasets\dkd如果不允许装图形软件也可以直接在 PyCharm 里跑 Python 的 py7zr 库这个库 pip 安装即可适合纯终端环境import py7zr with py7zr.SevenZipFile(糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z, moder) as z: names z.getnames() print(len(names), names[:10]) # 先看包内路径确认目录结构 z.extractall(path/tmp/dkd_preview) # 全量解压到临时目录参数说明moder是只读打开不会碰原包getnames()返回包内完整路径列表能在真正解压前先判断有没有images/、Annotations/这些顶层目录extractall(path...)全量解压输出目录不存在会自动创建。macOS 同样用 p7zip 或 py7zr不必额外装图形界面。2.3 解压后抽五分钟做三件事文件数、图片可读性、哈希一致数据从别人手里拿过来最怕的不是压缩包打不开而是解压一半断电、下载丢包导致某个 jpg 损坏训练时 dataloader 在某个 epoch 突然崩掉再回头查是哪张图。解压完先按下面顺序排查。# 1) 统计各类文件数量4122 张图应一一对应 find . -name *.jpg | wc -l find . -name *.xml | wc -l find . -name *.txt | wc -l # 2) 对整包做一次 sha256 校验和发布方给的哈希对比 sha256sum 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z文件数对不上时先对照 xml 和 txt 的文件名集合优先怀疑两版标注不是同一批人补的有人单独改过图。图片可读性用 Python 批量检查4122 张规模用verify()足够它只校验文件头和完整度不会真的把像素解码一遍。from PIL import Image import glob bad [] for p in glob.glob(/data/dkd/**/*.jpg, recursiveTrue): try: im Image.open(p) im.verify() except Exception as e: bad.append((p, str(e))) print(损坏图片数:, len(bad), bad[:5])注意verify()之后如果要再读像素必须重新Image.open因为 verify 会关闭文件句柄。这一步做完数据链路才算真正立住后面训练崩了就不会再把锅甩给数据文件。3. 把 VOC 和 YOLO 两套标注对齐格式差异、转换脚本与五类别标签映射3.1 VOC 的 xml 到底存了什么逐字段看一遍 bndbox拿到数据后先打开一个标注文件确认 object 字段里的 name、bndbox 与 difficult/truncated。下面用示意性名称展示结构真实五类以解压后 grep 的结果为准。annotation folderDKD/folder filenamecase_00123.jpg/filename size width1920/width height1080/height depth3/depth /size object nameglomerulus/name bndbox xmin420/xmin ymin280/ymin xmax610/xmax ymax470/ymax /bndbox difficult0/difficult /object object nameinterstitial_fibrosis/name bndbox xmin1200/xmin ymin150/ymin xmax1390/xmax ymax330/ymax /bndbox difficult0/difficult /object /annotation这段结构里有几个关键判断点size决定后续 YOLO 归一化的分母filename一般不带扩展名前缀difficult1的样本在转换时通常跳过因为它代表边界难以确认的样本。还要注意同一张图可以出现多个 object也就是多个目标框。拿到压缩包后第一步应该先对比所有 xml 里 name 的去重结果确认五类的真实拼写别凭印象写 CLASS_MAP。grep -hoP (?name)[^] Annotations/*.xml | sort | uniq -c这条命令会用正则把每个 object 的 name 字段抽出来统计每个类别出现次数。输出里的类别名才是你写 CLASS_MAP 的唯一依据。如果出现第六个类名先回去翻数据说明别急着把多余类别合到背景里。3.2 用脚本把 VOC 转 YOLO归一化坐标从精度这个坑说起YOLO 标签是每行一个目标的 txt五个字段分别是class_id x_center y_center width height全部相对图片宽高归一化而且是中心点表示。转换时翻车点很集中有人直接用 int 相除把x_center算成 0也有人忘记跳过 difficult把噪声框当正样本。下面是一段可以直接用的转换脚本骨架。#!/usr/bin/env python3 import xml.etree.ElementTree as ET # 这里填上一步 grep 到的真实类别名顺序即类别 ID写错全完 CLASS_MAP { glomerulus: 0, tubular_atrophy: 1, interstitial_fibrosis: 2, arteriolosclerosis: 3, normal_tubule: 4, } def voc_to_yolo(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() w int(root.findtext(size/width)) h int(root.findtext(size/height)) with open(out_path, w) as f: for obj in root.findall(object): name obj.findtext(name) if name not in CLASS_MAP: continue if obj.findtext(difficult) 1: continue box obj.find(bndbox) x1 float(box.findtext(xmin)) y1 float(box.findtext(ymin)) x2 float(box.findtext(xmax)) y2 float(box.findtext(ymax)) xc ((x1 x2) / 2) / w yc ((y1 y2) / 2) / h bw (x2 - x1) / w bh (y2 - y1) / h f.write(f{CLASS_MAP[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}\n)代码逻辑说明先解析 xml 拿图片宽高遍历每个 object用findtext取子字段避免缺字段时直接抛 KeyErrordifficult 为 1 时跳过最后写出与图片同名的 txt。这里CLASS_MAP是命门必须和包内真实类别拼写完全一致大小写、空格都不能差。参数说明xc、yc是中心点归一化坐标理论范围在 0 到 1 之间bw、bh是目标宽高占图片的比例。写入时保留 6 位小数精度足够。YOLO 训练时labels/case_00123.txt和images/case_00123.jpg必须同名且目录结构对应否则 dataloader 根本找不到标签。如果压缩包已经自带 YOLO 版 txt这个脚本只用来抽检别盲转全量覆盖。3.3 VOC/YOLO 两套标注的一致性检查找到并排除悬空文件数据包同时存在两种格式最容易埋下隐患的是两套标注不同步某张图的 xml 有 3 个目标txt 却只有 1 个或者 txt 里类别 ID 和 CLASS_MAP 对不上。逐张核对用一段十行脚本就能完成。import glob, os xml_files {os.path.splitext(os.path.basename(p))[0]: p for p in glob.glob(Annotations/*.xml)} txt_files {os.path.splitext(os.path.basename(p))[0]: p for p in glob.glob(labels/*.txt)} only_xml set(xml_files) - set(txt_files) only_txt set(txt_files) - set(xml_files) print(仅 xml 有标注:, len(only_xml), 仅 txt 有标注:, len(only_txt)) for k in set(xml_files) set(txt_files): n_xml open(xml_files[k]).read().count(object) with open(txt_files[k]) as f: n_txt len(f.readlines()) if n_xml ! n_txt: print(k, xml:, n_xml, txt:, n_txt)统计不一致的目的不是立即去改而是先标记xml 有 5 个 object、txt 只有 4 个的文件训练时 YOLO 看到的是 5 个真值验证时却按 4 个算指标漂移会很难排查。对这类文件最简单的处理是直接从训练集里剔除而不是手工补标注。提示VOC 的 xml 可以包含 truncated、occluded、difficult 等附加属性转 YOLO 时默认只保留 bndbox 和 name。如果某些框的 difficult1建议在统计一致性的脚本里先把它们排除否则两套标注的目标数永远对不上。4. 用 YOLO 在糖尿病肾病数据上开训data.yaml、模型选型与第一轮参数4.1 data.yaml 怎么配才对得上五类别直接把 VOC/YOLO 目录丢给 yolo 训练是行不通的它只认 data.yaml。yaml 里的 train/val 可以是绝对路径也可以是相对目录nc必须和实际类别数一致names顺序必须和训练标签里的 class_id 顺序一致。标签里类别 ID 是 0就对应 names 下标 0顺序错了模型会在训练中静默学会错序映射运行起来看似正常实际两个类的 bounding box 被完全调换。path: /data/dkd # 数据集根目录 train: images/train val: images/val nc: 5 names: 0: glomerulus 1: tubular_atrophy 2: interstitial_fibrosis 3: arteriolosclerosis 4: normal_tubule提示上面 names 只是示意必须用第 3.1 节 grep 出来的真实五类替换。如果压缩包里没有划分好的 train/val先按 8:2 划分目录并保证两边类别分布近似。划分完成后建议立即对比 train 和 val 每类的目标数量不要只看图片数量。4.2 预训练权重、输入分辨率与 epochs 的第一枪针对肾组织切片这种目标尺度小、轮廓不规则、正样本密度高的场景建议从 yolov8s.pt 起步别一上来就用 yolov8n。yolo detect train \ datadkd_data.yaml \ modelyolov8s.pt \ imgsz640 \ epochs200 \ batch16 \ patience30 \ seed42 \ cacheTrue参数说明modelyolov8s.pt会从 COCO 预训练权重继续训练imgsz640对切片类图像够用若原图分辨率很高几千乘几千且目标很小建议先做 tiling 切块把每块降到 640 或 1280否则小目标会直接消失在多次下采样后的特征图里batch16取决于显存16G 显卡跑 yolov8s 一般能撑到 16 左右patience30表示验证集连续 30 个 epoch 没提升就早停医学数据集噪声大早停值设太小容易把模型掐在震荡期。第一轮训练别急着调 anchor 和各种损失函数权重。先跑通一遍数据链路把损失曲线和验证集指标看成形再谈优化。模型选 yolov8s 而不是 yolov8n是因为糖尿病肾病这类病灶目标在整张切片里占比小n 模型容量不足以拟合细粒度纹理边界。4.3 训练中途该盯哪几个数loss 不降先查数据而不是调参训练日志会同时给出 box_loss、cls_loss、dfl_loss 和验证集 mAP。这里有一条原则第一个 epoch 结束因为预训练权重没有收敛到医学域loss 偏高很正常但前 20 个 epoch 如果 box_loss 完全没有下降趋势基本可以断定是数据问题而不是学习率。回去查三类错误图片有坏图、标签越界、class_id 超出 nc。用 yolo 训练时runs/detect/train目录下会产出results.png和混淆矩阵。我一般只盯三条box_loss 训练集上是否在前 50 个 epoch 内从较高值平滑下降val/box_loss 不能比 train/box_loss 低太多低很多说明样本划分有问题五类中每一类的 AP 不能只有一类特别突出那是类别不平衡的信号。凡是看到 mAP 很高而 PR 曲线面积很小先怀疑训练脚本是不是用了错误的标签目录。因为把labels指到了和images不一致的地方时yolo 会静默地用空标签训练模型学到的只是背景。5. 糖尿病肾病数据集避坑5 个会让人半夜改代码的常见问题5.1 EXIF 旋转把框带偏坐标转换后目标全部偏离现象用 OpenCV 读取 jpg 训练目标框整体偏移目标中心普遍落在框外。原因医学影像设备拍出来的 jpg 常带 EXIF Orientation 信息OpenCV 的imread默认不解析 EXIF读出来的图像被旋转了 90 度或 180 度而 xml 里的 width/height 是按原始方向记录的转换出的 YOLO 归一化框基于错误坐标系。解决转换前对所有图片做一次ImageOps.exif_transpose把像素方向固化后再重写 xml 的宽高再执行 VOC 转 YOLO。不做这步后面所有指标都是假的而且很难在指标层面直接看出来。from PIL import Image, ImageOps import os for p in os.listdir(images): if not p.endswith(.jpg): continue im Image.open(os.path.join(images, p)) im ImageOps.exif_transpose(im) im.save(os.path.join(images_fixed, p))逻辑说明exif_transpose会根据 EXIF 里的 Orientation 字段自动旋转图像并清除该字段旋转后原图的宽高可能互换所以 xml 里的width和height也要同步更新再重新生成 YOLO 标签。这一步必须在任何转换之前做。5.2 目标尺度与默认 anchor 不匹配肾小球只有几十像素现象训练过程中 bbox_loss 高验证集 mAP 偏低分类概率看着正常输出的框要么过大要么抖动。原因YOLO 默认 anchor 尺寸是在 COCO 常见物体上统计出来的肾小球、肾小管这类小目标在 640 分辨率下可能只有 30-50 像素很容易落在 stride 32 那一层的特征图上空间信息丢失严重。解决训练前打开 autoanchor确认它重新计算出的 anchor 尺寸如果目标仍然太小就把原图切成 512 或 640 的 patch 再做检测每张 patch 单独出框推理时再合并回原图坐标。tiling 的切分重叠率一般取 10%-20%避免目标恰好被切在边界上。5.3 五类样本量悬殊mAP 参考意义有限现象整体 mAP 上了 0.8但拆开看只有两类高少数病变类别 AP 不到 0.3。原因糖尿病肾病数据集里正常结构多、病变成分少默认分类损失在多数类上权重过高少数类的梯度被淹没。解决先统计每类 bbox 数量对不平衡幅度大的类加 class weightsYOLO 的默认分类损失可以换成带 focal 性质的版本或者对少数类额外做 copy-paste 增强。评估时不要只看整体 mAP要看少数类的 AP 是否被多数类拉高。5.4 相似样本在 train/val 里重复出现验证集虚高现象训练时 loss 降得顺利验证集 mAP 极高一到新切片上就崩。原因整理数据集时可能把同一张大图的不同裁剪放进 train 和 val也可能一张原图被切块后分散到两个集合里模型在验证时直接认出了几乎相同的图。解决对全量图片算 perceptual hash筛出哈希重复的样本保证 train 和 val 之间没有任何近重复图片。这一步虽然耗时但比日后在部署现场怀疑模型要快得多。5.5 标签越界与浮点精度width 为 0、x_center 超过 1现象某个 epoch 训练中断报错指向一个 txt打开发现x_center是 1.0000001或width是 0。原因xml 里目标贴着图片边缘bndbox等于图片边界或标注工具多标了一个 0 宽度的点转换脚本没做 clip。解决转换时对所有值执行max(0, min(1, value))并跳过x2 x1或y2 y1的框。xc max(0.0, min(1.0, ((x1 x2) / 2) / w)) yc max(0.0, min(1.0, ((y1 y2) / 2) / h)) bw max(0.0, min(1.0, (x2 - x1) / w)) bh max(0.0, min(1.0, (y2 - y1) / h)) if bw 0 or bh 0: continue逻辑说明clip 保证归一化数值落在 0-1 区间YOLO 训练时不会因为越界触发断言跳过宽高为 0 的框是为了不让 dataloader 在数据加载阶段抛异常。转换完后再加一行检查加载全部 txt确认每行的 5 个字段都能转成 float 且在 0-1 区间。这行检查能省下一个通宵的排查时间。6. 用验证集出混淆矩阵再谈效果逐类 AP 与推理阶段置信度调整6.1 固定置信度阈值跑一遍验证脚本保留一份可复现的结果训练结束后先跑一遍验证脚本拿到混淆矩阵和逐类 AP这时候看到的数字才代表这个糖尿病肾病数据集到底训成了什么样。建议固定 conf 阈值先用 0.25取 PR 曲线的平衡点后再单独看五类中每一类的 AP50 和 AP75。医学场景下漏检和误检代价不同倾向不漏检conf 降到 0.1 再看 AP 和推理耗时倾向少误报conf 提到 0.4 再看。T4 上用 TensorRT 把 640 分辨率 yolo 加速后做这类推理单路帧率会明显提升但这是推理侧的事训练侧先把验证集口径定齐。6.2 逐类混淆矩阵协助判断形态特征识别能力常被忽略的一步是在验证集上画出逐类混淆矩阵重点看两两混淆的类到底错在哪。糖尿病肾病病理图像里纤维化和萎缩形态接近模型容易互相串这类错误靠调 anchor 完全没用要看模型学到的是形状还是纹理。后处理可以做两步一是用 test-time augmentation推理时把图像翻转/缩放后合并预测二是对模型输出的坐标加一个基于目标尺度的 NMS 阈值调节小目标之间重叠多NMS 阈值要适当调低。这两步不改训练直接作用于最终预测精度。6.3 换 loss 改 head 之前先在同一验证脚本下做基线对比我自己的习惯每个数据集版本只保留一个验证结果文件里面存混淆矩阵、每类 AP、训练曲线图版本间对比才有意义。换 backbone、换 efficient head 这类改进或改损失函数、加 EMA 平滑时先在同一验证集上跑同一套评估代码再谈论哪个修改更好。最后说一句实在话糖尿病肾病这类医疗影像数据集能凑到 4122 张双格式标注已经很奢侈把数据校验和验证口径做扎实比多调十个参数更值。希望帮到你。本文还有配套的精品资源点击获取
返回列表