AE(Autoencoder)详解 (pytorch实现)

什么是自编码器

简单的自编码是一种三层神经网络模型,包含了数据输入层、隐藏层、输出重构层,同时它是一种无监督学习模型。在有监督的神经网络中,我们的每个训练样本是(X,y),然后y一般是我们人工标注的数据。比如我们用于手写的字体分类,那么y的取值就是0~9之间数值,最后神经网络设计的时候,网络的输出层是一个10个神经元的网络模型(比如网络输出是(0,0,1,0,0,0,0,0,0,0),那么就表示该样本标签为2)。

然而自编码是一种无监督学习模型,我们训练数据本来是没有标签的,那么自编码是这样干的,它令每个样本的标签为y=x,也就是每个样本的数据x的标签也是x。自编码就相当于自己生成标签,而且标签是样本数据本身。

自编码器的结构

在这里插入图片描述

自编码器包含两个过程:

  1. 从输入层到隐藏层的原始数据X的编码过程:
    h = g θ 1 ( x ) = σ ( w 1 T x + b 1 ) h = g_{\theta_1}(x) = \sigma(w_1^Tx+b_1) h=gθ1(x)=σ(w1Tx+b1)
  2. 从隐藏层到输出层的解码过程:
    x ^ = g θ 2 ( h ) = σ ( w 2 T h + b 2 ) \hat{x} = g_{\theta_2}(h) = \sigma(w_2^Th +b_2) x^=gθ2(h)=σ(w2Th+b2)

那么数据X的重构误差损失函数就是 就是衡量 x 与 x ^ \hat{x} x^ 之间的距离即可,如SML,BinaryCrossEntropy 等 。

因此等我们训练完网络后,当我们随便输入一个测试样本数据X’,那么自编码网络将对X‘先进行隐藏层的编码,然后再从隐藏-》输出完成解码,重构出X’。隐藏层可以看成是原始数据x的另外一种特征表达。

自编码器的应用

  1. 数据降维 可视化
  2. 数据去噪
  3. 图像压缩
  4. 特征学习

常见的几种自编码器

  1. 稀疏自编码(SAE)
  2. 降噪自编码(DAE)
  3. 收缩自编码(CAE)
  4. 栈式自编码

表示能力、层的大小和深度

万能近似定理保证至少有一层隐藏层且隐藏单元足够多的前馈神经网络能以任意精度近似任意函数(在很大范围里),这是非平凡深度(至少有一层隐藏层)的一个主要优点。这意味着具有单隐藏层的自编码器在数据域内能表示任意近似数据的恒等函数。但是,从输入到编码的映射是浅层的。这意味这我们不能任意添加约束,比如约束编码稀疏。深度自编码器(编码器至少包含一层额外隐藏层)在给定足够多的隐藏单元的情况下,能以任意精度近似任何从输入到编码的映射。

深度可以指数地降低表示某些函数的计算成本。深度也能指数地减少学习一些函数所需的训练数据量。实验中,深度自编码器能比相应的浅层或线性自编码器产生更好的压缩效率。

训练深度自编码器的普遍策略是训练一堆浅层的自编码器来贪心地预训练相应的深度架构。所以即使最终目标是训练深度自编码器,我们也经常会遇到浅层自编码器。

使用 Pytorch 建立简单的自编码器

"""
Created on Fri Mar 22 12:55:55 2019

@author: zhe
"""

import torch
from torch import nn, optim
from torch.utils.data import DataLoader
from torch.autograd import Variable
from torchvision import transforms, datasets, models
import visdom
import time
import numpy as np

np.random.seed(123)
torch.manual_seed(123)


viz = visdom.Visdom()

BATCH_SIZE = 64
LR = 0.001
EPOCHS = 10
HIDDEN_SIZE = 30

USE_GPU = True
if USE_GPU:
    gpu_status = torch.cuda.is_available()
else:
    gpu_status = False

train_dataset = datasets.MNIST('../../data/', True, transforms.ToTensor(), download=False)
test_dataset = datasets.MNIST('../../data/', False, transforms.ToTensor())
train_loader = DataLoader(train_dataset, BATCH_SIZE, True)
test_loader = DataLoader(test_dataset, 400, False)

dataiter = iter(train_loader)
inputs, labels = dataiter.next()
# 可视化visualize
viz.images(inputs[:16], nrow=8, padding=3)
time.sleep(0.5)
image = viz.images(inputs[:16], nrow=8, padding=3)

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

        self.en_conv = nn.Sequential(
            nn.Conv2d(1, 16, 4, 2, 1),
            nn.BatchNorm2d(16),
            nn.Tanh(),
            nn.Conv2d(16, 32, 4, 2, 1),
            nn.BatchNorm2d(32),
            nn.Tanh(),
            nn.Conv2d(32, 16, 3, 1, 1),
            nn.BatchNorm2d(16),
            nn.Tanh()
        )
        self.en_fc = nn.Linear(16*7*7, HIDDEN_SIZE)
        self.de_fc = nn.Linear(HIDDEN_SIZE, 16*7*7)
        self.de_conv = nn.Sequential(
            nn.ConvTranspose2d(16, 16, 4, 2, 1),
            nn.BatchNorm2d(16),
            nn.Tanh(),
            nn.ConvTranspose2d(16, 1, 4, 2, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        en = self.en_conv(x)
        code = self.en_fc(en.view(en.size(0), -1))
        de = self.de_fc(code)
        decoded = self.de_conv(de.view(de.size(0), 16, 7, 7))
        return code, decoded

net = AutoEncoder()

data = torch.Tensor(BATCH_SIZE ,28*28)
data = Variable(data)
if torch.cuda.is_available():
    net = net.cuda()
    data = data.cuda()

optimizer = torch.optim.Adam(net.parameters(), lr=LR)
loss_f = nn.MSELoss()

scatter=viz.scatter(X=np.random.rand(2, 2), Y=(np.random.rand(2) + 1.5).astype(int), opts=dict(showlegend=True))

for epoch in range(EPOCHS):
    net.train()
    for step, (images, _) in enumerate(train_loader, 1):
        net.zero_grad()
        data.data.resize_(images.size()).copy_(images)
        # data = data.view(-1, 28*28)
        code, decoded = net(data)
        loss = loss_f(decoded, data)
        loss.backward()
        optimizer.step()

        if step % 10 == 0:
            net.eval()
            eps = Variable(inputs)   #.view(-1, 28*28))
            if torch.cuda.is_available():
                eps = eps.cuda()
            tags, fake = net(eps)

            viz.images(fake[:16].data.cpu().view(-1, 1, 28, 28), win=image, nrow=8)
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, step * len(data), len(train_loader.dataset),
                       100. * step / len(train_loader),
                       loss.item()))
            if step == 200:
               viz.images(fake[:16].data.cpu().view(-1, 1, 28, 28), nrow=8 ,opts=dict(title="epoch:{}".format(epoch)))
               # viz.scatter(X=tags.data.cpu(), Y=labels + 1, win=scatter, opts=dict(showlegend=True))

if HIDDEN_SIZE == 3:
    for step, (images, labels) in enumerate(test_loader, 1):
        if step > 1:
            break
        if torch.cuda.is_available():
            images = images.cuda()
        images = Variable(images)
        tags, fake = net(images)
        viz.scatter(X=tags.data.cpu(), Y=labels + 1, win=scatter, opts=dict(showlegend=True))

程序运行结果

原始图片
训练图片

参考文献1
参考文献2
参考文献3

  • 9
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TransientYear

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

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

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

打赏作者

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

抵扣说明:

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

余额充值