用于时间序列异常检测的卷积自编码器(CAE)持续过拟合,寻求成因排查与优化建议
时间序列异常检测卷积自编码器过拟合排查与优化建议
现有实现基础
模型架构
class CAE(nn.Module): def __init__(self): super(CAE, self).__init__() self.encoder = Encoder() self.decoder = Decoder() self.encoder.to(get_device()) self.decoder.to(get_device()) def forward(self, x): x = self.encoder(x) x = self.decoder(x) return x class Encoder(nn.Module): def __init__(self): super(Encoder, self).__init__() self.conv_enc = nn.Sequential( nn.AlphaDropout(p=0.5), nn.Conv1d( in_channels=1, out_channels=16, kernel_size=5, stride=2, padding=0 ), nn.SELU(), nn.AlphaDropout(p=0.5), nn.Conv1d( in_channels=16, out_channels=8, kernel_size=5, stride=2, padding=0 ), nn.SELU() ) self.conv_enc.apply(self.init_weights) def init_weights(self, m): if type(m) == nn.Conv1d or type(m) == nn.ConvTranspose1d: torch.nn.init.xavier_normal_(m.weight) m.bias.data.fill_(0.01) def forward(self, x): x = self.conv_enc(x) return x class Decoder(nn.Module): def __init__(self): super(Decoder, self).__init__() self.conv_dec = nn.Sequential( nn.ConvTranspose1d( in_channels=8, out_channels=16, kernel_size=5, stride=2, padding=0 ), nn.SELU(), #nn.AlphaDropout(p=0.5), nn.ConvTranspose1d( in_channels=16, out_channels=1, kernel_size=6, stride=2, padding=0 ) ) self.conv_dec.apply(self.init_weights) def init_weights(self, m): if type(m) == nn.Conv1d or type(m) == nn.ConvTranspose1d: torch.nn.init.xavier_normal_(m.weight) m.bias.data.fill_(0.01) def forward(self, x): x = self.conv_dec(x) return x
训练配置
- 优化器:学习率为0.0001的AdamW
- 学习率调度器:OneCycleLR
- 数据预处理:采用MinMaxScaler完成数据归一化
- 损失函数:L1Loss
- 批大小:250
- 训练集规模:12000个样本
- 初始输入序列长度为800,后续将输入长度调整为140重新训练
已尝试的优化动作
- 数据增强
- 调整输入向量长度
- 修改dropout率
执行完上述所有调整后,模型的过拟合问题仍未解决,表现为训练集损失持续下降,验证集损失进入平台甚至反弹,训练准确率持续走高但验证集准确率停滞,调整输入长度后该现象未得到本质改善。
过拟合问题核心排查方向
1. 模型结构层面的问题
- Dropout放置位置失效:当前将AlphaDropout放在卷积层最前端、直接作用于原始输入的设计,会在模型提取时序特征前就随机打散连续的时间序列信号,既破坏了时序特征的连续性,也无法起到应有的正则化效果。
- Latent空间维度过高,模型记忆能力过强:输入长度800时,经过两层stride=2的卷积压缩后,编码输出的 latent 维度可达千级;即使将输入长度缩短为140,latent维度仍有数百级。对仅12000个样本的训练集来说,过高的 latent 维度意味着模型可以直接记忆每个训练样本的细节,不需要学习可泛化的正常时序模式,自然会出现过拟合。
- 正则化分布失衡:编码端设置了两个p=0.5的AlphaDropout,解码端完全移除了Dropout,相当于编码阶段做了过度的随机扰动,解码端却可以不受约束地拟合训练集的误差分布,进一步放大过拟合效应。
- Dropout强度不匹配:SELU激活搭配AlphaDropout的自归一化网络设计,推荐的Dropout率通常不超过0.2,当前使用的0.5失活率过强,会直接导致模型训练不稳定,无法收敛到泛化性好的状态。
2. 训练流程层面的问题
- 数据集划分方式错误:时间序列任务如果采用随机打乱、随机切分的方式构建训练集和验证集,会导致两个集合的样本来自完全重叠的时间窗口,分布没有本质差异。这种场景下观察到的“低验证损失”其实是假象,一旦换到独立时间窗口的样本上测试,性能会直接暴跌,很容易被误判为过拟合。
- 训练集纯度不足:CAE用于异常检测的核心逻辑是仅在纯正常样本上训练,让模型学会重构正常时序的模式。如果训练集中混入了带异常的样本,模型会同步学习异常模式的重构方法,后续检测时就会把本该识别为异常的样本也生成很小的重构误差,相当于训练方向出现偏差。
- 缺少训练早停机制:如果没有设置基于验证集性能的早停规则,会一直训练到模型把训练集的所有细节都记住、训练损失趋近于零才停止,必然出现过拟合。
可直接落地的优化调整方案
- 调整Dropout的位置与强度:把原来放置在输入端的AlphaDropout移到每个卷积/转置卷积层的激活函数之后,Dropout率从0.5降到0.15;同时在解码端每个卷积层的激活后加上同比例的AlphaDropout,让正则化在整个网络中均衡分布。
- 压缩Latent空间维度:在编码器的最后一层卷积后增加全局平均池化操作,把每个通道的时间维度压缩为1,不管输入序列长度是800还是140,最终Latent维度固定为8,直接降低模型的记忆容量,逼模型学习通用的正常时序特征,而非记忆单个样本的细节。
- 增加严格的早停规则:以验证集的重构损失最低值为判断标准,连续10个epoch验证损失没有下降就终止训练,避免过度拟合训练集细节。
- 修正数据集划分逻辑:严格按时间顺序切分数据集,将时间轴前70%的正常样本划分为训练集,中间20%的样本划分为验证集,最后10%带标注的样本划分为测试集,不要随机打乱样本顺序做切分。
- 校验训练集样本纯度:确保训练集中99%以上的样本为无异常的正常时序片段,剔除混入的异常样本,保证模型的训练目标正确。
内容的提问来源于stack exchange,提问作者YvanEhtNioj
相关产品推荐
相关产品推荐

