ARTICLE DETAIL

资讯详情

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

糖尿病肾病检测数据集VOC转YOLO格式及YOLOv8训练实战指南

糖尿病肾病检测数据集VOC转YOLO格式及YOLOv8训练实战指南 简介糖尿病肾病检测数据集以VOC与YOLO双格式组织面向医学影像中的DR糖尿病视网膜病变分级检测任务适合训练目标检测模型也可服务科研实训、算法验证和比赛备赛适配YOLO系列、Faster R-CNN、SSD等常见检测框架。压缩包共2000个文件以XML标注文件为主体1999个另含1个说明性TXT文档整体大小约44.31MB便于下载、归档和快速解压。数据集覆盖mild-DR、moderate-DR、normal、proliferation-DR、severe-DR五个类别共涉及4122张眼底图像对应的标注信息VOC与YOLO格式配套齐全训练集与验证集划分后可直接接入现有训练管线免去手工格式转换成本。已有138人学习适合医学影像AI初学者获取规范数据集也方便中高级研究者在统一标注格式下复现实验、评估不同检测模型在糖尿病视网膜病变筛查场景中的性能表现。1. 糖尿病肾病检测数据集VOCYOLO格式拿到手先别急着解压开训收到一包「糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z」第一反应很容易是双击解压、打开文件夹、赶紧开始训练。但病理图像的目标检测和自然图像完全不一样肾小球、肾小管这类结构密集分布边界模糊5个类别在视觉上互相纠缠4122张图听着不多单张图往往带十几个候选框真实参与训练的目标样本量并不小。这套数据的特殊之处在于VOC和YOLO双格式同时给足看起来省了转换的麻烦实际上最容易埋坑——两套标注如果各自划分训练验证集验证集会偷偷重叠指标虚高而不自知。这篇笔记按真实操作顺序来先把VOC和YOLO两套格式的差异讲透再教你在Linux下安全解压校验然后直接上YOLOv8训练最后拆解5个高频翻车点。适合要用YOLO系列做糖尿病肾病检测的工程师、以及负责标注和数据核查的同学。2. VOC与YOLO两套标注不是两份数据格式差异决定你的训练入口先把结论放在前面VOC和YOLO是同一个标注信息的两套表达不是两份数据。VOC用xml文件记录像素坐标YOLO用txt记录归一化坐标。之所以给两套是因为生态不一样——VOC/Pascal格式是labelimg的默认输出也是很多经典检测框架的通用中间格式而YOLO系列包括现在的YOLOv8原生吃txt。这个包把两套都给了省了你手写转换的那一步但也意味着你必须搞清楚两套标注之间如何对应否则后面排查问题会非常痛苦。2.1 用labelimg看VOC的xml一个框对应一个病理结构labelimg打标完保存的默认格式就是VOC。打开任意一个xml结构非常直观根节点annotation下filename记录图像文件名size记录宽高和通道数每一个object就是一个目标框。object里的name是类别名bndbox四个字段是xmin、ymin、xmax、ymax单位是像素原点在图像左上角。和自然图像检测不同糖尿病肾病病理图像里的object语义不是人、车、猫而是肾小球、肾小管、间质、动脉、硬化小球这类组织学结构。为什么先讲VOC因为在医学影像标注流程里xml通常是“母本”。很多标注团队或个人打标第一步用labelimg输出VOC再由脚本转成YOLO、COCO等格式。如果你后面想切到mmrotate、Detectron2这类框架VOC反而是最通用的中间格式。训练前用脚本快速看一眼xml里到底标了什么这一步能发现很多低级问题比如类别名拼写不一致、某个xml坐标写反、或者混进了空白标注。import xml.etree.ElementTree as ET from pathlib import Path ann_dir Path(Annotations) # 按实际解压路径修改 for xml_file in sorted(ann_dir.glob(*.xml))[:3]: tree ET.parse(xml_file) root tree.getroot() filename root.find(filename).text objs root.findall(object) print(f{filename}: {len(objs)} objects) for obj in objs: name obj.find(name).text box obj.find(bndbox) coords [int(box.find(tag).text) for tag in (xmin, ymin, xmax, ymax)] print(f {name}: {coords})这段脚本遍历Annotations目录下前三个xml打印文件名、目标数量和每个框的像素坐标。关键是把坐标全部读成int后面转YOLO时再转float。如果某个object的name不在预期的五类里说明标注污染了先处理再训练。把[:3]去掉就是全量遍历可以顺便统计类别分布。xml目录路径要和实际解压路径一致这是个绕不开的前提。2.2 YOLO的txt为什么要归一化0到1的坐标反而更抗变形YOLO的txt标注每一行只有五个数字类别id、归一化后的中心点x、中心点y、框宽、框高。注意三个特点坐标全是0到1的小数由像素坐标除以图像宽高得到位置信息存的是中心点而不是左上角类别用整数id而不是名称。归一化的好处是不管训练时把图像缩放到640还是1280标注不需要跟着改。举个例子一张1000×800的病理图某个肾小管框是xmin100ymin150xmax400ymax450换算出来就是cx(100400)/2/10000.25cy(150450)/2/8000.375w(400-100)/10000.30h(450-150)/8000.375。这个换算看起来简单但一次除以宽、一次除以高不少转完的脚本会把w和h算错或者把cx写成xmin/width导致整个框左移半个身位。YOLO格式对病理图像还有一个隐藏优势整张切片原图可能非常大很多标注软件对超大图会先缩放再打标。如果直接存像素坐标不同缩放级别下标注就对不上原图了归一化坐标天然免疫这种缩放。所以训练YOLO系列模型直接吃txt是效率最高的路径。这也是这类数据集给双格式的原因——xml方便人检查和跨框架迁移txt方便直接训练。2.3 VOC转YOLO的转换脚本类别顺序错了模型会学歪既然知道了两套格式的换算关系自己写转换脚本也不难。常见做法是先定一个类别表CLASSES列表顺序就是训练时txt里类别id的顺序。然后遍历Annotations目录对每个xml做三件事读size得到宽高、读object得到类别和像素坐标、按公式写出txt。import xml.etree.ElementTree as ET from pathlib import Path # 类别顺序必须和训练data.yaml里的names完全一致 CLASSES [glomerulus, tubule, interstitium, arteriole, sclerosis] def convert_voc_to_yolo(xml_path: Path, out_dir: Path) - None: tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: print(f跳过未知类别: {name} {xml_path.name}) continue class_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_txt out_dir / (xml_path.stem .txt) out_txt.write_text(\n.join(lines)) # 批量转换 in_dir Path(Annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) for xml_path in in_dir.glob(*.xml): convert_voc_to_yolo(xml_path, out_dir)这里有两个边界点要注意。第一分母w和h如果为0说明xml里size字段缺失这种文件要单独挑出来不能静默跳过否则图片和txt数量对不上。第二CLASSES的索引顺序必须和后面data.yaml里的names顺序完全一致。常见的翻车是把类别和id对应错训练时模型把硬化小球学成了肾小球的样子mAP还会虚高。双格式最典型的一个误用是把VOC和YOLO当成两份数据。比如有人把Annotations里80%的xml复制到train同时又把labels里80%的txt复制到另一个train看似都是8:2实际两套划分互相对不上验证集里混进了训练图像。验证集不干净模型报出来的mAP就失去了参考意义。处理这类数据集正确做法是以图像文件名为唯一主键一套划分同时套用到images、Annotations、labels三个目录。这个原则在第三章划分脚本里还会用到。3. 把4122张图从7z里安全取出来解压命令、文件校验与目录规划拿到这个包第一步不是训练是解压和校验。7z格式在医学图像数据集里出现频率很高因为PNG、TIFF这类无损格式的重复块多7z的LZMA压缩率比zip高明显一截病理图像转成7z通常能再省不少体积。但也正因为7z不是系统默认解压格式很多问题在第一步就炸了——有人解压完发现文件数对不上有人训练时报错找不到标签回头排查才发现解压就没解全。3.1 在Linux解压7z文件三条命令从安装到完整性测试我在Linux服务器上处理这个包第一步是装p7zip。Ubuntu/Debian用aptmacOS用brew。装完先别急着解压先跑7z t测试压缩包完整性。# Ubuntu / Debian sudo apt update sudo apt install -y p7zip-full # macOS brew install p7zip # 先测完整性输出末尾有Error说明包损坏 7z t 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z # 解压到指定目录注意-o后面不要加空格 7z x 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z -o./dn_dataset # 解压后查看目录大小和文件数 cd dn_dataset du -sh . find . -type f | wc -l三个命令各有用途。7z t先测完整性它会逐文件显示OK最后一行告诉你是否有错误这一步不能省特别是包是从网盘或服务器传输过来的。7z x是解压-o指定输出目录注意-o和目录之间不能有空格否则会被当成压缩包名的一部分。解压完成后立刻检查目录大小和文件数避免解压到一半磁盘不够。如果压缩包是分卷形式.7z.001、.7z.002直接对第一个分卷执行同样的7z x命令即可工具会自动拼接。3.2 核对4122张图像和两套标注数量和类别名都要验解压后第一件事不是急着写yaml是核对数量和质量。标题说4122张那就该有4122张图像。这类包通常会按JPEGImages、Annotations、labels三个目录组织分别放图像、VOC的xml、YOLO的txt。医学图像常见格式是jpg或png下面命令按实际后缀调整。cd dn_dataset echo images: $(ls JPEGImages/*.jpg 2/dev/null | wc -l) echo xml: $(ls Annotations/*.xml 2/dev/null | wc -l) echo txt: $(ls labels/*.txt 2/dev/null | wc -l)三个数字应该相等。如果不相等优先怀疑三件事标注文件没生成完、某些图像没有对应目标框、某些标注是空文件。空txt在YOLO里是允许的表示这张图没有目标但很多训练脚本会直接跳过空文件导致参与训练的图变少。图像数量和xml数量对得上、但txt数量差一截大概率是某几张图的xml里没有object转换脚本没写出txt。再统计类别分布这一步能看出来5个类别是否均衡。python3 - EOF import xml.etree.ElementTree as ET from pathlib import Path from collections import Counter counter Counter() for p in Path(Annotations).glob(*.xml): tree ET.parse(p) for obj in tree.getroot().findall(object): counter[obj.find(name).text] 1 for name, cnt in counter.most_common(): print(f{name}: {cnt}) EOF这里用Python遍历而不是grep是因为xml里可能有命名空间或特殊字符grep容易漏。统计结果有没有极端不平衡的类别比如某个类别只有几十个框那训练策略就要调整。另外如果你打算直接用YOLO训练xml只是校验用的母本真正喂给模型的是labels里的txt。检查完xml的类别名后还要检查txt第一列的id是否都在0到4之间出现5以上说明转换脚本的类别表不对训练时会直接报错或静默学歪。3.3 目录结构怎么摆train和val按文件名主键划分YOLOv8默认按子目录找数据images/train、images/val、labels/train、labels/val。我给这类医学数据集划分时固定随机种子按8:2或9:1切保证同一张图的三份文件jpg/xml/txt进同一个集合。手动拖拽不可取一张一张拖到后面就会混。下面这个脚本只做两件事划分图像、同步复制对应txt。from pathlib import Path import random import shutil random.seed(42) # 固定种子保证每次划分结果一致 image_dir Path(JPEGImages) label_dir Path(labels) out_img Path(images) out_lbl Path(labels_yolo) for part in (train, val): (out_img / part).mkdir(parentsTrue, exist_okTrue) (out_lbl / part).mkdir(parentsTrue, exist_okTrue) imgs sorted(image_dir.glob(*.jpg)) random.shuffle(imgs) split int(len(imgs) * 0.8) for part, items in ((train, imgs[:split]), (val, imgs[split:])): for img in items: shutil.copy(img, out_img / part / img.name) txt label_dir / (img.stem .txt) if txt.exists(): shutil.copy(txt, out_lbl / part / txt.name) else: print(missing label:, img.name) # 缺失标注不能静默跳过random.seed(42)保证每次划分结果完全一致这对复现实验很重要。0.8是训练集比例实际可以按数据量调病理图像有4000多张8:2够用。脚本里缺失txt会打印文件名而不是跳过因为这些文件会导致训练时该图标签缺失YOLO默认当背景图处理静默污染训练集。如果你要同时保留VOC的xml划分把复制txt那行改成复制xml即可原则一样——以图像文件名为主键一套划分三处同步。4. 用YOLOv8训这套糖尿病肾病数据data.yaml、超参数与一次推理目录摆好后训练本身反而是整个流程里最机械的一步。YOLOv8训练自己的数据集核心就三件事写对data.yaml、选对超参数、跑起来后看得懂loss曲线。医学检测和自然图像检测在训练上的差别主要体现在图像分辨率和目标密度上这两个因素直接决定imgsz和batch的取舍。4.1 准备data.yaml类别顺序和txt第一列必须一一对应data.yaml是YOLOv8找数据、读类别的唯一入口。一个典型的yaml文件长这样path: /home/user/dn_dataset # 改成你解压后的绝对路径 train: images/train val: images/val names: 0: glomerulus 1: tubule 2: interstitium 3: arteriole 4: sclerosispath必须写绝对路径train和val是相对path的目录名。names的顺序必须和txt第一列的类别id完全一致这是双格式数据集最容易踩的坑——如果xml里类别名和txt id对不上训练不会报错但类别会互相学串。建议写完yaml后随机挑一张图把它的txt内容打印出来对照names逐行看一遍确认第0行是glomerulus而不是其他类别。这一步五秒钟能省后面几个小时的排查。4.2 训练命令与超参数取舍imgsz、batch、epochs怎么定yolo detect train \ datadn_dataset.yaml \ modelyolov8s.pt \ imgsz640 \ epochs100 \ batch16 \ patience15 \ projectruns_dn \ namev8s_640 \ device0几个参数按场景调。model选择取决于显存和精度要求yolov8n最快但小目标召回率偏低yolov8s是性价比最高的起点显存够再上m或l。imgsz对病理图像很关键肾小球这类小目标在640分辨率下可能只占二三十个像素如果显卡显存允许直接上1280代价是训练时间翻倍、batch要减半。patience15的意思是连续15轮验证集不涨就早停这个值对医学数据集比较保守防止过拟合也防止白跑。参数作用入门建议显存充足时imgsz训练分辨率6401280batch每批图像数832或更大epochs最大训练轮数100100以上model模型骨架yolov8syolov8m/lpatience早停耐心值1515训练过程中最值得看的是三类lossbox_loss是框回归误差cls_loss是分类误差dfl_loss是框分布损失。前10个epoch训练loss应该快速下降val loss先降后稳。如果cls_loss一直纹丝不动优先怀疑labels里的类别id和names对应错了如果box_loss降了但cls_loss反升大概率是类别混淆太严重需要考虑合并语义接近的类别。4.3 第一次推理置信度门限和NMS对密集目标的影响训练结束后用best.pt跑一次验证集推理重点看可视化结果而不是直接看指标。yolo detect predict \ modelruns_dn/v8s_640/weights/best.pt \ source./test_imgs \ conf0.25 \ iou0.45 \ saveTrueconf是置信度门限只有分数超过0.25的框才保留iou是NMS的阈值两个框的IoU超过0.45时低分框会被抑制。病理图像目标密集肾小球和硬化小球相邻如果iou设得太高比如0.7NMS会杀掉相邻的框导致检测结果比标注稀疏很多。我从0.45起步跑完看图上漏检情况再往下调。conf同理类别间差异大的场景0.25挺好但医学场景里目标边界模糊conf调到0.15往往能把弱特征的小结构捞回来代价是假阳性变多。这一步没有标准答案只能看图调。5. 训练糖尿病肾病数据集的避坑记录5个高频翻车现场这一章写的都是我实际处理这类病理数据集时踩过的坑每一条按现象、原因、解决三步拆解。前两条发生在训练之前后三条在训练和调优阶段。新手最容易在第一条就卡住半天。5.1 现象7z压缩文件密码是正确的但一直报错解压时报错提示密码错误但明明密码是对的。还有一种情况是解压到一半报Data Error某几个文件CRC校验失败。原因通常有三个中文文件名或中文路径在7z实现里存在编码问题报错提示会误导你以为是密码错压缩包传输不完整64位的包只有前一半传到了本地或者下载工具改了文件编码。7z报Password is wrong的提示并不总是密码本身的问题文件损坏也可能出现类似现象。解决先跑7z t测完整性如果输出里有Error重新下载或重新传输压缩包把压缩包和解压目标都放到纯英文路径下再试排查确认压缩包本身完整后再怀疑密码。如果只是个别文件损坏可以用7z x先解压出完好部分只对损坏文件重新获取这比全部重来省时间。5.2 现象训练时报No labels found或0 imagesyolo detect train刚跑起来就报错说找不到labels文件或者提示images目录为空。原因YOLOv8要求images和labels目录名与yaml中的train/val字段完全匹配且txt要和对应jpg同名同后缀。很多人解压后目录叫labels_yoloyaml里写labels自然对不上也有人是jpg是img_001.jpgtxt是img_001.txt但放在不同子目录脚本递归不到。解决先确认yaml里的路径和实际目录完全一致然后直接打印一个图像文件和对应txt的文件名核对再判断是扩展名不一致还是目录不一致。还有一个细节如果yaml中path写的是相对路径而训练命令是从其他目录执行的相对路径会解析错这种问题统一用绝对路径解决。5.3 现象某个类别完全检不出或者整个类别mAP为0训练跑完验证结果里某个类别的precision很高但recall极低甚至valid batch里压根看不到这个类别的预测框。原因类别不均衡。5个类别里肾小球可能有几千个框但硬化小球只有几十个框模型很容易把稀有类别当作背景学掉。另一个隐蔽原因是txt里类别id越界比如写了5而names只有0到4训练脚本不会报错但那个框永远不会被正确监督。解决先统计每类框数这一步第三章的Counter脚本已经做过。对极端小类可以单独给它的图像做离线增强让它在每个batch里出现的频率提上去或者暂时合并语义接近的类别比如把硬化小球并入肾小球先跑通基线再细分。如果确认是id越界回归到转换脚本检查CLASSES列表是否完整。5.4 现象train loss在降但验证集指标原地不动或直接发散训练日志里train loss一路向下val loss却波动上升明显是过拟合。还有一个常见的玄学场景train loss和val loss都在降但mAP0.5一动不动。原因病理图像背景高度相似大量图像来自同一批染色切片模型在没有强增强时很快就会记住背景纹理而不是目标结构。如果batch太小比如4或8BN统计量不稳定验证集指标也会反复横跳。手动在训练集做随机裁剪但验证集没做对齐处理也会让指标失真。解决加大增强YOLOv8的hsv_h、hsv_s、hflip、mosaic按默认开如果服务器资源允许把mosaic概率调高到1.0能让模型看到更多目标局部batch提到显存能容纳的最大值提高BN稳定性patience设15到20让早停兜底不要硬跑满epochs过拟合后的best.pt会被覆盖掉白白浪费算力。5.5 现象VOC转YOLO后框全部偏移或大小明显不对转换完用训练脚本或可视化检查框整体偏左上或者比实际目标大一圈小一圈。原因转换时用的尺寸源不对。xml里size如果记录的是标注时的缩放图尺寸而训练时用的原图尺寸坐标必然错位。另一个常见错误是w和h写反把(xmax-xmin)/width写成了除以height。解决先单张验证。挑一张图读出xml的size和bndbox把像素坐标画在原图上确认无误后再按公式转YOLO格式转完再把txt归一化坐标画回去二次确认。批量转换前单张验证这个习惯能避免几百张图全部转错之后才发现那时候返工成本就高了。6. 医学场景的验收不止mAP盯混淆矩阵逐类调置信度门限训练结束不是终点尤其是医学相关任务模型报告的和临床要用的可能是两回事。mAP0.5再看也只是所有类别拉平后的一个数病理结构检测的验收要看逐类表现。验证集跑完后会生成混淆矩阵图在runs/dn/v8s_640/目录下。先看两个关键位置类别之间的互相误检以及背景的假阳性占比。肾小球和硬化小球这类语义相近的类别误检通常集中在相邻类这种情况靠加数据比调阈值更有效背景假阳性高说明模型把染色噪声当成了结构需要检查图像预处理是不是没做标准化。再看每个类别的PR曲线。recall偏低的类别把conf从0.25调低到0.15能把弱特征目标捞回来代价是假阳性变多precision偏低但recall高的类别可以反过来把conf调高到0.4用精度换稳定。目标特别密集的类别把iou门限从0.45调到0.5可以减少NMS对相邻框的误杀。这个调参过程没有捷径只能一轮一轮看图肾小球与硬化小球掺在一起时先观察到底哪个类别被吞再对症调整。我最早只看mAP0.5觉得指标过得去就收工后来把预测框叠加到原图上才发现硬化小球几乎全被NMS和低置信度吞掉了单独调类别阈值才救回来。医学场景的检测结果直接关系到计数和分级每张图十几个框错一个都影响结论。希望帮到你。本文还有配套的精品资源点击获取
返回列表