简介这份眼睛疾病分类数据集面向医学图像处理初学者、计算机视觉课程实验者及需要快速验证分类模型的研究人员解决眼疾图像数据获取难、预处理繁琐的问题。数据按文件夹组织可直接用ImageFolder加载无需额外清洗。压缩包共604个文件以601张jpeg图像为主另含1个py可视化脚本、1个png示例图和1个json分类字典整体约61.34MB。目录下分train与test两个子集训练集481张、测试集120张覆盖白内障、青光眼、正常、视网膜疾病4个类别并附4类字典文件也可直接用于YOLOv5分类任务。资源提供随机展示4张图片的可视化脚本无需修改即可运行方便快速检查样本分布与图像质量。目前已有413人学习下载适合作为课程作业、模型对比实验或轻量级医学AI入门的基准数据。1. 眼科四分类数据集70MB 的小体量为什么反而更适合跑通全流程眼科图像分类这件事真正卡住大多数人的从来不是模型结构而是数据。你搜「眼疾分类数据集」跳出来的要么是几百 GB 的眼底图需要申请权限要么是标注格式五花八门光写解析脚本就得耗掉一个下午。这份眼睛疾病分类数据集走的是另一条路70 MB 总大小train 481 张、test 120 张四个类别——白内障、青光眼、正常、视网膜疾病按文件夹分好ImageFolder直接打开就能用。它适合谁适合想快速验证一个分类 pipeline 能不能跑通的工程师适合教学场景里需要一份「不折腾」数据的讲师也适合拿它当模板去替换成自己业务数据的开发者。体量小意味着你可以在几分钟内完成一轮训练把注意力放在数据增强、类别不均衡、评估指标这些真正影响结果的地方而不是等 GPU 排队。下面从目录结构开始把这份数据集的用法和边界一次讲清楚。2. 目录结构与加载方式ImageFolder 能直接吃但有两个前提2.1 文件夹布局与类别映射拿到数据集后先别急着写训练脚本用一条命令把结构看清楚。常见做法是# 查看目录树确认 train/test 下是否按类别分子文件夹 find data -type d | sort你会看到类似这样的结构data/ ├── train/ │ ├── cataract/ │ ├── glaucoma/ │ ├── normal/ │ └── retina_disease/ └── test/ ├── cataract/ ├── glaucoma/ ├── normal/ └── retina_disease/ImageFolder的硬性要求是每个类别一个子文件夹文件夹名就是类别标签且 train 和 test 下的类别文件夹名称必须完全一致。这份数据集满足这个条件所以下面这行代码能直接跑from torchvision.datasets import ImageFolder from torchvision import transforms # 训练集加增强 train_tf transforms.Compose([ transforms.Resize((224, 224)), # 统一尺寸适配主流骨干 transforms.RandomHorizontalFlip(), # 眼底图左右翻转通常安全 transforms.ToTensor(), ]) train_ds ImageFolder(rootdata/train, transformtrain_tf) print(train_ds.classes) # 类别名列表顺序即标签索引 print(len(train_ds)) # 应为 481逻辑说明ImageFolder在初始化时会扫描 root 下所有子目录按字母序给类别编号classes属性返回的就是这个顺序。参数上transform决定每张图被读进来后做什么处理训练集和测试集要用不同的 transform——训练集加随机增强测试集只做 Resize 和 ToTensor否则评估结果会失真。2.2 训练集与测试集的 transform 差异很多人在这里翻车训练时用了RandomHorizontalFlip验证时忘了去掉导致同一张图每次评估结果都在跳。正确做法是分开定义# 测试集只做确定性变换 val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), ]) val_ds ImageFolder(rootdata/test, transformval_tf) print(len(val_ds)) # 应为 120注意Resize的目标尺寸要和你的骨干网络匹配。用 ResNet 系列一般是 224用 EfficientNet 可能是 300 或 380。尺寸不匹配不会报错但会浪费预训练权重的优势。另外这份数据集没有提供单独的验证集目录只有 train 和 test所以实践中我一般会从 train 里按 8:2 切出一部分做验证test 留到最后只跑一次。提示切分验证集时用固定随机种子否则每次跑出来的验证集不一样指标没法横向对比。3. 四分类训练实战从 DataLoader 到混淆矩阵3.1 构建 DataLoader 与类别不均衡检查数据加载器本身不复杂但 batch size 和 shuffle 的设置直接影响训练稳定性from torch.utils.data import DataLoader, random_split # 从训练集切 20% 做验证 val_size int(0.2 * len(train_ds)) train_size len(train_ds) - val_size train_sub, val_sub random_split( train_ds, [train_size, val_size], generatortorch.Generator().manual_seed(42) # 固定种子 ) train_loader DataLoader(train_sub, batch_size32, shuffleTrue, num_workers2) val_loader DataLoader(val_sub, batch_size32, shuffleFalse, num_workers2) test_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers2)参数说明batch_size32对 481 张图来说大约每轮 12 个 batch梯度更新频率够用num_workers在 Windows 上建议设 0 或 2设大了容易卡在共享内存上。shuffleTrue只给训练集验证和测试必须关掉。在开跑之前先数一下每个类别的样本量from collections import Counter labels [y for _, y in train_ds.samples] print(Counter(labels))如果四个类别数量差距超过 2:1就要考虑加权采样或类别权重。这份数据集四个类别大致均衡但替换成自己的数据后这一步不能省。3.2 训练循环与关键监控指标训练脚本的核心不是 loss 下降而是验证集上的表现是否同步改善import torch import torch.nn as nn from torchvision.models import resnet18 device torch.device(cuda if torch.cuda.is_available() else cpu) model resnet18(weightsIMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 4) # 四分类 model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) for epoch in range(15): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() # 验证阶段 model.eval() correct total 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) preds model(imgs).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fepoch {epoch}: val_acc {correct/total:.4f})逻辑说明resnet18的预训练权重来自 ImageNet换成 4 类输出后只训练全连接层也能拿到不错的结果。lr1e-4配合AdamW是分类任务的稳妥起点如果 loss 震荡就降到 5e-5。15 个 epoch 对 481 张图来说足够收敛再多容易过拟合。3.3 混淆矩阵与分类报告准确率会骗人尤其当某一类特别难分的时候。用sklearn出一份分类报告from sklearn.metrics import classification_report, confusion_matrix import numpy as np model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in test_loader: imgs imgs.to(device) preds model(imgs).argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namestrain_ds.classes)) print(confusion_matrix(all_labels, all_preds))重点看两件事一是glaucoma和normal之间有没有大量互串二是retina_disease的召回率是否明显低于其他类。如果某一类召回率低于 0.7优先补该类样本或加类别权重而不是调模型结构。4. 避坑与排查小数据集上最容易踩的五条4.1 现象训练准确率 99%测试准确率 60%原因481 张图切了验证集之后训练子集更小模型几轮就记住了。加上眼底图本身类间差异小过拟合来得特别快。解决把weight_decay提到 1e-3加Dropout在 fc 前插一层nn.Dropout(0.5)并做早停——验证准确率连续 3 轮不升就停。4.2 现象ImageFolder报FileNotFoundError原因root 路径写错或者解压后多了一层嵌套目录比如data/data/train。解决先用find确认实际路径再把 root 指到包含类别子文件夹的那一层。不要凭感觉写路径。4.3 现象某些图片读进来是灰度图或 RGBA原因眼底图来源不统一部分图片通道数不是 3。解决在 transform 里加transforms.Lambda(lambda x: x.convert(RGB))放在Resize之前。这一步不加上ToTensor之后通道数对不上模型直接报错。4.4 现象验证集准确率每次跑都不一样波动超过 5%原因random_split没固定种子或者验证集 transform 里混入了随机增强。解决固定torch.Generator().manual_seed()并检查验证集用的 transform 是否只包含确定性操作。4.5 现象换成自己的数据后类别数对不上原因model.fc的输出维度写死了 4但新数据的类别文件夹数量变了。解决用len(train_ds.classes)动态设置输出维度不要硬编码。同时确认新数据的类别文件夹命名和旧的一致否则标签映射会错位。5. 进阶技巧用可视化脚本快速验数据再决定要不要清洗这份数据集带了一个可视化 py 文件随机传 4 张图就能展示并保存到当前目录。很多人拿到数据直接开训跑完发现某些类别里混了明显不对的图回头再查已经浪费了半天。我的习惯是训练之前先跑一遍可视化把每个类别各抽几张看一眼。import random import matplotlib.pyplot as plt from PIL import Image from pathlib import Path def preview(root, n4): 从每个类别随机抽 n 张图拼成一行展示 classes sorted([d.name for d in Path(root).iterdir() if d.is_dir()]) fig, axes plt.subplots(len(classes), n, figsize(3*n, 3*len(classes))) for i, cls in enumerate(classes): imgs list((Path(root) / cls).glob(*.jpeg)) for j, p in enumerate(random.sample(imgs, min(n, len(imgs)))): axes[i][j].imshow(Image.open(p)) axes[i][j].set_title(cls) axes[i][j].axis(off) plt.tight_layout() plt.savefig(preview.png, dpi100) print(saved to preview.png) preview(data/train)逻辑说明Path.glob(*.jpeg)匹配该类别下所有 jpeg 文件random.sample保证抽到的图不重复。参数n控制每类抽几张默认 4 张四个类别就是 4×4 的网格。跑完打开preview.png重点看三件事图片是否清晰可辨、类别标签和内容是否一致、有没有明显不属于该类的图混进来。如果发现某个类别里有脏数据别急着删。先统计脏数据占比低于 5% 可以直接剔除高于 5% 就要考虑是不是类别定义本身有歧义。眼底图里glaucoma和normal的边界有时候连专科医生都要结合眼压和视野报告才能判断单张图看不出来是正常的这种情况不要强行清洗保留原样反而更接近真实分布。另一个技巧是用torchvision的make_grid把整个 batch 拼成一张大图训练过程中每隔几个 epoch 存一次观察增强后的图有没有出现明显失真。RandomHorizontalFlip对眼底图通常是安全的但如果你加了RandomRotation或ColorJitter就要确认旋转角度和色彩偏移没有破坏病灶特征。我一般把旋转限制在 ±15 度以内色彩抖动只调亮度和对比度不动色相。从那以后我每次拿到新的分类数据集都强制走一遍「先可视化、再统计类别分布、最后才开训」的流程。这份 70MB 的眼疾数据集体量小跑一轮可视化不到十秒但省下来的返工时间是以小时计的。希望帮到你。本文还有配套的精品资源点击获取