大家好,欢迎来到IT知识分享网。
1.自编码器的由来
最初的自编码器是用来降维的,后来也逐渐用于去噪、生成任务。
2.自编码器的基本结构
自编码器(autoencoder)内部有一个隐藏层 h,可以产生编码(code)表示输入。该网络可以看作由两部分组成:一个由函数 h = f ( x ) h = f(x) h=f(x) 表示的编码器和一个生成重构的解码器 r = g ( h ) r = g(h) r=g(h),整体结构如下图所示:
自编码器通过内部表示或编码 h 将输入 x 映射到输出(称为重构)r。自编码器具有两个组件:编码器 f (将 x 映射到 h)和解码器 g(将 h 映射到 r)。映射关系可以分别表示为 p e n c o d e r ( h ∣ x ) p_{encoder}(h|x) pencoder(h∣x)、 p d e c o d e r ( r ∣ h ) p_{decoder}(r|h) pdecoder(r∣h)。
3.自编码器的一些基本概念
3.1欠完备与过完备
3.2自编码器的目的与损失函数
3.3自编码器的正则化
4.多种自编码器
自编码器可以分为以下几种:
下面介绍几种常见的自编码器。
4.1 传统自编码器(AE)
4.2 去噪自编码器(DAE)
4.3稀疏自编码器(SAE)
稀疏自编码器利用了X的先验信息,这个先验信息就是X的稀疏度。它的网络结构和AE没有什么区别,但是损失函数变了,添加了一项KL散度,是编码后h的稀疏度和真实稀疏度之间的散度。其中 β \beta β是控制稀疏惩罚的系数,为0~1.
J S A E ( θ ) = J ( X , X d ) + β ∑ j = 1 t K L ( ρ ∥ ρ ^ j ) J_{SAE}(\theta)=J(X,X^d)+\beta\sum_{j=1}^tKL(\rho\parallel\hat{\rho}_j) JSAE(θ)=J(X,Xd)+β∑j=1tKL(ρ∥ρ^j)
首先定义每个隐藏单元 j j j的平均激活值 ρ ^ j : \hat{\rho}_j: ρ^j:
ρ ^ j = 1 n ∑ i = 1 n h j ( x i ) \hat{\rho}_j=\frac{1}{n}\sum_{i=1}^nh_j{(x_i)} ρ^j=n1i=1∑nhj(xi)
其中, n n n是训练样本数量, h j ( x i ) h_j{(x_i)} hj(xi)是第 i i i个样本对于隐藏单元 j j j的激活值。
然后定义目标稀疏度 ρ \rho ρ,这是希望隐藏单元的平均激活值。例如,如果 ρ \rho ρ较小(如0.05),则希望大多数隐藏单元在任何给定时间都不活跃。
最后,将稀疏惩罚项加入到损失函数中,使用KL散度来衡量目标稀疏度和实际稀疏度之间的差异:
K L ( ρ ∣ ∣ ρ ^ j ) = ρ log ρ ρ ^ j + ( 1 − ρ ) log 1 − ρ 1 − ρ ^ j \mathrm{KL}(\rho||\hat{\rho}_j)=\rho\log\frac{\rho}{\hat{\rho}_j}+(1-\rho)\log\frac{1-\rho}{1-\hat{\rho}_j} KL(ρ∣∣ρ^j)=ρlogρ^jρ+(1−ρ)log1−ρ^j1−ρ
稀疏惩罚项的总和是所有隐藏单元的KL散度之和:
∑ j = 1 t K L ( ρ ∣ ∣ ρ j ^ ) \sum_{j=1}^{t}\mathrm{KL}(\rho||\hat{\rho_j}) j=1∑tKL(ρ∣∣ρj^)
KL散度是描述两个分布之间差异的指标,KL散度越小,分布越接近,具体公式如下:
K L ( ρ ∥ ρ ^ j ) = ρ log ρ ρ ^ j + ( 1 − ρ ) log 1 − ρ 1 − ρ ^ j KL(\rho\parallel\hat{\rho}_j)=\rho\log\frac{\rho}{\hat{\rho}_j}+(1-\rho)\log\frac{1-\rho}{1-\hat{\rho}_j} KL(ρ∥ρ^j)=ρlogρ^jρ+(1−ρ)log1−ρ^j1−ρ
4.4 收缩自编码器(CAE)
这里的收缩指的是在学习过程中对隐藏层表示进行收缩,使得隐藏层表示对输入数据的小变化不敏感,从而增强模型的鲁棒性和特征提取能力。
使得隐藏层表示对输入数据的小变化不敏感?也就是说X变化,h不变或变化很小,如果这种关系用导数关系来表示,h对X的一阶导应该越小越好,所以有如下损失函数,同样通过系数 λ \lambda λ来控制收缩。
J C A E ( θ ) = J ( X , X d ) + λ ∥ J f ( x ) ∥ F 2 J_{CAE}(\theta)=J(X,X^d)+\lambda\parallel J_f(x)\parallel_F^2 JCAE(θ)=J(X,Xd)+λ∥Jf(x)∥F2
其中, ∥ J f ( x ) ∥ F 2 = ∑ j = 1 t ∑ i = 1 n ( ∂ h j ( x ) ∂ x i ) 2 \parallel J_f(x)\parallel_F^2=\sum_{j=1}^t\sum_{i=1}^n(\frac{\partial h_j(x)}{\partial x_i})^2 ∥Jf(x)∥F2=∑j=1t∑i=1n(∂xi∂hj(x))2, J f ( x ) J_f(x) Jf(x)是雅可比矩阵(Jacobian Matrix),雅可比矩阵是向量值函数对其输入向量的偏导数组成的矩阵。
Tips:①这里可以看到,如果CAE的编码是线性的,那么CAE就和正则化自编码器没有区别了,因为线性的一阶导就是权重参数。
②CAE和DAE都对带噪数据有鲁棒性,CAE是对提取的特征有鲁棒性;DAE是对输出的去噪数据有鲁棒性。
4.5 卷积自编码器(CoAE)
4.6 变分自编码器(VAE)
# -*- coding: utf-8 -*- """ Created on January 28, 2021 @author: Siqi Miao """ import os from tqdm import tqdm os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" import torch import torch.nn as nn from torchvision import transforms from torchvision.utils import save_image from torchvision.datasets import MNIST class VAE(nn.Module): def __init__(self, in_features, latent_size, y_size=0): super(VAE, self).__init__() self.latent_size = latent_size self.encoder_forward = nn.Sequential( nn.Linear(in_features + y_size, in_features), nn.LeakyReLU(), nn.Linear(in_features, in_features), nn.LeakyReLU(), nn.Linear(in_features, self.latent_size * 2) # latent_size表示潜变量的个数,每一个变量有均值和方差两个数值 ) self.decoder_forward = nn.Sequential( nn.Linear(self.latent_size + y_size, in_features), # 解码器输入的时候只需要输入编码器输出的潜在变量的均值 nn.LeakyReLU(), nn.Linear(in_features, in_features), nn.LeakyReLU(), nn.Linear(in_features, in_features), nn.Sigmoid() ) def encoder(self, X): out = self.encoder_forward(X) mu = out[:, :self.latent_size] # 前latent_size个数据是均值 log_var = out[:, self.latent_size:] # 后latent_size个数据是log(方差) return mu, log_var def decoder(self, z): mu_prime = self.decoder_forward(z) return mu_prime def reparameterization(self, mu, log_var): # Reparameterization Trick epsilon = torch.randn_like(log_var) # 产生和log_var维度一样的高斯分布数据 z = mu + epsilon * torch.sqrt(log_var.exp()) # log_var.exp()=var,sqrt(var)就是sigema return z def loss(self, X, mu_prime, mu, log_var): # mu_prime编码器的输出;mu潜变量的均值,也就是潜变量的值了;log_var潜变量的log(方差) # reconstruction_loss = F.mse_loss(mu_prime, X, reduction='mean') is wrong! #print(mu_prime.shape) # [1024,784] #print(mu.shape) # [1024,64] #print(log_var.shape) # [1024,64] # torch.square 是一个用于计算张量中每个元素的平方的函数。这个函数返回一个新的张量,其中包含原始张量中每个元素的平方值 reconstruction_loss = torch.mean(torch.square(X - mu_prime).sum(dim=1)) # sum(dim=1)表示对列求和,torch.mean就相当于是对1024个样本求均值了,也就是公式里的1/n latent_loss = torch.mean(0.5 * (log_var.exp() + torch.square(mu) - log_var).sum(dim=1)) # sum(dim=1)表示对潜变量求和,torch.mean相当于是对1024个样本求均值 return reconstruction_loss + latent_loss def forward(self, X, *args, kwargs): mu, log_var = self.encoder(X) z = self.reparameterization(mu, log_var) mu_prime = self.decoder(z) return mu_prime, mu, log_var class CVAE(VAE): # 继承VAE类,所以可以使用VAE的编码和解码器 def __init__(self, in_features, latent_size, y_size): super(CVAE, self).__init__(in_features, latent_size, y_size) def forward(self, X, y=None, *args, kwargs): y = y.to(next(self.parameters()).device) #print(y.shape) # [1024] X_given_Y = torch.cat((X, y.unsqueeze(1)), dim=1) #print(X_given_Y.shape) # [1024,785] mu, log_var = self.encoder(X_given_Y) z = self.reparameterization(mu, log_var) z_given_Y = torch.cat((z, y.unsqueeze(1)), dim=1) mu_prime_given_Y = self.decoder(z_given_Y) return mu_prime_given_Y, mu, log_var def train(model, optimizer, data_loader, device, name='VAE'): model.train() total_loss = 0 pbar = tqdm(data_loader) for X, y in pbar: #print(X.shape) # [1024,1,28,28] #print(y.shape) # [1024] batch_size = X.shape[0] X = X.view(batch_size, -1).to(device) #print(X.shape) # [1024,784] model.zero_grad() #将模型中所有参数的梯度缓存清零。在进行反向传播计算梯度之前,必须先将之前计算的梯度清零。这是因为在 PyTorch 中,梯度是累积的。 if name == 'VAE': mu_prime, mu, log_var = model(X) else: mu_prime, mu, log_var = model(X, y) loss = model.loss(X.view(batch_size, -1), mu_prime, mu, log_var) loss.backward() optimizer.step() total_loss += loss.item() pbar.set_description('Loss: {loss:.4f}'.format(loss=loss.item())) return total_loss / len(data_loader) @torch.no_grad() def save_res(vae, cvae, data, latent_size, device): num_classes = len(data.classes) # raw samples from dataset out = [] # 用于存储每个类别的图像 for i in range(num_classes): # 提取类别为 i 的图像 img = data.data[torch.where(data.targets == i)[0][:num_classes]] out.append(img) out = torch.stack(out).transpose(0, 1).reshape(-1, 1, 28, 28) # 将图像堆叠在一起,并转置维度以便于保存 save_image(out.float(), './img/raw_samples.png', nrow=num_classes, normalize=True) # 100张图像 # samples generated by vanilla VAE z = torch.randn(num_classes 2, latent_size).to(device) # print(z.shape) # [100,64] out = vae.decoder(z) save_image(out.view(-1, 1, 28, 28), './img/vae_samples.png', nrow=num_classes) # sample generated by CVAE z = torch.randn(num_classes 2, latent_size).to(device) y = torch.arange(num_classes).repeat(num_classes).to(device) z_given_Y = torch.cat((z, y.unsqueeze(1)), dim=1) out = cvae.decoder(z_given_Y) save_image(out.view(-1, 1, 28, 28), './img/cvae_samples.png', nrow=num_classes) def main(): device = 'cuda' if torch.cuda.is_available() else 'cpu' device = torch.device(device) batch_size = 256 * 4 epochs = 50 latent_size = 64 in_features = 28 * 28 lr = 0.001 data = MNIST('./dataset/', download=True, transform=transforms.ToTensor()) data_loader = torch.utils.data.DataLoader(data, batch_size=batch_size, shuffle=True) # train VAE vae = VAE(in_features, latent_size).to(device) optimizer = torch.optim.AdamW(vae.parameters(), lr=lr) print('Start Training VAE...') for epoch in range(1, 1 + epochs): loss = train(vae, optimizer, data_loader, device, name='VAE') print("Epochs: {epoch}, AvgLoss: {loss:.4f}".format(epoch=epoch, loss=loss)) print('Training for VAE has been done.') # train VCAE cvae = CVAE(in_features, latent_size, y_size=1).to(device) optimizer = torch.optim.AdamW(cvae.parameters(), lr=lr) print('Start Training CVAE...') for epoch in range(1, 1 + epochs): loss = train(cvae, optimizer, data_loader, device, name='CVAE') print("Epochs: {epoch}, AvgLoss: {loss:.4f}".format(epoch=epoch, loss=loss)) print('Training for CVAE has been done.') save_res(vae, cvae, data, latent_size, device) if __name__ == '__main__': main()
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/109948.html


