☰
Matlab实现VGG与ResNet图像分类:从预训练微调到对比实验
2026/10/2 2:36:27 网站建设 项目流程

简介:这是一份将VggNet与ResNet两大经典卷积神经网络应用于物体分类识别研究的Matlab代码包,适合深度学习初学者、计算机视觉研究者及Matlab开发者对照学习。资源共60012个文件,主体为60002张图像样本,另含4个Matlab脚本、3个预训练网络权重文件以及1份PDF说明文档,包体约398.58MB,便于直接加载数据进行训练与验证。目前已有759人学习下载。资源覆盖网络结构对比、分类性能评估、训练策略调优、Matlab模型搭建与预训练权重加载等关键环节,并提供可运行的get_vggnet.m、get_resnet.m及配套fig交互界面,读者可结合飞机等示例图像快速上手,理解残差连接与深度卷积的差异,并迁移至目标检测、语义分割等扩展任务。

1. 先说清楚这个项目在解决什么问题

一个做物体分类识别的任务,给你一套设备拍摄的照片或者网上抓的图片集,要求把每一张图归到它所属的类别里。VggNet和ResNet就是两条最成熟、最不容易出错的卷积神经网络路线,而“附Matlab代码”则是说整套训练、验证、预测流程都能在一个熟悉的工具里跑完。对于还在纠结“深度学习是不是非得装Python环境”“上课只学过Matlab怎么做课题”的人来说,这个方向几乎是投入产出比最高的选择。它能解决的是:不用自己写底层卷积、反向传播、残差计算的数学实现,把精力放在数据整理、网络选型和实验对比上,同时用手写数字识别这类小数据集快速验证,再平移到自己课题的图片分类场景里。适合本科毕设、课程设计以及刚入门CV的工程师,下面按我自己的实操顺序来讲。

2. 把VGG和ResNet的底摸清:为什么在Matlab里选它们当基线

2.1 VGG16为什么靠“堆小卷积核”就能赢

VGG的核心思路非常朴素:把卷积层全部用3x3的小卷积核堆起来,通过连续多个小卷积核获得与大卷积核等效的感受野。比如两层3x3卷积堆叠,感受野等效于一个5x5卷积,三层3x3等效于7x7,但参数量更少,非线性表达能力更强。VGG16的常见结构是13个卷积层加3个全连接层,中间穿插2x2最大池化,最后接Softmax分类层。整条网络从头到尾的规则非常一致,这对新手调试来说是极大的优点——每一层输出尺寸的变化都能用手算出来,出问题时不至于像黑匣子一样无从下手。

VGG16在当年的分类精度上其实已经做到很能打,但代价是模型体积大、计算量大。常见数据结构里,VGG16的权重文件超过500MB,单次前向推理需要几百兆浮点运算,放到现在的眼光里属于“笨重但可靠”的代表。让我在实际课题里更常用它的原因有两个:一是作为特征提取器时,VGG中层的语义信息比较直白,可视化出来贴近人的直觉;二是Matlab对VGG16的预训练权重支持得非常完善,加载后微调几乎不会遇到版本兼容问题。

这里有一个常被忽略的工程点:VGG的全连接层占了绝大部分参数量。在Matlab里用analyzeNetwork查看VGG16结构时,能看到fc6、fc7两个全连接层的参数量是千万级别的。如果你只是做物体分类而不用它做目标检测的候选框分支,微调时完全可以把fc6、fc7换成更小的全连接层,甚至直接只保留一个全局平均池化再接分类层,这样微调速度会有肉眼可见的提升。

2.2 ResNet的恒等映射到底解决了什么问题

ResNet要解决的是网络加深之后效果反而变差的问题,也就是“退化问题”。不是过拟合,是训练集上的误差都降不下去。反向传播的过程中,梯度要一层一层往回传,层数一深,连乘效应会让梯度指数级变小,前面的层几乎学不到东西。ResNet给出的是残差块结构:让这一层的输出不是直接拟合目标映射H(x),而是拟合残差F(x)=H(x)-x,然后把输入x以恒等映射的方式加到输出上。这样一来,如果这一层“没什么可学的”,它只需要把F(x)压到接近0,信号就能原样穿过,梯度也因此有了一条高速公路回传到浅层。

在Matlab里看ResNet50的层图,最直观的感受是它不像VGG那样是一条直线,而是出现了很多“分叉再合并”的结构,这就是残差块产生的跳连。常见的ResNet系列里,18层和34层用的是BasicBlock,50层、101层、152层用的是Bottleneck结构,Bottleneck先用1x1卷积压缩通道数,再用3x3卷积做空间特征提取,最后用1x1卷积把通道数扩张回去,目的就是控制计算量。

需要强调的是,ResNet的“残差计算”在理论分析中和BP里的常规梯度计算没有本质区别,它只是改变了网络结构,让梯度在不经过非线性激活的情况下也能流通。所以如果你在论文里写“ResNet改善了反向传播中的梯度衰减”,这个表述是准确的;如果写“ResNet发明了新的反向传播算法”,那就是把概念搞混了。

2.3 在Matlab里查看两种网络结构的最小命令

不需要自己从头搭建网络,Matlab的Deep Learning Toolbox直接提供了预训练模型。代码只有三行,目的是先把结构点亮,让心里有数再动手。

vggModel = vgg16; analyzeNetwork(vggModel);
resModel = resnet50; analyzeNetwork(resModel);

第一段代码把VGG16实例化,第二行打开一个交互式窗口,能看到每一层的名称、类型、激活尺寸、可学习参数数量。这个窗口还能点击任意一层查看详细属性,对后续修改网络结构非常关键。第二段代码同理,但注意resnet50首次执行时会检查是否存在预训练权重文件,文件不在本地时会发起下载,网络环境不稳定时这一步就是后面要讲的坑之一。

提示:Matlab版本越新,这两种网络的支持越完整。如果你用的是较旧的版本,没有resnet50时可以先试resnet18,流程完全一样。

3. 用Matlab把自己的数据集跑起来:数据整理、训练脚本与关键参数

3.1 让图片文件夹自动变成带标签的数据集

Matlab做图像分类任务,第一个动作永远是建立imageDatastore,而不是自己写循环读图。imageDatastore能自动按照子文件夹的名字给每张图片打标签,这个特性直接决定了数据集的目录结构必须是“根目录/类别名/图片文件”。比如我有cat和dog两个类别,就要建两个子文件夹cat和dog,然后一行代码搞定数据读取。

imds = imageDatastore('D:\dataset\catdog', ... 'IncludeSubfolders', true, ... 'LabelSource', 'foldernames');

这段代码的第一个参数是数据集根目录,第二个参数表示递归读取子文件夹,第三个参数说明标签来源于文件夹名。执行后可以在命令行输入imds.Labels查看每个样本的标签是否与实际类别一一对应,这一步能提前发现文件夹里混入异常文件的问题,避免了后面训练时加载图片报错。

接下来把数据集划分成训练集和验证集。常见做法是按8:2的比例随机分层抽样,确保每个类别在两个集合中的比例一致。

[imdsTrain, imdsValidation] = splitEachLabel(imds, 0.8, 'randomized');

splitEachLabel的第三个参数是训练集占比,0.8表示80%用于训练,20%用于验证,'randomized'表示随机打乱后再切分。这里容易出现一个误区:很多人在切分之前忘了先打乱数据,导致同一个类别的图片全集中在前面,切出来的训练集和验证集分布严重失衡,准确率看起来很高,换个实际场景就翻车。所以你一定加'randomized',而且如果类别间样本数差异很大,建议先调用countEachLabel确认每类数量,再决定要不要做数据增强来平衡。

3.2 图片尺寸统一与数据增强:这两个参数别拍脑袋

VGG和ResNet的输入尺寸都是224x224,普通手机照片动辄4000x3000像素,直接喂进去网络会报尺寸不匹配的错误。Matlab提供了augmentedImageDatastore,负责把图片缩放、按batch打包,并且以随机变换的方式做数据增强,这是训练中既重要又容易被当成玄学的一部分。

inputSize = [224 224]; augTrain = augmentedImageDatastore(inputSize, imdsTrain, ... 'DataAugmentation', imageDataAugmenter( ... 'RandXTranslation', [-10 10], ... 'RandYTranslation', [-10 10], ... 'RandScale', [0.9 1.1], ... 'RandXReflection', true), ... 'OutputSizeMode', 'resize'); augValidation = augmentedImageDatastore(inputSize, imdsValidation, ... 'OutputSizeMode', 'resize');

train这边加入了随机平移、随机缩放、随机水平翻转,validation这边则不做任何增强,只负责把图片缩放成224x224。原因很简单:验证集的目的是模拟真实预测环境,如果你在验证集上也做随机裁剪和翻转,那你评估的指标就不是模型真实水平,而是“模型加上数据增强后”的水平,这种混合会误导你对精度的判断。这里每个参数我一般建议从小到大试:RandXTranslation范围先设正负5像素,训练不稳定再放大,不要一上来就做很强烈的增强,因为小数据集配合大增强经常会欠拟合,准确率在低点徘徊。

3.3 从预训练模型微调:替换分类层与训练选项

用预训练模型做迁移学习,而不是从头训练,是这种课题在有限硬件条件下的唯一现实选择。借助Matlab代码做微调,只需要把最后原本分类到1000类的全连接层和softmax层替换成自己数据集对应的分类层。

net = resnet50; lgraph = layerGraph(net); numClasses = numel(categories(imdsTrain.Labels)); newFCLayer = fullyConnectedLayer(numClasses, 'Name', 'fc_new'); newClassLayer = classificationLayer('Name', 'classoutput'); lgraph = replaceLayer(lgraph, 'fc1000', newFCLayer); lgraph = replaceLayer(lgraph, 'ClassificationLayer_fc1000', newClassLayer);

这里replaceLayer的作用是把旧分类层替换掉,同时保留前面所有卷积层已经学好的参数。之所以不动浅层卷积,是因为浅层学到的是边缘、颜色块、纹理这些通用特征,在任何图片数据集上都可用;可学习的重点是高层语义特征和分类层之间的组合方式。如果换成vgg16,替换的位置则是'fc8'和'ClassificationLayer_fc8',不同版本网络内部层名略有差异,用analyzeNetwork确认后再写,避免replaceLayer时找不到名字报错。

训练选项是我最看重的一段配置,这里直接给出常用的一组微调参数,并解释为什么这样设置。

options = trainingOptions('sgdm', ... 'InitialLearnRate', 1e-3, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropFactor', 0.3, ... 'LearnRateDropPeriod', 3, ... 'MiniBatchSize', 16, ... 'MaxEpochs', 30, ... 'Shuffle', 'every-epoch', ... 'ValidationData', augValidation, ... 'ValidationFrequency', 20, ... 'Plots', 'training-progress', ... 'ExecutionEnvironment', 'auto', ... 'Verbose', true);

这组参数里最值得琢磨的是学习率和BatchSize的关系。学习率1e-3是微调ResNet比较稳的起点,因为预训练权重已经在一个大分布上收敛过了,学习率太大一步跨出去就会把已有特征破坏掉,表现为训练loss一开始就震荡或者跳到nan。MiniBatchSize设16是因为预训练网络显存占用较大,普通8G显存的显卡跑224x224的ResNet50,batch再大一点就容易OOM;如果你的显卡显存更大,可以调到32,训练更稳定。LearnRateDropFactor是每个周期结束后学习率乘以0.3,让训练后期用更小的步长去精细调整权重。

3.4 训练完怎么快速出指标

trainNetwork跑完之后,把模型存下来并在验证集上预测,这是闭环的最后一环。考虑到后续论文实验往往需要反复对比精度,我习惯封装一个简单的评估脚本。

net = trainNetwork(augTrain, lgraph, options); [YPred, scores] = classify(net, augValidation); YValidation = imdsValidation.Labels; accuracy = mean(YPred == YValidation); plotconfusion(YValidation, YPred);

classify返回两个值,第一个是每个样本的预测类别,第二个是每个类别的置信度分数。如果你只想要Top-1准确率,直接比较向量即可;如果想分析错在哪里,plotconfusion会生成混淆矩阵图,从图上能看到哪两类之间最容易混淆。这一步对第6章的细粒度特征分析有直接用处,不要跳过。

4. 对比实验怎么设计才不算白跑:控制变量、读表、选型

4.1 控制变量:三组对比实验的标准做法

既然是“基于VggNet网络与ResNet神经网络”的研究,结果里必须有说服力强的对比数据。最常见、也最不容易被质疑的做法是固定同一份数据集、同一套训练选项,只切换网络结构,跑出精度曲线和混淆矩阵。注意“同一套训练选项”这一条,很多人在VGG上用batch=8,在ResNet上用batch=32,最后说两个网络精度差距很大,这就是无效对比。硬件资源和时间允许的话,至少跑三组:VGG16、ResNet18、ResNet50;条件有限就做VGG16和ResNet50两组,外加一组从头训练的VGG16作为参照,用来说明预训练迁移学习的收益。

为了尽量保证公平,MaxEpochs可以按“同样迭代步数”或者“同样训练轮数”来定。我一般按同样轮数来定,比如都设30个epoch,让每个网络看完的图片总批次数接近;如果ResNet收敛慢,可以把它的MaxEpochs加到40,记录两组在各自最后十轮的平均验证准确率,而不是拿中间某一个epoch的峰值来比。这里有一点要提前说明:在论文里写清楚“基线设置”比精确对比本身更重要,审稿人质疑的往往就是没交代清楚的部分。

4.2 从训练曲线和混淆矩阵里读出来的才是结论

跑完三组以后,你把训练进度曲线截图保存,再把混淆矩阵和准确率记录成一张表。表的基本格式是:网络名称、参数量、训练耗时、验证准确率、最大BatchSize、显存占用。以常见公开数据来看,VGG16参数量约1.38亿,ResNet50约2550万;训练耗时的差距在网络和硬件配置上具体体现不同,但VGG通常会比ResNet慢30%到一倍,显存占用也明显更高。需要明确的是,具体数字与你的GPU型号、图片分辨率、batch大小强相关,论文中只写相对趋势,不写绝对标准值。

读结果时先看混淆矩阵的对角线。如果某一类的召回率特别低,说明这个类别内部样本差异大,或者类别间相似度高。再结合训练曲线看:VGG16如果在验证集上出现典型的“训练loss降、验证loss升”的情况,说明过拟合已经比较明显了,此时可以把Dropout打开,或者在数据增强上加重。ResNet因为参数量相对小,过拟合问题一般轻一些,但如果你的数据集只有几千张图片,Deep层特征也可能会让准确率异常,对应处理办法是减少训练轮数、提高Dropout。这些都是按数据量来调,没有放之四海而皆准的数值。

4.3 训练耗时、显存和精度的三角形取舍

在这个项目里,精度不是唯一指标。有一句血泪经验是:用VGG做出来的高精度,如果训练时间占到总排期的三分之二,那这个方案在实际场景里就不可复用。VGG的优点是可控、结构透明;ResNet的优点是同精度下训练更快、模型更小,部署在Matlab编译出来的独立可执行文件里也更方便携带。因此我的选型建议是:如果数据集在2万张以内,实验时间充足,优先用ResNet50微调,如果需要做消融实验或者特征可视化,才去对比VGG。所谓“消融实验”,在物体分类里最常见的做法就是逐层冻结、逐层放开的训练方式,这需要网络结构有清晰的阶段划分,VGG和ResNet在这个点上表现都不差。

5. 常见问题排查:从卡在“Preparing”到“预训练模型下载不动”

5.1 现象:首次运行resnet50一直停在“Preparing”

初次调用预训练模型时,Matlab需要把权重文件下载到本机缓存目录。由于资源文件体积比较大,网络稍不稳定就会一直停在Preparing很久,或者下载到一半中断报错。原因是Matlab默认从国外服务器获取模型文件,国内网络访问速度不稳定。

解决分两步:第一步,用命令行检查缓存目录是否存在不完整的下载文件,通常位于用户路径下的“AppData\Local\MathWorks”里,找到对应模型缓存文件夹,把不完整的文件删掉,避免断点续传死循环;第二步,找一个网络时段相对好、能稳定下载的时候重新执行。如果多次尝试仍然失败,比较省事的方式是更换为googlenet或resnet18这类体积更小的预训练模型,在Matlab api写法上几乎一样,等网络条件好后需要ResNet50时再补下载。

5.2 现象:trainNetwork报错“参数名不存在”或“层名称无效”

常见情况有两种:一是Matlab版本较老,训练选项里某些参数在当前版本不支持;二是replaceLayer时填的层名与analyzeNetwork里实际显示的名字不同,典型如ResNet50的分类层在不同版本里叫法不完全一致,有人写'ClassificationLayer_fc1000',有人写'ClassificationLayer_fc',写错自然报“无法找到层”。

解决方法是先执行analyzeNetwork(net)打开结构图,直接把分类层所在的名称复制下来,粘贴到代码里,不要凭记忆手敲。值得留意的是,如果你手边代码是从网上找的别人版本,层名很可能对应的是另一个版本,这时候必须一层一层查清楚再动手。

5.3 现象:训练准确率从一开始就停滞在25%左右不动

如果类别数刚好是4类,准确率停在25%附近,几乎可以肯定是分类层或标签出了问题,模型在“瞎猜”。检查两个地方:一是imdsTrain.Labels是否真的按类别打好了标,没有出现所有图片都被标成同一个类别的低级错误;二是replaceLayer之后是否忘记把最终的classificationLayer也替换掉,残留的1000类输出会让loss在计算时错位。一般来说这种问题都能在数据标签检查环节发现,养成跑训练前先countEachLabel、再随机抽一张图用imshow逐张人工确认的习惯,能省下大量排查时间。

5.4 现象:ResNet比VGG精度还低,甚至不收敛

很多人换上ResNet50后实验结果反而变差,而且训练曲线震荡剧烈。最常见原因是学习率过低或过高。ResNet因为残差结构让梯度流通更顺畅,学习率可以适当比VGG大一点,但还是建议从1e-3起步;如果训练震荡,就把1e-3改成3e-4并同步把MiniBatchSize调大。另一个原因是ResNet50的深度相对较大,小数据集上需要更多epoch才能让高层特征适应新分类任务,只跑10个epoch就下了“性能不行”的结论,其实周期还不够。

5.5 现象:验证集准确率高,但换一批图片预测明显变差

这说明模型过拟合了验证集的拍摄条件。你的训练数据可能来源单一,比如全部是实验室固定背景拍的,而实测图片来自不同光线、不同角度。解决方法是引入更强的数据增强:随机亮度、随机色彩抖动、随机缩放范围加大,让网络学会忽略背景和光照变化。另外一个常被忽略的点是验证集划分后要保证每个类别都有足够样本,至少20张。如果某些类别只有5张图,验证指标本身就不具备统计意义,合理做法是不做随机划分,而是从每类中单独抽出固定比例。

5.6 现象:显存不够,训练中途直接报OOM

Intel核显跑不动大网络、普通8G显卡VGG17个层次反向传播时显存瞬间拉满、MiniBatchSize从16改到8还是不够,这些都是常见现场。解决的优先级依次是:把MiniBatchSize降到4或2、关闭Plots窗口(即训练时不要可视化)、改用resnet18、最后才考虑用CPU训练但接受速度大幅下降。注意ExecutionEnvironment设为auto会自动选择可用的GPU,如果你没有GPU,Matlab会自动降级CPU,但训练时间可能是GPU的十倍以上,此时请把输入图片用imresize进一步缩小再训练,效果往往比硬等更快。

6. 用可解释性验证分类模型:Grad-CAM与特征再分析

6.1 一个命令看ResNet到底“看”哪里

训练完成准确率达到可接受范围后,千万别急着收工。物体分类研究的说服力不仅来源于最终准确率,还来源于“模型是因为什么做出判断的”。Matlab里直接内置了Grad-CAM可视化工具,调用起来非常简单:

img = imread('D:\dataset\catdog\cat\test01.jpg'); img = imresize(img, [224 224]); map = gradCAM(net, img, 'cat'); imshow(img); hold on; imagesc(map, 'AlphaData', 0.5); colormap jet;

gradCAM第一个参数是训练好的网络,第二是输入图片,第三是指定的关注类别。返回值是一张和输入图同尺寸的热力图,叠加在原图上后能够清晰看到模型在判断“猫”时主要依赖哪些像素区域。这里的AlphaData设为0.5表示热力图半透明,不会遮住原图。如果你是验证集上的错误分类样本,它会告诉你模型“把注意力放在了错误区域”,这比单纯看一个准确率数字更有诊断价值。

6.2 把注意力热图和混淆矩阵放到一起读

做完Grad-CAM后,把混淆矩阵中出错最多的几个类别的图片挑出来,逐一叠加热力图,会看到两类很典型的现象:一类是模型的注意力被背景中的类似纹理带走了,比如“猫”的背景木地板纹理与“狗”的毛色极其接近,这说明数据集中存在背景泄漏,应该加强训练集多样性;另一类是模型注意力确实在目标上,但目标本身的局部特征太相似,这时就说明网络层数还不够深,或者数据集分辨率太低。连续性研究时可以考虑把ResNet50换成101层,或者引入细粒度特征提取的思路——常见的改进方向是借鉴FPN的分层思想,把粗粒度的高层语义特征与细粒度的底层纹理特征做融合,再配合位置编码增强空间对应关系,让分类不再依赖单一的全局池化向量。这一块在Matlab里需要自定义层,工作量陡增,但作为研究生的延伸方向是成立的。

我的习惯是把每张错误样本的热力图保存下来,连同混淆矩阵一起整理成一张总图放进实验记录里,写结论的时候直接引用。这一步看起来与分类识别没有直接关系,但它能让你在答辩时说出“这个错误是因为模型注意力转移到了背景而非目标区域”这种可验证的分析,而不是只能用“数据不够多”来搪塞。借着一次课题把VGG和ResNet的预训练微调、对比实验、误差分析完整走一遍,同时掌握Grad-CAM这类模型可视化能力,之后遇到其他图像识别任务,你手里就有一套可以反复复用的流程和代码基础,希望帮到你。

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

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

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

立即咨询