You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何无法过拟合单批次数据?损失持续震荡问题排查

问题描述

尝试训练一个用于回归任务的简单前馈神经网络,目标是过拟合包含32个样本(9个特征)的单批次数据,以此验证模型实现是否正确。但无论如何调整学习率和隐藏层大小,损失始终呈现震荡状态。

数据已做标准化处理,网络结构为带ReLU激活函数的单隐藏层,代码如下:

BATCH_SIZE = 32
LR = 0.0001
NUM_EPOCHS = 100
HIDDEN_SIZE = 512

class FullyConnected(nn.Module):
    def __init__(self, hidden_size=HIDDEN_SIZE):
        super().__init__()
        self.fc1 = nn.Linear(in_features=in_features, out_features=hidden_size)
        self.fc2 = nn.Linear(in_features=hidden_size, out_features=1)
        
    def forward(self, x): 
        out = self.fc1(x)
        out = F.relu(out)
        out = self.fc2(out)
        return out

训练循环仅针对单批次数据进行过拟合,代码如下:

model = FullyConnected().to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=LR)
model.train()

X, y = next(iter(train_dataloader))

for epoch in range(NUM_EPOCHS):
    y_pred = model(X)
    loss = criterion(y_pred, y)
                                                                                                                                                                                                                    
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()

为何学习率已设置较小,仍无法过拟合单批次且损失异常震荡?


可能的原因与解决办法
  • 梯度清零时机错误
    训练循环里的梯度清零步骤放在了参数更新之后,这会导致第一轮迭代时梯度未清零(使用初始随机梯度),后续轮次也可能残留上一轮的梯度,直接引发参数更新混乱,造成损失震荡。
    修正后的训练循环:

    for epoch in range(NUM_EPOCHS):
        optimizer.zero_grad()  # 先清零梯度
        y_pred = model(X)
        loss = criterion(y_pred, y)
        loss.backward()
        optimizer.step()
    
  • 训练轮次不足
    100轮的训练次数对于过拟合单批次数据来说可能不够。即使是小数据集,也需要足够多的轮次让模型参数收敛到能完全拟合该批次的状态。可以尝试将轮次提升至500甚至1000,观察损失是否会逐渐下降并稳定。

  • 目标值未标准化
    虽然你对特征做了标准化,但如果目标值y未做同样处理(比如缩放到均值为0、方差为1的范围),过大的目标值会导致初始损失过高,即使小学习率也会引发参数更新的剧烈波动。确认y是否完成标准化处理。

  • Adam优化器动量参数不匹配小数据集
    Adam默认的betas=(0.9, 0.999)在小数据集上可能导致动量累积不稳定,进而引发损失震荡。可以尝试换成SGD优化器(搭配1e-3左右的学习率),或者调整Adam的betas为(0.5, 0.9),降低一阶动量的累积速度。

  • 隐藏层ReLU激活导致梯度消失
    如果隐藏层神经元大部分处于“死亡”状态(输出为0),会导致梯度无法有效传递到输入层,参数更新停滞或混乱。可以尝试减小隐藏层大小(比如从512降到64),或者在ReLU后添加批量归一化层,提升梯度流动的稳定性:

    class FullyConnected(nn.Module):
        def __init__(self, hidden_size=HIDDEN_SIZE):
            super().__init__()
            self.fc1 = nn.Linear(in_features=in_features, out_features=hidden_size)
            self.bn = nn.BatchNorm1d(hidden_size)
            self.fc2 = nn.Linear(in_features=hidden_size, out_features=1)
            
        def forward(self, x): 
            out = self.fc1(x)
            out = self.bn(out)
            out = F.relu(out)
            out = self.fc2(out)
            return out
    

内容的提问来源于stack exchange,提问作者SlimakSlimak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 20:35:24