线性FFNN预测求和任务快速达100%准确率的原因分析
分析FFNN快速达到100%训练准确率的原因及验证方法
一、模型快速收敛的核心原因
- 任务复杂度极低:两个整数求和是纯线性任务,哪怕是没有隐藏层的单一线性层都能完美拟合。你的模型配备了两层隐藏层,拟合能力远超任务需求,自然能快速收敛。
- 数据集完全覆盖所有可能样本:
np.random.randint(-10,10)生成的是-10到9的整数,所有可能的输入组合仅20×20=400种。2000个训练样本已经重复覆盖所有组合多次,模型很容易直接“记住”所有样本的映射关系,甚至不需要学习通用求和逻辑。 - 准确率判定条件宽松:你用
|预测值-真实值| < 1作为正确标准,而真实标签是整数,只要预测值落在真实值的相邻整数区间内(比如真实值为3,预测2.1或3.9都算对),这种宽松的判定加速了准确率达标。
二、验证模型是否真正学会求和逻辑
要区分模型是“死记硬背”还是“学会规则”,可以做以下测试:
- 测试非整数输入:比如输入
[2.5, 3.7],如果模型预测接近6.2,说明它学到了通用的加法逻辑,而非仅仅记住整数组合。 - 简化模型对比:去掉隐藏层,只用
nn.Linear(2,1)训练,如果依然能达到100%准确率,证明任务本身是线性的,模型确实学到了线性求和关系。 - 查看输出层参数:如果模型学会求和,输出层的权重应该接近
[1, 1],偏置接近0。可以打印net.output.weight和net.output.bias验证。
三、代码中的潜在问题
- 测试准确率存储错误:原代码中
testacc被反复赋值为标量,最终返回的testAcc仅保留最后一轮的测试准确率,应该改为testacc.append(test_acc.item())来记录每一轮结果。 - 数据类型转换冗余:可以在创建
TensorDataset时直接转换为float类型,避免训练循环中重复转换。 - SGD学习率偏低:任务简单,把学习率从0.001调高到0.01或0.1,能进一步加快收敛速度。
改进后的代码示例
import torch import torch.nn as nn import torch.nn.functional as F from torch.utils.data import DataLoader,TensorDataset from sklearn.model_selection import train_test_split import numpy as np # 创建数据集 data = [] labels = [] datasetAmount = 2000 for i in range(datasetAmount): x = np.random.randint(-10, 10) y = np.random.randint(-10,10) data.append([x, y]) labels.append(x + y) data_np = np.array(data) labels_np = np.array(labels).reshape(-1,1) # 划分数据集并直接转换为float类型 train_data, test_data, train_labels, test_labels = train_test_split(data_np, labels_np, train_size=0.9) train_data = TensorDataset(torch.tensor(train_data).float(), torch.tensor(train_labels).float()) test_data = TensorDataset(torch.tensor(test_data).float(), torch.tensor(test_labels).float()) batchsize = 20 train_loader = DataLoader(train_data, batch_size=batchsize, shuffle=True, drop_last=True) test_loader = DataLoader(test_data, batch_size=len(test_data)) # 模型定义 def createModel(): class myModel(nn.Module): def __init__(self): super().__init__() self.input = nn.Linear(2,8) self.fc1 = nn.Linear(8,8) self.output = nn.Linear(8,1) def forward(self,x): x = F.relu(self.input(x)) x = F.relu(self.fc1(x)) return self.output(x) net = myModel() lossfun = nn.MSELoss() optimizer = torch.optim.SGD(net.parameters(), lr=0.01) # 调高学习率 return net, lossfun, optimizer def trainModel(): numepochs = 100 net, lossfun, optimizer = createModel() losses = torch.zeros(numepochs) trainacc = [] testacc = [] for epochi in range(numepochs): batchLoss = [] # 训练循环 for X, y in train_loader: yHat = net(X) loss = lossfun(yHat, y) batchLoss.append(loss.item()) optimizer.zero_grad() loss.backward() optimizer.step() losses[epochi] = np.mean(batchLoss) # 计算训练准确率 with torch.no_grad(): train_preds = [] train_targets = [] for X, y in train_loader: train_preds.append(net(X)) train_targets.append(y) train_preds = torch.cat(train_preds) train_targets = torch.cat(train_targets) train_acc = 100 * torch.mean((torch.abs(train_preds - train_targets) < 1).float()) trainacc.append(train_acc.item()) # 计算测试准确率(修复为列表存储) with torch.no_grad(): X, y = next(iter(test_loader)) yHat = net(X) test_acc = 100 * torch.mean((torch.abs(yHat - y) < 1).float()) testacc.append(test_acc.item()) return trainacc, testacc, losses, net # 训练并验证 trainAcc, testAcc, losses, net = trainModel() # 验证模型是否学到求和逻辑:打印输出层参数 print("输出层权重:", net.output.weight.detach().numpy()) print("输出层偏置:", net.output.bias.detach().numpy()) # 测试非整数输入 test_input = torch.tensor([[2.5, 3.7], [-5.2, 8.9], [9.9, -9.9]]).float() with torch.no_grad(): print("非整数输入预测结果:", net(test_input).detach().numpy()) print("真实求和结果:", [[6.2], [3.7], [0.0]])
内容的提问来源于stack exchange,提问作者Joey
相关产品推荐
相关产品推荐

