首页/新闻资讯/正文详情

糖尿病视网膜病变分级诊断:EfficientNet与模型融合实战解析

发布时间:2026/9/26 5:37:13 来源:云帆数科 栏目:资讯中心
糖尿病视网膜病变分级诊断:EfficientNet与模型融合实战解析
简介基于Jupyter的糖尿病视网膜疾病诊断项目以糖尿病性视网膜病变0-4级分级任务为切入点提供从需求分析、数据来源、EDA分析、预处理与增强到建模调参、模型验证、错误分析及结果总结的完整实现流程对毕业设计、课程设计或实际项目开发均有参考价值。压缩包共30个文件以16个Jupyter Notebook为核心辅以Python脚本、CSV数据集、结果可视化图片和Markdown项目文档整体约30.44MB目录按源码、笔记本、图片与说明划分便于按模块查阅。已有179人学习浏览。项目不仅给出EfficientNet-B4/B5等模型训练与交叉验证方案还包括数据划分脚本、堆叠集成尝试和提交结果。这些内容可复现用于糖尿病视网膜筛查也能在此基础上扩展更多分类或迁移学习实验。1. 把糖尿病视网膜病变诊断做成 Jupyter 项目从 Kaggle 竞赛到课程设计落地如果你正在找一份能真正跑通、又能写进毕业设计或课程设计的技术项目糖尿病视网膜病变Diabetic Retinopathy简称 DR分级诊断是一个非常典型的切入点。这个项目基于 Jupyter Notebook 实现完整覆盖了从 EDA 分析、数据预处理、数据增强、EfficientNet 系列模型训练到模型融合与错误分析的全流程训练集就是 Kaggle 上经典的 DR 检测数据集临床标签按 0 到 4 五级划分。换句话说你拿到的不是一段孤零零的模型代码而是一整套可以复现、可以拆解、也可以在此基础上二次开发的完整工程。我拆这个项目时最直观的感受是它不是那种能跑就行的玩具代码而是认真处理过真实问题的方案。比如训练集和 Public Test 分布差异大导致的过拟合问题、类别之间距离不等的分类问题、外部数据源的利用方式这些都是在实际比赛中才会遇到的硬骨头。对准备答辩的学生来说这些恰恰是老师最爱追问的点。2. 数据与 EDA先搞清楚五级分类的分布陷阱2.1 数据集构成与标签分布这个项目的数据来自 Kaggle 的 APTOS 2019 blindness detection 竞赛每张眼底图像对应一个 0 到 4 的 DR 严重等级标签。项目根目录下的 DiabeticRetinopathy-master 结构里图片资源集中在 images 文件夹模型结构图EfficientNet.png、训练过程截图Fold-1.png 到 Fold-5.png、视网膜样例图视网膜2.png、ShiWangMo.png等都在其中。我先说结论这类医学影像数据集最典型的坑不是图片分辨率不够而是类别分布极度不均衡。正常眼底等级 0的图像数量往往是等级 3、等级 4 的几十倍如果直接用原始分布训练模型会倾向于把所有输入都预测成等级 0因为这样准确率也很高。这个项目里的 EDA.ipynb 就是用来把这个分布问题量化的。import pandas as pd import matplotlib.pyplot as plt train_df pd.read_csv(train.csv) counts train_df[diagnosis].value_counts().sort_index() plt.figure(figsize(10, 5)) plt.bar(range(5), counts.values) plt.xticks(range(5), [0: No DR, 1: Mild, 2: Moderate, 3: Severe, 4: Proliferative]) plt.xlabel(DR Severity Level) plt.ylabel(Number of Images) plt.title(Class Distribution in Training Data) plt.show() print(counts.values) print(Imbalance ratio (max/min):, counts.max() / counts.min())这段代码先把 train.csv 读进来按 diagnosis 列统计五个等级的样本量然后画柱状图并打印最大类别和最小类别的比值。如果你自己跑这份数据不均衡比通常会超过 30 比 1这就是后面要做数据增强和加权损失的前提。2.2 为什么类别之间距离不等是个真问题DR 分级不是简单的多分类等级 0 和等级 1 之间的差异远小于等级 3 和等级 4 之间的差异。临床上等级 3Severe和等级 4Proliferative都意味着患者需要紧急干预而等级 0 和等级 1 往往只需要定期复查。这个项目在需求分析里明确提出了如何解决类别之间距离不等的分类问题常见的处理方式是把标签转成有序回归ordinal regression或者对相邻类别的预测错误施加不同惩罚。在 EfficientNet 模型输出层常见做法是使用 5 个输出节点的 softmax配合 label smoothing 来缓解过度自信。我一般会在损失函数上动手脚比如给等级 3 和等级 4 的样本更高的权重让模型在训练时更在意严重病例的召回率而不是一味追求整体准确率。2.3 public test 与训练集分布差异的识别方法项目中专门提到Train 数据和 public test 数据分布差异很大over-fitting 怎么办。这个问题在 Kaggle 上非常常见主办方划分的测试集和你拿到的训练集可能来自不同医院、不同拍摄设备甚至不同时期。如果你只在训练集上验证模型本地得分很高一提交就大跌眼镜。应对手段在项目里分了两层。第一层是交叉验证src/cv_data_split.py 就是干这个的它把训练数据按 Fold 拆成 5 份每个 Fold 轮流做验证集最终取平均得分避免单一划分的偶然性。第二层是在线测试时用 TTATest-Time Augmentation和多模型融合来提升泛化能力这个在后面的 stacking-logits.ipynb 里体现得最明显。3. 预处理与数据增强让 EfficientNet 吃对食物3.1 图像预处理流程与关键参数EfficientNet 系列对输入图像有固定要求B4 是 380×380B5 是 456×456。这个项目里同时出现了 efficientnet-b4 和 efficientnet-b5 两个版本的 notebook明显是在做模型对比实验。import cv2 import numpy as np def preprocess_image(image_path, target_size(380, 380)): # 读取彩色眼底图像 img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 眼底图像常见黑边裁剪 gray cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) _, thresh cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY) contours, _ cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: x, y, w, h cv2.boundingRect(contours[0]) img img[y:y h, x:x w] # 统一缩放到目标尺寸 img cv2.resize(img, target_size, interpolationcv2.INTER_AREA) return img这段代码做了两件事先把图像从 BGR 转成 RGB然后用阈值分割找到眼底图像的外轮廓裁掉黑边最后缩放到目标尺寸。裁剪黑边这一步很重要因为眼底相机拍摄的图像四周通常有一圈黑色区域如果不过滤模型会学到边缘黑 某种类别这种伪特征。还有一个容易被忽略的细节预处理时要不要做归一化。EfficientNet 官方推荐使用 ImageNet 的均值和标准差做归一化但医学图像领域有另一种观点认为眼底图像的色彩分布和自然图像差异很大直接在原始像素上训练可能更好。项目里同时保留了两种思路的痕迹方便你自己做对比实验。3.2 数据增强策略为什么是 augmentation-s10打开任何一个训练 notebook名字里都带一个augment-s10后缀。这个 s10 表示每张原始图片在训练时生成 10 个增强版本。医学图像的标注成本极高数据集不可能像 ImageNet 那样动辄百万张数据增强是唯一能把有限标注数据利用率拉满的手段。from albumentations import Compose, HorizontalFlip, VerticalFlip, RandomBrightnessContrast, ShiftScaleRotate, CoarseDropout def get_augmentation_pipeline(): return Compose([ HorizontalFlip(p0.5), VerticalFlip(p0.5), ShiftScaleRotate(shift_limit0.1, scale_limit0.15, rotate_limit30, p0.7), RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.5), CoarseDropout(max_holes4, max_height40, max_width40, fill_value0, p0.3) ]) aug_pipeline get_augmentation_pipeline()这个增强管线用了五个操作水平翻转、垂直翻转、随机平移缩放旋转、亮度对比度调整、粗粒度 dropout。其中 CoarseDropout 是近几年在医学影像上效果很好的技巧它随机挖掉图像中的一小块区域迫使模型不能只依赖某个局部特征做判断——眼底图像中的病变区域可能出现在任何位置这种增强相当于在教模型看全局。增强后的数据直接喂给模型每个 epoch 都会生成不同的增强版本相当于模型在训练过程中看到了更多样化的数据分布。我自己的经验是augmentation 的强度不是越大越好太大的 shift 和 rotate 会把视网膜的关键结构移出视野反而损害模型对细微病变的识别能力。3.3 外部数据源的作用与边界项目需求里提到了如何利用好外部数据源提高模型的 performance。这里的外部数据源指的是 Kaggle 上更早的 DR 检测数据集2015 年的那一版它和 2019 年的 APTOS 数据集拍摄设备不同、标注标准略有差异但病灶特征基本一致。常见做法是先在外部数据上做预训练冻结主干网络再用目标数据集微调。或者在训练时直接把两个数据集混合起来但给外部数据样本降权避免它喧宾夺主。这个项目实际采用的是第一种思路也就是用预训练权重初始化 EfficientNet再在 APTOS 数据上训练。这样做的原因是外部数据量更大能让模型先学会通用的眼底图像特征最后再用目标数据适配。4. EfficientNet 训练实战B4 与 B5 的对比实验4.1 模型选型理由与结构拆解EfficientNet 在 2019 年提出时核心创新是复合缩放Compound Scaling简单说就是同时调整网络的深度、宽度和输入分辨率而不是只调一个维度。这个项目选 EfficientNet-B4 和 B5 做对比是因为这两个规格在 APTOS 竞赛中是性能和计算量的最佳折中点——B0 太轻识别细微病变能力不足B7 太重单卡训练一个 epoch 的时间让人崩溃。4.2 训练循环与关键参数配置看 efficientnet-b5_augament-s10-cv.ipynb 这个文件cv 后缀表示它带交叉验证。训练循环里除了标准的 forward-backward 更新还做了梯度裁剪和模型快照保存。import torch import torch.nn as nn from efficientnet_pytorch import EfficientNet class DRModel(nn.Module): def __init__(self, model_nameefficientnet-b5, num_classes5): super(DRModel, self).__init__() self.backbone EfficientNet.from_pretrained(model_name) in_features self.backbone._fc.in_features self.backbone._fc nn.Sequential( nn.Dropout(0.5), nn.Linear(in_features, num_classes) ) def forward(self, x): return self.backbone(x) model DRModel(model_nameefficientnet-b5, num_classes5) criterion nn.CrossEntropyLoss(weighttorch.tensor([0.2, 0.5, 1.0, 2.0, 2.5])) optimizer torch.optim.Adam(model.parameters(), lr3e-4, weight_decay1e-5) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30)这里的核心设计是损失函数的 weight 参数给不同类别设置了差异化的权重等级 0 只有 0.2等级 3 和 4 分别给到 2.0 和 2.5。也就是说模型把一张等级 4 的重症图片判错产生的 loss 是普通图片的 10 倍以上。这会牺牲一些整体准确率但换来了对严重病例的高召回率——在医疗场景中漏诊重症比误诊健康更危险。学习率调度用的是 CosineAnnealingLR前 30 个 epoch 学习率沿余弦曲线从 3e-4 平滑衰减到接近 0。这种调度方式比固定学习率在医学图像任务上表现更好因为它在训练后期能逐步收敛到更平滑的损失区域减少震荡。4.3 交叉验证src/cv_data_split.py 的工作方式cv_data_split.py 做的事情是把带标签的数据均匀拆成 5 份每个 Fold 的类别分布尽量和整体保持一致分层抽样。训练时依次拿 4 份做训练、1 份做验证跑 5 轮最后把 5 次验证分数平均。这样做比单次划分更可靠因为你可以看到模型在不同数据子集上的表现波动波动太大说明模型稳定性有问题。拿到的 5 个 Fold 结果图 Fold-1.png 到 Fold-5.png 就是 5 轮训练各自验证集上的指标曲线。我建议你做项目展示时保留这些图它们能直观说明你做了充分的模型评估而不只是跑了一次训练。5. 常见问题与避坑我把这个项目跑下来踩过的五个坎5.1 现象训练时 loss 下降很快但验证集指标几乎不动原因数据增强强度过大模型在训练时看到的是严重扭曲的图像学习到的特征在干净的验证集上失效。尤其是 CoarseDropout 的 fill_value0如果挖掉的是视网膜中央的黄斑区域特征完全丢失。解决把增强管线拆成两部分训练前期用轻度增强翻转轻度亮度调整训练后期再逐步加入更激进的 ShiftScaleRotate 和 CoarseDropout。或者把 CoarseDropout 的 max_holes 从 4 降到 2。5.2 现象Fold 之间得分差异巨大一个 Fold 的 AUC 0.96另一个只有 0.88原因分层抽样只保证了类别比例一致但没保证图片的拍摄设备、患者年龄分布一致。某些医院来源的图像质量整体偏高模型在这个子集上自然表现好。解决不要单看一个 Fold 的结果。我在做项目时会把 5 个 Fold 的预测 logits 全部保存下来最后用 stacking 融合而不是只选最好的那个 Fold。这个项目的 stacking-logits.ipynb 干的就是这件事。5.3 现象加载 EfficientNet 预训练权重时报错key 不匹配原因没安装匹配版本的 efficientnet-pytorch 库或者 PyTorch 版本太新导致 state_dict 加载方式变化。解决先用 pip 安装指定版本的库然后确认 backbone._fc 层替换后再加载权重。注意 from_pretrained 默认会加载完整模型权重如果你先改了 _fc 层再加载可能会因为输出维度不一致报错所以一定要先加载权重再替换头部顺序不能反。5.4 现象Jupyter Notebook 里训练到一半内核崩溃没有任何报错信息原因显存溢出。B5 模型的输入分辨率是 456×456如果 batch size 设置过大显存会直接打满CUDA 驱动 kill 掉进程。解决把 batch size 降到 8 或 4同时开启梯度累积模拟更大的 batch。另外可以在训练循环里加上 torch.cuda.empty_cache()每个 epoch 结束后手动清理缓存。5.5 现象读取图片时 cv2.imread 返回 None程序直接报错原因路径中包含中文字符OpenCV 的 imread 函数对非 ASCII 路径支持不好返回空指针。解决先用 np.fromfile 读文件字节流再用 cv2.imdecode 解码。这是我在实际项目里踩过最多次的坑之一尤其是把项目放在带中文目录名的路径下时必现。6. 模型融合与提分技巧从 logits 到最终提交项目最后阶段的 stacking-logits.ipynb 是整个方案的点睛之笔。前面训练出了 B4 和 B5 两个模型每个模型又在 5 个 Fold 上各跑了一遍手上最多有 10 组预测结果。如果直接用 argmax 取每个模型的最高分类别就浪费了 logits 里的置信度信息——等级 3 的样本可能所有模型都给了 0.35、0.30、0.32 的概率虽然没到 0.5但一致性本身就能说明问题。import numpy as np import pandas as pd # 假设有两个模型的 logits形状都是 (num_samples, 5) logits_b4 np.load(fold_logits/b4_fold1.npy) logits_b5 np.load(fold_logits/b5_fold1.npy) # 先做 softmax 归一化 def softmax(x): e_x np.exp(x - np.max(x, axis-1, keepdimsTrue)) return e_x / np.sum(e_x, axis-1, keepdimsTrue) probs_b4 softmax(logits_b4) probs_b5 softmax(logits_b5) # 等权融合 ensemble_probs 0.5 * probs_b4 0.5 * probs_b5 # 按行取最大值对应的类别 final_preds np.argmax(ensemble_probs, axis-1) # 生成提交文件 sub_df pd.DataFrame({id_code: test_ids, diagnosis: final_preds}) sub_df.to_csv(submission.csv, indexFalse)这段代码展示了最基础的 logits 融合把两个模型的 softmax 概率按 0.5 和 0.5 加权平均然后取最大值对应的类别作为最终预测。我一般是先看两个模型在验证集上的独立分数再试不同的权重组合比如 B4 单独 0.40 分、B5 单独 0.42 分融合后可能到 0.44 分提升虽然不大但每一点提升在竞赛排名和答辩评分里都是实打实的。还有第三个提升点把 Fold 当成天然的模型多样性来源。同一模型在不同 Fold 上学到的特征有细微差异融合 5 个 Fold 的预测比只融合 2 个模型的预测效果更稳。代价是推理时间翻倍但课程设计和毕业设计的场景下推理速度完全不是瓶颈。我从这个项目里学到的最深的一课是医学图像分类任务的提分不是靠某个惊艳的模型结构而是靠数据清洗、合理的增强策略、交叉验证和模型融合的层层叠加。从那以后我每次做这类竞赛项目都会强制走一遍EDA 分析 → 分布检查 → 多模型交叉验证 → logits 融合的流程不再一上来就无脑调网络结构。希望这个完整的开源项目也能帮你在毕设或课程设计上少走弯路。本文还有配套的精品资源点击获取

相关推荐

PyCharm报错Disk quota exceeded?pip安装失败排查与解决完整指南
PyCharm报错Disk quota exceeded?pip安装失败排查与解决完整指南

1. 认识这个报错的真实面目先说一下现场。装包装到一半,PyCharm 底部 Console 突然刷出一片红字,最后一行定格在OSError: [Errno 122] Disk quota exceeded,这时候项目里 import 相关库全是红的,代码根本跑不起来。如果你是第一次… · 2026/9/26 5:37:13

面向工程落地的大模型推理、多模态与Agent技术路线图
面向工程落地的大模型推理、多模态与Agent技术路线图

1. 这不是论文列表,而是一份面向工程落地的前沿技术路线图你点开arXiv cs.AI板块,刷到2026年9月那期“大模型推理、多模态与Agent前沿速览”,第一反应可能是:又一篇综述?又一堆公式堆砌?又一个只讲“能做什… · 2026/9/26 5:37:13

华为云IoTDA设备接入实战:从实例、产品到MQTT上云全流程
华为云IoTDA设备接入实战:从实例、产品到MQTT上云全流程

很多做物联网开发的朋友,第一次打开华为云IoTDA物联网平台的界面时都会愣一下:实例、产品、设备这三层概念还没搞清楚,就开始注册账号、创建实例,结果设备侧怎么也连不上,数据传不上来,控制台上一堆红色报错… · 2026/9/26 5:37:07

纯原生HTML+CSS+JavaScript实现响应式尺码查询表
纯原生HTML+CSS+JavaScript实现响应式尺码查询表

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 6:15:53

Python 自动化行为模拟避坑实战:风控特征复盘,日志监控与行为随机化优化
Python 自动化行为模拟避坑实战:风控特征复盘,日志监控与行为随机化优化

前言前面三篇文章,我们分别讲解 requests 基础请求、Selenium 浏览器行为模拟、多账号 IP 与 Cookie 隔离。很多同学在学习过程中,写出来的自动化脚本,短期能跑,运行一段时间账号就触发风控。很多人把问题归结于代理或者 UA&#… · 2026/9/26 6:15:53

OpenLess Android实战:无障碍服务与悬浮窗如何让你的手机语音也能直达桌面光标
OpenLess Android实战:无障碍服务与悬浮窗如何让你的手机语音也能直达桌面光标

OpenLess Android实战:无障碍服务与悬浮窗如何让你的手机语音也能直达桌面光标 【免费下载链接】openless Hold a key, speak, release — AI-polished text appears at your cursor in any app. Open-source voice input for macOS & Windows. (按住快捷键说话… · 2026/9/26 6:15:53

video-use实战:从转码剪辑到编码输出的视频处理全流程
video-use实战:从转码剪辑到编码输出的视频处理全流程

干了这么多年视频相关的工作,我越来越觉得,工具这东西不怕多,就怕乱。手上囤了一堆软件,真到要剪片子的时候反而不知道该用谁。最近我在整理自己手头的项目时,重新梳理了一遍“video-use”这套处理视频的完整流程&… · 2026/9/26 6:15:47

游戏平台账号关联绑定全攻略:跨平台联机与云存档同步实操指南
游戏平台账号关联绑定全攻略:跨平台联机与云存档同步实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 6:15:47

多层纸袋内层热封合格,外层界面容易脱层?
多层纸袋内层热封合格,外层界面容易脱层?

多层纸袋的内层热封合格性与外层界面脱层现象是包装行业中的重要课题。确保内层的热封合理,能够加强纸袋的整体强度,防止包装失效。而外层脱层的发生,常常是因为热封工艺不达标或者材料选择不当。这些问题可能影响纸袋的性能、导致包装失败。… · 2026/9/26 6:15:28

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码