自编码器(Autoencoder,AE)

一 核心原理

自编码器是一种神经网络结构,通过无监督学习方式对输入的数据进行编码和解码。核心作用是压缩数据特征,也就是降维,由编码器和解码器两部分构成。

1.1 编码器(Encoder)

将数据压缩到低维空间,维度通常远小于输入数据。、

数学表达:

$h = f(x) = \sigma(W x + b)$

x 是输入,h 是潜在表示,Wb 是权重与偏置,\sigma  是激活函数。

1.2 解码器(Decoder

利用编码器给出的潜在表示,重建原始输入数据。

数学表达:

$\hat{x} = g(h) = \sigma'(W' h + b')$

损失函数为:

$\mathcal{L} = \| x - \hat{x} \|^2$  均方误差

1.3 自编码器结构流程

流程为:

输入 x → 编码器网络 → 潜在表示 h → 解码器网络 → 重建输出 x̂

二 使用场景

2.1数据降维

替代PCA,捕捉非线性特征  eg:图像特征压缩

PCA(Principal Component Analysis,主成分分析):是一种经典的线性降维算法,将高维数据映射到低维,保留数据中的主要方差信息。

特性PCA自编码器(AE)
模型类型线性变换非线性神经网络
优化目标最大方差 / 最小重建误差(线性)最小化输入与解码输出的差异(非线性)
可解释性高(主成分为显式组合)低(潜在空间隐含特征)
计算成本低(矩阵分解)高(需训练神经网络)
数据适应性仅适合线性结构数据适合复杂非线性结构数据

2.2去噪

输入含有噪声,输出后变为干净数据。 eg:模糊图像复原

常见的噪声类型:

噪声类型        描述应用场景
高斯噪声服从正态分布图像传感器噪声去除
椒盐噪声随机像素变为黑白点图像修复
遮蔽噪声随机遮蔽部分输入(类似Dropout)文本或语音补全
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 去噪自编码器网络结构
class DenoisingAutoencoder(nn.Module):
    def __init__(self):
        super().__init__()
        # 编码器
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 32, 3, padding=1),  # 输入通道1,输出通道32
            nn.ReLU(),
            nn.MaxPool2d(2, 2),              # 尺寸减半
            nn.Conv2d(32, 64, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)               # 尺寸再次减半
        )
        # 解码器
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(64, 32, 2, stride=2),  # 上采样
            nn.ReLU(),
            nn.ConvTranspose2d(32, 1, 2, stride=2),
            nn.Sigmoid()  # 输出像素值在[0,1]之间
        )
    
    def forward(self, x_noisy):
        z = self.encoder(x_noisy)
        x_clean = self.decoder(z)
        return x_clean

# 添加高斯噪声的函数
def add_noise(img, noise_factor=0.5):
    noise = torch.randn_like(img) * noise_factor
    img_noisy = img + noise
    return torch.clamp(img_noisy, 0.0, 1.0)  # 限制到[0,1]范围

# 训练流程
def train_dae(model, train_loader, epochs=10):
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    
    for epoch in range(epochs):
        for data in train_loader:
            img_clean, _ = data
            img_noisy = add_noise(img_clean, noise_factor=0.5)  # 添加噪声
            output = model(img_noisy)
            loss = criterion(output, img_clean)
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')

# 使用MNIST数据集
transform = transforms.Compose([transforms.ToTensor()])
train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_data, batch_size=64, shuffle=True)

# 初始化模型并训练
dae = DenoisingAutoencoder()
train_dae(dae, train_loader, epochs=10)

# 测试去噪效果
import matplotlib.pyplot as plt
test_img, _ = next(iter(train_loader))
test_img_noisy = add_noise(test_img[:5], noise_factor=0.5)
with torch.no_grad():
    output = dae(test_img_noisy)

# 可视化输入与输出对比
fig, axes = plt.subplots(2, 5, figsize=(15, 5))
for i in range(5):
    axes[0, i].imshow(test_img_noisy[i].squeeze(), cmap='gray')
    axes[1, i].imshow(output[i].squeeze(), cmap='gray')
axes[0, 0].set_ylabel('Noisy Input')
axes[1, 0].set_ylabel('Denoised Output')
plt.show()

2.3特征提取

潜在层表示可作为下游任务(分类/聚类)的特征输入   eg:推荐系统的用户嵌入

2.4生成模型

生成与训练数据相似的新样本(常结合变分自编码器, VAE)  eg:生成人脸/手写数字图像

三 自编码器类型

类型特点适用场景
标准自编码器基础结构,无特殊约束通用特征学习
稀疏自编码器通过正则化约束潜在层稀疏性(如L1正则化)特征选择/可视化
降噪自编码器输入添加噪声,强制模型学习鲁棒性特征数据清洗与增强
变分自编码器潜在空间服从高斯分布,支持概率生成(VAE)生成多样化新样本
卷积自编码器编码器和解码器使用卷积操作图像/视频重建

import torch
import torch.nn as nn

# 定义一个简单的自编码器
class Autoencoder(nn.Module):
    def __init__(self, input_dim=784, latent_dim=32):
        super().__init__()
        # 编码器
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, 256),
            nn.ReLU(),
            nn.Linear(256, latent_dim)
        )
        # 解码器
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.ReLU(),
            nn.Linear(256, input_dim),
            nn.Sigmoid()  # 输出值在[0,1]间(如图像像素)
        )

    def forward(self, x):
        latent = self.encoder(x)
        reconstruction = self.decoder(latent)
        return reconstruction

# 使用示例
model = Autoencoder(input_dim=784, latent_dim=32)
loss_fn = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters())

# 训练循环(以MNIST为例)
for epoch in range(100):
    for batch in dataloader:
        images, _ = batch
        images = images.view(-1, 784)  # 展平为向量
        outputs = model(images)
        loss = loss_fn(outputs, images)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

四 局限性

(1)生成质量有限:标准AE生成的样本通常模糊,缺乏多样性。

(2)潜在空间不可解释:非变分AE的潜在变量无概率意义,难以直接控制生成过程。

(3)依赖重构损失:若损失函数设计不当,可能忽略数据的关键特征。

五 变体

5.1变分自编码器(VAE):将潜在空间建模为概率分布,支持生成新数据。

特性传统自编码器(AE)变分自编码器(VAE)
潜在空间确定性的隐向量概率化的分布
目标最小化重建误差最大化ELBO(同时优化重建和KL散度)
生成能力无法生成新样本(仅重建输入)可生成新样本(通过从先验采样)
隐空间解释性无明确分布约束隐变量服从先验分布(如正态分布)

局限性:

(1)生成质量限制:图像生成结果可能较模糊(与GAN相比)。

(2)KL散度矛盾:KL项可能导致隐变量过度简化,削弱生成多样性。

(3)训练复杂度高:需设计合适的网络结构和损失函数。

应用场景:

场景说明
数据生成生成图像、文本、音乐(如手写数字补全、虚拟人脸生成)。
数据补全填充缺失值(如修复残缺图像或表格数据)。
异常检测通过重构误差识别异常样本(低概率数据难以被重构)。
隐空间探索分析隐变量对数据特征的控制(如人脸的表情、姿态解耦)。
import torch
import torch.nn as nn
import torch.optim as optim

class VAE(nn.Module):
    def __init__(self, input_dim, latent_dim):
        super().__init__()
        # 编码器
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, 512),
            nn.ReLU(),
            nn.Linear(512, 256),
            nn.ReLU()
        )
        self.fc_mu = nn.Linear(256, latent_dim)
        self.fc_logvar = nn.Linear(256, latent_dim)
        
        # 解码器
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.ReLU(),
            nn.Linear(256, 512),
            nn.ReLU(),
            nn.Linear(512, input_dim),
            nn.Sigmoid()  # 假设输入为归一化的图像像素值(0-1)
        )
    
    def encode(self, x):
        h = self.encoder(x)
        return self.fc_mu(h), self.fc_logvar(h)
    
    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 train_vae(model, train_loader, epochs=10):
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    for epoch in range(epochs):
        for batch in train_loader:
            x, _ = batch
            x_recon, mu, logvar = model(x)
            
            # 计算损失
            recon_loss = nn.BCELoss(reduction='sum')(x_recon, x)  # 二值交叉熵
            kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
            total_loss = recon_loss + kl_loss
            
            optimizer.zero_grad()
            total_loss.backward()
            optimizer.step()
        print(f'Epoch {epoch+1}, Loss: {total_loss.item()/len(x):.2f}')

# 数据加载(以MNIST为例)
from torchvision import datasets, transforms
transform = transforms.Compose([transforms.ToTensor()])
train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=128, shuffle=True)

# 初始化并训练VAE
vae = VAE(input_dim=784, latent_dim=20)
train_vae(vae, train_loader, epochs=20)

# 生成新样本
with torch.no_grad():
    z = torch.randn(64, 20)  # 从标准正态分布采样
    generated_images = vae.decode(z).reshape(-1, 28, 28)

5.2对抗自编码器(AAE):结合生成对抗网络(GAN),提升生成样本的真实性。

加入了判别器,强制潜在向量分布匹配先验分布。

对抗目标:

判别器(D):区分潜在向量是来自编码器(“假样本”)还是先验分布(“真样本”)。

编码器(E):欺骗判别器,使编码的潜在向量分布与先验分布一致。

训练流程: 重构阶段➡正则化阶段(对抗训练):判别器训练➡生成器(编码器)训练

特性变分自编码器(VAE)对抗自编码器(AAE)
潜在分布匹配方法显式KL散度最小化,要求可解析计算的分布隐式对抗训练,支持任意复杂分布
生成质量重构图像较模糊(KL项限制)生成更清晰(对抗训练提升多样性)
训练稳定性优化目标明确(ELBO),通常稳定需平衡对抗损失与重构损失,可能不稳定
灵活度限于简单的先验分布(如高斯)可匹配混合分布或自定义先验(如分类分布)

典型应用场景:

  • 数据生成:生成图像、文本或结构化数据(如MNIST手写数字)。
  • 特征解耦:潜在空间分离数据的不同属性(如姿态、光照)。
  • 异常检测:重构误差高的样本视为异常。
  • 图像到图像的转换:通过对抗约束潜在向量的语义对齐。
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms

# 编码器网络
class Encoder(nn.Module):
    def __init__(self, input_dim=784, latent_dim=20):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Linear(input_dim, 512),
            nn.ReLU(),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Linear(256, latent_dim)
        )
    def forward(self, x):
        return self.fc(x)

# 解码器网络
class Decoder(nn.Module):
    def __init__(self, latent_dim=20, output_dim=784):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.ReLU(),
            nn.Linear(256, 512),
            nn.ReLU(),
            nn.Linear(512, output_dim),
            nn.Sigmoid()
        )
    def forward(self, z):
        return self.fc(z)

# 判别器网络
class Discriminator(nn.Module):
    def __init__(self, latent_dim=20):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.LeakyReLU(0.2),
            nn.Linear(256, 128),
            nn.LeakyReLU(0.2),
            nn.Linear(128, 1),
            nn.Sigmoid()
        )
    def forward(self, z):
        return self.fc(z)

# AAE主模型
class AAE:
    def __init__(self, latent_dim=20, input_dim=784):
        self.encoder = Encoder(input_dim, latent_dim)
        self.decoder = Decoder(latent_dim, input_dim)
        self.discriminator = Discriminator(latent_dim)
        
        self.optimizer_AE = optim.Adam(
            list(self.encoder.parameters()) + list(self.decoder.parameters()),
            lr=1e-3
        )
        self.optimizer_D = optim.Adam(self.discriminator.parameters(), lr=1e-3)

        self.criterion_recon = nn.MSELoss()

    def train_step(self, x_real):
        # 1. 更新自编码器 (重构 + 对抗生成器的角色)
        z_fake = self.encoder(x_real)
        x_recon = self.decoder(z_fake)
        recon_loss = self.criterion_recon(x_recon, x_real)
        
        # 计算对抗损失(编码器尝试欺骗判别器)
        validity_fake = self.discriminator(z_fake)
        loss_adv = -torch.log(validity_fake).mean()
        
        ae_loss = recon_loss + loss_adv
        self.optimizer_AE.zero_grad()
        ae_loss.backward(retain_graph=True)
        self.optimizer_AE.step()

        # 2. 更新判别器 (区分真实和生成潜在向量)
        z_real = torch.randn(x_real.size(0), latent_dim)  # 从先验分布采样
        validity_real = self.discriminator(z_real)
        validity_fake = self.discriminator(z_fake.detach())
        
        d_loss_real = -torch.log(validity_real).mean()
        d_loss_fake = -torch.log(1 - validity_fake).mean()
        d_loss = (d_loss_real + d_loss_fake) / 2
        
        self.optimizer_D.zero_grad()
        d_loss.backward()
        self.optimizer_D.step()
        
        return recon_loss.item(), loss_adv.item(), d_loss.item()

# 数据加载(MNIST示例)
transform = transforms.Compose([transforms.ToTensor(), transforms.Lambda(lambda x: x.view(-1))])
train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_data, batch_size=128, shuffle=True)

# 训练流程
latent_dim = 20
aae = AAE(latent_dim)

for epoch in range(100):
    for batch in train_loader:
        x, _ = batch
        recon_loss, adv_loss, d_loss = aae.train_step(x)
    print(f'Epoch {epoch+1}, Recon Loss: {recon_loss:.3f}, Adv Loss: {adv_loss:.3f}, D Loss: {d_loss:.3f}')

# 生成新样本
with torch.no_grad():
    z = torch.randn(64, latent_dim)  # 从先验分布采样
    generated_images = aae.decoder(z).reshape(-1, 28, 28)
挑战解决方法
对抗训练不稳定采用梯度惩罚(WGAN-GP)、标签平滑
潜在空间塌缩增加重构损失权重或引入多样性正则项
复杂先验建模困难分层潜在空间或多判别器架构(如类别专用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值