简介:这份资源面向深度学习入门者与计算机视觉方向的学生和研究者,围绕VggNet与ResNet两种经典卷积神经网络在物体分类识别中的应用展开,提供可运行的Matlab代码实现,帮助读者从理论理解过渡到动手实践。压缩包共包含60012个文件,以60002个png图像数据为主,另有4个m脚本、3个mat权重文件、1个pdf说明文档及fig、jpg等辅助文件,整体约398.58MB,规模较大,适合完整复现训练与测试流程。目前已有759人学习下载。资源重点比较了VggNet逐层加深结构与ResNet残差连接的设计差异,涵盖网络搭建、预训练权重加载、前向与反向传播、参数初始化、正则化与优化器选择等训练策略,并给出物体分类准确率的实验对比。读者可据此掌握两种网络的Matlab实现方法、代码解读思路与迁移学习技巧,为后续目标检测、语义分割等任务打下基础。
1. VggNet 与 ResNet 做物体分类:为什么这两个网络值得放在一起对比
如果你手头有一批物体图片,想用 Matlab 跑一个能落地的分类模型,大概率会卡在同一个问题上:选 VggNet 还是 ResNet?这不是学术层面的“谁更强”,而是工程层面的“谁更适合你现在的数据量、显存和交付时间”。VggNet 结构规整,16 到 19 层全是 3×3 卷积堆叠,特征提取路径清晰,改起来不容易翻车;ResNet 靠残差连接把梯度直接短路传下去,能堆到 50 层甚至 152 层还不退化,预训练模型在 ImageNet 上的迁移效果通常更稳。把这两个网络放在同一套物体分类识别流程里跑一遍,你能直观看到:同样一批图,VggNet 收敛慢但可解释性强,ResNet 收敛快但对输入尺寸和归一化更敏感。这篇笔记就按“数据准备 → 网络加载 → 训练调参 → 避坑排查 → 进阶技巧”的顺序,把 Matlab 代码和参数含义拆开讲,适合正在做图像处理大作业、或者要把分类模型塞进实际检测流程的工程师。
2. 数据准备与两种网络的输入规范:从图片文件夹到可训练的数据存储
2.1 物体分类数据集怎么组织才不报错
Matlab 的imageDatastore对文件夹结构有硬性要求:每个类别一个子文件夹,文件夹名就是标签名。常见做法是建一个根目录,下面按类别分文件夹,比如dataset/apple/、dataset/banana/。图片格式支持 jpg、png、bmp,但不要混入 gif 或带透明通道的 png,否则imread读出来是四通道,后面augmentedImageDatastore会直接报维度不匹配。
% 假设数据集根目录为 dataset,下面有 apple、banana、orange 三个子文件夹 imds = imageDatastore('dataset', ... 'IncludeSubfolders', true, ... 'LabelSource', 'foldernames'); % 查看类别分布,防止某类样本过少导致训练偏斜 labelCount = countEachLabel(imds); disp(labelCount); % 按 7:3 划分训练集和验证集,保证每类比例一致 [imdsTrain, imdsVal] = splitEachLabel(imds, 0.7, 'randomized');这段代码的逻辑是:imageDatastore自动遍历子文件夹并打标签,countEachLabel用来检查类别是否均衡。如果某类只有十几张图,后面要么做数据增强,要么直接合并到相似类。splitEachLabel的第三个参数'randomized'很关键,不加的话默认按文件名顺序切,容易把某一类全切进训练集。
2.2 VggNet 与 ResNet 的输入尺寸差异及预处理
VggNet 在 Matlab 里通常加载vgg16或vgg19,输入层要求 224×224×3。ResNet 的resnet18、resnet50同样接受 224×224,但resnet50对输入归一化更敏感,它用的是 ImageNet 的均值减法,不是简单的 0-1 归一化。Matlab 的augmentedImageDatastore可以统一做尺寸缩放,但归一化方式要跟网络匹配。
% 定义目标尺寸 inputSize = [224 224 3]; % 数据增强:随机翻转、随机缩放、随机旋转 augmenter = imageDataAugmenter( ... 'RandXReflection', true, ... 'RandYReflection', false, ... 'RandScale', [0.9 1.1], ... 'RandRotation', [-15 15]); % 构建增强后的数据存储 augimdsTrain = augmentedImageDatastore(inputSize, imdsTrain, ... 'DataAugmentation', augmenter); augimdsVal = augmentedImageDatastore(inputSize, imdsVal);参数说明:RandXReflection对物体分类通常安全,因为左右翻转不改变类别;RandYReflection要慎用,上下翻转可能把“杯子”变成“倒扣的杯子”,语义变了。RandScale控制在 0.9 到 1.1 之间,模拟拍摄距离变化。RandRotation给 ±15 度,再大就可能把物体转出画面。注意augmentedImageDatastore只做几何变换,不做像素归一化,ResNet 需要的均值减法要在网络内部或训练选项里处理。
提示:如果显存不足,把
inputSize降到 [128 128 3] 能跑,但 VggNet 的全连接层参数量不变,显存瓶颈在最后三层,降输入尺寸救不了多少。
3. 加载预训练模型与迁移学习:VggNet 和 ResNet 的 Matlab 实现
3.1 用vgg16和resnet50替换最后三层
Matlab 的 Deep Learning Toolbox 支持直接加载预训练模型,前提是你已经安装了对应的支持包。加载后不要从头训练,而是把最后三层(全连接、softmax、分类输出)替换成自己的类别数。VggNet 和 ResNet 的层名不同,替换方式也有差异。
% 加载预训练网络 netVgg = vgg16; netRes = resnet50; % 查看最后几层,确认层名 layersVgg = netVgg.Layers; layersRes = netRes.Layers; disp(layersVgg(end-4:end)); disp(layersRes(end-4:end)); % 获取类别数 numClasses = numel(categories(imdsTrain.Labels)); % 替换 VggNet 最后三层 lgraphVgg = layerGraph(netVgg); newLayersVgg = [ fullyConnectedLayer(numClasses, 'Name', 'new_fc', ... 'WeightLearnRateFactor', 10, 'BiasLearnRateFactor', 10) softmaxLayer('Name', 'new_softmax') classificationLayer('Name', 'new_classoutput')]; lgraphVgg = replaceLayer(lgraphVgg, 'fc8', newLayersVgg(1)); lgraphVgg = replaceLayer(lgraphVgg, 'prob', newLayersVgg(2)); lgraphVgg = replaceLayer(lgraphVgg, 'output', newLayersVgg(3)); % 替换 ResNet50 最后三层 lgraphRes = layerGraph(netRes); newLayersRes = [ fullyConnectedLayer(numClasses, 'Name', 'new_fc', ... 'WeightLearnRateFactor', 10, 'BiasLearnRateFactor', 10) softmaxLayer('Name', 'new_softmax') classificationLayer('Name', 'new_classoutput')]; lgraphRes = replaceLayer(lgraphRes, 'fc1000', newLayersRes(1)); lgraphRes = replaceLayer(lgraphRes, 'prob', newLayersRes(2)); lgraphRes = replaceLayer(lgraphRes, 'ClassificationLayer_predictions', newLayersRes(3));逻辑说明:replaceLayer是按层名替换,VggNet 的全连接层叫fc8,ResNet50 的叫fc1000。WeightLearnRateFactor设为 10 是让新加的全连接层学得快一点,因为它是随机初始化的,而前面的卷积层已经预训练好了,学习率要低。这一步如果层名写错,Matlab 会报 “Layer not found”,所以先用disp把最后几层名字打出来确认。
3.2 冻结早期层与微调策略
物体分类数据集通常只有几千张图,直接微调所有层容易过拟合。常见做法是冻结前几个卷积块,只训练后面的层。VggNet 的卷积层按conv1_1到conv5_4命名,ResNet50 按conv1、bn_conv1、res2a_branch1等命名。冻结的方式是把对应层的WeightLearnRateFactor和BiasLearnRateFactor设为 0。
% 冻结 VggNet 前 10 层(对应 conv1 到 conv3 的部分) layersVgg = lgraphVgg.Layers; for i = 1:10 if isprop(layersVgg(i), 'WeightLearnRateFactor') layersVgg(i).WeightLearnRateFactor = 0; layersVgg(i).BiasLearnRateFactor = 0; end end lgraphVgg = layerGraph(layersVgg); % 冻结 ResNet50 的前 20 层 layersRes = lgraphRes.Layers; for i = 1:20 if isprop(layersRes(i), 'WeightLearnRateFactor') layersRes(i).WeightLearnRateFactor = 0; layersRes(i).BiasLearnRateFactor = 0; end end lgraphRes = layerGraph(layersRes);参数说明:冻结层数没有固定标准,一般看数据集大小。5000 张图以下,VggNet 冻到conv4之前,ResNet50 冻到res3之前。如果验证集准确率一直上不去,再解冻一两层试试。注意 ResNet 的 Batch Normalization 层在冻结时也要把WeightLearnRateFactor设 0,否则均值和方差还在更新,等于没冻住。
4. 训练参数设置与两种网络的收敛对比
4.1trainingOptions里必须调的五个参数
Matlab 的trainingOptions参数很多,但物体分类迁移学习真正影响结果的只有五个:InitialLearnRate、MiniBatchSize、MaxEpochs、ValidationFrequency、ExecutionEnvironment。VggNet 和 ResNet 对这五个参数的敏感度不同。
% VggNet 训练选项 optionsVgg = trainingOptions('sgdm', ... 'InitialLearnRate', 1e-4, ... 'MiniBatchSize', 16, ... 'MaxEpochs', 20, ... 'Shuffle', 'every-epoch', ... 'ValidationData', augimdsVal, ... 'ValidationFrequency', 30, ... 'Verbose', false, ... 'Plots', 'training-progress', ... 'ExecutionEnvironment', 'auto'); % ResNet50 训练选项 optionsRes = trainingOptions('sgdm', ... 'InitialLearnRate', 1e-4, ... 'MiniBatchSize', 16, ... 'MaxEpochs', 20, ... 'Shuffle', 'every-epoch', ... 'ValidationData', augimdsVal, ... 'ValidationFrequency', 30, ... 'Verbose', false, ... 'Plots', 'training-progress', ... 'ExecutionEnvironment', 'auto');参数说明:InitialLearnRate用 1e-4 是迁移学习的常用起点,VggNet 可以稍高到 5e-4,ResNet 建议不超过 1e-4,否则残差分支容易震荡。MiniBatchSize设 16 是显存和梯度稳定性的折中,显存够就上 32。MaxEpochs设 20 是因为预训练模型收敛快,超过 30 轮基本过拟合。ValidationFrequency设 30 表示每 30 个迭代验证一次,太小会拖慢训练速度。
4.2 训练过程监控:看损失曲线判断是否翻车
training-progress窗口里重点看两条线:训练损失和验证准确率。VggNet 的损失曲线通常前 5 轮下降明显,之后变缓;ResNet 前 2 轮就能降到很低,但验证准确率可能波动大。如果训练损失一直不降,检查学习率是不是太小;如果验证准确率远低于训练准确率,说明过拟合,要加数据增强或冻结更多层。
% 训练 VggNet netVggTrained = trainNetwork(augimdsTrain, lgraphVgg, optionsVgg); % 训练 ResNet50 netResTrained = trainNetwork(augimdsTrain, lgraphRes, optionsRes); % 在验证集上评估 predVgg = classify(netVggTrained, augimdsVal); predRes = classify(netResTrained, augimdsVal); accVgg = mean(predVgg == imdsVal.Labels); accRes = mean(predRes == imdsVal.Labels); fprintf('VggNet 验证准确率: %.2f%%\n', accVgg * 100); fprintf('ResNet50 验证准确率: %.2f%%\n', accRes * 100);这段代码把两个网络跑完并对比验证准确率。注意classify的输出是 categorical 类型,直接和imdsVal.Labels比较即可。如果 VggNet 准确率比 ResNet 低 5 个点以上,不用惊讶,这是常态;如果低 20 个点,检查 ResNet 的输入归一化是不是没做对。
5. 避坑与排查:物体分类训练中最容易翻车的五个地方
5.1 现象:训练一开始就报 “Out of memory”
原因:MiniBatchSize设太大,或者inputSize没降下来。VggNet 的全连接层fc6有 4096 个神经元,参数量约 1 亿,显存占用比 ResNet50 还高。解决:把MiniBatchSize降到 8,或者用resnet18替代resnet50。如果还是不够,把inputSize改成 [128 128 3],但 VggNet 的全连接层不会因此变小,所以优先换网络。
5.2 现象:验证准确率一直停在 50% 左右,像随机猜
原因:标签没对上。imageDatastore的LabelSource设成'foldernames'后,标签是文件夹名,但splitEachLabel之后imdsTrain.Labels和imdsVal.Labels的类别顺序可能不一致。解决:在训练前用categories(imdsTrain.Labels)和categories(imdsVal.Labels)对比一下,确保完全一样。如果不一样,用subset重新切。
5.3 现象:ResNet 训练损失出现 NaN
原因:学习率太大,残差连接把梯度放大后导致数值溢出。解决:把InitialLearnRate从 1e-3 降到 1e-4,或者加'GradientThreshold', 1做梯度裁剪。Matlab 的trainingOptions支持GradientThreshold参数,设 1 到 5 之间比较安全。
5.4 现象:VggNet 训练很慢,一个 epoch 要十几分钟
原因:ExecutionEnvironment设成了'cpu',或者没装 GPU 支持。解决:用gpuDevice检查 GPU 是否可用,然后在trainingOptions里显式设'ExecutionEnvironment', 'gpu'。如果 GPU 显存小于 6GB,VggNet 基本跑不动,换 ResNet18 或降MiniBatchSize。
5.5 现象:验证集准确率比训练集高
原因:数据增强只加在训练集上,验证集是原始图,模型在验证集上反而“看到”了更清晰的样本。解决:这不是 bug,是正常现象,但如果差距超过 10 个点,说明训练集增强太狠,把RandRotation范围缩小到 ±10 度,或者关掉RandScale。
6. 进阶技巧:用混淆矩阵和特征可视化判断模型到底学到了什么
训练完两个网络后,别只看准确率。用混淆矩阵看哪些类别容易混,用activations提取中间层特征做 t-SNE 可视化,能判断模型是学到了物体形状还是背景颜色。Matlab 的confusionchart和tsne函数可以直接用。
% 混淆矩阵 figure; cmVgg = confusionchart(imdsVal.Labels, predVgg); title('VggNet 混淆矩阵'); figure; cmRes = confusionchart(imdsVal.Labels, predRes); title('ResNet50 混淆矩阵'); % 提取 ResNet50 的全局平均池化层特征 layerName = 'avg_pool'; features = activations(netResTrained, augimdsVal, layerName, ... 'OutputAs', 'rows'); % t-SNE 降维可视化 figure; tsneFeatures = tsne(features); gscatter(tsneFeatures(:,1), tsneFeatures(:,2), imdsVal.Labels); title('ResNet50 特征 t-SNE 可视化');逻辑说明:confusionchart的输入是真实标签和预测标签,对角线越深越好。如果某两类互相混,说明它们的视觉特征太接近,要么加更多样本,要么在分类层前加一个注意力模块。activations提取的是avg_pool层的输出,ResNet50 在这一层已经是 2048 维向量,直接做 t-SNE 计算量大,可以先pca降到 50 维再tsne。gscatter按类别着色,如果同一类聚成一团,说明模型学到了区分性特征;如果混在一起,说明特征空间没分开。
注意:
tsne对大数据集很慢,验证集超过 2000 张图时先随机采样 500 张再跑。
我自己的习惯是:每次训完一个分类模型,先看混淆矩阵,再看 t-SNE,最后才看准确率。准确率会骗人,但混淆矩阵和特征分布不会。VggNet 和 ResNet 没有绝对的好坏,只有跟你的数据、显存和交付时间匹配不匹配。如果数据集小、可解释性要求高,VggNet 更稳;如果追求精度、能接受调参成本,ResNet 上限更高。希望帮到你。
本文还有配套的精品资源,点击获取