仿TinyVGG的CNN模型各Epoch损失与准确率无变化的优化咨询
解决手写单词分类模型训练无进展问题
针对你训练时所有Epoch损失和准确率完全不变的问题,结合你的代码和场景,可从以下几个关键方向修改:
1. 修正分类器输入维度匹配问题
你模型中分类器的in_features=hidden_units*16*16是硬编码值,仅适用于初始尺寸为64×64的图像(经过两次2×2池化后特征图变为16×16)。如果你的输入图像尺寸不符,会导致特征维度不匹配,模型无法正常传递梯度,最终输出固定值。
修改方案:
先通过随机张量测试特征图尺寸,再动态设置分类器输入维度:
# 初始化模型后,用与你的输入同尺寸的随机张量测试 torch.manual_seed(42) dummy_input = torch.randn(1, 3, 64, 64).to(device) # 替换为你的图像实际尺寸(通道数, 高, 宽) with torch.inference_mode(): # 仅运行特征提取部分,获取输出形状 feat_map = model_2.block_2(model_2.block_1(dummy_input)) print(f"特征图形状: {feat_map.shape}") in_features = feat_map.numel() // feat_map.shape[0] # 计算单样本特征总数量 # 重新替换分类器 model_2.classifier = nn.Sequential( nn.Flatten(), nn.Linear(in_features=in_features, out_features=len(class_names)) ).to(device)
2. 移除重复的模型设备迁移
你的train_step和test_step函数中每次循环都调用model.to(device),这会重复迁移模型,可能导致参数状态异常。只需在模型初始化时执行一次设备迁移即可。
修改方案:
删除train_step和test_step中的model.to(device)行,保留初始化时的迁移:
# 初始化时已完成设备迁移,后续无需重复操作 model_2 = CUSTOMDATASETV2(input_shape=3, hidden_units=10, output_shape=len(class_names)).to(device)
3. 验证损失函数与输入的兼容性
如果使用nn.CrossEntropyLoss,它要求输入是未经过softmax的logits,且标签为类别索引(而非one-hot向量)。需确认两点:
- 你的
loss_fn确实是nn.CrossEntropyLoss() - 数据集标签是类别索引(如0=TRUE、1=FALSE、2=NONE),而非one-hot编码
若标签是one-hot格式,需在数据加载时转换:
# 在数据加载循环中添加标签转换 X, y = X.to(device), y.argmax(dim=1).to(device) # 将one-hot转为索引
4. 提升模型容量
手写单词的特征复杂度较高,当前模型hidden_units=10过小,TinyVGG基础结构可能不足以拟合数据。
调整方案:
- 增加隐藏单元数量,例如改为32或64:
model_2 = CUSTOMDATASETV2(input_shape=3, hidden_units=32, output_shape=len(class_names)).to(device)
- 可选:在分类器中添加一层全连接层,增强拟合能力:
self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(in_features=in_features, out_features=hidden_units*2), nn.ReLU(), nn.Linear(in_features=hidden_units*2, out_features=output_shape) )
5. 检查数据预处理与数据集质量
训练无进展的常见根源是数据问题:
- 图像归一化:将像素值缩放到0-1范围(除以255),可加速模型收敛:
# 在数据转换管道中添加归一化 transform = transforms.Compose([ transforms.Resize((64, 64)), # 统一图像尺寸 transforms.ToTensor(), transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) # 或直接除以255 ])
- 数据集验证:确认训练集/测试集包含所有三类数据,无类别缺失或比例失衡;随机抽查样本,确保标签与图像匹配。
6. 初始化与梯度检查
- 优化卷积层参数初始化,避免梯度消失:
# 在模型__init__方法末尾添加初始化逻辑 def __init__(self, input_shape: int, hidden_units: int, output_shape: int): super().__init__() # ... 原有结构定义 ... # 初始化卷积层参数 for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') if m.bias is not None: nn.init.constant_(m.bias, 0)
- 训练时可打印梯度均值,确认梯度是否正常更新(不为0或NaN):
# 在train_step的loss.backward()后添加 for name, param in model.named_parameters(): if param.grad is not None: print(f"{name} 梯度均值: {param.grad.mean().item()}")
内容的提问来源于stack exchange,提问作者Llana
相关产品推荐
相关产品推荐

