用自编码器重建误差抓异常:deep-learning-with-python-notebooks 的检测基线搭法
【免费下载链接】deep-learning-with-python-notebooksJupyter notebooks for the code samples of the book "Deep Learning with Python"项目地址: https://gitcode.com/gh_mirrors/de/deep-learning-with-python-notebooks
测试集上 F1 好看,上线后天天误报——异常样本少到几乎没得学,监督分类器在这种数据上天然吃亏。如果你的数据特征是"正常样本一大把、异常几乎没有标签",自编码器(Autoencoder)是最省事的基线:它只学正常数据的长什么样,异常样本因为没学过,重建出来必然"像不像"。这个仓库是《Deep Learning with Python》官方书的配套 notebook,VAE 章节里那套 encoder–decoder 的 Keras 实现,改几行就能挪去做异常检测。
为什么是 Autoencoder,先过一遍选型
结论先说:异常类型不可穷举时,分类器要求你提前知道"有哪些异常",而 Autoencoder 只要求你能圈出"什么是正常"。异常分数就是逐样本重建误差,阈值可以直接按分位数解释,调参空间小、行为可预期。
| 方法 | 适用场景 | 本项目覆盖 |
|---|---|---|
| 监督分类 | 异常有标签且类别已知 | 不覆盖 |
| 统计方法(z-score / Isolation Forest) | 低维表格数据、在线打分要快 | 不覆盖 |
| Autoencoder / VAE | 图像、时序、embedding 等高维数据 | 覆盖,有完整 Keras 实现 |
| GAN | 需要判别器二分类视角 | 覆盖(chapter12_part05_gans.ipynb),但训练不稳 |
项目里去哪找代码
- second_edition/chapter12_part04_variational-autoencoders.ipynb:核心参考。VAE 编码器、Sampler 采样层、解码器、带 reconstruction loss + KL 的自定义 train_step 全在这里,末尾还有从 2D 潜空间采样图像的完整流程。
- chapter17_image-generation.ipynb:第三版代码,同一套 VAE 之外还包含 U-Net 去噪自编码器(diffusion 部分),"去噪 AE"这个结构正好对应异常检测里的降噪式训练。
- first_edition/8.4-generating-images-with-vaes.ipynb:第一版写法更简单,第一次跑通用它热身。
核心流程:四步搭出检测基线
只用正常样本训练,异常一个别掺
模型学到的就是"正常的分布",掺进异常它会把异常也学会,误差差距直接抹平。
(x, y) = mnist.load_data() normal = x[x / 255.0, y < 5] # 挑一部分类别当"正常"踩坑提示:不确定数据里有没有污染时,先抽 20 张肉眼过一遍。
异常分数 = 逐样本重建误差
误差总量和特征数成正比,不除以元素数就没法跨数据集比较。逐样本取 mean squared error,就得到和样本一一对应的分数向量。
score = ops.mean(ops.square(x - vae(x)), axis=(-1, -2, -3))踩坑提示:VAE 输出是重参数化采样后的重建,同一条数据两次前向分数略有不同,打分时统一开 eval 模式。
阈值定在留出正常集的 95 分位
没有异常标签时,阈值只能从正常数据的误差分布上界估计。留一小撮训练时没见过的正常数据算分数,取 95 分位当起点,再按业务能忍的误报量上下挪。
thresh = np.percentile(scores_holdout, 95) flags = score > thresh踩坑提示:训练集和打分集必须是同一分布、同一量纲,换了预处理尺度,旧阈值直接作废。
异常也被重建得太好时,换去噪式训练
给输入加噪声、强迫模型输出干净的原图。模型被迫学"结构"而不是"背像素",异常样本因为没有对应的干净参照,重建会更差,分差被拉开。
noisy = x + 0.1 * ops.random.normal(shape=ops.shape(x)) model.compile(optimizer="adam", loss="mse") model.fit(noisy, x, epochs=20)踩坑提示:噪声系数从 0.1 起步,加太大会把正常样本也重建糊掉。
关键参数怎么定
| 决策项 | 推荐起点 | 偏离的后果 |
|---|---|---|
| 潜空间维度 latent_dim | 2~8 | 太大:异常也被无损重建,区分度消失 |
| 网络瓶颈宽度 | 低于数据固有维度(如 7x7x64) | 过参数化 = 记住异常样本 |
| 损失函数 | 连续数据 MSE,0~1 图像 BCE | 不匹配时误差分布偏斜,阈值难定 |
| 阈值分位 | 95 分位 | 调低 → 误报暴涨;调高 → 漏报 |
| 优化器 | Adam,默认学习率 | 学习率过高 → 重建曲线抖动,阈值跟着漂 |
一张表的原则:先动瓶颈宽度,再动分位数,最后才动优化器。
落地避坑
- 现象:异常和正常分数挤在一起。原因:训练集混了异常,或瓶颈太宽模型在硬背。处理:清洗训练集、砍小 latent_dim 和中间层宽度。
- 现象:验证集阈值稳定,上线后误报率翻倍。原因:数据分布漂移,正常样本的误差整体上移。处理:用滚动窗口定期重算分位数,并把阈值本身的漂移纳入告警。
- 现象:分数分布长尾,个别样本把 95 分位顶飞。原因:MSE 对极端特征敏感。处理:换 MAE,或对分数取 log 后再定分位。
- 现象:VAE 重建图模糊,看不出谁异常。原因:KL 项把表示平滑掉了。处理:纯检测任务去掉 KL 用标准 Autoencoder,判断依据看误差曲线而不是肉眼看图。
延伸
判别器视角的另一条路线可以对照读:second_edition/chapter12_part05_gans.ipynb;卷积骨干的写法参考 chapter08_image-classification.ipynb,换自己的数据时大概率要改的就是这两块。
把 VAE notebook 跑通之后,把输入换成你的业务正常样本,先画一张重建误差直方图——分布形态比任何理论都更能告诉你瓶颈宽不宽、阈值能不能定。
【免费下载链接】deep-learning-with-python-notebooksJupyter notebooks for the code samples of the book "Deep Learning with Python"项目地址: https://gitcode.com/gh_mirrors/de/deep-learning-with-python-notebooks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考