【深度学习基础篇06】从原理到代码:AlexNet/VGGNet/ResNet 逐行拆解与实现
上一篇文章中,我们系统梳理了AlexNet、VGGNet、ResNet三大经典卷积神经网络的核心架构与创新点——从AlexNet的ReLU激活、Dropout正则化,到VGGNet的小卷积核堆叠思想,再到ResNet解决网络退化的残差连接,这些设计奠定了现代深度学习的基础。
理论理解后,代码实现是巩固知识点的关键。本文将从实战角度出发,基于PyTorch框架逐行拆解这三大网络的模拟实现代码,重点解析每一层的参数设计、维度变化逻辑和核心功能对应。
一、AlexNet模拟实现代码分析
AlexNet作为深度学习复兴的里程碑,其代码实现包含了卷积、池化、激活、正则化等CNN核心组件,是入门深度网络代码的最佳范例。
完整代码
import torchvision.models as models
import torch.nn as nn
# 打印官方AlexNet结构,作为参考对比
alexnet = models.alexnet()
print(alexnet)
class MyAlexNet(nn.Module):
def __init__(self):
super(MyAlexNet, self).__init__()
# 定义激活函数和正则化层(全局复用)
self.relu = nn.ReLU()
self.drop = nn.Dropout(0.5) # Dropout概率0.5,符合AlexNet原版设计
# 卷积层模块:提取图像空间特征
# 第一层卷积:3通道输入→64通道输出,11×11大卷积核捕捉全局特征
self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=11, stride=4,padding=2)
self.pool1 = nn.MaxPool2d(3, stride=2) # 3×3池化核,步长2下采样
# 第二层卷积:64通道→192通道,5×5卷积核捕捉中尺度特征
self.conv2 = nn.Conv2d(64,192,5,1,2)
self.pool2 = nn.MaxPool2d(3, stride=2)
# 第三/四/五层卷积:3×3小卷积核堆叠,细化特征
self.conv3 = nn.Conv2d(192,384,3,1,1)
self.conv4 = nn.Conv2d(384,256,3,1,1)
self.conv5 = nn.Conv2d(256, 256, 3, 1, 1)
self.pool3 = nn.MaxPool2d(3, stride=2) # 最后一次池化
self.adapool = nn.AdaptiveAvgPool2d(output_size=6) # 自适应池化,固定输出6×6
# 全连接层模块:将空间特征转化为分类结果
self.fc1 = nn.Linear(9216,4096) # 256×6×6=9216,展平后输入
self.fc2 = nn.Linear(4096,4096) # 隐藏层保持4096维度
self.fc3 = nn.Linear(4096,1000) # 输出1000类,对应ImageNet分类任务
def forward(self,x):
# 前向传播:严格遵循"卷积→激活→池化"流程
# 第一层卷积+激活+池化
x = self.conv1(x)
x = self.relu(x) # ReLU激活引入非线性
x = self.pool1(x)
# 第二层卷积+激活+池化
x = self.conv2(x)
x = self.relu(x)
x = self.pool2(x)
# 第三/四/五层卷积(无池化,连续堆叠细化特征)
x = self.conv3(x)
x = self.relu(x)
print(x.size()) # 打印维度,验证特征图尺寸
x = self.conv4(x)
x = self.relu(x)
print(x.size())
x = self.conv5(x)
x = self.relu(x)
x = self.pool3(x)
print(x.size())
# 自适应池化+展平:将2D特征图转为1D向量
x= self.adapool(x)
x = x.view(x.size()[0], -1) # x.size()[0]为batch_size,-1自动计算剩余维度
# 全连接层:激活+Dropout正则化
x = self.fc1(x)
x = self.relu(x)
x = self.drop(x) # fc1后加Dropout,防止过拟合
x = self.fc2(x)
x = self.relu(x)
x = self.drop(x) # fc2后再次加Dropout
# 输出层:无Dropout,直接输出分类结果
x = self.fc3(x)
x = self.relu(x)
return x
逐行分析
1. 导入模块与参考对比
import torchvision.models as models
import torch.nn as nn
alexnet = models.alexnet()
print(alexnet)
torchvision.models:PyTorch官方提供的经典网络实现,打印出来可直观对比我们手写代码与官方版本的差异;torch.nn:PyTorch神经网络核心模块,包含所有层(Conv2d、Linear等)和激活函数;- 打印官方AlexNet是重要的“参考校验”步骤,我们可通过对比快速发现自己代码的疏漏。
2. 类定义与初始化(__init__方法)
class MyAlexNet(nn.Module):
def __init__(self):
super(MyAlexNet, self).__init__()
- 所有自定义网络必须继承
nn.Module,这是PyTorch网络的基类,提供了参数管理、前向传播、设备迁移等核心功能; super(MyAlexNet, self).__init__():调用父类构造函数,初始化网络的基础属性(如参数列表、训练模式等),缺一不可。
2.1 激活与正则化层
self.relu = nn.ReLU()
self.drop = nn.Dropout(0.5)
nn.ReLU():AlexNet的核心创新之一,替代Sigmoid解决梯度消失问题,这里全局定义一个实例复用,减少内存占用;nn.Dropout(0.5):Dropout概率设为0.5(AlexNet原版参数),训练时随机丢弃50%的神经元,防止全连接层过拟合。
2.2 卷积层与池化层
self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=11, stride=4,padding=2)
in_channels=3:输入为RGB彩色图像,通道数固定为3;out_channels=64:输出64个特征图,对应64种卷积核提取的特征;kernel_size=11, stride=4:11×11大卷积核+大步长,快速缩小特征图尺寸,同时捕捉图像的全局特征;padding=2:填充2个像素,计算公式:输出尺寸=(输入尺寸 - 核尺寸 + 2×padding)/stride +1,保证224×224输入经过conv1后输出55×55((224-11+4)/4 +1=55)。
self.pool1 = nn.MaxPool2d(3, stride=2)
nn.MaxPool2d(3, stride=2):3×3最大池化核,步长2;- 池化的核心作用是下采样(缩小特征图尺寸)和特征聚合(保留局部最大值,增强鲁棒性);
- 55×55的特征图经过pool1后变为27×27((55-3)/2 +1=27)。
self.conv2 = nn.Conv2d(64,192,5,1,2)
self.pool2 = nn.MaxPool2d(3, stride=2)
- 卷积核缩小为5×5,步长1,padding=2,保证特征图尺寸不变(27×27);
- 池化后尺寸变为13×13((27-3)/2 +1=13)。
self.conv3 = nn.Conv2d(192,384,3,1,1)
self.conv4 = nn.Conv2d(384,256,3,1,1)
self.conv5 = nn.Conv2d(256, 256, 3, 1, 1)
self.pool3 = nn.MaxPool2d(3, stride=2)
- 3×3小卷积核+padding=1,是VGGNet的核心思想雏形:小核堆叠等价于大核的感受野,且参数量更少;
- 连续3层3×3卷积后,特征图尺寸仍为13×13,pool3后变为6×6((13-3)/2 +1=6)。
self.adapool = nn.AdaptiveAvgPool2d(output_size=6)
- 自适应平均池化:无论输入特征图尺寸是多少,输出固定为6×6;
- 核心优势:兼容不同尺寸的输入图像(如不是224×224时),保证后续全连接层输入维度固定。
2.3 全连接层
self.fc1 = nn.Linear(9216,4096)
9216:由256(通道数)×6(高)×6(宽)计算得出,是特征图展平后的维度;- 全连接层的作用是将卷积提取的“空间特征”转化为“类别特征”,实现从特征到分类结果的映射。
self.fc2 = nn.Linear(4096,4096)
self.fc3 = nn.Linear(4096,1000)
- fc2是隐藏层,保持4096维度增强表达能力;
- fc3输出1000维,对应ImageNet数据集的1000个类别。
3. 前向传播(forward方法)
前向传播是网络的核心执行逻辑,严格遵循“数据流动路径”:
# 卷积层流程
x = self.conv1(x)
x = self.relu(x)
x = self.pool1(x)
- 数据流动顺序:卷积→激活→池化,激活必须在卷积后、池化前,否则会丢失梯度信息;
- ReLU激活引入非线性,让网络能学习复杂特征(无激活则网络退化为线性模型)。
x= self.adapool(x)
x = x.view(x.size()[0], -1)
x.view():将4维张量(batch, 256, 6, 6)展平为2维张量(batch, 9216);x.size()[0]:固定batch维度(如4),-1让PyTorch自动计算剩余维度(9216),避免硬编码出错。
# 全连接层流程
x = self.fc1(x)
x = self.relu(x)
x = self.drop(x)
x = self.fc2(x)
x = self.relu(x)
x = self.drop(x)
x = self.fc3(x)
x = self.relu(x)
- 全连接层顺序:线性变换→激活→Dropout(仅fc1/fc2后);
- Dropout仅用在全连接层:卷积层参数量少、有空间局部性,不易过拟合;全连接层参数量大,是过拟合的重灾区,必须加正则化;
- fc3后不加Dropout:避免影响最终的分类输出,保证结果稳定性。
核心要点总结
- AlexNet代码的核心逻辑:卷积提取空间特征 → 池化下采样 → 全连接分类;
- 维度计算是关键:每一层的kernel_size/stride/padding决定了特征图尺寸,必须匹配后续层的输入维度;
- 正则化与激活的位置:ReLU在卷积/全连接后,Dropout仅在全连接层后,是经典的“防过拟合+增强非线性”组合。
二、VGGNet模拟实现代码分析
VGGNet的核心设计是“极简主义”——用统一的3×3小卷积核堆叠替代大卷积核,通过规整的层级结构实现“深度换性能”。相比于AlexNet的“零散设计”,VGGNet的模块化思想更易扩展,也是现代网络模块化构建的雏形。
完整代码
import torchvision.models as models
import torch.nn as nn
# 打印官方VGG13结构,作为参考对比
vgg = models.vgg13()
print(vgg)
# 定义VGG基础卷积层模块:2个3×3卷积 + 1个2×2池化
class vggLayer(nn.Module):
def __init__(self,in_cha, mid_cha, out_cha):
super(vggLayer, self).__init__()
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2) # 2×2池化核,步长默认2,特征图尺寸减半
# 连续2个3×3卷积,padding=1保证尺寸不变
self.conv1 = nn.Conv2d(in_cha, mid_cha, 3, 1, 1)
self.conv2 = nn.Conv2d(mid_cha, out_cha, 3, 1, 1)
def forward(self,x):
x = self.conv1(x)
x= self.relu(x)
x = self.conv2(x)
x = self.relu(x)
x = self.pool(x)
return x
# 组装完整VGG网络
class MyVgg(nn.Module):
def __init__(self):
super(MyVgg, self).__init__()
# 5个卷积阶段,每个阶段包含2个3×3卷积+1个2×2池化
self.layer1 = vggLayer(3, 64, 64) # 输入3通道→64通道
self.layer2 = vggLayer(64, 128, 128) # 64通道→128通道
self.layer3 = vggLayer(128, 256, 256)# 128通道→256通道
self.layer4 = vggLayer(256, 512, 512)# 256通道→512通道
self.layer5 = vggLayer(512, 512, 512)# 512通道→512通道
self.adapool = nn.AdaptiveAvgPool2d(7) # 自适应池化固定输出7×7
self.relu = nn.ReLU()
# 全连接层:512×7×7=25088 → 4096 → 4096 → 1000
self.fc1 = nn.Linear(25088, 4096)
self.fc2 = nn.Linear(4096, 4096)
self.fc3 = nn.Linear(4096, 1000)
def forward(self,x):
# 依次经过5个卷积阶段
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.layer5(x)
# 自适应池化+展平
x= self.adapool(x)
x = x.view(x.size()[0], -1)
# 全连接层
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu(x)
x = self.fc3(x)
x = self.relu(x)
return x
逐行分析
1. 模块化设计:vggLayer子模块
VGGNet的核心优势是模块化复用,将“2个3×3卷积+1个2×2池化”封装为独立模块,避免重复代码,这也是工业级网络的常用写法。
1.1 子模块初始化
class vggLayer(nn.Module):
def __init__(self,in_cha, mid_cha, out_cha):
super(vggLayer, self).__init__()
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2) # 等价于nn.MaxPool2d(kernel_size=2, stride=2)
self.conv1 = nn.Conv2d(in_cha, mid_cha, 3, 1, 1)
self.conv2 = nn.Conv2d(mid_cha, out_cha, 3, 1, 1)
in_cha/mid_cha/out_cha:参数化通道数,让模块可复用(如layer1传入3/64/64,layer2传入64/128/128);nn.Conv2d(..., 3, 1, 1):VGGNet的核心设计——所有卷积层统一用3×3核、步长1、padding1:- padding=1:保证卷积后特征图尺寸不变(输出尺寸=(输入-3+2×1)/1 +1=输入);
- 3×3小卷积核:2个3×3卷积堆叠的感受野=1个5×5卷积( 3 + 3 − 1 = 5 3+3-1=5 3+3−1=5),但参数量更少( 2 × 3 2 = 18 < 5 2 = 25 2×3²=18 < 5²=25 2×32=18<52=25);
nn.MaxPool2d(2):省略参数名时,默认kernel_size=2, stride=2,特征图尺寸直接减半(如224→112→56→28→14→7)。
1.2 子模块前向传播
def forward(self,x):
x = self.conv1(x)
x= self.relu(x)
x = self.conv2(x)
x = self.relu(x)
x = self.pool(x)
return x
- 流程固定:卷积1→激活→卷积2→激活→池化,激活仍在卷积后、池化前;
- 无Dropout:VGGNet的正则化主要靠数据增强,卷积层未使用Dropout,这是与AlexNet的重要区别。
2. 完整VGG网络组装(MyVgg类)
2.1 初始化:5个卷积阶段+全连接层
class MyVgg(nn.Module):
def __init__(self):
super(MyVgg, self).__init__()
self.layer1 = vggLayer(3, 64, 64)
self.layer2 = vggLayer(64, 128, 128)
self.layer3 = vggLayer(128, 256, 256)
self.layer4 = vggLayer(256, 512, 512)
self.layer5 = vggLayer(512, 512, 512)
- 5个阶段的通道数规律:3→64→128→256→512→512,每阶段通道数翻倍(最后一阶段保持512);
- 输入224×224的维度变化:
- layer1后:224→112(64通道);
- layer2后:112→56(128通道);
- layer3后:56→28(256通道);
- layer4后:28→14(512通道);
- layer5后:14→7(512通道)。
self.adapool = nn.AdaptiveAvgPool2d(7)
- 自适应池化输出7×7:即使输入不是224×224,也能保证输出512×7×7=25088,匹配全连接层输入;
- 对比AlexNet的6×6:VGGNet的池化次数更多(5次),特征图尺寸更小,因此自适应池化目标设为7×7。
self.fc1 = nn.Linear(25088, 4096)
self.fc2 = nn.Linear(4096, 4096)
self.fc3 = nn.Linear(4096, 1000)
- 25088的计算:512(通道)×7(高)×7(宽)=25088,是VGGNet全连接层的固定输入维度;
- 全连接层结构与AlexNet一致(4096→4096→1000),体现经典分类头的通用性。
2.2 完整前向传播
def forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.layer5(x)
x= self.adapool(x)
x = x.view(x.size()[0], -1)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu(x)
x = self.fc3(x)
x = self.relu(x)
return x
- 卷积阶段:依次调用5个vggLayer模块,流程高度规整,这是VGGNet最易理解的特点;
- 全连接层:无Dropout(VGGNet原版在全连接层加了Dropout(0.5),可补充
self.drop = nn.Dropout(0.5)并在fc1/fc2后调用)。
3. 核心要点总结
- VGGNet的设计精髓:
- 模块化:将重复的卷积-池化单元封装为子模块,代码更简洁易维护;
- 小核堆叠:3×3卷积核是“最小有效卷积核”,堆叠后既保证感受野,又减少参数量;
- 规整性:通道数翻倍、池化尺寸减半,维度变化有明确规律,易计算易调试。
- 与AlexNet的核心区别:
- 卷积核:AlexNet混用11×11/5×5/3×3,VGGNet全用3×3;
- 结构:AlexNet卷积层无统一模块,VGGNet高度模块化;
- 正则化:AlexNet依赖Dropout,VGGNet主要靠数据增强(代码中可补充Dropout增强鲁棒性)。
三、ResNet模拟实现代码分析
ResNet(残差网络)的核心突破是解决了“网络越深性能越差”的退化问题,其设计的残差连接为梯度传播提供了“高速公路”,让训练上百层的深度网络成为可能。ResNet18作为最轻量化的版本,是理解残差思想的最佳入门案例。
完整代码
import torch
import torch.nn as nn
import torchvision.models as models
# 打印官方ResNet18结构,作为参考对比
resNet = models.resnet18()
print(resNet)
# 定义ResNet基础残差块(Basic Block)
class Residual_block(nn.Module): #@save
def __init__(self, input_channels, out_channels, down_sample=False, strides=1):
super().__init__()
# 第一个卷积层:可通过strides实现下采样(维度匹配残差连接)
self.conv1 = nn.Conv2d(input_channels, out_channels,
kernel_size=3, padding=1, stride=strides)
# 第二个卷积层:固定stride=1,padding=1,保证尺寸不变
self.conv2 = nn.Conv2d(out_channels, out_channels,
kernel_size=3, padding=1, stride= 1)
# 短路连接(Shortcut):维度不匹配时用1×1卷积调整
if input_channels != out_channels:
self.conv3 = nn.Conv2d(input_channels, out_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None # 维度匹配时,短路连接直接传递输入
# 批量归一化(BN):ResNet核心组件,加速训练、缓解梯度消失
self.bn1 = nn.BatchNorm2d(out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU()
def forward(self, X):
# 残差路径:卷积→BN→激活→卷积→BN
out = self.relu(self.bn1(self.conv1(X)))
out= self.bn2(self.conv2(out))
# 短路连接:维度不匹配时用1×1卷积调整输入X
if self.conv3:
X = self.conv3(X)
# 残差相加:核心操作!输出 = 残差 + 原始输入
out += X
# 最后激活:保证输出的非线性
return self.relu(out)
# 组装完整的ResNet18网络
class MyResNet18(nn.Module):
def __init__(self):
super(MyResNet18, self).__init__()
# 首层卷积:7×7大卷积核+大步长,快速缩小特征图尺寸
self.conv1 = nn.Conv2d(3, 64, 7, 2, 3)
self.bn1 = nn.BatchNorm2d(64)
self.pool1 = nn.MaxPool2d(3, stride=2, padding=1) # 3×3池化,步长2
self.relu = nn.ReLU()
# 4个残差层:ResNet18共18层 = 1(首层) + 4×2×2(残差块) + 1(全连接)
self.layer1 = nn.Sequential(
Residual_block(64, 64), # 无下采样,通道数不变
Residual_block(64, 64)
)
self.layer2 = nn.Sequential(
Residual_block(64, 128, strides=2), # strides=2下采样,通道数翻倍
Residual_block(128, 128)
)
self.layer3 = nn.Sequential(
Residual_block(128, 256, strides=2),
Residual_block(256, 256)
)
self.layer4 = nn.Sequential(
Residual_block(256, 512, strides=2),
Residual_block(512, 512)
)
self.flatten = nn.Flatten()
self.adv_pool = nn.AdaptiveAvgPool2d(1) # 全局平均池化,输出1×1
self.fc = nn.Linear(512, 1000) # 全连接层:512→1000类
def forward(self, x):
# 首层:卷积→BN→激活→池化
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.pool1(x)
# 依次经过4个残差层
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
# 全局平均池化+展平+全连接分类
x = self.adv_pool(x)
x = self.flatten(x)
x = self.fc(x)
return x
# 实例化模型
myres = MyResNet18()
逐行分析
1. 核心模块:残差块(Residual_block)
残差块是ResNet的灵魂,其设计的核心是“残差连接 = 残差路径 + 短路连接”。
1.1 残差块初始化
class Residual_block(nn.Module):
def __init__(self, input_channels, out_channels, down_sample=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, out_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(out_channels, out_channels,
kernel_size=3, padding=1, stride= 1)
strides参数:仅在第一个卷积层生效,用于下采样(layer2/layer3/layer4的首个残差块设为2);- 卷积核固定为3×3:延续VGGNet的小核思想,padding=1保证卷积后尺寸不变(除strides=2时);
- 通道数变化:仅在第一个卷积层完成“输入通道→输出通道”的转换,第二个卷积层通道数不变。
if input_channels != out_channels:
self.conv3 = nn.Conv2d(input_channels, out_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
- 短路连接的维度匹配:这是残差块最关键的设计:
- 当输入/输出通道数不同(如64→128),用1×1卷积调整输入X的通道数和尺寸(strides同步下采样);
- 通道数相同时(如64→64),短路连接直接传递X,无需卷积(
self.conv3=None);
- 1×1卷积的作用:仅调整通道数/尺寸,不引入额外空间特征计算,保证短路连接的“轻量化”。
self.bn1 = nn.BatchNorm2d(out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU()
- BatchNorm(BN层):ResNet的核心组件之一,放在卷积后、激活前(与AlexNet/VGGNet的激活位置不同):
- 作用:归一化每层输出,缓解内部协变量偏移,加速训练,减少对Dropout的依赖;
- 位置:卷积→BN→激活,是现代CNN的标准范式。
1.2 残差块前向传播
def forward(self, X):
out = self.relu(self.bn1(self.conv1(X)))
out= self.bn2(self.conv2(out))
if self.conv3:
X = self.conv3(X)
out += X
return self.relu(out)
- 残差路径流程:
conv1 → BN1 → ReLU → conv2 → BN2(第二个卷积后先不加激活); - 核心操作:
out += X(残差相加):out是“残差路径的输出”,X是“短路连接的输入”,相加后才是残差块的最终特征;- 若先激活再相加,会导致恒等映射难以学习(ReLU会截断负值),这是ResNet的关键细节;
- 最后激活:相加后再通过ReLU,保证输出的非线性。
2. 完整ResNet18组装(MyResNet18类)
2.1 初始化:首层+4个残差层+分类头
class MyResNet18(nn.Module):
def __init__(self):
super(MyResNet18, self).__init__()
self.conv1 = nn.Conv2d(3, 64, 7, 2, 3)
self.bn1 = nn.BatchNorm2d(64)
self.pool1 = nn.MaxPool2d(3, stride=2, padding=1)
self.relu = nn.ReLU()
- 首层卷积:7×7大核(stride=2,padding=3),将224×224输入转为112×112(64通道);
- 首层池化:3×3池化(stride=2,padding=1),进一步将112×112转为56×56,减少后续计算量。
self.layer1 = nn.Sequential(
Residual_block(64, 64),
Residual_block(64, 64)
)
self.layer2 = nn.Sequential(
Residual_block(64, 128, strides=2),
Residual_block(128, 128)
)
self.layer3 = nn.Sequential(
Residual_block(128, 256, strides=2),
Residual_block(256, 256)
)
self.layer4 = nn.Sequential(
Residual_block(256, 512, strides=2),
Residual_block(512, 512)
)
- ResNet18的层级规律:4个残差层,每层包含2个残差块,共8个残差块(16层卷积)+ 1层首层卷积 + 1层全连接 = 18层;
- 下采样规则:仅在每个残差层的第一个残差块设
strides=2,实现:- layer1:56×56(64通道)→ 无下采样;
- layer2:56×56→28×28(128通道);
- layer3:28×28→14×14(256通道);
- layer4:14×14→7×7(512通道)。
self.flatten = nn.Flatten()
self.adv_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Linear(512, 1000)
AdaptiveAvgPool2d(1):全局平均池化,将7×7×512的特征图转为1×1×512(仅保留通道维度);- 全连接层简化:仅用1层
Linear(512, 1000),替代AlexNet/VGGNet的3层全连接,参数量大幅减少; - 对比VGGNet:ResNet用“全局平均池化+单层全连接”替代“多层全连接”,是轻量化设计的关键。
2.2 完整前向传播
def forward(self, x):
# 首层流程
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.pool1(x)
# 残差层流程
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
# 分类头
x = self.adv_pool(x)
x = self.flatten(x)
x = self.fc(x)
return x
- 首层流程:卷积→BN→激活→池化(符合ResNet原版设计);
- 残差层:直接调用封装好的Sequential模块,流程高度规整;
- 分类头:全局平均池化→展平→全连接,无Dropout/ReLU(输出层直接返回分类结果)。
3. 核心要点总结
- ResNet的设计精髓:
- 残差连接:
out += X让网络“学差值而非学映射”,解决退化问题; - BN层:卷积后立即归一化,加速训练并缓解梯度消失;
- 轻量化分类头:全局平均池化+单层全连接,大幅减少参数量。
- 残差连接:
- 与AlexNet/VGGNet的核心区别:
- 连接方式:ResNet有短路连接,后两者是“串行堆叠”;
- 归一化:ResNet全程用BN层,后两者(原版)无BN;
- 分类头:ResNet简化为单层全连接,后两者用3层全连接。
四、三大经典网络核心对比
| 网络 | 核心创新 | 卷积核特点 | 正则化方式 | 分类头设计 |
|---|---|---|---|---|
| AlexNet | ReLU、Dropout、重叠池化 | 混用11×11/5×5/3×3 | Dropout(全连接) | 3层全连接(4096→4096→1000) |
| VGGNet | 3×3小核堆叠、模块化 | 全3×3卷积核 | 数据增强 | 3层全连接(25088→4096→1000) |
| ResNet18 | 残差连接、BN层 | 全3×3卷积核 | BN层 | 全局平均池化+单层全连接 |
从AlexNet的“突破式创新”,到VGGNet的“规整化设计”,再到ResNet的“革命性连接”,三大网络完整覆盖了CNN从“能用”到“好用”再到“能用得深”的演进过程。掌握它们的代码实现,就掌握了现代卷积神经网络的核心设计逻辑。
更多推荐
所有评论(0)