首页/知识库/自编码器 Autoencoder:压缩与重建

自编码器 Autoencoder:压缩与重建

✍️ AI Master📅 创建 2026-04-12📖 18 min 阅读
💡

文章摘要

从编码到解码,理解自编码器如何学习数据的高效表示

1自编码器基础架构

自编码器(Autoencoder)是一类通过无监督学习来学习数据高效表示的神经网络。其核心思想非常直观:让网络先压缩输入,再尝试重建它。整个架构由编码器(Encoder)和解码器(Decoder)两部分组成,中间通过一个低维的瓶颈层(Bottleneck)连接。

编码器将高维输入 x 映射到隐表示 z = f(x),维度通常远小于输入;解码器则从 z 重建输出 x' = g(z),目标是使 x' 尽可能接近 x。损失函数一般采用均方误差 MSE 或交叉熵,衡量重建质量。自编码器不依赖标签,而是从数据本身的分布中学习,因此属于无监督学习范式。

与 PCA 等线性降维方法不同,自编码器可以捕捉非线性结构。当激活函数采用 ReLU 或 Sigmoid 时,编码器学习到的是一种非线性流形上的坐标变换。这也使得自编码器在图像去噪、异常检测等任务中展现出独特优势。

python
import torch
import torch.nn as nn

class Autoencoder(nn.Module):
    """基础自编码器:Encoder + Decoder"""
    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()
        # 编码器:逐步降维到潜空间
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim),
            nn.ReLU()
        )
        # 解码器:从潜空间重建
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, input_dim),
            nn.Sigmoid()  # 假设输入归一化到 [0,1]
        )
    
    def forward(self, x):
        z = self.encoder(x)
        x_recon = self.decoder(z)
        return x_recon, z
python
# 训练循环
def train_ae(model, dataloader, epochs=50, lr=1e-3):
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    criterion = nn.MSELoss()
    
    for epoch in range(epochs):
        total_loss = 0.0
        for batch in dataloader:
            x = batch.view(batch.size(0), -1)
            x_recon, _ = model(x)
            
            loss = criterion(x_recon, x)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        
        avg_loss = total_loss / len(dataloader)
        if (epoch + 1) % 10 == 0:
            print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.4f}")
组件输入维度输出维度作用

编码器

(batch, 784)

(batch, 128)

特征提取与压缩

瓶颈层

(batch, 128)

(batch, 32)

强制学习紧凑表示

解码器

(batch, 32)

(batch, 784)

从潜向量重建数据

潜空间维度是关键超参数:太小会丢失信息,太大会导致恒等映射

不要对未归一化的数据直接使用 MSE 损失,先做 Min-Max 标准化到 [0,1]

2欠完备自编码器

欠完备自编码器(Undercomplete Autoencoder)是最基础的自编码器变体,其核心约束在于潜空间维度严格小于输入维度。这种设计强迫网络学习数据中最显著的特征,而非简单地复制输入。

当潜空间维度 d_latent << d_input 时,编码器无法保留全部信息,只能选择性地编码对重建最重要的特征。从信息论角度看,这相当于在给定重建误差上限下寻找最小描述长度(Minimum Description Length)。如果潜空间维度大于或等于输入维度,网络可能学到恒等映射,完全失去降维意义。

欠完备自编码器可以视为非线性版本的 PCA。当编码器和解码器都是线性变换且使用 MSE 损失时,欠完备自编码器学习到的子空间与 PCA 的主成分子空间完全一致。引入非线性激活后,它能够捕捉更复杂的流形结构,这也是其超越线性方法的关键所在。

python
# 对比 PCA 与欠完备自编码器
from sklearn.decomposition import PCA
import numpy as np

# PCA 线性降维
pca = PCA(n_components=32)
z_pca = pca.fit_transform(X)  # X: (N, 784)
X_pca_recon = pca.inverse_transform(z_pca)

# 欠完备自编码器等价于非线性 PCA
# 当 encoder/decoder 均为 Linear 且无激活函数时
# AE 的潜空间 = PCA 的主成分子空间
# 差异:AE 可以加入 ReLU/Sigmoid 等非线性
python
class UndercompleteAE(nn.Module):
    """严格欠完备的自编码器"""
    def __init__(self, input_dim, latent_dim):
        super().__init__()
        assert latent_dim < input_dim, "欠完备要求潜维度 < 输入维度"
        
        layers = []
        dims = [input_dim, 512, 256, 128, latent_dim]
        for i in range(len(dims) - 1):
            layers.extend([
                nn.Linear(dims[i], dims[i+1]),
                nn.BatchNorm1d(dims[i+1]),
                nn.ReLU()
            ])
        layers.pop()  # 最后一层不加激活
        
        self.encoder = nn.Sequential(*layers)
        # 解码器对称结构
        dec_dims = [latent_dim] + dims[:-1][::-1]
        dec_layers = []
        for i in range(len(dec_dims) - 1):
            dec_layers.extend([
                nn.Linear(dec_dims[i], dec_dims[i+1]),
                nn.BatchNorm1d(dec_dims[i+1]),
                nn.ReLU()
            ])
        dec_layers[-1] = nn.Linear(dec_dims[-2], input_dim)
        self.decoder = nn.Sequential(*dec_layers)
    
    def forward(self, x):
        return self.decoder(self.encoder(x)), self.encoder(x)
潜空间维度重建误差压缩率是否欠完备

64

0.012

12.3x

128

0.006

6.1x

784

~0.0

1.0x

否(恒等映射风险)

逐步缩小潜空间维度,观察重建质量的变化曲线,找到拐点即最优维度

网络容量过大会抵消欠完备约束的效果,增加隐藏层参数时需要同时减小潜空间维度

3正则化自编码器:稀疏与去噪

正则化自编码器通过在损失函数中添加约束项,而非单纯依赖潜空间维度限制,来引导网络学习更有意义的表示。最常见的两种变体是稀疏自编码器(Sparse Autoencoder)和去噪自编码器(Denoising Autoencoder)。

稀疏自编码器在损失函数中加入 L1 正则项或 KL 散度惩罚,迫使大部分隐藏单元保持静默(接近零),只有少数单元被激活。这模拟了生物神经元的稀疏编码原理,每个隐藏单元学习到输入数据中的特定局部特征。稀疏性约束可以用平均激活值与目标稀疏度之间的 KL 散度来度量。

去噪自编码器则在训练时向输入添加噪声(如高斯噪声或随机掩码),要求网络从损坏版本中重建原始干净数据。这迫使编码器学习数据的鲁棒表示,而非简单的恒等映射。去噪过程本质上是在学习数据流形的切线方向,使网络对垂直于流形的扰动不敏感。

python
class SparseAutoencoder(nn.Module):
    """稀疏自编码器:加入 L1 稀疏性约束"""
    def __init__(self, input_dim, hidden_dim, latent_dim, sparsity=0.05, beta=3.0):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim), nn.Sigmoid()
        )
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, input_dim), nn.Sigmoid()
        )
        self.sparsity = sparsity  # 目标平均激活率
        self.beta = beta  # 稀疏惩罚权重
    
    def sparse_penalty(self, z):
        """KL 散度稀疏惩罚"""
        p = self.sparsity
        p_hat = z.mean(dim=0)  # 每个神经元的平均激活
        p_hat = torch.clamp(p_hat, 1e-8, 1 - 1e-8)
        kl = p * torch.log(p / p_hat) + (1 - p) * torch.log((1 - p) / (1 - p_hat))
        return kl.sum()
    
    def forward(self, x):
        z = self.encoder(x)
        x_recon = self.decoder(z)
        recon_loss = nn.MSELoss()(x_recon, x)
        loss = recon_loss + self.beta * self.sparse_penalty(z)
        return x_recon, z, loss
python
class DenoisingAutoencoder(nn.Module):
    """去噪自编码器:输入加噪声后重建"""
    def __init__(self, input_dim, hidden_dim, latent_dim, noise_level=0.3):
        super().__init__()
        self.noise_level = noise_level
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim), nn.ReLU()
        )
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, input_dim), nn.Sigmoid()
        )
    
    def add_noise(self, x):
        """添加高斯噪声或随机掩码"""
        noise = torch.randn_like(x) * self.noise_level
        return torch.clamp(x + noise, 0, 1)
    
    def forward(self, x):
        x_noisy = self.add_noise(x)
        z = self.encoder(x_noisy)
        x_recon = self.decoder(z)
        loss = nn.MSELoss()(x_recon, x)  # 对比干净输入
        return x_recon, z, loss
变体约束方式优势适用场景

欠完备

潜维度 < 输入维度

简单直接

通用降维

稀疏

L1/KL 惩罚

学习局部特征

特征提取/可视化

去噪

输入加噪声

鲁棒性强

图像去噪/增强

去噪噪声强度建议从 0.1-0.3 开始,过大则重建任务过于困难导致不收敛

稀疏自编码器的 KL 散度计算需要 clamp 防止 log(0),否则会出现 NaN

4变分自编码器 VAE

变分自编码器(Variational Autoencoder, VAE)是 Kingma 和 Welling 于 2013 年提出的生成模型,它将概率图模型与深度学习结合,赋予自编码器生成新样本的能力。与传统自编码器不同,VAE 不将输入编码为确定的向量,而是编码为一个概率分布。

具体来说,编码器输出两个向量:均值 mu 和对数方差 log_sigma_sq,定义了一个多元高斯分布 q(z|x) = N(mu, sigma^2)。然后从该分布中采样得到 z,再用解码器重建。关键创新是重参数化技巧(Reparameterization Trick):z = mu + sigma * epsilon,其中 epsilon ~ N(0, 1),使采样操作可导,从而支持端到端训练。

VAE 的损失函数由两部分组成:重建损失保证生成质量,KL 散度约束潜分布接近标准正态分布 N(0, I)。这使得潜空间连续且平滑,任意采样点都能通过解码器生成有意义的样本,这是 VAE 作为生成模型的核心能力。

python
class VAE(nn.Module):
    """变分自编码器:编码为分布,支持生成"""
    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()
        # 编码器输出 mu 和 log_var
        self.fc_enc = nn.Sequential(
            nn.Linear(input_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim), nn.ReLU()
        )
        self.fc_mu = nn.Linear(hidden_dim, latent_dim)
        self.fc_logvar = nn.Linear(hidden_dim, latent_dim)
        # 解码器
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, input_dim), nn.Sigmoid()
        )
    
    def reparameterize(self, mu, logvar):
        """重参数化技巧:使采样可导"""
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std
    
    def forward(self, x):
        h = self.fc_enc(x)
        mu = self.fc_mu(h)
        logvar = self.fc_logvar(h)
        z = self.reparameterize(mu, logvar)
        return self.decoder(z), mu, logvar
python
def vae_loss(x_recon, x, mu, logvar, beta=1.0):
    """VAE 损失 = 重建损失 + KL 散度"""
    recon_loss = nn.MSELoss()(x_recon, x)
    # KL(q(z|x) || p(z)),闭式解
    kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    kl_loss /= x.size(0) * x.size(1)  # 归一化
    return recon_loss + beta * kl_loss, recon_loss, kl_loss

# 采样新数据
def sample_vae(model, n_samples, latent_dim, device):
    model.eval()
    with torch.no_grad():
        z = torch.randn(n_samples, latent_dim, device=device)
        samples = model.decoder(z)
        return samples  # 生成的新样本
特性标准自编码器VAE

潜空间

确定性向量

概率分布 N(mu, sigma)

采样

不可导

重参数化技巧可导

生成能力

无法直接生成

可从 N(0,I) 采样生成

损失函数

仅重建误差

重建误差 + KL 散度

潜空间质量

可能不连续

连续且平滑

使用 beta-VAE(增大 KL 权重)可以得到更解耦的潜变量,便于理解和操控

VAE 生成结果往往偏模糊,这是因为 KL 散度项鼓励潜变量接近标准正态分布,牺牲了部分重建精度

5对抗自编码器 AAE

对抗自编码器(Adversarial Autoencoder, AAE)由 Makhzani 等人于 2015 年提出,它将 GAN 的对抗训练思想引入自编码器框架,用于规范化潜空间分布。与 VAE 通过 KL 散度显式约束不同,AAE 使用判别器来隐式地迫使聚合后验分布 q(z) 匹配先验分布 p(z)。

AAE 包含三个阶段:重建阶段优化编码器使重建误差最小;正则化阶段让判别器区分真实先验样本和编码产生的潜向量;对抗阶段更新编码器使判别器无法区分两者。这种分离式训练使得 AAE 可以匹配任意形式的先验分布,而不仅限于高斯分布,这比 VAE 更加灵活。

AAE 的优势在于潜空间可以更精确地匹配目标分布,且没有 VAE 的模糊问题。但它也引入了 GAN 训练的不稳定性,需要仔细平衡重建损失和对抗损失。实践中常使用 Wasserstein 距离或梯度惩罚来稳定训练。

python
class Discriminator(nn.Module):
    """判别潜向量分布的判别器"""
    def __init__(self, latent_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(latent_dim, 128), nn.LeakyReLU(0.2),
            nn.Linear(128, 64), nn.LeakyReLU(0.2),
            nn.Linear(64, 1), nn.Sigmoid()
        )
    
    def forward(self, z):
        return self.net(z)


class AAE(nn.Module):
    def __init__(self, input_dim, hidden_dim, latent_dim):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, latent_dim)
        )
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, hidden_dim), nn.ReLU(),
            nn.Linear(hidden_dim, input_dim), nn.Sigmoid()
        )
        self.discriminator = Discriminator(latent_dim)
python
# AAE 训练:交替优化
def train_aae_step(aae, x, optimizer_ae, optimizer_d, device):
    batch_size = x.size(0)
    
    # --- 阶段 1: 重建阶段 ---
    optimizer_ae.zero_grad()
    z = aae.encoder(x)
    x_recon = aae.decoder(z)
    recon_loss = nn.MSELoss()(x_recon, x)
    recon_loss.backward(retain_graph=True)
    
    # --- 阶段 2: 判别器阶段 ---
    optimizer_d.zero_grad()
    z_fake = aae.encoder(x).detach()
    z_real = torch.randn_like(z_fake)
    d_fake = aae.discriminator(z_fake)
    d_real = aae.discriminator(z_real)
    d_loss = -(torch.log(d_real + 1e-8).mean() + torch.log(1 - d_fake + 1e-8).mean())
    d_loss.backward()
    optimizer_d.step()
    
    # --- 阶段 3: 对抗阶段(更新编码器) ---
    optimizer_ae.zero_grad()
    z_fake = aae.encoder(x)
    d_fake = aae.discriminator(z_fake)
    g_loss = -torch.log(d_fake + 1e-8).mean()
    g_loss.backward()
    optimizer_ae.step()
    
    return recon_loss.item(), d_loss.item(), g_loss.item()
特性VAEAAE标准 AE

潜空间正则

KL 散度(显式)

判别器(隐式)

先验灵活性

仅高斯

任意分布

无约束

生成质量

中等(偏模糊)

较好

无法直接生成

训练稳定性

稳定

不稳定(需技巧)

稳定

计算开销

较高(需判别器)

最低

使用 Wasserstein 距离代替标准 GAN 损失可以显著提升 AAE 训练稳定性

AAE 需要平衡三个阶段的训练步数,建议 1:1:1 或根据损失动态调整比例

6应用:异常检测与图像生成

自编码器在异常检测(Anomaly Detection)中有着天然优势。核心直觉是:自编码器在训练数据上学习到了正常样本的重建模式,当输入异常样本时,重建误差会显著增大。这是因为异常样本偏离了数据流形,编码器无法将其映射到训练时学习到的潜空间区域,导致解码器重建失败。

具体流程:训练阶段仅使用正常样本训练自编码器,使其在正常数据上达到低重建误差;推理阶段计算每个样本的重建误差,超过阈值的判定为异常。这种方法无需异常样本标签,属于无监督异常检测,在工业质检、网络安全、金融欺诈等场景广泛应用。

在图像生成方面,VAE 和 AAE 都可以从潜空间采样生成新图像。通过操控潜向量可以实现属性编辑(如改变人脸表情、调整图像风格),这是因为连续平滑的潜空间使得语义操作成为可能。结合条件编码(CVAE)还能实现条件生成,即指定类别或属性后生成对应图像。

python
class AnomalyDetector:
    """基于自编码器的异常检测器"""
    def __init__(self, autoencoder, threshold_percentile=95):
        self.ae = autoencoder
        self.threshold_percentile = threshold_percentile
        self.threshold = None
    
    def fit_threshold(self, dataloader):
        """在正常数据上确定异常阈值"""
        self.ae.eval()
        errors = []
        with torch.no_grad():
            for batch in dataloader:
                x = batch.view(batch.size(0), -1)
                x_recon, _ = self.ae(x)
                error = torch.mean((x - x_recon) ** 2, dim=1)
                errors.extend(error.cpu().numpy())
        # 使用百分位数作为阈值
        self.threshold = np.percentile(errors, self.threshold_percentile)
    
    def predict(self, x):
        """判断是否为异常"""
        self.ae.eval()
        with torch.no_grad():
            x_recon, _ = self.ae(x)
            error = torch.mean((x - x_recon) ** 2, dim=1)
            return (error > self.threshold).int()
python
# 潜空间属性编辑:图像生成示例
def latent_interpolation(model, z1, z2, n_steps=10):
    """在两个潜向量之间线性插值"""
    alphas = torch.linspace(0, 1, n_steps).view(-1, 1)
    z_interp = (1 - alphas) * z1 + alphas * z2
    model.eval()
    with torch.no_grad():
        images = model.decoder(z_interp)
        return images  # 平滑过渡的图像序列


def latent_arithmetic(model, z_a, z_b, z_c, alpha=1.0):
    """潜空间向量运算:z = z_a - z_b + z_c"""
    z_result = z_a - z_b + z_c
    model.eval()
    with torch.no_grad():
        return model.decoder(z_result)
应用场景方法输入输出

异常检测

重建误差阈值

单一样本

正常/异常标签

图像去噪

去噪自编码器

带噪图像

干净图像

图像生成

VAE 潜空间采样

随机噪声 z

新图像

属性编辑

潜向量算术

潜向量 + 方向

编辑后图像

数据压缩

欠完备编码

原始数据

压缩表示

异常检测中使用 Per-Sample MSE 而非 Batch MSE,逐样本判断更准确

异常检测的阈值选择至关重要,建议用 ROC 曲线或 PR 曲线在验证集上寻找最优阈值

7PyTorch 实战:MNIST 重建与潜空间插值

本节通过完整的 PyTorch 代码实现一个卷积变分自编码器(Convolutional VAE),在 MNIST 数据集上训练,展示从数据准备、模型训练到生成新样本的完整流程。卷积架构相比全连接能更好地捕捉图像的空间结构信息。

卷积 VAE 的编码器使用 Conv2d + BatchNorm + ReLU 逐步降低空间维度并增加通道数,最终通过 Flatten 和全连接层输出 mu 和 log_var。解码器则使用 ConvTranspose2d 逐步上采样恢复空间维度。这种设计使潜向量保留了更多空间语义信息,重建质量显著提升。

训练完成后,我们将演示三项关键操作:第一,查看重建效果对比原图和重建图;第二,从标准正态分布采样生成全新的手写数字;第三,在两个数字的潜向量之间插值,观察生成图像的平滑过渡。这三项操作分别验证了模型的重建能力、生成能力和潜空间连续性。

python
class ConvVAE(nn.Module):
    """卷积变分自编码器 for MNIST"""
    def __init__(self, latent_dim=20):
        super().__init__()
        # 编码器:28x28 -> 4x4
        self.enc = nn.Sequential(
            nn.Conv2d(1, 32, 4, stride=2, padding=1),   # 28->14
            nn.BatchNorm2d(32), nn.ReLU(),
            nn.Conv2d(32, 64, 4, stride=2, padding=1),   # 14->7
            nn.BatchNorm2d(64), nn.ReLU(),
            nn.Conv2d(64, 128, 4, stride=2, padding=1),  # 7->4
            nn.BatchNorm2d(128), nn.ReLU(),
        )
        # 全连接层
        self.fc_mu = nn.Linear(128 * 4 * 4, latent_dim)
        self.fc_logvar = nn.Linear(128 * 4 * 4, latent_dim)
        self.fc_decode = nn.Linear(latent_dim, 128 * 4 * 4)
        # 解码器:4x4 -> 28x28
        self.dec = nn.Sequential(
            nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1),
            nn.BatchNorm2d(64), nn.ReLU(),
            nn.ConvTranspose2d(64, 32, 4, stride=2, padding=1),
            nn.BatchNorm2d(32), nn.ReLU(),
            nn.ConvTranspose2d(32, 1, 4, stride=2, padding=1),
            nn.Sigmoid()
        )
    
    def reparameterize(self, mu, logvar):
        return mu + torch.exp(0.5 * logvar) * torch.randn_like(mu)
    
    def forward(self, x):
        h = self.enc(x)
        h = h.view(h.size(0), -1)
        mu, logvar = self.fc_mu(h), self.fc_logvar(h)
        z = self.reparameterize(mu, logvar)
        h_dec = self.fc_decode(z).view(-1, 128, 4, 4)
        return self.dec(h_dec), mu, logvar
python
# 完整训练与可视化
import torchvision.transforms as transforms
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader

transform = transforms.Compose([transforms.ToTensor()])
train_ds = MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_ds, batch_size=128, shuffle=True)

model = ConvVAE(latent_dim=20)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(30):
    model.train()
    total_loss = 0
    for x, _ in train_loader:
        x_recon, mu, logvar = model(x)
        recon = nn.BCELoss()(x_recon, x)
        kl = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp()) / x.size(0)
        loss = recon + kl
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+1}: Loss={total_loss/len(train_loader):.4f}")

# 潜空间插值可视化
model.eval()
with torch.no_grad():
    z1 = torch.randn(1, 20)  # 数字 A 的潜向量
    z2 = torch.randn(1, 20)  # 数字 B 的潜向量
    alphas = torch.linspace(0, 1, 8).view(-1, 1)
    z_path = (1 - alphas) * z1 + alphas * z2
    interp_images = model.decoder(model.fc_decode(z_path).view(-1, 128, 4, 4))
    # interp_images: (8, 1, 28, 28) 从 A 平滑过渡到 B
训练阶段Epoch 范围典型 Loss观察指标

初始阶段

1-5

200-100

重建质量快速提升

收敛阶段

5-15

100-80

KL 散度逐渐增大

稳定阶段

15-30

80-75

生成质量稳定

使用 2D 潜空间(latent_dim=2)可以可视化整个潜空间分布,便于理解 VAE 的流形学习过程

MNIST 图像已经是 0-1 范围,不需要额外归一化;如果使用自定义数据集,务必确认输入范围匹配 Sigmoid 输出

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目