PyTorch训练Loss居高不下无下降趋势,求排查代码问题
你的代码存在几个基础问题,直接导致Loss居高不下且无下降趋势,以下是具体问题和修复方法:
1. 数据未做标准化/归一化
输入x的范围是0到9999,y仅为0-9,两者量级差距极大;输出goal随x线性增长到近30000。这种极端的数据分布会导致模型权重更新时梯度爆炸或消失,优化器无法有效调整参数。
修复方法:对输入和输出做标准化(减去均值除以标准差),或者归一化到[0,1]区间,这里用sklearn.preprocessing.StandardScaler处理。
2. 训练循环遍历错误
训练函数里遍历的是train_data(自定义Dataset实例),会逐个取出单样本,而非用train_dataloader按批次加载数据。这不仅效率低下,还会导致模型每次处理单个样本,梯度波动极大,难以收敛。
修复方法:将enumerate(train_data)改为enumerate(train_dataloader)。
3. 冗余的Variable封装
PyTorch 0.4版本之后,Tensor已整合Variable的功能,不需要手动将数据转为Variable,直接使用Tensor即可。
修复方法:删除data = Variable(data)和target = Variable(target)两行代码。
4. 损失函数定义位置错误
每次迭代中重复定义criterion = F.mse_loss属于冗余操作,应将其移到循环外定义。
5. 激活函数选择不当
隐藏层使用ReLU激活函数,但目标是拟合线性函数。若初始权重导致layer_1输出为负,ReLU会将其置0,阻断梯度传递到输入层,模型无法学习x的线性关系。
修复方法:要么去掉ReLU激活(线性任务无需非线性激活),要么先做数据标准化保证ReLU能正常传递梯度。
完整修复后的代码
数据集生成代码(无需修改)
import numpy as np import pandas as pd list_x = [] list_y = [] list_goal = [] for i in range(0, 10000): list_x.append(i) list_y.append(int(np.random.rand()*10)) list_goal.append(3*i + list_y[i] + np.random.rand()*2) df_new = pd.DataFrame() df_new['x'] = list_x df_new['y'] = list_y df_new['goal'] = list_goal df_new.to_csv('dataset.csv', sep=',', encoding='utf-8', index=False)
训练代码(已修复)
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.utils.data import Dataset, DataLoader from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import pandas as pd import numpy as np # 加载数据 df = pd.read_csv('dataset.csv') X = df[["x", "y"]].values y = df[["goal"]].values # 数据标准化 scaler_X = StandardScaler() scaler_y = StandardScaler() X_scaled = scaler_X.fit_transform(X) y_scaled = scaler_y.fit_transform(y) # 划分数据集 X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_scaled, test_size=.2, random_state=42) # 自定义Dataset class Data(Dataset): def __init__(self, X, y): self.X = torch.from_numpy(X.astype(np.float32)) self.y = torch.from_numpy(y.astype(np.float32)) self.len = self.X.shape[0] def __getitem__(self, index): return self.X[index], self.y[index] def __len__(self): return self.len batch_size = 32 train_data = Data(X_train, y_train) train_dataloader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True) test_data = Data(X_test, y_test) test_dataloader = DataLoader(dataset=test_data, batch_size=batch_size, shuffle=True) # 定义模型(去掉ReLU,适配线性任务) input_dim = 2 hidden_dim_1 = 2 output_dim = 1 class NeuralNetwork(nn.Module): def __init__(self, input_dim, hidden_dim_1, output_dim): super(NeuralNetwork, self).__init__() self.layer_1 = nn.Linear(input_dim, hidden_dim_1) self.layer_out = nn.Linear(hidden_dim_1, output_dim) def forward(self, x): x = self.layer_1(x) # 使用线性激活 x = self.layer_out(x) return x model = NeuralNetwork(input_dim, hidden_dim_1, output_dim) optimizer = optim.SGD(model.parameters(), lr=0.01) criterion = F.mse_loss # 移至循环外定义 def train(epoch): model.train() total_loss = 0.0 for batch_id, (data, target) in enumerate(train_dataloader): optimizer.zero_grad() out = model(data) loss = criterion(out, target) total_loss += loss.item() loss.backward() optimizer.step() # 每个epoch打印平均损失 print(f'Epoch {epoch}, Average Training Loss: {total_loss / len(train_dataloader):.6f}') for epoch in range(1, 30): train(epoch)
内容的提问来源于stack exchange,提问作者doctor

