Pytorch基础-07-自动编码器

自动编码器(AutoEncoder)是一种可以进行无监督学习的神经网络模型。一般而言,一个完整的自动编码器主要由两部分组成,分别是用于核心特征提取的编码部分和可以实现数据重构的解码部分。

1 自动编码器入门

在自动编码器中负责编码的部分也叫作编码器(Encoder),而负 责解码的部分也叫作解码器(Decoder)。编码器主要负责对原始的输 入数据进行压缩并提取数据中的核心特征,而解码器主要是对在编码器 中提取的核心特征进行展开并重新构造出之前的输入数据。
在这里插入图片描述
如上图就是一个简化的自动编码器模型,它的主要结构是神 经网络,该模型的最左边是用于数据输入的输入层,在输入数据通过神 经网络的层层传递之后得到了中间输入数据的核心特征,这就完成了在 自编码器中输入数据的编码过程。然后,将输入数据的核心特征再传递 到一个逆向的神经网络中,核心特征会被解压并重构,最后得到了一个 和输入数据相近的输出数据,这就是自动编码器中的解码过程。输入数 据通过自动编码器模型的处理后又被重新还原了。

我们会好奇自动编码器模型这种先编码后解码的神经网络模型到底 有什么作用,下面进行讲解。自动编码器模型的最大用途就是实现输入 数据的清洗,比如去除输入数据中的噪声数据、对输入数据的某些关键特征进行增强和放大,等等。举一个比较简单的例子,假设我们现在有 一些被打上了马赛克的图片需要进行除码处理,这时就可以通过自动编码器模型来解决这个问题。其实可以将这个除码的过程看作对数据进行除噪的过程,这也是我们接下来会实现的实践案例。下面看看具体如何实现基于PyTorch的自动编码器。

2 PyTorch之自动编码实战

2.1 通过线性变换实现自动编码器模型

完成代码:

import torch
import torchvision
from torchvision import datasets,transforms
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt

transform = transforms.Compose([transforms.ToTensor(),
                               transforms.Normalize(mean=[0.5],std=[0.5])])
dataset_train = datasets.MNIST(root='./data',
                              transform = transform,
                              train = True,
                              download= False)
dataset_test = datasets.MNIST(root= './data',
                             transform=transform,
                             train=False)
train_load = torch.utils.data.DataLoader(dataset = dataset_train,
                                        batch_size=4,
                                        shuffle=True)
test_load = torch.utils.data.DataLoader(dataset = dataset_test,
                                        batch_size=4,
                                        shuffle=True)
images,label = next(iter(train_load))
print (images.shape)
images_example = torchvision.utils.make_grid(images)
images_example = images_example.numpy().transpose(1,2,0)
mean = [0.5]
std = [0.5]
images_example = images_example*std + mean
plt.imshow(images_example)
plt.show()
noisy_images = images_example + 0.5*np.random.randn(*images_example.shape)
noisy_images = np.clip(noisy_images,0.,1.)
plt.imshow(noisy_images)
plt.show()

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

class AutoEncoder(torch.nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.encoder = torch.nn.Sequential(
            torch.nn.Linear(28 * 28, 128),
            torch.nn.ReLU(),
            torch.nn.Linear(128, 64),
            torch.nn.ReLU(),
            torch.nn.Linear(64, 32),
            torch.nn.ReLU(),
        )
        self.decoder = torch.nn.Sequential(
            torch.nn.Linear(32, 64),
            torch.nn.ReLU(),
            torch.nn.Linear(64, 128),
            torch.nn.ReLU(),
            torch.nn.Linear(128, 28 * 28)
        )

    def forward(self, input):
        output = self.encoder(input)
        output = self.decoder(output)
        return output


model = AutoEncoder()
print(model)

optimizer = torch.optim.Adam(model.parameters())
loss_f = torch.nn.MSELoss()

epoch_n = 10
for epoch in range(epoch_n):
    running_loss = 0

    print('Epoch {}/{}'.format(epoch, epoch_n))
    print('===' * 10)

    for data in train_load:
        X_train, _ = data

        noisy_X_train = X_train + 0.5 * torch.rand(X_train.shape)
        noisy_X_train = torch.clamp(noisy_X_train, 0., 1.)

        X_train, noisy_X_train = Variable(X_train.view(-1, 28 * 28)), (noisy_X_train.view(-1, 28 * 28))
        train_pre = model(noisy_X_train)
        loss = loss_f(train_pre, X_train)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.data

    print('Loss is:{}'.format(running_loss / len(dataset_train)))

在以上代码中损失函数使用的是torch.nn.MSELoss,即计算的是均方误差,我们在之前处理的都是图片分类相关的问题,所以在这里使用交叉熵来计算损失值。而在这个问题中我们需要衡量的是图片在去码后和原始图片之间的误差,所以选择均方误差这类损失函数作为度量。总体的训练流程是我们首先获取一个批次的图片,然后对这个批次的图片进行打码处理并裁剪到指定的像素值范围内,因为之前说过,在MNIST数据集使用的图片中每个像素点的数字值在0到1之间。在得到了经过打码处理的图片后,将其输入搭建好的自动编码器模型中,经过模型处理后输出一个预测图片,用这个预测图片和原始图片进行损失值计算,通过这个损失值对模型进行后向传播,最后就能得到去除图片马赛克效果的模型了。

在每轮训练中,我们都对预测图片和原始图片计算得到的损失值进 行输出,在训练10轮之后,输出的结果如下:
在这里插入图片描述

从以上结果可以看出,我们得到的损失值在逐渐减小,而且损失值 已经在一个足够小的范围内了。最后,我们通过使用一部分测试数据集中的图片来验证我们的模型能否正常工作,代码如下:

data_loader_test = torch.utils.data.DataLoader(dataset=dataset_test,
                                              batch_size = 4,
                                              shuffle = True)
x_test,_ = next(iter(data_loader_test))

img1 = torchvision.utils.make_grid(x_test)
img1 = img1.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
img1 =img1*std + mean

noisy_x_test = img1 +0.5*np.random.randn(*img1.shape)
noisy_x_test = np.clip(noisy_x_test,0.,1.)

plt.figure()
plt.imshow(noisy_x_test)

img2 = x_test + 0.5*torch.randn(*x_test.shape)
img2 = torch.clamp(img2,0.,1.)

img2 = Variable(img2.view(-1,28*28))

test_pred = model(img2)

img_test = test_pred.data.view(-1,1,28,28)
img2 = torchvision.utils.make_grid(img_test)
img2 = img2.numpy().transpose(1,2,0)
img2 = img2*std +mean
img2 = np.clip(img2,0.,1.)
plt.figure()
plt.imshow(img2)

在这里插入图片描述
在这里插入图片描述
下面是使用普通的滤波器的效果:

在这里插入图片描述
自动编码器的去噪效果,还是可圈可点的。

2.2 通过卷积变换实现自动编码器模型

以卷积变换的方式和以线性变换方式构建的自动编码器模型会有较大的区别,而且相对复杂一些,卷积变换的方式仅使用卷积层、最大池化层、上采样层和激活函数作为神经网络结构的主要组成部分,代码如下:

class AutoEncoder2(torch.nn.Module):
    def __init__(self):
        super(AutoEncoder2, self).__init__()
        self.encoder = torch.nn.Sequential(
            torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2),
            torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2)
        )
        self.decoder = torch.nn.Sequential(
            torch.nn.Upsample(scale_factor=2, mode='nearest'),
            torch.nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.Upsample(scale_factor=2, mode='nearest'),
            torch.nn.Conv2d(64, 1, kernel_size=3, stride=1, padding=1)
        )

    def forward(self, input):
        output = self.encoder(input)
        output = self.decoder(output)
        return output

在以上代码中出现了一个我们之前从来没有接触过的上采样层,即 torch.nn.Upsample类。这个类的作用就是对我们提取到的核心特征进行 解压,实现图片的重写构建,传递给它的参数一共有两个,分别是 scale_factor和mode:前者用于确定解压的倍数;后者用于定义图片重构 的模式,可选择的模式有nearest、linear、bilinear和trilinear,其中nearest 是最邻近法,linear是线性插值法,bilinear是双线性插值法,trilinear是 三线性插值法。因为在我们的代码中使用的是最邻近法,所以这里通过 一张图片来看一下最邻近法的具体工作方式。
在这里插入图片描述

训练代码如下:

model = AutoEncoder2()
print(model)

optimizer = torch.optim.Adam(model.parameters())
loss_f = torch.nn.MSELoss()

epoch_n = 5
for epoch in range(epoch_n):
    running_loss = 0

    print('Epoch {}/{}'.format(epoch, epoch_n))
    print('===' * 10)

    for data in train_load:
        X_train, _ = data

        noisy_X_train = X_train + 0.5 * torch.rand(X_train.shape)
        noisy_X_train = torch.clamp(noisy_X_train, 0., 1.)

        X_train, noisy_X_train = Variable(X_train), Variable(noisy_X_train)
        train_pre = model(noisy_X_train)
        loss = loss_f(train_pre, X_train)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.data

    print('Loss is:{}'.format(running_loss / len(dataset_train)))

我们在每轮训练中都对预测图片和原始图片计算得到的损失值进行 输出,在训练5轮之后,输出结果如下:
在这里插入图片描述
看一下测试效果:

data_loader_test = torch.utils.data.DataLoader(dataset=dataset_test,
                                              batch_size = 4,
                                              shuffle = True)
x_test,_ = next(iter(data_loader_test))

img1 = torchvision.utils.make_grid(x_test)
img1 = img1.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
img1 =img1*std + mean

noisy_x_test = img1 +0.5*np.random.randn(*img1.shape)
noisy_x_test = np.clip(noisy_x_test,0.,1.)

plt.figure()
plt.imshow(noisy_x_test)

img2 = x_test + 0.5*torch.randn(*x_test.shape)
img2 = torch.clamp(img2,0.,1.)

img2 = Variable(img2)

test_pred = model(img2)

img_test = test_pred.data.view(-1,1,28,28)
img2 = torchvision.utils.make_grid(img_test)
img2 = img2.numpy().transpose(1,2,0)
img2 = img2*std +mean
img2 = np.clip(img2,0.,1.)
plt.figure()
plt.imshow(img2)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值