前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【干货】对抗自编码器PyTorch手把手实战系列——PyTorch实现自编码器

【干货】对抗自编码器PyTorch手把手实战系列——PyTorch实现自编码器

作者头像
WZEARW
发布2018-04-16 11:49:54
3K0
发布2018-04-16 11:49:54
举报
文章被收录于专栏:专知专知

即使是非计算机行业, 大家也知道很多有名的神经网络结构, 比如CNN在处理图像上非常厉害, RNN能够建模序列数据. 然而CNN, RNN之类的神经网络结构本身, 并不能用于执行比如图像的内容和风格分离, 生成一个逼真的图片, 用少量的label信息来分类图像, 或者做数据压缩等任务. 因为上述几个任务, 都需要特殊的网络结构和训练算法 .

有没有一个网络结构, 能够把上述任务全搞定呢? 显然是有的, 那就是对抗自编码器Adversarial Autoencoder(AAE) . 在本文中, 我们将构建一个AAE, 来压缩数据, 分离图像的内容和风格, 用少量样本来分类图像, 然后生成它们。

本系列文章, 专知小组一共分成四篇讲解:

  • 自编码器, 以及如何用PyTorch实现自编码器
  • 对抗自编码器, 以及如何用PyTorch实现对抗自编码器
  • 自编码器实例应用: 被玩坏的神经画风迁移(没办法太典型了)
  • 自编码器实例应用: 用极少label分类MNIST

PyTorch实现自编码器

首先我们先回顾一下什么是自编码器 , 然后用PyTorch 进行简单的实现。

1.自编码器



如图所示, 自编码器的输入和输出是一样的, 也就是说, 它不需要监督信息(label), 它主要有两部分构成:

编码器(Encoder) : 输入数据

(可以是文本, 图像, 视频, 语音), 输出latent code, 比如上图, 输入数据是

的一张图像, 输出的是

的隐层值h, 或者称之为latent code, 当然h的大小你可以随便设置. 在这种设置下, encoder 起到了压缩图片的作用, 将一个图片从

变化成了

, 就像你用压缩软件( 比如WinRAR)压缩图片一样. 如果我们把Encoder记做函数q, 那么Encoder就是在做:

解码器(Decoder) : 输入数据为上一步的输出数据h, 它努力把h重构成x, 上图的例子中, Decoder需要把

的重构回

,并使得

和原来的x约相似越好, 就像你用压缩然见解压一个压缩文件一样. 如果我们把Decoder记做函数p, 那么Decoder就是在做:

这个模型似乎是一个天然的降维模型. 但是, 除了降维,Autoencoder还能干什么?

  • 图片降噪(Image Denosiong), 输入嘈杂的图像, Autoencoder可以生成清晰无噪声的图像. 当把数据输入自编码器后, 我们可以强制让自编码器的隐层学习更鲁棒的特征, 而不是仅仅识别他们, 这样的自编码器, 在下图左边的图上进行训练, 就可以把中间的噪声数据, 重建成右边的样子。
  • 语义哈希, 这可以降低数据的维度, 加速信息检索, 目前有非常的人在研究这一方向.
  • 生成模型, 比如本系列文章要介绍的Adversarial Autoencoder(AAE)
  • 其他大量应用

2.PyTorch实现



我们先从简单的全连接网络开始我们的第一部分.

这个Encoder包含

的输入层, 两层隐层, 每层1000个节点, 一个输出层层数为2

代码语言:javascript
复制
import torch
import torch.nn as nn

encoder = nn.Sequential(
  nn.Linear(28*28, 1000)
  nn.ReLU()
  nn.Linear(1000, 1000)
  nn.ReLU()
  nn.Linear(1000, 2)
)
代码语言:javascript
复制
decoder = nn.Sequential(
  nn.Linear(2, 1000)
  nn.ReLU()
  nn.Linear(2, 1000)
  nn.ReLU()
  nn.Linear(1000, 28*28)
  nn.Sigmoid() #压缩到0-1之间 
)

所以, 整个模型是:

代码语言:javascript
复制
import torch
import torch.nn as nn


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

        self.encoder = nn.Sequential(
            nn.Linear(28 * 28, 1000)
        nn.ReLU()
        nn.Linear(1000, 1000)
        nn.ReLU()
        nn.Linear(1000, 2)
        )
        self.decoder = nn.Sequential(
            nn.Linear(2, 1000)
        nn.ReLU()
        nn.Linear(2, 1000)
        nn.ReLU()
        nn.Linear(1000, 28 * 28)
        nn.Sigmoid()
        )

        def forward(self, x):
            encoded = self.encoder(x)
            decoded = self.decoder(encoded)

            return encoded, decoded

模型实现完成后, 我们要准备一下数据:

代码语言:javascript
复制
import torchvision
import torch.utils.data as Data


BATCH_SIZE = 64
DOWNLOAD_MNIST = False # 本地没有数据的话, 设成True可以下载

# 从torchvison里加载MNIST数据, 然后转成Tensor
train_data = torchvision.datasets.MNIST(
    root='./mnist/',
    train=True,                                    
    transform=torchvision.transforms.ToTensor(),   
    download=DOWNLOAD_MNIST,
)

# 加载数据, 维度是(batch_size, n_channel, n_width, n_height )
train_loader = Data.DataLoader(dataset=train_data, 
                               batch_size=BATCH_SIZE, 
                               shuffle=True)

我们选择MSE损失函数来度量重构出来的图像

与原来的图像x的相似程度

代码语言:javascript
复制
loss_func = nn.MSELoss()

LR = 0.005
# 用Adam来优化参数
optimizer = torch.optim.Adam(autoencoder.parameters(), lr=LR)

接下来就可以实现训练步骤了:

代码语言:javascript
复制
EPOCH = 10
autoencoder = AutoEncoder()
for epoch in range(EPOCH):
    for step, (x, y) in enumerate(train_loader):
        b_x = Variable(x.view(-1, 28*28))   # reshape 成(batch, 28*28)
        b_y = Variable(x.view(-1, 28*28))   
        b_label = Variable(y)               # batch label

        encoded, decoded = autoencoder(b_x)

        loss = loss_func(decoded, b_y) # 算误差     
        optimizer.zero_grad()          # 清空累计导数     
        loss.backward()                # 求导     
        optimizer.step()               # 优化一步                    

        if step % 100 == 0:
            print('Epoch: ', epoch, '| train loss: %.4f' % loss.data[0])

可以看一下重建的图像怎么样:

我们可以观察到, 输入的这张3的图片, 一些奇怪的地方呗去掉了(3的左上角).

接下来, 让我们看一下latent code, 它只有2维, 我们可以随便填一个值让Decoder去生成图片, 比如我们认为的令

,让后将它输入到Decoder中:

这好像是个6的图片, 当然也可能是0, 不管怎么说, 这不是一个清晰的数字图片. 这是因为Encoder的输出并不能覆盖整个2维空间(它的输出分布有很多空白)。 因此,如果我们输入一些Decoder没见过的值,我们会看到一下奇怪的输出图像。 这可以通过在生成latent code 时, 将Encoder的输出限制为随机分布(比如,均值为0.0和标准偏差为2.0的正态分布)。 Adversarial Autoencoder就是这么做到的,我们将在第2部分中看看它的实现。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 专知 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档