采用逐层预训练的深度去噪自编码器损失发散问题求助
解决深度自编码器预训练损失发散的方案
1. 归一化编码特征,控制输入范围
前一层编码后的ReLU输出未做归一化,数值范围会随层数加深逐渐扩大,直接导致MSE损失被放大。在传递data_in时,需将编码结果缩放到0-1区间:
with torch.no_grad(): data_in = ae.encode(data_in) # 归一化到0-1区间,稳定后续层输入范围 data_in = (data_in - data_in.min()) / (data_in.max() - data_in.min())
2. 优化中间层激活函数与参数初始化
- 中间层解码器使用ReLU易出现神经元死亡问题,替换为LeakyReLU保证梯度流通;同时对线性层做He初始化(适配ReLU类激活函数),避免初始输出数值过大:
修改Autoencoder类:
class Autoencoder(nn.Module): def __init__(self, input_dim, enc_dim, last_layer=True): super(Autoencoder, self).__init__() self.last_layer = last_layer self.input_dim = input_dim self.enc_dim = enc_dim self.encoder = nn.Linear(input_dim, enc_dim) self.decoder = nn.Linear(enc_dim, input_dim) # 替换为LeakyReLU,避免神经元死亡 self.enc_activation = nn.LeakyReLU(0.2) if self.last_layer: self.dec_act = nn.Sigmoid() else: # 中间层解码器同步使用LeakyReLU self.dec_act = nn.LeakyReLU(0.2) # He初始化线性层参数,适配LeakyReLU nn.init.kaiming_normal_(self.encoder.weight, mode='fan_in', nonlinearity='leaky_relu') nn.init.zeros_(self.encoder.bias) nn.init.kaiming_normal_(self.decoder.weight, mode='fan_in', nonlinearity='leaky_relu') nn.init.zeros_(self.decoder.bias)
3. 针对后续层降低学习率
深层特征的学习需要更精细的参数更新,固定0.001的学习率易导致震荡发散。修改fit函数支持自定义学习率:
def fit(epochs, model, X_train, lr=0.001): print(X_train.shape) train_loader = torch.utils.data.DataLoader(X_train, batch_size=20, shuffle=True) criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 其余逻辑保持不变
预训练时逐层降低学习率:
data_in = X_train hidden_layers = [512, 256, 128] deep_ae = DeepAutoEncoder(X_train.shape[1], hidden_layers) # 第一层用0.001,后续层用更小的0.0001 lrs = [0.001, 0.0001, 0.0001] for idx, ae in enumerate(deep_ae.model): fit(5, ae, data_in, lr=lrs[idx]) # 增加训练轮数到5轮,确保收敛 with torch.no_grad(): data_in = ae.encode(data_in) data_in = (data_in - data_in.min()) / (data_in.max() - data_in.min())
4. 调整噪声强度,匹配输入特征范围
当前对所有层使用相同噪声强度,但中间层特征的数值范围和像素(0-1)差异大,噪声过强会破坏特征结构。修改add_noise逻辑,根据输入自身标准差动态调整噪声:
def add_noise(x, mean=0, std_scale=0.1): # 用输入自身的标准差乘以缩放因子作为噪声强度 noise_std = x.std() * std_scale return x + torch.randn_like(x) * noise_std + mean
或者仅在第一层(像素数据)添加固定噪声,中间层预训练时暂时关闭噪声——去噪逻辑可在整个深度模型联合训练阶段再启用。
5. 增加预训练轮数,确保每层收敛
当前每层仅训练2轮,不足以让模型稳定收敛。将每层训练轮数增加到5-10轮,观察损失曲线,直到损失不再明显下降再进入下一层训练。
内容的提问来源于stack exchange,提问作者JayJona
相关产品推荐
相关产品推荐

