基于深度学习的视网膜病变识别:从眼底彩照到临床落地的实战指南
简介面向医学影像识别与深度学习研究者的参考文献资料为2021年《计算机应用与软件》发表的视网膜病变图像识别论文PDF。内容围绕糖尿病性视网膜病变自动识别难题提出基于多特征融合的卷积神经网络方法在VGG-16基础上融合各层局部特征配合Softmax分类器并使用OpenCV进行加噪、翻转、对比度调整等5种数据扩充最终平均识别精度达94.23%较Alex-Net、Google-Net等模型提升明显。论文还指出该类疾病筛查率低、人工判读主观性强等现实痛点所提方法对临床辅助筛查具有参考价值。PDF含摘要、实验设计、结果对比和参考文献可直接用于论文研读、方法复现与综述写作。资源共1个PDF文件压缩包大小3.31MB已有267人学习浏览适合需要了解深度学习图像分类、特征融合技术的研究生、算法工程师参考。1. 一张眼底彩照判读背后的硬骨头为什么视网膜病变识别要坚持用深度学习眼科门诊里医生看一张眼底彩照的平均时间是十几秒。真正的问题不是这十几秒而是看完一百张之后的那十几秒——疲劳、扫视习惯、经验差异都会让判读漂移。视网膜病变的病灶往往是小血管瘤、微出血、棉絮斑这类毫米级信号早中期特征很容易混在复杂的眼底纹理里。传统图像识别算法靠人为设计的特征算子比如用形态学提取血管、用阈值圈出亮斑换一个数据集、换一台相机参数就要重新调鲁棒性撑不住规模化筛查。深度学习图像识别解决的不是“看图”这件事而是把“看什么”和“怎么判”交给数据驱动。CNN通过卷积核自动学习从像素到病灶语义的分层特征训练到位的模型能稳定输出病变分级、病灶位置甚至转诊建议。这个标题下的方案常见落地形态是用公开的眼底彩照数据集训练一个分类模型在门诊筛查、体检中心、远程影像平台里做辅助判读。适合正在做医学图像毕设、想进医疗AI方向的开发者以及想把“能跑通的模型”推向“能用的模型”的工程师。以下内容按实际项目最常用的技术路线展开从数据准备一路讲到避坑和落地。2. 数据与预处理眼底彩照的清洗、增强与归一化要细到什么程度2.1 公开数据集的选型先问自己“分类几类”再选数据视网膜病变识别最常见的起步任务是糖尿病视网膜病变DR分级。公开数据集里常用的有中国学者标注的眼底彩照数据集、国际上的DR检测竞赛数据集等。不同数据集的标签体系不一样有的是二分类正常/异常有的是五分类无病变、轻度、中度、重度、增殖期有的还带病灶分割标注。选数据集的第一步不是看谁规模大而是看任务定义。如果做辅助筛查二分类“是否需转诊”在临床上比精细的五分类更实用也更容易在中小数据集上训练出可靠的模型。如果做学术研究、写论文五分类更常见但对数据质量和数量要求明显更高——五分类的相邻类别之间边界很模糊比如“轻度”和“中度”非增殖期病变专家之间标注一致性本身就堪忧模型学起来更是容易翻车。我一般建议先跑二分类验证整个流程再视数据量决定是否上五分类。另外要注意数据集的来源构成。有些数据集里的图像是多种相机、多种拍摄条件混在一起的这反而是好事——泛化性更好但如果某些类别只来自某一台相机模型很容易学到“这台相机的色调”而不是“病灶特征”。这个坑后面避坑章节会展开。2.2 预处理流水线的标准顺序尺寸、颜色归一化与CLAHE的取舍眼底彩照的原始尺寸差异很大常见的有 1444×1444、2124×2056、3872×2592 等。直接原图送进网络不现实要先定一个统一输入尺寸。常见做法是先等比缩放再把短边补齐到目标尺寸或者直接中心裁剪。对眼底图来说视盘和黄斑在中部区域占比大中心裁剪一般不会丢掉关键结构但要注意——如果病灶分布在视网膜边缘比如周边出血中心裁剪会损失信息。我一般用以下顺序先等比缩放到短边对齐比如短边 512再做中心裁剪到 448×448 或 512×512最后做颜色归一化。这个流程比直接 resize 要稳因为直接拉长宽比会让视盘变成椭圆干扰模型的形态学判断。预处理里最有争议的是要不要做 CLAHE对比度受限自适应直方图均衡化。眼底彩照因为拍摄光线、散瞳程度不同亮度和对比度差异极大。CLAHE 能显著提升微血管瘤和出血点的局部对比度对最终识别效果有正面帮助。但参数设不好就会出现过度增强——背景噪声被放大血管看起来像出血点。import cv2 import numpy as np def preprocess_fundus(img, target_size448, clahe_clip2.0, clahe_grid(8, 8)): # 等比缩放短边对齐 target_size避免直接拉变形 h, w img.shape[:2] scale target_size / min(h, w) new_w, new_h int(w * scale), int(h * scale) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_AREA) # 中心裁剪到 target_size x target_size sh (new_h - target_size) // 2 sw (new_w - target_size) // 2 cropped resized[sh:sh target_size, sw:sw target_size] # 转为 LAB 色彩空间只对 L 通道做 CLAHE避免颜色失真 lab cv2.cvtColor(cropped, cv2.COLOR_BGR2LAB) l, a, b cv2.split(lab) clahe cv2.createCLAHE(clipLimitclahe_clip, tileGridSizeclahe_grid) l_enhanced clahe.apply(l) enhanced cv2.merge((l_enhanced, a, b)) enhanced cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR) # 归一化到 [0, 1]再用 ImageNet 均值方差做标准化 normalized enhanced.astype(np.float32) / 255.0 mean np.array([0.485, 0.456, 0.406]) std np.array([0.229, 0.224, 0.225]) normalized (normalized - mean) / std return normalized这段代码的关键逻辑先等比缩放再做中心裁剪顺序不能反。先裁剪再缩放会把本就有限的边缘病灶进一步缩小反而丢失信息。CLAHE 只作用于 L亮度通道a、b 通道保持原样这样增强的是局部对比度而不是改变色相——眼底出血点和微血管瘤的判定对颜色很敏感色相改变会误导模型。clipLimit 控制对比度增强强度取值 1.53.0 之间比较稳超过 4.0 就会明显看到背景噪声被抬起来。tileGridSize 控制局部区域大小8×8 适合 448 左右的输入尺寸如果输入是 224建议降到 4×4否则每个网格区域太小统计信息不稳定。标准化用 ImageNet 的均值方差是迁移学习的惯例。如果是从零训练模型直接用 (x - 整体均值) / 整体标准差会更合理。用 ImageNet 的统计量对后面加载预训练权重友好这是最常见的做法。2.3 数据划分的隐性红线按患者分不按图像分很多人在这一步踩了最重的坑。视网膜病变数据集里同一个患者可能有多张眼底彩照左眼、右眼或同一只眼的多次拍摄。如果划分训练集和验证集时直接按图像随机切分同一个患者的不同图像会同时出现在训练集和验证集里。模型在训练时见过这个患者左眼的病灶纹理验证时遇到右眼识别成绩会虚高。这个问题的严重程度取决于数据集的构成测绘。如果数据集中每个患者只有一张图按图划分没有影响但如果包含多图必须按患者ID分组划分。另一个容易被忽略的问题是类别不平衡。五分类的DR数据集中正常0级和轻度1级通常占大头中重度样本偏少增殖期最少。模型会倾向把所有样本预测为多数类来压低损失表现为整体accuracy虚高、少数类recall极低。处理方法在后面训练部分详述但数据划分阶段至少要确认每个子集中各类别比例接近不能折叠。数据增强环节眼底图像和自然图像有个显著差异水平翻转是可以接受的但垂直翻转要谨慎。眼底结构虽然有解剖学对称性但视盘和中央凹的相对位置在上下方向上是固定的垂直翻转会制造出解剖上不存在的“左眼/右眼混淆”样本。旋转增强建议在 ±20° 以内过大的旋转会让血管走向变得不符合生理特征。3. 模型选型与搭建预训练权重、网络深度与输入分辨率怎么组合3.1 为什么迁移学习是这类任务的默认选择视网膜病变识别任务的数据量通常只有几千到几万张远不足以从零训练一个深层CNN。ImageNet预训练模型学到的低层特征——边缘、纹理、斑点——对眼底图像同样有效比如微血管瘤在浅层特征里可以理解为圆形小斑点出血点和瘀斑在深层特征里表现为区域性色块。迁移学习可以理解为站在特征表达的肩膀上只让模型的后半部分学习病变专属的高层语义。在网络结构的选择上有几个直观规律输入分辨率比网络深度更重要。因为眼底病灶尺寸小一个出血点可能只占几十个像素低分辨率输入下这些细节会被下采样抹掉。EfficientNet系列中B4-B6在224-320分辨率上训练时增大分辨率到512左右还能持续涨点。ResNet50在448分辨率下也能跑但继续加深到ResNet101提升有限因为瓶颈不在表示能力而在数据量。VitVision Transformer这类结构也会出现尤其在较新的工作里。但Vit需要海量数据或强正则化在对病灶位置敏感、数据量有限的情况下卷积归纳偏置局部感受野、平移不变性反而是一种优势。实际项目中我更推荐先跑EfficientNet-B4它的参数量适中在医疗影像上的表现经过大量验证不会像Vit那样一言不合就欠拟合。3.2 微调策略的两种流冻结特征层与全量微调迁移学习落地时第一个要决定的问题是冻结策略。全冻结只训练最后的分类头特征提取层全部用ImageNet的权重。这个方案训练极快、不容易过拟合效果也能达到可用的程度因为眼底图像的纹理和结构特征与ImageNet有足够的共享部分。全量微调所有层参与训练设置较小的学习率。效果上限更高因为医学图像的领域偏移比较大——眼底彩照的视觉特征和自然照片差异显著冻结太狠会限制高层特征针对性。我常用的策略是两段式先用全冻结训练几个epoch让分类头收敛再解锁网络的后半部分用低学习率微调。这个顺序能减少初期不稳定的梯度回传破坏预训练特征的风险。具体实现可以用PyTorch的requires_grad控制也可以用FastAI的unfreeze机制。import torch import torch.nn as nn import timm class FundusClassifier(nn.Module): def __init__(self, backbone_nameefficientnet_b4, num_classes2, dropout0.3): super().__init__() # 创建 backbone去掉自带分类头 self.backbone timm.create_model( backbone_name, pretrainedTrue, num_classes0, # 只取特征 global_poolavg ) # 获取特征维度 feat_dim self.backbone.num_features # 分类头两层 MLP Dropout比单层线性层更稳 self.head nn.Sequential( nn.Dropout(dropout), nn.Linear(feat_dim, 512), nn.BatchNorm1d(512), nn.ReLU(inplaceTrue), nn.Dropout(dropout * 0.5), nn.Linear(512, num_classes) ) def freeze_backbone(self): 冻结所有 backbone 参数只训练分类头 for param in self.backbone.parameters(): param.requires_grad False def unfreeze_backbone(self, start_from0.6): 解冻 backbone 的前 start_from 比例层 # 实际用的时候按层名索引更精确 for param in self.backbone.parameters(): param.requires_grad True这段代码里的头结构设计值得说num_classes0和global_poolavg是timm库的关键写法返回的是特征向量而不是分类logits。分类头用了两层MLP加BatchNorm比单层线性层多一些可学习的非线性映射。Dropout设置为0.3是比较适合小数据量的值数据量越少Dropout应该越高。BatchNorm放在全连接层之间有助于稳定训练尤其是微调阶段特征分布会被大幅度调整时。解冻时最稳的做法不是按比例解冻而是按层名精确控制。EfficientNet的backbone内部按block编号0-6我通常先冻结0-3区段微调4-6区段和分类头。如果效果还差再逐步往前解冻。3.3 训练配置优化器、学习率与batch设置的实战参数训练配置没有银弹但有经过验证的起点。优化器最稳妥的是AdamWweight decay设1e-4到1e-5。SGD配合余弦退火在某些任务上能刷更高分但对超参数更敏感前期跑通流程用AdamW省心。学习率方面全冻结阶段的分类头可以用1e-3微调阶段全局学习率降到1e-4或更低的5e-5。最好的方式是先用学习率扫描器——跑5-10个batch输出loss曲线找梯度下降最快的区间再定学习率。batch size受限于显存EfficientNet-B4在512分辨率下单卡8GB显存通常只能塞4-6张图。batch size过小时BatchNorm统计不稳定可以使用梯度累积来模拟更大的batchaccum_steps 4 # 相当于 batch_size * 4 optimizer.zero_grad() for i, (images, labels) in enumerate(train_loader): outputs model(images) loss criterion(outputs, labels) / accum_steps # 归一化很重要 loss.backward() if (i 1) % accum_steps 0: optimizer.step() optimizer.zero_grad()除以accum_steps是这个技巧的关键否则累积4次再更新等效学习率变成原来的4倍训练容易发散。训练轮数一般设30-50个epoch配合早停。医学数据集小过拟合来得比想象的快。验证集loss通常在15-20个epoch就开始回升这个时候不是“再跑跑就好”的问题而是模型开始死记硬背训练集了。4. 训练与评估损失函数陷阱、评价指标和临床价值的对齐4.1 类别不平衡下的损失函数选择不能只盯着Accuracy医疗影像里标签分布天然偏斜。正常样本可能占70%病变更常见的轻微阶段占20%中重度占10%以下。模型假如全部预测为正常类别accuracy有70%但这个模型的临床价值等于零——漏掉的是一个正在走向失明的患者。这时候要做的第一件事是不再以accuracy为主要指标改用敏感度Sensitivity/Recall、特异度Specificity、AUC和F1-Score。对筛查类场景敏感度排在最高优先级漏诊曲线和误诊曲线不能等权。对转诊决策场景AUC衡量的是模型区分能力比单点阈值更全面。损失函数的调整也有必要。常见做法是给少数类更高的权重或者在每轮采样时对少样本类别过采样。相比之下Focal Loss焦点损失是更有细粒度效果的解决路径它让模型把注意力集中在“难分样本”上而不是把所有错误都无差别惩罚。在视网膜病变分级任务中中度非增殖期这件事本身就有标签模糊性存在标注者之间的分歧Focal Loss能有效缓解这种因为标签噪声导致的训练不稳定。import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alphaNone, gamma2.0, reductionmean): super().__init__() # alpha: 类别权重向量例如 [0.3, 0.7] 表示第0类0.3第1类0.7 self.alpha alpha self.gamma gamma self.reduction reduction def forward(self, logits, targets): ce_loss F.cross_entropy(logits, targets, weightself.alpha, reductionnone) pt torch.exp(-ce_loss) # 该样本预测正确的置信度 focal_weight (1 - pt) ** self.gamma loss focal_weight * ce_loss if self.reduction mean: return loss.mean() return loss.sum()Focal Loss的原理可以这么理解对于已经预测得很对的样本pt接近1(1-pt)^gamma趋近于0让它的loss几乎被抹去对于预测错的样本pt小这个权重接近1loss保持原样。gamma越大对容易样本的压制越强。gamma2是最常见的选择gamma3会让训练变得很难收敛。alpha是类别权重向量如果算上alpha惩罚会双管齐下——既压低易分样本的梯度又让少数类的错误代价更高。4.2 五分类问题的特殊处理有序回归的思路视网膜病变的五个等级之间存在天然的顺序关系0到4级病情递进。如果把五分类当作平级的多类问题模型可能在2级和3级之间犯“方向性”错误——把重度判成轻度、把轻度判成重度实际的危害程度差异巨大。有一个思路值得参考把有序分类建模为多个二分类任务。比如训练4个二分类器分别回答“是否大于0级”“是否大于1级”“是否大于2级”“是否大于3级”然后综合输出。这种做法在实际数据上有两个优点一是利用了标签的顺序信息不会出现跨越式误判二是在标注不确定的边界样本上容错空间更大。实现时只需要改一下输出层的类别数依然可以用预训练backbone。代价是推理时要做多次前向传播但视网膜病变识别这个场景对延迟的要求并不严苛一张图多推几次完全可控。更简化的替代方案是在损失函数里加一个排序惩罚项当模型把重度误判为轻度时惩罚大于误判为正常。用二次权重矩阵对预测错误做加权比如用一个4×4的平方距离矩阵当作额外的损失项。4.3 评估指标的完整拼图混淆矩阵配Kappa不看单点Accuracy医学AI审稿和临床应用里常规的嵌入指标是加权Kappa系数。Kappa统计量衡量的是预测与真实标签之间的一致性剔除了随机猜测的影响对相邻等级的轻微偏差惩罚较小、对跨级错误惩罚严谨。Kappa大于0.8视为优秀0.6-0.8为良好。相比accuracyKappa更不易被类别不平衡绑架。前文说过敏感度是筛查任务的核心。这里还需要提到Precision-Recall曲线在高不平衡场景下比ROC曲线更值得看。ROC曲线在负样本占绝对多数时假阳性率的基数很大会让曲线看起来“过分乐观”PR曲线直接衡量正类的识别质量对少数类的性能变化更敏感。临床上还需要一张详细的混淆矩阵热力图。比如模型容易混淆“轻度”和“中度”——这反映了标注本身的模糊性不一定是模型缺陷但模型如果在“正常”和“重度”之间犯错误就是严重问题。这类分析会直接影响你如何设定判断决策阈值。from sklearn.metrics import cohen_kappa_score, confusion_matrix import numpy as np y_true np.array([0, 1, 2, 3, 4, 1, 2, 0, 3, 4]) y_pred np.array([0, 1, 2, 2, 4, 1, 3, 0, 2, 4]) kappa cohen_kappa_score(y_true, y_pred, weightsquadratic) cm confusion_matrix(y_true, y_pred) # 逐类别敏感度对角线值 / 该类真实样本总和 sensitivity_per_class cm.diagonal() / (cm.sum(axis1) 1e-8) print(fQuadratic Kappa: {kappa:.4f}) print(Per-class Sensitivity:, np.round(sensitivity_per_class, 3))选择weightsquadratic是因为顺序分类任务中相邻类别的错误比跨级错误更可接受二次权重能平滑地反映这种代价差异。逐类别Sensitivity的对比可以帮助定位模型到底在哪一类上拖后腿配合混淆矩阵给出的错误迁移方向能引出针对性的数据补充方案。5. 常见问题与避坑视网膜病变识别最容易翻车的五个环节5.1 同一患者多张图划进不同数据集验证结果虚高现象训练时loss正常下降验证集accuracy高达0.95以上但一放到新数据上表现大幅缩水甚至不如传统方法。原因同一个患者的多张眼底图左眼/右眼或不同时间点拍摄被随机分进了训练集和验证集。模型在训练时等于“见过”这个患者的病变纹理验证集分数虚高。这是医疗影像任务最常见的数据泄漏比自然图像里的泄漏更容易发生因为用户经常忽略眼睛不只一张图这件事。解决按患者ID划分数据集确保同一个患者的所有图像落在同一个子集里。这在代码上只需要把划分依据从文件名改为患者ID字段。划分完成后建议检查一下训练集和验证集是否存在同一患者ID写个assert确保干净。5.2 直接使用原始尺寸resize小病灶被直接抹掉现象模型训练很快收敛但医生看识别结果时发现早期的小出血点、微血管瘤完全没被捕捉到模型只会识别大面积病灶。原因输入分辨率太低比如224×224或者直接拉伸导致病灶在缩放过程中被插值算法抹平。微血管瘤在原始眼底图上可能只有十几个像素缩放到224分辨率后可能只剩两三个像素信息几乎丢失。解决把输入分辨率提高到至少384×384条件允许上512×512。同时确认resize方式——缩小用INTER_AREA放大用INTER_CUBIC或INTEER_LINEAR插值算法选错会引入不必要的伪影。5.3 CLAHE参数一刀切把噪声当病灶现象增强处理后图像看起来对比度很高血管边缘发白背景纹理颗粒感明显。训练出的模型在测试集上敏感度很高但误诊率也高得离谱。原因眼底图的黑暗区域噪声本来就多clipLimit设置过大会把噪声与病灶的对比度一起抬起来。模型学到的“病灶特征”实际上有一部分是噪声纹理。解决clipLimit从2.0开始每次加0.5观察效果不要超过4.0。同时建议在CLAHE之后加一个轻度的中值滤波或高斯滤波kernel 3×3压一下噪声响应。对比增强的目的是让病灶更清晰不是让图像更“好看”。5.4 以为五分类模型一定比二分类模型更“高级”现象换了五分类之后整体指标反而下降“轻度”病变几乎完全识别不出来输出类别集中在0和3两个极端。原因五分类的标注不一致性远高于二分类。不同眼科医生对“轻度和中度”的边界判定差异很大同一张图在多个标注者之间可能分布在不同等级。模型面对这种标签噪声学到的决策边界是混乱的。此外中型数据集中重度病例少每一类的有效样本量被摊薄。解决先想清楚应用场景再定分类粒度。做筛查——二分类无需转诊/需转诊能覆盖90%的价值做研究——标注一致性必须经多专家审核不能直接用单一标注源。五分类模型训练时建议先跑二分类热身后再初始化五分类头的某些层让模型先学会“有无异常”再学“异常有多重”。5.5 训练环境配置反复出问题timm库与PyTorch版本的兼容性现象代码在同事电脑上跑得好好的自己装的conda环境里一import timm就报错或者训练时突然报device-side assert显存占比还特别高。原因PyTorch与timm版本不匹配新版timm去掉了旧模型名CUDA toolkit版本与PyTorch编译版本不一致数据加载部分某个张量类型不对在GPU端才暴露出来。解决用conda创建独立环境锁定关键版本组合。我在多个项目上验证过的组合是PyTorch 2.0-2.1 timm 0.9-0.12 CUDA 11.8。首次训练前先跑一个batch的前向反向操作做冒烟测试不要直接开全量训练。报device-side assert时先把pin_memoryFalse、num_workers0试一遍八成是数据加载端的问题。6. 进阶用Grad-CAM做病灶定位让模型从“判官”变成“助手”视网膜病变识别模型被接受的门槛不在于准确率而在于解释性。医生不会因为一个模型说自己AUC 0.98就信任它——他要看到模型根据什么做出了判断。Grad-CAM梯度加权类激活映射正是用来解决这个信任问题的常用技术。它通过计算类别得分对最后一个卷积层特征图的梯度加权求和得到热力图标出模型重点关注的区域。import torch import cv2 import numpy as np from torch.nn import functional as F def grad_cam(model, input_tensor, target_classNone): model.eval() gradients [] activations [] def backward_hook(module, grad_input, grad_output): gradients.append(grad_output[0].detach()) def forward_hook(module, input, output): activations.append(output.detach()) # 注册钩子取最后一层卷积的特征图 target_layer model.backbone.blocks[-1].forward_pre fwd_handle target_layer.register_forward_hook(forward_hook) bwd_handle target_layer.register_full_backward_hook(backward_hook) output model(input_tensor) if target_class is None: target_class output.argmax(dim1).item() model.zero_grad() one_hot torch.zeros_like(output) one_hot[0, target_class] 1 output.backward(gradientone_hot) # 计算权重并对特征图做加权平均 weights gradients[0].mean(dim(2, 3), keepdimTrue) cam (weights * activations[0]).sum(dim1, keepdimTrue) cam F.relu(cam) cam F.interpolate(cam, sizeinput_tensor.shape[2:], modebilinear, align_cornersFalse) cam cam.squeeze().cpu().numpy() # 归一化到0-255并转热力图 cam (cam - cam.min()) / (cam.max() - cam.min() 1e-8) cam np.uint8(255 * cam) heatmap cv2.applyColorMap(cam, cv2.COLORMAP_JET) fwd_handle.remove() bwd_handle.remove() return heatmap这段代码有几个值得注意的实现细节目标层选最后一层卷积前的特征因为这一层空间分辨率足够保留病灶位置特征图的梯度通过register_full_backward_hook获得注意PyTorch中这个钩子返回的参数顺序和旧版不一样。激活值在torch1.8中放到了output而不是input。热力图生成后可以和原图按0.4-0.6的比例alpha融合。实际使用时有三个常见观察点一是热力图是否集中在黄斑、视盘附近还是分散在整张图集中且清晰说明模型定位到了关键结构。二是正常样本的热力图是否基本无响应如果正常样本也有大片激活区这个模型的判据可能有问题。三是同一个患者左右眼的热力图是否能保持结构一致性左右眼病变分布有对称性头部模型输出不一致说明学到的是噪声。综合流程上视觉解释和分类结果、置信度放在一起输出才算完整地支撑了一个转诊判断。我在实际项目里用Grad-CAM发现了模型在识别“渗出”时其实在看视盘边缘的亮斑而不是真正分布在黄斑区域的渗出——这促使我们去补充了黄斑区域渗出专用的训练样本整个团队的判断粒度才真正上了一个台阶。最后说一句经验之谈做这个方案模型结构的进展曲线会很长很慢但数据质量、标注一致性和评估口径会让你的模型成长曲线陡峭得多。先把基础流程里的每个环节做扎实再去追SOTA结构路会顺很多。希望帮到你。本文还有配套的精品资源点击获取