news 2026/9/23 15:33:07

糖尿病肾病眼底图像数据集:VOC/YOLO双格式详解与YOLOv8训练踩坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
糖尿病肾病眼底图像数据集:VOC/YOLO双格式详解与YOLOv8训练踩坑指南

简介:糖尿病肾病检测数据集面向医学影像中的糖尿病视网膜病变分级识别任务,提供完整的Pascal VOC与YOLO双格式标注数据。资源围绕5个临床分级类别展开,包含mild-DR、moderate-DR、normal、proliferation-DR、severe-DR,适用于医疗AI算法工程师、科研人员及高校相关专业学生进行模型训练、算法对比与毕业设计。压缩包共2000个文件,以XML标注文件为主,辅以TXT格式文件与使用说明,整体大小约44.31MB,解压后无需复杂转换即可接入VOC或YOLO训练流程,也可使用LabelImg等标注工具对XML标注进行二次校验;目前已有138人学习下载。资源在数据整理与格式适配方面较为便捷,五类病变分级覆盖从正常到增殖期的典型样本,有助于快速搭建糖尿病肾病检测基线、验证模型泛化能力,或进一步开展细粒度分类探索。

1. 糖尿病肾病检测数据集:VOC和YOLO双标注一次给全,先看清这批图像的门道

如果你在找一份能直接扔进YOLO训练的医学检测数据集,这批4122张糖尿病肾病相关眼底图像值得花十分钟了解。它同时提供了Pascal VOC的xml和YOLO的txt标注,覆盖normal、mild-DR、moderate-DR、severe-DR、proliferation-DR五个类别,没有做训练集/验证集切分,也没有mask分割文件,适合目标检测入门、迁移学习、毕业设计或者算法对比。很多新手拿到手就解压开训,结果类别索引错、xml和txt对不上,白跑一轮。下面我把解压、校验、格式转换、训练前检查到YOLOv8跑通的完整细节和踩坑记录都拆开讲,照着做能少走弯路。

2. 数据集的真实构成:VOC和YOLO两种格式并存,先看清再动手

很多人拿到压缩包第一反应是直接双击解压,然后一股脑扔给训练脚本。我建议你先花五分钟把里面的文件结构盘清楚,因为这类医学数据集常有“jpg、xml、txt数量一致但内容对不上”的隐患,特别是VOC和YOLO双格式同时提供时,两边的类别顺序和坐标范围很容易不一致。

2.1 压缩包解开后你应该看到什么

按照资源说明,压缩包解开后包含的是jpg图片、Pascal VOC格式的xml标注文件和YOLO格式的txt标注文件。数量上三种文件都是4122个,一一对应。还有一个“使用前必读.txt”,里面一般写的是类别名称、格式说明和引用信息,我习惯先读它。文件名是firc_shenbing_数字编号,比如firc_shenbing_1063.jpg对应firc_shenbing_1063.xml和firc_shenbing_1063.txt,这个命名规律能帮你快速定位问题样本。

先看文件类型清单:

文件类型数量内容说明用途
.jpg4122眼底图像模型输入
.xml4122Pascal VOC标注基础标注格式
.txt4122YOLO格式标注训练时直接读取

注意资源说明里特别提到“不包含分割路径的txt文件”,意思是压缩包里没有train.txt、val.txt这类文件列表,也没有像images/labels/这样强制分好的目录结构。你需要自己划分训练集和验证集,并决定要不要保留原始目录组织。目录结构大概率是下面这样:

. ├── 使用前必读.txt ├── firc_shenbing_1063.jpg ├── firc_shenbing_1063.txt ├── firc_shenbing_1063.xml ├── firc_shenbing_1065.jpg ├── firc_shenbing_1065.txt ├── firc_shenbing_1065.xml ...

如果你解压后发现发布者已经按images、annotations、labels分好了文件夹,原理一样,不影响下面的检查操作。先做一次最基础的数量校验。在Linux或macOS的终端里进入数据集目录,执行:

ls *.jpg | wc -l ls *.xml | wc -l ls *.txt | wc -l

三条命令分别统计jpg、xml、txt文件的个数。如果和4122对不上,说明压缩包不完整或者有重复文件,后续训练前一定要处理掉。注意wc -l统计的是行数,如果文件名带换行符会统计错,但这种医学图像数据集基本不会出现。如果担心,可以用find . -name "*.jpg" | wc -l代替。

如果你的文件分散在多个子目录里,把ls换成find即可:

find . -name "*.jpg" | wc -l find . -name "*.xml" | wc -l find . -name "*.txt" | wc -l

这一步看起来初级,但能过滤掉一半的“缺文件”问题。我遇到过有人用网盘下载时部分小文件被静默跳过,最后train阶段报错找不到对应标注,往往就是这里没查。

2.2 VOC标注和YOLO标注的坐标对应关系

先打开一个xml文件看一眼。VOC标注每个目标由object节点描述,包含类别名和bndbox框坐标:

<annotation> <size> <width>960</width> <height>540</height> <depth>3</depth> </size> <object> <name>mild-DR</name> <bndbox> <xmin>128</xmin> <ymin>96</ymin> <xmax>384</xmax> <ymax>288</ymax> </bndbox> </object> </annotation>

这里xmin、ymin是框左上角坐标,xmax、ymax是右下角坐标,单位是像素,基于原始图片尺寸。而对应的YOLO格式txt文件里,一行代表一个目标对象,格式是:

0 0.266667 0.355556 0.266667 0.355556

这五个数字依次是:类别索引、归一化中心点x坐标、归一化中心点y坐标、归一化框宽、归一化框高。类别索引不是字符串,是数字,顺序由你在训练配置里定义的classes列表决定,这一点是新手最容易踩的坑。

从VOC到YOLO的坐标换算关系是:

x_center = (xmin + xmax) / 2 / image_width y_center = (ymin + ymax) / 2 / image_height box_width = (xmax - xmin) / image_width box_height = (ymax - ymin) / image_height

注意除以的是图片的实际宽高,不是标注里某个框的宽高。很多人在这里把width/height取成了xml里第一个object的框宽,结果所有归一化坐标全部溢出。用上面给出的xml实例代入:图片宽960,高540,框左上角(128,96),右下角(384,288)。那么x_center=(128+384)/2/960=0.2667,y_center=(96+288)/2/540=0.3556,w=(384-128)/960=0.2667,h=(288-96)/540=0.3556。正好对应txt里的数字。

需要特别注意的是,txt中数字的顺序只与CLASSES列表顺序有关,与xml文件中object的出现顺序无关。如果同一个图片里有三个框,txt里就是三行,每行独立。读取VOC标注时,你可以改变object的遍历顺序,但写入txt时数字索引必须严格按照CLASSES列表来。

2.3 用脚本摸清类别分布和空标注情况

在开始训练前,我强烈建议统计一下每个类别的目标数量。医学影像数据集普遍存在类别不均衡,正常样本往往远多于病变样本。写一个Python脚本扫一遍xml:

import glob import xml.etree.ElementTree as ET from collections import Counter xml_files = glob.glob("your_dataset_path/*.xml") class_counter = Counter() empty_images = [] for xml_file in xml_files: tree = ET.parse(xml_file) root = tree.getroot() objects = root.findall("object") if len(objects) == 0: empty_images.append(xml_file) for obj in objects: name = obj.find("name").text.strip() class_counter[name] += 1 print("类别统计:", dict(class_counter)) print("空标注图片数:", len(empty_images)) print("部分空标注文件:", empty_images[:5])

脚本思路是遍历所有xml,找到每一个object节点的name文本,用Counter计数;同时如果某个xml文件里没有任何object,则记为空标注图片,后续训练时应直接排除或人工复标。注意obj.find("name").text.strip()里的strip()很有必要,因为有些标注工具会在类别名两侧留下空格,不清理会出现“normal”和“ normal ”两个类名,YOLO训练时直接多出一个类别。

如果这个资源按摘要给出了类别顺序为["mild-DR","moderate-DR","normal","proliferation-DR","severe-DR"],你统计出来也应该是这五个名字,且数量总和等于所有xml里object的总数。脚本运行后输出的类别统计可以直观告诉你哪一类样本少:如果severe-DR或proliferation-DR的数量远低于normal,后面训练时必须处理样本不均衡,否则模型会对多数类过拟合。

如果你想进一步核对txt里的类别索引是否和这个顺序一致,可以这样交叉验证:

import glob for txt_file in glob.glob("your_dataset_path/*.txt"): with open(txt_file) as f: for line in f: parts = line.strip().split() if len(parts) != 5: print("字段数异常:", txt_file, line)

这只是检查字段数量,更严格的索引检查要结合转换脚本完成,后面第四章会详细讲。

3. 解压和校验:7z包的打开方式与数据完整性检查

压缩包是.7z格式,这和常见的zip、rar不太一样。7z的压缩率高,但需要有对应的解压工具,很多Linux服务器默认不带7z命令,直接解压会报“command not found”。这一章把解压、关联校验和标签数值范围检查一次说清。

3.1 Linux和Windows下正确解压7z文件

在Ubuntu/Debian服务器上,先装p7zip:

sudo apt install p7zip-full

然后解压到当前目录,保留包的目录结构:

7z x 糖尿病肾病检测数据集VOC+YOLO格式4122张5类别.7z

x表示保留完整路径,如果你不想破坏当前目录结构,可以指定输出目录:

7z x 糖尿病肾病检测数据集VOC+YOLO格式4122张5类别.7z -o./DRD_dataset

注意参数-o后面紧跟目录路径,中间没有空格,写成-o /path会报错。如果你需要覆盖已存在的文件,加-y参数跳过询问:

7z x dataset.7z -y -o./DRD_dataset

解压完成后可以用ls DRD_dataset | wc -l看文件数量是否和预期一致。如果压缩包设置了密码,在命令行里用-p传入,但密码含特殊字符时极易出错,后面避坑章节细说。

Windows环境下推荐直接用7-Zip软件。右键压缩包,在“7-Zip”子菜单里选择“提取到当前文件夹”即可。如果右键菜单里没有7-Zip,也可以先打开7-Zip主界面,再双击压缩包进入,最后点“提取”。我一般不用Windows自带的“全部解压缩”去解7z,因为这个功能只支持zip格式,强行解7z会提示格式不支持。

这里有个非常常见的问题:明明密码输入正确,但7z一直提示密码错误。其实绝大多数情况不是密码错,而是命令行下密码里的特殊字符被shell转义了。比如密码包含$&!等字符,在bash里直接输入会被解释成变量或历史命令。解决方法是把密码用单引号包起来:

7z x dataset.7z -p'实际密码'

或者在Windows图形界面里手动复制密码粘贴,避免键盘布局或输入法干扰。还有一小部分情况是压缩包本身做了Unicode文件名编码,解压时提示文件名乱码但密码仍报错,这时候先用LANG=C 7z l dataset.7z列出内容看看是否能正常读取列表,能读取就说明密码没问题,是操作系统编码问题,换7-Zip图形界面或加-mcp=932这类参数再试。

3.2 用Python核对图片和标注是否一一对应

不要相信眼睛,直接用脚本统计最稳妥。下面的脚本会找出有多余或缺失的标注文件:

from pathlib import Path data_dir = Path("dataset_path") jpg_stems = {p.stem for p in data_dir.glob("*.jpg")} xml_stems = {p.stem for p in data_dir.glob("*.xml")} txt_stems = {p.stem for p in data_dir.glob("*.txt")} print("图片数:", len(jpg_stems)) print("xml数:", len(xml_stems)) print("txt数:", len(txt_stems)) print("缺xml的图片:", jpg_stems - xml_stems) print("缺txt的图片:", jpg_stems - txt_stems) print("多余xml:", xml_stems - jpg_stems) print("多余txt:", txt_stems - jpg_stems)

Path.stem返回不带后缀的文件名,比如firc_shenbing_1063.jpg的stem是firc_shenbing_1063。通过集合差运算能快速找到不匹配项。如果缺xml,说明该图片没有标注,不能参与VOC相关训练;如果缺txt,说明该图片没有YOLO标签,直接训练时会报“missing label”错误。

这里要说明一点:如果发布者把jpg、xml、txt分别放在三个子目录里,改一下glob路径,比如data_dir.glob("images/*.jpg")data_dir.glob("labels/*.txt"),逻辑不变。注意三个子目录的stem集合要基于同一层级拼接,避免同名不同文件造成假阳性。

除了文件名对应,图片本身也要检查是否损坏。尤其是网盘下载的压缩包,偶发某个jpg文件头被截断。用Pillow库快速扫一遍:

from PIL import Image import glob broken = [] for img_file in glob.glob("dataset_path/*.jpg"): try: img = Image.open(img_file) img.verify() # 不加载像素,只验证文件头 except Exception as e: broken.append((img_file, str(e))) print("损坏图片数:", len(broken)) for item in broken[:10]: print(item)

img.verify()只检查文件结构是否正确,速度很快,不会把整个图像读入内存。如果扫出来损坏图片,单独记录文件名,训练前从数据清单里剔除或用备份文件替换。这个检查对YOLO训练特别重要,因为一旦dataloader读到坏图,可能整个epoch直接崩掉。

3.3 检查YOLO标签数值是否落在0到1区间

YOLO的归一化坐标本应在[0,1]范围内,但很多转换脚本没做边界保护,偶尔会出现cx、cy或wh略大于1的情况。这种标签训练时不会直接报错,但会导致loss异常大,推理时预测框全部偏向图像边缘。写一个检查脚本:

import glob bad = [] for txt_file in glob.glob("dataset_path/*.txt"): with open(txt_file) as f: for line_no, line in enumerate(f, 1): parts = line.strip().split() if len(parts) != 5: bad.append((txt_file, line_no, "字段数不是5", line.strip())) continue try: cls_id, cx, cy, w, h = map(float, parts) except ValueError: bad.append((txt_file, line_no, "数字解析失败", line.strip())) continue if not (0 <= cx <= 1 and 0 <= cy <= 1 and 0 < w <= 1 and 0 < h <= 1): bad.append((txt_file, line_no, f"越界 c={cx:.3f},{cy:.3f} w={w:.3f} h={h:.3f}", line.strip())) print("异常行数:", len(bad)) for item in bad[:10]: print(item)

这个脚本把每个txt文件的每一行拆成五个字段,逐一检查类别索引是否为有效数字、中心坐标和宽高是否在合法区间。如果找到了越界标签,需要回溯对应的xml和原始图片,重新计算或用第四章的转换脚本重新生成。正常情况下,这份数据集应该一行异常都没有。如果你看到大量越界,建议先怀疑解压是否完整,再怀疑发布者的转换脚本是否靠谱。

4. 把VOC转成YOLO格式:转换脚本、坐标换算和两个绕不开的坑

虽然这份资源已经同时给了xml和txt,但你在实际使用中几乎一定会遇到需要自己转换的场景:比如给这批数据补充新标注、把其他来源的VOC数据合并进来、或者想更换类别顺序。自己掌握转换逻辑,比每次都求助于在线转换网站可靠得多。

4.1 一个可以直接跑的VOC转YOLO脚本

下面是一个我常用的批量转换脚本,直接用Python标准库,不依赖额外包:

import os import glob import xml.etree.ElementTree as ET # 类别顺序:必须与后续训练data.yaml中的names顺序完全一致 CLASSES = ["mild-DR", "moderate-DR", "normal", "proliferation-DR", "severe-DR"] def convert_voc_to_yolo(xml_path, output_dir): tree = ET.parse(xml_path) root = tree.getroot() img_w = float(root.find("size/width").text) img_h = float(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASSES: print(f"跳过未知类别 {name} in {os.path.basename(xml_path)}") continue cls_id = CLASSES.index(name) bndbox = obj.find("bndbox") xmin = float(bndbox.find("xmin").text) ymin = float(bndbox.find("ymin").text) xmax = float(bndbox.find("xmax").text) ymax = float(bndbox.find("ymax").text) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") if lines: os.makedirs(output_dir, exist_ok=True) txt_path = os.path.join(output_dir, os.path.basename(xml_path).replace(".xml", ".txt")) with open(txt_path, "w") as f: f.write("\n".join(lines)) if __name__ == "__main__": xml_dir = "path/to/xmls" out_dir = "path/to/txts" for xml_file in glob.glob(os.path.join(xml_dir, "*.xml")): convert_voc_to_yolo(xml_file, out_dir)

几个关键点你需要知道。

CLASSES列表的顺序直接决定输出txt中每个框的类别索引。如果这个列表和训练时的data.yaml里的names顺序不一致,模型会学混淆。我见过有人VOC转换时用字典排序,训练时用手写顺序,结果normal和mild-DR整体置换,测试时完全没法看。

root.find("size/width")这里用了带斜杠的路径,ElementTree支持这种相对查找。有些标注工具会在不同层级放置图片尺寸信息,比如在annotation下有个segmented节点,宽度信息就放在size节点里。转换前先打印一个xml的root结构,确认路径不出错。

f"{cls_id} {x_center:.6f} ..."用格式化字符串控制小数位数,六位小数已经足够YOLO训练使用,不必保留全部浮点位数,文件体积也更小。如果你要把这个脚本改成命令行工具,可以在末尾加argparse来接收xml目录和输出目录,这样不修改代码就能复用到其他VOC数据集。

4.2 归一化坐标的边界保护和负值处理

医学影像里经常出现所谓“贴边框”——病灶区域一直延伸到图片边缘,人工标注时会把xmax标成图片宽度。这时候(xmax - xmin)除以图片宽度正好等于1.0,YOLO的w=1.0是合法值,但如果某些图像增强库对1.0宽度做了缩放,容易在Mosaic或RandomAffine时产生NaN。我一般会在转换时加一个下限保护:

w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h # 防止浮点误差造成的负值或零值 w = max(min(w, 1.0), 1e-6) h = max(min(h, 1.0), 1e-6) x_center = max(min(x_center, 1.0), 0.0) y_center = max(min(y_center, 1.0), 0.0)

1e-6是个很小的数,避免因标注错误导致宽度为0。如果你发现某个框的xmin大于xmax,说明原始xml有坐标顺序问题,这类样本需要记录下来另行处理,不要默默clip掉。

转换前最好先校验VOC框的合法性,省得生成一堆无意义的txt:

for obj in root.findall("object"): bndbox = obj.find("bndbox") xmin = float(bndbox.find("xmin").text) ymin = float(bndbox.find("ymin").text) xmax = float(bndbox.find("xmax").text) ymax = float(bndbox.find("ymax").text) if xmin >= xmax or ymin >= ymax: print(f"非法框: {os.path.basename(xml_path)}, {obj.find('name').text}") continue if xmin < 0 or ymin < 0 or xmax > img_w or ymax > img_h: print(f"越界框: {os.path.basename(xml_path)}, {obj.find('name').text}")

这段代码把异常框打印出来,而不是带病转换。很多老数据集里有个别框坐标溢出图片边界,不处理的话训练时可能不会崩溃,但推理结果会莫名对不上。

4.3 坑:类别索引从0开始,以及Windows环境的路径转义

我见过最典型的翻车就是把类别索引写成从1开始。VOC里第一个类别的名字对应YOLO索引0,很多人受Excel表格从1开始的习惯影响,把mild-DR写成了1,结果所有框都被误判成moderate-DR。这个问题不会导致训练崩溃,但会让precision和recall完全错乱,且很难发现,因为loss还在下降。

排查方法是随机抽几个txt文件,读取第一个数字,再回到对应的xml里看第一个object的name,对照CLASSES列表确认索引一一对应。下面的代码可以帮你做这个抽查:

import glob import random CLASSES = ["mild-DR", "moderate-DR", "normal", "proliferation-DR", "severe-DR"] sample_txts = random.sample(glob.glob("path/to/txts/*.txt"), 5) for txt_path in sample_txts: xml_path = txt_path.replace(".txt", ".xml") with open(txt_path) as f: first_line = f.readline().strip() cls_id = int(first_line.split()[0]) print(txt_path, "->", CLASSES[cls_id], "对应xml中的第一个object")

另一个坑在Windows下批量处理时,路径分隔符是反斜杠。如果代码里直接手写路径拼接,比如out_path = "labels/" + txt_file.split("\\")[-1],一旦漏掉转义就会把路径写错。我习惯统一用os.path.basenamePath(txt_file).name来取文件名,不要在字符串里手工处理目录分隔符。

批量转换完成后,务必再跑一次数量校验:

ls path/to/xmls/*.xml | wc -l ls path/to/txts/*.txt | wc -l

两个数字应该一致。不一致时优先查是否有xml里没有object的情况,因为我的脚本在lines为空时不会创建txt文件,这是预期行为,但要心里有数。

5. 训练前排查清单:五个最容易翻车的细节

这一章不聊网络结构,只聊把数据集喂给YOLO之前,最值得花时间排除的五个坑。每一个都是我或者周围同事实际踩过的,写出来帮大家省时间。

5.1 现象一:训练日志里类别数不对,mAP总在低位徘徊

原因:xml里有类别名和txt里的索引对不上,或者某些txt里写了不在CLASSES范围内的类别索引。这个数据集本身类别固定5个,但如果你合并过其他数据,或者用labelimg重新保存过xml,可能会出现类别名大小写不一致,比如“Normal”和“normal”被当成两个类别。

解决:训练前用第四章的抽查脚本,从txt反查xml,确认每个数字索引都对应正确的类别名。同时跑一遍全量标签扫描:

grep -RhoE "^[0-9]+" labels/ | sort -n | uniq -c

这条命令统计labels目录下所有txt第一个字段(类别索引)的出现次数。如果输出里出现了5以上的数字,说明有越界类别索引,需要立即定位并修正。注意grep的-o会匹配出每个符合条件的数字,-h禁止输出文件名,-R递归目录。

5.2 现象二:图片能正常打开,但训练时突然报错“Expected 3 input channels, got 4”

原因:一部分医学图像是RGBA四通道,或者带有透明度通道。YOLOv8默认输入是三通道RGB,四通道图片在数据加载阶段会直接崩溃。这类图片用系统自带看图工具能显示,肉眼很难发现。

解决:写一段预处理脚本把所有图片统一转成RGB三通道,并覆盖保存:

from PIL import Image import glob for img_path in glob.glob("dataset_path/*.jpg"): img = Image.open(img_path).convert("RGB") img.save(img_path, quality=95)

convert("RGB")会把RGBA、灰度图、调色板图都统一成RGB三通道。注意如果原图是灰度图,转换后虽然变成三通道,但三个通道内容相同,训练速度会稍慢,但不影响推理。如果还想顺便修复EXIF旋转方向,可以用Pillow的ImageOps.exif_transpose

5.3 现象三:所有验证结果都偏向normal,病变框全部漏检

原因:这是典型的类别不均衡。医学筛查类数据集中正常样本占比很高,这个数据集虽然每张图都有标注,但可能出现1000个normal目标对应200个proliferation-DR目标。模型训练时对占多数类的样本过拟合,导致少数类几乎不预测。

解决:先跑2.3节的统计脚本,打印每个类别的目标个数。如果少数类数量少于多数类的一半,建议做三类处理:

一是按类别比例重采样图片,让每类的目标数量接近;二是使用YOLOv8的class_weights参数,给少样本类别更大损失权重;三是简单粗暴,把少数类的图片复制几份加入训练集,但不建议复制超过3次,否则模型会对重复样本过拟合。对于这份数据,我建议先看看normal和其他DR分级的比例,再决定用哪种方案。

5.4 现象四:自己划分训练集和验证集后,训练集loss和验证集loss趋势一样,但实测效果差

原因:数据划分时出现了泄漏。最常见的做法是用random.shuffle打乱所有文件名再按比例切分,但你可能在划分前解压了两次,数据集路径里包含了两份完全相同的图片副本,或者同一个病人的不同眼底图被随机分到了训练集和验证集。由于医学数据集没有官方划分,文件编号里的firc_shenbing_可能蕴含患者级信息,跨患者验证才更合理。

解决:先按文件名去重,再按编号分组划分。简单做法是只保留一份副本,然后固定随机种子:

import os import random random.seed(42) files = os.listdir("dataset_path") random.shuffle(files) split = int(len(files) * 0.8) train_files = files[:split] val_files = files[split:]

固定随机种子能保证每次运行划分结果一致,避免调参时因数据划分不同导致无法复现实验。如果你怀疑存在患者级泄漏,可以先把文件名前缀相同的图片全部归入同一个集合再划分,这样验证集更接近真实场景。

5.5 现象五:解压后txt文件行尾是^M字符,训练时标签解析报错

原因:在Windows下编辑过txt后,行尾符可能从LF变成CRLF,某些YOLO数据加载器在Linux下解析时会把\r一并读入,导致坐标值变成0.266667\r,float()转换失败。

解决:用sed清理整个labels目录的行尾符:

sed -i 's/\r$//' dataset_path/*.txt

sed -i直接修改原文件,s/\r$//表示把每行结尾的回车符删除。改完后再跑一次3.3节的检查脚本,确认没有解析异常。这个坑在Windows下用labelimg保存的txt里特别常见,初看文件完全正常,但一进训练就报错。

6. 用这个数据集跑通YOLOv8:目录组织、配置文件和验收技巧

当你把前面的检查都做完,下面这些步骤能让你在半小时内跑出第一版模型。

先把数据集组织成YOLO要求的目录结构。我习惯在项目根目录下建一个datasets/DRD文件夹,然后按train/val划分图片和标签:

datasets/DRD/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/

用下面的Python脚本完成划分并复制文件:

import shutil from pathlib import Path src = Path("dataset_path") dst = Path("datasets/DRD") split = 0.8 jpg_files = sorted(src.glob("*.jpg")) stop = int(len(jpg_files) * split) for i, jpg in enumerate(jpg_files): sub = "train" if i < stop else "val" shutil.copy(jpg, dst / "images" / sub / jpg.name) shutil.copy(jpg.with_suffix(".txt"), dst / "labels" / sub / jpg.with_suffix(".txt").name)

然后写data.yaml:

path: datasets/DRD train: images/train val: images/val names: 0: mild-DR 1: moderate-DR 2: normal 3: proliferation-DR 4: severe-DR

训练命令我一般用:

yolo train data=datasets/DRD/data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0

yolov8n.pt是小模型,适合先验证数据管线通不通。epochs先跑100看趋势,batch大小按显存调整,12GB显存用16通常没问题。

训练完成后,验证阶段我会这么做:跑一次带混淆矩阵的完整验证:

yolo val model=runs/detect/train/weights/best.pt data=datasets/DRD/data.yaml plots=True

然后重点看confusion_matrix.png,不是只看mAP。医学检测场景里,漏掉一个病变比多框一个正常区域严重得多,所以我会把置信度阈值调低到0.15左右,观察recall曲线的变化。再拿一批完全没参与训练的外部图片,逐个跑推理,记录误检和漏检,专门看normal和proliferation-DR这类边缘类别。

我很早以前踩过一个坑:只依赖mAP选模型,结果某个模型mAP挺高,但对severe-DR的recall只有0.5,因为正样本太少被整体带偏。从那以后,我每次训练完都强制自己走一遍“混淆矩阵 + 外部图片推理样本”的验证流程,并对每一类单独看precision/recall,不再用单一指标说事。这套流程对这份糖尿病肾病数据集同样适用,希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 15:33:04

SAP销售寄售配置全攻略:客户寄售库存与631/633移动类型解析

简介&#xff1a;SAP销售寄售业务配置与操作讲解PDF&#xff0c;面向SAP SD模块顾问、后勤实施人员及ERP从业者&#xff0c;尤其适合正在学习寄售流程或需要落地相关配置的读者。内容系统梳理寄售全流程&#xff0c;涵盖寄售补货&#xff08;KB&#xff09;、寄售结算&#xff…

作者头像 李华
网站建设 2026/9/23 15:33:02

NS-3 TDMA仿真工程实战:从静态时隙分配到动态调度

简介&#xff1a;这份资源是面向通信工程、电子信息类专业毕业设计的学习资料包&#xff0c;围绕TDMA时分多址技术展开&#xff0c;适合正在做无线通信方向课题、需要理解多用户共享频带机制的学生参考。内容涉及帧结构、时隙分配、同步机制、信道编码与交织、功率控制、切换漫…

作者头像 李华
网站建设 2026/9/23 15:29:49

SpringBoot+协同过滤算法构建大学生兼职平台实践

1. 项目概述作为一名有多年全栈开发经验的工程师&#xff0c;我最近完成了一个基于SpringBoot的大学生兼职平台项目。这个项目源于我观察到当前大学生兼职市场存在的信息不对称、匹配效率低下等问题。传统兼职平台往往缺乏有效的审核机制和智能推荐功能&#xff0c;导致学生和企…

作者头像 李华
网站建设 2026/9/23 15:26:06

基于BP神经网络与近红外光谱的汽油辛烷值预测MATLAB实现

简介&#xff1a;面向数据分析与机器学习初学者&#xff0c;围绕使用BP神经网络预测汽油辛烷值这一具体任务&#xff0c;切实解决工业场景中辛烷值难以快速准确测量的痛点&#xff0c;提供从数据理解、网络结构搭建、隐藏层设计、参数设置到训练评估的完整实践案例。压缩包共2个…

作者头像 李华
网站建设 2026/9/23 15:25:55

政务大模型落地实践:私有化部署、AI网关与RAG知识库全解析

简介&#xff1a;全省一体化政务平台接入AI大模型应用方案是一份面向政务平台管理人员、信息技术人员及政策制定者的完整设计文档&#xff0c;重点解决政务服务智能化升级、用户体验优化与数据安全等核心问题&#xff0c;适合有一定技术背景的读者作为规划参考。资源共1个docx文…

作者头像 李华