Mini-batch梯度下降训练鸢尾花模型精度偏低问题求助
问题分析与代码改进建议
你在鸢尾花数据集上用Mini-batch梯度下降训练模型时,精度卡在75-80%,同时损失和精度计算存在多处错误,以下是具体问题和改进方案:
一、数据加载错误
问题:训练集和测试集使用了同一个完整数据集,没有划分训练/测试子集,导致测试时模型在训练数据上评估,无法反映真实泛化能力。
改进:将数据集划分为训练集和测试集,分别构建DataLoader:
from sklearn.model_selection import train_test_split import torch.utils.data as data # 假设dataset是原始鸢尾花数据集 train_data, test_data = train_test_split(dataset, test_size=0.2, random_state=42, stratify=dataset.targets) batch_size = 10 train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False) # 测试集无需shuffle
二、训练循环的核心错误
1. 迭代次数与epoch混淆
问题:n_iters = 1000被当作epoch数,但1000个epoch对于鸢尾花数据集来说过多,且日志打印逻辑(epochs+steps)计算epoch编号错误。
2. 损失记录错误
问题:每次epoch只记录最后一个batch的损失,而非整个epoch的平均损失,导致损失曲线失真。
3. 标签格式不匹配CrossEntropyLoss要求
问题:transform_label返回的是形状为(N,1)的二维张量,但CrossEntropyLoss要求输入的标签是形状为(N,)的一维类别索引张量,会导致计算错误。
改进后的训练循环:
# 合理设置epoch数,鸢尾花数据集20-50个epoch足够 n_epochs = 30 LOSS = [] # 统一标签转换函数,放在外部避免重复定义 def transform_label(label_data): label_map = {"Iris-setosa": 0, "Iris-versicolor": 1, "Iris-virginica": 2} # 返回一维张量 return torch.tensor([label_map[label] for label in label_data], dtype=torch.long) for epoch in range(n_epochs): epoch_loss = 0.0 total_batches = 0 model.train() # 切换到训练模式 for inputs, labels in train_loader: optim.zero_grad() # 先清零梯度,避免累积 out = model(inputs) train_labels = transform_label(labels) l = loss(out, train_labels) l.backward() optim.step() epoch_loss += l.item() total_batches += 1 avg_epoch_loss = epoch_loss / total_batches LOSS.append(avg_epoch_loss) # 每5个epoch打印一次训练状态 if (epoch + 1) % 5 == 0: print(f"epoch: {epoch+1}/{n_epochs}, 平均损失: {avg_epoch_loss:.4f}")
三、精度计算函数的严重错误
问题:
number_correct未初始化,会抛出变量未定义错误;sum_acc未累加正确预测数,也没有返回最终精度;transform_label返回二维张量,与预测的一维索引形状不匹配;- 使用while循环逐个比较效率低下,可直接用张量操作简化。
改进后的精度计算函数:
def accuracy(model, test_loader): model.eval() # 切换到评估模式,关闭dropout/batchnorm等训练层行为 total_correct = 0 total_samples = 0 def transform_label(label_data): label_map = {"Iris-setosa": 0, "Iris-versicolor": 1, "Iris-virginica": 2} return torch.tensor([label_map[label] for label in label_data], dtype=torch.long) with torch.no_grad(): # 评估时无需计算梯度,节省内存和时间 for X_test, test_labels in test_loader: test_labels = transform_label(test_labels) x_label_pre = model(X_test) _, x_label_pre_hat = torch.max(x_label_pre, 1) # 直接用张量比较求和,高效简洁 correct = torch.sum(x_label_pre_hat == test_labels).item() total_correct += correct total_samples += len(X_test) return total_correct / total_samples # 返回最终准确率
四、模型与优化器的参数调整
问题:Adam优化器的学习率lr=0.01过大,会导致模型训练不稳定,难以收敛到最优解。
改进:
- 降低Adam的学习率至
0.001; - 模型结构对于鸢尾花数据集来说偏复杂,可适当简化(可选),但先修复上述错误后再调整。
调整后的优化器配置:
import torch.nn as nn lr = 0.001 model = NeuralNetwork() optim = torch.optim.Adam(model.parameters(), lr=lr) loss = torch.nn.CrossEntropyLoss() class NeuralNetwork(nn.Module): def __init__(self): super().__init__() self.linear_stack = nn.Sequential( nn.Linear(4, 64), # 减少隐藏层单元数,简化模型 nn.ReLU(), nn.Linear(64, 3), ) def forward(self, x): logits = self.linear_stack(x) return logits
关键总结
- 必须划分训练/测试集,确保评估的是模型泛化能力;
- 标签格式要匹配损失函数要求,CrossEntropyLoss需要一维类别索引;
- 训练时记录整个epoch的平均损失,评估时使用
torch.no_grad()和模型评估模式; - 调整学习率至合适范围,避免过大导致训练震荡。
内容的提问来源于stack exchange,提问作者rndnewbiee22
相关产品推荐
相关产品推荐

