残差网络 (ResNet 与 ResNeXt)
探索残差连接的革命性突破,深入理解ResNet如何解决深度网络训练难题

展开文章目录
1. 残差网络的理论基础
1.1 函数嵌套与表达能力
1.2 最优函数寻找问题
只要问题是可计算的,理论上就一定存在最佳函数 将问题完美解决。使用现有神经网络 解决该问题的前提,是找到当前架构 下的最佳函数 作为问题的近似解。训练模型,调整现有函数 的参数,使输入数据 经网络 输出后与真实标签 的损失值 最小,最终得到使损失值最小的函数 :
1.3 函数嵌套的重要性
如下图,对于非嵌套的函数类,虽 到 逐渐与 接近,但 到 又逐渐远离 ;只有对于嵌套的函数类,, 逐步比 更接近 :
/1736564006963-179cc7fa-2712-4da0-98c4-7046ed06f616.jpeg)
1.4 残差网络的核心洞察
2. ResNet 网络架构设计
2.1 残差块的设计原理
残差块设计
传统网络层的学习方式
- 直接学习 :输入到输出的完整映射
- 当网络很深时,直接学习复杂映射变得困难
- 梯度消失和训练困难问题
残差学习方式
- 学习残差
- 假设残差比原映射更容易学习
- 最终输出:
- 当 时,自然退化为恒等映射
3. ResNet 与 ResNeXt 完整网络架构
3.1 ResNet 网络结构
3.2 ResNet-18 结构分析
ResNet结构组成
网络开始部分
- 7×7 卷积层 (输出通道64,步幅2)
- 3×3 最大池化层 (步幅2)
残差块分组设计
- 第1组:两个连续的64通道残差块
- 第2组:特征图减半、通道数翻倍 + 瓶颈结构
- 第3组:重复第2组的设计模式
- 第4组:继续重复,形成深层特征提取
ResNet-18 层数计算
- 不考虑跳跃连接的1×1卷积层
- 共计:1 + 2×2 + (2+2)×3 = 17 个卷积层
- 考虑最后一个全连接层:共18个计算密集层
- 因此称为 ResNet-18
- 其他变体:ResNet-101、ResNet-152等
3.3 ResNeXt 的改进
/1736762684679-d53228c0-437a-4119-95d6-c68f3403c54f.jpeg)
3.4 PyTorch 实现
from typing import Optional
import torch.nn.functional as F
from torch import nn, Tensor
class ResNetBlock(nn.Module):
def __init__(
self,
in_channels: int,
out_channels: int, *,
stride: int = 1,
bottleneck_ratio: int = 4,
downsample: Optional[nn.Module] = None):
"""
后激活的残差块
:param in_channels: 输入通道数
:param out_channels: 输出通道数
:param stride: 卷积步长
:param bottleneck_ratio: 瓶颈比率,控制中间层宽度
:param downsample: 下采样层,用于维度匹配
"""
super().__init__()
# 计算中间层通道数
assert bottleneck_ratio >= 1, f'瓶颈比率 ({bottleneck_ratio}) 应大于或等于 1'
mid_channels = out_channels // bottleneck_ratio if bottleneck_ratio > 1 else out_channels
if bottleneck_ratio > 1:
# 1x1 Conv -> 3x3 Conv -> 1x1 Conv
assert mid_channels > 0, f'中间通道数 ({mid_channels}) 必须大于 0'
self.main_path = nn.Sequential(
nn.Conv2d(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(mid_channels),
nn.ReLU(),
nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=1, bias=False),
nn.BatchNorm2d(mid_channels),
nn.ReLU(),
nn.Conv2d(mid_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_channels)
)
else:
# 3x3 Conv -> 3x3 Conv
self.main_path = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False),
nn.BatchNorm2d(out_channels)
)
# 匹配维度用于跳跃连接
if downsample is not None:
self.shortcut = downsample
elif stride != 1 or in_channels != out_channels: # 空间尺寸、通道数发生变化时,再调整维度
self.shortcut = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)
else:
self.shortcut = nn.Identity() # 恒等映射
def forward(self, x: Tensor) -> Tensor:
out = self.main_path(x) + self.shortcut(x) # 主路径与跳跃连接相加
out = F.relu(out) # 激活加和后的结果
return out
class ResNeXtBlock(nn.Module):
def __init__(
self,
in_channels: int,
out_channels: int, *,
stride: int = 1,
groups: int = 1,
bottleneck_ratio: int = 4,
downsample: Optional[nn.Module] = None):
"""
预激活的分组残差块
:param in_channels: 输入通道数
:param out_channels: 输出通道数
:param stride: 卷积步长
:param groups: 分组卷积的组数
:param bottleneck_ratio: 瓶颈比率,控制中间层宽度
:param downsample: 下采样层,用于维度匹配
"""
super().__init__()
# 计算中间层通道数
assert bottleneck_ratio >= 1, f'瓶颈比率 ({bottleneck_ratio}) 应大于或等于 1'
mid_channels = out_channels // bottleneck_ratio if bottleneck_ratio > 1 else out_channels
if bottleneck_ratio > 1:
# 1x1 Conv -> 3x3 Conv -> 1x1 Conv
assert mid_channels % groups == 0, f'中间通道数 ({mid_channels}) 必须能被卷积分组数 ({groups}) 整除'
assert mid_channels > 0, f'中间通道数 ({mid_channels}) 必须大于 0'
self.main_path = nn.Sequential(
nn.BatchNorm2d(in_channels), nn.ReLU(),
nn.Conv2d(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(mid_channels), nn.ReLU(),
nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=1, groups=groups,
bias=False),
nn.BatchNorm2d(mid_channels), nn.ReLU(),
nn.Conv2d(mid_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False)
)
else:
# 3x3 Conv -> 3x3 Conv
assert out_channels % groups == 0, f'输出通道数 ({out_channels}) 必须能被卷积分组数 ({groups}) 整除'
self.main_path = nn.Sequential(
nn.BatchNorm2d(in_channels), nn.ReLU(),
nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, groups=groups,
bias=False),
nn.BatchNorm2d(out_channels), nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, groups=groups, bias=False)
)
# 匹配维度用于跳跃连接
if downsample is not None:
self.shortcut = downsample
elif stride != 1 or in_channels != out_channels: # 空间尺寸、通道数发生变化时,再调整维度
self.shortcut = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)
else:
self.shortcut = nn.Identity()
def forward(self, x: Tensor) -> Tensor:
return self.main_path(x) + self.shortcut(x)
class ResNet(nn.Module):
def __init__(self, num_classes: int):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=64, kernel_size=7, stride=2, padding=3, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
ResNetBlock(64, 64, bottleneck_ratio=1),
ResNetBlock(64, 64, bottleneck_ratio=1),
# 组 1
ResNetBlock(64, 128, stride=2, bottleneck_ratio=1),
ResNetBlock(128, 128, bottleneck_ratio=4),
# 组 2
ResNetBlock(128, 256, stride=2, bottleneck_ratio=1),
ResNetBlock(256, 256, bottleneck_ratio=4),
# 组 3
ResNetBlock(256, 512, stride=2, bottleneck_ratio=1),
ResNetBlock(512, 512, bottleneck_ratio=4),
nn.AdaptiveAvgPool2d(1), nn.Flatten(),
nn.Linear(in_features=512, out_features=num_classes),
)
self._initialize_weights()
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
if m.bias is not None: nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight)
if m.bias is not None: nn.init.constant_(m.bias, 0)
def forward(self, x) -> Tensor:
return self.model(x)
class ResNeXt(nn.Module):
def __init__(self, num_classes: int, cardinality: int = 32):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=64, kernel_size=7, stride=2, padding=3, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
ResNeXtBlock(64, 64, groups=1, bottleneck_ratio=1),
ResNeXtBlock(64, 64, groups=1, bottleneck_ratio=1),
# 组 1
ResNeXtBlock(64, 128, stride=2, groups=cardinality, bottleneck_ratio=4),
ResNeXtBlock(128, 128, groups=cardinality, bottleneck_ratio=4),
# 组 2
ResNeXtBlock(128, 256, stride=2, groups=cardinality, bottleneck_ratio=4),
ResNeXtBlock(256, 256, groups=cardinality, bottleneck_ratio=4),
# 组 3
ResNeXtBlock(256, 512, stride=2, groups=cardinality, bottleneck_ratio=4),
ResNeXtBlock(512, 512, groups=cardinality, bottleneck_ratio=4),
nn.AdaptiveAvgPool2d(1), nn.Flatten(),
nn.Linear(in_features=512, out_features=num_classes),
)
self._initialize_weights()
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
if m.bias is not None: nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.xavier_uniform_(m.weight)
if m.bias is not None: nn.init.constant_(m.bias, 0)
def forward(self, x) -> Tensor:
return self.model(x)
4. 网络结构分析
4.1 ResNet 与 ResNeXt 参数对比
使用torchinfo库的summary函数执行输出维度测试:
from torchinfo import summary
model = ResNet(num_classes=10)
summary(model, input_size=(1, 1, 224, 224))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
ResNet [1, 10] --
├─Sequential: 1-1 [1, 10] --
│ └─Conv2d: 2-1 [1, 64, 112, 112] 3,136
│ └─BatchNorm2d: 2-2 [1, 64, 112, 112] 128
│ └─ReLU: 2-3 [1, 64, 112, 112] --
│ └─MaxPool2d: 2-4 [1, 64, 56, 56] --
│ └─ResNetBlock: 2-5 [1, 64, 56, 56] --
│ │ └─Sequential: 3-1 [1, 64, 56, 56] 73,984
│ │ └─Identity: 3-2 [1, 64, 56, 56] --
│ └─ResNetBlock: 2-6 [1, 64, 56, 56] --
│ │ └─Sequential: 3-3 [1, 64, 56, 56] 73,984
│ │ └─Identity: 3-4 [1, 64, 56, 56] --
│ └─ResNetBlock: 2-7 [1, 128, 28, 28] --
│ │ └─Sequential: 3-5 [1, 128, 28, 28] 221,696
│ │ └─Conv2d: 3-6 [1, 128, 28, 28] 8,192
│ └─ResNetBlock: 2-8 [1, 128, 28, 28] --
│ │ └─Sequential: 3-7 [1, 128, 28, 28] 17,792
│ │ └─Identity: 3-8 [1, 128, 28, 28] --
│ └─ResNetBlock: 2-9 [1, 256, 14, 14] --
│ │ └─Sequential: 3-9 [1, 256, 14, 14] 885,760
│ │ └─Conv2d: 3-10 [1, 256, 14, 14] 32,768
│ └─ResNetBlock: 2-10 [1, 256, 14, 14] --
│ │ └─Sequential: 3-11 [1, 256, 14, 14] 70,400
│ │ └─Identity: 3-12 [1, 256, 14, 14] --
│ └─ResNetBlock: 2-11 [1, 512, 7, 7] --
│ │ └─Sequential: 3-13 [1, 512, 7, 7] 3,540,992
│ │ └─Conv2d: 3-14 [1, 512, 7, 7] 131,072
│ └─ResNetBlock: 2-12 [1, 512, 7, 7] --
│ │ └─Sequential: 3-15 [1, 512, 7, 7] 280,064
│ │ └─Identity: 3-16 [1, 512, 7, 7] --
│ └─AdaptiveAvgPool2d: 2-13 [1, 512, 1, 1] --
│ └─Flatten: 2-14 [1, 512] --
│ └─Linear: 2-15 [1, 10] 5,130
==========================================================================================
Total params: 5,345,098
Trainable params: 5,345,098
Non-trainable params: 0
Total mult-adds (Units.GIGABYTES): 1.08
==========================================================================================
Input size (MB): 0.20
Forward/backward pass size (MB): 36.93
Params size (MB): 21.38
Estimated Total Size (MB): 58.51
==========================================================================================
from torchinfo import summary
model = ResNeXt(num_classes=10, cardinality=16)
summary(model, input_size=(1, 1, 224, 224))
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
ResNeXt [1, 10] --
├─Sequential: 1-1 [1, 10] --
│ └─Conv2d: 2-1 [1, 64, 112, 112] 3,136
│ └─BatchNorm2d: 2-2 [1, 64, 112, 112] 128
│ └─ReLU: 2-3 [1, 64, 112, 112] --
│ └─MaxPool2d: 2-4 [1, 64, 56, 56] --
│ └─ResNeXtBlock: 2-5 [1, 64, 56, 56] --
│ │ └─Sequential: 3-1 [1, 64, 56, 56] 73,984
│ │ └─Identity: 3-2 [1, 64, 56, 56] --
│ └─ResNeXtBlock: 2-6 [1, 64, 56, 56] --
│ │ └─Sequential: 3-3 [1, 64, 56, 56] 73,984
│ │ └─Identity: 3-4 [1, 64, 56, 56] --
│ └─ResNeXtBlock: 2-7 [1, 128, 28, 28] --
│ │ └─Sequential: 3-5 [1, 128, 28, 28] 6,976
│ │ └─Conv2d: 3-6 [1, 128, 28, 28] 8,192
│ └─ResNeXtBlock: 2-8 [1, 128, 28, 28] --
│ │ └─Sequential: 3-7 [1, 128, 28, 28] 9,152
│ │ └─Identity: 3-8 [1, 128, 28, 28] --
│ └─ResNeXtBlock: 2-9 [1, 256, 14, 14] --
│ │ └─Sequential: 3-9 [1, 256, 14, 14] 27,392
│ │ └─Conv2d: 3-10 [1, 256, 14, 14] 32,768
│ └─ResNeXtBlock: 2-10 [1, 256, 14, 14] --
│ │ └─Sequential: 3-11 [1, 256, 14, 14] 35,840
│ │ └─Identity: 3-12 [1, 256, 14, 14] --
│ └─ResNeXtBlock: 2-11 [1, 512, 7, 7] --
│ │ └─Sequential: 3-13 [1, 512, 7, 7] 108,544
│ │ └─Conv2d: 3-14 [1, 512, 7, 7] 131,072
│ └─ResNeXtBlock: 2-12 [1, 512, 7, 7] --
│ │ └─Sequential: 3-15 [1, 512, 7, 7] 141,824
│ │ └─Identity: 3-16 [1, 512, 7, 7] --
│ └─AdaptiveAvgPool2d: 2-13 [1, 512, 1, 1] --
│ └─Flatten: 2-14 [1, 512] --
│ └─Linear: 2-15 [1, 10] 5,130
==========================================================================================
Total params: 658,122
Trainable params: 658,122
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 571.92
==========================================================================================
Input size (MB): 0.20
Forward/backward pass size (MB): 39.04
Params size (MB): 2.63
Estimated Total Size (MB): 41.87
==========================================================================================
4.2 网络参数统计对比
参数对比分析
ResNet 网络参数:
- 总参数量:5,345,098 个参数(约 535 万)
- 计算复杂度:1.08 GB 乘加运算
- 内存占用:总计约 58.51 MB
- 特点:深层网络但参数控制合理
ResNeXt 网络参数:
- 总参数量:658,122 个参数(约 66 万)
- 计算复杂度:571.92 MB 乘加运算
- 内存占用:总计约 41.87 MB
- 特点:分组卷积显著减少参数数量
ResNet vs ResNeXt 对比:
- 参数效率:ResNeXt 参数量仅为 ResNet 的 12.3%
- 计算量:ResNeXt 计算量约为 ResNet 的 52.8%
- 内存效率:ResNeXt 内存占用减少约 28.5%
- 设计哲学:ResNeXt “分而治之” vs ResNet “深度加宽”
5. 模型训练与评估
5.1 训练配置设置
继续使用training_tools.py中的工具训练评估模型:
if __name__ == '__main__':
import torch
from torch import optim
from training_tools import fashionMNIST_loader, Trainer
BATCH_SIZE = 256
EPOCHS_NUM = 30
LEARNING_RATE = 0.01
USE_RESNEXT = False
model = ResNeXt(num_classes=10, cardinality=16) if USE_RESNEXT else ResNet(num_classes=10)
train_loader, test_loader = fashionMNIST_loader(BATCH_SIZE, resize=96)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), LEARNING_RATE)
platform = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
with Trainer(model, train_loader, test_loader, criterion, optimizer, platform) as trainer:
trainer.train(EPOCHS_NUM)
5.2 ResNet 训练结果
查看ResNet完整训练过程
第 001/30 轮,训练损失:0.7456,训练精度:75.69%,测试损失:0.5445,测试精度:81.32%
第 002/30 轮,训练损失:0.4158,训练精度:85.33%,测试损失:0.4383,测试精度:84.61%
第 003/30 轮,训练损失:0.3470,训练精度:87.80%,测试损失:0.3901,测试精度:86.17%
第 004/30 轮,训练损失:0.3044,训练精度:89.31%,测试损失:0.3702,测试精度:87.16%
第 005/30 轮,训练损失:0.2712,训练精度:90.54%,测试损失:0.3472,测试精度:87.71%
第 006/30 轮,训练损失:0.2454,训练精度:91.48%,测试损失:0.3242,测试精度:88.35%
第 007/30 轮,训练损失:0.2219,训练精度:92.31%,测试损失:0.3324,测试精度:88.03%
第 008/30 轮,训练损失:0.2010,训练精度:93.13%,测试损失:0.3449,测试精度:88.19%
第 009/30 轮,训练损失:0.1823,训练精度:93.92%,测试损失:0.3210,测试精度:88.26%
第 010/30 轮,训练损失:0.1638,训练精度:94.73%,测试损失:0.3600,测试精度:87.18%
第 011/30 轮,训练损失:0.1469,训练精度:95.39%,测试损失:0.3198,测试精度:88.87%
第 012/30 轮,训练损失:0.1310,训练精度:95.96%,测试损失:0.3082,测试精度:89.11%
第 013/30 轮,训练损失:0.1142,训练精度:96.72%,测试损失:0.3256,测试精度:88.75%
第 014/30 轮,训练损失:0.1003,训练精度:97.25%,测试损失:0.3263,测试精度:88.85%
第 015/30 轮,训练损失:0.0884,训练精度:97.69%,测试损失:0.3254,测试精度:89.12%
第 016/30 轮,训练损失:0.0743,训练精度:98.27%,测试损失:0.3286,测试精度:89.86%
第 017/30 轮,训练损失:0.0629,训练精度:98.67%,测试损失:0.3608,测试精度:88.71%
第 018/30 轮,训练损失:0.0535,训练精度:98.98%,测试损失:0.3162,测试精度:89.73%
第 019/30 轮,训练损失:0.0440,训练精度:99.36%,测试损失:0.3553,测试精度:89.56%
第 020/30 轮,训练损失:0.0376,训练精度:99.48%,测试损失:0.3171,测试精度:90.01%
第 021/30 轮,训练损失:0.0311,训练精度:99.69%,测试损失:0.3264,测试精度:89.91%
第 022/30 轮,训练损失:0.0249,训练精度:99.81%,测试损失:0.3746,测试精度:88.89%
第 023/30 轮,训练损失:0.0214,训练精度:99.90%,测试损失:0.3387,测试精度:89.95%
第 024/30 轮,训练损失:0.0185,训练精度:99.92%,测试损失:0.3616,测试精度:89.70%
第 025/30 轮,训练损失:0.0155,训练精度:99.96%,测试损失:0.3775,测试精度:89.90%
第 026/30 轮,训练损失:0.0134,训练精度:99.97%,测试损失:0.3476,测试精度:90.39%
第 027/30 轮,训练损失:0.0117,训练精度:99.99%,测试损失:0.3604,测试精度:90.26%
第 028/30 轮,训练损失:0.0103,训练精度:99.99%,测试损失:0.3939,测试精度:89.50%
第 029/30 轮,训练损失:0.0095,训练精度:99.99%,测试损失:0.3723,测试精度:90.18%
第 030/30 轮,训练损失:0.0084,训练精度:100.00%,测试损失:0.3758,测试精度:90.04%/1747018686667-340a5b8e-940a-4ec0-bc4e-13c72b128cb9.png)
5.3 ResNeXt 训练结果
查看ResNeXt完整训练过程
第 001/30 轮,训练损失:1.1331,训练精度:73.02%,测试损失:0.5520,测试精度:80.88%
第 002/30 轮,训练损失:0.4683,训练精度:83.26%,测试损失:0.5709,测试精度:79.78%
第 003/30 轮,训练损失:0.4076,训练精度:85.35%,测试损失:0.4815,测试精度:83.95%
第 004/30 轮,训练损失:0.3679,训练精度:86.83%,测试损失:0.4586,测试精度:83.86%
第 005/30 轮,训练损失:0.3410,训练精度:87.82%,测试损失:0.4085,测试精度:85.31%
第 006/30 轮,训练损失:0.3200,训练精度:88.52%,测试损失:0.4353,测试精度:84.45%
第 007/30 轮,训练损失:0.3013,训练精度:89.15%,测试损失:0.3780,测试精度:86.73%
第 008/30 轮,训练损失:0.2866,训练精度:89.76%,测试损失:0.3776,测试精度:86.71%
第 009/30 轮,训练损失:0.2721,训练精度:90.30%,测试损失:0.3855,测试精度:86.57%
第 010/30 轮,训练损失:0.2600,训练精度:90.77%,测试损失:0.4883,测试精度:82.86%
第 011/30 轮,训练损失:0.2459,训练精度:91.23%,测试损失:0.3821,测试精度:86.15%
第 012/30 轮,训练损失:0.2366,训练精度:91.65%,测试损失:0.5613,测试精度:82.66%
第 013/30 轮,训练损失:0.2259,训练精度:92.01%,测试损失:0.4315,测试精度:84.59%
第 014/30 轮,训练损失:0.2160,训练精度:92.39%,测试损失:0.3506,测试精度:87.52%
第 015/30 轮,训练损失:0.2065,训练精度:92.66%,测试损失:0.3490,测试精度:87.52%
第 016/30 轮,训练损失:0.1982,训练精度:93.01%,测试损失:0.3982,测试精度:85.88%
第 017/30 轮,训练损失:0.1889,训练精度:93.42%,测试损失:0.3827,测试精度:86.97%
第 018/30 轮,训练损失:0.1802,训练精度:93.83%,测试损失:0.3707,测试精度:87.14%
第 019/30 轮,训练损失:0.1715,训练精度:94.11%,测试损失:0.3806,测试精度:87.30%
第 020/30 轮,训练损失:0.1661,训练精度:94.28%,测试损失:0.4515,测试精度:85.46%
第 021/30 轮,训练损失:0.1573,训练精度:94.65%,测试损失:0.3794,测试精度:87.28%
第 022/30 轮,训练损失:0.1514,训练精度:94.88%,测试损失:0.3444,测试精度:88.06%
第 023/30 轮,训练损失:0.1449,训练精度:95.07%,测试损失:0.4038,测试精度:86.90%
第 024/30 轮,训练损失:0.1397,训练精度:95.25%,测试损失:0.3766,测试精度:87.71%
第 025/30 轮,训练损失:0.1312,训练精度:95.71%,测试损失:0.5073,测试精度:86.13%
第 026/30 轮,训练损失:0.1256,训练精度:95.88%,测试损失:0.4904,测试精度:85.24%
第 027/30 轮,训练损失:0.1181,训练精度:96.25%,测试损失:0.4093,测试精度:86.69%
第 028/30 轮,训练损失:0.1125,训练精度:96.36%,测试损失:0.5151,测试精度:85.10%
第 029/30 轮,训练损失:0.1063,训练精度:96.60%,测试损失:0.3796,测试精度:87.71%
第 030/30 轮,训练损失:0.0987,训练精度:96.85%,测试损失:0.4043,测试精度:87.98%/1747019496847-06fcadb4-033c-45b7-8c3c-15653ff558c9.png)
5.4 性能对比分析
性能对比
ResNet 训练表现:
- 最终训练精度:100.00%
- 最终测试精度:90.04%
- 收敛特点:快速收敛,过拟合明显
- 参数优势:结构简单,参数集中
ResNeXt 训练表现:
- 最终训练精度:96.85%
- 最终测试精度:87.98%
- 收敛特点:较稳定,泛化性更好
- 参数优势:分组卷积,参数更少
实验结论分析:
参考资料
如果这篇记录对你有帮助,可以留下一句回应。

DISCUSSION
讨论与回应
欢迎补充细节、指出问题,或分享与这篇文章有关的经验。
昵称与邮箱为必填项,邮箱仅用于头像和回复通知,不会公开。网址可以留空。