☰
基于VGG-16特征融合的视网膜病变识别技术路线解析
2026/9/30 10:27:26 网站建设 项目流程

简介:一份面向医学影像智能分析领域的学术论文PDF,聚焦糖尿病性视网膜病变图像的自动识别问题,适合从事深度学习、图像识别方向研究的科研人员,也适合计算机、医学交叉学科的高校学生与课题组成员阅读。整份资源为单篇PDF电子文档,压缩包约3.31MB,目前已有267人学习浏览。论文提出基于多特征融合的卷积神经网络识别方法,在VGG-16模型基础上融合每层网络的局部特征,配合Softmax分类器提升病变图像识别精度,并利用OpenCV图像处理工具,以加噪、上下左右翻转、调整对比度等5种方式扩充训练集,增强模型泛化能力。实验结果表明,该方法的平均识别精度达到94.23%,较AlexNet、GoogleNet、CompactNet、ResNet-101分别提高了10.56%、7.80%、6.01%和0.02%,验证了多特征融合策略的有效性与模型鲁棒性。文档附有完整算法框架、实验设置、特征可视化与结果对比分析,可作为相关课题论文写作、算法设计、毕业设计以及医学图像处理课程学习的参考资料。

1. 视网膜病变图像识别:这篇深度学习论文给了你一条能落地的技术路线

如果你做过医学图像识别,大概率会遇到这种尴尬:文献里写“平均识别精度94.23%”,但你照着复现,连环境都跑不起来。这篇《一种基于深度学习的视网膜病变图像识别方法》就是典型的“可抄作业”论文,它不玩虚的,把数据集来源、预处理方式、网络结构和训练参数全给你摊开了。它解决的是糖尿病性视网膜病变图像的二分类问题——区分正常眼底图像和病变眼底图像,核心手段是VGG-16模型上的多特征融合,把网络各卷积层提取的局部特征融合起来,配合Softmax分类器,最终在测试集上做到了94.23%的平均识别精度。如果你是做医学图像识别、深度学习图像分类的入门者或工程人员,这篇论文的资源价值在于:它给出了完整的数据扩充、模型构建和训练评估链路,你不需要从零摸索,直接按它的参数走一遍就能跑通。

2. 从30571到128339幅图像:数据预处理与五种扩充方式

2.1 数据来源与统一化处理:为什么分辨率要先缩到64×64

论文用的初始数据来自五个公开数据集:FIRE、DIARETDB1、Messidor、DR1和Kaggle-DR,合计30571幅眼底图像,其中正常图像13615幅,病变图像16956幅。这批数据的属性差异很大,FIRE分辨率是2912×2912,而DR1只有640×480,拍摄相机从Nidek AFC-210到拓普康NW100都有。论文的处理思路很直接:不要高分辨率,先把所有图像统一缩小到64×64×3。

这里要理解一个关键点,它不是不能处理大图,而是为了控制训练成本。65×64×3的输入配合VGG-16的五层卷积池化结构,在GTX1060 3GB这种入门级显卡上能跑得动。你如果手头显卡显存不大,这个分辨率选择值得参考。实际工程里,医学图像分辨率往往比通用图像数据集高得多,统一缩放到固定尺寸是必经之路,64×64是一个性价比不错的选择。当然,代价是丢失了大量细节信息,这点后面的避坑章会细说。

2.2 数据集划分策略:四层切分的逻辑

数据划分是这篇论文容易被忽略的细节。它没有简单地把30571幅图分为训练集和测试集,而是切了四层:

  • 原始30571幅 → 先分80%(约24457幅)作为Data1,20%(约6114幅)作为DataFinal
  • 对Data1做5倍扩充 → 得到122285幅
  • 扩充后的Data1再分80%(97828幅)作为DataTrain,20%(24457幅)作为DataTest

这种做法在工程上是合理的。DataFinal是原始未扩充的测试集,用来检验模型在真实分布数据上的表现;DataTest与DataTrain同分布但不重叠,用来观察训练过程中的效果;DataFinal则验证模型泛化能力。训练集和最终测试集来自同一批原始数据,但在分布上有所差异,这更接近实际部署时的场景。

以下是数据集划分的示意逻辑:

import cv2 import numpy as np from sklearn.model_selection import train_test_split # 假设 images 为原始图像数组,labels 为对应标签 # 第一步:分出最终测试集 DataFinal(20%) images_train_val, images_final, labels_train_val, labels_final = train_test_split( images, labels, test_size=0.2, random_state=42, stratify=labels ) # 第二步:对 images_train_val 做扩充(代码见下文 2.3 节) # 扩充完成后得到 augmented_images, augmented_labels # 第三步:将扩充后的数据再按 80/20 切分为 DataTrain 与 DataTest data_train, data_test, labels_train, labels_test = train_test_split( augmented_images, augmented_labels, test_size=0.2, random_state=42, stratify=augmented_labels )

这里stratify=labels的作用是保持正常/病变图像比例在切分前后一致,避免某一类样本在测试集中占比失衡。random_state=42则是固定随机种子,保证每次运行得到一致的划分结果——这在调试和对比实验时非常重要,不然你很难分辨效果差异是模型带来的还是数据划分带来的。工程里我习惯把随机种子和划分比例写进配置文件,方便回溯。

2.3 OpenCV数据扩充:五种方式与关键参数

论文用了OpenCV做数据增强,五种方式分别是:加噪、上下翻转、左右翻转、仿射变换、调节对比度。这些操作全部基于OpenCV内置函数。

import cv2 import numpy as np def augment_image(image): """对单张眼底图像执行5种随机增强操作""" augmented = [] # 1. 加高斯噪声 noise = np.random.normal(0, 0.05, image.shape).astype(np.float32) noisy_img = np.clip(image.astype(np.float32) + noise, 0, 255).astype(np.uint8) augmented.append(noisy_img) # 2. 上下翻转 flip_ud = cv2.flip(image, 0) augmented.append(flip_ud) # 3. 左右翻转 flip_lr = cv2.flip(image, 1) augmented.append(flip_lr) # 4. 仿射变换(旋转 + 平移) rows, cols = image.shape[:2] M = cv2.getRotationMatrix2D((cols/2, rows/2), angle=15, scale=0.9) affine_img = cv2.warpAffine(image, M, (cols, rows)) augmented.append(affine_img) # 5. 调节对比度 alpha = 1.5 # 对比度增益系数 contrast_img = cv2.convertScaleAbs(image, alpha=alpha, beta=10) augmented.append(contrast_img) return augmented

高斯噪声的参数选择有讲究。np.random.normal(0, 0.05, ...)里的0.05是标准差,值越小噪声越轻微,值过大会把眼底图像原本就细微的血管特征彻底淹没。我做过实验,这个场景下0.03到0.08之间比较安全。仿射变换用getRotationMatrix2D设定中心和旋转角度,15度是小角度旋转,不会让眼底图像的解剖结构产生畸变——眼底图像不像自然图像,旋转90度那种激进增强会破坏视盘和血管的方位关系。对比度增强用convertScaleAbs,alpha=1.5是增益系数,数值大于1增强对比度,beta=10是亮度偏移量,配合使用让病变区域的血斑、渗出物更明显。

这五种方式协同工作的效果,是把24457幅扩充到122285幅,整整5倍。论文在测试集上对比发现,用扩充后数据训练出来的模型泛化能力更强,在DataFinal上准确率达到94.41%,比DataTest高出3.25个百分点——这个反直觉的结果恰恰说明数据扩充让模型学到了更多特征,而不是死记硬背训练样本。

3. VGG-16模型改进与add特征融合:网络结构与参数细节

3.1 为什么选VGG-16而不是更深的网络

论文选择VGG-16作为基础模型,理由有三点:结构规整、参数量相对较少、分类性能好。对比实验中,ResNet-101的平均准确率是94.21%,和论文算法的94.23%几乎持平,但ResNet-101有101层网络结构,参数量和计算复杂度远高于16层的VGG-16。这是工程里的核心权衡逻辑:用更简单的结构达到接近甚至超越深层网络的精度,训练速度和资源占用都占优。

VGG-16结构规整在于它只有两种卷积层配置:3×3卷积+1步长,2×2池化+2步长。不像Inception系列要考虑不同尺寸卷积核的并行组合,也不像ResNet要设计残差块和跳跃连接。VGG-16的所有卷积层都是同一个模式堆叠,参数理解成本极低——这对复现者来说非常友好。

3.2 特征融合的两种方式:concat与add的选择

论文对特征融合方式专门做了对比分析,这是整篇论文技术含量最高的部分。concat和add是两种完全不同的特征融合策略。

在一段描述中可以参考作者的重要表述:“特征融合有concat和add两种方式:concat方式是将图像的通道数合并,即图像本身的特征数(通道数)增加,特征信息没有增加;add方式是将图像对应的特征信息相加,通道数不变,之后进一步执行卷积操作。”

两个公式可以清晰地展示这两种融合路径:

C_concat = Σ(X_i * K_i) + Σ(Y_i * K_i) + c C_add = Σ((X_i + Y_i) * K_i)

公式中K代表卷积核,*是卷积操作。concat操作把两路特征图沿着通道维度拼接,维度直接翻倍,后续卷积核数量也要对应增加,参数和计算量随之暴涨。add则是逐元素相加,通道数不变,相当于把两路特征在同一位置的值叠加起来,增强了网络对特定位置的响应强度。

论文选择add的决策依据是:concat需要更多参数,融合后维度更高、计算量更大、负载更重。而在这个二分类任务里,两种方式的分类效果相当。工程上遇到“效果相当但计算量差距明显”的情况,毫无疑问选轻量方案。

以下是特征融合层的简洁实现示意:

import tensorflow as tf def feature_fusion_add(layers): """ 将多个卷积层的特征图按位置求和融合 layers: 来自不同卷积层的特征图列表,形状必须一致 """ if len(layers) == 1: return layers[0] fused = layers[0] for layer in layers[1:]: fused = tf.add(fused, layer) # 逐元素相加,通道数不变 return fused

这里有个隐藏陷阱:不同卷积层的输出尺寸和通道数完全不一样,不能直接做add。论文的解决方案是逐层追踪,Cov1的Feature Fusion输入是4×4×512,融合层输出也是4×4×512,它融合的是各层经过后续卷积池化后尺寸对齐的特征图,而不是原始卷积层输出直接相加。这个细节在复现时很关键——你需要对每一层的输出尺寸做计算和验证,确保融合时维度一致。我通常的做法是在融合前加一个Assert断言,跑通一次后确认无误再移除。

3.3 网络完整参数与全连接层设计

论文给出了完整的模型参数表,复现时可以照着搭。整个网络结构是5个卷积层(每层包含两个子卷积)、5个最大池化层、1个Feature Fusion层、2个全连接层和1个Softmax层。

import tensorflow as tf def build_model(input_shape=(64, 64, 3)): """基于VGG-16的多特征融合视网膜病变识别模型""" model = tf.keras.Sequential() # 卷积块 1:输出 64x64x64 model.add(tf.keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu', input_shape=input_shape)) model.add(tf.keras.layers.Conv2D(64, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.MaxPooling2D((2, 2))) # 卷积块 2:输出 32x32x128 model.add(tf.keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.Conv2D(128, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.MaxPooling2D((2, 2))) # 卷积块 3:输出 16x16x256 model.add(tf.keras.layers.Conv2D(256, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.Conv2D(256, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.MaxPooling2D((2, 2))) # 卷积块 4:输出 8x8x512 model.add(tf.keras.layers.Conv2D(512, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.Conv2D(512, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.MaxPooling2D((2, 2))) # 卷积块 5:输出 4x4x512,添加池化后特征进入融合 model.add(tf.keras.layers.Conv2D(512, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.Conv2D(512, (3, 3), padding='same', activation='relu')) model.add(tf.keras.layers.MaxPooling2D((2, 2))) # 全连接层 + Dropout model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(8192, activation='relu')) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.Dense(4096, activation='relu')) model.add(tf.keras.layers.Dropout(0.3)) # Softmax 二分类输出 model.add(tf.keras.layers.Dense(2, activation='softmax')) return model

这个结构里Filter个数依次是64、128、256、512、512,所有卷积核尺寸都是3×3×3,步长1;池化层Filter尺寸2×2×3,步长2。全连接层维度是8192和4096,这个数值相当大——就是因为输入64×64经过Flatten后是4×4×512=8192维,FC1正好对应8192,FC2降到4096,最后Softmax输出2类。注意这个8192维的全连接层占据了模型绝大部分参数量,训练时对内存的占用极大,GTX1060 3GB跑起来会比较吃力,batch_size=30可能就是显存限制下的选择。

4. 训练配置与超参调优:从初值到收敛的完整链路

4.1 核心训练参数:权重初始化、学习率与SGD

论文的训练参数给得非常具体,这节内容是复现的“抄作业指南”。权重初始化服从标准差0.01、均值0.1的截断正态分布,学习率设为0.07,batch_size为30,最大迭代次数3000次,损失函数权重衰减因子0.1,Dropout保留率p=0.3。

import tensorflow as tf # 权重初始化:截断正态分布 initializer = tf.keras.initializers.TruncatedNormal(mean=0.1, stddev=0.01) model = build_model() model.compile( optimizer=tf.keras.optimizers.SGD(learning_rate=0.07), loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) history = model.fit( x_train, y_train, batch_size=30, epochs=60, # 3000次迭代 / (97828 / 30) ≈ 每个epoch约61次迭代 validation_data=(x_test, y_test), verbose=1 )

TruncatedNormal(mean=0.1, stddev=0.01)是截断正态分布,不同于标准正态分布的地方在于它会丢弃均值两侧之外的值,避免初始化权重过大或过小导致梯度爆炸或消失。均值0.1偏正向的设定,配合ReLU激活函数,能保证网络在初始阶段更多神经元处于激活状态。学习率0.07属于偏高的值,这是为了在数据量大、迭代次数多的情况下加快收敛速度。论文里Loss在训练轮次约1000轮前快速下降至0.18,Accuracy上升到0.82,就是高学习率的直接效果。

4.2 Dropout与ReLU:过拟合抑制的两个手段

模型在两个全连接层FC1和FC2上用了Dropout技术,这是针对全连接层参数量巨大的对症方案。Dropout的原理是按一定概率随机断开网络连接——被断开的神经元输出置为0,相当于该连接不存在了。训练阶段因为每次前向传播都随机丢弃一部分连接,网络不会过度依赖某条特定路径,从而降低过拟合。

论文做了一个关键参数选择:Dropout丢失率p=0.3。文献给出的合理取值范围是0.1到0.5,但具体数值需要实验验证。这里要注意,丢失率p=0.3意味着保留概率q=0.7,被保留神经元的输出要乘以1/q做缩放,这是训练和预测阶段的行为差异——预测时不丢弃任何神经元,但要把权重乘以q来补偿。TensorFlow的Dropout层会自动处理这个细节,如果你手写实现,千万别漏掉这个缩放。ReLU激活函数则负责把线性计算转换为非线性关系,公式是f(x) = max(0, x),小于0的输入直接截断为0。相比sigmoid或tanh,ReLU的计算量小,收敛速度快,这是论文选它的直接原因。我用ReLU踩过的坑是“神经元死亡”:当输入进入负区间后梯度恒为0,神经元永远不再更新。如果训练中Accuracy卡在某个值不动,检查一下全连接层有没有大面积神经元死亡,必要时可以用LeakyReLU替代。

4.3 Loss与Accuracy的变化曲线:怎么知道训练正常

论文记录了完整训练过程中Loss和Accuracy的变化:训练次数从0到约1000轮,Loss从高位跌至0.18,Accuracy从0升至0.82,这是SGD算法沿损失函数梯度方向快速下降的结果;1000轮到接近1750轮之间,Loss在0.06到0.27之间小幅度波动,Accuracy在0.73到0.94之间摆动,这是SGD在局部最优解附近震荡的表现;1750轮之后Loss收敛至0.06左右,Accuracy稳定在0.94,训练结束。

读曲线有个工程技巧:只看Loss不看Accuracy容易误判。Loss在不断下降,但Accuracy在波动——这通常是分类阈值和决策边界在训练中微调导致的。反过来,如果Accuracy在上升但Loss不降,可能是过拟合的早期信号,模型开始记忆训练样本而不是学习泛化特征。我在实际项目中会在每个epoch结束时同时记录这两个指标,并且额外记录验证集的指标,防止训练集指标好看但验证集一塌糊涂的情况。论文没有提到验证集早停策略,但从曲线的收敛情况看,模型在1750轮后已经进入稳定状态,3000次的迭代上限足够。

5. 避坑指南:复现这篇论文时最容易翻车的五个问题

5.1 TensorFlow 1.0代码在TF2.x环境跑不起来

现象:论文实验环境是TensorFlow 1.0、Python 3.5.6,你下载到代码后直接用最新版TensorFlow运行,报错一堆,tf.Session、tf.placeholder直接不存在。

原因:TensorFlow 2.x移除了大量1.x的API,tf.Session需要改用tf.compat.v1,Eager Execution是默认模式,图的构建和计算方式完全变了。

解决:二选一。一是装TF 1.15版本,pip install tensorflow==1.15,Python版本也要降到3.7以下;二是用tf.compat.v1兼容模式,代码开头加import tensorflow.compat.v1 as tf; tf.disable_v2_behavior(),但这可能引发其他兼容问题。我更推荐直接按2.x的Keras API改写,参考前面第3章的build_model()函数,结构清晰且不依赖旧版本API。

5.2 64×64分辨率导致眼底图像细节丢失

现象:训练完成后模型在验证集上准确率尚可,但部署到真实眼底图像上识别率明显下降,血斑、微动脉瘤等小病变特征识别不出来。

原因:输入分辨率只有64×64,相当于把2912×2912的原始图像压缩到约0.05%的像素量,微小的病变特征在降采样过程中被直接抹掉了。论文自己也承认色彩干扰了识别——黄斑与血斑在低分辨率下区分度更低。

解决:如果你不是复现论文而是实际应用,建议把输入分辨率提升到224×224或更高,同时调整卷积核尺寸和池化策略。代价是训练时间增加、显存压力变大,你的GPU得从GTX1060 3GB升级到至少6GB以上显存。

5.3 二分类与五分类指标不可比

现象:论文数据集只分正常和病变两类,准确率94.23%。对比文献里李琼等人用Alex-Net做五分类,准确率93%。你拿论文结果和五分类模型直接比,觉得效果差不多。

原因:二分类任务天然比五分类容易,二分类随机猜测基线是50%,五分类随机猜测是20%。两个模型的分类粒度和难度完全不同,准确率直接对比没有意义。论文对比的Alex-Net、Google-Net等都是同一个二分类任务上的结果,这才是公平对比。

解决:看文献时要关注分类任务设置,粒度一致才能对比。实际应用中,如果想细分病变程度等级,建议在二分类模型基础上做迁移学习,把Softmax维度从2改成5,冻结前几层卷积参数只训练高层和分类器,能大幅减少训练成本。

5.4 Dropout只在全连接层用,卷积层没用

现象:复现时想当然地在所有卷积层后都加了Dropout,结果训练收敛变慢,准确率反而下降。

原因:卷积层的参数量远小于全连接层,由池化和权重共享机制提供的正则化效果已经足够,叠加Dropout反而破坏了卷积层特征提取的连续性。

解决:严格按论文配置,Dropout只加在FC1和FC2上,丢失率p=0.3。如果确实担心过拟合,优先考虑其他正则化手段,如早停(early stopping)、L2权重衰减,而不是在卷积层加Dropout。

5.5 显存溢出或训练极慢

现象:GTX1060 3GB跑TF 1.0代码,batch_size=30时直接OOM,或者一个epoch要跑十几分钟。

原因:3GB显存是入门级配置,8192维的全连接层权重矩阵极大,反向传播时中间梯度也需要驻留显存。如果用的是TF1.0且没配置显存增长策略,框架会默认占用全部可用显存。

解决:在TensorFlow代码里设置gpu_options = tf.GPUOptions(allow_growth=True),让框架按需使用显存。同时确认batch_size=30是经过试验折中的值,如果实在带不动,可以降低到16并相应调低学习率。另一个做法是开启混合精度训练(mixed precision),显存占用能降一半左右,前提是GPU支持FP16运算。

6. 进阶与验证:如何在你的机器上复现并验证这篇论文的结果

6.1 环境推荐配置与项目目录组织

我强烈建议你不要按论文原版环境去配。在现在的技术条件下,合理的方式是Python 3.8以上、TensorFlow 2.10左右、OpenCV 4.x,Keras API实现全部网络结构。硬件方面,8GB显存起步,如果只有CPU也不是不能跑,但训练时间会拉长到以天计算。

目录结构推荐这样组织:

retina_fusion/ ├── data/ │ ├── raw/ # 原始数据集,按 FIRE/DIARETDB1 等分目录 │ ├── augmented/ # 扩充后的图像 │ └── split/ # 划分后的 DataTrain/DataTest/DataFinal ├── src/ │ ├── preprocess.py # 统一缩放 + 数据扩充 │ ├── model.py # VGG-16 特征融合模型定义 │ ├── train.py # 训练脚本 │ └── evaluate.py # F1-score 与准确率评估 └── checkpoints/ └── model.h5 # 训练好的模型权重

6.2 完整训练复现流程:从预处理到评估

以下是我整理的端到端复现流程,将论文的参数细节全部落到可执行代码:

# preprocess.py —— 数据预处理与扩充 import cv2 import numpy as np from glob import glob def load_images(img_paths, target_size=(64, 64)): """统一加载并缩放眼底图像""" images = [] for path in img_paths: img = cv2.imread(path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, target_size) # 统一为 64x64x3 images.append(img) return np.array(images, dtype=np.float32) / 255.0 def build_dataset(label_dirs): """按目录结构读取并标注,正常图像标0,病变图像标1""" images, labels = [], [] for label, dir_path in enumerate(label_dirs): img_paths = glob(f"{dir_path}/*.jpg") imgs = load_images(img_paths) images.extend(imgs) labels.extend([label] * len(imgs)) return np.array(images), np.array(labels)

这个预处理脚本注意两点:cv2.cvtColor(img, cv2.COLOR_BGR2RGB)是必须的,OpenCV默认加载BGR格式,直接送到模型里训练,颜色通道是反的,对色彩敏感的特征提取会造成严重影响。np.float32(images) / 255.0做像素归一化,让输入范围落在0到1之间,配合截断正态分布初始化和0.07学习率。

# train.py —— 训练主流程 import tensorflow as tf from model import build_model # 加载预处理后的数据(略,见 preprocess.py) x_train, y_train, x_test, y_test = load_split_data() model = build_model(input_shape=(64, 64, 3)) model.compile( optimizer=tf.keras.optimizers.SGD(learning_rate=0.07), loss='sparse_categorical_crossentropy', # 标签为整数,不需要one-hot metrics=['accuracy'] ) # 关键:早停 + 动态学习率 callbacks = [ tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True), tf.keras.callbacks.ReduceLROnPlateau(patience=5, factor=0.5, min_lr=0.001) ] history = model.fit( x_train, y_train, batch_size=30, epochs=60, validation_data=(x_test, y_test), callbacks=callbacks, # 论文没有显式提出早停,但这是我强烈建议加的 verbose=1 ) model.save('checkpoints/model.h5')

sparse_categorical_crossentropy配合整数标签,不需要手动做one-hot编码,如果标签已经是one-hot格式就用categorical_crossentropy,这个细节错了损失函数直接不起作用。EarlyStopping(patience=10)的意思是验证集指标连续10轮不提升就终止训练,restore_best_weights=True会回滚到验证集表现最好的权重——论文固定3000次迭代,但我建议加上早停,可以省去不必要的训练时间,也能避免后期过拟合。ReduceLROnPlateau在指标停滞后把学习率减半,弥补论文里直接固定学习率的缺陷。

# evaluate.py —— F1-score评估 from sklearn.metrics import f1_score, accuracy_score y_pred_probs = model.predict(x_final) # x_final 为 DataFinal 测试集 y_pred = np.argmax(y_pred_probs, axis=1) acc = accuracy_score(y_true, y_pred) f1 = f1_score(y_true, y_pred, average='macro') # 二分类用 binary 更精确 print(f"Accuracy: {acc:.4f}") print(f"F1 Score: {f1:.4f}")

average='macro'对每个类别的F1取算术平均,适合类别不平衡的场景。论文里对比不同模型的F1-score用的是同样的计算口径,你复现时保持一致即可。如果算出来的差距在合理范围内——比如准确率在92%到95%之间波动,说明复现成功;如果大幅低于论文值,优先检查数据预处理和扩充部分是否严格按照5种方式执行,这是最容易漏步骤的环节。

6.3 超出论文本身的改进空间

论文在结语部分已经指出了未来方向:将数据集转为灰度图像以消除色彩干扰、按病变程度分级做更细致的分类、使用基于判别区域的网络结构。这三个方向就是顺手的进阶路径。

灰度处理最直接,cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)就行,但要注意灰度图只有单通道,模型输入要从3通道改成1通道,第一层卷积的输入维度也要调整。分级分类则要把Softmax输出从2类改成5类——国际标准里糖尿病性视网膜病变分为无、轻、中、重、增殖期五个等级,每级的形态学特征差异很大,这需要更大的数据集和更高的输入分辨率才能支撑。基于判别区域的网络结构,常用做法是加入注意力机制(如CBAM、SE模块),让网络自适应地关注眼底图像中的关键区域,比如视盘周围、血管密集区——这些区域是病变高发区域。

复现论文时,一个有效的方法是先复现论文的结论,确认自己的环境和代码链路没问题,然后在这个基础上去尝试改进。就拿我来说,这些年接触的图像分类项目不少,见到很多人在复现前就直接开始“改进”,最后连原始结果都跑不出来——根本分不清是环境问题还是网络结构问题。从那以后,我做任何论文复现都强制先跑通原版实验、确认指标达标,再开始改结构和参数,这个习惯帮我少踩了很多莫名其妙的坑。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询