PyTorch自编码器无监督分类:损失值不更新问题求助
自编码器训练损失不变的问题排查与修复
核心问题分析
你的代码存在几个关键错误,直接导致训练过程中损失完全没有变化:
1. 优化步骤顺序完全颠倒
训练循环里的梯度更新逻辑完全错误:
- 你先执行
loss.backward()计算梯度,随后立刻调用opt.zero_grad()清空梯度,导致优化器执行step()时没有任何梯度可以用来更新模型参数,模型完全无法得到训练。 - 正确顺序应为:清零梯度 → 前向传播 → 计算损失 → 反向传播 → 优化器更新参数
2. Dummy目标的逻辑错误
dmmy=torch.nn.Linear(22,6)(txr) 这行代码存在两个问题:
- 临时创建的Linear层未加入优化器,导致dummy的值是固定不变的(仅计算一次后就不再更新)
- 自编码器的无监督训练核心是重构输入本身,你用随机线性变换的结果作为训练目标完全不符合无监督学习的逻辑,模型无法学到有意义的特征
3. 冗余与缺失代码
txr.requires_grad=True:输入数据无需开启梯度,模型参数会自动管理梯度属性__init__和forward函数中的torch.autograd是无效冗余代码,没有实际作用forward函数的retain_graph=True参数完全多余,无特殊多反向传播需求时无需设置- 缺失模型初始化代码
m = AE(),否则优化器无法获取模型参数
修正后的代码示例
import pandas as pd import torch import torch.nn as nn # 数据加载 x = pd.read_csv("<file.csv>") xray = x.to_numpy() tx = torch.from_numpy(xray) txr = tx.float() # 自编码器定义(含编码器+解码器,用于无监督重构训练) class AE(nn.Module): def __init__(self): super().__init__() # 编码器:输入→低维特征 self.L1 = nn.Linear(22, 20) self.E1 = nn.ELU(alpha=9) self.L2 = nn.Linear(20, 16) self.E2 = nn.ELU(alpha=9) self.L3 = nn.Linear(16, 12) self.E3 = nn.ELU(alpha=9) self.L4 = nn.Linear(12, 8) self.E4 = nn.ELU(alpha=9) self.L7 = nn.Linear(8, 6) self.E7 = nn.ELU(alpha=9) # 解码器:低维特征→重构输入 self.L8 = nn.Linear(6, 8) self.E8 = nn.ELU(alpha=9) self.L9 = nn.Linear(8, 12) self.E9 = nn.ELU(alpha=9) self.L10 = nn.Linear(12, 22) def forward(self, x: torch.Tensor): # 编码器路径 out = self.E1(self.L1(x)) out = self.E2(self.L2(out)) out = self.E3(self.L3(out)) out = self.E4(self.L4(out)) latent_feature = self.E7(self.L7(out)) # 解码器路径 recon_out = self.E8(self.L8(latent_feature)) recon_out = self.E9(self.L9(recon_out)) recon_x = self.L10(recon_out) return latent_feature, recon_x # 初始化组件 m = AE() recon_loss_fn = torch.nn.MSELoss() opt = torch.optim.SGD(m.parameters(), lr=0.01) epochs = 10 # 训练循环 for epoch in range(epochs): m.train() opt.zero_grad() # 先清空梯度 latent_feature, recon_x = m(txr) loss = recon_loss_fn(recon_x, txr) # 以输入本身为重构目标 loss.backward() # 计算梯度 opt.step() # 更新参数 print(f'Epoch {epoch+1}, loss: {loss.item():.4f}') print(f'First sample latent feature: {latent_feature[0]}') print('Done') # 训练后用低维特征生成Softmax输出 softmax = nn.Softmax(dim=1) ypred = softmax(latent_feature) print(f'First sample softmax output: {ypred[0]}')
额外说明
如果你的目标是无监督分类,标准流程是:
- 先用重构损失训练自编码器,让模型学习到输入数据的低维特征表示
- 提取编码器部分,结合Softmax层在少量标注数据上做半监督分类,或直接对低维特征做聚类实现无监督分类
内容的提问来源于stack exchange,提问作者Wick
相关产品推荐
相关产品推荐

