
简介这是一份面向医学影像目标检测与深度学习入门/进阶人群的糖尿病肾病DR检测数据集图片及标注均为标准的Pascal VOC与YOLO格式可直接用于YOLO系列、Faster R-CNN等常见检测模型的训练与验证。类别覆盖mild-DR、moderate-DR、normal、proliferation-DR、severe-DR共5类对研究糖尿病视网膜病变分级检测、类别不均衡处理及医学小目标检测均有较好参考价值。资源包共2000个文件其中包含1999个xml标注文件和1个txt使用说明图片与xml/txt标注一一对应压缩包整体44.31MB体积适中适合快速下载并接入现有训练流程。目前已有138人学习浏览方便在动手训练前快速评估数据分布、标签格式和目录组织。对需要构建医学图像检测基线、理解VOC与YOLO标注差异的学习者来说是一份可直接上手的数据资源。1. 糖尿病肾病数据集为什么值得用 VOCYOLO 双格式这个资源是一个 7z 压缩包里面是 4122 张眼底影像和一一对应的 4122 个 Pascal VOC xml、4122 个 YOLO txt 标签文件。类别包含 mild-DR、moderate-DR、normal、proliferation-DR、severe-DR 五个等级本质上是一份糖尿病视网膜病变分级检测数据集。和常见只给单一标注格式的公开数据相比它同时给出 VOC 与 YOLO 两套标签省去了 xml 到 txt 的转换工序也方便在 detectron2、mmdetection 和 ultralytics 之间来回切换。对想跑 YOLO 训练自己数据集的人来说直接基于 txt 就能开始不必重复踩坐标换算的坑。适合目标检测入门、医学影像分类预研以及需要快速验证模型效果的开发者。2. Pascal VOC XML 与 YOLO txt 标注字段解析2.1 两种标注格式的文件对应关系解压后每个样本都由三个文件组成jpg 原图、同名 xml、同名 txt。以firc_shenbing_1719为例三者文件名一致后缀不同。xml 是 Pascal VOC 标准标注保存像素级绝对坐标txt 是由 xml 换算得到的 YOLO 训练标注存的是归一化后的中心点坐标和宽高。两者描述同一批目标框但数据结构完全不同。理解这种对应关系才能在后续做格式转换、标签验证或可视化时不会搞混。下面用一个简化示例说明 xml 内部结构。真实文件可能包含更多字段但核心只有size和object两块annotation folderdiabetes/folder filenamefirc_shenbing_1719.jpg/filename size width512/width height512/height depth3/depth /size object namemild-DR/name bndbox xmin120/xmin ymin80/ymin xmax300/xmax ymax260/ymax /bndbox /object /annotation这段 XML 定义了一张 512×512 的图片包含一个类别为 mild-DR 的边界框。bndbox中四个值分别是左上角 (xmin, ymin) 和右下角 (xmax, ymax) 的像素坐标。解析时直接读取/annotation/size/width、/height再遍历object节点即可。name字段是字符串需要映射到整数类别 id映射顺序必须与训练配置一致。对应的 YOLO txt 每行一个目标由五个浮点数组成0 0.410156 0.332031 0.351562 0.351562第一个数 0 是类别索引按摘要给出的类别顺序排列mild-DR0moderate-DR1normal2proliferation-DR3severe-DR4。后四个数按顺序是 x_center、y_center、width、height全部除以图像宽高完成归一化。这种格式与 YOLOv5/YOLOv8 数据加载器完全匹配放进labels/train目录即可直接参与训练。两种格式的字段对照关系如下属性VOC XMLYOLO txt坐标基准像素绝对坐标归一化相对坐标框表示xmin, ymin, xmax, ymaxx_center, y_center, width, height类别字符串名称整数索引图片尺寸每张独立记录不记录训练时由加载器读取文件后缀.xml.txt这张表在换框架时很实用。比如把 YOLO 标注转到 mmdetection 的 COCO 格式仍然需要先从 txt 反推出像素坐标再按 COCO 的 bbox 字段重组。2.2 XML 转 txt 的坐标换算逻辑如果手里只有 VOC 格式就要自己写转换脚本。核心公式是x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height四个分母来自每张图片自己的size字段而不是一个统一固定值。很多初学者用固定宽度高度去归一化遇到不同分辨率的图片标注框就会整体偏移。保险做法是在 Python 里逐张读取 xml 的宽高不假设所有原图尺寸相同。下面是一个可复用的转换脚本import xml.etree.ElementTree as ET from pathlib import Path VOC_DIR Path(./voc) TXT_DIR Path(./yolo) CLASSES [mild-DR, moderate-DR, normal, proliferation-DR, severe-DR] TXT_DIR.mkdir(exist_okTrue) for xml_path in VOC_DIR.glob(*.xml): tree ET.parse(xml_path) root tree.getroot() img_w int(root.findtext(./size/width)) img_h int(root.findtext(./size/height)) lines [] for obj in root.findall(./object): name obj.findtext(name) if name not in CLASSES: continue class_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path TXT_DIR / f{xml_path.stem}.txt out_path.write_text(\n.join(lines), encodingutf-8)这段脚本遍历 VOC 目录下所有 xml先读取图片宽高再遍历每个object节点完成坐标换算和归一化。CLASSES.index(name)将类别字符串转为整数 id这一步要求CLASSES列表的顺序和最终训练配置保持一致否则标签错位不会报错只会让模型收敛到错误映射。输出采用 6 位小数对 1024 以内的图像足够精确不会对 mAP 结果产生可见影响。2.3 常见误区归一化坐标系与类别 id 偏移拿到 txt 后先检查每行第一个数字是否落在 04 范围。如果之前处理过其他数据集把背景也算成一个类类别 id 可能整体右移导致 normal 样本变成 mild-DR。另一个易错点是坐标含义YOLO txt 里永远存储中心点坐标不是左上角。用 OpenCV 可视化时需要从中心点反推左上角x1 int((x_center - w / 2) * img_w) y1 int((y_center - h / 2) * img_h) x2 int((x_center w / 2) * img_w) y2 int((y_center h / 2) * img_h)这段反推代码用来把 YOLO 标注画回原图验证转换是否成功。如果画出的框和 xml 原框不一致多半是归一化计算有误或者读取 txt 时把宽高顺序颠倒。建议随机抽取 20 张图分别用 xml 和 txt 绘制边界框并叠加对比确认后再进入训练环节。3. 7z 解压与数据集自检从压缩包到可训练的目录结构3.1 Linux 解压 7z 文件数据集以 7z 压缩包发布Linux 下用 p7zip 解压是最直接的方式。系统里如果还没有 p7zip先安装sudo apt update sudo apt install -y p7zip-full 7z x 糖尿病肾病检测数据集VOCYOLO格式4122张5类别.7z -o./dataset-o指定输出目录注意-o后面不能有空格这是 p7zip 一个容易踩的小细节。解压后进入./dataset里面三类文件混放jpg、xml、txt。Windows 用户可以用 7-Zip 图形界面解压路径过长时建议放到盘符根目录避免系统长路径限制导致文件写不完整。提示解压后先统计文件数预期 jpg、xml、txt 各 4122 个总计 12366 个。数量不匹配就重新下载校验不要直接开始训练。这一步虽然简单却决定了后续所有操作的可靠性。压缩包损坏但部分解压成功数据加载器通常不会立刻报错直到某次 epoch 随机采样到缺失文件才会中断浪费大量等待时间。3.2 按训练要求重组目录YOLO 训练默认从images/train读图片从labels/train读对应 txt。需要把解压后的平铺文件按 train/val/test 拆分组织成以下结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/推荐写一个 Python 脚本来划分下面这段按 7:2:1 的比例切分并移动文件import random from pathlib import Path src Path(./dataset) imgs sorted(src.glob(*.jpg)) random.seed(42) random.shuffle(imgs) val_ratio 0.2 test_ratio 0.1 n_val int(len(imgs) * val_ratio) n_test int(len(imgs) * test_ratio) n_train len(imgs) - n_val - n_test splits { train: imgs[:n_train], val: imgs[n_train:n_train n_val], test: imgs[n_train n_val:], } for split, files in splits.items(): img_out Path(fdataset/images/{split}) label_out Path(fdataset/labels/{split}) img_out.mkdir(parentsTrue, exist_okTrue) label_out.mkdir(parentsTrue, exist_okTrue) for img in files: txt src / f{img.stem}.txt if not txt.exists(): print(fmissing label: {img.name}) continue img.rename(img_out / img.name) txt.rename(label_out / txt.name)这段脚本先按文件名排序再用固定随机种子打乱保证每次运行得到同样的划分结果。val_ratio0.2切出验证集test_ratio0.1留出最终测试集n_train为剩余样本数量。移动文件时检查 txt 是否存在若缺失则跳过该样本避免训练中断。划分后的目录结构就是后续数据集 yaml 的标准输入。3.3 标注完整性校验与坐标边界检查文件移动后要再跑一次完整性校验重点确认每个images/train下的 jpg 都有对应 txt。下面这段脚本可以快速扫描from pathlib import Path for split in [train, val, test]: img_dir Path(fdataset/images/{split}) label_dir Path(fdataset/labels/{split}) for img in img_dir.glob(*.jpg): txt label_dir / f{img.stem}.txt if not txt.exists(): print(missing txt:, img)检查时也注意空文件。如果某个 txt 文件内容为空说明该图没有目标YOLO 会跳过但保留空文件比删除更安全因为删除会导致图片没有对应标签加载器可能告警。边界检查主要防止坐标越界。YOLO 格式的坐标应在 01 之间一旦出现大于 1 或负数说明原始 xml 标注有误。常见检查项和处理方式如下检查项判断方法处理方式文件数匹配统计 jpg/xml/txt 数量不足时重新解压空标签检查 txt 文件大小保留空文件不删除图片坐标越界awk 检查任意列 1裁剪到 01 或剔除样本宽高为 0检查第 4、5 列 0删除对应行坐标越界的快速筛查可以直接用单行命令awk $21 || $31 || $41 || $51 {print FILENAME, $0} dataset/labels/train/*.txtawk遍历所有训练标签只要第 2 到第 5 列任意数值超过 1 就打印文件路径和整行内容。width 或 height 等于 0 的情况不能用这个命令捕获需要再查$40 || $50。这类脏数据会让损失函数出现 nan最好在训练前删掉对应行或直接剔除样本。4. YOLOv8 训练配置数据集 YAML、损失函数与关键超参数4.1 编写数据集 yaml 文件YOLOv8 用 yaml 描述数据集结构。在项目根目录新建dataset.yamlpath: /abs/path/to/dataset train: images/train val: images/val test: images/test names: 0: mild-DR 1: moderate-DR 2: normal 3: proliferation-DR 4: severe-DRpath建议写绝对路径避免工作目录切换导致找不到数据。train、val、test是相对path的图片目录YOLO 会自动到同级labels目录寻找同名 txt。names的索引必须和 txt 第一列的类别 id 完全对应否则训练时标签错位但不会报错。特别是从 COCO 预训练权重继续训练时原有类别 id 是 COCO 的 80 类加载 yaml 后会被覆盖所以这里的映射顺序是唯一基准。启动训练的常见命令yolo detect train datadataset.yaml modelyolov8n.pt epochs100 imgsz640 batch16yolov8n.pt是 nano 版预训练权重显存占用小适合先跑通流程。显存足够可以换yolov8s.pt或yolov8m.pt检测精度通常会有提升。imgsz640是常用输入尺寸但眼底影像的原图分辨率远高于 640如果病灶区域较小可以尝试imgsz1024不过显存占用会成倍上升。环境配置阶段AMD 显卡用户要额外确认 PyTorch 是否安装为 ROCm 版本否则 ultralytics 会找不到 CUDA 设备从而无法利用 GPU 加速。4.2 训练损失函数与观察指标YOLOv8 的损失由三部分构成box loss 边界框回归损失、cls loss 分类损失、dfl loss 分布焦点损失。训练日志会实时输出这三项指标。正常情况下三者在训练前 20 个 epoch 都有明显下降后期趋于平缓。如果 cls loss 反复震荡大概率是类别不平衡或学习率过高如果 box loss 长期不降优先检查标注质量而不是改模型结构。对眼底病灶这种边界不清晰的检测任务dfl loss 对框精度的贡献比 box loss 更敏感。关键超参数调整参考下面这张表参数建议值调参方向epochs100数据量小加大到 200配合早停batch16显存不足减小按比例降低学习率imgsz640小目标多升到 1024lr00.01loss 震荡时降到 0.005patience20验证集 mAP 停止增长就提前结束augmentTrue医疗影像慎用强旋转和翻转lr0初始学习率默认 0.01几千张图片的数据集上通常偏大容易让前几个 epoch 的 loss 剧烈跳动。可以先用lr00.005搭配cos_lrTrue跑一版再逐步调高。augment参数需要特别谨慎眼底影像中的微动脉瘤、出血点有方向性和位置先验过度翻转可能让模型学到错误的方向特征建议只开启颜色抖动和缩放Mosaic 可以保留但概率不要拉满。4.3 类别不平衡时的损失权重与样本策略五类样本在医学数据集中极少均匀normal 通常占大头proliferation-DR 和 severe-DR 可能只有几百张。直接训练会出现典型的长尾问题多数类精度高少数类 recall 低。一个快速验证不平衡影响的方法是统计验证集混淆矩阵观察少样本类是否被整体预测为相邻级别。处理策略有两个方向数据层面可以对少样本类图片做复制和光度扰动损失层面可以在训练配置中调整分类损失权重。如果要用更精细的类别权重推荐在数据加载阶段实现类别重采样。常见做法是写一个自定义 Dataset在__getitem__里根据类别分布随机选择样本保证每个 batch 内各类别比例相对均匀。实现成本不高但对 mAP50-95 的提升往往比调整学习率更明显。单纯调高cls损失系数会让模型更注意分类但对边界框回归没有帮助少样本类框不准的问题依然存在。因此不平衡严重的数据集优先做样本级过采样再考虑损失权重。4.4 验证模型与导出部署格式训练结束后用验证集评估最优权重yolo detect val modelruns/detect/train/weights/best.pt datadataset.yaml命令输出整体 mAP同时生成混淆矩阵、PR 曲线和类别指标。如果某个高风险类别比如 severe-DR 的 mAP 明显低于平均需要回到数据层补样本。验证阶段可以调低置信度阈值把低置信度的预测框可视化叠加到原图观察模型是在病灶区域内定位不准还是把背景伪影当成了病灶。部署阶段导成 ONNXyolo export modelbest.pt formatonnx opset12导出的模型可以进一步转成 TensorRT用于 RK3588 之类的边缘设备推理。导出前检查输入尺寸是否和训练时一致不同尺寸的模型在设备上的延迟差异很大。YOLOv8 的 NMS 过程默认已包含在导出图中但转 TensorRT 时要注意自定义 NMS 算子的兼容性必要时在推理端单独实现后处理流程。5. 类别不平衡下的 mAP 评估与样本筛选技巧5.1 统计类别分布与不均衡比训练前先统计各类别标注框数量明确数据分布。用 Python 读取全部训练标签from collections import Counter from pathlib import Path counter Counter() label_dirs [Path(dataset/labels/train), Path(dataset/labels/val), Path(dataset/labels/test)] for d in label_dirs: for txt in d.glob(*.txt): for line in txt.read_text().splitlines(): cls int(line.split()[0]) counter[cls] 1 for cls_id in range(5): print(cls_id, counter[cls_id])如果发现某类占比低于 5%模型大概率会把它忽略。一个可行的快速做法是先把 normal 类随机下采样到与中等类接近再用完整数据训练一版对比。对于医学数据集下采样可能会丢掉重要样本所以通常优先做少样本类过采样而不是直接丢弃 normal。5.2 用 mAP50 与 mAP50-95 判断模型改进方向mAP50 是 IoU 阈值 0.5 下的平均精度mAP50-95 是阈值从 0.5 到 0.95 每步 0.05 的均值。眼底病灶边界模糊mAP50-95 通常远低于 mAP50。如果 mAP50 上涨但 mAP50-95 停滞说明定位精度不够此时提高输入分辨率或增加 dfl loss 权重更有效。如果两者都停在低位先检查标签框是否贴合病灶边界再决定是否强化增强策略。5.3 从测试集中筛选易错样本做结构化复查把测试集预测结果按错误类型分类用脚本输出典型错误样本。人工复查时重点看两类一是标注本身有没有框偏二是模型是否抓住病灶纹理。对少样本类可以把它们的预测结果单独抽出来统计 confidence 分布找出模型不确定的样本再决定是补充训练还是调整类别权重。实际操作中对样本极少的类别可以在训练配置里把分类损失权重调高到 1.52.0但不要超过 3。权重过大会让模型对少数类过拟合严重时反而拉低整体 mAP。先用默认权重跑一版作为 baseline再逐步增加少数类权重每次对比验证集 mAP50-95 的变化而不是只盯训练集 loss。本文还有配套的精品资源点击获取