4.3 自编码器:压缩与重建的双程管道


文档摘要

4.3 自编码器:压缩与重建的双程管道 本节摘要:自编码器是第一个不依赖标签的家族:编码器把输入压进低维瓶颈,解码器再还原回去,损失是输入与重建的差。因为没人告诉它"什么是重要的",它必须自己学会在瓶颈里保留最关键的信息——这个被逼出来的表示就是它的价值。本节在 CIFAR-10 图像上搭卷积自编码器,演示重建、去噪两个用法,并把重建误差用成异常检测的分数。瓶颈的宽窄是它唯一的灵魂旋钮。 读完你应当能做到 阅读完本节,你应当能够: 说清自编码器"无监督学表示"的机理与瓶颈的作用; 搭一条卷积编码、转置卷积解码的双程管道并训练; 用同一模型完成去噪任务,理解噪声输入下的训练变式; 把重建误差用作异常分数,实现一个最小异常检测器。 瓶颈:被逼出来的表示 有标签的学习里,损失直接指向任务目标;

4.3 自编码器:压缩与重建的双程管道

本节摘要:自编码器是第一个不依赖标签的家族:编码器把输入压进低维瓶颈,解码器再还原回去,损失是输入与重建的差。因为没人告诉它"什么是重要的",它必须自己学会在瓶颈里保留最关键的信息——这个被逼出来的表示就是它的价值。本节在 CIFAR-10 图像上搭卷积自编码器,演示重建、去噪两个用法,并把重建误差用成异常检测的分数。瓶颈的宽窄是它唯一的灵魂旋钮。

读完你应当能做到

阅读完本节,你应当能够:

  1. 说清自编码器"无监督学表示"的机理与瓶颈的作用;
  2. 搭一条卷积编码、转置卷积解码的双程管道并训练;
  3. 用同一模型完成去噪任务,理解噪声输入下的训练变式;
  4. 把重建误差用作异常分数,实现一个最小异常检测器。

瓶颈:被逼出来的表示

有标签的学习里,损失直接指向任务目标;没标签时,自编码器的替代目标是"重建自己"。听起来像恒等映射的把戏——但瓶颈挡在中间:中间层维度远小于输入维度,恒等映射被物理性地排除,网络必须学会"哪些信息压得掉、哪些必须保住"。压得掉的是冗余(相邻像素的相关性、背景的平坦区域),必须保住的是结构(轮廓、纹理、类别线索)。这个在压缩约束下学到的中间表示,就是自编码器的产出物——它可以直接拿去做下游特征的输入、可视化的降维、或异常分数的计算源。

图 18 双程管道:编码压缩、瓶颈、解码重建

图 18 双程管道:编码压缩、瓶颈、解码重建

搭建与训练:卷积版双程管道

图像数据的自编码器用卷积搭:编码器卷积加池化逐级收窄,解码器用上采样加卷积逐级还原。CIFAR-10 上实现:

import tensorflow as tf import numpy as np (xtr, _), (xte, _) = tf.keras.datasets.cifar10.load_data() xtr = xtr[:8000].astype("float32") / 255.0 xte = xte[:2000].astype("float32") / 255.0 # 注意:标签全部弃用——自编码器不看标签 encoder = tf.keras.Sequential([ tf.keras.layers.Input(shape=(32, 32, 3)), tf.keras.layers.Conv2D(32, 3, padding="same", activation="relu"), tf.keras.layers.MaxPooling2D(2), # 32 到 16 tf.keras.layers.Conv2D(64, 3, padding="same", activation="relu"), tf.keras.layers.MaxPooling2D(2), # 16 到 8 tf.keras.layers.Conv2D(8, 3, padding="same", activation="relu"), ]) # 瓶颈:8 乘 8 乘 8 decoder = tf.keras.Sequential([ tf.keras.layers.Input(shape=(8, 8, 8)), tf.keras.layers.Conv2D(64, 3, padding="same", activation="relu"), tf.keras.layers.UpSampling2D(2), # 8 到 16 tf.keras.layers.Conv2D(32, 3, padding="same", activation="relu"), tf.keras.layers.UpSampling2D(2), # 16 到 32 tf.keras.layers.Conv2D(3, 3, padding="same", activation="sigmoid"), ]) # sigmoid 配 0 到 1 像素 auto = tf.keras.Sequential([encoder, decoder]) auto.compile(optimizer="adam", loss="mse") h = auto.fit(xtr, xtr, # 输入即目标:x 到 x validation_data=(xte, xte), epochs=10, batch_size=64, verbose=0) print(f"recon mse {h.history['val_loss'][-1]:.5f}") # 参考输出:recon mse 0.00421 # 重建均方误差千分之四量级——模糊但轮廓可辨的重建

两个训练细节:目标就是输入本身(fit(xtr, xtr)),这是"自监督"的字面实现;末层激活选 sigmoid 是因为像素归一在 0 到 1,若没归一则用线性激活配相应损失。瓶颈越窄,表示越抽象但重建越糊——8 乘 8 乘 8 是演示值,实用中按下游用途调。

变式一:去噪自编码器

把训练目标从"重建自己"改成"从加噪版本还原干净版",模型的表示会更强韧:它必须从噪声里猜出结构,而不是照抄输入。实现上只改数据供给,模型不变:

noise = np.random.randn(*xtr.shape).astype("float32") * 0.1 xtr_noisy = np.clip(xtr + noise, 0.0, 1.0) noise_te = np.random.randn(*xte.shape).astype("float32") * 0.1 xte_noisy = np.clip(xte + noise_te, 0.0, 1.0) auto.fit(xtr_noisy, xtr, # 噪声输入,干净目标 validation_data=(xte_noisy, xte), epochs=10, batch_size=64, verbose=0) denoised = auto.predict(xte_noisy[:4], verbose=0) mse_noisy = np.mean((xte_noisy[:4] - xte[:4]) ** 2) mse_deno = np.mean((denoised - xte[:4]) ** 2) print(f"noisy mse {mse_noisy:.4f}, denoised mse {mse_deno:.4f}") # 参考输出:noisy mse 0.0100, denoised mse 0.0047 # 重建误差比直接对比干净图还小一半——噪声被结构知识过滤掉了

变式二:重建误差当异常分数

异常检测的逻辑链:模型只见过正常样本,遇到异常输入时"不会重建"——误差显著偏高。分数现成,阈值靠正常验证集定:

# 只用正常样本训练的示意:假设类别 0(飞机)是"正常" (xtr_all, ytr_all), _ = tf.keras.datasets.cifar10.load_data() x_normal = xtr_all[ytr_all.reshape(-1) == 0][:3000].astype("float32") / 255.0 x_anomaly = xtr_all[ytr_all.reshape(-1) == 1][:500].astype("float32") / 255.0 ae = tf.keras.Sequential([encoder, decoder]) ae.compile(optimizer="adam", loss="mse") ae.fit(x_normal, x_normal, epochs=10, batch_size=64, verbose=0) score_normal = np.mean((ae.predict(x_normal[:500], verbose=0) - x_normal[:500]) ** 2, axis=(1, 2, 3)) score_anomaly = np.mean((ae.predict(x_anomaly, verbose=0) - x_anomaly) ** 2, axis=(1, 2, 3)) threshold = np.percentile(score_normal, 95) print(f"normal mean {score_normal.mean():.5f}, anomaly mean {score_anomaly.mean():.5f}") print(f"alert rate on anomaly set: {(score_anomaly > threshold).mean():.2f}") # 参考输出: # normal mean 0.00385, anomaly mean 0.00602 # alert rate on anomaly set: 0.68 # 异常集的均分接近正常集的两倍,近七成异常样本触发告警 # 工业质检、流量审计的异常检测就是这个骨架

瓶颈的取舍与进阶

瓶颈设计是唯一的灵魂决策,它由下游用途倒推:做异常检测,瓶颈要足够窄,宽了恒等映射近似可行、异常也能被"背"出来;做下游特征,瓶颈要保留任务相关结构,太窄信息不够。去噪训练是缓解"瓶颈太宽照样恒等"的实用技巧——即使瓶颈不窄,噪声也逼着模型学结构。进阶方向两支:变分自编码器把瓶颈变成概率分布,生成能力质变;与 4.4 的 GAN 结合(对抗训练替代像素损失)能让重建更锐利。

⚠️ 常见坑:异常检测场景用全类别数据训练自编码器,"异常"也混进了训练集,模型把异常也学会了重建——分数失去区分度。训练集必须只含正常样本,这是任务成立的前提。

💡 关键直觉:自编码器的价值不在重建,而在"为了重建必须学到的中间表示"。重建是训练手段,表示才是产品。

本节要点回顾

  • 瓶颈机理:压缩约束排除恒等映射,逼出结构化表示。
  • 双程管道:卷积编码收窄、上采样解码还原,输入即目标。
  • 去噪变式:噪声进、干净出,表示更强韧,缓解宽瓶颈问题。
  • 异常分数:只训正常样本,重建误差即异常度,阈值从正常集分位数定。
  • 瓶颈取舍:窄了抽象、宽了失守,由下游用途倒推。

下节进入对抗的家族:生成对抗网络。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U