为何训练数据为gen(x)、验证数据为x时,分类器训练Loss降但验证效果差?
问题描述
我构建了一个分类器SimpleModel:
- 当使用
input=torch.randn(100,1)、label=(input>0时为1,否则为0)训练时,分类器训练效果良好,验证准确率接近1。 - 但当使用
input=gen(torch.randn(100,1))、label=(input>0时为1,否则为0)(其中gen()为自定义生成模型)训练时,分类器训练效果不佳:train loss下降,但验证准确率始终未提升,最终约为0.5,同时validation loss上升。
相关代码
import torch from torch import nn from torch import optim import matplotlib.pyplot as plt class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.linear1=nn.Linear(1,1) self.sigmoid=nn.Sigmoid() def forward(self,x): x=self.linear1(x) x=self.sigmoid(x) return x class Gen(nn.Module): def __init__(self): super(Gen, self).__init__() self.linear1=nn.Linear(1,2) self.relu1=nn.ReLU() self.linear2=nn.Linear(2,1) def forward(self,x): x=self.linear1(x) x=self.relu1(x) x=self.linear2(x) return x model=SimpleModel() gen=Gen() criterion = nn.BCELoss() optimizer = optim.Adam(model.parameters(), lr=0.0001) loss_history=[] val_loss_history=[] acc_history=[] val_acc_history=[] for step in range(10000): z=torch.randn(100,1) x=gen(z) label=(x>0).float().reshape(-1,1) out=model(x) loss=criterion(out,label) optimizer.zero_grad() loss.backward() optimizer.step() acc=(out.round()==label).float().mean() val_x=torch.randn(100,1) val_label=(val_x>0).float().reshape(-1,1) val_out=model(val_x) val_loss=criterion(val_out,val_label) val_acc=(val_out.round()==val_label).float().mean() print(loss.item(), acc.item(), val_loss.item(), val_acc.item()) loss_history.append(loss.item()) val_loss_history.append(val_loss.item()) acc_history.append(acc.item()) val_acc_history.append(val_acc.item()) plt.figure() plt.plot(loss_history, label="train loss") plt.plot(val_loss_history, label="validation loss") plt.legend()
训练效果对比
- 正常训练效果:

- 使用gen数据训练效果:

问题原因分析
1. 训练集与验证集分布完全脱节
训练数据是gen(z)的输出,验证数据是直接的torch.randn(100,1),两者分布完全不同:
- 原始
torch.randn是对称的标准正态分布,正负样本均衡。 Gen模型的ReLU层会把线性变换后的负数值全部置0,再经过第二层线性变换后,输出x的分布会严重偏移,大概率出现正负样本失衡,甚至大部分样本集中在单侧区间。
分类器在这种偏移的训练分布上学到的规律,完全无法适配验证集的标准正态分布,最终只能随机猜测,准确率卡在0.5。
2. 输入与标签的线性关联被破坏
SimpleModel是单层线性+sigmoid模型,只能学习输入到标签的线性决策边界(判断输入是否大于0)。但Gen的ReLU层截断了负向信息,导致gen(z)的输出和原始输入不再是线性关系,甚至不同的z会对应相同的x值(比如多个负数被ReLU置0)。
此时分类器虽然能在训练集上拟合(loss下降),但学到的是Gen输出后的局部规律,完全不适用于验证集的原始分布,导致验证准确率无法提升。
3. 验证任务与训练任务完全不符
训练时分类器的任务是判断gen(z)的输出是否大于0,但验证时却要求它判断torch.randn是否大于0,相当于让模型完成两个完全无关的任务,自然无法得到合理的验证结果。
内容的提问来源于stack exchange,提问作者beginner
相关产品推荐
相关产品推荐

