Skip to content

PyTorch 自编码器 (Autoencoder)

自编码器(Autoencoder,AE)是一种无监督学习的神经网络,通过学习将输入数据压缩到低维潜在空间,再从压缩表示重构原始数据。

自编码器广泛应用于数据降维、特征提取、异常检测、图像去噪、生成模型等场景。


1. 自编码器基础原理

自编码器的基本结构包含三个部分:

  • 编码器(Encoder):将输入数据 x 映射到低维潜在表示 z
  • 潜在空间(Latent Space):编码器输出的低维向量,也称为瓶颈层
  • 解码器(Decoder):将潜在表示 z 重构为输出 x^

1.1 网络结构

自编码器的目标是让输出 x^ 尽可能接近输入 x

min_θ,ϕ1n_i=1nx_iD_ϕ(E_θ(x_i))2

其中 θ 是编码器参数,ϕ 是解码器参数。

1.2 降维效果

自编码器通过强制数据通过比输入维度更小的瓶颈层,从而学习数据的压缩表示。这种压缩保留了数据的主要信息。

与主成分分析(PCA)相比,自编码器可以学习非线性降维,能够捕捉更复杂的数据结构。


2. 基础自编码器实现

2.1 简单自编码器

实例

python

import torch

import torch.nn as nn

import torch.optim as optim

from torch.utils.data import DataLoader, TensorDataset

# ── 自编码器模型 ─────────────────────────────────

class Autoencoder(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, 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, hidden_dim),

            nn.ReLU(),

            nn.Linear(hidden_dim, input_dim),

        )

    def forward(self, x):

        z = self.encoder(x)

        x_recon = self.decoder(z)

        return x_recon

    def encode(self, x):

        """编码:获取潜在表示"""

        return self.encoder(x)

    def decode(self, z):

        """解码:从潜在表示重构"""

        return self.decoder(z)

# ── 使用示例 ──────────────────────────────────────

INPUT_DIM = 784   # 例如 MNIST 图像展开后

HIDDEN_DIM = 256

LATENT_DIM = 32   # 潜在空间维度,远小于输入维度

model = Autoencoder(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)

print(f"输入维度: {INPUT_DIM}")

print(f"潜在维度: {LATENT_DIM}")

print(f"压缩比: {INPUT_DIM / LATENT_DIM:.1f}x")

# 查看参数量

total_params = sum(p.numel() for p in model.parameters())

print(f"总参数量: {total_params:,}")

2.2 卷积自编码器

对于图像数据,使用卷积层的自编码器效果更好:

实例

python

import torch

import torch.nn as nn

class ConvAutoencoder(nn.Module):

    """

    卷积自编码器:适用于图像

    """

    def __init__(self, channels=3, latent_dim=128):

        super().__init__()

        # 编码器:逐步减小尺寸,增加通道数

        # 输入: (batch, channels, 64, 64)

        self.encoder = nn.Sequential(

            # 32 -> 16

            nn.Conv2d(channels, 32, kernel_size=3, stride=2, padding=1),

            nn.ReLU(),

            # 16 -> 8

            nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1),

            nn.ReLU(),

            # 8 -> 4

            nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),

            nn.ReLU(),

            # 4 -> 2

            nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1),

            nn.ReLU(),

        )

        # 潜在空间映射

        self.to_latent = nn.AdaptiveAvgPool2d((1, 1))

        # 解码器:逐步增大尺寸

        # 输入: (batch, 256, 2, 2)

        self.from_latent = nn.Sequential(

            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),

            nn.Conv2d(256, 128, kernel_size=3, padding=1),

            nn.ReLU(),

            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),

            nn.Conv2d(128, 64, kernel_size=3, padding=1),

            nn.ReLU(),

            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),

            nn.Conv2d(64, 32, kernel_size=3, padding=1),

            nn.ReLU(),

            nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),

            nn.Conv2d(32, channels, kernel_size=3, padding=1),

            nn.Sigmoid()  # 输出 [0, 1]

        )

    def forward(self, x):

        z = self.encode(x)

        x_recon = self.decode(z)

        return x_recon

    def encode(self, x):

        """编码"""

        features = self.encoder(x)

        z = self.to_latent(features)

        z = z.view(z.size(0), -1)  # (batch, 256)

        return z

    def decode(self, z):

        """解码"""

        # 将向量 reshape 为特征图

        batch_size = z.size(0)

        z = z.view(batch_size, 256, 1, 1)

        z = z.expand(-1, -1, 2, 2)  # 上采样到 2x2

        x_recon = self.from_latent(z)

        return x_recon

# 测试

model = ConvAutoencoder(channels=3, latent_dim=128)

x = torch.randn(4, 3, 64, 64)

x_recon = model(x)

print(f"输入形状: {x.shape}")

print(f"输出形状: {x_recon.shape}")

print(f"潜在向量形状: {model.encode(x).shape}")

2.3 训练与重构

实例

python

import torch

import torch.nn as nn

import torch.optim as optim

# ── 训练配置 ─────────────────────────────────────

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model = ConvAutoencoder(channels=3, latent_dim=128).to(device)

criterion = nn.MSELoss()

optimizer = optim.Adam(model.parameters(), lr=1e-3)

# ── 训练循环 ─────────────────────────────────────

def train_autoencoder(model, dataloader, criterion, optimizer, num_epochs=10):

    model.train()

    for epoch in range(num_epochs):

        total_loss = 0

        for batch in dataloader:

            images = batch[0].to(device)

            # 前向传播

            outputs = model(images)

            loss = criterion(outputs, images)

            # 反向传播

            optimizer.zero_grad()

            loss.backward()

            optimizer.step()

            total_loss += loss.item()

        avg_loss = total_loss / len(dataloader)

        print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.6f}")

    return model

# 假设已有数据加载器

# train_autoencoder(model, train_loader, criterion, optimizer, num_epochs=10)

print("自编码器训练完成!")

3. 去噪自编码器 (DAE)

去噪自编码器(Denoising Autoencoder,DAE)在训练时给输入添加噪声,然后学习去除噪声恢复原始输入。这使模型学到更鲁棒的特征表示。

3.1 去噪自编码器实现

实例

python

import torch

import torch.nn as nn

class DenoisingAutoencoder(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, 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, hidden_dim),

            nn.ReLU(),

            nn.Linear(hidden_dim, input_dim),

            nn.Sigmoid()  # 输出 [0, 1]

        )

    def forward(self, x):

        z = self.encode(x)

        return self.decode(z)

    def encode(self, x):

        return self.encoder(x)

    def decode(self, z):

        return self.decoder(z)

def add_noise(x, noise_factor=0.3):

    """

    添加高斯噪声

    """

    noise = torch.randn_like(x) * noise_factor

    noisy_x = x + noise

    return torch.clamp(noisy_x, 0.0, 1.0)

# 训练去噪自编码器

def train_dae(model, dataloader, noise_factor=0.3, lr=1e-3):

    criterion = nn.MSELoss()

    optimizer = optim.Adam(model.parameters(), lr=lr)

    model.train()

    for epoch in range(10):

        for batch in dataloader:

            images = batch[0]

            # 添加噪声

            noisy_images = add_noise(images, noise_factor)

            noisy_images = noisy_images.to(next(model.parameters()).device)

            images = images.to(next(model.parameters()).device)

            # 前向传播

            outputs = model(noisy_images)

            loss = criterion(outputs, images)  # 与原始图像比较,而非噪声图像

            # 反向传播

            optimizer.zero_grad()

            loss.backward()

            optimizer.step()

    return model

3.2 其他噪声类型

实例

python

def salt_pepper_noise(x, prob=0.1):

    """盐椒噪声"""

    random_mask = torch.rand_like(x)

    noisy = x.clone()

    noisy[random_mask < prob / 2] = 0.0

    noisy[random_mask > 1 - prob / 2] = 1.0

    return noisy

def mask_noise(x, prob=0.1):

    """遮挡噪声(随机置零)"""

    mask = torch.rand_like(x) > prob

    return x * mask.float()

def dropout_noise(x, rate=0.2):

    """Dropout 噪声"""

    mask = torch.rand_like(x) > rate

    return x * mask.float() / (1 - rate)

4. 变分自编码器 (VAE)

变分自编码器(Variational Autoencoder,VAE)是一种生成模型,它将数据编码为潜在空间中的概率分布,而非固定向量。这使得我们可以从潜在空间中采样生成新数据。

4.1 VAE 核心原理

VAE 的关键创新是学习潜在变量的概率分布:

  • 编码器输出均值 μ 和标准差 σ
  • 从正态分布 N(μ,σ) 中采样得到潜在向量 z
  • 解码器从 z 重构数据

为了实现可微的采样过程,使用了重参数化技巧(Reparameterization Trick)

z=μ+σϵ,ϵN(0,1)

4.2 VAE 实现

实例

python

import torch

import torch.nn as nn

import torch.optim as optim

class VAE(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, 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, hidden_dim),

            nn.ReLU(),

            nn.Linear(hidden_dim, input_dim),

            nn.Sigmoid()

        )

    def encode(self, x):

        h = self.encoder(x)

        mu = self.fc_mu(h)

        logvar = self.fc_logvar(h)

        return mu, logvar

    def reparameterize(self, mu, logvar):

        """重参数化技巧"""

        std = torch.exp(0.5 * logvar)

        eps = torch.randn_like(std)

        return mu + eps * std

    def decode(self, z):

        return self.decoder(z)

    def forward(self, x):

        mu, logvar = self.encode(x)

        z = self.reparameterize(mu, logvar)

        x_recon = self.decode(z)

        return x_recon, mu, logvar

def vae_loss(x_recon, x, mu, logvar, beta=1.0):

    """

    VAE 损失函数

    重构损失 + KL 散度

    """

    # 重构损失

    recon_loss = nn.functional.mse_loss(x_recon, x, reduction='sum')

    # KL 散度:-0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)

    kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())

    return recon_loss + beta * kl_loss, recon_loss, kl_loss

# 使用示例

INPUT_DIM = 784

HIDDEN_DIM = 256

LATENT_DIM = 2  # 二维潜在空间便于可视化

model = VAE(INPUT_DIM, HIDDEN_DIM, LATENT_DIM)

# 测试

x = torch.randn(32, 784)

x_recon, mu, logvar = model(x)

print(f"输入形状: {x.shape}")

print(f"重构形状: {x_recon.shape}")

print(f"均值形状: {mu.shape}")       # (32, 2)

print(f"方差形状: {logvar.shape}")   # (32, 2)

4.3 VAE 生成与可视化

实例

python

import matplotlib.pyplot as plt

def visualize_latent_space(model, dataloader, device):

    """可视化潜在空间"""

    model.eval()

    all_mu = []

    all_labels = []

    with torch.no_grad():

        for batch in dataloader:

            images, labels = batch[0].to(device), batch[1]

            mu, _ = model.encode(images)

            all_mu.append(mu.cpu())

            all_labels.append(labels)

    all_mu = torch.cat(all_mu, dim=0).numpy()

    all_labels = torch.cat(all_labels, dim=0).numpy()

    plt.figure(figsize=(10, 8))

    scatter = plt.scatter(all_mu[:, 0], all_mu[:, 1], c=all_labels,

                          cmap='tab10', alpha=0.5, s=10)

    plt.colorbar(scatter)

    plt.xlabel('Latent Dimension 1')

    plt.ylabel('Latent Dimension 2')

    plt.title('VAE Latent Space')

    plt.show()

def generate_from_latent(model, z, device):

    """从潜在向量生成图像"""

    model.eval()

    with torch.no_grad():

        z = z.to(device)

        generated = model.decode(z)

    return generated

def interpolate_latent(model, z1, z2, steps=10, device):

    """潜在空间插值生成"""

    model.eval()

    # 线性插值

    alphas = torch.linspace(0, 1, steps)

    interpolated = []

    with torch.no_grad():

        for alpha in alphas:

            z = z1 * (1 - alpha) + z2 * alpha

            generated = model.decode(z)

            interpolated.append(generated)

    return torch.cat(interpolated, dim=0)

# 生成新图像

def generate_new_images(model, num_images, latent_dim, device):

    """从随机潜在向量生成新图像"""

    model.eval()

    with torch.no_grad():

        # 从标准正态分布采样

        z = torch.randn(num_images, latent_dim).to(device)

        generated = model.decode(z)

    return generated

VAE 的潜在空间是连续的,可以在潜在空间中进行插值,生成平滑过渡的图像。但 VAE 生成的图像通常较模糊,这是因为它优化的是下界而非精确的对数似然。


5. 稀疏自编码器

稀疏自编码器(Sparse Autoencoder)在损失函数中加入稀疏性约束,限制潜在向量的激活数量。这使模型能够学习更有意义的特征。

5.1 稀疏自编码器实现

< h2 class="example">实例

python

import torch

import torch.nn as nn

import torch.nn.functional as F

class SparseAutoencoder(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(),

        )

        # 潜在层

        self.bottleneck = 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 forward(self, x):

        h = self.encoder(x)

        z = self.bottleneck(h)

        z_activated = F.relu(z)  # 稀疏激活

        x_recon = self.decoder(z_activated)

        return x_recon, z_activated

def sparse_loss(z, rho=0.05, beta=1.0):

    """

    稀疏损失:KL 散度

    rho: 目标稀疏度(例如 0.05 表示只有 5% 的神经元应该激活)

    beta: 稀疏项权重

    """

    # 计算平均激活

    rho_hat = torch.mean(z, dim=0)

    # KL 散度

    kl = rho * torch.log(rho / (rho_hat + 1e-8)) + \

         (1 - rho) * torch.log((1 - rho) / (1 - rho_hat + 1e-8))

    return beta * torch.sum(kl)

def total_sparse_loss(x_recon, x, z, rho=0.05, beta=1.0):

    """总损失 = 重构损失 + 稀疏损失"""

    recon_loss = F.mse_loss(x_recon, x)

    sparsity = sparse_loss(z, rho, beta)

    return recon_loss + sparsity

6. 序列到序列自编码器

对于序列数据(如文本、时间序列),使用 RNN/LSTM 作为编码器和解码器。

6.1 序列自编码器实现

实例

python

import torch

import torch.nn as nn

class Seq2SeqAutoencoder(nn.Module):

    """

    序列到序列自编码器:用于序列数据

    """

    def __init__(self, input_size, hidden_size, latent_size, num_layers=2):

        super().__init__()

        # 编码器 LSTM

        self.encoder = nn.LSTM(

            input_size=input_size,

            hidden_size=hidden_size,

            num_layers=num_layers,

            batch_first=True,

            bidirectional=True

        )

        # 潜在空间映射

        # 双向 LSTM 输出是 hidden_size * 2

        self.to_latent = nn.Linear(hidden_size * 2, latent_size)

        self.from_latent = nn.Linear(latent_size, hidden_size * 2)

        # 解码器 LSTM

        self.decoder = nn.LSTM(

            input_size=hidden_size,

            hidden_size=hidden_size,

            num_layers=num_layers,

            batch_first=True,

            bidirectional=True

        )

        # 输出映射

        self.output_proj = nn.Linear(hidden_size * 2, input_size)

    def forward(self, x):

        # 编码

        _, (h_n, _) = self.encoder(x)

        # 合并双向最后隐藏状态

        # h_n: (num_layers * 2, batch, hidden_size)

        h_forward = h_n[-2]

        h_backward = h_n[-1]

        h_combined = torch.cat([h_forward, h_backward], dim=-1)

        # 映射到潜在空间

        z = self.to_latent(h_combined)

        # 从潜在空间映射回来

        decoder_init = self.from_latent(z)

        decoder_init = decoder_init.view(2, decoder_init.size(0), -1)  # (2, batch, hidden_size*2)

        # 解码(使用原始输入长度)

        decoder_output, _ = self.decoder(

            x,

            (decoder_init, torch.zeros_like(decoder_init))

        )

        # 输出映射

        output = self.output_proj(decoder_output)

        return output, z

# 使用示例

model = Seq2SeqAutoencoder(

    input_size=128,   # 输入特征维度

    hidden_size=256,  # LSTM 隐藏维度

    latent_size=64,   # 潜在空间维度

    num_layers=2

)

# 测试

x = torch.randn(8, 20, 128)  # (batch, seq_len, input_size)

output, z = model(x)

print(f"输入形状: {x.shape}")        # (8, 20, 128)

print(f"输出形状: {output.shape}")  # (8, 20, 128)

print(f"潜在向量形状: {z.shape}")    # (8, 64)

7. 自编码器的应用场景

7.1 异常检测

自编码器可以用于检测异常数据。正常数据的重构误差小,异常数据的重构误差大:

实例

python

import torch

import torch.nn as nn

def detect_anomalies(model, data_loader, threshold=None, device='cpu'):

    """

    使用自编码器检测异常

    """

    model.eval()

    reconstruction_errors = []

    with torch.no_grad():

        for batch in data_loader:

            images = batch[0].to(device)

            outputs = model(images)

            # 计算重构误差(均方误差)

            errors = torch.mean((outputs - images) ** 2, dim=(1, 2, 3))

            reconstruction_errors.extend(errors.cpu().numpy())

    reconstruction_errors = torch.tensor(reconstruction_errors)

    # 如果没有给定阈值,使用统计方法

    if threshold is None:

        # 使用 95% 分位数

        threshold = torch.quantile(reconstruction_errors, 0.95).item()

    # 标记异常

    anomalies = reconstruction_errors > threshold

    return anomalies, reconstruction_errors, threshold

# 训练异常检测模型

def train_anomaly_detector(normal_data_loader):

    """只用正常数据训练自编码器"""

    model = ConvAutoencoder(channels=1, latent_dim=32)

    criterion = nn.MSELoss()

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

    model.train()

    for epoch in range(10):

        for batch in normal_data_loader:

            images = batch[0]

            outputs = model(images)

            loss = criterion(outputs, images)

            optimizer.zero_grad()

            loss.backward()

            optimizer.step()

    return model

7.2 图像着色与风格迁移

实例

python

class ColorizationAutoencoder(nn.Module):

    """

    图像着色自编码器

    输入:灰度图像 (batch, 1, H, W)

    输出:彩色图像 (batch, 2, H, W) (ab 色彩空间)

    """

    def __init__(self):

        super().__init__()

        # 编码器:逐步提取特征

        self.encoder = nn.Sequential(

            nn.Conv2d(1, 64, kernel_size=4, stride=2, padding=1),   # H/2

            nn.ReLU(),

            nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),  # H/4

            nn.BatchNorm2d(128),

            nn.ReLU(),

            nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1), # H/8

            nn.BatchNorm2d(256),

            nn.ReLU(),

        )

        # 解码器:上采样生成颜色

        self.decoder = nn.Sequential(

            nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),

            nn.BatchNorm2d(128),

            nn.ReLU(),

            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),

            nn.BatchNorm2d(64),

            nn.ReLU(),

            nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1),

            nn.ReLU(),

            nn.Conv2d(32, 2, kernel_size=3, padding=1),  # 输出 ab 通道

            nn.Sigmoid()  # ab 通道范围 [0, 1]

        )

    def forward(self, x):

        z = self.encoder(x)

        color = self.decoder(z)

        return color

7.3 数据降维可视化

实例

python

import matplotlib.pyplot as plt

def visualize_latent_2d(model, dataloader, device, num_samples=1000):

    """

    使用自编码器将数据降到 2 维进行可视化

    """

    model.eval()

    all_latents = []

    all_labels = []

    with torch.no_grad():

        count = 0

        for batch in dataloader:

            if count >= num_samples:

                break

            images, labels = batch[0], batch[1]

            images = images.to(device)

            # 如果是 2D AE,直接使用

            # 如果维度更高,需要先投影到 2D

            z = model.encode(images)

            all_latents.append(z.cpu())

            all_labels.append(labels)

            count += images.size(0)

    latents = torch.cat(all_latents, dim=0)[:num_samples].numpy()

    labels = torch.cat(all_labels, dim=0)[:num_samples].numpy()

    plt.figure(figsize=(10, 8))

    scatter = plt.scatter(latents[:, 0], latents[:, 1], c=labels,

                          cmap='tab10', alpha=0.6, s=20)

    plt.colorbar(scatter)

    plt.xlabel('Latent Dimension 1')

    plt.ylabel('Latent Dimension 2')

    plt.title('Autoencoder 2D Latent Space Visualization')

    plt.show()

8. API 快速参考

8.1 常见自编码器类型

类型特点适用场景
基础自编码器简单对称结构降维、特征提取
卷积自编码器使用卷积层保留空间结构图像处理
去噪自编码器学习去除噪声图像去噪、鲁棒特征
变分自编码器学习概率分布,可生成新数据生成模型、数据生成
稀疏自编码器稀疏约束,学习可解释特征特征解耦、可解释性
序列自编码器使用 RNN/LSTM 处理序列文本、时间序列

8.2 损失函数选择

任务推荐损失函数
图像重构MSELoss、SSIMLoss
二值图像BCELoss、BCEWithLogitsLoss
文本重构CrossEntropyLoss
VAEMSE + KL Divergence
异常检测MSE、MAE

8.3 潜在维度选择

python
数据维度低(<100维
    -> 潜在维度设为 2~10

数据维度中等(100~1000维
    -> 潜在维度设为 10~50

数据维度高(>1000维
    -> 潜在维度设为 50~200

生成任务(VAE
    -> 潜在维度 2~32(便于采样和可视化)

异常检测
    -> 潜在维度 16~64(保留足够信息检测异常)

AI 思考中...

PyTorch 生成对抗网络 (GAN)

PyTorch 模型评估与调试

基于 VitePress 构建,部署于 GitHub Pages