PyTorch-11 自编码器AutoEncoders 、 Variational AutoEncoders

PyTorch-11 自编码器AutoEncoders 、 Variational AutoEncoders

这里我们主要涉及如何利用大量Unlabeled数据?
因此Unsupervised Learning这个领域就需要解决这个问题。

下图是主要的三个领域:
在这里插入图片描述

Why needed为什么需要非监督学习

▪ Dimension reduction 从杂乱的数据中找到有用的信息
▪ Preprocessing: Huge dimension, say 224x224, is hard to process
▪ Visualization: https://projector.tensorflow.org/
这里我们查看一下Mnist数据集并用T-SNE方法:
在这里插入图片描述
我们再查看一下Mnist数据集并用PCA方法:
在这里插入图片描述
很明显,PCA方法数字都混在一起,而T-SNE数字之间较为分散且不同类型间相互聚集。

▪ Taking advantages of unsupervised data 利用非监督数据的优势
▪ Compression, denoising, super-resolution … 压缩、去噪、超分辨率……

unsupervised learning最简单的形态:Auto-Encoders

Auto-Encoders就是重建过程:
特殊点1:输入和输出的维度是一样的,这样才能保证其能够重建。
特殊点2:在网络中有一个特殊的latenet code,这部分称为neck。这个neck可以用于升维,也可以用于降维。下图是用于降维,从784=>2。
在这里插入图片描述
在这里插入图片描述

PCA V.S. Auto-Encoders

▪ PCA, which finds the directions of maximal variance in high-dimensional data, select only those axes that have the largest variance.

▪ The linearity of PCA, however, places significant limitations
on the kinds of feature dimensions that can be extracted.
在这里插入图片描述
mnist数据集:
在这里插入图片描述
文本类型:
在这里插入图片描述

降噪 Denoising AutoEncoders

我们添加一些噪声对原来的图片,之后通过重建,依然能发掘正确的语义内容。
在这里插入图片描述

Dropout AutoEncoders

Dropout是帮助神经网络提升的一个小技巧:
比如说我们在到下一层时,有20%的可能会断掉之前的连接,这样会使得网络更加robust,因为这断掉的20%有可能是断掉了噪声部分,因此在做test时,效果可能会好一些。但是如果随着断掉的概率不断提高,到达某一dropout时,会比没有dropout时test效果还要差,所以这个dropout是需要选取一个最好的概率,从而更好的使test得到提升。
在这里插入图片描述

对抗Adversarial AutoEncoders

▪ Distribution of hidden code
可以发现其分布是偏向一侧的,如果我们重建后,其分布依然保持偏向一侧,那我们应该如何解决呢?因此我们这里就提出了Adversarial AutoEncoders。
在这里插入图片描述
▪ Give more details after GAN
给与类似GAN的一个辨别器,Discriminator鉴别器,这样可以用于判别真实值Fake(即经过Encode的值)与我们期望按照某一种正太分布的期望值Real都送于Discriminator这个网络来鉴别,如果这两个之间的差距大,则为0,不属于这个分布,输出Fake,差距小则为1,符合这个分布,输出Real。

这样处理能够完成重建工作以外,还能符合我们期望的某种分布状态,这样就解决了重建后分布依然偏向一侧的问题。
在这里插入图片描述
由Adversarial GAN引出另一个重点Variational autoencoders

Variational Autoencoders 的初衷

Variational Autoencoders (VAEs) 变分自动编码器
Kullback-Leibler散度 (通常简称为KL散度) = KL divergence

Another Approach: 𝒒 𝒛 → 𝒑(𝒛)
▪ Explicitly enforce
在这里插入图片描述
直观地理解KL (p◎q) Intuitively comprehend KL(p◎q)

当两个曲线越接近的时候,即重合区域越多,其p=q;
p和q的差异越来越大时,KL divergence就越来越大。
即两个曲线完全重叠时,KL divergence就是谷底,即最右侧曲线的最小值。
中间图的曲线面积越小(即积分值越小),两条曲线重合度越大,其KL divergence值就越小。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Autoencoders V.S. Variational Autoencoders

对于Fashion-Mnist数据集,VAE重建效果要好于AE。
在这里插入图片描述
VAE在重建效果上好于AE以外,更重要的是VAE是作为一个stochastic随机模型,即不同的sample可以重建出不同的图片,其中sample是随机的,因此可以重建大量不同的图片。
在这里插入图片描述

VAE V.S. GAN

GAN效果好于VAE。
其中下图GAN(右侧)的图片笑脸明显好于左侧的VAE。
在这里插入图片描述

总结:

AE不是stochastic随机模型,因为AE没有随机性。
VAE是有distribution,这个distribution可以sample,sample是随机的,因此是stochastic随机模型。
通过这单元我们将引出下个单元GAN的内容。

实践:

两部分:
1、Auto-Encoders
2、Variational AutoEncoders
都是在mnist数据集

1、Auto-Encoders

(1)首先加载mnist数据集:
main.py文件名:

import torch
from torch.utils.data import DataLoader
from torchvision import transforms,datasets

def main():
    #加载mnist数据集
    mnist_train=datasets.MNIST('mnist',True,transform=transforms.Compose({
        transforms.ToTensor()
    }),download=True)
    mnist_train = DataLoader(mnist_train,batch_size=32, shuffle=True)

    mnist_test= datasets.MNIST('mnist',False,transform=transforms.Compose({
        transforms.ToTensor()
    }),download=True)
    mnist_test = DataLoader(mnist_test,batch_size=32, shuffle=True)

    #测试:定义一个迭代器
    #这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
    x, _ = iter(mnist_train).next()
    print('x:',x.shape)
    print(1*28*28)
    
    

if __name__ == '__main__':
    main()

在这里插入图片描述

(2)编写AutoEncoders
ae.py文件:

import torch
from torch import nn

class AE(nn.Module):
    def __init__(self):
        super(AE, self).__init__()

        #这里假设已经经过reshape了
        #x: torch.Size([32, 1, 28, 28])
        #=>[b,784]
        self.encoder = nn.Sequential(
            nn.Linear(784,256), #降维操作
            nn.ReLU(),#激活函数
            nn.Linear(256,64), #降维操作
            nn.ReLU(),
            nn.Linear(64, 20), #再降维操作
            nn.ReLU()
        ) #=>[b,20]

        #将hidden layer decode出来
        #=>[b,20]
        self.decoder = nn.Sequential(
            nn.Linear(20,64),
            nn.ReLU(),
            nn.Linear(64,256),
            nn.ReLU(),
            nn.Linear(256, 784),
            nn.Sigmoid() #这里是用来将像素压缩到0-1
        ) #=>[b,784]

    def forward(self,x):
        """

        :param x: [b,1,28,28]
        :return:
        """
        batchsz = x.size(0)
        #进行flatten打平操作
        x = x.view(batchsz,784)
        #进行encoder
        x = self.encoder(x) #=>[b,20]
        #进行decoder
        x = self.decoder(x) #=>[b,784]
        #因为是打平过的,所有我们需要reshape一下,变成图片
        x = x.view(batchsz,1,28,28)

        return x

(3)继续编写main.py文件的代码
将mnist数据集输入到AE中进行重建
main.py

import torch
from torch.utils.data import DataLoader
from torchvision import transforms,datasets
from torch import nn, optim

from ae import AE

#利用visdom可视化生成图片
import visdom

def main():
    #加载mnist数据集
    mnist_train=datasets.MNIST('mnist',True,transform=transforms.Compose({
        transforms.ToTensor()
    }),download=True)
    mnist_train = DataLoader(mnist_train,batch_size=32, shuffle=True)

    mnist_test= datasets.MNIST('mnist',False,transform=transforms.Compose({
        transforms.ToTensor()
    }),download=True)
    mnist_test = DataLoader(mnist_test,batch_size=32, shuffle=True)

    #测试:定义一个迭代器
    #这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
    x, _ = iter(mnist_train).next()
    print('x:',x.shape)
    print(1*28*28)

    device = torch.device('cuda')
    model = AE().to(device)
    #这里使用mse作为损失函数
    criteon = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(),lr=1e-3)
    #这里查看一下网络结构:
    print(model)

    #生成一个visdom实例
    viz = visdom.Visdom()

    for epoch in range(1000):

        for batchidx,(x,_) in enumerate(mnist_train):
            # [b,1,28,28]
            x = x.to(device)

            #这里的x_hat表示重建过的结果
            x_hat= model(x)

            loss = criteon(x_hat,x)

            #backpropagation
            #先清零
            optimizer.zero_grad()
            #回退计算
            loss.backward()
            #跟新一次优化器
            optimizer.step()

        #loss是一个tensor类型,因此这里需要转为numpy
        print('epoch:',epoch ,'; loss:', loss.item())

        #测试数据
        #这里是不需要梯度了
        x,_ = iter(mnist_test).next()
        x=x.to(device)
        with torch.no_grad():
            x_hat =model(x)
        viz.images(x,nrow=8, win='x',opts=dict(title='x'))
        viz.images(x_hat,nrow=8,win='x_hat',opts=dict(title='x_hat'))



if __name__ == '__main__':
    main()

在这里插入图片描述
效果还是可以的,就是重建后有一些些模糊。
在这里插入图片描述
以上就完成了Auto-Encoders

2、Variational AutoEncoders

vae.py
在原来的ae.py基础上做一些调整:

import numpy as np
import torch
from torch import nn


class VAE(nn.Module):
    def __init__(self):
        super(VAE, self).__init__()

        # 这里假设已经经过reshape了
        # x: torch.Size([32, 1, 28, 28])
        # =>[b,784]
        # u:[b,10]
        # Sigma:[b,10]
        self.encoder = nn.Sequential(
            nn.Linear(784, 256),  # 降维操作
            nn.ReLU(),  # 激活函数
            nn.Linear(256, 64),  # 降维操作
            nn.ReLU(),
            nn.Linear(64, 20),  # 再降维操作
            nn.ReLU()
        )  # =>[b,20]

        # 将hidden layer decode出来
        # =>[b,20]
        self.decoder = nn.Sequential(
            nn.Linear(10, 64),
            nn.ReLU(),
            nn.Linear(64, 256),
            nn.ReLU(),
            nn.Linear(256, 784),
            nn.Sigmoid()  # 这里是用来将像素压缩到0-1
        )  # =>[b,784]

    def forward(self, x):
        """

        :param x: [b,1,28,28]
        :return:
        """
        batchsz = x.size(0)
        # 进行flatten打平操作
        x = x.view(batchsz, 784)
        # 进行encoder
        x = self.encoder(x)  # =>[b,20],including mean and sigma

        h_ = x
        # [b,20] =>拆分为[b,10] and [b,10]
        mu, sigma = h_.chunk(2, dim=1)  # 这里chunk函数就是拆分
        # Variational AutoEncoders是在encoder与decoder中间有所变化
        # reparametrize trick这里是用来解决不能导sample的问题:
        # epison~N(0,1)
        # 形成了一个可导的sample的操作
        h = mu + sigma * torch.randn_like(sigma)

        # decoder
        x_hat = self.decoder(h)
        # reshpae
        x_hat = x_hat.view(batchsz, 1, 28, 28)

        # KL-divergence
        kld = 0.5 * torch.sum(
            torch.pow(mu, 2) +
            torch.pow(sigma, 2) -
            torch.log(1e-8 + torch.pow(sigma, 2)) - 1
        ) / (batchsz * 28 * 28)

        # #进行decoder
        # x = self.decoder(x) #=>[b,784]
        # #因为是打平过的,所有我们需要reshape一下,变成图片
        # x = x.view(batchsz,1,28,28)

        return x_hat, kld

main.py
在原来的main.py基础上做一些调整:

import torch
from torch.utils.data import DataLoader
from torchvision import transforms, datasets
from torch import nn, optim

from ae import AE
from vae import VAE

# 利用visdom可视化生成图片
import visdom


def main():
    # 加载mnist数据集
    mnist_train = datasets.MNIST('mnist', True, transform=transforms.Compose({
        transforms.ToTensor()
    }), download=True)
    mnist_train = DataLoader(mnist_train, batch_size=32, shuffle=True)

    mnist_test = datasets.MNIST('mnist', False, transform=transforms.Compose({
        transforms.ToTensor()
    }), download=True)
    mnist_test = DataLoader(mnist_test, batch_size=32, shuffle=True)

    # 测试:定义一个迭代器
    # 这里需要注意,我们不需要label,因为我们做的是unsupervised,不能使用label信息
    x, _ = iter(mnist_train).next()
    print('x:', x.shape)
    print(1 * 28 * 28)

    # 将mnist数据集输入到AE中进行重建
    device = torch.device('cuda')
    # model = AE().to(device)
    model = VAE().to(device)
    # 这里使用mse作为损失函数
    criteon = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    # 这里查看一下网络结构:
    print(model)

    # 生成一个visdom实例
    viz = visdom.Visdom()

    for epoch in range(1000):

        for batchidx, (x, _) in enumerate(mnist_train):
            # [b,1,28,28]
            x = x.to(device)

            # 这里的x_hat表示重建过的结果
            x_hat, kld = model(x)

            loss = criteon(x_hat, x)

            if kld is not None:
                elbo = -loss - 1.0 * kld
                loss = -elbo

            # backpropagation
            # 先清零
            optimizer.zero_grad()
            # 回退计算
            loss.backward()
            # 跟新一次优化器
            optimizer.step()

        # loss是一个tensor类型,因此这里需要转为numpy
        print('epoch:', epoch, '; loss:', loss.item(), '; kld:', kld.item())

        # 测试数据
        # 这里是不需要梯度了
        x, _ = iter(mnist_test).next()
        x = x.to(device)
        with torch.no_grad():
            x_hat, kld  = model(x)
        viz.images(x, nrow=8, win='x', opts=dict(title='x'))
        viz.images(x_hat, nrow=8, win='x_hat', opts=dict(title='x_hat'))


if __name__ == '__main__':
    main()

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Henrik698

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值