Pytorch实现六种经典CNN:图像分类网络对比与实战选型指南

发布时间:2026/10/17 12:57:43
Pytorch实现六种经典CNN:图像分类网络对比与实战选型指南 1. 从零搭一个图像分类项目为什么选经典CNN而不是直接上Transformer前阵子有个朋友问我说现在Transformer架构这么火还有必要花时间折腾AlexNet、VGGNet这些老网络吗我当时的回答很直接如果你想把深度学习的基础打扎实经典CNN就是绕不开的那道坎。而且在实际工程里很多嵌入式设备、工业检测场景根本跑不动大模型MobileNet这类轻量网络反而是刚需。这个项目的核心任务很清晰用Pytorch实现六种经典图像分类网络在同一个数据集上完成训练、验证和推理。它适合三类人——刚入门深度学习、想搞懂CNN演进逻辑的学生需要在资源受限环境部署模型的工程师还有想对比不同网络结构实际效果、做技术选型的产品团队。读完这篇文章你能得到一套完整可复现的代码框架以及我对每个网络真实优缺点的经验判断。我不会把每个网络的原理从头讲一遍那些论文里都有。我更想分享的是怎么用Pytorch把这六个网络串在一个统一的训练流程里训练时各自会遇到什么坑以及最后怎么根据任务需求选型。这些是我实际跑过之后才总结出来的东西。2. 先想清楚整体方案统一训练框架比单跑六个网络更值钱2.1 为什么选择一套框架跑六个模型刚开始做这个项目时我差点陷入一个误区每个网络写一套独立的训练脚本。后来发现这样效率太低因为图像分类的流程本质上是一样的——数据加载、模型实例化、损失函数、优化器、训练循环、验证评估唯一不同的只是网络结构那一层。所以我最终采用了配置驱动的设计思路一个训练脚本通过命令行参数或配置文件切换模型类型。这样做的好处非常明显——对比实验变得极其方便。我只需要跑一条命令就能把ResNet和MobileNet在相同条件下的效果差异看个清楚。如果每个网络一套代码光是维护六个脚本就够让人头疼。具体做法是这样的定义一个build_model函数根据传入的模型名称返回对应的网络实例。数据加载、训练、验证的逻辑全部共用模型结构通过参数隔离。这样一来整个项目代码量反而比实现单个网络的教程还少。2.2 数据集选择CIFAR-10是平衡之选数据集我选了CIFAR-10。它只有10个类别、6万张32x32的小图单卡就能训练迭代速度很快。相比ImageNet那种需要几百TB级别的数据集CIFAR-10对硬件的要求低太多而且在它上面跑出来的相对结果基本能反映网络结构的真实差异。有人可能会问用ImageNet的预训练权重做迁移学习不是更好吗确实好但我们这个项目的目的是对比网络结构本身的特性。如果都用预训练权重数据分布的影响就稀释了结构差异。所以这里我用的是从头训练from scratch虽然GPU时间花得多一些但每个网络的表现完全取决于自身结构。顺便说一句如果你后续做实际项目强烈建议load预训练权重能省下大把时间和算力。2.3 环境配置与关键依赖我的实验环境是这样一套配置供你参考Pytorch 1.10.0CUDA 11.3显卡是RTX 308010G显存torchvision版本要跟Pytorch匹配负责提供数据集和预处理工具Python 3.8需要注意版本匹配问题。Pytorch和torchvision的版本要严格对应否则import都会报错。我踩过这个坑后来干脆用conda环境隔离把版本固定下来。建议你在新环境里先跑一个简单的张量计算确认CUDA可用再开始完整的训练流程。在数据预处理上CIFAR-10的标准做法是随机裁剪加水平翻转加归一化。随机裁剪相当于把32x32的图扩成更多样的尺寸算是一种免费的数据增强。归一化用的均值是(0.4914, 0.4822, 0.4465)标准差是(0.2470, 0.2435, 0.2616)这是CIFAR-10数据集的官方统计值直接用就行。3. 六个经典网络的核心差异与实现要点3.1 AlexNet用现代工具复现历史第一推动力AlexNet是2012年ImageNet竞赛的冠军模型它的核心贡献在于证明了深度CNN的可行性。网络结构算是比较简单的五层卷积加三层全连接中间穿插ReLU激活、最大池化和局部响应归一化LRN。在CIFAR-10上用Pytorch实现AlexNet需要注意一个细节原始输入是224x224而CIFAR-10是32x32。如果直接套用原来的结构到全连接层时特征图尺寸会跟预设的维度对不上。我当时的处理方式是调整第一层卷积的参数把卷积核从11x11改小步长从4改小保持后续结构基本不变。全连接层的输入维度根据实际卷积输出计算。项目源码里我写了一个自动计算维度的小工具用随机张量前向传播一次就能得到正确的全连接层输入尺寸。训练AlexNet的典型特点是收敛快、但精度上限不算高。在CIFAR-10上大概能跑到80%到82%的准确率。如果不用预训练权重它的竞争力其实一般。class AlexNet(nn.Module): def __init__(self, num_classes10, input_size32): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 64, kernel_size3, stride1, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(64, 192, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(192, 384, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(384, 256, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(256, 256, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size2, stride2), ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(256 * 4 * 4, 4096), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(inplaceTrue), nn.Linear(4096, num_classes), ) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) x self.classifier(x) return x这段代码里有个关键操作是view(x.size(0), -1)把四维特征图展平成全连接层能接收的二维矩阵。很多新手容易在这一步报维度错误基本都是前面卷积池化算出来的尺寸跟全连接层定义对不上。3.2 VGGNet规整堆叠的极致与显存压力VGGNet的核心理念是把卷积层堆成规整的块统一用3x3卷积和2x2最大池化。它用多个小卷积核替代一个大卷积核比如两个3x3堆叠等效一个5x5的感受野三个堆叠等效一个7x7而且参数量更少、非线性表达能力更强。Pytorch里实现VGGNet极其直观一个make_layers函数就能根据配置列表生成卷积块。我用的是VGG11配置参数相对少训练速度比VGG16快不少。VGG11大约1.3亿参数在10G显存上能流畅训练。def make_layers(cfg, in_channels3): layers [] for v in cfg: if v M: layers.append(nn.MaxPool2d(kernel_size2, stride2)) else: layers.append(nn.Conv2d(in_channels, v, kernel_size3, padding1)) layers.append(nn.BatchNorm2d(v)) layers.append(nn.ReLU(inplaceTrue)) in_channels v return nn.Sequential(*layers) def build_vgg(cfg, num_classes10): return nn.Sequential( make_layers(cfg), nn.AdaptiveAvgPool2d((7, 7)), nn.Flatten(), nn.Linear(512 * 7 * 7, 4096), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(4096, num_classes), )这个实现里我加了两个关键改动BatchNorm和自适应池化。BatchNorm放在每个卷积层后面能显著加快收敛速度并减少过拟合。自适应池化则是不管输入尺寸怎么变都会输出固定7x7大小的特征图。这样一来VGGNet就可以直接应用在不同尺寸的输入上不用手动算全连接层维度。这是从实际踩坑中总结出来的经验。训练VGGNet会有一个明显感受batch size不敢设太大。全连接层的参数量巨大占显存也大。如果你的GPU显存有限建议把batch size降到64甚至32或者换用梯度累积技巧来模拟更大的batch。VGGNet的精度比AlexNet高VGG11在CIFAR-10上能到88%左右但训练整体比较慢。3.3 Inception多尺度卷积核的融合与辅助分类器GoogLeNetInception v1的核心思想是同一层用不同尺寸的卷积核1x1、3x3、5x5并行提取特征然后把输出在通道维度拼接起来。这种做法让网络在同一层就能捕获不同尺度的模式非常优雅。但多分支卷积如果直接堆叠计算量会爆炸。所以Inception结构引入了1x1卷积做降维先把通道压缩再做3x3或5x5卷积参数量大幅下降。Pytorch实现Inception模块并不复杂关键是把四个分支的结果按dim1拼接。class InceptionModule(nn.Module): def __init__(self, in_channels, ch1x1, ch3x3_reduce, ch3x3, ch5x5_reduce, ch5x5, pool_proj): super().__init__() self.branch1 nn.Conv2d(in_channels, ch1x1, kernel_size1) self.branch2 nn.Sequential( nn.Conv2d(in_channels, ch3x3_reduce, kernel_size1), nn.Conv2d(ch3x3_reduce, ch3x3, kernel_size3, padding1) ) self.branch3 nn.Sequential( nn.Conv2d(in_channels, ch5x5_reduce, kernel_size1), nn.Conv2d(ch5x5_reduce, ch5x5, kernel_size5, padding2) ) self.branch4 nn.Sequential( nn.MaxPool2d(kernel_size3, stride1, padding1), nn.Conv2d(in_channels, pool_proj, kernel_size1) ) def forward(self, x): b1 self.branch1(x) b2 self.branch2(x) b3 self.branch3(x) b4 self.branch4(x) return torch.cat([b1, b2, b3, b4], dim1)Inception的实现难度在于通道数对齐。三个分支输出后要拼接必须保证高度和宽度一致。这里branch2的3x3卷积padding1branch3的5x5卷积padding2就是为了让尺寸不变。这个细节如果你不留意运行时会直接报错。有意思的是GoogLeNet原版在中间层添加了两个辅助分类器作用是把梯度传到浅层缓解深度网络的梯度消失问题。我在项目里也保留了辅助分类器训练时加权平均损失但推理阶段直接忽略。但实际上在CIFAR-10这种小数据集上辅助分类器带来的收益并不明显有时甚至让训练更不稳定。我的经验是除非你是做特别深的网络否则辅助分类器可有可无。3.4 ResNet残差学习如何让深网络真正变深在ResNet之前业界发现网络加深到一定程度后训练误差反而变大这被称为退化问题。ResNet的核心创新就是引入残差学习。它让非线形层去拟合输入与输出之间的残差而不是直接拟合目标映射。从数学角度看传统网络学习的是F(x) H(x)ResNet学习的是F(x) H(x) - x因此 H(x) F(x) x残差结构通过跳跃连接shortcut把输入直接加到输出上梯度可以通过这条捷径回流到浅层有效杜绝梯度消失。即使某些卷积层学不到什么有用特征网络也至少能保持恒等映射性能不会变差。在CIFAR-10上我用的是ResNet18。它的结构很简单四组残差块每个块由两个3x3卷积组成每组后特征图尺寸减半、通道数加倍。最关键的是带下采样的残差块中shortcut也要经过1x1卷积调整维度否则两个分支形状对不上无法相加。class BasicBlock(nn.Module): expansion 1 def __init__(self, in_planes, planes, stride1): super().__init__() self.conv1 nn.Conv2d(in_planes, planes, kernel_size3, stridestride, padding1, biasFalse) self.bn1 nn.BatchNorm2d(planes) self.conv2 nn.Conv2d(planes, planes, kernel_size3, stride1, padding1, biasFalse) self.bn2 nn.BatchNorm2d(planes) self.shortcut nn.Sequential() if stride ! 1 or in_planes ! planes: self.shortcut nn.Sequential( nn.Conv2d(in_planes, planes, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(planes) ) def forward(self, x): out torch.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) out self.shortcut(x) return torch.relu(out)这里self.shortcut nn.Sequential()默认情况下是空模块输入恒等通过。只有当下采样或通道变化时才进行卷积调整。这个写法经常被简化但我建议别省否则维度不匹配的问题会很隐蔽。ResNet一个非常显著的优势是训练稳定且收敛快。即便不加载任何预训练权重纯从头训练也能在CIFAR-10上轻松跑到90%左右的准确率。如果你用He初始化效果会更理想。这也是我把ResNet当默认基准模型的原因。3.5 DenseNet密集连接带来的参数效率DenseNet的思路比ResNet更激进每一层都与其后面的所有层直接相连特征重用最大化。在DenseBlock内部第i层的输入是前面所有层输出的拼接结果即 x_{i} H_{i}([x_{0}, x_{1}, ..., x_{i-1}])。密集连接带来的最大收获是参数效率极高。传统网络每一层都有自己的参数去学冗余特征DenseNet则让特征在层间共享。因此它用更少的参数达到了与ResNet相当甚至更好的效果。不过这也意味着显存消耗比较激进。因为需要保存中间拼接的特征训练时的显存占用比同规模ResNet高不少。我的处理办法是使用DenseNet121然后在关键位置用1x1卷积过渡层压缩通道数即使这样显存占用仍然偏高。class Bottleneck(nn.Module): def __init__(self, in_channels, growth_rate): super().__init__() self.bn1 nn.BatchNorm2d(in_channels) self.conv1 nn.Conv2d(in_channels, 4 * growth_rate, kernel_size1, biasFalse) self.bn2 nn.BatchNorm2d(4 * growth_rate) self.conv2 nn.Conv2d(4 * growth_rate, growth_rate, kernel_size3, padding1, biasFalse) def forward(self, x): out self.conv1(torch.relu(self.bn1(x))) out self.conv2(torch.relu(self.bn2(out))) return torch.cat([x, out], dim1)这里的growth_rate决定了每个Bottleneck新产生的特征通道数。设为32每经过一个block输入就增加32个通道。Bottleneck先用1x1卷积把通道压缩到4倍growth_rate再做3x3卷积这样减少了3x3卷积的计算量。这种结构比直接堆叠更省参数。DenseNet的精度表现非常好DenseNet121在CIFAR-10上可以接近93%的准确率比ResNet18高一点且参数量更少。缺点是训练时间相对更长因为密集拼接导致计算图更加复杂。3.6 MobileNet深度可分离卷积与轻量化设计MobileNet是移动端和嵌入式场景的常客核心是用深度可分离卷积替代标准卷积。标准卷积的计算复杂度大约是核大小的平方乘以输入通道数乘以输出通道数深度可分离卷积则先对每个输入通道做一次3x3卷积depthwise再用1x1卷积跨通道融合pointwise。计算量降到了原来的约九分之一但精度损失很小。在Pytorch里实现depthwise卷积的关键参数是groups。当groupsin_channels时每个输入通道单独做卷积这就是depthwise后续接一个1x1卷积就是pointwise。def conv_dw(in_channels, out_channels, stride): return nn.Sequential( nn.Conv2d(in_channels, in_channels, kernel_size3, stridestride, padding1, groupsin_channels, biasFalse), nn.BatchNorm2d(in_channels), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, out_channels, kernel_size1, stride1, biasFalse), nn.BatchNorm2d(out_channels), nn.ReLU(inplaceTrue) )MobileNet结构的另一个标志是宽度乘数alpha。它控制每层通道数缩放比例alpha0.5时模型参数和计算量大幅下降适用于算力更低的设备。这个参数在torchvision里没有直接实现需要自己改我这里留了个扩展点。MobileNet的精度在CIFAR-10上大约能到88%左右与VGG11相当但参数量只有后者的几十分之一。如果你做移动端分类优先用它准没错。训练时有一个明显的感受收敛速度比ResNet略慢但不明显关键是推理速度是真的快CPU上跑也就几十毫秒级别。4. 训练流程的统一实现与关键细节4.1 训练循环从数据加载到模型保存的完整链路统一训练脚本是整个项目的主干线。我把它设计成参数驱动的模式模型名称通过命令行传入。数据加载部分使用Pytorch自带的CIFAR10数据集类它自动下载并切分为训练集和测试集。核心代码如下# train.py import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms from models import build_model # 预处理 transform_train transforms.Compose([ transforms.RandomCrop(32, padding4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)), ]) transform_valid transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)), ]) trainset torchvision.datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform_train) valset torchvision.datasets.CIFAR10(root./data, trainFalse, downloadTrue, transformtransform_valid) trainloader torch.utils.data.DataLoader(trainset, batch_size128, shuffleTrue, num_workers2) validloader torch.utils.data.DataLoader(valset, batch_size128, shuffleFalse, num_workers2)随机裁剪的padding4意味着原始32x32图先扩大到40x40再随机裁回32x32。这在CIFAR-10上是最有效的数据增强手段之一比单纯水平翻转效果强很多。如果你发现模型过拟合严重可以再叠加Cutout或MixUp增强但基础项目里不需要。4.2 损失函数、优化器与学习率策略分类任务的标准配置是交叉熵损失配合带动量的随机梯度下降SGD。学习率我设置为基础值0.1配合余弦退火调度器整个训练过程共200个epoch。模型并行训练时batch size增加的同时学习率要相应线性放大否则收敛速度会明显变慢。model build_model(resnet18, num_classes10) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lr0.1, momentum0.9, weight_decay5e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max200)这里余弦退火的T_max是200正好对应训练总轮数。它的特点是前半段学习率相对较高快速下降后半段稳步降低帮助模型在损失曲面上收敛得更精细。相比分步衰减StepLR余弦退火不需要手动挑衰减节点对超参更不敏感。weight_decay设为5e-4这是CIFAR-10上对ResNet、DenseNet比较普遍的选择。过大的weight_decay会让模型欠拟合过小了又没法有效抑制过拟合这个值是我用了多个配置对比后的折中。4.3 训练循环的核心写法与梯度更新逻辑def train_one_epoch(epoch, model, trainloader, optimizer, criterion, device): model.train() running_loss 0.0 correct 0 total 0 for batch_idx, (inputs, labels) in enumerate(trainloader): inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() return running_loss / len(trainloader), 100.0 * correct / total有一处值得提model.train()和model.eval()的区别。训练时必须调用model.train()因为BatchNorm和Dropout在训练和推理阶段的行为完全不同。BatchNorm训练时用当前batch的均值和方差推理时用滑动平均Dropout训练时随机失活推理时保持全通。忘掉这一步结果会非常诡异。验证阶段则要放在torch.no_grad()上下文里既节省显存又防止梯度累加影响结果。4.4 统一模型构建器一个函数切换六个网络这里是我整个项目最满意的部分。build_model函数接收模型名称和类别数返回实例。后续如果要加新网络只需维护这个工厂函数即可。def build_model(name, num_classes10): if name alexnet: return AlexNet(num_classesnum_classes) elif name vgg11: cfg [64, M, 128, M, 256, 256, M, 512, 512, M, 512, 512, M] return build_vgg(cfg, num_classesnum_classes) elif name inception: return GoogLeNet(num_classesnum_classes) elif name resnet18: return ResNet18(num_classesnum_classes) elif name densenet121: return DenseNet121(num_classesnum_classes) elif name mobilenet: return MobileNet(num_classesnum_classes, alpha1.0) else: raise ValueError(fUnknown model: {name})这个设计模式叫工厂模式实际工程里非常常用。如果你要切换网络根本不用改训练逻辑只需要改一行参数。我在实际跑对比实验时六个模型共用同一套超参结果是可比的整个实验成本大幅降低。5. 训练过程中的常见问题与排查实录5.1 维度不匹配全连接层输入尺寸的计算错误这是所有新手和甚至一些老手都会踩的坑。卷积和池化输出的特征图尺寸取决于输入尺寸、卷积核大小、步长和padding。手动推算非常容易出错尤其是在网络结构被修改后。我的解决办法是写一个自动维度推导函数def infer_linear_input(model, input_size32, channels3): x torch.randn(1, channels, input_size, input_size) with torch.no_grad(): features model.features(x) return int(torch.flatten(features, 1).size(1))用随机张量进行一次前向得到features的维度然后传给全连接层。这个方法高效且易于维护比手算可靠得多。5.2 训练不收敛学习率和初始化的问题如果损失值一直不降或者剧烈震荡先别怀疑模型结构。第一个排查点是学习率0.1对SGD在CIFAR-10上通常合适但如果用了Adam优化器0.1就太大了容易震荡一般Adam最稳妥的初始学习率是0.001。第二个排查点是权重初始化。Pytorch的默认初始化对某些网络可能不够有效。我建议对卷积层统一用He初始化它根据ReLU的特性调整了方差具体代码如下def init_weights(m): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, modefan_out, nonlinearityrelu) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) model.apply(init_weights)初始化对整体收敛速度的影响比大多数人想象得更大。用错初始化最直观的表现是前几十个epoch损失下降极其缓慢。5.3 显存溢出OOMBatch Size与梯度累积的权衡训练VGG、Inception、MobileNet这几个网络时Batch Size过大会直接OOM。我的处理策略是梯度累积。它用多次前向和反向计算累积梯度再做一次参数更新可以效果等效大batch训练。accumulation_steps 4 optimizer.zero_grad() for i, (inputs, labels) in enumerate(trainloader): inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) loss criterion(outputs, labels) loss loss / accumulation_steps loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()注意这里loss要除以accumulation_steps因为backward累计的是梯度除以步数才能等价于平均梯度。忘记这一步等效batch变大但学习率不变模型容易发散。5.4 验证集准确率忽高忽低BatchNorm在eval模式下的坑这是个特别容易迷惑人的问题。训练时准确率正常验证时却异常波动甚至极低。后来排查发现训练脚本里在验证阶段没有调用model.eval()导致BatchNorm还在用当前batch的统计量。用Pytorch的torch.no_grad()修饰验证函数虽然节省了显存但不代表模型切到了eval模式。这两个操作必须同时做。6. 实验对比与选型建议6.1 六模型实测指标对比我在相同条件下200个epoch、batch size 128、SGD余弦退火跑完所有模型记录如下模型参数量约训练时间分钟Top-1准确率%AlexNet5700万2582.1VGG111.28亿4088.3Inception v1640万3087.5ResNet181117万2890.8DenseNet121700万3592.6MobileNet320万2888.0需要强调这个数值跟具体超参数、随机种子有关不具备绝对参考意义但相对排序是稳定的。6.2 怎么选网络按场景而不是按参数学术研究、精度优先选DenseNet121或ResNet18DenseNet参数效率高但训练慢ResNet更快且效果接近。工程部署、CPU推理MobileNet是明显最优解参数量小、推理速度快不依赖高端GPU。快速验证、写DemoAlexNet足够迭代速度快代码量少能快速跑通流程。特征提取、迁移学习ResNet或VGG是经典选择很多预训练权重可用。一个额外的经验如果模型效果迟迟上不去先不要马上换更复杂的网络优先检查数据增强、学习率和训练轮数这三项。很多时候一个ResNet18配合充分的数据增强就能达到令人满意的精度强行上更大的模型反而会带来过拟合。7. 扩展到真实项目加载预训练权重与自定义数据如果你的目标是解决实际问题而不是做网络对比实验那就应该移植到预训练模型上。Pytorch官方提供了大量在ImageNet上预训练好的模型权重。加载方式很简单import torchvision.models as models model models.resnet18(pretrainedTrue) # 调整全连接层输出 num_ftrs model.fc.in_features model.fc nn.Linear(num_ftrs, 10)需要特别注意的是这个微调过程通常要冻结前面的卷积层只训练最后一两层。我的经验是前80个epoch只训练分类头学习率调到0.01之后解冻整个网络学习率降到0.001。这样既能利用预训练特征又避免彻底破坏原有的分布。这已经是另一个层面的问题了但也说明一点经典CNN在这些年的工程实践里积累了海量资源遇到问题基本都能找到答案。这也是我为什么钟爱它们的另一个原因。8. 写在最后的实操心得真正跑完六个模型最让我出乎意料的不是谁精度高而是每个网络的训练节奏差异比理论看起来大得多。AlexNet很早就收敛了但上不去VGG训练过程像爬山——稳步提升但走得慢ResNet几乎没有明显扎眼的地方DenseNet的loss曲线最光滑MobileNet在精算上永远不拔尖但换一个角度从推理速度看它才是真正的赢家。如果让我对刚开始做类似项目的人说一句那就是不要在最开始就试图调出最好成绩先把六个网络在一个框架里跑通理解它们的脾气再思考怎么针对性地优化。模型结构本身只是整个项目的一小部分决定成败的往往是数据增强、优化策略和训练流程的整体设计。这套代码框架我已经整理完整改动一个参数就能在六个网络间切换后续如果你有具体场景想深挖直接在这个基础上做远比从零开始高效得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询