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

PyTorch神经网络训练后期损失曲线出现振荡的原因探究

为何我的PyTorch神经网络训练后期会出现损失曲线振荡?

我使用了一个基础的序列神经网络,搭配Adam optimizer,学习率设为0.0001。

模型代码

class ANN(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(in_features=956, out_features=500)
        self.fc2 = nn.Linear(in_features=500, out_features=500)
        self.fc3 = nn.Linear(in_features=500, out_features=100)
        self.output = nn.Linear(in_features=100, out_features=2)
 
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = self.output(x)
        return x

损失函数、优化器及训练代码

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)

epochs = 400
loss_arr = []

for i in range(epochs):
    y_hat = model.forward(X_train)
    loss = criterion(y_hat, y_train)
    loss_arr.append(loss)
    
 
    if i % 10 == 0:
        print(f'Epoch: {i} Loss: {loss}')
 
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

损失曲线

训练后期振荡的损失曲线

我预期损失曲线会随训练正常下降,或在模型过拟合时骤升,但实际后期出现振荡。由于数据量不足,无法划分验证集监测过拟合,只能通过反复训练来避免过拟合。


可能的原因及解决办法

  • 学习率适配问题:训练后期模型参数接近最优解时,固定的0.0001学习率可能导致参数在最优值附近来回跳动。可以引入学习率衰减机制,比如用ReduceLROnPlateau,当损失连续多轮不再下降时自动降低学习率:

    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=5, factor=0.5)
    # 训练循环中添加
    scheduler.step(loss)
    
  • 模型容量过剩:你的模型有两层500维的隐藏层,对于小数据集来说容量过大,容易在后期拟合数据中的噪声,引发损失振荡。可以尝试:

    • 缩小隐藏层维度,比如把500改成256或128;
    • 在隐藏层后加入Dropout层抑制过拟合:
      def forward(self, x):
          x = F.relu(self.fc1(x))
          x = nn.Dropout(0.2)(x)  # 添加Dropout
          x = F.relu(self.fc2(x))
          x = nn.Dropout(0.2)(x)
          x = F.relu(self.fc3(x))
          x = self.output(x)
          return x
      
  • 全量训练的不稳定性:当前用全量数据计算梯度并更新参数,后期梯度波动会直接导致参数大幅跳动。改成小批量训练,用DataLoader拆分数据,每次用一个batch更新,让梯度更平稳:

    from torch.utils.data import TensorDataset, DataLoader
    dataset = TensorDataset(X_train, y_train)
    dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
    
    # 修改训练循环
    for i in range(epochs):
        total_loss = 0
        for batch_x, batch_y in dataloader:
            y_hat = model(batch_x)
            loss = criterion(y_hat, batch_y)
            total_loss += loss.item()
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        avg_loss = total_loss / len(dataloader)
        loss_arr.append(avg_loss)
        if i % 10 == 0:
            print(f'Epoch: {i} Loss: {avg_loss}')
    
  • 数据预处理缺失:输入特征维度达956,若不同特征尺度差异大,会干扰梯度更新的稳定性。确保对输入数据做标准化/归一化,比如用StandardScaler将特征缩放到均值0、方差1的范围。

  • 标签格式验证:CrossEntropyLoss要求目标标签是类别索引(不是one-hot编码),如果y_train格式错误,也可能导致损失异常波动,检查标签的形状和数值范围是否符合要求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:22:12