堆疊自編碼器
對於很多數據來說,僅使用兩層神經網絡的自編碼器結構還不足以獲取一種好的數據表示,為了獲取更好的數據表示,我們可以使用更深層的神經網絡。深層神經網絡作為自編碼器提取的數據表示一般會更加抽象,能夠更好地捕捉到數據的語義信息。在實踐中經常使用逐層堆疊的方式來訓練一個深層的自編碼器,稱為堆疊自編碼器(SAE)。堆疊自編碼器一般可以采用逐層訓練來學習網絡參數。

降噪自編碼器
我們使用自編碼器的目的是得到有效的數據表示,而有效的數據表示除了具有最小重構誤差或稀疏性等性質之外,還可以要求其具備其他性質,比如對數據部分損壞的魯棒性。高維數據(如圖像)一般具有一定的冗余信息,比如我們可以根據一張部分損壞的圖像聯想到完整的內容。因此我們希望自編碼器也能夠從部分損壞的數據中得到有效的數據表示,並能夠恢復出完整的原始信息。
降噪自編碼器就是一種通過引入噪聲來增加編碼魯棒性的自編碼器。對於一個向量\(x\),我們首先根據一個比例\(\mu\)(一般小於0.5)隨機將\(x\)的一些維度的值設置為0,得到一個被損壞的向量\(\tilde x\),然后將被損壞的向量\(\tilde x\)輸入給自編碼器得到編碼\(z\),並重構出無損的原始輸入\(x\)。


堆疊降噪自編碼器
堆疊降噪自編碼器(SDAE)與降噪自編碼器(DAE)的關系就像堆疊自編碼器(SAE)與自編碼器(AE)之間的關系。SDAE的輸入是加入噪聲的原始數據\(x\),將DAE原先的單隱藏層堆疊多次便是SDAE。
代碼實現
本文利用pytorch實現了SDAE,數據集是MNIST數據,我們通過對原始圖片加入一定比例的椒鹽噪聲,然后訓練能夠重構出無損圖片的SDAE。下圖是無損圖和加入噪聲的圖。
![]() |
![]() |
文件結構
- config.py:保存模型超參數
import argparse
class HyperParam:
def __init__(self):
self.parse = argparse.ArgumentParser()
self.parse.add_argument("--epoch", type=int, default=40)
self.parse.add_argument("--batch_size", type=int, default=64)
self.parse.add_argument("--image_size", type=int, default=28 * 28)
self.parse.add_argument("--lr", type=float, default=0.0002)
- sdae.py:堆疊降噪自編碼器模型實現
import os
import torch
from config import HyperParam
import torch.nn as nn
import torch.optim as optim
import torchvision
import torch.utils.data as Data
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
hyparam = HyperParam()
opt = hyparam.parse.parse_args()
datapath = "C:/Users/DZL102/Desktop/VAE/data"
train_dataset = torchvision.datasets.MNIST(root=datapath,
train=True,
transform=transforms.ToTensor())
train_loader = Data.DataLoader(dataset=train_dataset,
batch_size=opt.batch_size,
shuffle=True)
test_dataset = torchvision.datasets.MNIST(root=datapath,
transform=transforms.ToTensor())
test_loader = Data.DataLoader(dataset=test_dataset,
batch_size=opt.batch_size,
shuffle=True)
def make_noise(X, noise_prop):
X_clone = X.clone()
# X:batchsize, channel, height, width
batchsize, channel, height, width = X.shape
num = int(height * width * noise_prop)
for b in range(batchsize):
for c in range(channel):
for i in range(num):
h = np.random.randint(0, height)
w = np.random.randint(0, width)
if np.random.randint(0, 2) == 0:
X_clone[b, c, h, w] = 0 # 黑點
else:
X_clone[b, c, h, w] = 0.98 # 白點
return X_clone
def draw(X):
h, w = 2, 2
fig, axs = plt.subplots(nrows=h, ncols=w)
for i in range(h):
for j in range(w):
axs[i, j].imshow(X[(i + 1) * (j + 1) - 1, 0, :] * 255, cmap="gray")
axs[i, j].set_xticks([])
axs[i, j].set_yticks([])
plt.show()
class SDAE(nn.Module):
def __init__(self, in_size):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(in_size, 400),
nn.ReLU(),
nn.Linear(400, 100),
nn.ReLU())
self.decoder = nn.Sequential(
nn.Linear(100, 400),
nn.ReLU(),
nn.Linear(400, in_size))
self.sdae = nn.Sequential(self.encoder, self.decoder)
def forward(self, X):
return self.sdae(X)
model = SDAE(opt.image_size)
loss = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=opt.lr)
def train(epochs):
print("training..........")
for epoch in range(epochs):
for i, (X, _) in enumerate(train_loader):
y = X.view(-1, opt.image_size)
X = make_noise(X, 0.1)
X = X.view(-1, opt.image_size)
output = model(X)
train_loss = loss(output, y)
optimizer.zero_grad()
train_loss.backward()
optimizer.step()
test_loss = 0
with torch.no_grad():
for i, (X, _) in enumerate(test_loader):
X = X.view(-1, opt.image_size)
output = model(X)
test_loss += loss(output, X).item()
print("epoch [{}/{}], test_loss: {:.4f}".format(epoch + 1,
epochs, test_loss))
train(opt.epoch)
# 持久化模型
torch.save(model, 'model/model.pth')