PyTorch模型训练精度低于随机猜测,求故障原因
PyTorch模型训练精度远低于随机猜测的问题排查与修复
核心问题分析
- 训练/测试集完全重叠:你定义的
SDSS和testSDSS类加载了同一个SDSS.csv文件,没有划分独立的训练集和测试集,无法真实评估模型的泛化能力。 - 测试阶段正确数未累加:测试循环中
n_correct = (predictions == labels).sum().item()是赋值而非累加,导致最终仅统计最后一个batch的正确样本数,精度计算完全错误。 - 输入特征未标准化:不同特征的量级可能差异极大,导致模型梯度更新不稳定,难以收敛。
- 训练步骤数计算错误:
n_total_steps = len(dataset)错误使用样本总数作为step数,实际应使用len(data_loader)(即batch的总数)。 - 数据与模型未绑定设备:代码中未将模型和输入数据移到指定
device,后续切换GPU会出现兼容问题。 - 冗余张量操作:
inputs = torch.flatten(inputs)在模型前向传播后执行,对训练无任何作用,属于无效代码。
修复后的完整代码
import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 设备配置 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') input_size = 5 hidden_size = 10 num_classes = 3 num_epochs = 100 batch_size = 10 learning_rate = 0.001 # Adam默认学习率为0.001,适当调大更易收敛 # 统一数据集类,支持传入划分好的特征和标签 class SDSSDataset(Dataset): def __init__(self, features, labels): self.x_data = torch.from_numpy(features).float() self.y_data = torch.from_numpy(labels).long() # 直接转为long类型,避免后续重复转换 self.n_samples = features.shape[0] def __getitem__(self, index): return self.x_data[index], self.y_data[index] def __len__(self): return self.n_samples # 数据预处理流程 # 1. 读取原始数据 xy = np.loadtxt('SDSS.csv', delimiter=',', dtype=np.float32, skiprows=0) labels = xy[:, 0] features = xy[:, 1:] # 2. 确保标签从0开始(CrossEntropyLoss要求标签范围为0~num_classes-1) if np.min(labels) == 1: labels = labels - 1 # 3. 划分训练/测试集(8:2比例,分层抽样保证类别分布一致) train_features, test_features, train_labels, test_labels = train_test_split( features, labels, test_size=0.2, random_state=42, stratify=labels ) # 4. 特征标准化(关键步骤,消除特征量级差异) scaler = StandardScaler() train_features = scaler.fit_transform(train_features) test_features = scaler.transform(test_features) # 创建数据加载器 train_dataset = SDSSDataset(train_features, train_labels) test_dataset = SDSSDataset(test_features, test_labels) train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, num_workers=0) test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False, num_workers=0) # 定义神经网络 class NeuralNet(nn.Module): def __init__(self, input_size, hidden_size, num_classes): super(NeuralNet, self).__init__() self.l1 = nn.Linear(input_size, hidden_size) self.relu = nn.LeakyReLU() self.l2 = nn.Linear(hidden_size, num_classes) def forward(self, x): out = self.l1(x) out = self.relu(out) out = self.l2(out) return out # 初始化模型并绑定到设备 model = NeuralNet(input_size, hidden_size, num_classes).to(device) # 损失函数与优化器 criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # 训练循环 n_total_steps = len(train_loader) # 正确的step数为batch总数 for epoch in range(num_epochs): print(f'epoch: {epoch + 1} / {num_epochs}') model.train() for i, (inputs, labels) in enumerate(train_loader): # 将数据移到指定设备 inputs = inputs.to(device) labels = labels.to(device) # 前向传播 outputs = model(inputs) loss = criterion(outputs, labels) # 反向传播与参数更新 optimizer.zero_grad() loss.backward() optimizer.step() if (i + 1) % 100 == 0: print(f'epoch {epoch + 1}/{num_epochs}, step {i + 1}/{n_total_steps}, loss = {loss.item():.4f}') # 测试模型 model.eval() # 切换到评估模式,关闭Dropout等训练特有的层 with torch.no_grad(): n_correct = 0 n_samples = 0 for inputs, labels in test_loader: inputs = inputs.to(device) labels = labels.to(device) outputs = model(inputs) _, predictions = torch.max(outputs, 1) n_samples += labels.shape[0] n_correct += (predictions == labels).sum().item() # 累加正确样本数 acc = 100 * n_correct / n_samples print(f'测试集准确率 = {acc:.2f}%')
额外优化建议
- 类别不平衡处理:如果数据集存在类别占比差异大的情况,可使用加权
CrossEntropyLoss或过采样/欠采样策略。 - 模型复杂度调整:当前模型较简单,可尝试增加隐藏层数量、神经元个数,或添加
Dropout层防止过拟合。 - 学习率调度:使用
torch.optim.lr_scheduler.StepLR等学习率衰减策略,在训练后期降低学习率以稳定收敛。 - 数据增强:若特征允许,可添加适当的扰动操作提升模型泛化能力。
内容的提问来源于stack exchange,提问作者spookysynth
相关产品推荐
相关产品推荐

