Autoencoder 自编码器
压缩与重建的无监督学习 — 从自编码器到降维与去噪
- 自编码器通过编码器-解码器结构学习数据的压缩表示
- 瓶颈层(bottleneck)迫使网络学习最重要的特征
- 欠完备 vs 正则化:控制瓶颈大小 vs 添加训练约束
- 应用:降维、去噪、异常检测、数据生成
什么是自编码器?
自编码器(Autoencoder)是一种无监督学习的神经网络,它的目标非常简单: 把输入数据压缩成一个更小的表示(编码),然后再从这个压缩表示中重建出原始数据。 如果重建的结果和原始输入很接近,说明压缩保留了数据中最重要的信息。
生活类比:想象你看到一幅复杂的画,你需要用几句话把它描述给朋友。 朋友再根据你的描述画出一幅尽可能接近原作的画。你的"描述"就是编码(压缩),朋友的"还原"就是解码(重建)。 描述得越好,还原就越接近原画。
核心思想:自编码器由两部分组成 — 编码器(Encoder)将输入压缩为低维表示, 解码器(Decoder)从低维表示重建输入。 中间的低维表示称为瓶颈层(Bottleneck),它的维度远小于输入维度。
自编码器结构
3 维 → 编码器
隐藏层 → 瓶颈层
2 维 → 解码器
隐藏层 → 输出层
3 维
输入 → 压缩 → 瓶颈(信息瓶颈) → 重建 → 输出 ≈ 输入
目标:最小化重建误差 L = ||x - x̂||²
📊 编码器 (Encoder) — Excel 手推
我们用一个简单的自编码器:3 维输入 → 2 维瓶颈。 编码器执行线性变换 h = Wenc · x + benc,将 3 维输入压缩为 2 维。 点击单元格可以编辑输入值!
| 输入 x1 | 输入 x2 | 输入 x3 | h = W·x + b | sigmoid 激活 | |
|---|---|---|---|---|---|
| 瓶颈 h1 | 1.0 | 0.5 | 0.8 | 0.92 | 0.715 |
| 瓶颈 h2 | 1.0 | 0.5 | 0.8 | 0.66 | 0.659 |
| 权重说明 | W_enc = [[0.3, 0.5, 0.2], [0.2, 0.3, 0.4]], b_enc = [0.1, 0.1] | W·x + b | σ(sum) | ||
💡 观察:编码器将 3 维输入 [x₁, x₂, x₃] 压缩为 2 维 [h₁, h₂]。 权重矩阵 W_enc 的形状是 2×3,这就是"压缩"的关键 — 用更少的维度表示数据。 sigmoid 激活函数将输出限制在 (0, 1) 范围内。
📊 解码器 (Decoder) — Excel 手推
解码器从 2 维瓶颈重建回 3 维输出: x̂ = Wdec · h + bdec。 下面展示解码过程和重建误差的计算。
| 瓶颈 h1 | 瓶颈 h2 | x̂ = W·h + b | 原始输入 | 误差 (x - x̂)² | |
|---|---|---|---|---|---|
| 重建 x̂1 | 0.715 | 0.659 | 0.876 | 1.000 | 0.015 |
| 重建 x̂2 | 0.715 | 0.659 | 0.589 | 0.500 | 0.008 |
| 重建 x̂3 | 0.715 | 0.659 | 0.724 | 0.800 | 0.006 |
| 权重说明 | W_dec = [[0.4, 0.3], [0.2, 0.5], [0.3, 0.2]], b_dec = [0.1, 0.05, 0.1] | W·h + b | (x - x̂)² | ||
重建误差 (MSE Loss)
MSE 越小,说明重建效果越好。训练的目标就是最小化这个损失函数。
📋 重建误差分析
下面是输入与重建的完整对比。通过对比可以直观看到自编码器的重建质量。
| 维度 1 | 维度 2 | 维度 3 | MSE | |
|---|---|---|---|---|
| 原始输入 x | 1.000 | 0.500 | 0.800 | |
| 重建输出 x̂ | 0.876 | 0.589 | 0.724 | 0.010 |
| 绝对误差 |x - x̂| | 0.124 | 0.089 | 0.076 |
信息损失
压缩不可避免地丢失一些信息,瓶颈层越小,损失越大
关键特征
瓶颈层迫使网络学习数据中最重要的特征表示
压缩与质量
压缩比越大,模型越小但重建质量越差,需要权衡
🎮 互动实验:压缩比滑块
拖动滑块调整瓶颈层维度(1~3),观察重建质量如何变化。 瓶颈层越小,压缩比越大,重建误差通常越高。
💡 观察:
- • 瓶颈维度 = 3:等于输入维度,几乎无压缩,重建误差最小
- • 瓶颈维度 = 2:适度压缩,重建有一定误差但保留了主要信息
- • 瓶颈维度 = 1:高度压缩,信息损失严重,重建误差最大
- • 这体现了信息瓶颈理论:压缩越狠,丢失越多
当前配置
💻 PyTorch 代码
import torch
import torch.nn as nn
class Autoencoder(nn.Module):
def __init__(self, input_dim=784, bottleneck_dim=32):
super().__init__()
# 编码器:逐步压缩
self.encoder = nn.Sequential(
nn.Linear(input_dim, 256),
nn.ReLU(),
nn.Linear(256, 64),
nn.ReLU(),
nn.Linear(64, bottleneck_dim),
)
# 解码器:逐步重建
self.decoder = nn.Sequential(
nn.Linear(bottleneck_dim, 64),
nn.ReLU(),
nn.Linear(64, 256),
nn.ReLU(),
nn.Linear(256, input_dim),
nn.Sigmoid(), # 输出归一化到 [0,1]
)
def forward(self, x):
z = self.encoder(x) # 编码 → 瓶颈表示
x_hat = self.decoder(z) # 解码 → 重建
return x_hat
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 加载 MNIST 数据集
transform = transforms.ToTensor()
train_data = datasets.MNIST('./data', train=True,
download=True, transform=transform)
loader = DataLoader(train_data, batch_size=128, shuffle=True)
# 初始化模型
model = Autoencoder(input_dim=784, bottleneck_dim=32)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.MSELoss()
# 训练循环
for epoch in range(20):
total_loss = 0
for images, _ in loader: # 无监督,不需要标签 _
x = images.view(-1, 784) # 展平为 784 维向量
x_hat = model(x) # 前向传播
loss = criterion(x_hat, x) # 重建误差
optimizer.zero_grad()
loss.backward() # 反向传播
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss/len(loader):.4f}")